NodeMCU event handlers – 18b20 as good start

We have a nice NodeMCU server for LCDs. But there is one catch. We can control one LCD or all connected LCDs. But imagine that we have two LCDs and we want to display different content on each.

In message we can set node name but not device connected to it. So there is case with 1+ screens or broader with many devices connected to one Node.

Another problem: we start LCD server and we want lets say start a server with temperature sensor. We cannot. Lcd would take port and that is all. To do something with this we will introduce event handlers and listers. Big and useful change.
Each module would have an event handler, a function that gets message and do something with it. Returns true when it can handle this event and false when not.

I mention temperature sensor, currently we have none so first lets create one.

Temperature sensor

I’m talking about sensor but what will we use? I have a spare 18b20 – ideal for this. Logic behind this module is very straightforward, respond to events asking for temperature. But that is good, we can focus on handler and listener idea.

Wiring

I’m using 18B20 connected via 1-wire bus to NodeMCU. Pin that I use is D3

1 ----GND
2 -+-- D ---- D3 @ nodemcu
   |
   [] - resistor 470
   |
3 -+-- +5 

Displaying temperature

Here you can find a nice example of how to get and display temperature. We will use it and adapt to our case.
I have moved parts into functions, do some cleaning and made a module from it.

local sensor = {}

sensor.pin = nil
sensor.addr = nil
sensor.device_id = {0x10, 0x28}

sensor.init = function(pin)
    sensor.pin = pin
    ow.setup(sensor.pin) 
    count = 0
    repeat
      count = count + 1
      addr = ow.reset_search(pin)
      addr = ow.search(pin)
      tmr.wdclr()
    until (addr ~= nil) or (count > 100)
    if addr == nil then
        print("Device not found")
    else
        sensor.addr = addr
        crc = ow.crc8(string.sub(sensor.addr,1,7))
        if crc == sensor.addr:byte(8) then
            if (not sensor.is_device(sensor.addr:byte(1))) then
                print("Device is not a DS18B20 family device.")
                sensor.addr = nil
            end
        else
            print("CRC invalid - not a device ?")
            sensor.addr = nil
        end
    end

    return sensor.addr
end

sensor.is_device = function(data)
    for i, v in ipairs(sensor.device_id) do        
        if v == data then
            return true
        end
    end

    return false
end

sensor.get_temperature = function(round)
    ow.reset(sensor.pin)
    ow.select(sensor.pin, sensor.addr)
    ow.write(sensor.pin, 0x44, 1)
    present = ow.reset(sensor.pin)
    ow.select(sensor.pin, sensor.addr)
    ow.write(sensor.pin,0xBE,1)
    data = string.char(ow.read(sensor.pin))
    for i = 1, 8 do
        data = data .. string.char(ow.read(sensor.pin))
    end    
    crc = ow.crc8(string.sub(data,1,8))
    if crc == data:byte(9) then
        t = (data:byte(1) + data:byte(2) * 256) * 625
        t1 = t / 10000
        t2 = t % 10000        
        if round ~= nil and round ~= false then return t1 else return t1.."."..t2 end
    else
        return nil
    end       
end

return sensor;

Usage:

pin = 3

temp = require "18b20"

temp.init(pin)
print(temp.get_temperature())

Function init takes pin as parameter, setup this pin as ow bus and search for device that is 18b20. When it is found function store addr for future use.
This module has one function for user: get_temperature. If you pass parameter it will round temperature data. Without it raw value is returned.

Events

Our module has only one event that will listen to: temperature.current.
As I mention earlier we gonna use event handlers. But what it is?
I will use temperature module to show this. We have a main module: 18b20, now we gonna create another module to handle events: 18b20_handler. This module will receive main class as parameter, so it would know on what node it would execute events.
Okey, we have the Node, we have the handler to call events on Node but what would call this handler ?
Good question 🙂 We need something to do this, like a server that would listen to incoming connections and pass message with event to all handlers. We need of course register handlers in listener.
To sum it up:

Node -[node]-> Handler <-[event]- server <- message
                  |
               KABOOM

Lets start from handler. In our case, temperature sensor handler, we handle only one event: temperature.current. So our handler should look like this (nice file 18b20_handler.lua):

local handler = {}

handler.node = nil
handler.round = nil

handler.handle = function (socket, message)
    response = false
    if message ~= nil and message.event ~= nil then
        if message.event == 'temperature.current' then
            r = handler.round
            if type(message['parameters']) == 'table' and type(message.parameters['round'] ~= nil)then
                r = message.parameters['round'] 
            end
            message = network_message.prepareMessage()
            message.response = handler.node.get_temperature(r)
            network_message.sendMessage(socket, message)
            response = true
        end 
    end

    return response
end

return handler

This is a nice handler:) It react to an event and can round variable if proper attribute in message is set. It gets socket and message. Response is transferred back to source via socket.

Another question: how to use it? And answer: my main.la looks like this:

print ("core ready")
network_message = require "network_message"

pin = 3

temp = require "18b20"
temp.init(pin)
temp_handler = require "18b20_handler"
temp_handler.node = temp

--print(temp.get_temperature())

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

svr:listen(PORT)

Simple is it? We start a server and pass received message to handler. To test it all I used such python script:

import socket
import json

address = ('192.168.1.255', 5053)

packet = {
    'protocol': 'iot:1',
    'node': 'computer',
    'chip_id': 'd45656b45afb58b1f0a46',
    'event': 'temperature.current',
    'parameters': {
        # 'round': False
    },
    'targets': [
        'ALL'
    ]
}

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
msg = json.dumps(packet)
print(msg)
s.sendto(msg.encode(), address)
#
(buf, ip) = s.recvfrom(1024)
print("from %s: %s" % (ip, buf.decode()))

And it is all working:

{"chip_id": "d45656b45afb58b1f0a46", "targets": ["ALL"], "parameters": {}, "event": "temperature.current", "protocol": "iot:1", "node": "computer"}
from ('192.168.1.147', 5053): {"chip_id":194799,"protocol":"iot:1","node":"node-40x4","targets":["ALL"],"event":"","response":"26.3750"}

Summary

First part of refactoring is done. We have a nice event handler for temperature module. Next we need to do handler a for lcd_hd44780 module. After that we will do something with server (event listener).

Module 18B20 added to boilerplae

Advertisements

One comment

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