NodeMCU and light detector – part 4: a module and a handler

We have a nice node that detects light, it works in toilet @ my work 🙂 It does it jobs almost perfectly. There is still a hack for it – leave a light on when leaving the toilet. I have an idea to fix this but first, let’s reforge a lousy code into a module and a handler. After refactor we may safely add it to the NodeMCU boilerplate.

This is going to be our second data node for display.  It will show area lightness and with conjunction with a time, we can tell if an artificial light is on or off.

NodeMCU boilerplate @ GitHub


We have already done this section so this time only quick review. The node supports three events. Two are broadcasted into the network on light change, it is detect.light and detect.dark. And third is a state message, in response current status is returned.


Copy and paste from previous part:

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


Finally, something that is new. I named this module light_detector. Using PIR as a template and code from the previous part, we merge it into the new code:

local light_detector = {}
light_detector.__index = light_detector
light_detector.states = {
    "detect.light", "detect.dark"

setmetatable(light_detector, {
    __call = function (cls, ...)

function, pin, interval)
    local self = setmetatable({}, light_detector) = pin
    self.tmr = tmr.create()
    self.last_state = nil
    self.current_state = 0
    gpio.mode(pin, gpio.INPUT)
    if interval == nil then interval = 1000 end

    self.tmr:register(interval, tmr.ALARM_AUTO, function()
        self.current_state =
        if self.last_state ~= self.current_state then
            self.last_state = self.current_state           
            message = network_message.prepareMessage()            
            message.event = light_detector.states[self.current_state + 1]           
            network_message.sendMessage(socket, message)
    return self

function light_detector:get_state()
    return light_detector.states[self.current_state + 1]
return light_detector

The heart is a timer that checks for state and sends the message only when it changes. I tried and used gpio.trig but the effect was bad. The sensor is very sensitive and it spammed with state changes, seems timer is better in this case.

Usage of this:

network_message = require "network_message"
light_sensor = require "light_detector"
send_socket = net.createConnection(net.UDP, 0)
send_socket:connect(PORT, wifi.sta.getbroadcast())

handler = pir_handler(sensor)
light = light_sensor(send_socket, 4)

The job is almost done, we need a handler for the message asking for the current state.
It is almost the same as with PIR so let’s copy it and change slightly:

local light_detector_handler = {}
light_detector_handler.__index = light_detector_handler

setmetatable(light_detector_handler, {
    __call = function (cls, ...)

    local self = setmetatable({}, light_detector_handler)
    self.node = node
    return self

function light_detector_handler:handle(socket, message)
    response = false
    if message ~= nil and message.event ~= nil then
        if message.event == 'light.state' then
            message = network_message.prepareMessage()
            message.response = self.node:get_state()
            network_message.sendMessage(socket, message)
            response = true


    return response

return light_detector_handler


Another sensor is ready to be used. We can detect when a light in a room goes below some level. The event is broadcasted into the network for any devices to react or an LCD to display 🙂



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 )

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