2019-10-04 20:37:52 +00:00
# include <FS.h> //this needs to be first, or it all crashes and burns...
# include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
//needed for library
# include <DNSServer.h>
# include <ESP8266WebServer.h>
# include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
2019-09-29 19:24:58 +00:00
# define ARDUINOJSON_DECODE_UNICODE 1
# include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
2019-11-01 23:39:21 +00:00
# include <ESP8266mDNS.h> // https://tttapa.github.io/ESP8266/Chap08%20-%20mDNS.html
2019-09-21 21:59:17 +00:00
2020-06-12 19:49:14 +00:00
# include <WiFiClientSecure.h>
2020-02-24 22:35:01 +00:00
2019-10-04 20:37:52 +00:00
//for LED status
# include <Ticker.h>
2020-06-12 19:49:14 +00:00
Ticker ledTicker ;
2019-09-21 18:23:39 +00:00
2020-06-01 22:57:46 +00:00
const byte RELAY_PIN = D2 ; // SHOULD BE 12 for Sonoff S20
const byte LED_PIN = LED_BUILTIN ; // 13 for Sonoff S20, 2 for NodeMCU/ESP12 internal LED
2019-09-29 19:24:58 +00:00
const byte BUTTON_PIN = 0 ;
2019-09-21 18:23:39 +00:00
2019-11-01 23:39:21 +00:00
bool fuzIsOpen = false ;
2020-06-01 22:57:46 +00:00
void blinkLED ( ) {
2019-10-04 20:37:52 +00:00
//toggle state
bool state = digitalRead ( LED_PIN ) ; // get the current state of LED_PIN pin
digitalWrite ( LED_PIN , ! state ) ; // set pin to the opposite state
}
//gets called when WiFiManager enters configuration mode (for LED status)
void configModeCallback ( WiFiManager * myWiFiManager ) {
Serial . println ( " Entered config mode " ) ;
Serial . println ( WiFi . softAPIP ( ) ) ;
//if you used auto generated SSID, print it
Serial . println ( myWiFiManager - > getConfigPortalSSID ( ) ) ;
//entered config mode, make led toggle faster
2020-06-12 19:49:14 +00:00
ledTicker . attach ( 0.5 , blinkLED ) ;
2019-10-04 20:37:52 +00:00
}
//define your default values here, if there are different values in config.json, they are overwritten.
String matrixUsername ;
String matrixPassword ;
2020-06-12 19:49:14 +00:00
String notifiedEndpoint = " https://presence-button-staging.glitch.me/status?fuzisopen= " ; // #entropy:matrix.fuz.re
2019-10-04 20:37:52 +00:00
//flag for saving data
bool shouldSaveConfig = false ;
//callback notifying us of the need to save config
void saveConfigCallback ( ) {
Serial . println ( F ( " Should save config " ) ) ;
shouldSaveConfig = true ;
}
WiFiManager wifiManager ;
2019-09-21 21:59:17 +00:00
2019-09-25 20:13:54 +00:00
# include <ESP8266HTTPClient.h> // for https://github.com/matt-williams/matrix-esp8266/blob/master/matrix-esp8266.ino
2019-11-01 23:39:21 +00:00
ESP8266WebServer httpServer ( 80 ) ; // webserver on port 80 https://github.com/esp8266/Arduino/blob/14262af0d19a9a3b992d5aa310a684d47b6fb876/libraries/ESP8266WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino
void handleRoot ( ) {
String html =
String ( " <!DOCTYPE HTML><html> " ) +
" <head><meta charset=utf-8><title>presence</title></head><body> " +
" <a href='/admin'>Admin</a><br> " +
" Rotating light: " + String ( digitalRead ( RELAY_PIN ) = = HIGH ) + " <br> " +
" Fuz is open: " + String ( fuzIsOpen ) + " <br> " +
" </body></html> " ;
httpServer . send ( 200 , " text/html " , html ) ;
}
void handleAdmin ( ) {
if ( ! httpServer . authenticate ( matrixUsername . c_str ( ) , matrixPassword . c_str ( ) ) ) {
return httpServer . requestAuthentication ( ) ;
}
for ( int i = 0 ; i < httpServer . args ( ) ; i + + ) {
if ( httpServer . argName ( i ) = = " disablerotatinglight " ) {
digitalWrite ( RELAY_PIN , LOW ) ;
continue ;
}
if ( httpServer . argName ( i ) = = " enablerotatinglight " ) {
digitalWrite ( RELAY_PIN , HIGH ) ;
continue ;
}
if ( httpServer . argName ( i ) = = " setfuzisopen " ) {
fuzIsOpen = true ;
continue ;
}
2019-11-03 16:34:39 +00:00
if ( httpServer . argName ( i ) = = " resetesp " ) {
httpServer . sendHeader ( " Location " , httpServer . uri ( ) , true ) ;
httpServer . send ( 302 , " text/plain " , " " ) ;
delay ( 500 ) ;
2020-06-13 15:57:19 +00:00
ESP . restart ( ) ;
2019-11-03 16:34:39 +00:00
return ;
}
2019-11-01 23:39:21 +00:00
}
if ( httpServer . method ( ) = = HTTP_POST ) {
for ( int i = 0 ; i < httpServer . args ( ) ; i + + ) {
if ( httpServer . argName ( i ) = = " matrixUsername " ) {
matrixUsername = httpServer . arg ( i ) ;
continue ;
}
if ( httpServer . argName ( i ) = = " matrixPassword " ) {
matrixPassword = httpServer . arg ( i ) ;
continue ;
}
2020-06-12 19:49:14 +00:00
if ( httpServer . argName ( i ) = = " notifiedEndpoint " ) {
notifiedEndpoint = httpServer . arg ( i ) ;
2019-11-01 23:39:21 +00:00
continue ;
}
}
Serial . println ( F ( " saving config " ) ) ;
2020-06-12 19:49:14 +00:00
const size_t capacity = JSON_OBJECT_SIZE ( 3 ) + 440 ; // https://arduinojson.org/v6/assistant/
2020-06-01 22:57:46 +00:00
DynamicJsonDocument jsonBuffer ( capacity ) ;
2019-11-01 23:39:21 +00:00
jsonBuffer [ " matrixUsername " ] = matrixUsername ;
jsonBuffer [ " matrixPassword " ] = matrixPassword ;
2020-06-12 19:49:14 +00:00
jsonBuffer [ " notifiedEndpoint " ] = notifiedEndpoint ;
2019-11-01 23:39:21 +00:00
File configFile = SPIFFS . open ( " /config.json " , " w " ) ;
if ( ! configFile ) {
Serial . println ( F ( " failed to open config file for writing " ) ) ;
}
serializeJson ( jsonBuffer , Serial ) ;
Serial . println ( ) ;
serializeJson ( jsonBuffer , configFile ) ;
configFile . close ( ) ;
}
if ( httpServer . args ( ) > 0 | | httpServer . method ( ) = = HTTP_POST ) { // trim GET parameters and prevent resubmiting same form on refresh
2019-11-03 16:34:39 +00:00
httpServer . sendHeader ( " Location " , httpServer . uri ( ) , true ) ;
2019-11-01 23:39:21 +00:00
return httpServer . send ( 302 , " text/plain " , " " ) ;
}
String html =
String ( " <!DOCTYPE HTML><html> " ) +
" <head><meta charset=utf-8><title>presence admin</title></head><body> " +
( digitalRead ( RELAY_PIN ) = = HIGH ? " <a href='?disablerotatinglight'>Disable rotating light</a> " : " <a href='?enablerotatinglight'>Enable rotating light</a> " ) + " <br> " +
2019-11-03 16:34:39 +00:00
( fuzIsOpen ? " " : " <a href='?setfuzisopen'>Set Fuz as open</a> " ) + " <br> " +
" <a href='?resetesp'>Reboot ESP</a> " + " <br><br> " +
2019-11-01 23:39:21 +00:00
" <form method='post'> " +
" <div><label for='matrixUsername'>matrixUsername </label><input name='matrixUsername' id='matrixUsername' value=' " + matrixUsername + " '></div> " +
" <div><label for='matrixPassword'>matrixPassword </label><input name='matrixPassword' id='matrixPassword' value=' " + matrixPassword + " '></div> " +
2020-06-12 19:49:14 +00:00
" <div><label for='notifiedEndpoint'>notifiedEndpoint </label><input name='notifiedEndpoint' id='notifiedEndpoint' value=' " + notifiedEndpoint + " '></div> " +
2019-11-01 23:39:21 +00:00
" <div><button>Submit</button></div></form> "
" </body></html> " ;
httpServer . send ( 200 , " text/html " , html ) ;
}
void handleNotFound ( ) {
httpServer . send ( 404 , " text/plain " , httpServer . uri ( ) + " not found " ) ;
}
2020-02-24 22:35:01 +00:00
BearSSL : : WiFiClientSecure secureClient ;
2020-06-01 22:57:46 +00:00
HTTPClient http ;
void notifyFuzIsOpen ( bool fuzIsOpen ) {
2020-06-12 19:49:14 +00:00
http . begin ( secureClient , notifiedEndpoint + String ( fuzIsOpen ) ) ;
2020-06-01 22:57:46 +00:00
http . setAuthorization ( matrixUsername . c_str ( ) , matrixPassword . c_str ( ) ) ;
int httpCode = http . GET ( ) ;
2020-06-12 19:49:14 +00:00
Serial . println ( " notifyFuzIsOpen body: " + http . getString ( ) ) ;
2020-06-01 22:57:46 +00:00
Serial . println ( " notifyFuzIsOpen return code: " + String ( httpCode ) ) ;
http . end ( ) ;
2020-06-12 19:49:14 +00:00
if ( httpCode ! = 200 ) { // something is wrong, bad network or misconfigured credentials
ledTicker . attach ( 0.1 , blinkLED ) ;
2020-06-01 22:57:46 +00:00
} else {
2020-06-12 19:49:14 +00:00
ledTicker . detach ( ) ;
digitalWrite ( LED_PIN , LOW ) ; // ensure the LED is lit
2020-06-01 22:57:46 +00:00
}
2019-11-03 16:34:39 +00:00
}
2019-09-29 19:24:58 +00:00
bool loggedInMatrix = false ;
2019-09-21 18:37:23 +00:00
void setup ( ) {
Serial . begin ( 115200 ) ;
2019-09-21 21:59:17 +00:00
Serial . println ( ) ;
2019-09-29 19:24:58 +00:00
//set relay pin as output
pinMode ( RELAY_PIN , OUTPUT ) ;
2019-09-21 18:37:23 +00:00
//set led pin as output
pinMode ( LED_PIN , OUTPUT ) ;
2019-09-29 19:24:58 +00:00
//set button pin as input
2019-09-21 18:37:23 +00:00
pinMode ( BUTTON_PIN , INPUT ) ;
2019-09-21 18:23:39 +00:00
2020-06-12 19:49:14 +00:00
// start ledTicker with 1 because we start in AP mode and try to connect
ledTicker . attach ( 1.1 , blinkLED ) ;
2019-10-04 20:37:52 +00:00
Serial . println ( F ( " mounting FS... " ) ) ;
if ( SPIFFS . begin ( ) ) {
Serial . println ( F ( " mounted file system " ) ) ;
if ( SPIFFS . exists ( " /config.json " ) ) {
//file exists, reading and loading
Serial . println ( F ( " reading config file " ) ) ;
File configFile = SPIFFS . open ( " /config.json " , " r " ) ;
if ( configFile ) {
Serial . println ( F ( " opened config file " ) ) ;
size_t size = configFile . size ( ) ;
// Allocate a buffer to store contents of the file.
std : : unique_ptr < char [ ] > buf ( new char [ size ] ) ;
configFile . readBytes ( buf . get ( ) , size ) ;
2020-06-12 19:49:14 +00:00
const size_t capacity = JSON_OBJECT_SIZE ( 3 ) + 440 ; // https://arduinojson.org/v6/assistant/
2020-06-01 22:57:46 +00:00
DynamicJsonDocument jsonBuffer ( capacity ) ;
2019-10-04 20:37:52 +00:00
auto error = deserializeJson ( jsonBuffer , buf . get ( ) ) ;
if ( error ) {
Serial . print ( F ( " deserializeJson() failed with code " ) ) ;
Serial . println ( error . c_str ( ) ) ;
return ;
} else {
Serial . println ( F ( " deserializeJson() successful: " ) ) ;
serializeJsonPretty ( jsonBuffer , Serial ) ;
Serial . println ( ) ;
String m0 = jsonBuffer [ " matrixUsername " ] ;
matrixUsername = m0 ;
String m1 = jsonBuffer [ " matrixPassword " ] ;
matrixPassword = m1 ;
2020-06-12 19:49:14 +00:00
String m2 = jsonBuffer [ " notifiedEndpoint " ] ;
notifiedEndpoint = m2 ;
2019-10-04 20:37:52 +00:00
}
configFile . close ( ) ;
}
}
} else {
Serial . println ( F ( " failed to mount FS " ) ) ;
}
//end read
2019-09-21 21:59:17 +00:00
2019-10-04 20:37:52 +00:00
// The extra parameters to be configured (can be either global or just in the setup)
// After connecting, parameter.getValue() will get you the configured value
// id/name placeholder/prompt default length
WiFiManagerParameter customMatrixUsername ( " Matrix username " , " Matrix username " , matrixUsername . c_str ( ) , 50 ) ;
WiFiManagerParameter customMatrixPassword ( " Matrix password " , " Matrix password " , matrixPassword . c_str ( ) , 50 ) ;
2020-06-12 19:49:14 +00:00
WiFiManagerParameter customNotifiedEndpoint ( " Notified endpoint " , " Notified endpoint " , notifiedEndpoint . c_str ( ) , 200 ) ;
2019-10-04 20:37:52 +00:00
//WiFiManager
//Local intialization. Once its business is done, there is no need to keep it around
//WiFiManager wifiManager;
//reset settings - for testing
//wifiManager.resetSettings();
//set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode (for status LED)
wifiManager . setAPCallback ( configModeCallback ) ;
//set config save notify callback
wifiManager . setSaveConfigCallback ( saveConfigCallback ) ;
//set static ip
// wifiManager.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
//add all your parameters here
wifiManager . addParameter ( & customMatrixUsername ) ;
wifiManager . addParameter ( & customMatrixPassword ) ;
2020-06-12 19:49:14 +00:00
wifiManager . addParameter ( & customNotifiedEndpoint ) ;
2019-10-04 20:37:52 +00:00
//reset settings - for testing
//wifiManager.resetSettings();
//sets timeout until configuration portal gets turned off
//useful to make it all retry or go to sleep
//in seconds
//wifiManager.setTimeout(120);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name
//here "AutoConnectAP"
//and goes into a blocking loop awaiting configuration
if ( ! wifiManager . autoConnect ( ) ) {
Serial . println ( F ( " failed to connect and hit timeout " ) ) ;
delay ( 3000 ) ;
//reset and try again, or maybe put it to deep sleep
2020-06-13 15:57:19 +00:00
ESP . restart ( ) ;
2019-10-04 20:37:52 +00:00
delay ( 5000 ) ;
}
//if you get here you have connected to the WiFi
Serial . println ( F ( " connected...yeey :) " ) ) ;
2020-06-12 19:49:14 +00:00
ledTicker . detach ( ) ;
2019-10-04 20:37:52 +00:00
//keep LED on
digitalWrite ( LED_PIN , LOW ) ;
//read updated parameters
matrixUsername = customMatrixUsername . getValue ( ) ;
matrixPassword = customMatrixPassword . getValue ( ) ;
2020-06-12 19:49:14 +00:00
notifiedEndpoint = customNotifiedEndpoint . getValue ( ) ;
2019-10-04 20:37:52 +00:00
//save the custom parameters to FS
if ( shouldSaveConfig ) {
Serial . println ( F ( " saving config " ) ) ;
2020-06-12 19:49:14 +00:00
const size_t capacity = JSON_OBJECT_SIZE ( 3 ) + 440 ; // https://arduinojson.org/v6/assistant/
2020-06-01 22:57:46 +00:00
DynamicJsonDocument jsonBuffer ( capacity ) ;
2019-10-04 20:37:52 +00:00
jsonBuffer [ " matrixUsername " ] = matrixUsername ;
jsonBuffer [ " matrixPassword " ] = matrixPassword ;
2020-06-12 19:49:14 +00:00
jsonBuffer [ " notifiedEndpoint " ] = notifiedEndpoint ;
2019-10-04 20:37:52 +00:00
File configFile = SPIFFS . open ( " /config.json " , " w " ) ;
if ( ! configFile ) {
Serial . println ( F ( " failed to open config file for writing " ) ) ;
}
serializeJson ( jsonBuffer , Serial ) ;
Serial . println ( ) ;
serializeJson ( jsonBuffer , configFile ) ;
configFile . close ( ) ;
//end save
2019-09-21 21:59:17 +00:00
}
2019-10-04 20:37:52 +00:00
Serial . println ( F ( " local ip: " ) ) ;
2019-09-21 21:59:17 +00:00
Serial . println ( WiFi . localIP ( ) ) ;
2019-11-01 23:39:21 +00:00
Serial . println ( WiFi . SSID ( ) ) ;
2019-10-04 20:37:52 +00:00
2019-11-01 23:39:21 +00:00
httpServer . on ( " / " , handleRoot ) ;
httpServer . on ( " /admin " , handleAdmin ) ;
httpServer . onNotFound ( handleNotFound ) ;
httpServer . begin ( ) ;
Serial . println ( " HTTP server started " ) ;
if ( ! MDNS . begin ( " presence " ) ) { // https://github.com/esp8266/Arduino/blob/14262af0d19a9a3b992d5aa310a684d47b6fb876/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino
Serial . println ( " Error setting up MDNS responder! " ) ;
} else {
Serial . println ( " mDNS responder started " ) ;
}
MDNS . addService ( " http " , " tcp " , 80 ) ;
2020-02-24 22:35:01 +00:00
secureClient . setInsecure ( ) ;
2019-09-21 18:37:23 +00:00
}
2019-09-21 18:23:39 +00:00
2019-09-29 19:24:58 +00:00
bool buttonState = HIGH ;
bool previousButtonState = HIGH ;
long previousMillis = 0 ;
2020-06-12 19:49:14 +00:00
const long notifyInterval = 5000 ;
2019-10-04 20:37:52 +00:00
unsigned long pressedTime = millis ( ) ;
2019-09-21 18:37:23 +00:00
void loop ( ) {
2019-11-01 23:39:21 +00:00
MDNS . update ( ) ;
httpServer . handleClient ( ) ;
2019-10-04 20:37:52 +00:00
buttonState = digitalRead ( BUTTON_PIN ) ;
if ( buttonState = = LOW & & previousButtonState = = HIGH ) { // long press handling, reset settings https://forum.arduino.cc/index.php?topic=276789.msg1947963#msg1947963
2019-11-01 23:39:21 +00:00
Serial . println ( " Button pressed (longpress handling) " ) ;
2019-10-04 20:37:52 +00:00
pressedTime = millis ( ) ;
}
if ( buttonState = = LOW & & previousButtonState = = LOW & & ( millis ( ) - pressedTime ) > 5000 ) {
2020-06-01 22:57:46 +00:00
Serial . println ( " Button STILL pressed (longpress handling) " ) ;
2019-10-04 20:37:52 +00:00
wifiManager . resetSettings ( ) ;
SPIFFS . format ( ) ;
delay ( 500 ) ;
2020-06-13 15:57:19 +00:00
ESP . restart ( ) ;
2019-10-04 20:37:52 +00:00
}
2019-09-29 19:24:58 +00:00
unsigned long currentMillis = millis ( ) ;
2020-06-12 19:49:14 +00:00
if ( currentMillis - previousMillis > notifyInterval ) {
2019-09-29 19:24:58 +00:00
previousMillis = currentMillis ;
2020-06-01 22:57:46 +00:00
notifyFuzIsOpen ( fuzIsOpen ) ;
2019-09-29 19:24:58 +00:00
}
2020-06-12 19:49:14 +00:00
if ( ! fuzIsOpen ) {
2020-06-01 22:57:46 +00:00
digitalWrite ( RELAY_PIN , HIGH ) ;
2020-02-24 22:14:00 +00:00
}
2019-09-29 19:24:58 +00:00
bool relayState = digitalRead ( RELAY_PIN ) ;
2019-10-04 20:37:52 +00:00
2020-06-01 22:57:46 +00:00
if ( buttonState = = LOW & & previousButtonState = = HIGH /* && relayState == HIGH*/ ) { // button just pressed while light is up
2019-09-29 19:24:58 +00:00
delay ( 100 ) ;
Serial . println ( " Button pressed " ) ;
digitalWrite ( RELAY_PIN , LOW ) ;
2019-11-01 23:39:21 +00:00
fuzIsOpen = true ;
2020-06-12 19:49:14 +00:00
notifyFuzIsOpen ( fuzIsOpen ) ;
2019-09-21 18:37:23 +00:00
}
2019-09-29 19:24:58 +00:00
previousButtonState = buttonState ;
2019-09-21 18:23:39 +00:00
}