NodeMCU event listener and HD44780 handler

In last post we started moving from single purpose node to multiple purpose node by introducing handlers and listener. We also created our first handler for temperature sensor.

This time we will take next two steps. First create a handler for lcd module and remove server that it was using.
Next we will start using server_listener as message dispatcher to handlers. We will of course write it first πŸ™‚

Creating a handler for lcd module is quite simple, we already have an event parser. What we need to do is move it from server module to handler.
Handler source code:

local handler = {}

handler.lcd = nil

handler.handle = function(socket, message)   
    response = false
    if message ~= nil and message.event ~= nil then        
        if message.event == 'lcd.cmd' then
            handler.lcd.drv.command(
                message.parameters.data,
                message.parameters.enable + 1
            )
            response = true
        end
        if message.event == 'lcd.char' then
            handler.lcd.drv.write(
                message.parameters.data,
                message.parameters.enable + 1
            )
            response = true
        end
        if message.event == 'lcd.content' then          
            for k,v in pairs(message.parameters.content) do
                handler.lcd.set_xy(0, k-1)
                handler.lcd.write(v)
            end
            if handler.lcd.mode == 'buffered' then
                handler.lcd.flush()
            end
            response = true
        end
    end

    return response
end

return handler;

To make it complete lets look at new main.py that use this handler:

print ("core ready")

drv = require("lcd_hd44780_i2c")
lcd = require("lcd_hd44780")
lcd_handler = require "lcd_hd44780_handler"
network_message = require "network_message"

drv.addr = 0x20
drv.pins = {
    RS= 4,
    E1= 5,
    E2= 6,
    DB4= 0,
    DB5= 1,
    DB6= 2,
    DB7= 3,
}

--lcd.lcd(drv, 40, 4)
lcd.buffered(drv, 40, 4)
lcd.cursor_visible = 1
lcd.init()


lcd_handler.lcd = lcd

svr = net.createServer(net.UDP)
svr:on('receive', function(socket, message) 
    message = network_message.decodeMessage(message) 
    lcd_handler.handle(socket, message)
end)  

svr:listen(PORT)

I do not think we have something new here. So lets move on.

LCD and Temperature

This is something new. Run two workers at one node. With handlers it is as easy as pie.

print ("core ready")

network_message = require "network_message"
drv = require("lcd_hd44780_i2c")
lcd = require("lcd_hd44780")
temp = require "18b20"

temp_handler = require "18b20_handler"
lcd_handler = require "lcd_hd44780_handler"

--setup LCD
drv.addr = 0x20
drv.pins = {
    RS= 4,
    E1= 5,
    E2= 6,
    DB4= 0,
    DB5= 1,
    DB6= 2,
    DB7= 3,
}

lcd.buffered(drv, 40, 4)
lcd.cursor_visible = 1
lcd.init()

-- setup 18b20
pin = 3
temp.init(pin)

--attach lcd to handler
lcd_handler.lcd = lcd

-- atach 18b20 to nahdler
temp_handler = require "18b20_handler"
temp_handler.node = temp

-- start server
svr = net.createServer(net.UDP)
svr:on('receive', function(socket, message) 
    message = network_message.decodeMessage(message) 
    lcd_handler.handle(socket, message)
    temp_handler.handle(socket, message)
end)  

svr:listen(PORT)

This is nice, setting up workers takes more code that using them πŸ™‚ But if you look closely you will see that there is one more aspect that can be improved.
Server code is in main file but we can move it to another module.

server listener module

Server that listens for messages is a common part of many projects but we still have to create it manually. Lets change it!
Our server_listener require UDP port so we will pass it. Harder part is with handlers. We need a method that would add handler to server. And server must loop over all handlers and pass socket and message to them.
We will use this opportunity to introduce module names. Why?
When adding handler to server we will set its name, like display, thermometer and such. And later we will use them to target messages to selected device on node.
Code for listener:

local listener = {}

listener.handlers = {}
listener.svr = net.createServer(net.UDP)

listener.add = function(name, handler)
    listener.handlers[name] = handler
end

listener.start = function(port)
    listener.svr:on('receive', function(socket, message) 
        message = network_message.decodeMessage(message)
        if message ~= nil then
            for name, handler in pairs(listener.handlers) do   
                handler.handle(socket, message)                    
            end
        end      
    end)
    
    listener.svr:listen(port)
    print("server online")
end

return listener

It is not a long code but do what we want.

Using listener

Nothing is better in describing code than source code:

print ("core ready")

network_message = require "network_message"

drv = require("lcd_hd44780_i2c")
lcd = require("lcd_hd44780")
temp = require "18b20"

server_listener = require "server_listener"
temp_handler = require "18b20_handler"
lcd_handler = require "lcd_hd44780_handler"

--setup LCD
drv.addr = 0x20
drv.pins = {
    RS= 4,
    E1= 5,
    E2= 6,
    DB4= 0,
    DB5= 1,
    DB6= 2,
    DB7= 3,
}

lcd.buffered(drv, 40, 4)
lcd.cursor_visible = 1
lcd.init()

-- setup 18b20
pin = 3
temp.init(pin)

--attach lcd to handler
lcd_handler.lcd = lcd

-- atach 18b20 to nahdler
temp_handler = require "18b20_handler"
temp_handler.node = temp

-- add handlers to listener
server_listener.add("lcd", lcd_handler)
server_listener.add("thermometer", temp_handler)

-- run server
server_listener.start(PORT)

It is almost the same as in previous example. The difference is in not using server manually but add handlers and run server. Server listen to UDP packets and call handlers to execute event.

Summary

Second refactoring completed. From now on we will use listener and handlers. Our logic looks like this:

MODULE/WORKER — code that do something, get temperature, display char on display and so
HANDLER — code that get UDPmessage and handle events. Event may be get temperature and return it, display full content on display
LISTENER — server that get handlers, receive packet from network, parse it into message and call all handlers with socket and message parameters

All this imply one important rule: creating new module force us to create handler for it.

Code is in boilerplate ready for use.

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