Compare commits

...

5 Commits

6 changed files with 132 additions and 53 deletions

View File

@@ -2,11 +2,6 @@
* Libraries and global scope code * Libraries and global scope code
*/ */
unsigned long lastMillis = 0;
String devname; // Device identifier for MQTT
char nodename[80] = "UNDEF";
struct Settings { struct Settings {
String name = ""; String name = "";
String ip = ""; String ip = "";
@@ -14,6 +9,16 @@ struct Settings {
time_t now; time_t now;
/*
* Modules will plug in to this thing via function pointers
*/
// see also http://www.gammon.com.au/forum/?id=12607
//void module_message(const String& shorttopic, const String& message) { //@@@@FIXME@@@@ instead of brightness, adjust mode?
//void module_message(const String& shorttopic, const String& message) {
//
//}
/* /*
* Debugging might be nice sometimes * Debugging might be nice sometimes
*/ */

View File

@@ -13,6 +13,8 @@ WiFiClientSecure net;
//BearSSL::WiFiClientSecure net; //BearSSL::WiFiClientSecure net;
unsigned long ota_lastcheck = 0;
// //
// If the WifiManager configuration portal is called, we can do stuff // If the WifiManager configuration portal is called, we can do stuff
// //
@@ -21,9 +23,13 @@ WiFiClientSecure net;
//} //}
void wifi_associate() { void wifi_associate() {
char apname[80];
settings.name.toCharArray(apname, 80);
WifiManager.setConfigPortalTimeout(300); // If no configuration is done in 5 mins, exit/restart quietly WifiManager.setConfigPortalTimeout(300); // If no configuration is done in 5 mins, exit/restart quietly
if (WifiManager.autoConnect(nodename)) { // @@@FIXME@@@ we should really set a PSK for the AP (but in my brief testing it crashed the ESP, so what's up with that) if (WifiManager.autoConnect(apname)) { // @@@FIXME@@@ we should really set a PSK for the AP (but in my brief testing it crashed the ESP, so what's up with that)
PRINTLN_SERIAL("Wifi connected"); settings.ip = WiFi.localIP().toString();
PRINTLN_SERIAL("Figured out my network \\O/");
//wificlient.setFingerprint(WTR_SHA1); //wificlient.setFingerprint(WTR_SHA1);
net.setInsecure(); // Do not check fingerprint net.setInsecure(); // Do not check fingerprint
@@ -41,35 +47,102 @@ void wifi_associate() {
struct tm timeinfo; struct tm timeinfo;
gmtime_r(&now, &timeinfo); gmtime_r(&now, &timeinfo);
PRINT_SERIAL("Current time: "); PRINT_SERIAL("Current time: ");
APPENDLN_SERIAL(asctime(&timeinfo)); APPENDLN_SERIAL(asctime(&timeinfo));
} }
} }
void wifi_checkin() {
// 'Native' in WiFiClient because we need to push the entire clientbuffer in the API
// As a result copying the buffer in to a new String for HTTPClient library use is highly inefficient
if (!net.connect(OTA_SERVER, OTA_PORT)) {
Serial.println("HTTPS connection failed");
} else {
Serial.println("HTTPS connection up");
// Check for firmware upgrades
String version = settings.name;
version.concat("-v");
version.concat(FW_VERSION);
String json = "{\"mac\":\"" + WiFi.macAddress() + "\",";
json.concat("\"version\":\"" + version + "\",");
json.concat("\"millis\":" + String(millis()) + "}");
// Send request to the server:
net.println("POST " OTA_CHECKIN " HTTP/1.1");
net.println("Host: " OTA_SERVER);
net.println("Accept: */*");
net.println("Content-Type: application/json");
net.print("Content-Length: ");
net.println(json.length());
net.println("Connection: close");
net.println();
// Construct the REST/JSON POST data
net.print(json);
Serial.println("POST sent");
while (net.connected()) {
String line = net.readStringUntil('\n');
if (line == "\r") {
Serial.println("Headers received");
break;
}
}
String line = net.readStringUntil('\n');
Serial.println("reply was:");
Serial.println("==========");
Serial.println(line);
Serial.println("==========");
Serial.println("closing connection");
net.stop(); // DISCONNECT FROM THE SERVER
}
}
void wifi_setup() { void wifi_setup() {
// WifiManager.setAPCallback(WifiManagerCallback); // Enable WifiManager callback to clear the cache // WifiManager.setAPCallback(WifiManagerCallback); // Enable WifiManager callback to clear the cache
wifi_associate(); wifi_associate();
} }
void wifi_loop() { void wifi_loop() {
if(WiFi.status() != WL_CONNECTED) { if(WiFi.status() != WL_CONNECTED) {
PRINTLN_SERIAL("Wifi disconnected, trying to reconnect"); PRINTLN_SERIAL("Wifi disconnected, trying to reconnect");
wifi_associate(); wifi_associate();
} }
}
/* // OTA routine to be run once every X seconds
unsigned long ota_check = millis()/1000;
if((ota_lastcheck == 0) || ((ota_check - ota_lastcheck) > OTA_UPDATE_INTERVAL)) {
PRINTLN_SERIAL("Checking for OTA update");
// Check for firmware upgrades // Check for firmware upgrades
Serial.println("Checking for firmware upgrade"); String version = settings.name;
t_httpUpdate_return ret = ESPhttpUpdate.update(wificlient, WTR_SERVER, WTR_SRVPORT, "/fw/", nodeversion); version.concat("-v");
version.concat(FW_VERSION);
PRINTLN_SERIAL("net.status: " + String(net.status()));
t_httpUpdate_return ret = ESPhttpUpdate.update(net, OTA_SERVER, OTA_PORT, OTA_URI, version);
PRINTLN_SERIAL("after httpupdate check net.status: " + String(net.status()));
switch(ret) { switch(ret) {
case HTTP_UPDATE_FAILED: // HTTP 403, 404 case HTTP_UPDATE_FAILED: // HTTP 403, 404
Serial.printf("Firmware upgrade available, but update failed (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); PRINT_SERIAL("Connection error, update failed ");
APPEND_SERIAL("(ERR ");
APPEND_SERIAL(String(ESPhttpUpdate.getLastError()));
APPEND_SERIAL(") ");
APPENDLN_SERIAL(ESPhttpUpdate.getLastErrorString().c_str());
break; break;
case HTTP_UPDATE_NO_UPDATES: // HTTP 304 case HTTP_UPDATE_NO_UPDATES: // HTTP 304
Serial.println("No firmware upgrade available"); PRINTLN_SERIAL("No firmware upgrade available");
break; break;
case HTTP_UPDATE_OK: // HTTP 200 case HTTP_UPDATE_OK: // HTTP 200
Serial.println("Firmware upgrade available, upgraded ok."); // may not called we reboot the ESP PRINTLN_SERIAL("Firmware upgrade available, upgraded ok."); // may not called we reboot the ESP
break; break;
} }
ota_lastcheck = ota_check;
*/ // Check in with IoT server
wifi_checkin();
}
}

View File

@@ -1,23 +1,13 @@
#ifdef ENABLE_MQTT #ifdef ENABLE_MQTT
// sample schema's to use: https://www.home-assistant.io/integrations/light.mqtt/ // sample schema's to use: https://www.home-assistant.io/integrations/light.mqtt/
/*
* Define a function newcmd in your module to be able to accept mqtt commands
* Syntax and parameters are like this:
*/
void newcmd(String cmd);
void module_message(const String& shorttopic, const String& message);
#include <PubSubClient.h> #include <PubSubClient.h>
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. // Setup the MQTT client class by passing in the WiFi client
PubSubClient mqtt(net); PubSubClient mqtt(net);
String deviceprefix; String deviceprefix;
const char* fingerprint = "EA:2A:2B:80:C1:00:57:35:66:26:FF:FC:FA:27:EA:5F:3D:91:5A:F9";
long logtimerMillis = 0; long logtimerMillis = 0;
void mqtt_subscribe(const String& topic) { void mqtt_subscribe(const String& topic) {
@@ -76,24 +66,37 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
} }
void mqtt_connect() { void mqtt_connect() {
/* bool success= false;
success = net.connect(MQTT_SERVER, MQTT_SERVERPORT);
if (success) {
PRINTLN_SERIAL("Connection complete, valid cert, valid fingerprint.");
} else {
PRINTLN_SERIAL("Connection failed!");
} */
PRINTLN_SERIAL("prepare mqtt connect check net.status: " + String(net.status()));
mqtt.setServer(MQTT_SERVER, MQTT_SERVERPORT); mqtt.setServer(MQTT_SERVER, MQTT_SERVERPORT);
mqtt.setCallback(mqtt_callback); mqtt.setCallback(mqtt_callback);
PRINTLN_SERIAL("before mqtt connect check net.status: " + String(net.status()));
if(!mqtt.connected()) {
while (!mqtt.connected()) { while (!mqtt.connected()) {
PRINTLN_SERIAL("MQTT not connected, connecting..."); PRINTLN_SERIAL("MQTT not connected, connecting...");
PRINTLN_SERIAL("while mqtt connecting check net.status: " + String(net.status()));
// Attempt to connect
// Note - the default maximum packet size is 128 bytes. If the // Attempt to connect
// combined length of clientId, username and password exceed this, // Note - the default maximum packet size is 128 bytes. If the
// you will need to increase the value of MQTT_MAX_PACKET_SIZE in // combined length of clientId, username and password exceed this,
// PubSubClient.h // you will need to increase the value of MQTT_MAX_PACKET_SIZE in
if (mqtt.connect(settings.name.c_str(), MQTT_USERNAME, MQTT_PASSWORD)) { // PubSubClient.h
PRINTLN_SERIAL("MQTT connected"); if (mqtt.connect(settings.name.c_str(), MQTT_USERNAME, MQTT_PASSWORD)) {
} else { PRINTLN_SERIAL("MQTT connected");
PRINTLN_SERIAL("MQTT connection failed, rc=" + String(mqtt.state()) + "..."); PRINTLN_SERIAL("mqtt just connected check net.status: " + String(net.status()));
delay(10000); // @@@FIXME@@@ delays are not cool
} } else {
PRINTLN_SERIAL("MQTT connection failed, rc=" + String(mqtt.state()) + "...");
PRINTLN_SERIAL("mqtt failed connected check net.status: " + String(net.status()));
delay(10000); // @@@FIXME@@@ delays are not cool
} }
// (re)subscribe to the topics we need // (re)subscribe to the topics we need
@@ -104,12 +107,13 @@ void mqtt_connect() {
mqtt_subscribe(deviceprefix + "#"); mqtt_subscribe(deviceprefix + "#");
mqtt_publish(deviceprefix + "log", "Connected " + settings.name + " running v" + FW_VERSION + " at " + settings.ip); mqtt_publish(deviceprefix + "log", "Connected " + settings.name + " running v" + FW_VERSION + " at " + settings.ip);
mqtt_publish(deviceprefix + "log", "Free Heap: " + String(ESP.getFreeHeap())); mqtt_publish(deviceprefix + "log", "Free Heap: " + String(ESP.getFreeHeap()));
PRINTLN_SERIAL("after mqtt connect check net.status: " + String(net.status()));
} }
} }
void mqtt_setup() { void mqtt_setup() {
PRINTLN_SERIAL(devname + "initialising module MQTT"); PRINTLN_SERIAL("Initialising MQTT");
} }
void mqtt_loop() { void mqtt_loop() {

View File

@@ -6,14 +6,10 @@
void newcmd(String cmd) { void newcmd(String cmd) {
cmd.toLowerCase(); cmd.toLowerCase();
if(cmd.equals("off")) { if(cmd.equals("off")) {
settings.mode = 0;
PRINTLN_SERIAL("Lights off"); PRINTLN_SERIAL("Lights off");
state.text = "Sonoff switched off";
} }
if(cmd.equals("on")) { if(cmd.equals("on")) {
settings.mode = 1;
PRINTLN_SERIAL("Lights on"); PRINTLN_SERIAL("Lights on");
state.text = "Sonoff switched off";
} }
} }

View File

@@ -23,7 +23,7 @@ void setup() {
// If we run in to trouble, this can painlessly be removed and the resulting device name will just be longer // If we run in to trouble, this can painlessly be removed and the resulting device name will just be longer
settings.name.remove(devlen+1, 6); settings.name.remove(devlen+1, 6);
PRINTLN_SERIAL("device name generated"); PRINTLN_SERIAL("Figured out who I am \\O/");
wifi_setup(); wifi_setup();
@@ -37,7 +37,7 @@ void setup() {
moon_setup(); moon_setup();
#endif #endif
// Start the cooperative scheduler loops // Start the cooperative scheduler loops
PRINTLN_SERIAL(devname + "done setting up, ready for main loop"); PRINTLN_SERIAL("Done setting up, ready for main loop \\O/");
} }
void loop() { void loop() {

View File

@@ -13,7 +13,7 @@
// Implement versioning so that new firmware can be done OTA // Implement versioning so that new firmware can be done OTA
// Todo: // Todo:
// Split the wifi and mqtt code in order to support http interface // maybe: loading settings on SPIFFS or similar so we can create more generic firmware? https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
// Stay on the lookout for watchdog crashes, see also https://www.sigmdel.ca/michel/program/esp8266/arduino/watchdogs_en.html#ESP8266_WDT_RECOV // Stay on the lookout for watchdog crashes, see also https://www.sigmdel.ca/michel/program/esp8266/arduino/watchdogs_en.html#ESP8266_WDT_RECOV
// Keeps crashing after a few days. Dunno why. https://www.pieterverhees.nl/sparklesagarbage/esp8266/130-difference-between-esp-reset-and-esp-restart // Keeps crashing after a few days. Dunno why. https://www.pieterverhees.nl/sparklesagarbage/esp8266/130-difference-between-esp-reset-and-esp-restart
// More on this subject https://github.com/esp8266/Arduino/issues/1017 // More on this subject https://github.com/esp8266/Arduino/issues/1017
@@ -30,11 +30,12 @@
// (background: http://iot-bits.com/esp8266/esp8266-reset-causes-rst-cause/ // (background: http://iot-bits.com/esp8266/esp8266-reset-causes-rst-cause/
// Version history: // Version history:
// v14: Initial OTA support
// v13: Refactored so config-settings are in config.h, move from Adafruit Neopixel to WS2812FX library // v13: Refactored so config-settings are in config.h, move from Adafruit Neopixel to WS2812FX library
// v12: Refactored inbound commands to hook in to the modules (module_message function), completed assimilation of (never-published) Moon_Connected sketch // v12: Refactored inbound commands to hook in to the modules (module_message function), completed assimilation of (never-published) Moon_Connected sketch
// v11: Simplified scheduler, back to cooperative looping // v11: Simplified scheduler, back to cooperative looping
// v10: First 'production' run. MQTT over TLS, but no OTA support. // v10: First 'production' run. MQTT over TLS, but no OTA support.
const int FW_VERSION = 13; const int FW_VERSION = 14;
// //
// Configuration settings // Configuration settings