This chapter shows some program samples which were ported from NodeMCU running on ESP8266. You will see that there are differences between WiFiMCU and NodeMCU but the program structure stays unchanged.

The NodeMCU program samples are available for download from SourceForge. The WiFiMCU program samples have their own repository at SourceForge.

WiFiMCU Init

After Reset the WiFiMCU module calls the file init.lua. This file handles the WLAN connection and other initialization tasks. The connection to WLAN needs user identification and password which I have isolated in the file setup.lua.

In the simplest case the file setup.lua can look like

print("set up wifi mode")

cfg={}
cfg.ssid='guest'
cfg.pwd=''
wifi.startsta(cfg)
cfg=nil

To initialize the WLAN connection the WiFiMCU module tries to connect and waits for a valid IP address. The timer repeats querying the IP address by wifi.sta.getip() until one is received. After that the timer can stop. The end of configuration and the assigned IP address are output via console.

--load credentials
--SSID and PassWord should be saved according wireless router in use
dofile("setup.lua")

function startup()
    if wifi.sta.getip()== nil or wifi.sta.getip()== "0.0.0.0" then
        print("IP unavaiable, Waiting...")
    else
        tmr.stop(1)
        print("Config done, IP is "..wifi.sta.getip())
--        dofile("myfile.lua")
    end
end

tmr.start(1, 1000, function() startup() end)

Starting a reboot shows the running files. If you compare witrh the source code listed above you can see the message "set up wifi mode" from setup.lua followed by the messages from init.lua.

WiFiMCU Info

The program sample wifimcu_info.lua provides system information of the WiFiMCU module connected to the PC. The functions mcu.ver(), mcu.chipid(), mcu.mem, file.info(), and tmr.tick() are responsible for gathering the data.

-- Title   : Lists WiFiMCU and hardware information
-- Date    : 2015-08.31
-- Id      : wifimcu_info.lua
-- Firmware: v0.9.4 build@20150824)
-- Created : Claus Kuehnel

-- get WiFiMCU and hardware information.
nv, bd = mcu.ver()
print("WiFiMCU Version "..nv, bd)
print("ChipID "..mcu.chipid())

-- get memory info
fm,tas,mtas,fc = mcu.mem()
print("Memory info:")
print("Maximum allocated: " .. mtas .. " Bytes")
print("Total allocated: " .. tas .. " Bytes")
print("Free: "..fm.." Bytes")

-- get file system info
last, used, total = file.info()
print("File system info:")
print("Total : "..total.." Bytes")
print("Used : "..used.." Bytes")
print("Last: "..last.." Bytes")

-- print current value of timer
print("WiFiMCU running "..tmr.tick() .." ms from last restart")

Benchmarks

Benchmarks should help to compare processors, but there is still plenty of variability. Because of the many influencing factors it has never been easy to compare microprocessors. Things get even tougher in the embedded world, where the number of processors and configurations is practically infinite.

But, if we compare quite similar systems then the results can be used to describe performance parameters.

With NodemCU and WifiMCU we have two systems programmable in Lua but equipped with different microcontrollers and WLAN devices.

Here we want to benchmark the CPU performance by two simple numeric and one IO benchmarks.

Sieve of Erastothenes

The sieve of Eratosthenes is one of the most efficient ways to find all the smaller primes and is a well-known CPU benchmark. The program bm1.lua is an implementation of the sieve of Eratosthenes with global variables. The program bm2.lua works with local variables for x and iter. This is the only difference. The file bm2.lua is available for download from Sourceforge but not listed here.

function sieve(n)
  x = {}
  iter = 0
  repeat
    x[1] = 0
    i = 2
    repeat
      x[i] = 1
      i = i + 1
    until i > n
    p = 2
    while(p * p <= n) do
      j = p
      while(j <= n) do
        x[j] = 0
        j = j + p
      end
      repeat
        p = p + 1
      until x[p] == 1
    end
    iter = iter + 1
  until iter == 101
end

print("Sieve of Eratosthenes - Lua Benchmark (Global Variables)")
print("========================================================")
print("Start testing .....")
start = tmr.tick() 
sieve(500)
stop = tmr.tick(0)
print("Done!")
print("Total Time = " .. stop - start .. " ms")

The results of both benchmarks are listed in the table below. The compiled versions bm1.lc and bm2.lc show the same runtime behavior.

Factorial

--------------------------------------------------------
-- CPU Bench Mark
--------------------------------------------------------

CYCLES = 1000

function factorial(n)
  local x = 1.
  for i = 2, n do
    x = x * i
  end
  return x
end


x=20 print("Calculating fak("..x..") as benchmark")

local t = tmr.tick()  -- due to quick access defined as local

--------------------------------------------------------
-- Benchmark runtime of code phragments
--------------------------------------------------------
for n=1, CYCLES do factorial(x) end --user code
--------------------------------------------------------

print("For "..CYCLES.." cycles of calculation")
print("fak("..x..") = "..factorial(x))
print("Elapsed time: "..tmr.tick() - t.." ms")

IOBench

--------------------------------------------------------
-- IO Bench Mark
--------------------------------------------------------

CYCLES = 1000

-- Connect LED & resistor to Pin8 (D8) of NodeMCU
LED = 8 -- D8

-- set gpio 8 as output.
gpio.mode(LED, gpio.OUTPUT)

-- get start time
local t = tmr.tick() -- ms

--------------------------------------------------------
-- Benchmark runtime of code phragments
--------------------------------------------------------
for i = 1, CYCLES do
  gpio.write(LED, gpio.LOW)
  gpio.write(LED, gpio.HIGH)
end
--------------------------------------------------------

t = tmr.tick() - t
print("IO Cycle     = ".. string.format("%.3f",t/CYCLES) .." ms")
print("IO Frequency = ".. string.format("%.3f",CYCLES/t) .." kHz")

Results

The results presented here should help to compare the CPU performance of NodeMCU based on ESP8266 and WifiMCU based on STM32F411CE Cortex-M4 microcotroller.

The used firmware version is WiFiMCU_Firmware_Float_V0.9.5_20150905.bin here. For NodeMCU the version nodemcu_float_0.9.6-dev_20150704.bin was used.

The run time of the programs here is regardless of whether the Lua source or the compiled file will be started.

Benchmark/Device NodeMCU WifiMCU
Sieve (Global, bm1.lua) 2797.443 ms 1737 ms
Sieve (Local, bm2.lua) 1055.539 ms 509 ms
Sieve (Local, bm2.lc) 1056.585 ms 509 ms
Factorial (FactorBench.lua) 144.707 ms 42 ms
Factorial (FactorBench.lc) 144.678 ms 42 ms
IOBench (.lua) 1.86 kHz 6.536 kHz
IOBench (.lc) 1.86 kHz 6.536 kHz

Basically, the Lua interpreter always works in two steps:

  1. Parsing source code (file.lua)* & creating byte code (file.lc)
  2. Execution of the generated byte code

Running byte code (file.lc) does not need the parser. Furthermore, the files will be smaller and a user can not change them. This is an important point if you want to make your program available to others. If the parsing part is small then the difference of the run time can be neglected.

Filesize/Device NodeMCU WifiMCU
Sieve (Global, bm1.lua) 658 Bytes
Sieve (Local, bm2.lua) 752 Bytes
Sieve (Local, bm2.lc) 620 Bytes
Factorial (FactorBench.lua) 780 Bytes
Factorial (FactorBench.lc) 540 Bytes
IOBench (.lua) 785 Bytes
IOBench (.lc) 504 Bytes