NodeMCU, broadcast and an event

Goal for this post: program ESP to send event. We will broadcast an event packet into network and read it. But lets start from beginning. What is broadcast?
Broadcast is a method of transferring a message to all recipients simultaneously [Wikipedia].
Contrary to unicast when you send data to one and only one recipient. But how to send something everywhere? Broadcast address are IP one that ends with 255, for a base network 192.168.1.0/24 broadcast address is 192.168.1.255.
One important aspect is that broadcast use UDP not TCP.  In short words, TCP connect and provide reliability and UDP send packet and forget but is faster.

First we will write simple server and client application. Of course in Python 🙂

Broadcast client and server

I hope you know something about network programming, sockets and client-server architecture.

Our server script is typical Python implementation with one difference:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind(('', 5053))

print("waiting for messages")
while 1:
    try:
        message, address = s.recvfrom(1024)
        print("Message from %s: %s" % (address, message.decode()))
        s.sendto("OK!".encode(), address)
    except:
        raise

This line:

s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

enable our socket to listen to broadcast packets. Without it all such packets are dropped. We must enable it manually.

And client:

import socket
import random

address = ('<broadcast>', 5053)

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(("lucky number is: "+str(random.randint(1, 100))).encode(), address)

(buf, ip) = s.recvfrom(1024)
print("from %s: %s" % (ip, buf.decode()))

See that we are using special address. You may specify address manualy, for example 192.168.1.255 or even 255.255.255.255.

This server we will use when playing with NodeMCU.

Broadcast from ESP

Back to NodeMCU and Lua. Get boilerplate.
For start we will transmit one broadcast packet to see if it works. Open main.lua and copy:

print ("core ready")

srv=net.createConnection(net.UDP, 0)
srv:connect(5053, wifi.sta.getbroadcast())

srv:send("From: "..NODE_ID )

srv:close()

We are creating UDP socket and connect it to broadcast address. It is very nice that we may get broadcast IP for our network by wifi.sta.getbroadcast().

And that is it. Simple code to send packet. Our server can receive it.

Buttons

Lets connects eight buttons to NodeMCU board. We have lots of GPIOs. And first fail, some GPIOs are used internally… D9 and D10 are connected to serial, D4 to led and in my case D0 didn’t work.. so we have only 6 pins! Plan changed, 4 buttons as directions and two as actions.

D1 __/__.
D2 __/__|
D3 __/__|
        |
D5 __/__|
D6 __/__|
D7 __/__|
        |
GND ____|

See that we connect GPIOs via buttons to the ground, and in code we check for 0 value. It was something new for me.
Pins that we used are: D1, D2, D3, D5, D6 and D7. To call function when button is pressed we use triggers.
Our packet will consist of few fields: chip_id, node and event. Chip_id is set to node.chipid() – this is current device internal id. Next is node=NODE_ID, this one is from parameters-device. And finally we have event, this one is our event name like event.up, event.down and so.
There is another small trick we are using, it is lock handler. When button is pressed we check for lock and if we can we send data and start timer that will release lock. Lock time is defined in parameters.lua as BTN_TIMEOUT = 500.
This lock allows us to send event only after half a second, small anti-flood protection.
Last thing defined in parameters is SERVER_PORT = 5053. This one define port we choose for communication.
Lets see the code:

print ("core ready")

lock = false

data = {}
data.event = 0
data.chip_id = node.chipid()
data.node = NODE_ID

srv=net.createConnection(net.UDP, 0)
srv:connect(SERVER_PORT, wifi.sta.getbroadcast())

We set lock to false and prepare data object. This one will be send. Next we initialize UDP socket.

function sendEvent(event)
    if lock then
        return
    else
        lock = true
    end 

    data.event = event
    ok, json = pcall(cjson.encode, data)
    if ok then
      print (json)
      srv:send(json)
    end
    tmr.alarm(0, BTN_TIMEOUT, 0, resetLock)
end

This function is used to broadcast event. We call it from triggers attached to buttons. Event attribute is a string with event name. First we set our lock to true, next event name is assigned to object, object is converted to json and we send it.
Then we start a timer with unlock.

function resetLock()
    lock = false
end

This code set our lock to false – release lock

function btnDown()
   sendEvent("event.down")
end  

function btnLeft()
   sendEvent("event.left")
end  

function btnUp()
   sendEvent("event.up")
end  

function btnRight()
   sendEvent("event.right")
end  

function btnAction1()
   sendEvent("event.action1")
end  

function btnAction2()
   sendEvent("event.action2")
end  

gpio.mode(1, gpio.INT, gpio.PULLUP)
gpio.mode(2, gpio.INT, gpio.PULLUP)
gpio.mode(6, gpio.INT, gpio.PULLUP)
gpio.mode(7, gpio.INT, gpio.PULLUP)

gpio.mode(3, gpio.INT, gpio.PULLUP)
gpio.mode(5, gpio.INT, gpio.PULLUP)

gpio.trig(1, 'down', btnDown)
gpio.trig(2, 'down', btnLeft)
gpio.trig(7, 'down', btnUp)
gpio.trig(6, 'down', btnRight)

gpio.trig(5, 'down', btnAction1)
gpio.trig(3, 'down', btnAction2)

We setup pins and attach functions that will be triggered upon clicking.

Summary

We made small circuit with 6 buttons, when button is pressed event is broadcasted and our python script can read it and display.

Advertisements

2 comments

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