January 15th, 2017
Updated: January 21st, 2017
Updated: June 22nd, 2017

When I purchased my first ITEAD Sonoff WiFi switches, I also got a Wemos D1 mini board. I thought getting a ESP8266 development kit would be a good way to prepare for hacking the switches. Two You Tube videos on educ8s.tv, Wemos D1 mini: A first look at this ESP8266 based board and WeMOS D1 ESP8266 vs Arduino Uno, Arduino Due and Teensy 3.2. Which one is the fastest board? and the series of articles at HACKADAY by Elliot Williams were positive influences in that decision. Indeed, what follows borrows heavily from the latter.

The very first thing I did with the Wemos D1 mini is to blink the LED using the Arduino 1.6.13 IDE. The only problem encountered was getting access to the serial link (ttyUSB0) on my Linux desktop. I found two solutions, changing the permissions to the device or, the one I adopted, becoming a member of the dialout group. The second thing I did was to get an HTML page from the Web. Both sketches are in the Arduino library.

I then wanted to follow Elliot Williams' articles on MQTT, notably the first MINIMAL MQTT: NETWORKED NODES. He uses NodeMCU firmware, which is based on the ESpressif SDK and was initially developed in conjunction with the NodeMCU development kit. The Wemos D1 mini is like a younger sibling. Using NodeMCU is a little bit trickier than using the Arduino IDE. I have documented how I managed to do it below.

NodeMCU utilities

While there is at least one usable IDE for the NodeMCU, Elliot Williams does not suggest using one. Following his lead, we will do this like "real programmers", and use Python command line utilities to flash the NodeMCU firmware and to upload Lua script files.

michel@hp:~$ sudo pip install esptool michel@hp:~$ sudo pip install nodemcu-uploader
When installing esptool.py about five week ago, I obtained version 1.2.1. A newer version 1.3 is available but I have not needed to update. Interestingly, Wemos recommends Christian Klippel's version: esptool-ck. What's the difference? Dunno!

We also need a way to establish a serial link with the D1 mini. Apparently this can be done with PuTTy on a Windows desktop. Since I had the Linux version installed, I tried using it in my Ubuntu box, but without success. Initially, I used GNU Screen but found it very awkward and eventually decided on using yet another Python script: miniterm.py. To make things simple, the latter seems to have been installed along with esptool, so if that route is chosen nothing needs to be installed.

If you do want to use Screen, it may have to be installed on your system:

michel@hp:~$ sudo apt-get install screen
If it is already installed, that command will do no harm. Alternatively, you can use the Ubuntu Software Center application.

Flashing the D1 mini

The firmware, NodeMCU , is very modular. It needs to be because the typical Esp8266 based controller has rather limited memory. Everything is relative, my first computer had 4k bytes of memory, my first serious Z80 machine had 64k bytes of memory and external storage was on 256k bytes 8" floppies. In comparison, the Wemos D1 mini has "only" 4M bytes of memory. In any case, not all modules available for the chip will fit in 4MB and there must be some room for the application code. Here are two possible ways of getting the appropriate firmware.

1. Download Elliot Williams' built of NodeMCU. Go to https://github.com/hexagon5un/hackaday_mqtt and click on the green [Clone or download] button then click on the Download Zip link. I saved the archive to /home/michel/Téléchargements/WemosD1mini/hackaday and then extracted the content.

2. To experiment using NodeMCU with Domoticz, I wanted other modules. So, as recommended by Elliot Williams, I went to the NodeMCU custom builds site by Marcel Stör and choose the modules to be included in the binary image to flash. These are the modules I am presently using.

SJSON JSON support module which allows encoding and decoding of JSON strings. SJSON is a recent replacement of CJSON previously used (update: June 22nd 2017).
DHT Read DHT humidity and temperature sensors
file provides access to the SPIFFS file system and its individual files on the internal flash memory.
GPIO provides access to the GPIO (General Purpose Input/Output) subsystem.
HTTP basic HTTP client which can do GET/POST/PUT and DELETE requests.
MQTT client that can publish and subscribe to an MQTT broker.
node provides basic chip information and access to the Lua interpreter.
timer provides access to simple timers, alarms, the system counter and uptime.
UART allows configuration of and communication over the UART serial port.
WiFi allows configuration of and communication over the wireless local area network (WiFi).
I then downloaded the integer version of the firmware to a file named /home/michel/Development/NodeMCU/nodemcu-master-10-modules-2017-01-16-19-52-22-integer.bin from the link provided in the message I received from the build site. The build took only a couple of minutes. If you don't seem to be getting a message, then as suggested on the site, look in the spam folder.

The final step is to download the firmware to the board. Connect the board to the desktop computer with a USB cable. On my system the serial link is the device /dev/ttyUSB0. It could be a different device on another computer. Then change the directory to the one containing the downloaded image file (whichever one you use) and flash it.

1.

michel@hp:~$ cd /Téléchargements/WemosD1mini/hackaday/firmware michel@hp:~/Téléchargements/WemosD1mini/hackaday/firmware$ esptool.py \ > --port /dev/ttyUSB0 --baud 57600 \ > write_flash 0x00000 nodemcu-master-9-modules-2016-05-05-20-09-20-integer.bin

2.

michel@hp:~$ cd /home/michel/Development/NodeMCU michel@hp:~/Development/NodeMCU$ esptool.py \ > --port /dev/ttyUSB0 --baud 57600 \ > write_flash 0x00000 nodemcu-master-10-modules-2017-01-16-19-52-22-integer.bin

Blink

To talk to the Wemos D1 mini over the serial link, it must be connected to the desktop computer with a USB cable. If using Screen on the desktop, you need to know the port and then establish a 9600bps 8N1 connection.

michel@hp:~$ screen /dev/ttyUSB0 9600 >
If you don't see the prompt, '>', press the Enter key a few times. If that does not work, press the RESET button on the D1 mini and then press the Enter key a few times.

To leave the Screen terminal press CtrlA and then \. That is the total extent of my knowledge of that program.

Eventually, I settled on using miniterm.py which prompts for the port and established the connection details on its own:

michel@hp:~$ miniterm.py --- Available ports: --- 1: /dev/ttyUSB0 USB2.0-Serial --- Enter port index or full name: 1 --- Miniterm on /dev/ttyUSB0 9600,8,N,1 --- --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- >
Again, if you don't see the prompt, '>', press the Enter key a few times. If that does not work, press the RESET button on the D1 mini and then press the Enter key a few times. By the way, while miniterm.py says Menu: Ctrl+T, nothing much happens if you press that key combination on its own. It is an escape character that must be followed by another keyboard combination. Do the CtrlT CtrlH sequence to get more information.

Once the '>' prompt is obtained, you are actually in the Lua interpreter and you can check that everything is fine by doing something very simple:

> print('hello world') hello world > print(5 + 8) 13 >

Time to blink the builtin led. It is connected to pin 4.

> led=4 > gpio.mode(led, gpio.OUTPUT) > period = 80*1000 > for i = 1,20,1 do >> gpio.write(led, gpio.LOW) >> tmr.delay(period) >> gpio.write(led, gpio.HIGH) >> tmr.delay(period) >> end

I should mention that tmr.delay in not a good idea. See the NodeMCU Documentation on that topic.

Let's make this a little easier to use by creating a blink function.

> led=4 -- not needed already defined > gpio.mode(led, gpio.OUTPUT) -- not needed already defined > period=50*1000 -- not needed already defined > function blink(count) >> for i=1,count,1 do >> gpio.write(led, gpio.LOW) >> tmr.delay(period) >> gpio.write(led, gpio.HIGH) >> tmr.delay(period) >> end -- for loop >> end -- function > -- run it > blink(10) > blink(20) -- more blinks

You can repeat the last two steps, varying the blink count, any number of times. I bet you will do it more than once, there is something strangely satisfying in seeing that LED flash on and off at one's command.

Using a File Uploader

By now, if you are not a perfect typist, you know that using the Screen terminal is a painful experience and miniterm.py is not much fun either. We can use the NodeMCU file system to upload and execute Lua scripts.

Use your favourite text editor to create a short lua script (or download it) and save it to a file named blink.lua:

-- Simple blink program -- The D1 mini's builtin led is connected to pin 4 builtin_led=4 gpio.mode(builtin_led, gpio.OUTPUT) -- blinking parameters period=50*1000 default_count=10 -- the function function doBlink(acount) acount = acount or default_count for i=1,acount,1 do gpio.write(builtin_led, gpio.LOW) tmr.delay(period) gpio.write(builtin_led, gpio.HIGH) tmr.delay(period) end end -- execute it doBlink(20)

I saved that file in the following directory ~/Documents/Site/michel/src/program/misc. The next step is to change to the file's directory, upload it, check that it is stored in the D1 mini flash memory and the execute it.

michel@hp:~$ cd Documents/Site/michel/src/program/misc michel@hp:~/Documents/Site/michel/src/program/misc$ nodemcu-uploader upload blink.lua opening port /dev/ttyUSB0 with 115200 baud Preparing esp for transfer. Preparation already done. Not adding functions again. Transferring blink.lua as blink.lua All done! michel@hp:~/Documents/Site/michel/src/program/misc$ nodemcu-uploader file list opening port /dev/ttyUSB0 with 115200 baud Listing files for key,value in pairs(file.list()) do print(key,value) end blink.lua 384 > michel@hp:~/Documents/Site/michel/src/program/misc$ nodemcu-uploader exec blink.lua
I find that I sometimes have to repeat commands using nodemcu-uploader. If problems persist, try resetting the D1 mini. If problems persist and you used miniterm.pi or screen with default values, try inserting the -B 9600 option just before the upload or exec with nodemcu-uploader.

Work Flow

The last line of the blink.luascript calls on the doBlink function to perform the action which is why something will be seen when nodemcu-uploader exec blink.lua is run. But it will often be the case that a script is meant to be used interactively. The work flow will then be different. To illustrate how to proceed, remove the last couple of lines in blink.lua and reload it just as before.

michel@hp:~$ cd Documents/Site/michel/src/program/misc michel@hp:~/Documents/Site/michel/src/program/misc$ nodemcu-uploader upload blink.lua opening port /dev/ttyUSB0 with 115200 baud ...

You could run nodemcu-uploader exec blink.lua but nothing visible will happen. Instead open a serial link with the D1 mini and then run the script and call on doBlink:

michel@hp:~/Documents/Site/michel/src/program/misc$ miniterm.py /dev/ttyUSB0 115200 --- Miniterm on /dev/ttyUSB0 115200,8,N,1 --- --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- > dofile('blink.lua') > doBlink(10) >
The function dofile(script-name) is the equivalent of nodemcu-uploader exec script-name; it executes the script, which in this case amounts to creating the doBlink function. The later can then be invoked in the interpreter as often as desired, with whatever value wanted for count.

You could invoke miniterm.py without the port and baud options. The program will find the port. Well it does that on my system maybe because there is only one serial connection to the computer. But the baud will be set to 9600 bps and this will cause problems because by default the Wemos D1 mini sets the rate of its serial port at 115200 bps. And as can be seen above, nodemcu-uploader uploads the script at that speed. It is difficult to negotiate a new serial rate. I have found it necessary to reset the D1 mini before using miniterm.py with its own default rate. It is much more productive to use the default 115200 bps rate. And with both miniterm.py and screen (which could be used instead of later but with the same options), the rate cannot be specified without putting the port before.

While a bit more awkward than an IDE, I have found running the sequence

quite efficient.

Contact Switch

We will finish this introduction with another rather simple program that will serve as the beginning of a remote garage door sensor. There will be a contact switch that will be closed when the garage door is closed and open when the garage door is open. The builtin led, that will serve as a warning signal, will reflect the opposite state, it will be on when the garage door is open and off otherwise. The Fritzing image below shows the connection. Note how D5 is grounded when the reed switch is closed and how it will be pulled high by a resistor when the reed switch will be open.

Use a text editor to create the following Lua script called switch.lua or download it.

-- Simple switch program -- The Wemos D1 mini builtin LED will follow the state of a switch. -- Script parameters builtin_led = 4 -- The D1 mini's builtin led is connected to pin 4 contact_switch = 5 -- The contact switch is connected to pin 5 normally_open = 0 -- Set to 1 for normally open switch, 0 for normally closed switch blinks = 15 -- number of led blinks at the beginning of the program period = 50*1000 -- on and off length of blinks interval = 250 -- polling interval for checking the state of the switch in ms -- setup the controller pins gpio.mode(builtin_led, gpio.OUTPUT) gpio.mode(contact_switch, gpio.INPUT) -- start by flashing builtin led for i=1,blinks,1 do gpio.write(builtin_led, gpio.LOW) tmr.delay(period) gpio.write(builtin_led, gpio.HIGH) tmr.delay(period) end -- function that read the state of the switch and -- updates the LED function update_led() state = gpio.read(contact_switch) if normally_open ~=1 then state = 1 - state end gpio.write(builtin_led, state) end -- finally create an alarm which will update the led at regular intervals tmr.alarm(0, interval, tmr.ALARM_AUTO, update_led)

Upload the script, check that it is in the file system and then execute it.

michel@hp:~$ cd Documents/Site/michel/src/program/misc michel@hp:~/Documents/Site/michel/src/program/misc$ nodemcu-uploader upload switch.lua opening port /dev/ttyUSB0 with 115200 baud Preparing esp for transfer. Preparation already done. Not adding functions again. Transferring blink.lua as blink.lua All done! michel@hp:~/Documents/Site/michel/src/program/misc$ nodemcu-uploader file list opening port /dev/ttyUSB0 with 115200 baud Listing files for key,value in pairs(file.list()) do print(key,value) end blink.lua 384 switch.lua 938 > michel@hp:~/Documents/Site/michel/src/program/misc$ nodemcu-uploader exec switch.lua
Of course you don't need a reed switch. Just two wires that are connected together in a row on the breadboard and that can be easily disconnected will simulate a switch quite nicely. Open and close the switch and check that the LED reflects the state of the switch.

This program is caught in an infinite loop. To stop, press the RESET button on the D1 mini.