NodeMCU and light detector – part 1

We have a small problem at the office. This problem is only one toilet. Additionally not all rooms can see it and know if it is busy or not. So quite often someone runs and slams into closed door.
Such runner has two options, form or join queue and wait or go back to desk and return later.
It would be much better for all of us if we could check toilet status from our desks.
How to do it? Simplest way is to detect if light is enabled. Hopefully this would be enough. If not I have another idea 🙂
As light detector I’m going to use lm393 light sensor and as the brain NodeMCU v3.

Download code

Planing and wiring

Three events are happening in this system. First is detecting the light, second is detecting dark. And third we should be able to ask sensor about current state. Nothing hard 🙂

I’m gonna use broadcasting approach, so light/dark events will be broadcasted into network for anyone to listen. And our system will respond for event state.
What else? Ah! Right, after booting NodeMCU must broadcast detected state.

Sample message for light/dark event:


Ask about state:

    "protocol": "iot:1",
    "node": "computer",
    "event": "state",
    "targets": [



It is very good that light detector can operate on both 5V and 3.3V. This time 3.3V is preferred because we are reading signal and 5V could damage our board.

NodeMCU    Light Detector
D2 ------------ D0
3V ------------ VCC
G  ------------ GND

As simple as it can be. On detector you will find a potentiometer. Adjust it properly.


Start point: boilerplate. We will slightly improve module network_message. How? Just by adding one function:

network_message.sendMessage = function(socket, message)
    ok, json = pcall(cjson.encode, message)
    if ok then       
        return true 
    return false

Sending message to socket is something that is gonna be used frequently. Before send, lua table is converted to json string.

Back to main code, main.lua:

gpio.mode(2, gpio.INPUT, gpio.PULLUP)
network_message = require "network_message" 
last_state = nil
states = {}

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

We import module network_message, declare some variables, setup pin and initialize broadcast socket. This socked is used to send messages.

svr = net.createServer(net.UDP) 
svr:on("receive", function(socket, message)
    message = network_message.decodeMessage(message)
    if message ~= nil and message.event ~= nil then
        if message.event == "state" then     
            data = network_message.prepareMessage()
            data.response = states[last_state]   
            network_message.sendMessage(socket, data)                            


Here we create a server that listens for incoming messages. After receiving message with state event it responds with light state.

function sendEvent(event)  
    data = network_message.prepareMessage()
    data.event = event   
    network_message.sendMessage(srv, data)    

tmr.alarm(2, 500, 1, function()  
    current_state =
    if last_state ~= current_state then        
        last_state = current_state

Finally we have function sendEvent to create and send message and timer that checks for light state in half second intervals. If change in state is detected proper message is broadcasted.


We have a nice detector. But currently we have nothing that use it. And you know what does it mean – part 2 on the way 😀
I found one small inconvenience, we can add only one AP credentials. It is something that will soon change. Why ? It is much easier to set two AP, work and home, than change it in code each time.



Leave a Reply

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

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

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s