Tag: Arduino IDE

Hacking the SONOFF RF

Hacking the SONOFF RF

Some time ago I bought two SONOFF RF’s, the power switch with an ESP8266 inside. I wasn’t planning on using them out of the box, mostly because I don’t want another app on my phone. I have domotica and I want to control all with the same app. My domotica can control devices based on an http call, so this should work.

While digging around on the internet, I found several sites which explain how to hack the normal SONOFF, without the RF. I liked this page but there are many others. Unfortunately, I couldn’t find any page on how to hack the SONOFF RF so I decided to figure it out myself.

I couldn’t get the ESP in flash mode, no matter how hard I tried. When you’re hacking the SONOFF apparently you can use the button which is connected to GPIO0 to put it in flash mode but that did not work on the SONOFF RF. Finally I decided to connect the GPIO0 to ground straight from the chip and see what happens. That worked!

Let’s look at the chip layout:

esp8266_extra_gpio

The LNA (pin 2) should be easy to find since that is the antenna. I looked at the bottom of the print and found it. Forgive the lousy quality but you can see what I found:

antenna and gpio0

You can see C27 connected to the 2nd pin from the top on the left side. That aligns with the schematic above and this means that the 2nd pin from the right on the bottom side should be GPIO0. It runs to a resistor at which point I soldered a wire to it.

The rest of the connections are the same as with the normal SONOFF:

sonoffrf1.jpg

I soldered a strip to the board, from the button to the top the pins are:

  • 3.3V
  • TX
  • RX
  • GND
  • GPIO14

You connect the 3.3V to your FTDI module on the 3.3V, TX on SONOFF RF to RX on FTDI, RX on SONOFF RF to TX on FTDI and GND to GND. What I did is put a break switch on the line to the power because that way I can quickly reboot the SONOFF RF without unplugging the FTDI (reboot to put it in flash or take it out of flash):
(forgive the sloppy soldering 🙂

SONOFF 2 FTDI

Almost there… Now I want to be able to put the SONOFF RF into flash mode whenever I want and apparently the button does not work. So I soldered a small push button (break if not pressed) between my wire on the bottom and GND but I made the button at the top so I can access it easily. I passed the wire to the front:

sonoffrf2

and connected it to the GND of the button of the SONOFF RF (which is the left side in the picture below) via a small push button:

sonoffrf3

Now if I want to put the ESP in flash mode, I push the button on the power cable (cutting the power to the ESP), push the small button (connecting GPIO0 to GND), release the power button (which boots the ESP) and after a short while release the small button because the ESP will now be in flash mode.

Now that I can program the ESP I can figure out why the regular push button does not work to connect GPIO0 to GND like in the normal SONOFFs.

In short, these are the connections:

  • The relay is connected to GPIO12
  • The led is connected to GPIO13
  • The button is connected to GPIO0

Even though the button is connected to GPIO0, pushing it however only brings GPIO0 to GND for a short while. Even if you keep the button pushed, it only briefly connects GPIO0 to ground which is why you cannot use the button to put the ESP in flash mode. The reason for that is that the RF module also does the same. Pushing the button on the remote actually causes GPIO0 to connect to GND which is probably why they did something different with the button than in the normal SONOFF.

Now that we know that we can build the program. We need to control the relay and the led and we need to watch the button/RF button through GPIO0.

I have created this simple program which should do the trick:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

// Replace with your network credentials
const char* ssid = "YourSSID";
const char* password = "YourPassword";

ESP8266WebServer server(80);

String pagePart1, pagePart2;

int sonoffLed = 13;
int sonoffRelay = 12;
int sonoffButton = 0;
bool buttonState = false;
bool powerState = false;

void powerOn (void) {
  digitalWrite(sonoffLed,LOW);
  digitalWrite(sonoffRelay,HIGH);
  powerState = true;
}

void powerOff (void) {
  digitalWrite(sonoffLed,HIGH);
  digitalWrite(sonoffRelay,LOW);
  powerState = false;
}

void switchPower (void) {
  if (powerState) {
    powerOff();
  } else {
    powerOn();
  }
}

void setup(void){
  pagePart1 = "<h1>SONOFF Switch</h1><br><br>Your switch is ";
  pagePart2 = "</font>.<br><br><a href='on'><button>ON</button></a> ";
  pagePart2 = pagePart2+<a href=\"off\"><button>OFF</button></a></p>";  
  // preparing GPIOs
  pinMode(sonoffLed,OUTPUT);
  pinMode(sonoffRelay,OUTPUT);
  powerOff();

  Serial.begin(9600); 
  Serial.println("");
  WiFi.begin(ssid,password);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/",[](){
    String tmp = pagePart1+"<font color=";
    if (powerState) {
      tmp = tmp+"green>on";
    } else {
      tmp = tmp+"red>off";
    }
    tmp = tmp+pagePart2;
    server.send(200,"text/html",tmp);
  });
  server.on("/on",[](){
    String tmp = pagePart1+"now switched <font color=green>on"+pagePart2;
    server.send(200,"text/html",tmp);
    powerOn();
    delay(1000);
  });
  server.on("/off",[](){
    String tmp = pagePart1+"now switched <font color=red>off"+pagePart2;
    server.send(200,"text/html",tmp);
    powerOff();
    delay(1000); 
  });
  server.begin();
  Serial.println("HTTP server started");
}
 
void loop(void){
  server.handleClient();
  bool currentState = digitalRead(sonoffButton);
  if (currentState!=buttonState) {
    buttonState = currentState;
    if (buttonState==LOW) {
      switchPower();
    }
  }
  delay(50);
}

The nice thing about the way the SONOFF RF is designed is that the RF part is completely separate from the ESP part so after reprogramming the ESP, the RF still works as originally designed. This means that you can still program the RF receiver as you would normally:

sonoff_rf_pair_with_433-1

Press the button on the SONOFF RF twice quickly and than press a button on the remote. This button will than be linked to the RF and can be used, with the above program, to turn the switch on and off.

Advertisements
Update for library PAM_WiFiServer

Update for library PAM_WiFiServer

An update to the PAM_WiFiServer library which will generate a settings.htm page based on the variables created with the PAM_Tools library. This way if you log variables using the PAM_Tools library, you can also easily maintain them with this function. Think for example of an API key or timeout parameters.

Example 2 is an elaborate example of how to use this function.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Update for library PAM_Tools

Update for library PAM_Tools

While creating projects with my libraries, I found that most of them required some additions which I have created and will document here.

I have expanded the PAM_Tools library to allow it to handle float and bool as well. To this I have created the following functions:

float getFloatKey (String, String);
bool getBoolKey (String, String);
void putFloatKey (String, String, float);
void putBoolKey (String, String, bool);
float getPutFloatKey (String, String, float);
bool getPutBoolKey (String, String, bool);

They work the same as the versions for String and int except that since there are only two values possible for bool there is no way to detect if there is no value present so it will return false if there is no way. If you want to have a default value, you can use getPutBoolKey.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Update for library PAM_WiFiConnect

Update for library PAM_WiFiConnect

I recently had a short power outage. Nothing major but a feature request for the connect library did come out of that. When the power came back on, my ESP with my temperature logger was back on faster than my wifi which meant that after the wifiConnect nothing else happened. Ok, I could have coded that in the program but than I realized that if you have some sort of IoT device, it does not make sense to return to the setup function from the wifiConnect if you have no wifi. A need was born.

The result? A function called waitWiFiConnect which does what it’s name implies; it waits for a wifi connection. It does this by turning on the internal led, GPIO2, try to connect via wifiConnect. When it returns from that function it checks whether there is a connection. If there is a connection the led is turned off and the function ends. If not, the led blinks shortly after which we wait for 1 minute before trying again. It will only return to the caller after it has a wifi connection.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Arduino IDE libraries

Arduino IDE libraries

After creating my initial library, I saw it grow rapidly with more generic functions I wanted so instead of staying with one library which could grow out of proportion (from a code size perspective), I decided to create a library for each of my needs (which of course makes sense from a lot of angles). So far this I have created the following libraries (so far mostly aimed at the ESP8266):

  • PAM_Tools
    This library contains functions to access the file system to store and retrieve information;
  • PAM_WiFiConnect
    This library contains functions to easily connect to the wifi, including selecting one of multiple access points;
  • PAM_WiFiClient
    This library contains functions to easily retrieve a webpage;
  • PAM_WebAPI
    This library contains functions to use web based APIs such as IFTTT and ThingSpeak.
  • PAM_WiFiServer
    This library contains functions for creating a webserver.

 

Library PAM_WiFiServer

Library PAM_WiFiServer

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is there to easily create a webserver.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

void wifiServerStart()

This function needs to be called once before you use any of the other functions.

bool serverRequestReceived()

This function you should call frequently in your loop function. If true, you should create a webpage to be send back using the other functions.

String pageRequested ()

You can call this function if a serverRequestReceived returned true and it will return the name of the page that is requested.

String giveURLParam (String search)

If a serverRequestReceived than you can check with this function whether a specific parameter has been send. It will return the value of that parameter or PARAMNOTFOUND if the parameter is not in the list. This function is used to get the values submitted for example throught a form.

void serverSendPage (String paramName[], String paramValue[], int paramCount, String pageName)

This is the function that sends back a page. The way it works is that a pageName is retrieved from the filesystem /page/<pageName> and line by line each parameter given in paramName is replaced by the corresponding paramValue. So for every line retrieved, the function loops through all paramNames and replaces each instance of %paramName% with the corresponding value. The value paramCount should contain the number of elements in both paramName and paramValue. The pageName variable should contain the page that needs to be parsed and send back to the client.

There are multiple instances of this function defined. If no pageName is given, it is assumed to be index.htm. If no parameters are given, no parameter replacement will be done. If neither parameters nor a pageName is given, the file index.htm will be send without parsing.

If the requested pageName is not found, the headers will be send put the page will be blank.

 

Library PAM_WebAPI

Library PAM_WebAPI

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is there to use different web based APIs such as that of IFTTT and ThingSpeak.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

String ifttt (String event, String value1, String value2, String value3)

If This Than That, IFTTT, is a very extensive online tool that allows you to act on an event. The concept is based on channels that can send and receive data. A specific combination of channels and actions is called an applet.

There is also a channel called Maker which you can use for your own IoT devices. This is what this library is for. You get your own Maker key which you should put in the library PAM_Defines.h which is nothing more than a way for you to select which APIs are compiled in. If you don’t use IFTTT for example, you should also not have to overhead when compiling so if you do not define a key, the functions will be excluded from the library which should save memroy. I will not go into detail on IFTTT usage just now. Suffice it to say you need a key which you define in PAM_Defines.h:

#ifndef PAM_Defines_H
#define PAM_Defines_H

//
// This is where you define your own IFTTT.com key
//
#define IFTTTKEY "YOUR-KEY-HERE"

#endif

and you need an event type which defines what kind of trigger you want to send. I have also defined these in my PAM_Defines.h:

#define IFTTT_BOOT "PAM_BOOT"
#define IFTTT_HTTP "PAM_HTTP"
#define IFTTT_MSG "PAM_MSG"

There are multiple instances of the same function created in the library so you can call it with no values, one, two or all three.

As an example, I use the event IFTTT_BOOT to send me a message if an IoT device boots where I send it’s IP as value 1. If it also contains a webserver I use the event IFTTT_HTTP along with it’s IP as value 1 so the message can actually contain a link to the IP.

I use IFTTT_MSG to send a message, for example if my plant needs watering.

void thingspeak (String api, String field1, String field2, String field3, String field4)

void thingspeak (String api, float field1, float field2, float field3, float field4)

The next API included is that for ThingSpeak. ThingSpeak call themselves an open data platform for the Internet of Things. In order to work with ThingSpeak, you need to set up an account and than set up a channel. Unlike IFTTT, you need a different API key per channel so in the library I choose to have one define to say you want to use the library. I use seperate defines for each of my channels:

#define THINGSPEAK
#define TS_TEST "MY-API-KEY"

The #define THINGSPEAK is needed to compile this part of the library. Like IFTTT there are multiple instances of the library. You can call the function with just one parameter or up to four parameters (ThingSpeak actually allows eight but I have not set up for that yet. If it is needed, let me know). The API key is mandator as well as the first field. The API of ThingSpeak allows you to set a date time for the event and if you don’t current date time is used. Since I wanted to keep the library small, I did not implement the seperate date time parameter.

You can use String for the values or int/float.

Library PAM_WiFiClient

Library PAM_WiFiClient

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library can be used for easy webpage retrieval. You can do a fire and forget or parse through every line, with or without the header.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

String getURLParse (String host, String url, bool fullContent, void (*g)(String l), int port)

This is the main function to retrieve a webpage. All other page retrieval functions call this function. You pass a host and a url, starting with a /, tell it whether to send just the body (false) or include the header (true) followed by a function which receives one String at a time which will be the retrieved String and finally a port. The port can be omitted in which case it defaults to port 80.

This function will send a request to the host asking for the specificed url. It will than retrieve every line of the page, it will determine where the header stops and the page begins and send it to the function g (only the body if that was requested in the boolean fullContent).

The last line received will be returned in the String.

String getURLFullParse (String host, String url, void (*g)(String l), int port)

This function will call the main function requesting to send all lines, including the header to the function g. The port is optional. If omitted it will default to port 80.

String getURLBodyParse (String host, String url, void (*g)(String l), int port)

The function will call the main function requesting to send all lines of the body of the page to the fuction g. The port is optional. If omitted it will default to port 80.

String getURL (String host, String url, int port)

The fire and forget version, get the url but do not use the resulting webpage. Again, the port is optional and will default to port 80.

void doSerial (String webLine)

If you are debugging it could be convenient to see what the host is sending back. If you use the function doSerial as the function to process your lines, you will see the page received on your serial monitor.

 

Library PAM_Tools

Library PAM_Tools

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is aimed at using the file system to store keys.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

The following four functions are used to store and retrieve String and int values persistently (meaning between reboots).

String getStringKey (String location, String key)

This function will retrieve the content of a file in a string where the filename is /<location>/<key>.txt The .txt extension is used so on Windows you can create
the needed files in a data directory which you can than upload using the ESP8266 Sketch Data Upload. If the key is not found a NOSTRINGKEYFOUND will be returned.

int getIntKey (String location, String key)

This function will retrieve the content of a file and put it in an int where the filename is /<location>/<key>.txt The .txt extension is used so on Windows you can create
the needed files in a data directory which you can than upload using the ESP8266 Sketch Data Upload. If the key is not found a NOINTKEYFOUND will be returned.

void putStringKey (String location, String key, String value)

This function will put a String value into the file with filename
/<location>/<key>.txt If the file exists it will be overwritten.

void putIntKey (String location, String key, int value)

This function will put an int value into the file with filename
/<location>/<key>.txt If the file exists it will be overwritten.

 

Library PAM_WiFiConnect

Library PAM_WiFiConnect

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is aimed at easily connecting to wifi. I know that there are other libraries that support this but mine supports me better and might support you better too.

The library needs one other library, PAM_Tools.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

void wifiConnect (String ssid, String password)

This function will connect you to the specified SSID using the specified  password. It is different from the standard connect to wifi and just waiting on a connected in two ways:

  • It will break trying to connect after 5 runs of 10 seconds waiting on a connection
  • It will disconnect from wifi and try again in every run.

In my experience some of the connections might not work in the first try (especially hotspots on a mobile phone) and by stopping the connection and starting again I noticed that most connections will succeed within 15 seconds.

I also wanted the wifi connection not to run forever but break off at some point so you could do some error handling.

void wifiConnect (String ssid)

This instance of the function will get the password for the specified SSID from the file system, through one of the functions in PAM_Tools, and than uses wifiConnect to connect to it.

Passwords are stored in a file on the file system in the directory wifi where the full name of the file will be /wifi/ssid.txt and this file will contain one line and that is the password for that ssid. There are two advantages to do this:

  • If you share your code and you forget to change your ssid to Your-SSID at least you will not have given away your password
  • If you have multiple ssid’s that you can connect to, which is what wifiConnect does if you do not supply any SSID, you do not have to have all SSIDs and passwords in your file.

void wifiConnect (bool debug)

This instance of the function in the library is probably the most powerful/convenient. What it does is scan the available networks and check for each one found whether there is a associated password stored in the file system. If so, it will try to connect.

You can list preferred networks by storing SSIDs in a numbered file in the same directory. So if there is a file called 1.txt in the wifi directory, it will open that file and check whether that SSID is available. If not it will try 2.txt etc. If none of them are available or there is no 1.txt then it will try to use any of it’s other credentials. If there is still no connection, it will try the SSID in 0.txt which could be a hidden SSID. I found this function works great if you have a mobile IoT device. Even with fixed devices I now use it and put my mobile hotspot as the first to check (1.txt). It usually isn’t on, but if I want to isolate the IoT, for example for testing, I can turn on my hotspot, reset it and it will connect to my hotspot.

If you want to see which networks are found and how it decides to connect to it, you can call it with a parameter true after which it prints out what it does on Serial.

void wifiConnect ()

This instance of the function in the library will call wifiConnect(false) and as a result try to automatically connect to a wifi network as described above.