// Weather Station 3 from AliExpress / TaoBao // // Code for ESP32-PoE-ISO // // Notice : this will use ONLY Ethernet card. With DHCP. // // 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 #include #ifndef ESP32 #include #endif #include #include //#include // EspSoftwareSerial (or plain SoftwareSerial) #ifndef ESP32 #include #endif // MQTT #include // WiFiManager #include #include // Version 5 ONLY (TODO upgrade this) #ifdef ESP32 #include #endif /* ESP32 */ #include "ws3_defs.h" #include "ws3_vars.h" #ifndef ESP32 SoftwareSerial WS3(RXD2,TXD2); #else #define WS3 Serial2 #endif WiFiClient espClient; PubSubClient client(espClient); // ws3_wifi.ino void setupSpiffs(); void saveConfigCallback(); // ws3_ota.ino void setup_ota(); // Setup the stuff. void setup() { #ifndef ESP32 Serial.begin(57600); #else Serial.begin(115200); #endif while (!Serial) ; // wait for Arduino Serial Monitor to open Serial.println("\n\n"); setupSpiffs(); Serial.println("Weather Station 3 Adapter by Kiwi"); #ifndef ESP32 Serial.println(ESP.getFullVersion()); #endif WiFiManager wm; wm.setSaveConfigCallback(saveConfigCallback); // Setup custom parameters 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_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(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["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(); #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 #ifndef ESP32 WS3.begin(WS3_BAUD); #else // We use Serial2 on ESP32 (hardware) WS3.begin(WS3_BAUD, SERIAL_8N1, RXD2, TXD2); while (!WS3) ; // wait for Arduino Serial Monitor to open #endif debugln("WS3 UART is ready..."); // Allocate memory for packet pkt_str.reserve(WS3_PKT_LEN); debugln(" -> Packet memory allocated!"); client.setServer(mqtt_server, mqtt_port); //client.setCallback(callback); debugln("MQTT started"); } // 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); } 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(); }