12/2017
A simple Wi-Fi http server for
Sonoff Basic and Arduino
- with Wi-Fi Protected Setup / PBC
- POST or GET method selector
- mDNS responder
- HTTP BASIC authentication
- simply OTA from browser
- for any classic esp8266 (with at least 1MB of memory for OTA)

//********************** Sonoff Basic ****************************
// HTTP server - controller V1.6 / 2018-03-18 for SONOFF Basic as server
// Copyright 2018 Pavel Janko, www.fancon.cz
// MIT license, http://opensource.org/licenses/MIT
// Wi-Fi Protected Setup with PBC ready,
// mDNS responder for connect by name,
// POST or GET method selector,
// HTTP BASIC authentication,
// supports simply OTA from browser.
//
// Tested with ARDUINO ESP8266 2.4.1, IDE 1.8.5
// Sonoff Basic has 1MB flash memory only !
// For flashing disconnect the AC power! Power Sonoff only from
// the USB to Serial converter for your safety !
// Uploading a new application to the Sonoff cancel original firmware !
// Set Generic ESP8266 or ESP8285 by chip types and 1MB flash memory.
// It can also be a classic esp8266 (with at least 1MB of memory for OTA).
// After flash restart the device.
//
// To connect to an access point, hold down the Sonoff button for about
// three seconds (green LED is lit permanently) and activate WPS
// (Wi-Fi Protected Setup) with PBC (Push Button Configuration) on your
// Access point.
//
// For mDNS install Bonjour to your Windows - https://support.apple.com/bonjour
// or Avahi for your Linux - https://www.avahi.org/
// mDNS name is defined as "sonoff" below here. Just enter http://sonoff.local
// into the browser. But in my case, mDNS is sometimes
// slower than a direct IP address.
//
// For web authentication set #define WWWAUTHENTI true and
// your WWWUSERNAME and WWWPASSWORD below only.
//
// For OTA activation set OTAUSER, OTAPASSWORD and then in operation
// write to browser address row http://sonoff.local/firmware or direct IP address
// and upload your *.bin
// file.
//
// |---------------|-----------|--------------------|
// | Button | GREEN LED | Action |
// |---------------|-----------|--------------------|
// |short press |relay state|change relay state |
// |long press > 3s| change |wait for WPS from AP|
// | -- | flashing |after restart |
// | -- |short flash|client got page |
// |before power on| -- |flash mode only |
// |---------------|-----------|--------------------|
//
#include <Ticker.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#define WWWAUTHENTI false // Set true for web authentication
#define WWWUSERNAME "admin" // Set www user name
#define WWWPASSWORD "admin" // Set www user password
#define OTAUSER "admin" // Set OTA user
#define OTAPASSWORD "admin" // Set OTA password
#define OTAPATH "/firmware"// Set path for update
#define MDNSNAME "sonoff" // Set local mDNS name
#define HTTPMETHOD "POST" // Set "GET" or "POST" method
#define BUTTONPIN 0 // GPIO0 button pin
#define RELAYPIN 12 // GPIO12 relay pin
#define LEDPIN 13 // GPIO13 GREEN LED (active low)
#define BUTTONTIME 0.25
#define WIFIDOGTIME 5
#define SERVERPORT 80
#define BUTTONON "color: green; border: 3px #fff outset;"
#define BUTTONOFF "color: red; border: 3px #fff outset;"
#define BUTTONNOACT "color: black; border: 7px #fff outset;"
bool LedState = true; // LED off
bool RelayState = false; // Relay off
bool ButtonFlag = false;
bool WifiDogFlag = false;
bool RafHo = false;
char ButtonCount = 0;
String OnButt = BUTTONNOACT;
String OffButt = BUTTONOFF;
String HowDelka;
String buff;
ESP8266WebServer HttpServer(SERVERPORT);
ESP8266HTTPUpdateServer httpUpdater;
Ticker ButtonTick; // Preparing for periodic button reading
Ticker WifiDogTick; // preparing for test WiFi connection
//-----------------------------------------------------------------
void setup(void) {
WiFi.begin();
/* set ports */
pinMode(RELAYPIN, OUTPUT);
pinMode(LEDPIN, OUTPUT);
pinMode(BUTTONPIN, INPUT);
RelaySet(RelayState);
/* wait for WiFi connect */
while (WiFi.status() != WL_CONNECTED) {
GetButton();
delay(250);
LedSet(!LedState);
}
LedSet(true);
ButtonTick.attach(BUTTONTIME, ButtonFlagSet);
WifiDogTick.attach(WIFIDOGTIME, WifiDogFlagSet);
httpUpdater.setup(&HttpServer, OTAPATH, OTAUSER, OTAPASSWORD);
MDNS.begin(MDNSNAME);
HttpServer.on("/", HTTP_GET, ModifySendPage);
HttpServer.on("/", HTTP_POST, ModifySendPage);
HttpServer.onNotFound(handleNotFound);
HttpServer.begin();
}
//-----------------------------------------------------------------
void loop(void) {
HttpServer.handleClient(); // Listen for HTTP requests from clients
if (ButtonFlag) GetButton(); // Periodic serve button
if (WifiDogFlag) WifiTest(); // Periodic test WiFi connection
}//-----------------------------------------------------------------
void SendPage() {
LedSet(!LedState);
buff = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
buff += "<html><head>\n";
buff += "<style type=\"text/css\">\n";
buff += "html {font-family: sans-serif;color: brown;background:#ffe4c4}\n";
buff += ".submit {width: 10%; height:5vw; font-size: 100%; font-weight: bold; border-radius: 4vw;}\n";
buff += "@media (max-width: 1281px) {\n";
buff += "html {font-size: 3vw; font-family: sans-serif;color: brown;background:#ffe4c4}\n";
buff += ".submit {width: 40%; height:20vw; font-size: 100%; font-weight: bold; border-radius: 15vw;}}\n";
buff += "</style>\n";
buff += "<meta content=\"text/html; charset=utf-8\">\n";
buff += "<title>SONOFF - Basic</title></head><body>\n";
buff += "<center>\n";
buff += "<form action=\"/\" method=\"" + String(HTTPMETHOD) + "\">\n";
buff += "<h1>Sonoff ID : " + String(ESP.getChipId()) + "<br>\n";
buff += "<input type=\"submit\" name=\"R1ON\" class=\"submit\" value=\"ON\" style=\"" + OnButt + "\">\n";
buff += "<input type=\"submit\" name=\"R1OFF\" class=\"submit\" value=\"OFF\" style=\"" + OffButt + "\">\n";
buff += "</h1></form></center></body></html>\n";
HttpServer.send(200, "text/html", buff);
delay(20);
LedSet(!LedState);
}
/* Send HTTP status 404 Not Found */
void handleNotFound() {
HttpServer.send(404, "text/plain", "404: Not found");
}
/* Read button on GPIO0*/
void GetButton(void) {
/* short press butoon to change state of LED */
if (digitalRead(BUTTONPIN) == false ) ++ButtonCount;
if (digitalRead(BUTTONPIN) == true && ButtonCount > 1 && ButtonCount < 12 )RelaySet(!RelayState);
/* long press button - WPS mode */
if (ButtonCount > 12) {
bool Before = LedState;
RelaySet(false); // Relay OFF
LedSet(!Before); // LED change
ButtonTick.detach(); // Stop Tickers
WifiDogTick.detach();
while (!digitalRead(BUTTONPIN))yield();
delay(100);
StartWpsWaiting();
}
if (digitalRead(BUTTONPIN) == true ) ButtonCount = 0;
ButtonFlag = false;
}
/* Service Wi-Fi Protected Setup with Push Button Configuration */
void StartWpsWaiting(void) {
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
bool RafHo = false;
String HowDelka = "";
/* Wait for access point WPS */
do {
RafHo = WiFi.beginWPSConfig();
//Serial.print("*");
HowDelka = WiFi.SSID();
}
while (RafHo == false || HowDelka.length() == 0);
delay(500);
ESP.restart();
}
/* change state of relay */
void ModifySendPage(void) {
if (WWWAUTHENTI == true) {
/* request for www user/password from client */
if (!HttpServer.authenticate(WWWUSERNAME, WWWPASSWORD))
return HttpServer.requestAuthentication();
}
if (HttpServer.arg("R1ON") == "ON")RelaySet(true);
if (HttpServer.arg("R1OFF") == "OFF")RelaySet(false);
SendPage();
}
/* set relay */
void RelaySet(bool SetRelayState) {
if (SetRelayState == true) {
OnButt = BUTTONON;
OffButt = BUTTONNOACT;
}
if (SetRelayState == false) {
OnButt = BUTTONNOACT;
OffButt = BUTTONOFF;
}
RelayState = SetRelayState;
digitalWrite(RELAYPIN, RelayState);
LedSet(!SetRelayState);
}
/* set green LED */
void LedSet(bool SetLedState) {
LedState = SetLedState; // set green LED
digitalWrite(LEDPIN, LedState);
}
/* If disconnected WiFi then restart */
void WifiTest(void) {
if (WiFi.status() != WL_CONNECTED) {
ESP.restart();
}
WifiDogFlag = false;
}
void ButtonFlagSet(void) {
ButtonFlag = true;
}
void WifiDogFlagSet(void) {
WifiDogFlag = true;
}