400 lines
14 KiB
C++

#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;
}