The Desired function
The software part of it was pretty easy to write. The hardware "end product" though has to be very small to fit into the normal wall sockets. It would be added behind a wall switch and contain an ESP8266 and a power supply all in the tiny box.In the future I aim to design a special PCB combined with a very small power supply so it can easily fit inside a wall socket box.
The way I wanted this to work is that the switch only flips a virtual dummy switch inside of Domoticz and coupled to that, any other action can take place. That way, I can program the ESP8266 with the correct Domoticz switch IDX once and then when I wish to change the function, I can always do so from Domoticz and won't need to change what's running on the ESP8266 module.
In my case I used a ESP-12 in this case. I recently received this version and when I got it to work in my breadboard I used that setup. It doesn't matter which ESP8266 module you wish to use yourself, just be sure to change the pin numbers accordingly.
How it works
My code is currently configured to use two GPIO pins of the ESP8266. If you wish to run multiple switches using a single ESP8266 you will need to use as many GPIO pins as you want switches.The wiring of the switch is easy. In my case the switch had a negative/ground in and two outputs, which get switched. I wired the negative/ground to the switch input and wired the outputs to the GPIO pins on the ESP8266.
The way it's setup is that the GPIO pins will be HIGH by default (The ESP8266 will pull it high internally) but when the negative/ground is switched on the circuit will be connected and so it will go LOW because now it's connected to ground. That state change is what we are looking for. The pin will remain LOW until the switch is flipped again. Each position will keep it's respective HIGH or LOW.
The code automatically detects when a switch was switched and when it's still in the same state so that it won't continuously hammer Domoticz. That does mean that when the action associated with the switch is run manually on the web interface of Domoticz, it could be that nothing happens.
Example: If you turn the switch on using the physical switch, but then turn it off using the Domoticz interface, the actions associated to this will be carried out, but the physical switch will remain in it's position. When you then flip the switch, you will be switching it to off again, so nothing will happen. To turn the light on, you will have to flip the physical switch to on, and the connection actions will take place.
With that in mind, let's continue!
ESP8266 code
This is version 0.2 of my test code, so don't be too harsh. It's working perfectly in my test setups though! Both switches are implemented in the code so make sure to change the pins, switch IDX, Domoticz IP, Wireless LAN, etc.
Currently the alarm loop is set to read the switch state every 0.1 of a second. This makes the reaction of the switch almost instant, but going through WiFi, then through Domoticz to another WiFi ESP8266 which then switches the light on has a little time delay. In my testing this delay was around 0.5 seconds or so, quite acceptable for my purpose.
Be sure to change the pins to your ESP layout!
update 2015-03-03
I updated the code so we now have variables for the most common settings and also added the code for connecting two separate switches. I also removed some logging so it won't keep spamming the console with messages.
Currently the alarm loop is set to read the switch state every 0.1 of a second. This makes the reaction of the switch almost instant, but going through WiFi, then through Domoticz to another WiFi ESP8266 which then switches the light on has a little time delay. In my testing this delay was around 0.5 seconds or so, quite acceptable for my purpose.
Be sure to change the pins to your ESP layout!
update 2015-03-03
I updated the code so we now have variables for the most common settings and also added the code for connecting two separate switches. I also removed some logging so it won't keep spamming the console with messages.
GPIOpin1 = 5
GPIOpin2 = 6
DomoticzIP = "10.10.128.14"
switchidx1 = 42
switchidx2 = 42
print "Vars set"
gpio.mode((GPIOpin1), gpio.INPUT, gpio.PULLUP)
gpio.mode((GPIOpin2), gpio.INPUT, gpio.PULLUP)
print "Pins set"
wifi.setmode(wifi.STATION)
wifi.sta.config("Q-LAN","yoloyoloyolo")
print "WiFi set"
print "Reading button states in loop"
tmr.alarm(0, 100, 1, function()
if gpio.read(GPIOpin1) == 0 then
-- print ("Button 0 = On")
if Switch0 == "On" then
-- print ("Switch already on")
else
Switch0 = "On"
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
conn:connect(8080,(DomoticzIP))
conn:send("GET /json.htm?type=command¶m=switchlight&idx=" .. (switchidx1) .. "&switchcmd=On&level=0 HTTP/1.1\r\n")
conn:send("Host: " .. (DomoticzIP) .. "\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
print ("SWITCHED Switch0 ON")
end
else
-- print ("Button 0 = Off")
if Switch0 == "Off" then
-- print ("Switch already Off")
else
Switch0 = "Off"
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
conn:connect(8080,(DomoticzIP))
conn:send("GET /json.htm?type=command¶m=switchlight&idx=" .. (switchidx1) .. "&switchcmd=Off&level=0 HTTP/1.1\r\n")
conn:send("Host: " .. (DomoticzIP) .. "\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
print ("SWITCHED Switch0 OFF")
end
end end )
tmr.alarm(2, 100, 1, function()
if gpio.read(GPIOpin2) == 0 then
-- print ("Button 0 = On")
if Switch1 == "On" then
-- print ("Switch already on")
else
Switch1 = "On"
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
conn:connect(8080,(DomoticzIP))
conn:send("GET /json.htm?type=command¶m=switchlight&idx=" .. (switchidx2) .. "&switchcmd=On&level=0 HTTP/1.1\r\n")
conn:send("Host: " .. (DomoticzIP) .. "\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
print ("SWITCHED Switch1 ON")
end
else
-- print ("Button 0 = Off")
if Switch1 == "Off" then
-- print ("Switch already Off")
else
Switch1 = "Off"
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
conn:connect(8080,(DomoticzIP))
conn:send("GET /json.htm?type=command¶m=switchlight&idx=" .. (switchidx2) .. "&switchcmd=Off&level=0 HTTP/1.1\r\n")
conn:send("Host: " .. (DomoticzIP) .. "\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
print ("SWITCHED Switch1 OFF")
end
end end )
Extra
A friendly comment-er suggested using a different method of reading the GPIO pins. I tried using this method (using interrupt based switching) but found it not reliable in my situation. From the 10 pushes I did it would not register one and it would actually reverse how the switch worked. So I am still using the above code right now.
Here is the code I tried:
wifi.setmode(wifi.STATION)
wifi.sta.config("Q-LAN","yoloyloyloyloylo")
print "WiFi set"
print "Reading button states in loop"
gpio.mode(5, gpio.INT, gpio.PULLUP)
gpio.trig(5, "both", function(level)
if level == 1 then
-- print ("Button 0 = On")
if Switch0 == "On" then
-- print ("Switch already on")
else
Switch0 = "On"
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
conn:connect(8080,'10.10.128.14')
conn:send("GET /json.htm?type=command¶m=switchlight&idx=42&switchcmd=On&level=0 HTTP/1.1\r\n")
conn:send("Host: 10.10.128.14\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
print ("SWITCHED IT ON")
end
elseif level == 0 then
-- print ("Button 0 = Off")
if Switch0 == "Off" then
-- print ("Switch already Off")
else
Switch0 = "Off"
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
conn:connect(8080,'10.10.128.14')
conn:send("GET /json.htm?type=command¶m=switchlight&idx=42&switchcmd=Off&level=0 HTTP/1.1\r\n")
conn:send("Host: 10.10.128.14\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
print ("SWITCHED IT OFF")
end end end )
Nice. Did you also did something with debouncing? With mechanical switches it is quite important to do so, i learned from playing with Arduino's. As you close the circuit (switch = on), when the switch is almost closed it will trigger between on/off/on/off/on. Maybe implement a delay of 200ms or so?
ReplyDeleteAnd i also like the rest of your ESP8266 tutorials, i'm still waiting for the day you will write a tutorial on how to drive an RGB LED-strip with an ESP8266 (ESP with min. 3 GPIO) and Domoticz.... *hint*
Hi, thnx!
DeleteI have not implemented any debouncing. I have not seen this phenomenon happening yet, but it might be something that comes with age of the switch? My test switch has a very definitive "click". Also I think Domoticz might actually buffer it a little bit so it wouldn't even be a problem?
If I do encountered the problem, adding a little delay after it sends the Domoticz switch command is easy!
About building an RGB version. Don't hold up your hopes, it's not what my intended usage scenario is really. With traditional strip it would mean adding a third mosfet and I find mostly I'm not even using a second one! You could easily do it yourself though?
But about RGB LEDs, what I am going to do at some point (I already have all the materials) is write a tutorial on how to drive a Pixel Addressable RGB LED strip. Although the strip will be more expensive, you won't need a mosfet or anything like that! More on that later on.
First of all, congratulations on the dimmer. I am going to replicate it. Now on the MOSFET, why do you ned both in the design and how are you not using both?
DeleteAnd finally, what is your progress on Pixel Addressable LEDs?
I might also have a solution for small power supply, just waiting to receive it and test myself.
The two MOSFETs are there because I'm using the dimmer modules with 2 channels. So you can use one ESP dimmer module to independently regulate two lights. If you only wish to use it for one, or multiple lamps but don't have the need to set them independently then using one MOSFET is fine.
DeleteAnother reason can be power requirements. One MOSFET can be used to run about 3A or 4A pulsed without problems. If you desire more, best to use another MOSFET because of heat dissipation.
Thank you for prompt reply. I have seen on one of the photos a board with one MOSFET and was curious.
DeleteOn the power supply, check this one: http://lygte-info.dk/review/Power%20Mains%20to%205V%200.6A%20Hi-Link%20HLK-PM01%20UK.html It has been recently tested and looks good also for putting in the wall and the price is really reasonable.
How about using interrupts for detecting on/off events instead of polling? NodeMCU has the gpio.trig() function which you can use like this:
ReplyDeletegpio.mode(5, gpio.INT, gpio.PULLUP)
gpio.trig(5, "both", function(level)
if level == 1 then
// changed to on
else
// changed to off
end)
This would probably improve the reaction time a bit.
Hey, that looks a lot better, awesome!
DeleteI'll test it tonight and then update the code here if it works just as well! :D
I tried your suggestion and while it works, it doesn't work consistently. From every 10 switches, 9 work and just sometimes it misses 1! When that happens, the switch actually reverses function, so the top position of the switch is then off and the bottom on. 10 clicks later, it reverses again. Really weird since it's reading ground or not. :S
DeleteGoing back to the script above I tried a 100 switches after each other, not a single one missed.
So I don't know what is going on, in theory it should work just as well, in practice that isn't the case!
I'm currently running Build 20150213 on a ESP-12
Isn't this the result of the type of interrupt you choose? With Arduino you can choose different types of interrupt on which it should trigger. Look here: http://arduino.cc/en/Reference/attachInterrupt (under 'Parameters' > 'mode'). The code you tried watches for both kinds (high/low), maybe try with only 'low' ? (GPIO pulled to GND = switch on for example).
DeleteI think it will work more stable then. In theory the interrupt part is better, because it uses less CPU. Also if you want to create a battery-operated switch it is better to use interrupts, you could put the ESP8266 to sleep, and wake it by interrupt. Your current (loop-based) code can't do that ;)
Great instruction!
ReplyDelete1. Maybe its necessary to have an anti-bounce when used with the interrupts. Capacitor to the switch?
2. Do the GPIO like it when they are connected directly without a resistor?
Hi Quindor,
ReplyDeleteThanks for this wall switch tutorial. I was looking for something like this a long time for replacing my kaku remote controls.
Unfortunately i have the following problem when i use your code for the esp8266 (with changed network and switch idx), this is what happens: when i operate the switch i see the text "SWITCHED Switch1 ON" (or OFF), the esp8266 is sending (blue light) and in the Domoticz log i see an incomming connection from the esp8266. But the switch state in Domoticz is not changed.
Can you tell me what i do wrong? It's maybe a stupid question but my programming skills are not as good as my hardware skills.
Hi,
ReplyDeleteI'm having the same problem with missed interrupts. I've not found a solution yet which is a shame as the .trig would enable much cleaner code...
Hi quindor,
ReplyDeleteI lik your series on the esp 8266. I'm also experimenting with the 8266 boards (esp-03). I already built a two channel mains switch prototype which I use to control my projection screen. My blogs are on www.huisbesturing.nl (they ate in dutch however) if you are interested. My goal is also to build a single channel unit that fits inside a wall socket box. I'm basing the power unit on the lnk306pn. The power supply will be not isolated, but I'm also writing software that can be updated over the wifi connection, so after an initial download of the software (before connecting to mains), no phycical connections are needed. This is also handy because you can leave the unit in the wall socket when you feel the urge to change the software.
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHi all,
ReplyDeleteAmazing tuto Quindor.
I'm learning a lot to start to play with my esp8266. But at this point. What about the script for domoticz to interactive with the swiches. I'm reading and reading documentation but nothing found about it.
Thanks for your help.
Hi Raul,
DeleteThnx for the praise! :D
I'm not really sure what you are talking about the Domoticz script? In Domoticz you don't need a script!
In Domoticz you create a virtual switch of which you then input the IDX into the ESP8266 code. From that point forward you will be able to flip the physical switch and the virtual Domoticz switch will follow it.
Then in Domoticz you create a blocky (drag and drop) to connect consequences to the switch being up or down. So it can influence another switch, or several others, whatever you want!
Hopefully that helps!
This comment has been removed by the author.
ReplyDeleteIs it maybe more handy if you make it a toggle? I don't know how to program, but:
ReplyDelete---------
state = 0
Loop () {
if gpio.read(GPIOpin1) != state then
state = gpio.read(GPIOpin1)
"send commando to domoticz"
wait (200)
}
-----
It would look a bit like this:
DeleteSwitch0 == 0
print "Reading button states in loop"
tmr.alarm(0, 100, 1, function()
if gpio.read(GPIOpin1) != Switch0 then
-- print ("Switch 1 State changed")
Switch0 = gpio.read(GPIOpin1)
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
conn:connect(8080,(DomoticzIP))
conn:send("GET /json.htm?type=command¶m=switchlight&idx=" .. (switchidx1) .. "&switchcmd=Toggle&level=0 HTTP/1.1\r\n")
conn:send("Host: " .. (DomoticzIP) .. "\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
print ("SWITCHED Switch0 Toggle")
end
end )
Not equal is ~=
DeleteWhen we press the switch is safely connect GPIO directly to the ground without resistance?
ReplyDeletePacitzu