Files
Ardiuno/WS3/Serial-ESP/Serial-ESP.ino

786 lines
24 KiB
C++

// Weather Station 3 from AliExpress / TaoBao
//
// Highly inspired by https://github.com/kquinsland/ws3-to-esphome-bridge/
//
// Maybe the code is not optimal but it works.
//
// Configuration of WS3 :
//
// JP1 toggle betwen ARS inch mode and professional metric mode
// short : inch
// open : metric
//
// JP2 baud rate
// short : 2400
// open : 9600
//
// Configuration used for this code :
// JP1 : open
// JP2 : closed
//
// Data is in the form :
//
// A4095B000C0000D0000E0000F0000G0000H0000I0000J0000K0000L0218M515N09654O.....*52
//
#include <FS.h>
//#include <ESP8266WiFi.h> // Needed?
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
//#include <AddrList.h>
// EspSoftwareSerial (or plain SoftwareSerial)
#include <SoftwareSerial.h>
// MQTT
#include <PubSubClient.h>
// WiFiManager
#include <WiFiManager.h>
#include <ArduinoJson.h> // Version 5 ONLY (TODO upgrade this)
#ifdef ESP32
#iclude <SPIFFS.h>
#endif /* ESP32 */
// Domoticz MQTT configuration by WiFiManager
//IPAddress MQipAddr; // MQTT IP address from WiFiManager
const char *mqtt_server = "portbuild.home.oav.net";
#define mqtt_port 1883
// configuration values
//char cfg_mqtt_server[40];
char idx_windir[4]="48";
char idx_temp[4] ="52";
char idx_rain[4] ="51";
// To handle Gust
volatile unsigned int loopcount=0; // number of loops (300 = 10 minutes, since every Serial loops is 2s)
volatile unsigned int WindGust=0; // wind gust data values to keep track of the strongest gust in the last 10 minutes
// Definitions
const char* Mqtt_clientid = "ESP-Weather-Station";
const char* dom_in = "domoticz/in";
#define MQTT_MAX_PACKET_SIZE 128
char msgToPublish[MQTT_MAX_PACKET_SIZE + 1];
// Defines
#define OTAPASSWORD "123" // Password for OTA upload thru Arduino IDE
// Debug
#define DEBUG 1
#ifdef DEBUG
#define debug(x) Serial.print(x)
#define debugln(x) Serial.println(x)
#else /* DEBUG */
#define debug(x)
#define debugln(x)
#endif /* DEBUG */
// Baud used to read see JP2
#define WS3_BAUD 2400
// Toggle support for PM2.5 sensor
//#define SUPPORT_PM25_SENSOR
// Define the length of data
#ifdef SUPPORT_PM25_SENSOR
// There is 88 bytes per packets
#define WS3_PKT_LEN 78
// And the checksum is the last 2 bytes
#define WS3_CHK_LEN 2
#define CHK_SUM_DELINEATOR 75
#else /* SUPPORT_PM25_SENSOR */
// There is 88 bytes per packets
#define WS3_PKT_LEN 78
// And the checksum is the last 2 bytes
#define WS3_CHK_LEN 2
#define CHK_SUM_DELINEATOR 75
#endif /* SUPPRT_PM25_SENSOR */
// Seems the Metric format does not have a correct checksum
// In this case we should not test the checksum, just see
// if we have a correct dataline
#define DONT_CHKSUM 1
// Define ALTITUDE if you need to get pression corrected
// Set this to 0 if you don't care
#define ALTITUDE 340 // Longwy is at 340m
// Place holder for the packet received
String pkt_str = "";
// Flag for packet OK
volatile byte pkt_ok = false;
// Flag for saving data
bool shouldSaveConfig = false;
SoftwareSerial WS3(15,16);
WiFiClient espClient;
PubSubClient client(espClient);
// After parsing the string of bytes, we'll have an easier to use struct
// TODO: this should be it's own file?
struct WS3Packet {
// The 1st field is "A0000" - Wind direction AD value in real time (0-4096)
unsigned int wind_dir;
// The 2nd field is "B000" - Wind direct angle value (16 direction)
unsigned int wind_angle; // new
// The 3rd field is "C0000" - Real time wind speed frequency 1Hz
unsigned int wind_freq; // New
// The 4th field is "D0000" - Real time wind speed, unit: 0.1 m/s
unsigned int wind_speed;
// The 5th field is "E0000" - Avg wind speed in the previous minute, unit: 0.1m/s
unsigned int wind_speed_1m;
// The 6th field is "F0000" - the highest wind speed in the last 5 minutes, unit: 0.1m/s
unsigned int wind_speed_5m;
// The 7th field is "G0000" - Real time rain bucket (0-9999), loop-count
int rain_bucket_cnt; // New
// The 8th field is "H0000" - Number of rain bucket in the last minute, (0-9999)
int rt_rain_bucket;
// The 9th field is "I0000" - Rain fall in 1 minute, unit: 0.1mm
unsigned int rain_1m;
// The 10th field is "J0000" - the previous hour's rainfall ( 0.1 mm)
unsigned int rain_1h;
// The 11th field is "K0000" - rainfall during the first 24 hours ( 0.1 mm)
unsigned int rain_24h;
// The 12th field is "L0000" - temperature, unit: degree C (unit 0.1 Degree)
float temp_f;
// The 13th field is "M000" - humidity ( 00 % - 99 %), unit 0.1%
float humidity;
// The 14th field is "M10020" - air pressure ( 0.1 hpa )
float air_pressure;
};
// Return the index according to Wind Angle
String str_windir(unsigned int WinVal){
if(WinVal >= 360) return "N"; //N
if(WinVal >= 0 && WinVal < 22) return "N"; //N
if(WinVal >= 22 && WinVal < 45) return "NNE"; //NNE
if(WinVal >= 45 && WinVal < 67) return "NE"; //NE
if(WinVal >= 67 && WinVal < 90) return "ENE"; //ENE
if(WinVal >= 90 && WinVal < 112) return "E"; //E
if(WinVal >= 112 && WinVal < 135) return "ESE"; //ESE
if(WinVal >= 135 && WinVal < 157) return "SE"; //SE
if(WinVal >= 157 && WinVal < 180) return "S"; //S
if(WinVal >= 180 && WinVal < 202) return "S"; //S
if(WinVal >= 202 && WinVal < 225) return "SSW"; //SSW
if(WinVal >= 225 && WinVal < 247) return "SW"; //SW
if(WinVal >= 247 && WinVal < 270) return "WSW"; //WSW
if(WinVal >= 270 && WinVal < 292) return "W"; //W
if(WinVal >= 292 && WinVal < 315) return "WNW"; //WNW
if(WinVal >= 315 && WinVal < 337) return "NW"; //NW
if(WinVal >= 337 && WinVal < 359) return "NNW"; //NNW
}
// Setup OTA stuff
void setup_ota() {
//Port defaults to 8266
//ArduinoOTA.setPort(8266);
// Hostname defaults to projet name
ArduinoOTA.setHostname(Mqtt_clientid);
// No auth per default
// XXX: Change this
ArduinoOTA.setPassword((const char *)OTAPASSWORD);
ArduinoOTA.onStart([]() {
Serial.println("OTA Update is Starting !");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nOTA is done. Rebooting...");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("OTA 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();
}
// Setup the stuff.
void setup() {
Serial.begin(57600);
while (!Serial) ; // wait for Arduino Serial Monitor to open
Serial.println("\n\n");
setupSpiffs();
Serial.println("Weather Station 3 Adapter by Kiwi");
Serial.println(ESP.getFullVersion());
WiFiManager wm;
wm.setSaveConfigCallback(saveConfigCallback);
// Setup custom parameters
//WiFiManagerParameter custom_mqtt_server("server", "Domoticz MQTT server hostname", cfg_mqtt_server, 40);
WiFiManagerParameter custom_idx_windir ("idx1", "Domoticz Index for Wind Virtual Module", idx_windir, 4);
WiFiManagerParameter custom_idx_temp ("idx2", "Domoticz Index for Temp/Hum/Baro Virtual Module", idx_temp, 4);
WiFiManagerParameter custom_idx_rain ("idx3", "Domoticz Index for Rain Virtual Module", idx_rain, 4);
// Add all parameters
//wm.addParameter(&custom_mqtt_server);
wm.addParameter(&custom_idx_windir);
wm.addParameter(&custom_idx_temp);
wm.addParameter(&custom_idx_rain);
// Automatic connect using saved stuff otherwise start as an AP to configure it
if (!wm.autoConnect("ESP-Weather-Station")) {
Serial.println("Failer to connect and hit timeout");
delay(3000);
// if we still have not connected restard and try again
ESP.restart();
delay(5000);
}
// Always start configportal for a little while (2 m)
wm.setConfigPortalTimeout(30);
// wm.startConfigPortal("ESP-Weather-Station","ESP");
wm.startConfigPortal("ESP-Weather-Station");
// If we get here we are connected !
Serial.println("Connected to WiFi !");
// Read the updated parameters
//strcpy(cfg_mqtt_server, custom_mqtt_server.getValue());
strcpy(idx_windir, custom_idx_windir.getValue());
strcpy(idx_temp, custom_idx_temp.getValue());
strcpy(idx_rain, custom_idx_rain.getValue());
// Save the custom parameters to FS
if (shouldSaveConfig) {
Serial.println("Saving config to FS...");
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
//json["mqtt_server"] = cfg_mqtt_server;
json["idx_windir"] = idx_windir;
json["idx_temp"] = idx_temp;
json["idx_rain"] = idx_rain;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
json.prettyPrintTo(Serial);
json.printTo(configFile);
configFile.close();
//end save
shouldSaveConfig = false;
}
// Launch OTA stuff
setup_ota();
//Serial.println("");
//Serial.println(F("WiFi Connected !"));
#if LWIP_IPV6
Serial.printf("IPV6 is enabled\n");
#else
Serial.printf("IPV6 is not enabled\n");
#endif
Serial.print("My IP address: ");
Serial.print(WiFi.localIP());
Serial.print("/");
Serial.print(WiFi.subnetMask());
Serial.print(" GW:");
Serial.println(WiFi.gatewayIP());
// Start the Software Serial for WS3
WS3.begin(WS3_BAUD);
debugln("WS3 UART is ready...");
// Allocate memory for packet
pkt_str.reserve(WS3_PKT_LEN);
debugln(" -> Packet memory allocated!");
// Now connect to MQTT
//if(WiFi.hostByName(cfg_mqtt_server, MQipAddr) != 1)
// Serial.println("ERROR: Unable to resolv MQTT Hostname");
//else
// Serial.printf("INFO: Hostname %s resolved to %s\n",cfg_mqtt_server, MQipAddr.toString().c_str());
client.setServer(mqtt_server, mqtt_port);
//client.setServer(MQipAddr, mqtt_port);
//client.setCallback(callback);
debugln("MQTT started");
}
#ifdef DONT_CHKSUM
// Validate packet using the checksum.
// Work only APRS data on this 51W3 board.
// Maybe the code on the board does not make the correct checksum ?
bool validate_packet(String pay, unsigned long chk) {
// Print the payload and the checksum we want
debugln("validate_packet:");
debug(pay);
debug(" * ");
debugln(chk);
// TEST DATA (actual packets)
// c000s000g000t075r000p019h43b09940*32
// String pay = "c000s000g000t075r000p019h43b09940";
// byte chk = 0x32; // this will be in HEX)
// c000s000g000t075r000p019h42b09940*33
// String pay = "c000s000g000t075r000p019h42b09940";
// byte chk = 0x33; // this will be in HEX)
// c000s000g000t075r000p019h42b09939*3D
// String pay = "c000s000g000t075r000p019h42b09939";
// byte chk = 0x3D; // this will be in HEX)
// SUPER grateful for the helpful https://toolslick.com/math/bitwise/xor-calculator to validate my
// code!
// Current byte
byte i1=0;
// the intermediate checksum
byte tmp = 0;
// starting from the second character, we begin XORing
for (int x = 0; x < pay.length() ; x++) {
i1=pay[x];
// Do the xOR
tmp = tmp^i1;
}
// do the check
if(tmp == chk){
return true;
} else {
debugln("INVALID!");
debug("calculated:");
debugln(tmp);
return false;
}
}
#endif /* DONT_CHKSUM */
// Parse the packet and fill the structure with data
void parse_packet(String payload, WS3Packet* p) {
// E.G.: A4095 B000 C0000 D0000 E0000 F0000 G0000 H0000 I0000 J0000 K0000 L0237 M502 N09810 O.....
// Parse in order, starting with A0000 (wind dir real time, 0-4096)
int wind_dir_idx = payload.indexOf('A');
p->wind_dir = payload.substring(wind_dir_idx+1, wind_dir_idx+5).toInt();
// Then move on to B000 - wind direction angle (16 direction)
int wind_angle_idx = payload.indexOf('B');
p->wind_angle = payload.substring(wind_angle_idx+1, wind_angle_idx+4).toInt();
// Then move on to C0000 - wind speed frequency (1 Hz)
int wind_freq_idx = payload.indexOf('C');
p->wind_freq = payload.substring(wind_freq_idx+1, wind_freq_idx+5).toInt();
// Then move on to D0000 - wind speed real time (unit 0.1 m/s)
int wind_speed_idx = payload.indexOf('D');
p->wind_speed = payload.substring(wind_speed_idx+1, wind_speed_idx+5).toInt();
// Then move on to E0000 - wind speed avg in the last minute (unit 0.1 m/s)
int wind_speed_1m_idx = payload.indexOf('D');
p->wind_speed_1m = payload.substring(wind_speed_1m_idx+1, wind_speed_1m_idx+5).toInt();
// Then move on to F0000 - wind speed over the last 5 min
int wind_speed_5_idx = payload.indexOf('F');
p->wind_speed_5m = payload.substring(wind_speed_5_idx+1, wind_speed_5_idx+5).toInt();
// Then move on to G0000 - Rain in Realtime (0-9999 counter) bucket
int rain_bucket_cnt_idx = payload.indexOf('G');
p->rain_bucket_cnt = payload.substring(rain_bucket_cnt_idx+1, rain_bucket_cnt_idx+5).toInt();
// Then move on to H0000 - Rain bucket in the last 1 minute (0-9999 counter)
int rt_rain_bucket_idx = payload.indexOf('H');
p->rt_rain_bucket = payload.substring(rt_rain_bucket_idx+1, rt_rain_bucket_idx+5).toInt();
// Then move on to I0000 - rain last minute (0.1mm)
int rain1m_idx = payload.indexOf('I');
p->rain_1m = payload.substring(rain1m_idx+1, rain1m_idx+5).toInt();
// Then move on to J0000 - rain last hour (0.1mm)
int rain1h_idx = payload.indexOf('J');
p->rain_1h = payload.substring(rain1h_idx+1, rain1h_idx+5).toInt();
// Then move on to K0000 - rain last 24h (0.1mm)
int rain24h_idx = payload.indexOf('K');
p->rain_24h = payload.substring(rain24h_idx+1, rain24h_idx+5).toInt();
// Then move on to L0200 - temp (0.1°C)
// XXX: Check with minus zero temperatures
int temp_idx = payload.indexOf('L');
p->temp_f = payload.substring(temp_idx+1, temp_idx+5).toInt()*.1;
// Then move on to M611 - Humidity
int humidity_idx = payload.indexOf('M');
// p->humidity = payload.substring(humidity_idx+1, humidity_idx+3).toInt()*.1;
p->humidity = payload.substring(humidity_idx+1, humidity_idx+3).toInt();
// Then move on to N10020 - air pressure
int pressure_idx = payload.indexOf('N');
p->air_pressure = payload.substring(pressure_idx+1, pressure_idx+6).toInt()*.1;
// Handle Gust
if(p->wind_speed > WindGust) {
WindGust = p->wind_speed;
debugln("Update WindGust");
}
if(loopcount >= 300) {
loopcount = 0;
WindGust = p->wind_speed;
debugln("10 min expired -> reset counter");
}
// Increment loopcount for Gust
loopcount++;
}
// Clear the packet before working on the next
void clear_pkt(WS3Packet* p) {
p->wind_dir = 0;
p->wind_angle = 0;
p->wind_freq = 0;
p->wind_speed = 0;
p->wind_speed_1m = 0;
p->wind_speed_5m = 0;
p->rain_bucket_cnt = 0;
p->rt_rain_bucket = 0;
p->rain_1m = 0;
p->rain_1h = 0;
p->rain_24h = 0;
p->temp_f = 0;
p->humidity = 0;
p->air_pressure = 0;
}
#ifdef DEBUG
// Print the data
void print_weather(WS3Packet* p){
Serial.print("Wind Direction (realtime): ");
Serial.println(p->wind_dir, DEC);
//Serial.println(" degrees");
Serial.print("Wind direction angle : ");
Serial.print(p->wind_angle, DEC);
Serial.print(" degree ");
Serial.println(str_windir(p->wind_angle));
Serial.print("Wind speed Frequency: ");
Serial.print(p->wind_freq, DEC);
Serial.println(" Hz");
Serial.print("Wind speed: ");
Serial.print(p->wind_speed/10, DEC);
Serial.println(" m/s");
Serial.print("Wind speed 1m: ");
Serial.print(p->wind_speed_1m/10, DEC);
Serial.println(" m/s");
Serial.print("Wind speed 5m: ");
Serial.print(p->wind_speed_5m/10, DEC);
Serial.println(" m/s");
Serial.print("temp_f: ");
Serial.print(p->temp_f, DEC);
Serial.println(" deg. C.");
Serial.print("Rain buckets / buckets 1m: ");
Serial.print(p->rain_bucket_cnt, DEC);
Serial.print(" / ");
Serial.println(p->rt_rain_bucket, DEC);
Serial.print("Rain 1m / 1H / 24H: ");
Serial.print(p->rain_1m*0.1, DEC);
Serial.print(" / ");
Serial.print(p->rain_1h*0.1, DEC);
Serial.print(" / ");
Serial.print(p->rain_24h*.1, DEC);
Serial.println(" mm");
Serial.print("humidity: ");
Serial.print(p->humidity, DEC);
Serial.println(" %");
Serial.print("air_pressure: ");
Serial.print(p->air_pressure, DEC);
Serial.println(" hpa");
}
#endif /* DEBUG */
// Print the data over MQTT
void push_weather(WS3Packet* p) {
String MQPayload;
byte humidity_status = 0; // Domoticz humdity status
byte bar_forecast = 0; // Domoticz baro forcast
float pression = 0.0; // Pression
float wct = 0.0; // Windchill temperature
// Rain
MQPayload = "{ \"idx\" : "+ String(idx_rain) +",\"nvalue\" : 0, \"svalue\": \"" + String(p->rain_1h*10) + ";" + String(p->rain_1m*0.1) + "\"}";
sendMQTTPayload(MQPayload);
// Temperature / Humidity / Baro
// Humidity stuff
// O = Normal
// 1 = Confortable
// 2 = Dry (<35%)
// 3 = Wet (>70%)
if (p->humidity < 35) {
humidity_status = 2; // Dry
} else if (p->humidity >= 70) {
humidity_status = 3; // Wet
} else {
if ((p->humidity >=35) && (p->humidity <50)) {
humidity_status = 1; // Confortable
} else {
// >=50 -> 70%
humidity_status = 0; // Normal
}
}
// Correct the pression according to altitude
pression = p->air_pressure + (ALTITUDE / 8.3);
// Pression should be adapted as it should be on sea level
// Prediction
// 0 : No info
// 1 : Sunny
// 2 : Partly cloudy
// 3 : Cloudy
// 4 : Rain
// Maybe have to store values for last 3 hours?
// XXX: fix value with altitude
if (pression < 1000) {
bar_forecast = 4; // Rain
} else if (pression < 1020) {
bar_forecast = 3; // Cloudy
} else if (pression < 1030) {
bar_forecast = 2; // Partly cloudy
} else {
bar_forecast = 1; // Sunny
}
// See : https://github.com/G6EJD/ESP32-Weather-Forecaster
MQPayload = "{ \"idx\" : "+ String(idx_temp) +",\"nvalue\" : 0, \"svalue\": \"" + String(p->temp_f) + ";" + String(p->humidity) + ";"+String(humidity_status)+ ";" + String(pression) +";"+String(bar_forecast)+"\"}";
sendMQTTPayload(MQPayload);
// Wind
// Last temperature is WindChill
// Have to compute it https://www.alpiniste.fr/windchill-calculateur/
//wct = p->temp_f;
wct = 13.12 + 0.6215*p->temp_f - 11,37*pow((p->wind_speed * 3.6),0.16)+0.3965*p->temp_f*pow((p->wind_speed * 3.6),0.16);
MQPayload = "{ \"idx\" : "+ String(idx_windir) +",\"nvalue\" : 0, \"svalue\": \"" + String(p->wind_angle) + ";" + String(str_windir(p->wind_angle)) + ";" + String(p->wind_speed) + ";" + String(WindGust) +";" + String(p->temp_f) + ";"+String(wct)+"\"}";
sendMQTTPayload(MQPayload);
}
// Processing the packet.
bool process_packet(String pkt, WS3Packet* p) {
debugln("[D] process_packet - ALive!");
debugln(pkt);
// Allocate bytes for the payload
String payload;
payload.reserve(WS3_PKT_LEN-WS3_CHK_LEN);
#ifdef DONT_CHKSUM
// everything after the * is checksum (2 char long)
unsigned long chksum;
#endif /* DONT_CHKSUM */
// Check if the 75rd character is *
if (pkt.charAt(CHK_SUM_DELINEATOR) != '*') {
debugln("Packed invalid; no * character at position 75!");
return false;
#ifdef DONT_CHKSUM
} else {
// The character indicating the checksum is coming is in the correct place. Yay.
// Now, we need to pull the two ascii characters that are transmitted to us
// and turn them into a single byte. E.G. Char 3, Char D should convert to 0x3D.
//
// We can do this with the strtoul() function; we indicate that we wante base 16
chksum = strtoul(pkt.substring(CHK_SUM_DELINEATOR+1, CHK_SUM_DELINEATOR+2).c_str(),NULL,16);
}
#endif /* DONT_CHKSUM */
// We have the checksum, Now we can bother to get the payload
payload = pkt.substring(0, CHK_SUM_DELINEATOR);
// And try to validate...
#ifndef DONT_CHKSUM
if(!validate_packet(payload, chksum)){
debugln("invalid packet! :(");
return false;
} else {
debugln("Valid packet!");
}
#endif /* DONT_CHKSUM */
parse_packet(payload, p);
return true;
}
void loop() {
// MQTT
if (!client.connected()) {
reconnect();
}
// While data comes in and we don't have a pending packet to process...
while (WS3.available() && pkt_ok !=true) {
// Pull the bytes off the stream
char inChar = (char)WS3.read();
// And build up the packet
pkt_str += inChar;
// Until we hit the end
if (inChar == '\n') {
pkt_ok = true;
}
}
// Yay, we now have a packet!
// Now, we attempt to parse out the string into a packet that we can work with
if (pkt_ok) {
debugln("pkt_ok!");
// At this point, we have a string of characters that was probably a valid packet
// We set get some memory and attempt to parse the string into the struct
WS3Packet p = {};
// Validate the payload, then parse it.
if (process_packet(pkt_str, &p)) {
// print results if parse OK
#ifdef DEBUG
print_weather(&p);
#endif /* DEBUG */
// Push to MQTT
push_weather(&p);
debugln("processed");
} else {
debugln("unable to parse packet :(");
}
// clear so we can start again
pkt_str = "";
pkt_ok = false;
clear_pkt(&p);
}
ArduinoOTA.handle();
}
// MQTT Stuff
//void callback(char* topic, byte* payload, unsigned int length) {
// debug("Message arrived [");
// Serial.print(topic);
// debug("] ");
// for (int i = 0; i < length; i++) {
// Serial.print((char)payload[i]);
// }
// debug(" ");
//}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
debug("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(Mqtt_clientid)) {
debugln("connected");
// ... and resubscribe
client.subscribe(dom_in);
} else {
debug("failed, rc=");
debug(client.state());
debugln(" try again in 5 seconds");
// Wait 5 seconds before retrying
for(int i = 0; i<5000; i++){
delay(1);
}
}
}
}
// Sends MQTT payload to the Mosquitto server running on a Raspberry Pi.
// Mosquitto server deliveres data to Domoticz server running on a same Raspberry Pi
void sendMQTTPayload(String msgpayload)
{
// Convert payload to char array
msgpayload.toCharArray(msgToPublish, msgpayload.length()+1);
//Publish payload to MQTT broker
if (client.publish(dom_in, msgToPublish))
{
debug("Following data published to MQTT broker: ");
debug(dom_in);
debug(" ");
debugln(msgpayload);
}
else
{
debug("Publishing to MQTT broker failed... ");
debugln(client.state());
}
}
// WiFiManager & Autoconfig
//callback notifying us of the need to save config
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
void setupSpiffs(){
//clean FS, for testing
//SPIFFS.format();
//read configuration from FS json
debugln("mounting FS...");
if (SPIFFS.begin()) {
debugln("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
debugln("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
debugln("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.printTo(Serial);
if (json.success()) {
debugln("\nparsed json");
//strcpy(cfg_mqtt_server, json["mqtt_server"]);
strcpy(idx_windir, json["idx_windir"]);
strcpy(idx_temp, json["idx_temp"]);
strcpy(idx_rain, json["idex_rain"]);
//Serial.printf("Server : %s, idx (wind/temp/rain) : %s/%s/%s\n", cfg_mqtt_server, idx_windir, idx_temp, idx_rain);
} else {
Serial.println("failed to load json config");
}
}
}
} else {
Serial.println("failed to mount FS");
}
}