#include "MQTT.h" #define MDNS_SERVICENAME "" WiFiUDP MQTT::udp; const char* MQTT::months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; AsyncWebServer* MQTT::server = new AsyncWebServer(80); AsyncWebSocket* MQTT::ws = new AsyncWebSocket("/ws"); bool MQTT::saveConfigFlag=false; WiFiManagerParameter custom_hostname("hostname", "Hostname", "", 40); WiFiManagerParameter custom_mqtt_server("server", "mqtt server", "", 40); WiFiManagerParameter custom_mqtt_port("port", "mqtt port", "",6); WiFiManagerParameter custom_mqtt_topic("topic", "topic", "", 40); WiFiManagerParameter custom_deftemp("deftemp", "Default Temperature", "", 6); WiFiManagerParameter custom_enbuff("enBuff", "Enable heatBuffer", "", 6); WiFiManagerParameter custom_ntpServer("ntpServer", "NTP Server", "", 40); MQTT::MQTT(){ } void MQTT::begin(){ saveConfigFlag = false; Settings::getInstance().loadSettings(); // Load dataset from persistent storage wm.setSaveConfigCallback(MQTT::saveConfigCallback); custom_hostname.setValue(Settings::prefs.hostname,40); custom_mqtt_server.setValue(Settings::prefs.mqtt_server,40); custom_mqtt_port.setValue(String(Settings::prefs.mqtt_port).c_str(),6); custom_mqtt_topic.setValue(Settings::prefs.mqtt_topic,40); custom_deftemp.setValue(String(Settings::prefs.defTemp).c_str(),6); custom_enbuff.setValue(String(Settings::prefs.enBuff).c_str(),6); custom_ntpServer.setValue(String(Settings::prefs.ntpServer).c_str(),40); wm.setCleanConnect(true); wm.setConnectRetries(5); wm.setWiFiAutoReconnect(true); wm.setMinimumSignalQuality(20); WiFi.persistent(true); WiFi.setAutoConnect(true); WiFi.setAutoReconnect(true); wm.addParameter(&custom_hostname); wm.addParameter(&custom_mqtt_server); wm.addParameter(&custom_mqtt_port); wm.addParameter(&custom_mqtt_topic); wm.addParameter(&custom_deftemp); wm.addParameter(&custom_enbuff); wm.addParameter(&custom_ntpServer); psclient.setClient(client); psclient.setServer(Settings::prefs.mqtt_server,Settings::prefs.mqtt_port); psclient.setCallback(receiveTopic); WiFi.setHostname(custom_hostname.getValue()); //define hostname wm.setConfigPortalBlocking(false); if(wm.autoConnect("TempReglerAP")){ //if you get here you have connected to the WiFi Serial.println("connected...yeey :)"); strcpy(glblData.myIP,WiFi.localIP().toString().c_str()); wm.setHttpPort(8080); wm.startWebPortal(); wifiConnected = true; } else { wm.startWebPortal(); Serial.println("non blocking config portal running on Port 80"); } if(wifiConnected){ setenv("TZ", TZ_INFO, 1); // Zeitzone muss nach dem reset neu eingestellt werden tzset(); configTzTime(TZ_INFO, Settings::prefs.ntpServer); // ESP32 Systemzeit mit NTP Synchronisieren ArduinoOTA.setHostname(Settings::prefs.hostname); ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .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(); server->on("/", MQTT::serveData); server->on("/override", MQTT::serveOverride); server->on("/data", MQTT::serveData); server->begin(); if (MDNS.begin(Settings::prefs.hostname)) { Serial.println("mDNS responder started"); // Add service to MDNS-SD MDNS.addService(MDNS_SERVICENAME, "tcp", 80); MDNS.addService("http", "tcp", 80); } reconnect(); } } void MQTT::saveConfigCallback(void){ Serial.println("SAVE"); saveConfigFlag = true; } void MQTT::saveConfigToFlash(void){ strcpy(Settings::prefs.mqtt_server,custom_mqtt_server.getValue()); Settings::prefs.mqtt_port = atoi(custom_mqtt_port.getValue()); strcpy(Settings::prefs.hostname,custom_hostname.getValue()); strcpy(Settings::prefs.mqtt_topic,custom_mqtt_topic.getValue()); strcpy(Settings::prefs.ntpServer,custom_ntpServer.getValue()); Settings::prefs.defTemp = atof(custom_deftemp.getValue()); if(custom_enbuff.getValue()[0] != '0'){ Settings::prefs.enBuff = true; }else{ Settings::prefs.enBuff = false; } Settings::getInstance().saveSettings(); // Save dataset to persistent storage } void MQTT::receiveTopic(char* topic, byte* payload, unsigned int length) { glblData.outTemp = String((char*)payload).toFloat(); } void MQTT::reconnect(void) { // Loop until we're reconnected Serial.print("Attempting MQTT connection..."); // Attempt to connect if (psclient.connect(Settings::prefs.hostname)) { psclient.subscribe("weatherStation/tempAmb"); Serial.println("connected"); } else { Serial.print("failed, rc="); Serial.print(psclient.state()); } } void MQTT::loop(void){ static uint32_t resetCnt = 0; unsigned long now = millis(); if(now-lastStatusUpdate > 15000){ //update wifi status every 15secs. lastStatusUpdate = now; if(WiFi.isConnected()){ resetCnt = 0; if(WiFi.getMode() == WIFI_MODE_STA){ glblData.wifiMode = WIFIMODE_STA; }else{ glblData.wifiMode = WIFIMODE_AP; } int32_t rssi = WiFi.RSSI(); if(rssi > -55){ glblData.wifiStrength = WIFISTRENGTH_HIGH; }else if(rssi > -75){ glblData.wifiStrength = WIFISTRENGTH_MED; }else if(rssi > -90){ glblData.wifiStrength = WIFISTRENGTH_LOW; }else{ glblData.wifiStrength = WIFISTRENGTH_OFF; } if(!psclient.connected()){ glblData.wifiStrength = WIFISTRENGTH_ERROR; if(now-lastReconnectTry > 60000){ lastReconnectTry = now; reconnect(); } } }else if(WiFi.getMode() == WIFI_MODE_STA){ resetCnt = 0; glblData.wifiStrength = WIFISTRENGTH_OFF; WiFi.disconnect(true); delay(200); WiFi.enableSTA(true); wm.connectWifi("", "", true); }else{ if(wm.getWiFiIsSaved()){ resetCnt++; if(resetCnt > 5*5){ Serial.println("Resetting WiFi AP..."); ESP.restart(); } } glblData.wifiMode = WIFIMODE_AP; switch(glblData.wifiStrength){ case WIFISTRENGTH_HIGH: glblData.wifiStrength = WIFISTRENGTH_LOW; break; case WIFISTRENGTH_MED: glblData.wifiStrength = WIFISTRENGTH_HIGH; break; case WIFISTRENGTH_LOW: glblData.wifiStrength = WIFISTRENGTH_MED; break; default: glblData.wifiStrength = WIFISTRENGTH_LOW; break; } } } psclient.loop(); ArduinoOTA.handle(); wm.process(); if(saveConfigFlag){ Serial.println("Save Settings"); saveConfigFlag = false; saveConfigToFlash(); Serial.println("disconnect mqtt to activate new settings"); psclient.disconnect(); } } void MQTT::publish(const char* msg){ psclient.publish(Settings::prefs.mqtt_topic,msg); } void MQTT::publish(const char* msg, unsigned int len){ psclient.publish(Settings::prefs.mqtt_topic,msg,len); } void MQTT::publish(const char* msg, bool retained){ psclient.publish(Settings::prefs.mqtt_topic,msg,retained); } void MQTT::publish_sub(const char* subtopic, const char* msg, bool retained){ char newtopic[60]; strcpy(newtopic,Settings::prefs.mqtt_topic); strcat(newtopic,"/"); strcat(newtopic,subtopic); psclient.publish(newtopic,msg, retained); } /* void MQTT::handleWebSocketMessage(void *arg, uint8_t *data, size_t len){ data[len] = 0; //ensure termination! Serial.printf("WebSocket Msg '%s'\n", data); } void MQTT::handleWebSocket(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { switch (type) { case WS_EVT_CONNECT: Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); client->text(makeStatusJSON()); client->text(makeTimerJSON()); break; case WS_EVT_DISCONNECT: Serial.printf("WebSocket client #%u disconnected\n", client->id()); break; case WS_EVT_DATA: handleWebSocketMessage(arg, data, len); break; case WS_EVT_PONG: case WS_EVT_ERROR: break; } } */ void MQTT::serveOverride(AsyncWebServerRequest *request){ String sending; sending = "{"; if(Settings::prefs.enBuff){ if(request->hasParam("sw")){ int ovr = request->getParam("valve")->value().toInt(); if(ovr == 1){ glblData.override = OVR_ALWAYSON; sending = "{\"Status\": \"Success\", "; }else if(ovr == 0){ glblData.override = OVR_ALWAYSOFF; sending = "{\"Status\": \"Success\", "; }else{ glblData.override = OVR_NONE; sending = "{\"Status\": \"Success\", "; } } }else{ sending = "{\"Status\": \"Not Enabled\", "; glblData.override = OVR_NONE; } switch(glblData.override){ case OVR_ALWAYSOFF: sending += "\"Override\": \"Always OFF\"}"; break; case OVR_ALWAYSON: sending += "\"Override\": \"Always ON\"}"; break; case OVR_NONE: sending += "\"Override\": \"NONE\"}"; break; } request->send(404, "text/plain", sending); } void MQTT::serveData(AsyncWebServerRequest *request){ String sending; sending = "{\"Temp[degC]\": "; sending += glblData.temp; sending += ", \"pressure[hPa]\": "; sending += glblData.seaLevelPress; sending += ", \"rHum[%]\": "; sending += glblData.hum; sending += ", \"Heating\": "; sending += glblData.heating; sending += ", \"Set Temp[degC]\": "; sending += glblData.settemp; sending += " }"; Serial.println("Sending website..."); request->send(200, FPSTR(HTTP_HEAD_JSON), sending); Serial.println("Sent website..."); } void MQTT::sendStatus(void){ tm timeinfo; char buffer[35]; strftime(buffer, sizeof(buffer), "%FT%T%z", &glblData.bootTime); publish_sub("Boot-Time",buffer,true); publish_sub("hostname", Settings::prefs.hostname, true); publish_sub("IP", glblData.myIP, true); publish_sub("NTP-Server", Settings::prefs.ntpServer, true); if(!getLocalTime(&timeinfo)){ strcpy(buffer, "00-00-0000T00:00:00+0000"); }else{ strftime(buffer, sizeof(buffer), "%FT%T%z", &timeinfo); } publish_sub("timestamp",buffer,true); snprintf(buffer, sizeof buffer, "%0.1f", glblData.temp); publish_sub("Temp[degC]",buffer,true); snprintf(buffer, sizeof buffer, "%0.0f", glblData.seaLevelPress); publish_sub("pressure[hPa]",buffer,true); snprintf(buffer, sizeof buffer, "%0.0f", glblData.hum); publish_sub("rHum[%]",buffer,true); snprintf(buffer, sizeof buffer, "%s", glblData.heating?"true":"false"); publish_sub("Heating",buffer,true); snprintf(buffer, sizeof buffer, "%0.1f", glblData.settemp); publish_sub("Set Temp[degC]",buffer,true); snprintf(buffer, sizeof buffer, "%d", WiFi.RSSI()); publish_sub("RSSI [dBm]",buffer,true); } void MQTT::remoteLog(const char* str, int32_t num){ udp.beginPacket(Settings::prefs.mqtt_server,13377); struct tm timeinfo; getLocalTime(&timeinfo); udp.printf("<13>%s %02d %02d:%02d:%02d %s Log: %s %d",months[timeinfo.tm_mon],timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec,Settings::prefs.hostname,str,num); udp.endPacket(); } void MQTT::remoteLog(const char* str){ udp.beginPacket(Settings::prefs.mqtt_server,13377); struct tm timeinfo; getLocalTime(&timeinfo); udp.printf("<13>%s %02d %02d:%02d:%02d %s Log: %s",months[timeinfo.tm_mon],timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec,Settings::prefs.hostname,str); udp.endPacket(); } void MQTT::remoteLog(const char* str,const char* str2){ udp.beginPacket(Settings::prefs.mqtt_server,13377); struct tm timeinfo; getLocalTime(&timeinfo); udp.printf("<13>%s %02d %02d:%02d:%02d %s Log: %s %s",months[timeinfo.tm_mon],timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec,Settings::prefs.hostname,str,str2); udp.endPacket(); } void MQTT::remoteLogf(const char* format, ...){ udp.beginPacket(Settings::prefs.mqtt_server,13377); struct tm timeinfo; getLocalTime(&timeinfo); char loc_buf[256]; char * temp = loc_buf; va_list arg; va_start(arg, format); int len = snprintf(temp, sizeof(loc_buf), "<13>%s %02d %02d:%02d:%02d %s Log: ", months[timeinfo.tm_mon],timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec,Settings::prefs.hostname); if(len < (int)sizeof(loc_buf)){ len += vsnprintf(temp+len, sizeof(loc_buf)-len, format, arg); } va_end(arg); if(len >= (int)sizeof(loc_buf)){ // comparation of same sign type for the compiler udp.write((uint8_t*)temp, len); udp.print("...Memory issue at log buffer!"); udp.endPacket(); return; } udp.write((uint8_t*)temp, len); udp.endPacket(); return; }