Clap your hands – sound sensor

Not so long ago I ordered a sound detector based on LM393. Today I thought that maybe it is time to use it. How? Maybe we can do a light-clap module.

With each clap relay will change its state. It is not complicated and may be a good fun πŸ™‚ Naturally, we will use NodeMCU and LUA.

Planing

I have a free 1 channel relay but anyone can be. A task is simple with each detected clap change state on the channel(s).
Funny thing because it is a local workflow, not network and our framework doesn’t support this. But it shouldn’t be hard to implement such ability. Maybe even add a custom callback to a handler? It would be ideal but require a change in all handlers… so what πŸ˜€ let’s get to work and add something new to NodeMCU boilerplate!

Having a user callback at any event is something very good and very useful.

Wiring

The sensor has 3 pins. When selected threshold is reached it gives a high signal on OUT pin. Wire it to any pin on NodeMCU board, in my case it is D1.
Single channel relay is hooked to D4.

Handler for sound sensor

It is similar to PIR sensor. Its output is triggered during some interaction, so we have a good template.
Let’s copy it, rename to sound_detector and change to:

local sound_sensor = {}
sound_sensor.__index = sound_sensor
sound_sensor.states = {
    "sound.detect",    
}

setmetatable(sound_sensor, {
    __call = function(cls, ...)
        return cls.new(...)
    end,
})

function sound_sensor.new(socket, pin)
    local self = setmetatable({}, sound_sensor)
    self.pin = pin
    gpio.mode(self.pin, gpio.INT)
    self.socket = socket

    local function alarm(level)           
        if (level == 1) then
            print(tmr.now())                    
            print(sound_sensor.states[1])     
            print("PING "..level)    
        end
    end

    gpio.trig(self.pin, "up", alarm)
     
    return self
end

return sound_sensor

In main.lua file:

sd = require "sound_detector"

send_socket = net.createConnection(net.UDP, 0)

sound_detector = sd(send_socket, 1)

This works in a strange way or my sensor is broken. Adjusting trigger level is very limited to around 1/4 of the potentiometer. Over this, I have high signal all the time even when there is no sound in the area.
If I set potentiometer slightly below trigger level it reacts to clapping but it is detected few times. Another problem to solve.
We can try and set the 0.5s delay between reactions. Maybe this will solve a multi-activation.
To achieve this we will use the timer in semi-automatic mode. It is a timer which we start manually.
So in function new add:

    self.hold = false
    self.timer_reset = tmr.create()
    self.timer_reset:register(500, tmr.ALARM_SEMI, function()        
        self.hold = false
    end)

and change function alarm to:

        if (level == 1 and self.hold == false) then
            self.hold = true                  
            print(sound_sensor.states[1])      
            self.timer_reset:start()    
        end

This time it is much better. We have no multiple activations.
To have a nice worker we need to use a socket to broadcast an event. But we have done it many times earlier, so let’s add new option – a custom callback.
It would be nice if we could pass a function that is triggered along with message send. Or maybe take the next step and allow the socket to be nil? With nil we wouldn’t send the message only trigger the callback.
This is nice, let’s implement it.

First, we need to change the constructor and add callback function as the last parameter. Next, we need single if statement with the function call:

function sound_sensor.new(socket, pin, callback)
    local self = setmetatable({}, sound_sensor)
    self.pin = pin
    gpio.mode(self.pin, gpio.INT)   
    self.socket = socket
    self.callback = callback

    self.hold = false
    self.timer_reset = tmr.create()
    self.timer_reset:register(500, tmr.ALARM_SEMI, function()        
        self.hold = false
    end)

    local function alarm(level)           
        if (level == 1 and self.hold == false) then
            self.hold = true   
            print("PING "..level)
            if (self.socket ~= nil) then
                message = network_message.prepareMessage()
                message.event = sound_sensor.states[1]
                network_message.sendMessage(self.socket, message)     
            end
            if (self.callback) then
                self.callback(sound_sensor.states[1])
            end
            self.timer_reset:start()
        end
    end

    gpio.trig(self.pin, "up", alarm)
     
    return self
end

Yep, it is so simple, why didn’t I thought about it earlier?
To see how it works change code in main.lua file:

sound_detector = sd(send_socket, 1, function(event)
    print(event)
end)  

Good. We have a nice piece of code.

Clap and there is a light!

Back to the main idea, to have a noise controlled light switcher πŸ˜€
With callback it is quite easy to implement:


gpio.mode(3, gpio.OUTPUT, gpio.PULLUP)
gpio.write(3, gpio.HIGH)
local bum = false

sound_detector = sd(send_socket, 1, function(event)
    if bum then
        gpio.write(3, gpio.HIGH)
    else
        gpio.write(3, gpio.LOW)                
    end
    bum = not bum
end)  

Do not forget to plug some 230V device to relay, and be VERY careful with this!

Other workers

Yey, another big refactor. We will add an ability to set a custom callback to all event based actions. Or maybe to all? I wonder if we need the custom thing when the node receives a message and trigger something?
Hmm.. it may be nice.
Ok. Let’s change workers and handlers, one by one πŸ™‚ All changes are going to be sent to boilerplate

Summary

Another small step in hardware and big in software. We refactored almost all handlers and workers. And it gave us an ability to set a callback function. It is very useful.

As a side effect, we may use noise to control the light πŸ™‚ – it is an awesome side effect!

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