Custom Remote WiFi Mains Socket Switch

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


VCC Where the white arrow/triangle is

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() {

  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);

void loop() {

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;


10 Responses to Custom Remote WiFi Mains Socket Switch

  1. Hi there, trandi, I stumbled here while surfing and looking for an auto router booter. A device that will power cycle a router if it senses that there is no internet. By pinging Google’s IP for example.
    Can you make this do that?

    • trandi says:

      Hi, yes. Currently, the ESP8266 inside this switch creates its own WiFi access point, to which I connect to remotely turn it on/off. However, it could be easily be reconfigured to connect to your router’s Wifi, then ping and depending on the result do nothing or power cycle.

      Hope this helps,

  2. Alex Spurling says:

    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?


    • trandi says:

      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.

    • trandi says:

      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

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

%d bloggers like this: