Using Sonoff basic switches with own firmware
15 Mar 2019 - tsp
Last update 18 Jul 2020
10 mins
Since Iāve been using them now for a while Iāve decided to collect that information somwhere ā¦
The Sonoff basic is an ESP8285 based 230V 10A AC switching device that includes an local 3.3V power supply.
The ESP8285 offers WiFi connectivity (currently only WPA-PSK modes are supported, some 802.1x setups using
EAP-PEAP with MS-CHAP v2 do also work; some people even claim to have got connections to EAP-TLS based 802.1x
networks but there seem to be problems with the currently available SDKs and EAP in general). Additionally
the devices bring an LED as well as an push button and a single useable GPIO (revision 1 boards allowed
soldering a pin header there, revision 2 devices only allow soldering to an surface pad). Mains current is
switched via an classical mechanical relais which limits inrush current and reactive loads (if not compensated)
that the switch is capable to operate.
These switches are readily available (you can easily get them from Amazon in small,
medium and large quantities )
Unfortunately not the whole SDK is open sourced - but most parts are. One requires some binary blobs to
interact with the ESP8266 / ESP8285 - and the SDK also uses some proprietary ROM routines. Nevertheless
itās currently one of the most easily accessible and most open embedded WiFi controllers available.
It provides an easy way of switching mains loads in existing locations when someone is not capable of wiring
up a wired network (though itās possible - and Iām using them sometimes that way - to attach them to an
network like 1-wire or an RS485 network. The latter one requires external drivers and the first one a
3.3V instead of a 5V 1wire network or some external level shifting mechanism though).

Note that it is of course not possible to use the switches for other applications than switching AC mains
current - if one wants to use the builtin power supply. If one doesnāT require this PSU there are of course
cheaper or more easily useable relais solutions.
The ESP8285 uses an 32 Bit Xtensa LX106 core (by Tensilica) which is clocked (at the Sonoff basic clocked
at 80 MHz). The main difference between the ESP8285 and the well known ESP8266 is the 1 MByte on chip
flash - which allows devices to be even smaller than ESP8266 based ones. Both devices boot from an integrated
boot-ROM that also includes some functions used by the SDK.
Safety
Because the switch is a mains current switch one should ahere to the standard safety rules when manipulating
mains current devices:
- Only attach and detach wires when circuit breakers are disconnected or devices are not connected to the main lines.
Do not ignore that advice just because you need artificial lighting - use a flashlight (note: Amazon affilate link)
in this case
- Never leave any leads open - not even for testing. One can always āforgetā to not touch such a circuit.
- Do always connect protective earth too
- Do apply strain strain relief to cables - the devices are not appropriate to simply lie on the ground
without some external housing


GPIO usage
The sonoff basic only directly exposes four functional GPIO pins:
- GPIO 0: Push button (Input)
- GPIO 2: IO2 Pad (freely useable)
- GPIO 12: Relais (LOW is off, HIGH is on)
- GPIO 13: The green LED (LOW is on, HIGH is off
Flashing firmware
Flashing firmware is done via esptool. This tool allows flashing via a serial connection (the ROM
bootloader of the ESP8285 allows flashing any firmware via a serial protocol) or via IP (OTA updates). Using
OTA requires OTA capable firmware. Note that using OTA is of course a good idea - but cuts the available flash
memory into halve itās size. Even though 1 MByte of flash seems to be large there is a complete WiFi stack that
has to be embedded and in many cases people also want so support local webpages, hotspot functionality, etc.
To flash firmware connect the TX, RX, GND and VCC pins to any serial adapter that support CMOS levels (i.e.
runs on 3.3V). Do never ever attach any part of the circuit to 5V logic, powersupply or even RS232 ports.

To attach the pins one can use:
- Either try to contact the pins with jumper wires. That works pretty well with some practicing but may also
lead to frustrating errors. When using this method and flashing does not work - itās the cause of error in
nearly every situation
- Use pogo-pins. Thatās the way such devices are programmed in production. They are not that costly but you
require an adapter that provides 4 pins. They can be pushed against the terminals and have to stay connected
during the programming. Thatās the least invasive relieable method that is also used during
manufacturing. (Such pins are - for example - available via Amazon)
- Solder an pin header to the (top side) of the device. This of course voids any warrany but since custom
firmware does too itās not really a problem. Note that one has to be careful when pushing away the mains
connection wires on the top (to not break them) but has to apply some force - and has to be careful not
to rip off the small 3.3V supply wires also put on top.
To enter programming mode press the button during powering up the device. The devices
does not show any indication of entering programming mode but will respond to esptool.
One can then flash the firmware. Depending on the development environment one uses one has to call esptool
or use for example the Arduino IDE (the latter one is nice to get started with the ESP8285 and ESP8266
when one doesnāt care about the internal workings of the SDK and doesnāt want to produce the smallest possible
firmware / doesnāt need build automation).
This is the preferred way when not using an learning / tutorial IDE like ArduinoIDE (for example when
requiring full and direct control over the used code, not wanting any abstraction above the SDK, having
licensing reasons, wanting build automation, etc.). One flashes the firmware with esptool and is capable
of compiling software using the esp-open-sdk (which is the preferred way of developing using the most
open SDK thatās currently possible). The SDK (along with Linux deployment scripts) is available
at https://github.com/pfalcon/esp-open-sdk
Compilation is done using xtensa-lx106-elf-g++, the gcc variant targeted at the Xtensa LX106
processor core. Compiling (without linking) requires a bunch of compiler options. These are basically
-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -c -w -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -ffunction-sections -fdata-sections -w -x c++ -E -CC -DF_CPU=80000000L -DLWIP_OPEN_SRC -DTCP_MSS=536 -DLED_BUILTIN=2 -DESP8266
Note that one also has to specify the include directory of the SDK.
Linking is done using xtensa-lx106-elf-gcc. One has to supply at least settings like
-w -Os -nostdlib -Wl,--no-check-sections -Wl,-static -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read
and depending on the libraries used link against some of them - for example:
-lhal -lphy -lpp -lnet80211 -llwip2 -lwpa -lcrypto -lmain -lwps -lbearssl -laxtls -lespnow -lsmartconfig -lairkiss -lwpa2 -lstdc++ -lm -lc -lgcc
After that esptool can be used to build the binary image that will be flashed later on - for example when using eboot:
esptool -eo eboot.elf -bo output.bin -bm dout -bf 40 -bz 1M -bs .text -bp 4096 -ec -eo input.bin -bs .irom0.text -bs .text -bs .data -bs .rodata -bc -ec
To upload the generated image one uses esptool again. Upload can be done either via a serial port
esptool -vv -cd ck -cb 115200 -cp /dev/ttyU0 -ca 0x00000 -cf output.bin
or when one uses a mechanism like ArduinoOTA or a compatible mechanism for example via the espota.py script:
espota.py -i IPADRESS -p 8266 -auth=PASSWORD -f output.bin
Using ArduinoIDE
First add the esp8266 boards package to the additional boards manager URLs (File > Preferences). Itās located
at http://arduino.esp8266.com/stable/package_esp8266com_index.json. Then one can install the required package
from Tools > Board > Boards Manager. Itās simply called esp8266.
After that settings to be used can be selected also from the tools menu:
- Device: Generic ESP8265 board
- Flash Memory: 1M, No SPIFFS
- Crystal frequency: 26 MHz
- CPU frequency: 80 MHz
- Upload speed: 115200 (In case of errors use 57600)
- Builtin LED Port: 13
- IwiP version: v2 lower memory
Additional examples can be found under File > Examples (theyāve been added by the boards manager). Itās a good
idea to flash an simple blink example together with enabled OTA functionality to be capable to flash the device
via wireless. A simple basic example using ArduinoOTA is shown below:
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#define LEDPORT 13
#define RELAISPORT 12
#define SWITCHPORT 0
const char* ssid = "{FILLIN_WIFI_SSID}";
const char* password = "{FILLIN_WIFI_PASSWORD}";
void setup() {
Serial.begin(115200);
Serial.println("Booting");
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("{FILLIN_HOSTNAME}");
// No authentication by default
// ArduinoOTA.setPassword("{FILLIN_OTAPASSWORD}");
ArduinoOTA.onStart([]() { Serial.println("Start updating"); });
ArduinoOTA.onEnd([]() { Serial.println("\nEnd"); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
static boolean ledStatus = false;
void loop() {
unsigned long currentMillis = millis();
if((currentMillis % 1000) > 500) {
if(!ledStatus) {
ledStatus = true;
digitalWrite(LEDPORT, LOW);
}
} else {
if(ledStatus) {
ledStatus = false;
digitalWrite(LEDPORT, HIGH);
}
}
ArduinoOTA.handle();
}
Note that there hsould be no delays inside setup or loop (except really small delays not solveable by
any other means) because that would trigger the watchdog timer to timeout, wireless not working
correctly, etc.
To use OTA one can (after initial flash and a power cycle of the target!) select the Board from the
tools menue - it will appear under the network ports section (this only works when the device is up
and running in the same network segment when the ArduinoIDE is starting up! It does not update
itself during runtime).
Just a word of caution when youāre using OTA in an automated fashion like me (for example when using
automated build and deployment via Jenkins or any other pipeline) - if you implemented automatic
updating do not forget to implement an test that checks if the devices are flashable twice
(once from the old and once from the new firmware) to not lock yourself out of hundreds of
deployed devices ā¦
This article is tagged: