From 10e888649bec840e34613f1848cdb2edb1cf720c Mon Sep 17 00:00:00 2001 From: Wolfgang Hottgenroth Date: Mon, 7 May 2018 16:55:49 +0200 Subject: [PATCH] rest of articles --- posts/2014-04-07.01/article.m4 | 12 ++ posts/2014-05-11.01/article.m4 | 25 +++ posts/2014-05-11.02/article.m4 | 8 + posts/2014-08-04.01/article.m4 | 31 ++++ posts/2014-10-04.01/article.m4 | 21 +++ posts/2014-10-27.01/article.m4 | 17 ++ posts/2014-12-11.01/article.m4 | 27 ++++ posts/2014-12-15.01/article.m4 | 38 +++++ posts/2015-01-01.01/article.m4 | 34 ++++ posts/2015-06-05.02/article.m4 | 86 ++++++++++ posts/2015-06-05.03/article.m4 | 174 +++++++++++++++++++++ posts/2015-06-05.04/article.m4 | 92 +++++++++++ posts/2015-06-17.01/article.m4 | 47 ++++++ posts/2015-09-14.01/article.m4 | 178 +++++++++++++++++++++ posts/2015-10-12.01/article.m4 | 120 ++++++++++++++ posts/2016-03-03.01/article.m4 | 19 +++ posts/2016-09-09.01/article.m4 | 24 +++ posts/2016-09-12.01/article.m4 | 18 +++ posts/2016-10-14.01/article.m4 | 38 +++++ posts/2016-11-02.01/article.m4 | 47 ++++++ posts/2016-12-19.01/article.m4 | 26 ++++ posts/2017-07-04.01/article.m4 | 11 ++ posts/2018-01-26.01/article.m4 | 277 +++++++++++++++++++++++++++++++++ posts/2018-02-22.01/article.m4 | 29 ++++ posts/2018-04-30.01/article.m4 | 143 +++++++++++++++++ 25 files changed, 1542 insertions(+) create mode 100644 posts/2014-04-07.01/article.m4 create mode 100644 posts/2014-05-11.01/article.m4 create mode 100644 posts/2014-05-11.02/article.m4 create mode 100644 posts/2014-08-04.01/article.m4 create mode 100644 posts/2014-10-04.01/article.m4 create mode 100644 posts/2014-10-27.01/article.m4 create mode 100644 posts/2014-12-11.01/article.m4 create mode 100644 posts/2014-12-15.01/article.m4 create mode 100644 posts/2015-01-01.01/article.m4 create mode 100644 posts/2015-06-05.02/article.m4 create mode 100644 posts/2015-06-05.03/article.m4 create mode 100644 posts/2015-06-05.04/article.m4 create mode 100644 posts/2015-06-17.01/article.m4 create mode 100644 posts/2015-09-14.01/article.m4 create mode 100644 posts/2015-10-12.01/article.m4 create mode 100644 posts/2016-03-03.01/article.m4 create mode 100644 posts/2016-09-09.01/article.m4 create mode 100644 posts/2016-09-12.01/article.m4 create mode 100644 posts/2016-10-14.01/article.m4 create mode 100644 posts/2016-11-02.01/article.m4 create mode 100644 posts/2016-12-19.01/article.m4 create mode 100644 posts/2017-07-04.01/article.m4 create mode 100644 posts/2018-01-26.01/article.m4 create mode 100644 posts/2018-02-22.01/article.m4 create mode 100644 posts/2018-04-30.01/article.m4 diff --git a/posts/2014-04-07.01/article.m4 b/posts/2014-04-07.01/article.m4 new file mode 100644 index 0000000..8990384 --- /dev/null +++ b/posts/2014-04-07.01/article.m4 @@ -0,0 +1,12 @@ +define(`TITLE', `Another (better) MBus-Master, Part 6') +define(`DATE', `2014-04-07') +define(`CONTENT', ` +Finally, over current protection has been added. + +main + +The over current protection is separated in a sensing part and a shutdown part. The comparator IC6B gets a threshold voltage using the potentiometer R18 and a signal voltage through the low pass filter of R19 and C3 (the values are not yet fine tuned). R21 and D7 are used to limit the output signal of the comparator, which is 35V power to a logic level. Through an inverter it goes to an interrupt line of the controller. An interrupt service routine in the controller firmware controls the transistor Q3 and pull down the positive input of the OP IC3A, which is part of the current source to shutdown the bus current. + +A receiver shutdown transistor (Q4) has been added. During tests with a dedicated poewr supply serious cross talk from the sender path in to the receiver path for a bus current of about 150mA has been discovered. This has not been investigated enough, just the receiver path has been shutdown during a send cycle. This is certainly only the second best solution, but it was a quick one. + +') \ No newline at end of file diff --git a/posts/2014-05-11.01/article.m4 b/posts/2014-05-11.01/article.m4 new file mode 100644 index 0000000..fb827b2 --- /dev/null +++ b/posts/2014-05-11.01/article.m4 @@ -0,0 +1,25 @@ +define(`TITLE', `Network-attached MBus-Master') +define(`DATE', `2014-05-11') +define(`CONTENT', ` +This is hopefully the final post concerning MBus-masters ... I replaced the Arduino Micro Pro in the "A better MBus-Master" design by a Teensy 3.1 and attached a WIZnet Ethernet module. Also, I put in a dedicated power supply providing 3.3V, 32V and 34V, based on two MC34063. + +3.3V for the Teensy and the WIZnet. + +34V to power the devices of the analog part of the design. + +32V for the bus. This give all the devices in the analog port 2V headroom. + +The "receive path disable MOSFET" has been removed again. + +Bringing the Teensy to reliable work was a bit hard, but this follows in another post. + +And this it is and it works: + +IMG_2028 + +IMG_2030 + +And finally it is in "production": + +Foto +') \ No newline at end of file diff --git a/posts/2014-05-11.02/article.m4 b/posts/2014-05-11.02/article.m4 new file mode 100644 index 0000000..e9ecd4d --- /dev/null +++ b/posts/2014-05-11.02/article.m4 @@ -0,0 +1,8 @@ +define(`TITLE', `4 Channel MBus-connected Thermometer, Part 3') +define(`DATE', `2014-05-11') +define(`CONTENT', ` +This thermometer is working too now and is attached to my local MBus: + +IMG_2032 + +') \ No newline at end of file diff --git a/posts/2014-08-04.01/article.m4 b/posts/2014-08-04.01/article.m4 new file mode 100644 index 0000000..e501ee8 --- /dev/null +++ b/posts/2014-08-04.01/article.m4 @@ -0,0 +1,31 @@ +define(`TITLE', `Review of network-attached M-Bus master, design version 6') +define(`DATE', `2014-08-04') +define(`CONTENT', ` +I got some questions about the design version 6, which I describe here and the modifications I applied to turn it into a network-attached M-Bus master, which I describe here. + +Here is a kind of a personal review of that design, especially considering the modifications: + + + + + +While in the design version 6 I had a very clear voltage shift at pin 1 of IC1A of about 100mV for the received logic levels at the bus, this signal was very noisy in the later design. Before replacing the diodes D1-4 by a separate supply voltage, it was nearly unusable and I was not able to communicate reliably using the device. + +After introducing the separate 32V supply voltage the signal got better, but still not as good as in design version 6. However, I was able to communicate again. + +Nevertheless I ran into another problem: the SNR was going worse for very small and very large bus currents. So, I added a 10k resistor parallel to R7 to drain a minimum bus current of about 3mA and I had to limit the maximum bus current to about 70mA. This is equal to about 70 slave devies. In my setup this is no problem, I use the device just for fun in the basement, in other setups, where actually 250 slaves should be handled, it is a problem. + +I think the major cause for the noise is the stronger, higher clocked, MCU and the WIZnet, which impact noise into the ground lines. (Unfortunately, the Arduino flash is not large enough for my firmware after adding the ethernet code.) And moreover my "breadboard production approach" is certainly a problem. A well layouted PCB with large supply planes leads hopefully to much better results and some more bypass capacitors and ferrit beads would help too. + +May be increasing the value of R6 should also be considered. It would increase the voltage shift at the output of a difference amplifier after the S/H device (pin 1 of IC1A). + +All in all, considering both the design version 6 and the network-attached M-Bus master, it becomes clear that it is possible to build a network-attached M-Bus master using the concept. + + + +') \ No newline at end of file diff --git a/posts/2014-10-04.01/article.m4 b/posts/2014-10-04.01/article.m4 new file mode 100644 index 0000000..783a771 --- /dev/null +++ b/posts/2014-10-04.01/article.m4 @@ -0,0 +1,21 @@ +define(`TITLE', `2-Wire Lightsensor with MBus-interface (bus-powered)') +define(`DATE', `2014-10-04') +define(`CONTENT', ` +Today it was a 2-wire (so: bus-powered) light sensor with MBus-interface: + +IMG_2378 + +Here, it is already mounted on the sill outside the window with a piece of double-faced adhesive tape, enclosed in the package of an Apple Magic Mouse. + +Note the piece in the lower-right corner (not the 10k resistor, next to it). This is finally the light sensor. It is regular BC177 transistor in metal case with the cap removed with a small Proxxon cutting-off machine. Works perfectly. + +The whole thing consumes about 500uA (the jumper in the top-middle is in the power line between the bus-transceiver and the rest of the circuit, just to measure the current). + +scan_006048 + +BTW, this is my first MSP430 project. It is much more "low-power" than the AVRs on the Arduino boards ... + +One note on the inhouse-wiring: The MBus-master is in the basement, the light sensor on the second floor. How to bring the MBus upstairs? Just use the already available infrastructure (Ethernet) cabling, which goes up from the patch panel in the basement into every room. + +Here are the sources: MBusLightSensor-2014-10-05 +') \ No newline at end of file diff --git a/posts/2014-10-27.01/article.m4 b/posts/2014-10-27.01/article.m4 new file mode 100644 index 0000000..b55aff3 --- /dev/null +++ b/posts/2014-10-27.01/article.m4 @@ -0,0 +1,17 @@ +define(`TITLE', `A simple Modbus RTU to TCP gateway') +define(`DATE', `2014-10-27') +define(`CONTENT', ` +Meterbus is a great Thing, especially as two-wire slave (bus-powered) are possible. Unfortunately the Meterbus Transceiver TSS721A is rather expensive and not that easy to obtain. + +So, I decided to test another fieldbus: Modbus. A Modbus Transceiver (here: LTC485) is much cheaper than the TSS721A and moreover a 1-phase powermeter with Modbus interface is about 30€, a 1-phase powermeter with Meterbus interface is about 90€. + +I found some Arduino libraries for both Modbus RTU and Modbus TCP on the Net so I started with a simple Modbus master talking to a 1-phase powermeter: + +IMG_1045 + +Communication works: + +SCR04 + +(Blue and yellow are the Modbus lines, purple is TX at the microcontroller, green is RT at the microcontroller.) +') \ No newline at end of file diff --git a/posts/2014-12-11.01/article.m4 b/posts/2014-12-11.01/article.m4 new file mode 100644 index 0000000..0920dab --- /dev/null +++ b/posts/2014-12-11.01/article.m4 @@ -0,0 +1,27 @@ +define(`TITLE', `Measuring the speed of a salad spinner') +define(`DATE', `2014-12-11') +define(`CONTENT', ` +Have you ever been wondering with how many revolutions your salad spinner is running? Here is the measuring: + +First, attach a hall-probe to the salad spinner bowl, the exact position needs to be found by experiment: + +IMG_2597 + +Second, attach two small but strong magnets at the spinner, one on each side, to let it run weighty: + +IMG_2600 + +Third, connect the probe to a power-supply, an oscilloscope and a frequency-counter: + +IMG_2599 + +The oscilloscope is required to find the right position of the probe, the counter is required for the actually measurement. + +And then: turn the spinner: + +[youtube https://www.youtube.com/watch?v=ezTvoh3vguI&w=560&h=315] + +Here, in this clip, we measured about 43Hz. In another test it was even 52Hz. + +Since we have two magnets, we had 21.5 rps and in the other test 26 rps. These are 1290 rpm and 1560 rpm. That's fast. +') \ No newline at end of file diff --git a/posts/2014-12-15.01/article.m4 b/posts/2014-12-15.01/article.m4 new file mode 100644 index 0000000..140bb46 --- /dev/null +++ b/posts/2014-12-15.01/article.m4 @@ -0,0 +1,38 @@ +define(`TITLE', `Very simple metal detector') +define(`DATE', `2014-12-15') +define(`CONTENT', ` +The design of the Theremin let my to the idea, that varying the value of the inductivity by approaching some metal should also detune one of two oscillators. + +20141215123534365a + +Here, I reuse the design of the oscillator and the mixer from my Theremin and fed the mixed signal into a low pass filter, calculated with Analog's excellent Filter Wizard. + +IMG_2629 + +This was btw the first deployment of my new Rigol power supply: + +IMG_2615 + +The both identical 30V-Max-channels, put in tracking mode, can easily be used as a symmetric power supply, as it is required for the mixer and the filter. + +The reference frequency is provided using a generator: + +IMG_2616 + + + +This is the full measurement setup: + +IMG_2630 + +I use one oscilloscope to show the varying (yellow) and the reference (blue) frequency, here in the tuned state: + +tuned + +When detuning the test oscillator by approaching some metal to the coil, there varying frequency starts wandering on the screen: + +detuned +And here are the results: + +[gallery columns="2" ids="546,545,544,525,543,528,541,540,535,534"]]]> +') \ No newline at end of file diff --git a/posts/2015-01-01.01/article.m4 b/posts/2015-01-01.01/article.m4 new file mode 100644 index 0000000..74363ee --- /dev/null +++ b/posts/2015-01-01.01/article.m4 @@ -0,0 +1,34 @@ +define(`TITLE', `A small Royer Induction Heater') +define(`DATE', `2015-01-01') +define(`CONTENT', ` +It actually works! + +IMG_2723 + +Of course it works, several people built it before and published photos and videos. But now I did it on my own: a rather small induction heater. + +IMG_2719 + +scan_006379 + +In the first photo I ran it with only 12V and at about 2.5A, but this was enough to bring a small nail to red heat. + +Some waveforms: + + +At the gate of one of the MOSFETs: + +gate + + +At the drain of one of the MOSFETs: + +drain + + +And finally across the capacitor: + +capacitor + + +') \ No newline at end of file diff --git a/posts/2015-06-05.02/article.m4 b/posts/2015-06-05.02/article.m4 new file mode 100644 index 0000000..3a7da12 --- /dev/null +++ b/posts/2015-06-05.02/article.m4 @@ -0,0 +1,86 @@ +define(`TITLE', `WiFi Powermeter') +define(`DATE', `2015-06-05') +define(`CONTENT', ` +The WiFi Powermeter is a electric power meter which sends its measurement results every minute using the MQTT protocol via WiFi to an MQTT broker in the local network. + +Here is the open test setup: + +IMG_3151 + + + + + +This is how it looks in its box: + +IMG_3217 + +I used it to measure the power consumption of the fridge in the basement or of the PC of my son and so on. + +On the MQTT broker there is one subscriber who transfers all the measurements into a MongoDB, where it now waits for further analysis. + +The hardware is rather simple: an Arduino Mega (since it has more than one serial interface), an Arduino WiFi shield and on top a RS485 adaptor for the Modbus communication. + +For the software I'm using the Modbus library from https://code.google.com/p/simple-modbus/. Unfortunately, it disappeared right there, don't know why. Seems, that I have to be really careful with the files. The MQTT library I'm using is from http://knolleary.net/arduino-client-for-mqtt/. + +With the couple of the WiFi library and the MQTT library I ran into two problems. + +First, it was not possible to establish an MQTT connection at all. Strange, since via Ethernet it wasn't a problem at all. I found, supported by Google that that the WiFi library and the shield handles a stop() on a fresh client socket somewhat strange: The socket can not be open or will be closed immediately after opening. Since at least in my case I always give a fresh WiFiClient into the PubSubClient I removed the connected() call from the top of the connect method in PubSubClient.cpp. + +Second, I ran into the problem that messages I published via PubSubClient using an Arduino Mega and an Arduino Wifi Shield which are large than about 80 octets did not appear at the broker. I found by googling that it is not possible to send more than 90 octets using the Wifi Shield and the related library at once: http://mssystems.emscom.net/helpdesk/knowledgebase.php?article=51 +And that exactly what I experienced too: I increased the buffer size one by one and at 90 octets it stopped and the WiFi library lost the connection. + +Using this patch, which I applied against release 1.9.1 everything works fine now: + +[code language="cpp"] +42,43c42 +< // if (!connected()) { +< if (true) { +--- +> if (!connected()) { +217d215 +< // Serial.print("pub len: "); Serial.println(length-5); +219,220d216 +< } else { +< // Serial.println("connection lost"); +290,308c286 +< +< // Serial.print("write len: "); Serial.println(length+1+llen); +< // size_t olen = length + 1 + llen; +< // rc = _client->write(buf+(4-llen),length+1+llen); +< +< const size_t SEND_AT_ONCE = 64; +< size_t remains = length + 1 + llen; +< // Serial.print("write len: "); Serial.println(remains); +< const uint8_t *writebuf = buf + (4 - llen); +< bool result = true; +< while ((remains > 0) && result) { +< size_t actuallySendChars = (remains > SEND_AT_ONCE) ? SEND_AT_ONCE : remains; +< // Serial.print("tbs: "); Serial.println(actuallySendChars); +< size_t sentChars = _client->write(writebuf, actuallySendChars); +< result = sentChars == actuallySendChars; +< remains -= sentChars; +< writebuf += sentChars; +< } +< +--- +> rc = _client->write(buf+(4-llen),length+1+llen); +311,312c289 +< // return (rc == 1+llen+length); +< return result; +--- +> return (rc == 1+llen+length); +357,362c334 +< //Serial.print("rc: "); Serial.println(rc); +< if (!rc) { +< //Serial.println("would stop"); +< _client->stop(); +< // while (true); +< } +--- +> if (!rc) _client->stop(); +[/code] + + +Sources for the firmware are here: WiModbusGateway +') \ No newline at end of file diff --git a/posts/2015-06-05.03/article.m4 b/posts/2015-06-05.03/article.m4 new file mode 100644 index 0000000..85c3b16 --- /dev/null +++ b/posts/2015-06-05.03/article.m4 @@ -0,0 +1,174 @@ +define(`TITLE', `Revisiting the MeterBus master') +define(`DATE', `2015-06-05') +define(`CONTENT', ` +I was already talking about the network attached MeterBus master here and here early. By now, a cronjob on my MacMini in the basement was querying the device every minute to collect measurement results from the attached meters (water, gas, electricity, out door temperature). + +Now, I changed and improved the software slightly: + +First, I enabled the watchdog timer, since it occasionally happened that the device became unresponsive. In this case now the watchdog timer is triggered to reset the beast. + +Second, I modified the upstream communication. Now the master publishes the results of configured meters using the MQTT protocol. + +IMG_3167 + + + + + +Doing this, I ran into another problem with the MQTT library from http://knolleary.net/arduino-client-for-mqtt/. I had to increase the maximum packet size to 1024 bytes since some of the meters sent about 250 bytes, which I represent in a hex string in the MQTT payload. But as soon as the packet was higher the 256 bytes, only the first 256 bytes had been sent to the broker. Tracking this down to the write method in the MQTT library I found a uint8_t which a uint16_t should be: + +[code language="cpp"] +boolean PubSubClient::write(uint8_t header, uint8_t* buf, + uint16_t length) { + uint8_t lenBuf[4]; + uint8_t llen = 0; + uint8_t digit; + uint8_t pos = 0; + uint8_t rc; + uint8_t len = length; +[/code] + +Using uint16_t for the variable len immediately fixed this problem. + +Aha, and enabling the watchdog on the Teensy was also not that easy. Finally, I came to this solution: + +[code language="cpp"] +extern "C" { + void startup_early_hook( ) __attribute__ ((weak)); + void startup_early_hook() { + // enable watchdog + WDOG_UNLOCK = WDOG_UNLOCK_SEQ1; + WDOG_UNLOCK = WDOG_UNLOCK_SEQ2; + + // one minute + WDOG_TOVALL = 0xea60; + WDOG_TOVALH = 0; + WDOG_PRESC = 0; + + WDOG_STCTRLH = WDOG_STCTRLH_WDOGEN | + WDOG_STCTRLH_ALLOWUPDATE | + WDOG_STCTRLH_STOPEN | + WDOG_STCTRLH_WAITEN; + } +} // extern "C" +[/code] + +It this require the enable the watchdog in the startup_early_hook since the Cortex accepts it only the first 256 clock cycles. +Next, the extern "C" is important, otherwise the link does not find the overwrite of the function, since it is compile with a C++ file. + +The clock for the watchdog is taken from the LPO of the CPU, which is running at 1kHz. Thus, a timeout value of 0xea60 equals 60000 let to a watchdog timeout of one minute. Reading the K20 reference manual, which I found on the Teensy homepage, was really helpful here. + + + +Today, all the measurements from a couple of meters find their way via MQTT and a small serverside script into the MongoDB for further analysis. + +[code language="javascript"] +{ + "data":{ + "telegram":"68 38 38 68 08 51 72 10 01 00 13 2E 19 24 02 + 9C 00 00 00 8C 10 04 12 06 00 00 8C 11 04 12 + 06 00 00 02 FD C9 FF 01 E2 00 02 FD DB FF 01 + 00 00 02 AC FF 01 00 00 82 40 AC FF 01 00 00 + 09 16 ", + "uptime":52815 + }, + "metadata":{ + "device":"MeterbusHub", + "token":5, + "name":"dryer" + } +} +{ + "data":{ + "telegram":"68 61 61 68 08 21 72 00 00 00 00 00 00 01 00 + AA 00 00 00 01 24 03 01 25 0A 01 26 10 02 27 + 01 00 05 67 28 14 B3 3D 05 67 D2 45 E3 BA 05 + 67 9F B5 A7 41 05 67 79 13 E0 41 0F D0 27 00 + 00 AA 08 00 00 05 00 00 00 00 00 00 00 01 00 + 00 00 A2 C3 7F 3F A5 BA 7F 3F 85 A7 7F 3F E7 + F9 7F 3F CD CC CC 3D E8 03 00 00 8B 16 ", + "uptime":52822 + }, + "metadata":{ + "device":"MeterbusHub", + "token":1, + "name":"thermom." + } +} +{ + "data":{ + "telegram":"68 38 38 68 08 52 72 99 51 00 13 2E 19 21 02 + 86 00 00 00 8C 10 04 06 09 00 00 8C 11 04 00 + 09 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 + B3 16 ", + "uptime":52840 + }, + "metadata":{ + "device":"MeterbusHub", + "token":6, + "name":"laundry" + } +} +{ + "data":{ + "telegram":"68 92 92 68 08 50 72 81 14 01 11 2E 19 16 02 + A2 00 00 00 8C 10 04 82 62 85 00 8C 11 04 82 + 62 85 00 8C 20 04 00 00 00 00 8C 21 04 00 00 + 00 00 02 FD C9 FF 01 DD 00 02 FD DB FF 01 08 + 00 02 AC FF 01 0F 00 82 40 AC FF 01 00 00 02 + FD C9 FF 02 E2 00 02 FD DB FF 02 0C 00 02 AC + FF 02 16 00 82 40 AC FF 02 F7 FF 02 FD C9 FF + 03 E9 00 02 FD DB FF 03 09 00 02 AC FF 03 11 + 00 82 40 AC FF 03 FA FF 02 FF 68 00 00 02 AC + FF 00 36 00 82 40 AC FF 00 F1 FF 01 FF 13 00 + D2 16 ", + "uptime":52867 + }, + "metadata":{ + "device":"MeterbusHub", + "token":4, + "name":"electricity" + } +} +{ + "data":{ + "telegram":"68 46 46 68 08 30 72 45 71 43 00 24 23 25 07 + 4B 00 00 00 0C 13 83 50 43 00 8C 10 13 00 00 + 00 00 0B 3B 00 00 00 0B 26 69 58 01 02 5A B6 + 00 04 6D 30 0D E5 16 4C 13 96 41 33 00 CC 10 + 13 00 00 00 00 42 6C DF 1C 42 EC 7E FF 1C 01 + 16 ", + "uptime":53047 + }, + "metadata":{ + "device":"MeterbusHub", + "token":2, + "name":"water" + } +} +{ + "data":{ + "telegram":"68 56 56 68 08 40 72 43 60 52 00 77 04 14 03 + 56 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 23 0D E5 16 02 7C 09 65 6D 69 74 + 20 2E 74 61 62 A9 09 04 13 F5 A9 04 00 04 93 + 7F 4E 01 00 00 44 13 FC A5 04 00 0F 01 00 1F + 1C 16 ", + "uptime":53049 + }, + "metadata":{ + "device":"MeterbusHub", + "token":3, + "name":"gas" + } +} + +[/code] + +IMG_3168 + + +Sources for the firmware are here: NetMeterBusMaster2 +') \ No newline at end of file diff --git a/posts/2015-06-05.04/article.m4 b/posts/2015-06-05.04/article.m4 new file mode 100644 index 0000000..812690d --- /dev/null +++ b/posts/2015-06-05.04/article.m4 @@ -0,0 +1,92 @@ +define(`TITLE', `RelayBox') +define(`DATE', `2015-06-05') +define(`CONTENT', ` +Have you ever been in the situation that you are on the way to the office, or even more worse into holiday, and you can not remember certainly enough whether you switched off the oven or the tumble dryer. I was so often in that situation that I decided to make this: + +IMG_3234 + +IMG_3235 + + + + + + +On the first photo you see three high current switches (and on the right hand side two MBus power meters), on the second photo is the control stuff. From the bottom to the top there is a Meanwell power supply with 24V DC output for the switches and next to it a 5V SMPS for the Arduino. In the middle left is a card with eight opto coupler and an insulating DC-DC-converter for the feedback from the switches and on the right hand side an Arduino Mega with an Ethernet Shield. On top left just a row of screw terminal and on the left hand side a 8 port relay board from SainSmart. It is so cheap on ebay (only about 11€) you can not make it on you own - the parts would be more expensive. + +For eight ports 16 input and 16 output pins on the Arduino are required: eight in and eight out for feedback and relays, eight in and eight out for push buttons and signaling LEDs (they are not on the photo). + +This thing is controlled using the MQTT protocol too. A small Python coded webapp also siting on the broker serves as UI: + +IMG_3258 + +("Waschküche" is laundry room, "Küche" is kitchen and "Herd" is oven.) + +The thing publishes every second the status of the switches via MQTT: + +[code language="javascript"] +{ + "data":{ + "uptime":75407, + "switchStates":[ + { + "stateConflict":0, + "index":0, + "state":1, + "feedbackState":1 + }, + { + "stateConflict":0, + "index":1, + "state":1, + "feedbackState":1 + }, + { + "stateConflict":0, + "index":2, + "state":1, + "feedbackState":1 + }, + { + "stateConflict":0, + "index":3, + "state":0, + "feedbackState":0 + }, + { + "stateConflict":0, + "index":4, + "state":0, + "feedbackState":0 + }, + { + "stateConflict":0, + "index":5, + "state":0, + "feedbackState":0 + }, + { + "stateConflict":0, + "index":6, + "state":0, + "feedbackState":0 + }, + { + "stateConflict":0, + "index":7, + "state":0, + "feedbackState":0 + } + ] + }, + "metadata":{ + "device":"RelayBox" + } +} +[/code] + + +The sources for the webapp are here: RelayBoxWebApp + +The sources for the firmware are here: RelayBox +') \ No newline at end of file diff --git a/posts/2015-06-17.01/article.m4 b/posts/2015-06-17.01/article.m4 new file mode 100644 index 0000000..63d3e23 --- /dev/null +++ b/posts/2015-06-17.01/article.m4 @@ -0,0 +1,47 @@ +define(`TITLE', `A fully digital controlled SMPS for up to 100V, 1.5KW') +define(`DATE', `2015-06-17') +define(`CONTENT', ` +The whole beast: + +IMG_2941 + + + + + + +Transformer, 230V to 90V, 1.5KW, 11kg, 12€ on ebay + +IMG_2944 + + +Transformer Start Relay + +IMG_2945 + + +Rectifier + +IMG_2946 + + +Filter capacitors (4x5100uF), with discharging resistor (10kOhm) + +IMG_2948 + + +Power stage with switch, diode (in one package, 300A, 1000V), inductor (about 140uH) and capacitors (2x2000uF) + +IMG_2943 + + +Controller, with MSP430, opto-coupler and push-pull-stage for driving the power switch with PWM signal from MCU, voltage-divider and op-amp for feedback voltage, display and some pushbuttons + +IMG_2942 + + +Load for testing + +IMG_2947 + +') \ No newline at end of file diff --git a/posts/2015-09-14.01/article.m4 b/posts/2015-09-14.01/article.m4 new file mode 100644 index 0000000..1508e31 --- /dev/null +++ b/posts/2015-09-14.01/article.m4 @@ -0,0 +1,178 @@ +define(`TITLE', `First ESP8266 Project: WiFi Power Switch') +define(`DATE', `2015-09-14') +define(`CONTENT', ` +IMG_3826 + +The ESP8266 boards (ESP-01, cmp. http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family#esp-01 +) were lying on my desk for already some time. I never took the time to install the SDK on my Mac. Now I came around nodemcu ( http://www.nodemcu.com/index_en.html) and it is exactly what I was looking for since a while: Lua on a microcontroller. The idea behind is simple: When you are coding on a PC (or a Mac) and you have a rather small problem, a rather simple task to perform, you (or at least: I) would tend to use a scripting language. On a PC my first choice would be Python, other people prefer Perl or even Lua. On a microcontroller I also was faced those simple tasks (like toggling one bit based on an MQTT message) but never had a scripting language available. But here it is. + + + + + +No need to install an SDK on the PC, just an upload tool like nodemcu-uploader.py or esplorer is required. First the nodemcu firmware needs to be installed on the ESP8266 board. I found a very good intro here. + +My Lua script for this project running on the microcontroller consists of two files: ''init.lua'' and ''mqtt.lua''. +Coding Lua for nodemcu isn't really intuitive, at least not when you come from Arduinos or so. You can't simple write a ''setup'' method and do all the initialization within and a ''loop'' method to be continuously run. Doing it this way would either drive you into the chip's watchdog and would consume any available CPU any and leave nothing for the WiFi and TCP/IP stack. You've to work with timers and callback functions. I found two really good articles here and here. + +What I also had to learn the hard way is that the nodemcu does not leave much space for the script. With a handful of debug messages containing some meaningful text (as it is no problem on the Arduino or Teensy) I over and over run into out of memory messages or even worse in misbehaving code. + +I finally came to this code: + +
+-- init.lua
+-- Constants
+SSID    = "MessWLAN"
+APPWD   = "geheim"
+CMDFILE = "mqtt.lua" 
+
+-- Some control variables
+wifiTrys     = 0    
+NUMWIFITRYS  = 200 
+
+function launch()
+  print("A")
+  print("B: " .. wifi.sta.getip())
+  tmr.alarm(0, 5000, 0, function() dofile(CMDFILE) end )
+end
+
+function checkWIFI() 
+  if ( wifiTrys > NUMWIFITRYS ) then
+    print("C")
+  else
+    ipAddr = wifi.sta.getip()
+    if ( ( ipAddr ~= nil ) and  ( ipAddr ~= "0.0.0.0" ) )then
+      tmr.alarm( 1 , 500 , 0 , launch )
+    else
+      tmr.alarm( 0 , 2500 , 0 , checkWIFI)
+      print("D: " .. wifiTrys)
+      wifiTrys = wifiTrys + 1
+    end 
+  end 
+end
+
+print("E")
+
+ipAddr = wifi.sta.getip()
+if ( ( ipAddr == nil ) or  ( ipAddr == "0.0.0.0" ) ) then
+  print("F")
+  wifi.setmode( wifi.STATION )
+  wifi.sta.config( SSID , APPWD)
+  print("G")
+  tmr.alarm( 0 , 2500 , 0 , checkWIFI )
+else
+ launch()
+end
+
+ + +
+BROKER = "172.16.2.15" 
+BRPORT = 1883
+BRUSER = ""
+BRPWD  = "" 
+CLIENTID = "ESP8266-" ..  node.chipid()
+
+SWITCH_PIN = 3
+
+SWITCH_ID = 1
+
+topics = {"IoT/Watchdog", "IoT/Switch" .. SWITCH_ID} 
+TOPIC_WATCHDOG = 1
+TOPIC_SWITCH = 2
+
+pub_sem = 0 
+current_topic  = 1
+topicsub_delay = 50 
+id2 = 0
+
+switch_state = false
+old_switch_state = true
+
+
+function alarm()
+  node.restart()
+end
+
+
+gpio.mode(SWITCH_PIN, gpio.OUTPUT)
+gpio.write(SWITCH_PIN, gpio.LOW)
+
+m = mqtt.Client( CLIENTID, 120, BRUSER, BRPWD)
+tmr.alarm(4, 60000, 1, alarm)
+m:connect( BROKER , BRPORT, 0, function(conn)
+     print("4")
+     mqtt_sub()
+end)
+
+
+
+
+function mqtt_sub()
+     if table.getn(topics) < current_topic then
+          run_main_prog()
+     else
+          m:subscribe(topics[current_topic] , 0, function(conn)
+          end)
+          current_topic = current_topic + 1 
+          tmr.alarm(5, topicsub_delay, 0, mqtt_sub )
+     end
+end
+
+
+function publish_status()
+   if pub_sem == 0 then 
+     pub_sem = 1
+     local uptime = tmr.time()
+     local switch_state_pres = 0
+     if (switch_state) then
+        switch_state_pres = 1
+     end
+     local msg = "{"metadata":{"device":"WiFiSwitch" .. SWITCH_ID .. ""}, "data":{"uptime":" .. uptime .. ", "state":" .. switch_state_pres .. "}}"
+     m:publish("IoT/Status/WiFiSwitch" .. SWITCH_ID, msg ,0,0, function(conn) 
+        print("8: " .. id2 .. ", " .. switch_state_pres)
+        pub_sem = 0 
+        id2 = id2 +1 
+     end)
+   end  
+end
+
+
+
+function run_main_prog()
+     tmr.alarm(3, 1000, 1, publish_status )
+     m:on("message", function(conn, topic, data)
+        print(topic .. ":" )
+        if (data ~= nil ) then
+          print ( data )
+          if (topic == topics[TOPIC_SWITCH] and data == "switch on") then
+            switch_state = true
+          elseif (topic == topics[TOPIC_SWITCH] and data == "switch off") then
+            switch_state = false
+          elseif (topic == topics[TOPIC_SWITCH] and data == "switch toggle") then
+            switch_state = not switch_state
+          elseif (topic == topics[TOPIC_WATCHDOG] and data == "WauWau!") then
+            print("9")
+            tmr.alarm(4, 60000, 1, alarm)
+          end
+
+          if (switch_state ~= old_switch_state) then
+            old_switch_state = switch_state
+            if (switch_state) then
+              gpio.write(SWITCH_PIN, gpio.LOW)
+            else
+              gpio.write(SWITCH_PIN, gpio.HIGH)
+            end
+          end
+        end
+      end )
+end
+
+ +IMG_3814 + +scan_006894 + + +Important note: this thing runs on mains power. Don't play with it unless you know exactly what you are doing! +') \ No newline at end of file diff --git a/posts/2015-10-12.01/article.m4 b/posts/2015-10-12.01/article.m4 new file mode 100644 index 0000000..9b93166 --- /dev/null +++ b/posts/2015-10-12.01/article.m4 @@ -0,0 +1,120 @@ +define(`TITLE', `Something completely different: SNMP to MySQL') +define(`DATE', `2015-10-12') +define(`CONTENT', ` +I'm helping to maintain a fleet of about 50 printers in a charity distributed over about 30 locations in the city. Now, we wanted to know how much the individual printers are used to get some numbers for an effort to reduce the number of printers. + +So, what is more self-evident then using SNMP to query the total page counter on a daily base - or better on an hourly base since the printers are switched off at this or that time and those we at least get sometimes valid data. + +Typical monitoring tools like Cacti or Zabbix are very good in drawing nice graphs but not so good in delivering absolute numbers. So I decided to write something on my own: query some devices for some SNMP data points and store the results in a database. Which devices and which data points and which combinations? Everything in the database too. + +I looked for a SNMP library, first in Python, then in Java - since I'm quite familiar with these languages - but everything I found was rather complicated to use. Then I looked for something in C# and found ''SnmpSharpNet''. Still not that easy to use but easier then what I found in Python or Java. + +For that reason I started coding the little tool in C#. And what I did is working very well now. Configuration and error-handling is not perfect yet, but it is working. Enough for today. + +BTW: I switched developing the code between Microsoft Visual Studio 2013 (on Windows) and Xamarin Studio (on the Mac) and run the tool on Linux. Works without any problem. Very nice! + + + + + +[code language="csharp"] +namespace Snmp2Mysql +{ + + + class Program + { + static void Main(string[] args) + { + string dbConnStr = "SERVER=localhost;" + + "DATABASE=statsdb;" + + "UID=statsuser;" + + "PASSWORD=xxx;"; + DatabaseLink dbLink = new DatabaseLink(dbConnStr); + using (DataCollector dc = dbLink.DataCollector) + using (DeviceProvider dp = dbLink.DeviceProvider) + { + foreach (DeviceTuple dt in dp) + { + // Console.WriteLine("dt: {0}, {1}", dt.DeviceAddress, dt.Community); + + using (SnmpGetter snmpGetter = new SnmpGetter(dt.Community, dt.DeviceAddress)) + { + using (DeviceDataItemProvider ddip = dt.DeviceDataItemProvider) + { + foreach (DeviceDataItemTuple ddit in dt.DeviceDataItemProvider) + { + // Console.WriteLine(" ddit: {0}, {1}", ddit.Id, ddit.Oid); + snmpGetter.AddOid(ddit.Id, ddit.Oid); + } + } + + try + { + SnmpGetterResultProvider res = snmpGetter.Exec(); + foreach (SnmpGetterResult r in res) + { + // Console.WriteLine("{0} {1} {2} {3}: {4}", dt.Description, r.Index, r.Oid, r.Type, r.Value); + dc.add((int)r.Index, r.Value); + } + } + catch (SnmpGetterException) { + // Console.WriteLine ("{0}, no result: {1}", dt.Description, e.Message); + } + } + } + } + } + } +} +[/code] + +[code language="SQL"] +CREATE TABLE `device_t` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `address` varchar(45) NOT NULL, + `community` varchar(45) NOT NULL, + `period` int(11) NOT NULL, + `description` varchar(123) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `address_UNIQUE` (`address`) + +CREATE TABLE `dataitem_t` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `oid` varchar(128) NOT NULL, + `description` varchar(45) NOT NULL, + `handling` varchar(45) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `oid_UNIQUE` (`oid`), + UNIQUE KEY `description_UNIQUE` (`description`) + +CREATE TABLE `devicedataitem_t` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `device` int(11) NOT NULL, + `dataitem` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `devicedataitem_uk_idx` (`device`,`dataitem`), + KEY `device_fk_idx` (`device`), + KEY `dataitem_fk_idx` (`dataitem`), + CONSTRAINT `dataitem_fk` FOREIGN KEY (`dataitem`) REFERENCES `dataitem_t` (`id`), + CONSTRAINT `device_fk` FOREIGN KEY (`device`) REFERENCES `device_t` (`id`) + +CREATE TABLE `collecteddata_t` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `devicedataitem` int(11) NOT NULL, + `value` varchar(512) NOT NULL, + `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `devicedataitem_fk_idx` (`devicedataitem`), + CONSTRAINT `devicedataitem_fk` FOREIGN KEY (`devicedataitem`) REFERENCES `devicedataitem_t` (`id`) +[/code] + +And here are the sources: + +Snmp2Mysql + +I'm using GPL code from MySQL (the MySQL Connector for .NET), find it here http://dev.mysql.com/downloads/connector/net/ and the SNMP library from http://www.snmpsharpnet.com. + +Ah, evaluation of the data? Haven't thought about, maybe using some tricky SQL statement or an Excel pivot table. I'll come back about that. + +') \ No newline at end of file diff --git a/posts/2016-03-03.01/article.m4 b/posts/2016-03-03.01/article.m4 new file mode 100644 index 0000000..483c17d --- /dev/null +++ b/posts/2016-03-03.01/article.m4 @@ -0,0 +1,19 @@ +define(`TITLE', `Why always SPI CS on pin 10?') +define(`DATE', `2016-03-03') +define(`CONTENT', ` +Thank you, Ladyada, for this great display. + +But why is TFT CS on pin 10? + +Bildschirmfoto 2016-03-03 um 21.48.49 + +Pin 10 is also the SPI CS for the Ethernet shield. Why not, for instance, pin 6: + +IMG_1211 + +Carefully lift pin 9 of the level shifter and connect it using a thin wire with pin 6 of the Arduino connector. + +And voila, both the display and the ethernet shield is working: + +IMG_1212 +') \ No newline at end of file diff --git a/posts/2016-09-09.01/article.m4 b/posts/2016-09-09.01/article.m4 new file mode 100644 index 0000000..a31e08e --- /dev/null +++ b/posts/2016-09-09.01/article.m4 @@ -0,0 +1,24 @@ +define(`TITLE', `Integrated Thermometer and Timer') +define(`DATE', `2016-09-09') +define(`CONTENT', ` +To brew green tea you need water of about 75°C. To know that your water has 100°C is easy, you can see that it is boiling. For 75°C you need a thermometer. Then you also need to take care about the brewing time. It should be usually two minutes. + +There are such integrated thermometer and timer things on Amazon, but that's not me cup of tea, I decided to build one on my own. It should run on batteries, it should be small and it should make no noise (for office use). + +In between I was reading "Patterns for Time-Triggered Embedded Systems" from Michael Pont. And afterwards I was curious to use such a simple cooperative scheduler instead of the super-loop, as it is common in the Arduino domain. + +I used a MSP430G2553 with 10bit-ADCs, a PT1000 sensor and two seven-segment LED displays. + +The code is available at https://bitbucket.org/wollud1969/teathermotimer. + +img_0813 + +img_0814 + +img_0815 + +Schematics: 201609091558_0001 + +Calculation for measurement: 201609091558_0002 + +') \ No newline at end of file diff --git a/posts/2016-09-12.01/article.m4 b/posts/2016-09-12.01/article.m4 new file mode 100644 index 0000000..a13dc05 --- /dev/null +++ b/posts/2016-09-12.01/article.m4 @@ -0,0 +1,18 @@ +define(`TITLE', `Integrated Tea Timer and Thermometer, Second Edition') +define(`DATE', `2016-09-12') +define(`CONTENT', ` +I've two offices, so I'm requiring two of this timers. + +The second one I built with a three-digit display and auto-power-down and start with a push-button. + +This thing consumes in operation about 20mA, which is a real lot for so few functionality, but most of the power goes into the display. I was looking for a simple LCD on ebay, but it seems that most of the LCD 7-segment display have only one common and one pin for every single segment instead of one common per digit. These are too many pins for the 2553 and I didn't want to put an additional LCD controller on the board. + +However, when the thing goes in LPM4, it only consumes about 0,1uA, as it is written in the datasheet. Very important to achieve this low power consumption: an input pin requires more power than an output pin put into the right level. And of course the reference generator of the ADC needs to be shutdown. + +The sources are still available here https://bitbucket.org/wollud1969/teathermotimer, look for the branch SecondEdition. + +The schematic is more or less the same, just on digit display more and a reset push-button. + +img_0818 +img_0821 +') \ No newline at end of file diff --git a/posts/2016-10-14.01/article.m4 b/posts/2016-10-14.01/article.m4 new file mode 100644 index 0000000..add04de --- /dev/null +++ b/posts/2016-10-14.01/article.m4 @@ -0,0 +1,38 @@ +define(`TITLE', `Three Phase Inverter') +define(`DATE', `2016-10-14') +define(`CONTENT', ` +Already when I was still in school, about 30 years ago, I was curious to make an inverter using some MOSFETs. I actually was able to build a simple one phase inverter with rectangular signal shape (I used a NE555). Using this thing I drove a transformer to light a blub. However, all of these inverters I built passed by in fire. + +Now, I tried it again, not longer using MOSFETs but IGBTs with free-wheeling diode. Moreover, I used some microcontrollers and sine values to feed a PWM to get a sine-alike signal shape. And immediately with three phases to drive an asynchronous motor. + +img_0053 + +The signal shaping is done with four MSP430 controllers, three as PWMs to drive the bridge and one to coordinate and control the three PWMs. The PWM controller is decoupled from the IGBT driver (IR2184) using optic couplers. + +img_0054 + +The bridge is a three phase IGBT module is a 6MB120F-060 I got for a few euros at ebay. + +img_0055 + +To avoid high voltages in my setup I got a 24V async motor, also from ebay. + +img_0056 + +The PWMs generate the signal from a sine table generated using Excel. Those we got this signal: +inverter0_2016-09-23-4 + +The main task of the coordinator is the start the PWMs with a phase shift of 120° (digital line 1, 2 and 3): +2016-10-13_1 + +Currently the PWMs start with random polarity. The interesting signals are the digital lines 4, 5 and 6. + +Sometimes the motor runs: +2016-10-13_works + +But sometimes not: +2016-10-13_works_not + +The firmware is available on bitbucket, for the PWM controllers here and for the coordinator here. + +') \ No newline at end of file diff --git a/posts/2016-11-02.01/article.m4 b/posts/2016-11-02.01/article.m4 new file mode 100644 index 0000000..857a87d --- /dev/null +++ b/posts/2016-11-02.01/article.m4 @@ -0,0 +1,47 @@ +define(`TITLE', `Something really useful: 433MHz Power-Outlet Control') +define(`DATE', `2016-11-02') +define(`CONTENT', ` +We've lots of more or less cheap 433MHz Switchable Power-Outlets in the house, mostly for decorative lights. You get them for under 15€ in the Building Center, three at once with a remote control. Usually, you can control four switches with one remote, period. (A promising exceptions are the device from Intertechno.) + +Now I was looking for a way to +
    +
  1. Control the switches from different manufacturers with one remote.
  2. +
  3. Control them from really remote - from the office or from holiday.
  4. +
  5. Control them programmatically.
  6. +
+There are a couple of Arduino libraries out there which are working with this cheap 433MHz senders and receivers from China: + +  + +sender_receiverI tried several ones, not all a really working, but finally I stopped at https://github.com/sui77/rc-switch, which works really good. + + + + +First challenge now is to get the codes out of the remotes. For this purposes the rc-switch library provides an example sketch for the Arduino. I extended it a bit and got this: + +img_0101 +  +img_0105 +  + +Software for this thing can be found here https://gitlab.com/wolutator/433Receiver.git. + +This is how it works: + +img_0111 +  + +The second challenge is to send the codes out into air and let the power switches receive them. This is done with this thing: + +img_0100 + +It is a Arduino Ethernet Board with a PoE-adapter and the 433MHz sender directly attached to it. I glued it under a table in the living room. + +This thing received messages via MQTT and send them out via 433MHz. + +The software is here https://gitlab.com/wolutator/Mqtt433Gateway.git + +BTW: do not expect beautiful software, this has been made on Halloween evening and night when waiting for the kids to come home. + +') \ No newline at end of file diff --git a/posts/2016-12-19.01/article.m4 b/posts/2016-12-19.01/article.m4 new file mode 100644 index 0000000..a5b0edd --- /dev/null +++ b/posts/2016-12-19.01/article.m4 @@ -0,0 +1,26 @@ +define(`TITLE', `Three Phase Inverter, Second Service') +define(`DATE', `2016-12-19') +define(`CONTENT', ` +I wrote in October about my first try to build a simple three phase inverter, see here https://a385e5.wordpress.com/2016/10/14/three-phase-inverter/. In the first try I used four MSP430 microcontroller, one for the PWM of each phase and one to coordinate the phase shift of the three phases. + +In this experiment I put everything on one STM32 microcontroller. Here I used the DMA feature to feed data into the PWM counter and I calculated the sine values at start-up time on the microcontroller. Additionally I put in the driver for a CAN interface, however, it is not yet supported in the firmware. + +img_0140 + +From top to bottom you see the CAN driver, the STM32 board, opto coupler to separate logic and power part and then from right to left in the bottom half the low-side/high-side MOSFET drivers and the MOSFETs. + +img_0144 + +The power supply consists of a traditional transformer and (top right) the rectifier and condensers for the power part, together with the 12V regulator for the drivers and (top left) the regulators for 3.3V and 5V for the logic part. + +img_0146 + +The motor is the same as in the earlier experiment - I don't have too much of them. And everything is put onto one board: + +  + +  +img_0143 + +(Some space reserved for a HMI unit to be connected via CAN ...) +') \ No newline at end of file diff --git a/posts/2017-07-04.01/article.m4 b/posts/2017-07-04.01/article.m4 new file mode 100644 index 0000000..f357197 --- /dev/null +++ b/posts/2017-07-04.01/article.m4 @@ -0,0 +1,11 @@ +define(`TITLE', `SPI Slave Select Multiplication for the Raspberry Pi') +define(`DATE', `2017-07-04') +define(`CONTENT', ` + + + +rpi_ss1 + +IMG_0594 + +') \ No newline at end of file diff --git a/posts/2018-01-26.01/article.m4 b/posts/2018-01-26.01/article.m4 new file mode 100644 index 0000000..46cde0e --- /dev/null +++ b/posts/2018-01-26.01/article.m4 @@ -0,0 +1,277 @@ +define(`TITLE', `Configuration-Webserver for ESP8266 Projects') +define(`DATE', `2018-01-26') +define(`CONTENT', ` +In my previous ESP8266 based weekend projects I always hardcoded configuration data. For the ESP8266 these are at least the WiFi credentials SSID and WPA key. Moving it into a different WiFi network requires re-flashing it. There must be a better way I thought and found that's a single line of code to run the ESP8266 as an accesspoint, which opens its own WiFi network. + +So, I first hardcoded a web page with a form to enter configuration data, a data structure to hold it and some code to store it into the EEPROM or load it there. +It appears that this was an error-prone process with a lot of redudancy in the code. + +For that reason I wrote an approach with a small template-based generator script: + + + + +[code language="python" title="configGen.py"] +#!/usr/bin/python + +from Cheetah.Template import Template + +configItems = [ + {"label":"_", "key":"magic", "type":"I", "default": ""}, + {"label":"Wifi SSID", "key":"wifiSsid", "type":"C", "length":32, "default":"test"}, + {"label":"Wifi Key", "key":"wifiKey", "type":"C", "length":64, "default":"geheim"}, + {"label":"MQTT Broker", "key":"mqttBroker", "type":"C", "length":64, "default":"broker.hottis.de"}, + {"label":"MQTT Username", "key":"mqttUser", "type":"C", "length":32, "default":"esp1"}, + {"label":"MQTT Password", "key":"mqttPass", "type":"C", "length":32, "default":"geheim"}, + {"label":"MQTT ClientId", "key":"mqttClientId", "type":"C", "length":32, "default":"changeThis"}, + {"label":"MQTT Topic", "key":"mqttTopic", "type":"C", "length":64, "default":"IoT/espThermometer2/location/measurement"}, + {"label":"MQTT Port", "key":"mqttPort", "type":"I", "default":8883}, + {"label":"Measure Period", "key":"measurePeriod", "type":"I", "default":300} +] + +h_file = Template(file="configuration_h.tmpl", searchList=[{"configItems":configItems}]) +open('configuration.h','w').write(str(h_file)) +c_file = Template(file="configuration_c.tmpl", searchList=[{"configItems":configItems}]) +open('configuration.cpp','w').write(str(c_file)) +[/code] + + +The two templates are: + +[code language="C" title="configuration_h.tmpl"] +typedef struct { +#for $configItem in $configItems +#if $configItem.type == 'C' + char ${configItem.key}[$configItem.length]; +#else if $configItem.type == 'I' + uint32_t $configItem.key; +#end if +#end for +} tConfigBlock; + +extern const uint32_t MAGIC; +extern tConfigBlock configBlock; + +void configServeIndex(); +void configServeGetConfiguration(); +void showConfiguration(); +[/code] + +And: + +[code language="C" title="configuration_c.tmpl"] +#raw +#include <Arduino.h> + +#include <ESP8266WiFi.h> +#include <ESP8266WebServer.h> +#include <EEPROM.h> + +#include "defines.h" +#include "configuration.h" +#end raw + + +tConfigBlock configBlock; +const uint32_t MAGIC = 0xC0DE0001; +extern ESP8266WebServer webServer; + +bool configSaved = false; + +void configServeIndex() { + bool configValid = (configBlock.magic == MAGIC); + + if (! configValid) { + #for $configItem in $configItems + #if $configItem.label != "_" + #if $configItem.type == "C" + strcpy(configBlock.$configItem.key, "$configItem.default"); + #else if $configItem.type == "I" + configBlock.$configItem.key = $configItem.default; + #end if + #end if + #end for + } + + String buffer = + "<!doctype html" + "<html lang=\"en\">" + " <head>" + " <title>ESP8266 Thermometer Configuration Page</title>" + " </head>" + " <body>" + " <h1>ESP8266 Configuration Page</h1>"; + + if (configSaved) { + configSaved = false; + buffer += "<h2>Configuration saved</h2>"; + } + + buffer += + " <form action=\"/config\" method=\"GET\">" + " <table>" + #for $configItem in $configItems + #if $configItem.label != "_" + " <tr>" + " <td>" + " <label for\"$configItem.key\">$configItem.label</label>" + " </td><td>" + " <input type=\"text\" name=\"$configItem.key\" id=\"$configItem.key\" "; + + #if $configItem.type == "C" + buffer += " size=\"$configItem.length\" "; + buffer += " value=\""; + buffer += configBlock.$configItem.key; + buffer += "\""; + #else if $configItem.type == "I" + buffer += " value=\""; + buffer += configBlock.$configItem.key; + buffer += "\""; + #end if + + buffer += + " />" + " </td>" + " </tr>" + #end if + #end for + " <tr>" + " <td colspan=\"2\">" + " <button type=\"submit\">Save</button>" + " </td>" + " </tr>" + " </table>" + " </form>" + " </body>" + "</html>"; + + webServer.send(200, "text/html", buffer); + + +#ifdef DEBUG + Serial.println("indexHtml request served"); +#endif +} + +void configServeGetConfiguration() { + String arg; + + #for $configItem in $configItems + #if $configItem.label != "_" + arg = webServer.arg("$configItem.key"); + #if $configItem.type == "C" + strcpy(configBlock.$configItem.key, arg.c_str()); + #else if $configItem.type == "I" + configBlock.$configItem.key = atoi(arg.c_str()); + #end if + #end if + #end for + + configBlock.magic = MAGIC; + + showConfiguration(); + + EEPROM.begin(512); + EEPROM.put(EEPROM_ADDR, configBlock); + EEPROM.commit(); + + Serial.println("EEPROM saved"); + + configSaved = true; + webServer.sendHeader("Location", String("/"), true); + webServer.send(302, "text/plain", ""); + //webServer.send(200, "text/html", "configuration saved"); +} + +void showConfiguration() { + Serial.println("Configuration is"); + + #for $configItem in $configItems + Serial.print("$configItem.key = "); + Serial.println(configBlock.$configItem.key); + + #end for + + Serial.println("---"); +} +[/code] + +Besides these generated files I also needed a framework to distinguish between "configuration mode" and "production mode" and in configuration mode to start the WiFi accesspoint. + +Since I'm programming the ESP8266 in an Eclipse-based Arduino-environment, this is all done in the setup() and loop() function: + +[code language="C"] +void setup() { + startTime = millis(); +#ifdef DEBUG + Serial.begin(115200); + Serial.println("Starting ..."); +#endif + + pinMode(CONFIG_SWITCH, INPUT_PULLUP); + pinMode(LED_PIN, OUTPUT); + + EEPROM.begin(512); + EEPROM.get(EEPROM_ADDR, configBlock); + + Serial.print("Magic: "); + Serial.println(configBlock.magic); + + configMode = ((LOW == digitalRead(CONFIG_SWITCH)) || (configBlock.magic != MAGIC)); + + if (configMode) { +#ifdef DEBUG + Serial.println("Configuration mode"); +#endif + digitalWrite(LED_PIN, LOW); + setupConfiguration(); + } else { +#ifdef DEBUG + Serial.println("Production mode"); + Serial.println(); + Serial.println(); + showConfiguration(); +#endif + + digitalWrite(LED_PIN, HIGH); + setupProduction(); + } + +#ifdef DEBUG + Serial.println("Started."); +#endif +} + +void loop() { + if (configMode) { + loopConfiguration(); + } else { + loopProduction(); + } +} + +void setupConfiguration() { + WiFi.mode(WIFI_AP); + WiFi.softAP(CONFIG_SSID); +#ifdef DEBUG + Serial.println("AP started"); +#endif + + webServer.on("/", configServeIndex); + webServer.on("/config", configServeGetConfiguration); + webServer.onNotFound(configServeNotFound); + webServer.begin(); +#ifdef DEBUG + Serial.println("Webserver started"); +#endif +} + +void loopConfiguration() { + webServer.handleClient(); +} +[/code] + + +This code can be also found embedded into two of my projects. Find them at https://gitlab.com/wolutator/EspThermometer2 and https://gitlab.com/wolutator/TouchSwitch. + +') \ No newline at end of file diff --git a/posts/2018-02-22.01/article.m4 b/posts/2018-02-22.01/article.m4 new file mode 100644 index 0000000..750514d --- /dev/null +++ b/posts/2018-02-22.01/article.m4 @@ -0,0 +1,29 @@ +define(`TITLE', `Home Automation Hub') +define(`DATE', `2018-02-22') +define(`CONTENT', ` +My home automation hub. (Photos inside) + +IMG_0304 + +IMG_0195 + +A Raspberry Pi runs Homegear and homekit2mqtt. It is equipped with two 866MHz radios for Homematic and MAX!. + +Additionally an MQTT broker and the homegrown control software (https://gitlab.com/wolutator/dispatcher_ng), which communicates with Homegear, homekit2mqtt, the MQTT433MHz bridge (see below) and other homegrown web clients via MQTT, resists on it. + +IMG_0307 + +There is also a DIY MQTT to 433MHz bridge to control Intertechno and other 433MHz plugs (https://gitlab.com/wolutator/Mqtt433Gateway). + +  + +  + +  + +  + +IMG_0308 + +An finally a Philips Hue Bridge powered using a PoE adaptor for Philips and IKEA Tradfri bulbs, paired to Homegear. +') \ No newline at end of file diff --git a/posts/2018-04-30.01/article.m4 b/posts/2018-04-30.01/article.m4 new file mode 100644 index 0000000..7470c83 --- /dev/null +++ b/posts/2018-04-30.01/article.m4 @@ -0,0 +1,143 @@ +define(`TITLE', `Yet Another Debouncing Method') +define(`DATE', `2018-04-30') +define(`CONTENT', ` +You can find several approaches for debouncing mechanical switches on the Internet, some work better, some not so good. + +One common approach is to ignore events in an ISR when they come too fast: + +[code language="C"] +void count() { + static uint32_t lastEvent = 0; + uint32_t currentEvent = micros(); + if (currentEvent > (lastEvent + configBlock.debounce)) { + lastEvent = currentEvent; + cnt++; + } +} + +void setup() { + pinMode(REED_PIN, INPUT_PULLUP); + attachInterrupt(REED_PIN, count, FALLING); +} +[/code] + +This works very good when only the tipping of a switch is relevant. + +When also the time the button was pressed is relevant and when it is especially necessary to distinguish between a short and a long press this approach doesn't work anymore. + +Since I couldn't remember the approaches I read about earlier I've sketched this state machine: + + + + + + +20180430110848869_0001.jpg + +(The double-lined states are action-states which send out the related information.) + +At least for me, this approach is working very reliable so far, I'm quite happy with it. + +[code language="C"] +enum tPressedState { psHIGH, psLOW, psACCEPTED_LOW, psLONG_START, psLONG_CONT, psLONG_CONT_SEND, psLONG_END, psSHORT, psINVALID }; + +typedef struct { + uint8_t index; + uint8_t buttonPin; + tPressedState pressedState; + tPressedState oldPressedState; + uint32_t lastStateChange; +} tButton; + +tButton buttons[] = { + { 1, SWITCH_1, psHIGH, psINVALID, 0 }, + { 2, SWITCH_2, psHIGH, psINVALID, 0 }, + { 3, SWITCH_3, psHIGH, psINVALID, 0 }, + { 0, 0, psINVALID, psINVALID, 0 } // END MARKER +}; + +static void buttonHandler(tButton *button) { + uint32_t currentMicros = micros(); + uint8_t buttonState = digitalRead(button->buttonPin); + +#ifdef DEBUG + if (button->oldPressedState != button->pressedState) { + Serial.print("Index "); + Serial.print(button->index); + Serial.print(", state changed from "); + Serial.print(button->oldPressedState); + Serial.print(" to "); + Serial.print(button->pressedState); + Serial.println(); + button->oldPressedState = button->pressedState; + } +#endif + + switch (button->pressedState) { + case psHIGH: + if (buttonState == LOW) { + button->pressedState = psLOW; + button->lastStateChange = currentMicros; + } + break; + case psLOW: + if (buttonState == HIGH) { + button->pressedState = psHIGH; + button->lastStateChange = currentMicros; + } else { + if (currentMicros > (button->lastStateChange + configBlock.debounce)) { + button->pressedState = psACCEPTED_LOW; + button->lastStateChange = currentMicros; + } + } + break; + case psACCEPTED_LOW: + if (buttonState == HIGH) { + button->pressedState = psSHORT; + button->lastStateChange = currentMicros; + } + if (currentMicros > (button->lastStateChange + (configBlock.longPress * 1000))) { + button->pressedState = psLONG_START; + button->lastStateChange = currentMicros; + } + break; + case psSHORT: + sendMsg(button->index, "PRESS_SHORT"); + button->pressedState = psHIGH; + button->lastStateChange = currentMicros; + break; + case psLONG_START: + sendMsg(button->index, "PRESS_LONG_START"); + button->pressedState = psLONG_CONT; + button->lastStateChange = currentMicros; + break; + case psLONG_CONT: + if (buttonState == HIGH) { + button->pressedState = psLONG_END; + button->lastStateChange = currentMicros; + } + if (currentMicros > (button->lastStateChange + (configBlock.longPressRepeat * 1000))) { + button->pressedState = psLONG_CONT_SEND; + button->lastStateChange = currentMicros; + } + break; + case psLONG_CONT_SEND: + sendMsg(button->index, "PRESS_LONG_CONT"); + button->pressedState = psLONG_CONT; + button->lastStateChange = currentMicros; + break; + case psLONG_END: + sendMsg(button->index, "PRESS_LONG_END"); + button->pressedState = psHIGH; + button->lastStateChange = currentMicros; + break; + default: + button->pressedState = psHIGH; + button->lastStateChange = currentMicros; + } +} +[/code] + +Find it embedded in the code of a small ESP8266-based switch thing I'm using in my home automation setup (home grown control code (https://gitlab.com/wolutator/dispatcher_ng), homegear (https://homegear.eu/) for device integration and openHAB (https://www.openhab.org/) as user interface) here: https://gitlab.com/wolutator/MySwitch. + +') \ No newline at end of file