NodeMCU and a tricolour led

I’m on a project that requires some feedback after the operation is made. I cannot use an LCD because they are only two pins left and almost no memory. So my mind focused on two LEDs but after a while, I made a choice, I will use tricolour LED.
From what I read there are two types, one with a common anode and one with common cathode. We will go with common anode.

Module added to boilerplate

Planning

What we need from LED module?
Standard ability to turn on/off any colour. And something more advanced, ability to blink with any colour. We should be able to set a number of blinks so it would work like this (for a 2-time blink): blink-blink-pause-blink-blink-pause and so.
And we need a function to turn off all colours and stop any activity.

From few troubles I had, I learnt one important thing. With the NodeMCU we need to stay simple. So no fancy class this time – I’m already close to out of memory problem.

LED module

Let’s start from initialization and on/off option:

local tricolor_led = {}
tricolor_led.pin_red = nil
tricolor_led.pin_green = nil
tricolor_led.pin_blue = nil

tricolor_led.init = function()
    if (tricolor_led.pin_green) then gpio.mode(tricolor_led.pin_green,gpio.OUTPUT) end
    if (tricolor_led.pin_blue) then gpio.mode(tricolor_led.pin_blue,gpio.OUTPUT) end
    if (tricolor_led.pin_red) then gpio.mode(tricolor_led.pin_red,gpio.OUTPUT) end
    tricolor_led.clear()
end
tricolor_led.red = function(status)
    if tricolor_led.pin_red then
        if status then gpio.write(tricolor_led.pin_red, gpio.LOW) else gpio.write(tricolor_led.pin_red, gpio.HIGH) end
    end        
end
tricolor_led.green = function(status)
    if tricolor_led.pin_green then
        if status then gpio.write(tricolor_led.pin_green, gpio.LOW) else gpio.write(tricolor_led.pin_green, gpio.HIGH) end
    end        
end
tricolor_led.blue = function(status)
    if tricolor_led.pin_blue then
        if status then gpio.write(tricolor_led.pin_blue, gpio.LOW) else gpio.write(tricolor_led.pin_blue, gpio.HIGH) end
    end
end

tricolor_led.clear = function()
    tricolor_led.red(false)
    tricolor_led.green(false)
    tricolor_led.blue(false)
end    

return tricolor_led

Sample code that shows how to set up and control the LED:

triled = require "triled"

triled.pin_red = 2
triled.pin_green = 1
triled.pin_blue = 8
triled.init()

triled.red(true)
triled.red(false)

triled.green(true)
triled.green(false)

triled.blue(true)
triled.blue(false)

We can set a state but what about blinking?
For that we need a timer, next we need a counter of blinks and finally, a bool variable to define if we are repeating or it is only one-time blink sequence.
The timer can be registered in init function.

local led = {}
led.pin_red = nil
led.pin_green = nil
led.pin_blue = nil
led.tick = 600
led.blink_pin = nil
led.freq = nil
led.freq_cnt = 0
led.timer_loop = nil
led.timer = tmr.create()
led.init = function()
    if (led.pin_green) then gpio.mode(led.pin_green,gpio.OUTPUT) end
    if (led.pin_blue) then gpio.mode(led.pin_blue,gpio.OUTPUT) end
    if (led.pin_red) then gpio.mode(led.pin_red,gpio.OUTPUT) end
    led.clear()
    led.timer:register(led.tick, tmr.ALARM_AUTO, function()        
        if gpio.read(led.blink_pin) == 1 and led.freq_cnt < led.freq then gpio.write(led.blink_pin, gpio.LOW) else gpio.write(led.blink_pin, gpio.HIGH) led.freq_cnt = led.freq_cnt + 1 if led.freq_cnt > led.freq + 1 then
                if led.timer_loop then led.freq_cnt = 0 else led.timer:stop() end
            end
        end
    end)
end

Default tick value is 600ms. Our timer works in following way, it changes states between high and low and counts high (disabled) states. When the counter is equal to the number of required blinks + 1, the counter is restarted or timer is stopped, depending on settings.
Why plus one? It creates a break between blink sequences.

led.red = function(status)
    if status then gpio.write(led.pin_red, gpio.LOW) else gpio.write(led.pin_red, gpio.HIGH) end        
end
led.green = function(status)
    if status then gpio.write(led.pin_green, gpio.LOW) else gpio.write(led.pin_green, gpio.HIGH) end      
end
led.blue = function(status)
    if status then gpio.write(led.pin_blue, gpio.LOW) else gpio.write(led.pin_blue, gpio.HIGH) end
end
led.clear = function()
    led.red(false)
    led.green(false)
    led.blue(false)
    led.timer:stop()
end 

Variable checkers are gone, we have less code and in case of mistake, we will know for sure that there is one 🙂
Function clear also stops the timer.

led.blink_red = function(freq, loop)
    led.blink_pin = led.pin_red
    led.blink(freq, loop)
end
led.blink_green = function(freq, loop)
    led.blink_pin = led.pin_green    
    led.blink(freq, loop)
end
led.blink_blue = function(freq, loop)
    led.blink_pin = led.pin_blue    
    led.blink(freq, loop)
end
led.blink = function(freq, loop)
    led.freq = freq
    led.timer_loop = loop
    led.freq_cnt = 0
    led.clear()
    led.timer:start()
end
return led

Blinking functions store pin corresponding to selected colour, next it resets variables and starts the timer.

How to use this?

triled.blink_green(2)

It will blink two times and stop.

triled.blink_blue(3, true)

This creates a 3 blinks sequence.

Real example

I need this LED to show current device status. It should blink red on wifi failure, show green on success and show red on failure.
Success and failure are connected with RC read and a server but wifi status is defined on the device.
So let’s start with it.

At the top of wifi-init.lua we must add:

triled = require "triled"
triled.pin_red = 2
triled.pin_green = 1
triled.pin_blue = 8
triled.init()
triled.blink_red(2, true)

This will initialize LEDs and starts blinking.
We will stop blink just before we start the keep-alive timer:

            _boot_wifi_timer:stop()
            triled.clear()
            _wifi_keepalive_timer:start()

In the timer that checks periodically for network accessibility, we need to add start and stop blink on proper actions. The start is when failure is detected and the counter is still 0. The stop is when a network is reachable again and the counter is greater than 0.

_wifi_keepalive_timer:register(5000, tmr.ALARM_AUTO, function()
    if wifi.sta.status() ~= 5 then
        if _WIFI_FAIL_COUNTER == 0 then 
            triled.blink_red(2, true) 
        end
        _WIFI_FAIL_COUNTER = _WIFI_FAIL_COUNTER + 1       
    else
        if _WIFI_FAIL_COUNTER > 0 then
            triled.clear()
        end            
        _WIFI_FAIL_COUNTER = 0
        
    end
    if _WIFI_FAIL_COUNTER > 10 then        
        print "Node reboot..."
        node.restart()
    end       
end)

This takes cares of emergency wifi crash and notification.

Summary

New module for NodeMCU boilerplate is always welcome 🙂 This time a little help with LEDs and practical use with the wifi-init module.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s