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:
- Parsing source code (file.lua)* & creating byte code (file.lc)
- 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 |