Playing with cheap IoT devices

Introduction

It is now possible to buy small WiFi enabled computers for less than a cup of coffee. These devices have many uses thanks to their size and ability to run custom code. This page will contain details of projects I undertake. All work is done on Linux (Ubuntu 17.10). So far it focuses on BroadLink and Espressif ESP8266 based designs. The goal is to use machine learning to optimise things in my flat (e.g. predicting what time I'll be home and setting the heating appropriately) and other application (e.g. remote monitoring in farming).

Hysen HY02B05H (BroadLink) thermostat/heating controller

Update: my GitHub fork of python-broadlink now has complete support for this device.

This mains 16A thermostatic timer switch was the first device I bought. It is supposed to be an "end-user" product and is by far the most expensive I bought (£20 posted). However, it is functional out-of-the-box, presentable and seems well made. So overall I consider it worth the cash. It fits onto a standard single gang pattress box. It looked fairly deep so I bought a 50mm box, but with hindsight suspect a slimmer (say 40mm) box would fit and look neater.

Chinglish instructions with a link to both iOS and Android mobile applications are supplied. I used the Android app on an old phone to setup and test the device (old phone used because I noted that the app uses the phone's IMEI as an identifier, which I think presents an unnecessary vulnerability). The Broadlink API is an undocumented closed source binary, i.e. not very useful if you want to write your own code.

Before ordering the device I had already found python-broadlink by Matthew Garrett. The existing code can discover the device on the local network and connect to/initialise it. However, device specific features are not handled and so must be reverse engineered. I have made progress and can get the current temperature, switch to manual override mode etc. The code is in my GitHub fork. Rather than allow the device access to the internet through my firewall I wrapped the python code in a more secure web interface.

The device switches mains (220v AC) but my boiler control logic is 24v (?) DC. As a temporary bodge I put a small Omron MY2N relay in a fire retardant box next to the boiler.

These two frustrations prompted me to look at a home brew solution.

ESP8266 overview

The ESP8266 is a small ARM-based SoC (system on a chip) with WiFi capability. The cheapest practical option seems to be the ESP-01 module made by AI-Thinker, which has its own section below.

ESP8266 Development Board (NodeMCU)

My favourite incarnation is an ESP-12-based £2.40 development board. It has useful features like a built in USB-serial chip (QinHeng Electronics HL-340 for mine) for easy flashing and programming, plenty of IO pins and a user button. Basically it is better for quick experiments and the one I've found most fun!

NodeMCU firmware installation

The first job after receiving the board is to install some firmware. I chose NodeMCU, an open source project to provide a Lua script based interactive firmware [github , documentation]. Due to all the extra options available it is best to use the NodeMCU build service to create a firmware image containing just what you want, rather than filling up the limited (4MB in my case) flash. This will give you an up to date firmware image, e.g. nodemcu-1.5.4.1-final-7-modules-2017-11-21-20-30-42-float.bin.

To flash the image using Linux you can use the python-based Espressif esptool, available either directly from github or from pip package esptool (i.e. do pip install esptool). Then, assuming your USB cable is connected and the corresponding port is /dev/ttyUSB0 do:

python -m esptool -p /dev/ttyUSB0 write_flash 0x00000 nodemcu-image.bin

If all goes well then 30s-1min later (depending on the size of your firmware) your device will be ready.

I may also try the Arduino core firmware later. (NodeMCU happened to be the first I tried and I was happy with it but make no claim it is better than Arduino, which seems to have a bigger community at least).

Lua script on NodeMCU

After the firmware has installed and you reset the device open a terminal program (like gtkterm) at baud 115200. You can then play with the Lua examples. For example, connect to an existing WiFi network with:

wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","password")

Then you can start a simple webserver and test it with the browser on your PC:

AI-Thinker ESP-01 module

This small module has all the essentials needed for a low powered WiFi device- an ESP8266, PCB antennae and flash memory chip. It costs around £1.60 posted. It does not have USB to serial adapter on the board so an external one is needed for flashing and programming. I have used a Prolific Technology, Inc. PL2303. Note that the module needs 3.3v power not 5v. It seems 5v tolerant for logic (but I am not liable if you melt yours using 5v signals!).

I am experimenting with a ready made "WiFi relay" that uses the AI-Thinker module. It is made by "LC Tech" and shown below. The whole thing cost around £3 and handily takes 5v power (stepped down to 3.3v for the ESP8266) so you can power it directly from the USB connection.

It is loaded with AI-Thinker firmware, which supports AT commands detailed here. To connect it to an WiFi router, for example, you connect via serial (9600 baud for mine but apparently the default varies!) and issue:

AT+CWMODE=1
AT+RST
AT+CWJAP=<ssid>, <password>

Then start a TCP server on port 8080 and get the IP address:

AT+CIPMUX=1
AT+CIPSERVER=1,8080
AT+CIFSR

You can now connect to this IP address and issue commands. For example, the LC Tech relay board can be controlled by netcat'ing the appropriate hex codes from the Ubuntu shell:

# switch on
echo -e '\xA0\x01\x01\xA2'  | nc -q 1 192.168.0.37 8080
# switch off
echo -e '\xA0\x01\x00\xA1'  | nc -q 1 192.168.0.37 8080

(You can also get the hostname and use that)

NodeMCU installation

The problem with the AI-Thinker firmware is that, off the shelf, it doesn't restart the TCP server when the chip gets reset. This makes it rather inconvenient even for the simple relay (typically you'd want the relay hidden away and not want to reconnect to your PC every time there's a power cut!). Also, it seems pretty limited in terms of interfacing a thermometer, for example. So, I decided to flash nodeMCU onto the AI-Thinker module (for consistency with what I was doing on the dev board).

Initially I used a minimal nodemcu-1.5.4.1-final (with 7 modules) from the nodeMCU build service (link above). That went well. Later I acquired some Dallas ds18b20 thermometers, wanted network time configuration, enduser setup for the WiFi and a few other toys and so I built a 14 module monster from the latest master.

The chip must be booted into flash mode manually; that is you need to connect the ground input to GPIO0 when connecting the power.

Afterwards you can just flash with esptool as above. First I erased the flash with:

python -m esptool -p /dev/ttyUSB0 erase_flash

Controlling the relay with nodeMCU

The LC Tech relay can be controlled from within nodeMCU/Lua by writing to the serial, like below. Note that first it is necessary to switch the baud rate to 9600.

-- must switch to 9600 baud to control relay
uart.setup(0,9600,8,0,1)
-- switch on
uart.write( 0, 0xA0,0x01,0x01,0xA2)
-- switch off
uart.write( 0, 0xA0,0x01,0x00,0xA1)

The cool thing now is that this can easily be wrapped in a web interface so the relay can be controlled from a browser. To do this I modified some code I found online- download the modified code here. If you save it as init.lua on the module then it will run when the device powers up, so your web switch is ready to use without intervention!

When using the latest master nodeMCU I had to make some minor modifications and also took the opportunity to add a proper HTTP header and clean up the code.

Thermometer (DS18B20)

In order for my heating controller to be effective it needs to know what the temperature is! The Dallas 18B20 digital thermometer is ideal for the ESP8266 and there is a choice of code available. It is also cheap (I got 5 for £2 including postage).

It is a "one wire" device and many such devices can be connected to a single GPIO pin. It is possible to power it from the data connection as it has a built in capacitor to store signals ("parasite" mode)- I can't imagine wanting to do this, however.

I decided to make a small piggy back board (/shield in the Arduino parlance) to hold the thermometer and ESP-01.

I also added a button to enter flash boot mode, as I seem to be flashing quite often these days and having to hold a jumper lead while plugging in the power is challenging for someone as uncoordinated as me :) The button should be useful for other things any way.

Note that a 4.7k pull-up resistor is required on the data connection (visible just below the ESP-01 module). The button and thermometer are to the right. I put the thermometer on GPIO0 (in addition to the button), which is pin index 3.

I compiled in the ds18b20 C module to nodeMCU, assuming this would be the fastest and most RAM efficient. A basic example of reading the thermometer is below (taken from the official docs):

ds18b20.setup(3) -- GPIO0
-- read all sensors and print all measurement results
ds18b20.read(
    function(ind,rom,res,temp,tdec,par)
        print(ind, string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")), res,temp,tdec,par)
    end,
    {})

The first time you read from the thermometer it typically returns 85 (C), which means it isn't ready yet. The example also prints the unique ID for the device (useful when you have multiple thermometers!).

For my application I read the temperature every minute (60*1000ms) using the timer, switch the relay on if it's cold (and vice-versa) and update a global variable.

-- read temperature every minute and change relay accordingly

tmr.alarm(0,60000,1,function()

ds18b20.read(

function(ind,rom,res,temp,tdec,par)

gbl_temp = temp;

if (gbl_temp < 20.5) then

uart.write( 0, 0xA0,0x01,0x01,0xA2);

end

if (gbl_temp > 22.0) then

uart.write( 0, 0xA0,0x01,0x00,0xA1);

end

end,

{});

-- print(gbl_temp)

collectgarbage()

end)

I should also be smoothing the temperature (even if just gbl_temp = (2*gbl_temp + temp)/3) and will add more error checking. I also use the global temperature variable e.g. in my web interface (which is still quite basic but the cosmetics can be done quickly at the end).

I am also considering a small LCD display, but this would be quite a bit of work on the (sole remaining) GPIO pin. Otherwise I am quite happy with the hardware. The main software part left is understanding the clock (rtc) and network time stuff and then putting everything together.