400 lines
14 KiB
C++
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;
|
|
} |