Light control with NodeMCU v3

The goal is to control all my lights with Domoticz and my wall switches.

A NodeMCU v3 will be used as the controller, a PCF8574 board to extand I/O, a 4 relays board to switch lights on/off and a mini AC-DC converter.

ESPeasy is the firmware of the main board and Domoticz the home automation manager.

Home configuration

Lights in the home are simple or 3-way switches. By the way, all cables from live, neutral and between switches and lights are gathered in a connecting box as the following: wiring_present

The house is divided into two part, one for living rooms with 6 lights (2 are peers) and one for sleeping rooms with 7 independant lights. Thus there are two boxes.

The idea is to implement all the stuff in these boxes. The configuration will be the following one : wiring_futur

I will only explain living rooms configuration.

Hardware

As NodeMCU as few GPIO pin available, PCF8574A boards will be used to expand I/O ports. For living rooms, one board is mandatory whereas in sleeping rooms two boards are mandatory.

GPIOs 4 (D1) and 5 (D2) will be used as SDA and SCL. To be sure, SDA and SCL lines are pulled-up with a 4,7k resistor (not mandatory for one board on the line).

Ports of the PCF8574 are open drain. During boot, ESPeasy initialize output as low and input as high (must be pulled-up). By the way, logic on the relays board is inverted, 0 to active a relay and 1 to deactivate.

To control relays, output pins are directly connected to input pins of the relay board, and PCF8574 pins are activated (send 1).

To monitor switches, input pins are connected to ground through switches and to a pull-up resistor (33k).

Software

Lights have to :

ESPeasy

Devices

There are 4 devices for input on PCF8574 and one dummy device for variables.

To control relays, there is no need to add devices, the control is done with the command pcfgpio, port, value.

Rules

After some tries and fails, rules are divided into 2 files.

Rules1.txt to control boot commands :

on System#boot do
pcfgpio,65,1
pcfgpio,66,1
pcfgpio,67,1
pcfgpio,68,1
taskvalueset 5,1,0
taskvalueset 5,2,0
taskvalueset 5,3,0
taskvalueset 5,4,0
Delay, 1000
Publish domoticz/in, {"command": "setuservariable", "idx": 1, "value": [EtatRelais#entree]}
Publish domoticz/in, {"command": "setuservariable", "idx": 2, "value": [EtatRelais#salon]}
Publish domoticz/in, {"command": "setuservariable", "idx": 3, "value": [EtatRelais#salleamangercuisine]}
Publish domoticz/in, {"command": "setuservariable", "idx": 4, "value": [EtatRelais#cellier]}
endon

Rules2.txt to manage buttons and commands :

on BoutonEnt#etat do
event,Toggleentree
endon

on BoutonSal#etat do
event,Togglesalon
endon

on BoutonSmc#etat do
event,Togglesalleamangercuisine
endon

on BoutonCel#etat do
event,Togglecellier
endon

on Toggleentree do
if [EtatRelais#entree]=1
pcfgpio,65,1
taskvalueset 5,1,0
else
pcfgpio,65,0
taskvalueset 5,1,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 1, "value": [EtatRelais#entree]}
endon

on Togglesalon do
if [EtatRelais#salon]=1
pcfgpio,66,1
taskvalueset 5,2,0
else
pcfgpio,66,0
taskvalueset 5,2,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 2, "value": [EtatRelais#salon]}
endon

on Togglesalleamangercuisine do
if [EtatRelais#salleamangercuisine]=1
pcfgpio,67,1
taskvalueset 5,3,0
else
pcfgpio,67, 0
taskvalueset 5,3,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 3, "value": [EtatRelais#salleamangercuisine]}
endon

on Togglecellier do
if [EtatRelais#cellier]=1
pcfgpio,68,1
taskvalueset 5,4,0
else
pcfgpio,68,0
taskvalueset 5,4,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 4, "value": [EtatRelais#cellier]}
endon

By the way, there are some trouble as the board reboot sometimes when a button is pushed.
Is it a problem with the power supply which is under rated or just to cheap ? Or something else...
First step, add a timer to reboot the board every 6h.
Second step, try an other power supply.

Domoticz

Devices

4 virtual switches are created :

UserVariables

To handle states of lights, a user variable is used :
- EtatLumiereentree as integer - EtatLumieresalon as integer - EtatLumieresalleamangercuisine as integer - EtatLumierecellier as integer

Events

Events are coded in dzVents language.

They are divided into 2 files.

lights_cmd to send command to ESPeasy from virtual switches :

return {
    on = {
        devices = {
            'Lumière*'
        }
    },
    execute = function(domoticz, device)
        local relaisPav = { entree = true, salon = true, salleamangercuisine = true, cellier = true }
        local relaisCha = { wc = true, salledebain = true, couloir = true, jaouen = true, leonie = true, parents = true }
        local piece = string.lower(domoticz.helpers.stripChars(device.name:gsub('Lumière ','')))
        if relaisPav[piece] then
            domoticz.openURL('http://Relais_PaV/control?cmd=event,Toggle'..piece)
        elseif relaisCha[piece] then
            domoticz.openURL('http://Relais_Cha/control?cmd=event,Toggle'..piece)
        end
    end
}

lights_state to change state of virtual switches after a local event :

return {
    on = {
        variables = {
            'EtatLumiere*'
        }
    },
    execute = function(domoticz, variable)
        -- UserVariable - Switch
        local groupVarSwi = {entree = 4, salon = 5, salleamangercuisine = 6, cellier = 7, wc = 11,
            couloir = 12, salledebain = 13, leonie = 14, jaouen = 15, parents = 16 }
        --print('variable.value : '..variable.value)
        --print('variable.id : '..variable.id)
        local piece = string.lower(domoticz.helpers.stripChars(variable.name:gsub('EtatLumiere','')))
        if variable.value == 1 and domoticz.devices(groupVarSwi[piece]).state == "Off" then
            domoticz.devices(groupVarSwi[piece]).switchOn().silent()
        elseif variable.value == 0 and domoticz.devices(groupVarSwi[piece]).state == "On" then
            domoticz.devices(groupVarSwi[piece]).switchOff().silent()
        end
    end
}

In order to handle special character, a generic dzVents helpers function is created :

return {
   helpers = {
        -- Normalize a given string
        stripChars = function(str)
            local tableAccents = {}
            tableAccents["à"] = "a"
            tableAccents["ç"] = "c"
            tableAccents["è"] = "e"
            tableAccents["é"] = "e"
            tableAccents["ê"] = "e"
            tableAccents["ù"] = "u"
            tableAccents["\\"] = ""
            tableAccents["/"] = ""
            tableAccents[" "] = ""
            local normalizedString = ''

            for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
                if tableAccents[strChar] ~= nil then
                    normalizedString = normalizedString..tableAccents[strChar]
                else
                    normalizedString = normalizedString..strChar
                end
            end
            return normalizedString
        end,
   }
}
Switches

Révisions