From b5c6422601f937c2a7c24bb5610aca33428acde7 Mon Sep 17 00:00:00 2001
From: Wolfgang Hottgenroth
-If you want to leave a comment on anything, please use my email or Twitter account
.
+If you want to leave a comment on anything, please use my email or Twitter account..
And: yes, I know, this homepage is soooo ugly. I'm sorry, no CSS yet. @@ -46,6 +46,134 @@ And: yes, I know, this homepage is soooo ugly. I'm sorry, no CSS yet. +
+A Theremin is a rather old electronic music instrument, invented in 1928. It is played by approaching hands to two antennas, without touching them. One antenna is used to manipulate the frequeny of the tone, the other one to manipulate the volume. +
+ +
+
+
+This is just another Theremin. Only basic structure of the circuit was taken from many other published Theremin circuits. +
+ +
+
+
+Completely new (or at least not found during my Theremin googling) is the digital zero-calibration. +
+ ++The both left-hand-side oscillators together with the mixer+filter block provide the signal to control the volume, the right-hand-side oscillators and mixer+filter block provide the signal to control the frequency. +
+ ++Each of these both couples consists of two oscillators and a mixer+filter block. Both oscillators have to swing on exactly the same frequency, in this case of about 1.3MHz. While the exact frequency does not matter, it is significant that both oscillators have the same frequency. The signals of both oscillators will be mixed, which means, multiplied. +
+ ++[math]sin(omega_0 t) sin(omega t)[/math] +
+ ++Here [math]omega[/math] is the frequncy of one of the oscillators while [math]omega_0[/math] is the frequency of the other one. +
+ ++This term can be modified using the addition rule for trigonometric functions into +
+ ++[math]frac{cos((omega_0-omega)t)-cos((omega_0+omega)t)}{2}[/math] +
+ ++Due to this transformation, two signals, one with the sum and one with the difference of both input signal frequencies, are accumulated. +When both frequencies are exactly the same, one part of the sum appears as a DC offset, while the other part is the doubled frequency. +If one oscillator is de-tuned by only a few Hzs, one part are this few Hzs (a very low, hearable frequency) and the other part is still (roughly) the doubled frequency (a high frequency). The high frequency part can now be suppressed using a lowpass-filter. +Multiplication of two signals can be done using an analog four quadrant multiplier, like the AD633. So, this is the schematic of the mixer+filter block: +
+ +
+
+
+The output signal of this block is the difference of the detuning of the one oscillator. +
+ ++Detuning of the oscillator will be achieved by approaching the hand to the antenna of the oscillator. +
+ +
+
+
+The antenna acts as a kind of a capacitive sensor and by approaching the hand a very small amount of capacity is added into the LC resonator. +
+ ++The other oscillator is a fix-frequency oscillator which can be tuned to swing on the same frequency as the first oscillator in a not detuned state. +
+ +
+
+
+This tuning is achieved by biasing the two varactor diodes. +
+ ++Here is automated tuning circuit steps in: +
+ +
+
+
+The low-frequency output signal of the mixer+filter block is provided through a 2-to-1 multiplexer (the four NAND-gates) into a microcontroller. The microcontroller measures the frequency and as long as it is above a frequency [math]epsilon[/math] of say 10Hz, the bias voltage [math]U_{tune}[/math] is increased. +
+ ++These both oscillators with mixer+filter and one channel of the zero-calibration appear twice in the whole circuit, one for frequency manipulation and one for volume manipulation. +
+ ++The low-frequency, hearable, signal and the volume-control signal are brought together in the volume-control circuit +
+ +
+
+
+Here, the low-frequency signal [math]U_{Lf1}[/math] is passed through a high-pass filter. The high-pass filter is calculated that way that the whole detunable frequency range comes onto the ramp of the filter. So, the not detuned output signal of the mixer+filter is a DC signal, which is suppressed completely by the high-pass filter (beginning of the ramp) and the maximum detuned output signal of about 2kHz matched roughly to the end of the ramp. This filtered signal is rectified and only the negative half-wave of the signal passes the diode. This half-wave signal is sieved by the larger capacitor to get a DC signal between 0 and the maximum amplitude which passed the fiter. This negative DC signal is fed into the FET, which is configured as a voltage controlled resistor. This voltage controlled resistor and the fix resistor (5k6) are building a voltage controlled voltage divider. The hearable frequency signal [math]U_{Lf2}[/math] is fed into this voltage divider and passed to an amplifier. +
+ ++The output signal of this block in turn is the volume-controlled and frequency-controlled signal which is the output signal of this Theremin. It is passed into a power-amplifier and into a speaker - done. +
+ + + + + + + + + diff --git a/posts/2013-07-01.01/article.m4 b/posts/2013-07-01.01/article.m4 new file mode 100644 index 0000000..d2536f2 --- /dev/null +++ b/posts/2013-07-01.01/article.m4 @@ -0,0 +1,126 @@ +define(`TITLE', `Theremin') +define(`DATE', `2013-07-01') +define(`CONTENT', ` ++A Theremin is a rather old electronic music instrument, invented in 1928. It is played by approaching hands to two antennas, without touching them. One antenna is used to manipulate the frequeny of the tone, the other one to manipulate the volume. +
+ +
+
+
+This is just another Theremin. Only basic structure of the circuit was taken from many other published Theremin circuits. +
+ +
+
+
+Completely new (or at least not found during my Theremin googling) is the digital zero-calibration. +
+ ++The both left-hand-side oscillators together with the mixer+filter block provide the signal to control the volume, the right-hand-side oscillators and mixer+filter block provide the signal to control the frequency. +
+ ++Each of these both couples consists of two oscillators and a mixer+filter block. Both oscillators have to swing on exactly the same frequency, in this case of about 1.3MHz. While the exact frequency does not matter, it is significant that both oscillators have the same frequency. The signals of both oscillators will be mixed, which means, multiplied. +
+ ++[math]sin(omega_0 t) sin(omega t)[/math] +
+ ++Here [math]omega[/math] is the frequncy of one of the oscillators while [math]omega_0[/math] is the frequency of the other one. +
+ ++This term can be modified using the addition rule for trigonometric functions into +
+ ++[math]frac{cos((omega_0-omega)t)-cos((omega_0+omega)t)}{2}[/math] +
+ ++Due to this transformation, two signals, one with the sum and one with the difference of both input signal frequencies, are accumulated. +When both frequencies are exactly the same, one part of the sum appears as a DC offset, while the other part is the doubled frequency. +If one oscillator is de-tuned by only a few Hz'`s, one part are this few Hz'`s (a very low, hearable frequency) and the other part is still (roughly) the doubled frequency (a high frequency). The high frequency part can now be suppressed using a lowpass-filter. +Multiplication of two signals can be done using an analog four quadrant multiplier, like the AD633. So, this is the schematic of the mixer+filter block: +
+ +
+
+
+The output signal of this block is the difference of the detuning of the one oscillator. +
+ ++Detuning of the oscillator will be achieved by approaching the hand to the antenna of the oscillator. +
+ +
+
+
+The antenna acts as a kind of a capacitive sensor and by approaching the hand a very small amount of capacity is added into the LC resonator. +
+ ++The other oscillator is a fix-frequency oscillator which can be tuned to swing on the same frequency as the first oscillator in a not detuned state. +
+ +
+
+
+This tuning is achieved by biasing the two varactor diodes. +
+ ++Here is automated tuning circuit steps in: +
+ +
+
+
+The low-frequency output signal of the mixer+filter block is provided through a 2-to-1 multiplexer (the four NAND-gates) into a microcontroller. The microcontroller measures the frequency and as long as it is above a frequency [math]epsilon[/math] of say 10Hz, the bias voltage [math]U_{tune}[/math] is increased. +
+ ++These both oscillators with mixer+filter and one channel of the zero-calibration appear twice in the whole circuit, one for frequency manipulation and one for volume manipulation. +
+ ++The low-frequency, hearable, signal and the volume-control signal are brought together in the volume-control circuit +
+ +
+
+
+Here, the low-frequency signal [math]U_{Lf1}[/math] is passed through a high-pass filter. The high-pass filter is calculated that way that the whole detunable frequency range comes onto the ramp of the filter. So, the not detuned output signal of the mixer+filter is a DC signal, which is suppressed completely by the high-pass filter (beginning of the ramp) and the maximum detuned output signal of about 2kHz matched roughly to the end of the ramp. This filtered signal is rectified and only the negative half-wave of the signal passes the diode. This half-wave signal is sieved by the larger capacitor to get a DC signal between 0 and the maximum amplitude which passed the fiter. This negative DC signal is fed into the FET, which is configured as a voltage controlled resistor. This voltage controlled resistor and the fix resistor (5k6) are building a voltage controlled voltage divider. The hearable frequency signal [math]U_{Lf2}[/math] is fed into this voltage divider and passed to an amplifier. +
+ ++The output signal of this block in turn is the volume-controlled and frequency-controlled signal which is the output signal of this Theremin. It is passed into a power-amplifier and into a speaker - done. +
+ + + + +') \ No newline at end of file diff --git a/export-media/foto-am-30-06-13-um-20-021.jpg b/posts/2013-07-01.01/foto-am-30-06-13-um-20-021.jpg similarity index 100% rename from export-media/foto-am-30-06-13-um-20-021.jpg rename to posts/2013-07-01.01/foto-am-30-06-13-um-20-021.jpg diff --git a/export-media/scan_005006-1024x654.jpg b/posts/2013-07-01.01/scan_005006-1024x654.jpg similarity index 100% rename from export-media/scan_005006-1024x654.jpg rename to posts/2013-07-01.01/scan_005006-1024x654.jpg diff --git a/export-media/scan_005006_2-1024x553.jpg b/posts/2013-07-01.01/scan_005006_2-1024x553.jpg similarity index 100% rename from export-media/scan_005006_2-1024x553.jpg rename to posts/2013-07-01.01/scan_005006_2-1024x553.jpg diff --git a/export-media/scan_005006_3.jpg b/posts/2013-07-01.01/scan_005006_3.jpg similarity index 100% rename from export-media/scan_005006_3.jpg rename to posts/2013-07-01.01/scan_005006_3.jpg diff --git a/export-media/scan_005006_4.jpg b/posts/2013-07-01.01/scan_005006_4.jpg similarity index 100% rename from export-media/scan_005006_4.jpg rename to posts/2013-07-01.01/scan_005006_4.jpg diff --git a/export-media/scan_005006_5-1024x717.jpg b/posts/2013-07-01.01/scan_005006_5-1024x717.jpg similarity index 100% rename from export-media/scan_005006_5-1024x717.jpg rename to posts/2013-07-01.01/scan_005006_5-1024x717.jpg diff --git a/export-media/scan_005006_6.jpg b/posts/2013-07-01.01/scan_005006_6.jpg similarity index 100% rename from export-media/scan_005006_6.jpg rename to posts/2013-07-01.01/scan_005006_6.jpg diff --git a/posts/2013-07-24.01/article.html b/posts/2013-07-24.01/article.html new file mode 100644 index 0000000..08c0371 --- /dev/null +++ b/posts/2013-07-24.01/article.html @@ -0,0 +1,91 @@ + + + + + + + ++The M-Bus (short for meter-bus) is primarily used for household meters for water, gas and electric power. See here and here. +
+ ++This is an attempt to build a simple master for this bus, which is a single-master multiple-slave bus, to be controlled using an Arduino system. +
+ + + ++Master to slave communication is performed by lowering the voltage at the bus by 12V, so here, from 30V to 18V. In the above schematic, T2 and IC1 together with the divider of R1 and R2 are forming a voltage source. When T1 is blocking, the bottom part of the divider is inactive and full 30V is the reference for the voltage source and the output voltage at the bus is 30V. When T1 is conducting (high signal at TX), the bottom part of the divider becomes active and the reference voltage is lowered to 18V, consequently the output voltage at the bus is 18V too. +
+ ++(Maybe the signal driving T1 needs to be inverted first to get the correct logical polarity, not yet verified.) +
+ ++This is the waveform with a feed of 10kHz at TX: +
+ + + ++Ch. 2 (blue) is the input signal from the function generator, Ch. 1 (yellow) is the output signal at the bus terminals. It still needs to be verified whether the setup time (delay between falling edge of input and raising edge of output) is short enough. +
+ ++R5 is a current sense for the receive path, R4 is a ground-load for an open bus and C1 serves to filter the output voltage. Only a rather small capacitor is used here to only filter the high-frequency noise (around 1MHz) from the source but not the signal of about 10kHz fed through T1 from TX. +
+ ++Slave to master communication is performed by changing the current sinked by the slave. A logical "1" is signalled by a current of maximum 1,5mA, a logical "0" is signalled by a current of 11-20mA. This is sensed at R5 and needs to be conditioned before feeding as RX into the microcontroller. +
+ ++The voltage over R5 is in the low load situation about 100mV, in the high load situation about 1V. These are good voltages to block or conduct a PNP transistor, in the following schematic T3. +
+ + + ++The voltage of the signal at the point RX is limited using a Z-diode to fit into the range acceptable for a microcontroller. It is: +
+ + + ++Ch. 1 (yellow) is the signal from the generator to switch the load situation, Ch. 2 (blue) the signal at RX. +
+ ++The MOSFET T1 has been replaced by a NPN transistor BC547, the waveforms are still very good: +
+ + + ++Ch. 1 (yellow) is the voltage on the M-bus terminal, Ch. 2 (blue) is the generator signal at TX. +
+ + + + + + diff --git a/posts/2013-07-24.01/article.m4 b/posts/2013-07-24.01/article.m4 new file mode 100644 index 0000000..d8198a8 --- /dev/null +++ b/posts/2013-07-24.01/article.m4 @@ -0,0 +1,78 @@ +define(`TITLE', `M-Bus Master, Part 1 (Basics)') +define(`DATE', `2018-07-24') +define(`CONTENT', ` +(Sorry, all images lost in a former migration.) + ++The M-Bus (short for meter-bus) is primarily used for household meters for water, gas and electric power. See here and here. +
+ ++This is an attempt to build a simple master for this bus, which is a single-master multiple-slave bus, to be controlled using an Arduino system. +
+ + + ++Master to slave communication is performed by lowering the voltage at the bus by 12V, so here, from 30V to 18V. In the above schematic, T2 and IC1 together with the divider of R1 and R2 are forming a voltage source. When T1 is blocking, the bottom part of the divider is inactive and full 30V is the reference for the voltage source and the output voltage at the bus is 30V. When T1 is conducting (high signal at TX), the bottom part of the divider becomes active and the reference voltage is lowered to 18V, consequently the output voltage at the bus is 18V too. +
+ ++(Maybe the signal driving T1 needs to be inverted first to get the correct logical polarity, not yet verified.) +
+ ++This is the waveform with a feed of 10kHz at TX: +
+ + + ++Ch. 2 (blue) is the input signal from the function generator, Ch. 1 (yellow) is the output signal at the bus terminals. It still needs to be verified whether the setup time (delay between falling edge of input and raising edge of output) is short enough. +
+ ++R5 is a current sense for the receive path, R4 is a ground-load for an open bus and C1 serves to filter the output voltage. Only a rather small capacitor is used here to only filter the high-frequency noise (around 1MHz) from the source but not the signal of about 10kHz fed through T1 from TX. +
+ ++Slave to master communication is performed by changing the current sinked by the slave. A logical "1" is signalled by a current of maximum 1,5mA, a logical "0" is signalled by a current of 11-20mA. This is sensed at R5 and needs to be conditioned before feeding as RX into the microcontroller. +
+ ++The voltage over R5 is in the low load situation about 100mV, in the high load situation about 1V. These are good voltages to block or conduct a PNP transistor, in the following schematic T3. +
+ + + ++The voltage of the signal at the point RX is limited using a Z-diode to fit into the range acceptable for a microcontroller. It is: +
+ + + ++Ch. 1 (yellow) is the signal from the generator to switch the load situation, Ch. 2 (blue) the signal at RX. +
+ ++The MOSFET T1 has been replaced by a NPN transistor BC547, the waveforms are still very good: +
+ + + ++Ch. 1 (yellow) is the voltage on the M-bus terminal, Ch. 2 (blue) is the generator signal at TX. +
+ +') \ No newline at end of file diff --git a/posts/2013-09-09.01/article.html b/posts/2013-09-09.01/article.html new file mode 100644 index 0000000..4fc0e77 --- /dev/null +++ b/posts/2013-09-09.01/article.html @@ -0,0 +1,123 @@ + + + + + + + +
+so 10 40 21 61 16
+success
+SO RESP: E5
+
+
+so
is the command for the firmware to send the following octets on the bus, success
is the acknowledgement, that this has been done and SO RESP:
is the prefix for the octets received from the bus.
+
+According the the manufacturers documentation the M-Bus command REQ_UD2
(see here in the M-Bus documentation should be used to request all the measurement data from the powermeter, and it works:
+
+Here without any consumer load:
+
+so 10 5b 21 7c 16
+success
+SO RESP: 68 38 38 68 08 21 72 99 51 00 13 2E 19 21 02 01 00 00 00 8C
+10 04 06 00 00 00 8C 11 04 00 00 00 00 02 FD C9 FF 01 E3 00 02 FD DB
+FF 01 00 00 02 AC FF 01 00 00 82 40 AC FF 01 00 00 EB 16
+
+
+Here with an electric kettle as consumer load:
+
+so 10 5b 21 7c 16
+success
+SO RESP: 68 38 38 68 08 21 72 99 51 00 13 2E 19 21 02 02 00 00 00 8C
+10 04 06 00 00 00 8C 11 04 00 00 00 00 02 FD C9 FF 01 DE 00 02 FD DB
+FF 01 56 00 02 AC FF 01 CD 00 82 40 AC FF 01 00 00 0A 16
+
+
+In the above answer of the powermeter the important measurement values are highlighted:
+
+[table]
+Data,measurement category,multiplicator and unit,
+06 00 00 00
,total consumption,0.01kWh,0.06kWh
+DE 00
,voltage,1V,222V
+56 00
,current,0.1A,8.6A
+CD 00
,power,0.01kW,2.05kW
+[/table]
+
+
+Here is the firmware for the Arduino.
+
+
+This is a closer look into the response data structure:
+
+(Manufacturer IDs can be found here.)
+
+[table]
+Octet(s), Field, Meaning
+68 38 38 68
, Preamble with lentgh,
+08
, C Field,
+21
, A Field,
+72
, CI Field, variable data response
+
+99 51 00 13
, Ident. No.,
+2E 19
, Manufacturer, FIN = Finder GmbH
+21
, Version,
+02
, Medium, Electricity
+02
, Access No,
+00
, Status,
+00 00
, Signature,
+
+8C
, DIF, ext; instantaneuos value; 8 digit BCD
+10
, DIFE, tariff 1; storage 0
+04
, VIF, Energy; 10^(4-3)Wh
+06 00 00 00
, Value, 0.06kWh
+8C
, DIF, ext; instantaneuos value; 8 digit BCD
+11
, DIFE, tariff 1; storage 1
+04
, VIF, Energy; 10^(4-3)Wh
+00 00 00 00
, Value, 0
+02
, DIF, 16bit integer
+FD
, VIF, ext; true VIF is next; see table for secondary VIF
+C9
, VIFE, ext; Voltage; 10^(9-9)V
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+DE 00
, Value, 222V
+02
, DIF, 16bit integer
+FD
, VIF, ext; true VIF is next; see table for secondary VIF
+DB
, VIFE, ext; Current; 10^(11-12)A
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+56 00
, Value, 8.6A
+02
, DIF, 16bit integer
+AC
, VIF, ext; Power; 10^(4-3)W
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+CD 00
, Value, 2.05kW
+82
, DIF, ext; 16bit integer
+40
, DIFE, tariff 0; storage 4
+AC
, VIF, ext; Power; 10^(4-3)W
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+00 00
, Value, 0
+
+0A
, Checksum,
+16
, Stopbyte,
+[/table]
+
+
+
+
+
diff --git a/posts/2013-09-09.01/article.m4 b/posts/2013-09-09.01/article.m4
new file mode 100644
index 0000000..e4d2c48
--- /dev/null
+++ b/posts/2013-09-09.01/article.m4
@@ -0,0 +1,110 @@
+define(`TITLE', `M-Bus Master, Part 2 (Electricity)')
+define(`DATE', `2013-09-09')
+define(`CONTENT', `
+
+so 10 40 21 61 16
+success
+SO RESP: E5
+
+
+so
is the command for the firmware to send the following octets on the bus, success
is the acknowledgement, that this has been done and SO RESP:
is the prefix for the octets received from the bus.
+
+According the the manufacturers documentation the M-Bus command REQ_UD2
(see here in the M-Bus documentation should be used to request all the measurement data from the powermeter, and it works:
+
+Here without any consumer load:
+
+so 10 5b 21 7c 16
+success
+SO RESP: 68 38 38 68 08 21 72 99 51 00 13 2E 19 21 02 01 00 00 00 8C
+10 04 06 00 00 00 8C 11 04 00 00 00 00 02 FD C9 FF 01 E3 00 02 FD DB
+FF 01 00 00 02 AC FF 01 00 00 82 40 AC FF 01 00 00 EB 16
+
+
+Here with an electric kettle as consumer load:
+
+so 10 5b 21 7c 16
+success
+SO RESP: 68 38 38 68 08 21 72 99 51 00 13 2E 19 21 02 02 00 00 00 8C
+10 04 06 00 00 00 8C 11 04 00 00 00 00 02 FD C9 FF 01 DE 00 02 FD DB
+FF 01 56 00 02 AC FF 01 CD 00 82 40 AC FF 01 00 00 0A 16
+
+
+In the above answer of the powermeter the important measurement values are highlighted:
+
+[table]
+Data,measurement category,multiplicator and unit,
+06 00 00 00
,total consumption,0.01kWh,0.06kWh
+DE 00
,voltage,1V,222V
+56 00
,current,0.1A,8.6A
+CD 00
,power,0.01kW,2.05kW
+[/table]
+
+
+Here is the firmware for the Arduino.
+
+
+This is a closer look into the response data structure:
+
+(Manufacturer IDs can be found here.)
+
+[table]
+Octet(s), Field, Meaning
+68 38 38 68
, Preamble with lentgh,
+08
, C Field,
+21
, A Field,
+72
, CI Field, variable data response
+
+99 51 00 13
, Ident. No.,
+2E 19
, Manufacturer, FIN = Finder GmbH
+21
, Version,
+02
, Medium, Electricity
+02
, Access No,
+00
, Status,
+00 00
, Signature,
+
+8C
, DIF, ext; instantaneuos value; 8 digit BCD
+10
, DIFE, tariff 1; storage 0
+04
, VIF, Energy; 10^(4-3)Wh
+06 00 00 00
, Value, 0.06kWh
+8C
, DIF, ext; instantaneuos value; 8 digit BCD
+11
, DIFE, tariff 1; storage 1
+04
, VIF, Energy; 10^(4-3)Wh
+00 00 00 00
, Value, 0
+02
, DIF, 16bit integer
+FD
, VIF, ext; true VIF is next; see table for secondary VIF
+C9
, VIFE, ext; Voltage; 10^(9-9)V
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+DE 00
, Value, 222V
+02
, DIF, 16bit integer
+FD
, VIF, ext; true VIF is next; see table for secondary VIF
+DB
, VIFE, ext; Current; 10^(11-12)A
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+56 00
, Value, 8.6A
+02
, DIF, 16bit integer
+AC
, VIF, ext; Power; 10^(4-3)W
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+CD 00
, Value, 2.05kW
+82
, DIF, ext; 16bit integer
+40
, DIFE, tariff 0; storage 4
+AC
, VIF, ext; Power; 10^(4-3)W
+FF
, VIFE, manufacturer specific next VIFE
+01
, VIFE,
+00 00
, Value, 0
+
+0A
, Checksum,
+16
, Stopbyte,
+[/table]
+')
\ No newline at end of file
diff --git a/posts/2013-09-10.01/article.html b/posts/2013-09-10.01/article.html
new file mode 100644
index 0000000..87af67d
--- /dev/null
+++ b/posts/2013-09-10.01/article.html
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+ ++ + +Responding the+ +IC2 was already added in the last step, since the output of the analog circuit and the requirements of the MCU didnt match concerning their logical polarity. + +Now, T3 (BS108) and R9 (2k2) have been added to disable the receive path while data is transmitted. +
REQ_UD2
command, the water meter sends this telegram (address 0x30 has been assigned to it first):
+
+
+so 10 5b 30 8b 16
+success
+SO RESP: 68 46 46 68 08 30 72 45 71 43 00 24 23 25 07 02 00 00 00 0C
+13 51 84 00 00 8C 10 13 00 00 00 00 0B 3B 00 00 00 0B 26 62 06 00 02
+5A CC 00 04 6D 17 16 A9 19 7C 13 00 00 00 00 FC 10 13 00 00 00 00 72
+6C 00 00 42 EC 7E BF 1C 35 16
+
+
+A closer look, consulting the M-Bus documentation reveals the meaning:
+
+[table]Octet(s), Field, Meaning
+68 46 46 68
, Preamble with length,
+08
, C field,
+30
, A field (address),
+72
, CI field, variable data response
+
+45 71 43 00
, Ident. No.,
+24 23
, Manufacturer , HYD = Hydrometer GmbH
+25
, Version,
+07
, Medium, Water
+02
, Access No.,
+00
, Status,
+00 00
, Signature,
+
+0C
, DIF, 8 digit BCD
+13
, VIF, Volume; 10^(3-6)m^3
+51 84 00 00
, Value, 8.451m^3
+8C
, DIF, ext; 8 digit BCD
+10
, DIFE, minimum value
+13
, VIF, Volume; 10^(3-6)m^3
+00 00 00 00
, Value, 0
+0B
, DIF, 6 digit BCD
+3B
, VIF, Volume flow; 10^(3-6)m^3/h
+00 00 00
, Value, 0
+0B
, DIF, 6 digit BCD
+26
, VIF, Operating time; hours
+62 06 00
, Value, 662h = 27.6d
+02
, DIF, 16bit integer
+5A
, VIF, Flow Temperature; 10^(2-3)°C
+CC 00
, Value, 20.4°C
+04
, DIF, 32bit integer
+6D
, VIF, Time Point; time&date
+17 16 A9 19
, Value,
+7C
, DIF, LSB; value during error state; 8 digit BCD
+13
, VIF, Volume; 10^(3-6)m^3
+00 00 00 00
, Value, 0
+FC
, DIF, ext; LSB; value during error state; 8 digit BCD
+10
, DIFE, tariff 1
+13
, VIF, Volume; 10^(3-6)m^3
+00 00 00 00
, Value, 0
+72
, DIF, LSB; value during error state; 16bit integer
+6C
, VIF, Time Point; date
+00 00
, Value, 0
+42
, DIF, LSB; 16bit integer
+EC
, VIF, ext; Time Point; date
+7E
, VIFE, ??
+BF 1C
, Value,
+
+35
, Checksum,
+16
, Stopbyte,
+[/table]
+
+Decoding of the time&date type is done like this:
++ if (t_data) + { + if (t_data_size == 4) // Type F = Compound CP32: Date and Time + { + if ((t_data[0] & 0x80) == 0) // Time valid ? + { + t->tm_min = t_data[0] & 0x3F; + t->tm_hour = t_data[1] & 0x1F; + t->tm_mday = t_data[2] & 0x1F; + t->tm_mon = (t_data[3] & 0x0F) - 1; + t->tm_year = ((t_data[2] & 0xE0) >> 5) | + ((t_data[3] & 0xF0) >> 1); + t->tm_isdst = (t_data[1] & 0x80) ? 1 : 0; // day saving time + } + } + else if (t_data_size == 2) // Type G: Compound CP16: Date + { + t->tm_mday = t_data[0] & 0x1F; + t->tm_mon = (t_data[1] & 0x0F) - 1; + t->tm_year = ((t_data[0] & 0xE0) >> 5) | + ((t_data[1] & 0xF0) >> 1); + } + } ++(Found on github in the libmbus repo.) + + + + + + diff --git a/posts/2013-09-10.01/article.m4 b/posts/2013-09-10.01/article.m4 new file mode 100644 index 0000000..3c37245 --- /dev/null +++ b/posts/2013-09-10.01/article.m4 @@ -0,0 +1,116 @@ +define(`TITLE', `M-Bus Master, Part 3 (Water)') +define(`DATE', `2013-09-10') +define(`CONTENT', ` +Now, also the water meter talks to me. + +
++ + +Responding the+ +IC2 was already added in the last step, since the output of the analog circuit and the requirements of the MCU didn'`t match concerning their logical polarity. + +Now, T3 (BS108) and R9 (2k2) have been added to disable the receive path while data is transmitted. +
REQ_UD2
command, the water meter sends this telegram (address 0x30 has been assigned to it first):
+
+
+so 10 5b 30 8b 16
+success
+SO RESP: 68 46 46 68 08 30 72 45 71 43 00 24 23 25 07 02 00 00 00 0C
+13 51 84 00 00 8C 10 13 00 00 00 00 0B 3B 00 00 00 0B 26 62 06 00 02
+5A CC 00 04 6D 17 16 A9 19 7C 13 00 00 00 00 FC 10 13 00 00 00 00 72
+6C 00 00 42 EC 7E BF 1C 35 16
+
+
+A closer look, consulting the M-Bus documentation reveals the meaning:
+
+[table]Octet(s), Field, Meaning
+68 46 46 68
, Preamble with length,
+08
, C field,
+30
, A field (address),
+72
, CI field, variable data response
+
+45 71 43 00
, Ident. No.,
+24 23
, Manufacturer , HYD = Hydrometer GmbH
+25
, Version,
+07
, Medium, Water
+02
, Access No.,
+00
, Status,
+00 00
, Signature,
+
+0C
, DIF, 8 digit BCD
+13
, VIF, Volume; 10^(3-6)m^3
+51 84 00 00
, Value, 8.451m^3
+8C
, DIF, ext; 8 digit BCD
+10
, DIFE, minimum value
+13
, VIF, Volume; 10^(3-6)m^3
+00 00 00 00
, Value, 0
+0B
, DIF, 6 digit BCD
+3B
, VIF, Volume flow; 10^(3-6)m^3/h
+00 00 00
, Value, 0
+0B
, DIF, 6 digit BCD
+26
, VIF, Operating time; hours
+62 06 00
, Value, 662h = 27.6d
+02
, DIF, 16bit integer
+5A
, VIF, Flow Temperature; 10^(2-3)°C
+CC 00
, Value, 20.4°C
+04
, DIF, 32bit integer
+6D
, VIF, Time Point; time&date
+17 16 A9 19
, Value,
+7C
, DIF, LSB; value during error state; 8 digit BCD
+13
, VIF, Volume; 10^(3-6)m^3
+00 00 00 00
, Value, 0
+FC
, DIF, ext; LSB; value during error state; 8 digit BCD
+10
, DIFE, tariff 1
+13
, VIF, Volume; 10^(3-6)m^3
+00 00 00 00
, Value, 0
+72
, DIF, LSB; value during error state; 16bit integer
+6C
, VIF, Time Point; date
+00 00
, Value, 0
+42
, DIF, LSB; 16bit integer
+EC
, VIF, ext; Time Point; date
+7E
, VIFE, ??
+BF 1C
, Value,
+
+35
, Checksum,
+16
, Stopbyte,
+[/table]
+
+Decoding of the time&date type is done like this:
++ if (t_data) + { + if (t_data_size == 4) // Type F = Compound CP32: Date and Time + { + if ((t_data[0] & 0x80) == 0) // Time valid ? + { + t->tm_min = t_data[0] & 0x3F; + t->tm_hour = t_data[1] & 0x1F; + t->tm_mday = t_data[2] & 0x1F; + t->tm_mon = (t_data[3] & 0x0F) - 1; + t->tm_year = ((t_data[2] & 0xE0) >> 5) | + ((t_data[3] & 0xF0) >> 1); + t->tm_isdst = (t_data[1] & 0x80) ? 1 : 0; // day saving time + } + } + else if (t_data_size == 2) // Type G: Compound CP16: Date + { + t->tm_mday = t_data[0] & 0x1F; + t->tm_mon = (t_data[1] & 0x0F) - 1; + t->tm_year = ((t_data[0] & 0xE0) >> 5) | + ((t_data[1] & 0xF0) >> 1); + } + } ++(Found on github in the libmbus repo.) + +') \ No newline at end of file diff --git a/posts/2013-09-11.01/article.html b/posts/2013-09-11.01/article.html new file mode 100644 index 0000000..414e3a2 --- /dev/null +++ b/posts/2013-09-11.01/article.html @@ -0,0 +1,87 @@ + + + + + + + +
+so 68 06 06 68 53 fe 51 01 7a 40 5d 16
+success
+SO RESP: E5
+
+
+And it works:
+
+so 10 40 40 80 16
+success
+SO RESP: E5
+
+
+And here is the measurement:
+
+so 10 5b 40 9b 16
+success
+SO RESP: 68 56 56 68 08 40 72 43 60 52 00 77 04 14 03 CA 10 00 00 0C 78 76 03 01 10 0D 7C 08 44 49 20 2E 74 73 75 63 0A 30 30 30 30 30 30 30 30 30 30 04 6D 38 15 AA 19 02 7C 09 65 6D 69 74 20 2E 74 61 62 22 0C 04 13 C1 0E 00 00 04 93 7F 4E 01 00 00 44 13 5D 08 00 00 0F 01 00 1F DE 16
+
+
+
+[table]Octet(s), Field, Meaning
+68 56 56 68
, preamble and length,
+08
, C Field,
+40
, A Field,
+72
, CI Field, variable data response
+43 60 52 00
, Ident. No.,
+77 04
, Manufacturer, ACW; Actaris (Itron)
+14
, Version,
+03
, Medium, Gas,
+CA
, Access No,
+10
, Status,
+00 00
, Signature,
+0C
, DIF, 8 digit BCD
+78
, VIF, Fabrication No
+76 03 01 10
, Value,
+0D
, DIF, variable length
+7C
, VIF, true in following string; length in first byte
+08
, VIF, first byte; length = 8
+44 49 20 2E 74 73 75 63
, VIF, Value (LSB first) = "cust. ID";
+0A
, Value, length = 10
+30 30 30 30 30 30 30 30 30 30
, Value, data
+04
, DIF, 32bit integer
+6D
, VIF, Time Point; time&date
+38 15 AA 19
, Value,
+02
, DIF, 16bit integer
+7C
, VIF, true in following string; length in first byte
+09
, VIF, first byte; length = 9
+65 6D 69 74 20 2E 74 61 62
, VIF, Value (LSB first) = "bat. time"
+22 0C
, Value;
+04
, DIF, 32bit integer
+13
, VIF, Volume; 10^(3-6)m^3
+C1 0E 00 00
, Value,
+04
, DIF, 32bit integer
+93
, VIF, ext; Volume; 10^(3-6)m^3
+7F
, VIFE,
+4E 01 00 00
, Value,
+44
, DIF, 32bit integer
+13
, VIF, Volume; 10^(3-6)m^3
+5D 08 00 00
, Value,
+0F
, DIF, Special Functions; Start of manufacturer specific data structures to end of user data
+01 00 1F
, ,
+DE
, Checksum,
+16
, Stopbyte
+[/table]
+
+
+
+
+
diff --git a/posts/2013-09-11.01/article.m4 b/posts/2013-09-11.01/article.m4
new file mode 100644
index 0000000..1d492a6
--- /dev/null
+++ b/posts/2013-09-11.01/article.m4
@@ -0,0 +1,74 @@
+define(`TITLE', `M-Bus Master, Part 4 (Gas)')
+define(`DATE', `2013-09-11')
+define(`CONTENT', `
+And finally, the gas meter talks:
+
+
+so 68 06 06 68 53 fe 51 01 7a 40 5d 16
+success
+SO RESP: E5
+
+
+And it works:
+
+so 10 40 40 80 16
+success
+SO RESP: E5
+
+
+And here is the measurement:
+
+so 10 5b 40 9b 16
+success
+SO RESP: 68 56 56 68 08 40 72 43 60 52 00 77 04 14 03 CA 10 00 00 0C 78 76 03 01 10 0D 7C 08 44 49 20 2E 74 73 75 63 0A 30 30 30 30 30 30 30 30 30 30 04 6D 38 15 AA 19 02 7C 09 65 6D 69 74 20 2E 74 61 62 22 0C 04 13 C1 0E 00 00 04 93 7F 4E 01 00 00 44 13 5D 08 00 00 0F 01 00 1F DE 16
+
+
+
+[table]Octet(s), Field, Meaning
+68 56 56 68
, preamble and length,
+08
, C Field,
+40
, A Field,
+72
, CI Field, variable data response
+43 60 52 00
, Ident. No.,
+77 04
, Manufacturer, ACW; Actaris (Itron)
+14
, Version,
+03
, Medium, Gas,
+CA
, Access No,
+10
, Status,
+00 00
, Signature,
+0C
, DIF, 8 digit BCD
+78
, VIF, Fabrication No
+76 03 01 10
, Value,
+0D
, DIF, variable length
+7C
, VIF, true in following string; length in first byte
+08
, VIF, first byte; length = 8
+44 49 20 2E 74 73 75 63
, VIF, Value (LSB first) = "cust. ID";
+0A
, Value, length = 10
+30 30 30 30 30 30 30 30 30 30
, Value, data
+04
, DIF, 32bit integer
+6D
, VIF, Time Point; time&date
+38 15 AA 19
, Value,
+02
, DIF, 16bit integer
+7C
, VIF, true in following string; length in first byte
+09
, VIF, first byte; length = 9
+65 6D 69 74 20 2E 74 61 62
, VIF, Value (LSB first) = "bat. time"
+22 0C
, Value;
+04
, DIF, 32bit integer
+13
, VIF, Volume; 10^(3-6)m^3
+C1 0E 00 00
, Value,
+04
, DIF, 32bit integer
+93
, VIF, ext; Volume; 10^(3-6)m^3
+7F
, VIFE,
+4E 01 00 00
, Value,
+44
, DIF, 32bit integer
+13
, VIF, Volume; 10^(3-6)m^3
+5D 08 00 00
, Value,
+0F
, DIF, Special Functions; Start of manufacturer specific data structures to end of user data
+01 00 1F
, ,
+DE
, Checksum,
+16
, Stopbyte
+[/table]
+')
\ No newline at end of file
diff --git a/posts/2013-09-21.01/article.html b/posts/2013-09-21.01/article.html
new file mode 100644
index 0000000..30a4906
--- /dev/null
+++ b/posts/2013-09-21.01/article.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+ +%% +due 1 DE:AD:BE:EF:FE:ED 192.168.75.20 ++ +/etc/bootpd.plist: +
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>bootp_enabled</key> + <false/> + <key>dhcp_enabled</key> + <array> + <string>en2</string> + </array> + <key>Subnets</key> + <array> + <dict> + <key>name</key> + <string>internal</string> + <key>net_mask</key> + <string>255.255.255.0</string> + <key>net_address</key> + <string>192.168.75.0</string> + <key>net_range</key> + <array> + <string>192.168.75.20</string> + <string>192.168.75.29</string> + </array> + <key>allocate</key> + <true/> + </dict> + </array> + </dict>z +</plist> ++ +And finally a small script to assign the address and start the server: +
+#!/bin/sh + +INTF=en2 + +ifconfig $INTF alias 192.168.75.1 +/usr/libexec/bootpd -D -d -i $INTF +ifconfig $INTF -alias 192.168.75.1 ++ +Done. + +Once the script is started, it assigns the IP address and starts the booted (the DHCP server). The server will not detach from the terminal and does not fork into background. So, it can simply be stopped using Ctrl-C. However, Ctrl-C also interrupts the script and the unassigning of the address is not executed. This unfortunately needs to be done manually. + +Now I can connect the Adruino Ethernet directly to my MacBook and it will receive the network configuration from it. + + + + + diff --git a/posts/2013-11-07.01/article.m4 b/posts/2013-11-07.01/article.m4 new file mode 100644 index 0000000..286e446 --- /dev/null +++ b/posts/2013-11-07.01/article.m4 @@ -0,0 +1,67 @@ +define(`TITLE', `DHCP server on a MacBook') +define(`DATE', `2013-11-07') +define(`CONTENT', ` +When working with the Arduino I almost have the Ethernet Shield attached or directly make use of the Arduino Ethernet board. Nearly everything I'`m playing around with is network-attached. + +Once it is done, I connect it to my home network, where a DHCP server provides network configuration. But while developing software for the beast, it is on the table in front of me with no network cables around, my laptop is connected via WLAN. A while a was using a small Mikrotik 750 with only the DHCP server enabled to connect my MacBook Air via the USB adapter and the Arduino. The problem: so much stuff and more over, the DHCP server of the Mikrotik overwrites the default gateway of my home network. So the MacBook has access to the home network, but when accessing the Internet it tries to route via the Mikrotik, which has no Internet connection. Bad! + +To solve this issue I considered to start a DHCP server on the MacBook, attaching it only to the USB network adapter. Since the related interface has no other connection, particularly hasn'`t received a network configuration from another DHCP server (the one in the home network has configured the WLAN interface), first an IP address has to be assigned to the interface. + +Actually, Mac OS X comes with a DHCP server, which just needs some configuration: + +/etc/bootptab (this is the MAC-address of the Arduino, just to know which IP address is assigned to it): +
+%% +due 1 DE:AD:BE:EF:FE:ED 192.168.75.20 ++ +/etc/bootpd.plist: +
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>bootp_enabled</key> + <false/> + <key>dhcp_enabled</key> + <array> + <string>en2</string> + </array> + <key>Subnets</key> + <array> + <dict> + <key>name</key> + <string>internal</string> + <key>net_mask</key> + <string>255.255.255.0</string> + <key>net_address</key> + <string>192.168.75.0</string> + <key>net_range</key> + <array> + <string>192.168.75.20</string> + <string>192.168.75.29</string> + </array> + <key>allocate</key> + <true/> + </dict> + </array> + </dict>z +</plist> ++ +And finally a small script to assign the address and start the server: +
+#!/bin/sh + +INTF=en2 + +ifconfig $INTF alias 192.168.75.1 +/usr/libexec/bootpd -D -d -i $INTF +ifconfig $INTF -alias 192.168.75.1 ++ +Done. + +Once the script is started, it assigns the IP address and starts the booted (the DHCP server). The server will not detach from the terminal and does not fork into background. So, it can simply be stopped using Ctrl-C. However, Ctrl-C also interrupts the script and the unassigning of the address is not executed. This unfortunately needs to be done manually. + +Now I can connect the Adruino Ethernet directly to my MacBook and it will receive the network configuration from it. +') \ No newline at end of file diff --git a/posts/2013-11-08.01/article.html b/posts/2013-11-08.01/article.html new file mode 100644 index 0000000..68f5291 --- /dev/null +++ b/posts/2013-11-08.01/article.html @@ -0,0 +1,60 @@ + + + + + + + +
+#include <stdio.h> +#include <stdlib.h> + +int main(int argc, const char* argv[]) { + if (argc != 2) { + printf("give filenamen"); + exit(1); + } + + + FILE *f = fopen(argv[1], "r"); + + const unsigned int BUFSIZE = 4096; + unsigned char buffer[BUFSIZE]; + + unsigned long cnt = 0; + unsigned long found = 0; + size_t r; + + while (0 != (r = fread(&buffer, 1, BUFSIZE, f))) { + cnt += r; + for (int i = 0; i < r; i++) { + if (0 != *(buffer + i)) { + found++; + } + } + if (0 == (cnt % 1000000)) { + printf("cnt: %ld, found: %ldn", cnt, found); + } + } + + printf("finally: cnt: %ld, found: %ldn", cnt, found); +} ++ +Compile it using: +
+gcc -std=c99 -o notnull notnull.c ++ + + + + diff --git a/posts/2013-11-08.01/article.m4 b/posts/2013-11-08.01/article.m4 new file mode 100644 index 0000000..9b9cc8d --- /dev/null +++ b/posts/2013-11-08.01/article.m4 @@ -0,0 +1,47 @@ +define(`TITLE', `Has this large file something else than zeros?') +define(`DATE', `2013-11-08') +define(`CONTENT', ` +This small piece of code was used during the analysis of an 800GB VMWare image after a server crash. + +notnull.c: +
+#include <stdio.h> +#include <stdlib.h> + +int main(int argc, const char* argv[]) { + if (argc != 2) { + printf("give filenamen"); + exit(1); + } + + + FILE *f = fopen(argv[1], "r"); + + const unsigned int BUFSIZE = 4096; + unsigned char buffer[BUFSIZE]; + + unsigned long cnt = 0; + unsigned long found = 0; + size_t r; + + while (0 != (r = fread(&buffer, 1, BUFSIZE, f))) { + cnt += r; + for (int i = 0; i < r; i++) { + if (0 != *(buffer + i)) { + found++; + } + } + if (0 == (cnt % 1000000)) { + printf("cnt: %ld, found: %ldn", cnt, found); + } + } + + printf("finally: cnt: %ld, found: %ldn", cnt, found); +} ++ +Compile it using: +
+gcc -std=c99 -o notnull notnull.c ++') \ No newline at end of file diff --git a/posts/2014-01-08.01/article.html b/posts/2014-01-08.01/article.html new file mode 100644 index 0000000..e4cbbb5 --- /dev/null +++ b/posts/2014-01-08.01/article.html @@ -0,0 +1,43 @@ + + + + + + + +
+const unsigned long DEBOUNCING_DEAD_TIME = 100; +const int DEBOUNCING_REPEAT = 1000; + + +volatile int rotaryCount = 0; +volatile bool switchState = false; +volatile unsigned long lastEvent = 0; + +int myDigitalRead(int a) { + int r = 0; + for (int i = 0; i < DEBOUNCING_REPEAT; i++) { + if (digitalRead(a) == HIGH) { + r++; + } else { + r--; + } + } + int res = -1; + if (r >= (DEBOUNCING_REPEAT / 2)) { + res = 1; + } else if (r <= -1 * (DEBOUNCING_REPEAT / 2)) { + res = 0; + } + return res; +} + +void rotary_a_interrupt() { + unsigned long currentEvent = millis(); + + if ((lastEvent == 0) || (lastEvent + DEBOUNCING_DEAD_TIME < currentEvent)) { + lastEvent = currentEvent; + + int a = myDigitalRead(ROTARY_A); + int b = myDigitalRead(ROTARY_B); + + if (((a != -1) && (b != -1))) { + if (a == b) { + rotaryCount++; + } else { + rotaryCount--; + } + } + } +} + +void rotary_b_interrupt() { + unsigned long currentEvent = millis(); + + if ((lastEvent == 0) || (lastEvent + DEBOUNCING_DEAD_TIME < currentEvent)) { + lastEvent = currentEvent; + + int a = myDigitalRead(ROTARY_A); + int b = myDigitalRead(ROTARY_B); + + if (((a != -1) && (b != -1))) { + if (a == b) { + rotaryCount--; + } else { + rotaryCount++; + } + } + } +} + +void switch_interrupt() { + unsigned long currentEvent = millis(); + + if ((lastEvent == 0) || (lastEvent + DEBOUNCING_DEAD_TIME < currentEvent)) { + lastEvent = currentEvent; + + switchState = true; + } +} + +void hardwareInit() { + pinMode(SWITCH, INPUT_PULLUP); + pinMode(ROTARY_A, INPUT_PULLUP); + pinMode(ROTARY_B, INPUT_PULLUP); + attachInterrupt(SWITCH_IRQ, switch_interrupt, FALLING); + attachInterrupt(ROTARY_A_IRQ, rotary_a_interrupt, CHANGE); + attachInterrupt(ROTARY_B_IRQ, rotary_b_interrupt, CHANGE); +} ++ +The primary problem was not to get any rotary event of the wrong direction, the secondary problem was not to miss too much events, best of course none. + +Only the combination of a dead time after one interrupt and validating a read pin state by rereading it MANY times solved the primary problem. To solve the secondary problem too, the validation has be softened by not requiring all reread states to have the same value but accepting a limited number of wrong values. + + + + + diff --git a/posts/2014-01-08.02/article.m4 b/posts/2014-01-08.02/article.m4 new file mode 100644 index 0000000..286aa98 --- /dev/null +++ b/posts/2014-01-08.02/article.m4 @@ -0,0 +1,94 @@ +define(`TITLE', `Debouncing a rotary encoder is really hard') +define(`DATE', `2014-01-08') +define(`CONTENT', ` +... but finally it worked: + +
+const unsigned long DEBOUNCING_DEAD_TIME = 100; +const int DEBOUNCING_REPEAT = 1000; + + +volatile int rotaryCount = 0; +volatile bool switchState = false; +volatile unsigned long lastEvent = 0; + +int myDigitalRead(int a) { + int r = 0; + for (int i = 0; i < DEBOUNCING_REPEAT; i++) { + if (digitalRead(a) == HIGH) { + r++; + } else { + r--; + } + } + int res = -1; + if (r >= (DEBOUNCING_REPEAT / 2)) { + res = 1; + } else if (r <= -1 * (DEBOUNCING_REPEAT / 2)) { + res = 0; + } + return res; +} + +void rotary_a_interrupt() { + unsigned long currentEvent = millis(); + + if ((lastEvent == 0) || (lastEvent + DEBOUNCING_DEAD_TIME < currentEvent)) { + lastEvent = currentEvent; + + int a = myDigitalRead(ROTARY_A); + int b = myDigitalRead(ROTARY_B); + + if (((a != -1) && (b != -1))) { + if (a == b) { + rotaryCount++; + } else { + rotaryCount--; + } + } + } +} + +void rotary_b_interrupt() { + unsigned long currentEvent = millis(); + + if ((lastEvent == 0) || (lastEvent + DEBOUNCING_DEAD_TIME < currentEvent)) { + lastEvent = currentEvent; + + int a = myDigitalRead(ROTARY_A); + int b = myDigitalRead(ROTARY_B); + + if (((a != -1) && (b != -1))) { + if (a == b) { + rotaryCount--; + } else { + rotaryCount++; + } + } + } +} + +void switch_interrupt() { + unsigned long currentEvent = millis(); + + if ((lastEvent == 0) || (lastEvent + DEBOUNCING_DEAD_TIME < currentEvent)) { + lastEvent = currentEvent; + + switchState = true; + } +} + +void hardwareInit() { + pinMode(SWITCH, INPUT_PULLUP); + pinMode(ROTARY_A, INPUT_PULLUP); + pinMode(ROTARY_B, INPUT_PULLUP); + attachInterrupt(SWITCH_IRQ, switch_interrupt, FALLING); + attachInterrupt(ROTARY_A_IRQ, rotary_a_interrupt, CHANGE); + attachInterrupt(ROTARY_B_IRQ, rotary_b_interrupt, CHANGE); +} ++ +The primary problem was not to get any rotary event of the wrong direction, the secondary problem was not to miss too much events, best of course none. + +Only the combination of a dead time after one interrupt and validating a read pin state by rereading it MANY times solved the primary problem. To solve the secondary problem too, the validation has be softened by not requiring all reread states to have the same value but accepting a limited number of wrong values. +') \ No newline at end of file diff --git a/posts/2014-01-14.01/article.html b/posts/2014-01-14.01/article.html new file mode 100644 index 0000000..2730bfb --- /dev/null +++ b/posts/2014-01-14.01/article.html @@ -0,0 +1,24 @@ + + + + + + + +
Well, it seems, that this way of over current signaling to the MCU does not work properly. In the first design, the input of the comparator was directly the lower end of the shunt resistor, which worked. So, the differentiator is required of current measurement, but is not usable for over current signaling. Needs some more work.+ + + +The both blue LEDs show activity on RX and TX, the red LED signals over current. + +The whole development setup: + +
Well, it seems, that this way of over current signaling to the MCU does not work properly. In the first design, the input of the comparator was directly the lower end of the shunt resistor, which worked. So, the differentiator is required of current measurement, but is not usable for over current signaling. Needs some more work.+ + + +The both blue LEDs show activity on RX and TX, the red LED signals over current. + +The whole development setup: + +