M0AGX / LB9MG

Amateur radio and embedded systems

First steps with NodeMCU

nodemcu1

NodeMCU is a versatile board with the WiFi-enabled ESP8266 system-on-chip, USB to UART bridge, voltage regulator and some auxiliary components. With a price tag of about $9 on Aliexpress it allows you develop your own Internet "things" literally in minutes.

The killer part is a build-in Lua interpreter, so no toolchain or development kit is required. The language is pretty easy. Text files containing Lua scripts are simply uploaded to the board and executed directly by the interpreter inside ESP8266. You can also type the commands directly via the terminal. NodeMCU firmware can also be uploaded to any ESP8266 module like the ESP-01, ESP-07 or bare ESP-12.

Getting the latest firmware

It is best practice to have the latest firmware as there were many features, improvements and fixes made to the firmware across it's development and you might not have the latest one programmed in the factory.

The board has a built-in "indestructible" bootloader. No matter what crashing, buggy scripts with endless loops (especially set to be started automatically at boot) you upload, there is always a way out (and I used it more often then I planned...).

To start the bootloader just keep the FLASH button pressed while plugging in the USB cable. To upload the firmware start esptool with the right serial port, write_flash command, starting address (zero) and file name:

1
2
3
4
5
6
$ ./esptool-master/esptool.py --port /dev/ttyUSB0 --baud 9600 write_flash 0x0 nodemcu_latest.bin 
Connecting...
Erasing flash...
Writing at 0x00062000... (100 %)

Leaving...

The process can take a couple of minutes. I noticed that it is much faster when done again with the same firmware version (think recovery...). Probably the bootloader is just smart enough not to rewrite the flash with the same data and only removes my custom scripts.

A terminal application will be required for the next steps. I prefer picocom because it is very easy to use and has only the required features. By default NodeMCU starts serial communication at 9600 bps.

You can press enter a couple of times to see the > prompt. If you can see the character it means that the new firmware has started and a Lua interpreter is ready. I typed node.restart(); to reboot the module and see the bootup messages (they are impossible to see right after plugging the cable because the node boots much faster than USB is able to establish a connection with a PC).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ picocom -b 9600 /dev/ttyUSB0 
picocom v1.7

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 9600
parity is      : none
databits are   : 8
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,

Terminal ready
> 
> node.restart();
> ��D�
      ��D���`
            9@H��

NodeMCU 0.9.5 build 20150214  powered by Lua 5.1.4
lua: cannot open init.lua
> 

Hardly impressive :)

Doing something useful - temperature sensing

ds18b20

NodeMCU without any other parts has little practical use. I decided to connect DS18B20 temperature sensor to the board (and pipe it to RRDtool some day). The sensor is originally packaged in a TO-92 case. There are fully-assembled, waterproof ones available on Aliexpress (with a no-brainer price tag below $1.5). The DS18B20 uses a 1-wire bus to communicate (and get power) from the microcontroller, though I use a separate power supply line for simplicity and reliability.

The sensor has three wires - black (ground), red (power), yellow (1-wire data) that have to go to the right pins of NodeMCU. This is the pinout of NodeMCU: NODEMCU-DEVKIT-INSTRUCTION-EN

Sensor's ground should of course be connected to any of the GND pins, power (red wire) to 3,3V.

Most of the GPIO pins can be used for any purpose (like 1-wire) with some exceptions. The ones out of scope are at least: RXD0 and TXD0 (it would interfere with the UART connected to USB), GPIO16 (it is used to wake up the device from deep sleep).

To increase the fun and confusion a pin can have multiple names like: D8 is also GPIO15, TXD2 and HSPICS. The GPIOxx are names of the lines of ESP8266 (they are the same on any board), the Dxx names are used by Lua firmware.

I picked GPIO4 also known as D2 for 1-wire data. In scripts this pin will simply be 2 (without the D).

1-wire requires a pull-up resistor (visible in the first picture) to Vcc (3.3V). I soldered one directly to the board.

The script

That's how the temperature reading code looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
tmr.delay(1000000); --make a delay to stabilize power

pin=2; --onewire pin number

ow.setup(pin); --configure that pin for 1-wire
p=ow.reset(pin); --send a reset over the bus, returns slave present status
print("present="..p); --prints 1 if a slave is present
ow.skip(pin); --SKIP ROM command, no address matching is necessary if there is only one device on a bus
ow.write(pin, 0x44, 1);--CONVERT T command (start temperature conversion)

tmr.delay(1000000); --make a delay to allow the DS18B20 complete temperature conversion (probably too long)

p2=ow.reset(pin);
print("present2="..p2);
ow.skip(pin);
ow.write(pin,0xBE,1); --READ SCRATCHPAD command
lsb=ow.read(pin); --read the first byte from the scratchpad ram
msb=ow.read(pin); --read the second byte from the scratchpad ram
temp=bit.lshift(msb,8)+lsb; --combine two bytes into a single integer
print("msb="..msb.." lsb="..lsb.." temp="..temp); --print out all the values

if (temp > 32767) then --if the temperature looks negative
    temp = temp - 65536; --convert from two's complement to decimal
end

Tc100=(6*temp)+temp/4; --multiply by 0,0625 to get centigrade*100
print("Tc100="..Tc100); --print the result
temperature=string.format("%d",Tc100); --truncate the fractional part
print("temperature="..temperature); --print the final temperature

How to upload scripts to NodeMCU

Each of the script lines can be simply copied and pasted into a terminal and executed line by line (pasting the whole script at once will not work, as it will overrun the USB to UART chip's buffers and the Lua interpreter will see junk). NodeMCU has a built-in file system and can store files very similarly to a regular PC. There is a handy tool for uploading files: luatool.

File upload looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$ ./luatool-master/luatool/luatool.py -p /dev/ttyUSB0 -b 9600 -f gettemperature.lua 

->file.open("gettemperature.lua", "w") -> ok
->file.close() -> ok
->file.remove("gettemperature.lua") -> ok
->file.open("gettemperature.lua", "w+") -> ok
->file.writeline([==[tmr.delay(1000000); --make a delay to stabilize power]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[pin=2; --onewire pin number]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[ow.setup(pin); --configure that pin for 1-wire]==]) -> ok
->file.writeline([==[p=ow.reset(pin); --send a reset over the bus, returns slave present status]==]) -> ok
->file.writeline([==[print("present="..p); --prints 1 if a slave is present]==]) -> ok
->file.writeline([==[ow.skip(pin); --SKIP ROM command, no address matching is necessary if there is only one device on a bus]==]) -> ok
->file.writeline([==[ow.write(pin, 0x44, 1);--CONVERT T command (start temperature conversion)]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[tmr.delay(1000000); --make a delay to allow the DS18B20 complete temperature conversion]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[p2=ow.reset(pin);]==]) -> ok
->file.writeline([==[print("present2="..p2);]==]) -> ok
->file.writeline([==[ow.skip(pin);]==]) -> ok
->file.writeline([==[ow.write(pin,0xBE,1); --READ SCRATCHPAD command]==]) -> ok
->file.writeline([==[lsb=ow.read(pin); --read the first byte from the scratchpad ram]==]) -> ok
->file.writeline([==[msb=ow.read(pin); --read the second byte from the scratchpad ram]==]) -> ok
->file.writeline([==[temp=bit.lshift(msb,8)+lsb; --combine two bytes into a single integer]==]) -> ok
->file.writeline([==[print("msb="..msb.." lsb="..lsb.." temp="..temp); --print out all the values]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[if (temp > 32767) then --if the temperature looks negative]==])
->file.writeline([==[temp = temp - 65536; --convert from two's complement to decimal]==]) -> ok
->file.writeline([==[end]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[Tc100=(6*temp)+temp/4; --multiply by 0,0625 to get centigrade*100]==]) -> ok
->file.writeline([==[print("Tc100="..Tc100); --print the result]==]) -> ok
->file.writeline([==[temperature=string.format("%d",Tc100); --truncate the fractional part]==]) -> ok
->file.writeline([==[print("temperature="..temperature); --print the final temperature]==]) -> ok
->file.flush() -> ok
->file.close() -> ok
--->>> All done <<<---

Luatool creates file on the NodeMCU and then executes a file write command for each line of the script. It can be quite slow, but it works (increasing the baudrate is an option).

To execute the script you have to open the terminal and type dofile('gettemperature.lua');

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ picocom -b 9600 /dev/ttyUSB0 
picocom v1.7

port is: /dev/ttyUSB0
flowcontrol: none
baudrate is: 9600
parity is  : none
databits are   : 8
escape is  : C-a
local echo is  : no
noinit is  : no
noreset is : no
nolock is  : no
send_cmd is: sz -vv
receive_cmd is : rz -vv
imap is: 
omap is: 
emap is: crcrlf,delbs,

Terminal ready

> dofile('gettemperature.lua');
present=1
present2=1
msb=1 lsb=186 temp=442
Tc100=2762.5
temperature=2762
> 

The sensor is detected, read and the temperature is 27.62 centigrade :) The delay between conversion and reading can be much shorter. For the networked thermometer I will try to do something useful in between.

Next post wll cover transmitting the temperature to a PC over WiFi and making a small server application.