diff --git a/README.md b/README.md index 51d10ac..7e6c0fc 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,4 @@ Developed with the Arduino IDE, the script relies on the following third-party l * https://arduinojson.org/ v6 6.13.0 * https://github.com/tzapu/WiFiManager 0.15.0-beta +* https://github.com/krzychb/EspSaveCrash 1.0.2 diff --git a/presence-button.ino b/main.ino similarity index 62% rename from presence-button.ino rename to main.ino index 58c5b44..77d9a4a 100644 --- a/presence-button.ino +++ b/main.ino @@ -1,3 +1,4 @@ + #include //this needs to be first, or it all crashes and burns... #include //https://github.com/esp8266/Arduino //needed for library @@ -8,19 +9,19 @@ #include //https://github.com/bblanchon/ArduinoJson #include // https://tttapa.github.io/ESP8266/Chap08%20-%20mDNS.html -#include +#include //for LED status #include -Ticker ledTicker; +Ticker ticker; -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 +const byte RELAY_PIN = 12; +const byte LED_PIN = 13; // 13 for Sonoff S20, 2 for NodeMCU/ESP12 internal LED const byte BUTTON_PIN = 0; bool fuzIsOpen = false; -void blinkLED() { +void tick() { //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 @@ -33,14 +34,15 @@ void configModeCallback (WiFiManager *myWiFiManager) { //if you used auto generated SSID, print it Serial.println(myWiFiManager->getConfigPortalSSID()); //entered config mode, make led toggle faster - ledTicker.attach(0.5, blinkLED); + ticker.attach(0.2, tick); } //define your default values here, if there are different values in config.json, they are overwritten. String matrixUsername; String matrixPassword; -String notifiedEndpoint = "https://presence-button-staging.glitch.me/status?fuzisopen="; // #entropy:matrix.fuz.re +String matrixRoom = "!ppCFWxNWJeGbyoNZVw:matrix.fuz.re"; // #entropy:matrix.fuz.re +String matrixMessage = "Test"; //flag for saving data bool shouldSaveConfig = false; @@ -54,8 +56,89 @@ void saveConfigCallback () { WiFiManager wifiManager; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include // for https://github.com/matt-williams/matrix-esp8266/blob/master/matrix-esp8266.ino +HTTPClient http; +String accessToken; +String lastMessageToken; + +String createLoginBody(String user, String password) { + String buffer; + StaticJsonDocument<1000> jsonBuffer; + //JsonObject& root = jsonBuffer.createObject(); + jsonBuffer["type"] = "m.login.password"; + jsonBuffer["user"] = user; + jsonBuffer["password"] = password; + jsonBuffer["identifier"]["type"] = "m.id.user"; + jsonBuffer["identifier"]["user"] = user; + serializeJson(jsonBuffer, buffer); + return buffer; +} + +String createMessageBody(String message) { + String buffer; + StaticJsonDocument<1000> jsonBuffer; + jsonBuffer["msgtype"] = "m.text"; + jsonBuffer["body"] = message; + serializeJson(jsonBuffer, buffer); + return buffer; +} + +bool login(String user, String password) { + bool success = false; + + String buffer; + buffer = createLoginBody(user.substring(0, user.indexOf(":")), password); + + String url = "http://corsanywhere.glitch.me/https://" + user.substring(user.indexOf(":") + 1) + "/_matrix/client/r0/login"; + // Serial.printf("POST %s\n", url.c_str()); + + http.begin(url); + http.addHeader("Content-Type", "application/json"); + int rc = http.POST(buffer); + Serial.printf("buffer %s\n", buffer.c_str()); + if (rc > 0) { + Serial.printf("Login return code %d\n", rc); + if (rc == HTTP_CODE_OK) { + String body = http.getString(); + StaticJsonDocument<1000> jsonBuffer; + deserializeJson(jsonBuffer, body); + String myAccessToken = jsonBuffer["access_token"]; + accessToken = String(myAccessToken.c_str()); + Serial.println(accessToken); + success = true; + } + } else { + Serial.printf("Error: %s\n", http.errorToString(rc).c_str()); + } + + return success; +} + +bool sendMessage(String roomId, String message) { + bool success = false; + + String buffer; + buffer = createMessageBody(message); + + String url = "http://corsanywhere.glitch.me/https://" + roomId.substring(roomId.indexOf(":") + 1) + "/_matrix/client/r0/rooms/" + roomId + "/send/m.room.message/" + String(millis()) + "?access_token=" + accessToken + "&limit=1"; + Serial.printf("PUT %s\n", url.c_str()); + + http.begin(url); + int rc = http.sendRequest("PUT", buffer); + if (rc > 0) { + // Serial.printf("%d\n", rc); + if (rc == HTTP_CODE_OK) { + success = true; + } + } else { + Serial.printf("Error: %s\n", http.errorToString(rc).c_str()); + } + return success; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ESP8266WebServer httpServer(80); // webserver on port 80 https://github.com/esp8266/Arduino/blob/14262af0d19a9a3b992d5aa310a684d47b6fb876/libraries/ESP8266WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino void handleRoot() { String html = @@ -88,7 +171,7 @@ void handleAdmin() { httpServer.sendHeader("Location", httpServer.uri(), true); httpServer.send(302, "text/plain", ""); delay(500); - ESP.restart(); + ESP.reset(); return; } } @@ -103,17 +186,21 @@ void handleAdmin() { matrixPassword = httpServer.arg(i); continue; } - if (httpServer.argName(i) == "notifiedEndpoint") { - notifiedEndpoint = httpServer.arg(i); + if (httpServer.argName(i) == "matrixRoom") { + matrixRoom = httpServer.arg(i); + continue; + } + if (httpServer.argName(i) == "matrixMessage") { + matrixMessage = httpServer.arg(i); continue; } } Serial.println(F("saving config")); - const size_t capacity = JSON_OBJECT_SIZE(3) + 440; // https://arduinojson.org/v6/assistant/ - DynamicJsonDocument jsonBuffer(capacity); + StaticJsonDocument<800> jsonBuffer; jsonBuffer["matrixUsername"] = matrixUsername; jsonBuffer["matrixPassword"] = matrixPassword; - jsonBuffer["notifiedEndpoint"] = notifiedEndpoint; + jsonBuffer["matrixRoom"] = matrixRoom; + jsonBuffer["matrixMessage"] = matrixMessage; File configFile = SPIFFS.open("/config.json", "w"); if (!configFile) { @@ -139,7 +226,8 @@ void handleAdmin() { "
" + "
" + "
" + - "
" + + "
" + + "
" + "
" ""; httpServer.send(200, "text/html", html); @@ -148,21 +236,39 @@ void handleNotFound() { httpServer.send(404, "text/plain", httpServer.uri() + " not found"); } -BearSSL::WiFiClientSecure secureClient; -HTTPClient http; -void notifyFuzIsOpen(bool fuzIsOpen) { - http.begin(secureClient, notifiedEndpoint + String(fuzIsOpen)); - http.setAuthorization(matrixUsername.c_str(), matrixPassword.c_str()); - int httpCode = http.GET(); - Serial.println("notifyFuzIsOpen body: " + http.getString()); - Serial.println("notifyFuzIsOpen return code: " + String(httpCode)); - http.end(); - if (httpCode != 200) { // something is wrong, bad network or misconfigured credentials - ledTicker.attach(0.1, blinkLED); - } else { - ledTicker.detach(); - digitalWrite(LED_PIN, LOW); // ensure the LED is lit +void morseSOSLED() { // ... ___ ... + for (int i = 0; i < 3; i++) { + digitalWrite(LED_PIN, LOW); // lit up + delay(100); + digitalWrite(LED_PIN, HIGH); // lit down + delay(100); } + delay(50); + for (int i = 0; i < 3; i++) { + digitalWrite(LED_PIN, LOW); // lit up + delay(300); + digitalWrite(LED_PIN, HIGH); // lit down + delay(100); + } + delay(50); + for (int i = 0; i < 3; i++) { + digitalWrite(LED_PIN, LOW); // lit up + delay(100); + digitalWrite(LED_PIN, HIGH); // lit down + delay(100); + } + delay(500); +} + +BearSSL::WiFiClientSecure secureClient; +HTTPClient http2; +// http2.setReuse(true); +void notifyFuzIsOpen() { + http2.begin(secureClient, "https://presence-button.glitch.me/status?fuzisopen=" + String(fuzIsOpen)); + http2.setAuthorization(matrixUsername.c_str(), matrixPassword.c_str()); + int httpCode = http2.GET(); + Serial.println("GET status return code: " + String(httpCode)); + http2.end(); } bool loggedInMatrix = false; @@ -177,8 +283,8 @@ void setup() { //set button pin as input pinMode(BUTTON_PIN, INPUT); - // start ledTicker with 1 because we start in AP mode and try to connect - ledTicker.attach(1.1, blinkLED); + // start ticker with 0.5 because we start in AP mode and try to connect + ticker.attach(0.6, tick); Serial.println(F("mounting FS...")); @@ -195,8 +301,7 @@ void setup() { std::unique_ptr buf(new char[size]); configFile.readBytes(buf.get(), size); - const size_t capacity = JSON_OBJECT_SIZE(3) + 440; // https://arduinojson.org/v6/assistant/ - DynamicJsonDocument jsonBuffer(capacity); + StaticJsonDocument<800> jsonBuffer; auto error = deserializeJson(jsonBuffer, buf.get()); if (error) { Serial.print(F("deserializeJson() failed with code ")); @@ -212,8 +317,10 @@ void setup() { matrixUsername = m0; String m1 = jsonBuffer["matrixPassword"]; matrixPassword = m1; - String m2 = jsonBuffer["notifiedEndpoint"]; - notifiedEndpoint = m2; + String m2 = jsonBuffer["matrixRoom"]; + matrixRoom = m2; + String m3 = jsonBuffer["matrixMessage"]; + matrixMessage = m3; } configFile.close(); } @@ -230,7 +337,8 @@ void setup() { // 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); - WiFiManagerParameter customNotifiedEndpoint("Notified endpoint", "Notified endpoint", notifiedEndpoint.c_str(), 200); + WiFiManagerParameter customMatrixRoom("Matrix room", "Matrix room", matrixRoom.c_str(), 200); + WiFiManagerParameter customMatrixMessage("Matrix message", "Matrix message", matrixMessage.c_str(), 500); //WiFiManager //Local intialization. Once its business is done, there is no need to keep it around @@ -250,7 +358,8 @@ void setup() { //add all your parameters here wifiManager.addParameter(&customMatrixUsername); wifiManager.addParameter(&customMatrixPassword); - wifiManager.addParameter(&customNotifiedEndpoint); + wifiManager.addParameter(&customMatrixRoom); + wifiManager.addParameter(&customMatrixMessage); //reset settings - for testing //wifiManager.resetSettings(); @@ -268,29 +377,30 @@ void setup() { Serial.println(F("failed to connect and hit timeout")); delay(3000); //reset and try again, or maybe put it to deep sleep - ESP.restart(); + ESP.reset(); delay(5000); } //if you get here you have connected to the WiFi Serial.println(F("connected...yeey :)")); - ledTicker.detach(); + ticker.detach(); //keep LED on digitalWrite(LED_PIN, LOW); //read updated parameters matrixUsername = customMatrixUsername.getValue(); matrixPassword = customMatrixPassword.getValue(); - notifiedEndpoint = customNotifiedEndpoint.getValue(); + matrixRoom = customMatrixRoom.getValue(); + matrixMessage = customMatrixMessage.getValue(); //save the custom parameters to FS if (shouldSaveConfig) { Serial.println(F("saving config")); - const size_t capacity = JSON_OBJECT_SIZE(3) + 440; // https://arduinojson.org/v6/assistant/ - DynamicJsonDocument jsonBuffer(capacity); + StaticJsonDocument<800> jsonBuffer; jsonBuffer["matrixUsername"] = matrixUsername; jsonBuffer["matrixPassword"] = matrixPassword; - jsonBuffer["notifiedEndpoint"] = notifiedEndpoint; + jsonBuffer["matrixRoom"] = matrixRoom; + jsonBuffer["matrixMessage"] = matrixMessage; File configFile = SPIFFS.open("/config.json", "w"); if (!configFile) { @@ -308,6 +418,16 @@ void setup() { Serial.println(WiFi.localIP()); Serial.println(WiFi.SSID()); + http.setReuse(true); + loggedInMatrix = login(matrixUsername, matrixPassword); + if (loggedInMatrix) { + Serial.println("Sucessfully athenticated"); + //keep LED on + digitalWrite(LED_PIN, LOW); + //light up rotating light + digitalWrite(RELAY_PIN, HIGH); + } + httpServer.on("/", handleRoot); httpServer.on("/admin", handleAdmin); httpServer.onNotFound(handleNotFound); @@ -326,7 +446,7 @@ void setup() { bool buttonState = HIGH; bool previousButtonState = HIGH; long previousMillis = 0; -const long notifyInterval = 5000; +const long getMatrixMessagesInterval = 5000; unsigned long pressedTime = millis(); void loop() { MDNS.update(); @@ -339,30 +459,32 @@ void loop() { pressedTime = millis(); } if (buttonState == LOW && previousButtonState == LOW && (millis() - pressedTime) > 5000) { - Serial.println("Button STILL pressed (longpress handling)"); wifiManager.resetSettings(); SPIFFS.format(); delay(500); - ESP.restart(); - } - unsigned long currentMillis = millis(); - if (currentMillis - previousMillis > notifyInterval) { - previousMillis = currentMillis; - notifyFuzIsOpen(fuzIsOpen); + ESP.reset(); } - if (!fuzIsOpen) { - digitalWrite(RELAY_PIN, HIGH); + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis > getMatrixMessagesInterval) { + previousMillis = currentMillis; + notifyFuzIsOpen(); + } + + if (!loggedInMatrix) { // send SOS in morse + morseSOSLED(); + loggedInMatrix = login(matrixUsername, matrixPassword); + return; } bool relayState = digitalRead(RELAY_PIN); - if (buttonState == LOW && previousButtonState == HIGH /* && relayState == HIGH*/) { // button just pressed while light is up + if (buttonState == LOW && previousButtonState == HIGH && relayState == HIGH && sendMessage(matrixRoom, matrixMessage)) { // button just pressed while light is up delay(100); Serial.println("Button pressed"); digitalWrite(RELAY_PIN, LOW); fuzIsOpen = true; - notifyFuzIsOpen(fuzIsOpen); } + digitalWrite(LED_PIN, LOW); // ensure the LED is lit previousButtonState = buttonState; }