NodeMCU programming model

NodeMCU is inspired by node.js, a javascript framework and server based on the concept of callback-based asynchronous programming.

This concept can take some getting used to. The following examples should give you an idea of how asynchronous programs are structured.

Synchronous vs. Asynchronous

The previos chapter ended in an example of a blinking LED program. Some of you may have already used Arduino to write a similar program:

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

The first thing to notice is that no other code can run while the loop function is running. This means that the microcontroller will sit idly for one second while the delay(1000) calls are being executed.

The code-snipped below shows the asynchronous counterpart to the Arduino code above.

function led_toggle()
   if (status == gpio.LOW) then
      status = gpio.HIGH
   else
      status = gpio.LOW
   end

   gpio.write(4, status)
end

function led_setup()
   gpio.mode(4, gpio.OUTPUT)

   -- schedule led_toggle() to run once a second
   tmr.create():alarm(1000, tmr.ALARM_AUTO, led_toggle)
end

The function led_toggle is scheduled to be executed every 1000ms using the tmr.create():alarm() call. In the meantime the microcontroller is free to run different pieces of code.

Hint: The NodeMCU firmware makes use of this property, for example for handling network connections. This is why you should avoid writing functions in NodeMCU that may take a long time to run, especially by avoiding the tmr.delay() function.

Handling input

After learning the basics of what asynchronous programming means we have to learn how they transfer to actual real-world tasks.

One real-world task is acting upon data input.

Replace the content of you application.lua file with the code-snippet below and upload it to your microcontroller.

function uart_on_char(char)
   print("Read character: "..char.." from uart")
end

function uart_setup()
   -- Whenever 1 byte of data is received
   -- run uart_on_char, do not interpret the
   -- received byte as lua command
   uart.on("data", 1, uart_on_char, 0)
end

uart_setup()

After uploading the program, resetting your microcontroller and connecting to it using picocom the microcontroller should respond to every character you enter with a line like the following:

[user@computer ~]$ picocom -b 115200 /dev/ttyUSB0
Read character: h from uart
Read character: e from uart
Read character: l from uart
Read character: l from uart
Read character: o from uart

Task: modify the application.lua to turn on the LED whenever the character l is received and off whenever the character d is received.