Grouch mumbling about computers.


IOT house with Sonoff and MicroPython

TAGS: [ iot ]


I bought a few sonoff basics and wanted to set up some sensors and triggers around the house, with some restrictions:

  • If a sensor or switch replaces something existing, then there should be always a fallback to that functionality even if there's no connectivity.
  • Code should be fully open
  • There should be no complex infrastructure to support this

Evaluating the board

I wanted a cheap board that included a mains transformer as both using phone chargers (NodeMCU) or building your own ends up being more expensive, cumbersome and prone to failures. Winner: Sonoff basic.

0 button IN
1 TX INOUT outputs garbage on boot
12 relay OUT powered from mains
13 led OUT inverted high/low

Also note that if you fuck up the TX/RX pins you won't be able to flash the Sonoff again.

I ended up choosing to go for independent modules written in MicroPython, where the communication bus is MQTT.

To get MicroPython on the board you should solder the 4 UART pins on the board and flash the firmware.


Hardware setup

From easiest to hardest:

  • USB UART/TTL breakout pin
  • Arduino as a serial 'proxy'
  • Raspberry Pi GPIO

sonoff pi pinout All wired up

Flashing MicroPython

I was running (as recommended here ) --port /dev/ttyUSB0 erase_flash --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 esp8266-20170108-v1.8.7.bin

The first issue I had was that the TX/RX pair is reversed on my revision of the board, but that's a simple cable switch. The second issue is that my firmware flashes kept failing, and as found here , the manufacturer of my board was 5e so I modified the flash command slightly to --port /dev/ttyUSB0 erase_flash --port /dev/ttyUSB0 write_flash -fs 1MB -fm dout 0x0 esp8266-20180511-v1.9.4.bin

Getting a REPL

While you can use screen , minicom , picocom or other software for serial communication, you also will (most likely) want to push files into the VFS .

What I used for both getting a REPL and pushing files is mpfshell , which sometimes sadly fails, but >90% of the time works great.

My rapid iterations are done by running

mpfshell ttyUSB0 -c "put; repl; exit"

and pressing Ctrl-D immediately which soft-reboots the esp8266. I'll see my code run, play around in the REPL and iterate. To exit mpfshell you need to press Ctrl-Alt-] .

Writing some code

With the way I set up the common functions, you can have something working in ~20 lines of code (omitting imports)



led = Pin(13, Pin.OUT)
dht = dht.DHT22(Pin(14))

def read_dht():
    common.mqtt.publish(TEMPTOPIC, "%.2f" % dht.temperature())
    common.mqtt.publish(HUMTOPIC, "%.2f" % dht.humidity())

def main():
    setup_fns = [ lambda: led(1) # Turn off LED, it is inverted
    common.loop(CLIENT_ID, setup_fn=setup_fns, loop_fn=[read_dht], callback=sub_cb, subtopic=SUBTOPIC)


Pub/sub + button for relay

SUBTOPIC = b"%s/set" % CLIENT_ID
PUBTOPIC = b"%s/state" % CLIENT_ID

button = Pin(0, Pin.IN)
led = Pin(13, Pin.OUT)
relay = Pin(12, Pin.OUT)

def set_pin(pin, state):
    common.mqtt.publish(PUBTOPIC, str(pin()))

def sub_cb(topic, msg):
    if msg == b'1':
        set_pin(relay, True)
    elif msg == b'0':
        set_pin(relay, False)
        set_pin(relay, not relay())

def handle_button(pin):
    set_pin(relay, not relay())

def main():
    setup_fns = [ lambda: button.irq(handler=handle_button, trigger=Pin.IRQ_RISING),
                  lambda: led(1) # Turn off LED, it is inverted
    common.loop(CLIENT_ID, setup_fn=setup_fns, loop_fn=[], callback=sub_cb, subtopic=SUBTOPIC)


The mqtt lib is umqtt.simple which is an official lib. You can find the common lib on github .

Getting code on the device

After writing the code, to put it into the device simply run

$ mpfshell ttyUSB0

** Micropython File Shell v0.9.1, **
-- Running on Python 3.7 using PySerial 3.4 --

mpfs [/]> put HOSTNAME
mpfs [/]> put
mpfs [/]> put
mpfs [/]> put
mpfs [/]> ls

Remote files in '/':


Testing the changes:

mpfs [/]> repl
MicroPython v1.9.4-8-ga9a3caad0 on 2018-05-11; ESP module with ESP8266
Type "help()"
*** Exit REPL with Ctrl+] ***
for more information.
PYB: soft reboot
#6 ets_task(40100130, 3, 3fff83ec, 4)
Connected to SSID!
Subscribing to b'PRINTER_POWER/set'
Subscribing to b'PRINTER_POWER/OTA'
#> Manually pressed button
Button was pressed
Setting pin to True
Publish 1 to b'PRINTER_POWER/state'
#> Manually pressed button
Button was pressed
Setting pin to False
Publish 0 to b'PRINTER_POWER/state'
#> Send '1' via MQTT
Setting pin to True
Publish 1 to b'PRINTER_POWER/state'