Custom Remote WiFi Mains Socket Switch
January 14, 2018 8 Comments
Here’s again one of these small hacks that I’ve been wanting to do for a while but never found the time…
There’s actually not that much to it, the most interesting part is probably just being amazed at how easy it is nowadays to pull off this kind of hack…
I bought (for probably only around 5£) this Sonoff S20 wireless remote control socket, and while pretty pleased with the build quality, I immediately disliked the software stack.
One is supposed to install the eWeLink Android app, which not only requires an account set up, but also sends the data externally to some of their servers.
More importantly it requires this switch to connect to an existing WiFi network, but I want to use it to turn on/off my very WiFi / internet router/modem.
What’s great however about this device, it’s being based on an ESP8266, which can be easily programmed even with the Arduino IDE. To the extend that they’ve even provided the 0.1″ spaced 4 holes to solder in the standard header for serial programming:
It took some time to put together the exact pinout of that header as well as what pins of the ESP8266 the various inputs/outputs are connected to, so here they are, all these details in one place:
ESP8266 pin usage
ESP8266 Pin | Function | GPIO (Use in Arduino Schetch) | Connected to |
---|---|---|---|
10 – MTDI | GPIO | GPIO 12 | Relay + BLUE LED (HIGH on) |
12 – MTCK | GPIO | GPIO 13 | GREEN LED (LOW to turn on) |
15 – GPIO0 | Flash | GPIO 0 | Switch (LOW when pressed) |
25 – RDX | UART RXD | GPIO 3 | J1 Pin 2 |
26 – TXD | UART TXD | GPIO 1 | J1 Pin 3 |
Pinout
VCC | Where the white arrow/triangle is |
---|---|
RX | |
TX | |
GND |
And finally the small Arduino script that:
- creates its own Access Point (essential for me, given the above)
- listens to presses on the physical switch to control the device (and does some basic debouncing)
- starts a very basic web server on port 80, that turns on/off the mains switch depending on which page is accessed
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> IPAddress localIP(192,168,4,1); IPAddress gateway(192,168,4,1); IPAddress subnet(255,255,255,0); #define BUTTON_PIN 0 // LOW when pressed #define GREEN_LED_PIN 13 // LOW ON #define RELAY_BLUE_LED_PIN 12 // HIGH ON #define DEBOUNCE_DELAY 200 // in millisecs ESP8266WebServer server(80); volatile bool isOn = false; void handleRoot() { server.send(200, "text/html", " <h1>Use either /on or / off</h1> "); } void handleOn() { if(isOn) { server.send(200, "text/html", " <h1>Already ON, nothing to do.</h1> "); } else { isOn = true; digitalWrite(RELAY_BLUE_LED_PIN, isOn); server.send(200, "text/html", " <h1>Turned ON.</h1> "); } } void handleOff() { if(!isOn) { server.send(200, "text/html", " <h1>Already OFF, nothing to do.</h1> "); } else { isOn = false; digitalWrite(RELAY_BLUE_LED_PIN, isOn); server.send(200, "text/html", " <h1>Turned OFF.</h1> "); } } void setup() { pinMode(BUTTON_PIN, INPUT); pinMode(GREEN_LED_PIN, OUTPUT); pinMode(RELAY_BLUE_LED_PIN, OUTPUT); digitalWrite(GREEN_LED_PIN, HIGH); // Turn Green LED OFF digitalWrite(RELAY_BLUE_LED_PIN, LOW); // Turn relay OFF // Attach an interrupt to the pin, assign the onChange function as a handler and trigger on changes (LOW or HIGH). attachInterrupt(BUTTON_PIN, onSwitchChange, CHANGE); // create access point // void softAP(const char* ssid, const char* passphrase, int channel = 1, int ssid_hidden = 0); WiFi.softAPConfig(localIP, gateway, subnet); WiFi.softAP("sonoff", "******", 1, 0); // hide the SSID // start web server server.on("/", handleRoot); server.on("/on", handleOn); server.on("/off", handleOff); server.begin(); } void loop() { server.handleClient(); } volatile long lastEventTime = 0; volatile bool lastState = HIGH; void onSwitchChange() { long currentTime = millis(); bool currentState = digitalRead(BUTTON_PIN); // debouce by making sure this event doesn't happen too often // debouncing is done on ALL changes if(currentTime - lastEventTime > DEBOUNCE_DELAY) { // toggle the relay and store its current state // toggling is done only on FALLING events (when switch is pressed -> LOW) // we need to check that it's actually Falling as opposed to just LOW to avoid dupe events if(currentState == LOW && lastState == HIGH) { isOn = !isOn; digitalWrite(RELAY_BLUE_LED_PIN, isOn); } } lastEventTime = currentTime; lastState = currentState; }
I am trying to follow these steps exactly but I am not able to upload a Sketch to the Sonoff device. I am pretty sure I have the 3.3V, GND, TX and RX connections wired correctly but when I try to upload, the Ardunio IDE gives the error:
warning: espcomm_sync failed
error: espcomm_open failed
error: espcomm_upload_mem failed
error: espcomm_upload_mem failed
How to you turn the Sonoff into “Flash” mode? I have tried holding the button down and the green LED goes from flashing once every 2 seconds to 2 times a second but I still get the same error above when I try to upload a sketch. Also, what are the exact settings in the Arduino IDE you are using to configure the board/serial protocol?
Thanks!
You need to hold the button down WHILE you power the ESP8266, in this case that means while you connect the 3.3V or simply the programming cable (hopefully you don’t have your Sonoff plugged into mains while you do this !!! 🙂 ) so that it goes into “Flash” mode.
Ah! This I didn’t try yet. I will try it tonight 🙂
Also re exact configuration/settings for the Arduino IDE:
Board: "Generic ESP8266 Module" <<<<!!!!
Flash Mode: "DOUT" <<<<!!!!
Flash Size: "1M (no SPIFFS)" <<<<!!!!
Debug Port: "Disabled"
Debug Level: "None"
LwIP Variant: "v2 Prebuilt (MSS=536)"
Reset Method: "ck"
Crystal Frequency: "26 MHz"
Flash Frequency: "40MHz"
Upload Using: "Serial"
CPU Frequency: "80 MHz"
Upload Speed: "115200"
Port: Your COM port connected to Sonoff
Thanks, this works perfectly! Eventually, I want to program the socket to synch with NTP to get the current time, then automatically switch off my router at midnight while giving an option to switch it back on for 30 minutes via the Soft AP. The idea is obviously to try to stop staying awake all night watching YouTube :).
Wow great ! Happy it helped, do send me a pic of your final result…:)
Here is the code I came up with. Seems to work perfectly – let’s hope it has the desired result: https://github.com/alexspurling/sonoff
Great, thanks for linking to my post… 🙂