Some updates
This commit is contained in:
35
ESP32-PoE-Test3/ESP32-PoE-Test3.ino
Normal file
35
ESP32-PoE-Test3/ESP32-PoE-Test3.ino
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
This sketch is combined demo of: 1) SD(esp_32) -- > SD_Test; 2) WiFi --> ETH_LAN8720
|
||||||
|
It displays info about the card on the terminal and initializes LAN after that. You can ping the IP address shown on the terminal.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#define ETH_CLK_MODE ETH_CLOCK_GPIO17_OUT
|
||||||
|
#define ETH_PHY_POWER 12
|
||||||
|
|
||||||
|
#include <ETH.h>
|
||||||
|
|
||||||
|
static bool eth_connected = false;
|
||||||
|
|
||||||
|
void WiFiEvent(WiFiEvent_t event);
|
||||||
|
void testClient(const char * host, uint16_t port);
|
||||||
|
|
||||||
|
#define BUTTON_PRESSED() (!digitalRead (34))
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
WiFi.onEvent(WiFiEvent);
|
||||||
|
ETH.begin();
|
||||||
|
pinMode (34, INPUT); // Button
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (BUTTON_PRESSED()) {
|
||||||
|
if (eth_connected) {
|
||||||
|
Serial.println("Button pressed");
|
||||||
|
testClient("google.com", 80);
|
||||||
|
}
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
ESP32-PoE-Test3/esp_eth.ino
Normal file
42
ESP32-PoE-Test3/esp_eth.ino
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include <ETH.h>
|
||||||
|
|
||||||
|
void WiFiEvent(WiFiEvent_t event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case SYSTEM_EVENT_ETH_START:
|
||||||
|
Serial.println("ETH Started");
|
||||||
|
//set eth hostname here
|
||||||
|
ETH.setHostname("esp32-ethernet");
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_CONNECTED:
|
||||||
|
Serial.println("ETH Connected");
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_GOT_IP:
|
||||||
|
Serial.print("ETH MAC: ");
|
||||||
|
Serial.print(ETH.macAddress());
|
||||||
|
Serial.print(", IPv4: ");
|
||||||
|
Serial.print(ETH.localIP());
|
||||||
|
if (ETH.fullDuplex()) {
|
||||||
|
Serial.print(", FULL_DUPLEX");
|
||||||
|
}
|
||||||
|
Serial.print(", ");
|
||||||
|
Serial.print(ETH.linkSpeed());
|
||||||
|
Serial.println("Mbps");
|
||||||
|
if (ETH.enableIpV6()) {
|
||||||
|
Serial.print(" IPv6 enabled : ");
|
||||||
|
Serial.println(ETH.localIPv6());
|
||||||
|
}
|
||||||
|
eth_connected = true;
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||||
|
Serial.println("ETH Disconnected");
|
||||||
|
eth_connected = false;
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_STOP:
|
||||||
|
Serial.println("ETH Stopped");
|
||||||
|
eth_connected = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
ESP32-PoE-Test3/testclient.ino
Normal file
19
ESP32-PoE-Test3/testclient.ino
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
void testClient(const char * host, uint16_t port)
|
||||||
|
{
|
||||||
|
Serial.print("\nconnecting to ");
|
||||||
|
Serial.println(host);
|
||||||
|
|
||||||
|
WiFiClient client;
|
||||||
|
if (!client.connect(host, port)) {
|
||||||
|
Serial.println("connection failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host);
|
||||||
|
while (client.connected() && !client.available());
|
||||||
|
while (client.available()) {
|
||||||
|
Serial.write(client.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("closing connection\n");
|
||||||
|
client.stop();
|
||||||
|
}
|
||||||
37
Fil_Pilot/Fil_Pilot/FP_OTA.ino
Normal file
37
Fil_Pilot/Fil_Pilot/FP_OTA.ino
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// OTA Stuff to push the Code using Arduino OTA
|
||||||
|
#include "Fil_Pilot.h"
|
||||||
|
|
||||||
|
// Setup OTA
|
||||||
|
void setup_ota() {
|
||||||
|
//Port defaults to 8266
|
||||||
|
//ArduinoOTA.setPort(8266);
|
||||||
|
|
||||||
|
// Hostname defaults to projet name
|
||||||
|
ArduinoOTA.setHostname(Mqtt_clientid);
|
||||||
|
|
||||||
|
// No auth per default
|
||||||
|
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();
|
||||||
|
}
|
||||||
147
Fil_Pilot/Fil_Pilot/FP_WMN.ino
Normal file
147
Fil_Pilot/Fil_Pilot/FP_WMN.ino
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// Wifi Manager
|
||||||
|
// Thanks to https://github.com/tzapu/WiFiManager/blob/b22ba24283ad3f22f8c8dfb2a52ed0583c0222d7/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino
|
||||||
|
|
||||||
|
//callback notifying us of the need to save config
|
||||||
|
void saveConfigCallback () {
|
||||||
|
Serial.println("Should save config");
|
||||||
|
shouldSaveConfig = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_WMN() {
|
||||||
|
//read configuration from FS json
|
||||||
|
Serial.println("mounting FS...");
|
||||||
|
|
||||||
|
if (SPIFFS.begin()) {
|
||||||
|
Serial.println("mounted file system");
|
||||||
|
if (SPIFFS.exists("/config.json")) {
|
||||||
|
//file exists, reading and loading
|
||||||
|
Serial.println("reading config file");
|
||||||
|
File configFile = SPIFFS.open("/config.json", "r");
|
||||||
|
if (configFile) {
|
||||||
|
Serial.println("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);
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_VERSION_MAJOR >= 6
|
||||||
|
DynamicJsonDocument json(1024);
|
||||||
|
DeserializationError deserializeError = deserializeJson(json, buf.get());
|
||||||
|
serializeJsonPretty(json, Serial);
|
||||||
|
if (!deserializeError) {
|
||||||
|
#else
|
||||||
|
DynamicJsonBuffer jsonBuffer;
|
||||||
|
JsonObject& json = jsonBuffer.parseObject(buf.get());
|
||||||
|
json.printTo(Serial);
|
||||||
|
if (json.success()) {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Serial.println("\nparsed json");
|
||||||
|
strcpy(mqtt_server, json["mqtt_server"]);
|
||||||
|
strcpy(mqtt_port, json["mqtt_port"]);
|
||||||
|
strcpy(blynk_token, json["blynk_token"]);
|
||||||
|
} else {
|
||||||
|
Serial.println("failed to load json config");
|
||||||
|
}
|
||||||
|
configFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.println("failed to mount FS");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The extra parameters to be configured (can be either global or just in the setup)
|
||||||
|
// After connecting, parameter.getValue() will get you the configured value
|
||||||
|
// id/name placeholder/prompt default length
|
||||||
|
WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
|
||||||
|
WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 6);
|
||||||
|
WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 32);
|
||||||
|
|
||||||
|
//WiFiManager
|
||||||
|
//Local intialization. Once its business is done, there is no need to keep it around
|
||||||
|
WiFiManager wifiManager;
|
||||||
|
|
||||||
|
//set config save notify callback
|
||||||
|
wifiManager.setSaveConfigCallback(saveConfigCallback);
|
||||||
|
|
||||||
|
//set static ip
|
||||||
|
wifiManager.setSTAStaticIPConfig(IPAddress(10, 0, 1, 99), IPAddress(10, 0, 1, 1), IPAddress(255, 255, 255, 0));
|
||||||
|
|
||||||
|
//add all your parameters here
|
||||||
|
wifiManager.addParameter(&custom_mqtt_server);
|
||||||
|
wifiManager.addParameter(&custom_mqtt_port);
|
||||||
|
wifiManager.addParameter(&custom_blynk_token);
|
||||||
|
|
||||||
|
//reset settings - for testing
|
||||||
|
//wifiManager.resetSettings();
|
||||||
|
|
||||||
|
//set minimu quality of signal so it ignores AP's under that quality
|
||||||
|
//defaults to 8%
|
||||||
|
//wifiManager.setMinimumSignalQuality();
|
||||||
|
|
||||||
|
//sets timeout until configuration portal gets turned off
|
||||||
|
//useful to make it all retry or go to sleep
|
||||||
|
//in seconds
|
||||||
|
//wifiManager.setTimeout(120);
|
||||||
|
|
||||||
|
//fetches ssid and pass and tries to connect
|
||||||
|
//if it does not connect it starts an access point with the specified name
|
||||||
|
//here "AutoConnectAP"
|
||||||
|
//and goes into a blocking loop awaiting configuration
|
||||||
|
if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
|
||||||
|
Serial.println("failed to connect and hit timeout");
|
||||||
|
delay(3000);
|
||||||
|
//reset and try again, or maybe put it to deep sleep
|
||||||
|
ESP.reset();
|
||||||
|
delay(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if you get here you have connected to the WiFi
|
||||||
|
Serial.println("connected...yeey :)");
|
||||||
|
|
||||||
|
//read updated parameters
|
||||||
|
strcpy(mqtt_server, custom_mqtt_server.getValue());
|
||||||
|
strcpy(mqtt_port, custom_mqtt_port.getValue());
|
||||||
|
strcpy(blynk_token, custom_blynk_token.getValue());
|
||||||
|
Serial.println("The values in the file are: ");
|
||||||
|
Serial.println("\tmqtt_server : " + String(mqtt_server));
|
||||||
|
Serial.println("\tmqtt_port : " + String(mqtt_port));
|
||||||
|
Serial.println("\tblynk_token : " + String(blynk_token));
|
||||||
|
|
||||||
|
//save the custom parameters to FS
|
||||||
|
if (shouldSaveConfig) {
|
||||||
|
Serial.println("saving config");
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_VERSION_MAJOR >= 6
|
||||||
|
DynamicJsonDocument json(1024);
|
||||||
|
#else
|
||||||
|
DynamicJsonBuffer jsonBuffer;
|
||||||
|
JsonObject& json = jsonBuffer.createObject();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
json["mqtt_server"] = mqtt_server;
|
||||||
|
json["mqtt_port"] = mqtt_port;
|
||||||
|
json["blynk_token"] = blynk_token;
|
||||||
|
|
||||||
|
File configFile = SPIFFS.open("/config.json", "w");
|
||||||
|
if (!configFile) {
|
||||||
|
Serial.println("failed to open config file for writing");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_VERSION_MAJOR >= 6
|
||||||
|
serializeJsonPretty(json, Serial);
|
||||||
|
serializeJson(json, configFile);
|
||||||
|
#else
|
||||||
|
json.printTo(Serial);
|
||||||
|
json.printTo(configFile);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
configFile.close();
|
||||||
|
//end save
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("local ip");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
8
Fil_Pilot/Fil_Pilot/FP_const.h
Normal file
8
Fil_Pilot/Fil_Pilot/FP_const.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Some constants and variables for this project.
|
||||||
|
|
||||||
|
const char* Mqtt_clientid = "ESP-FP";
|
||||||
|
const char* dom_in = "domoticz/in";
|
||||||
|
const char* dom_out = "domoticz/out";
|
||||||
|
|
||||||
|
// Flag for saving data
|
||||||
|
bool shouldSaveConfig = false;
|
||||||
5
Fil_Pilot/Fil_Pilot/Fil_Pilot.h
Normal file
5
Fil_Pilot/Fil_Pilot/Fil_Pilot.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// Personnal Settings
|
||||||
|
|
||||||
|
|
||||||
|
// OTA Password for Arduino
|
||||||
|
#define OTAPASSWORD "123"
|
||||||
28
Fil_Pilot/Fil_Pilot/Fil_Pilot.ino
Normal file
28
Fil_Pilot/Fil_Pilot/Fil_Pilot.ino
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Fil Pilot code
|
||||||
|
//
|
||||||
|
// Hande low level code for Xavier Beaudouin's Fil Pilot Board
|
||||||
|
//
|
||||||
|
#include <FS.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <DNSServer.h>
|
||||||
|
#include <ESP8266WebServer.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
|
||||||
|
#include "Fil_Pilot.h" // Defines and some configurations
|
||||||
|
#include "FP_const.h" // Constants variables.
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin (115200);
|
||||||
|
while (!Serial); // Wait for Arduino
|
||||||
|
Serial.println("\n\n");
|
||||||
|
|
||||||
|
// Launch OTA stuff
|
||||||
|
setup_ota();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// End
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
248
Fil_Pilot/Fp_2/Fp_2.ino
Normal file
248
Fil_Pilot/Fp_2/Fp_2.ino
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266mDNS.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
|
||||||
|
#ifndef STASSID
|
||||||
|
#define STASSID "Kiwi"
|
||||||
|
#define STAPSK "ZeKiwi127"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// FP status info LittleFS
|
||||||
|
#define FP_STATUS "/fp_status.txt"
|
||||||
|
|
||||||
|
const char* ssid = STASSID;
|
||||||
|
const char* password = STAPSK;
|
||||||
|
File Status_File;
|
||||||
|
|
||||||
|
// Configuration des IO
|
||||||
|
const uint16_t ALT_POS = 13; // Broche pilotant l'alternance positive
|
||||||
|
const uint16_t ALT_NEG = 14; // Broche pilotant l'alternance négative
|
||||||
|
|
||||||
|
const uint8_t DEFAULT_PILOTE_STATUS = 1; // Mode par défaut Eco de manière à ce que la LED passe en orange après entrée d'une configuration valide
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int fp = 0;
|
||||||
|
|
||||||
|
// Cette fonction pilote le changement d'état des sorties, de la LED bicolore d'état et le mémorise dans le fichier d'état
|
||||||
|
void Pilote (int Status) {
|
||||||
|
switch (Status) {
|
||||||
|
case 0 : // aucune alternance en sortie, LED allumée en rouge : Confort
|
||||||
|
Serial.println("Confort");
|
||||||
|
digitalWrite(ALT_NEG,LOW);
|
||||||
|
digitalWrite(ALT_POS,LOW);
|
||||||
|
break;
|
||||||
|
case 1 : // pleine alternance en sortie, LED allumée en orange (rouge+vert) : Eco
|
||||||
|
Serial.println("Eco");
|
||||||
|
digitalWrite(ALT_NEG,HIGH);
|
||||||
|
digitalWrite(ALT_POS,HIGH);
|
||||||
|
break;
|
||||||
|
case 2 : // demie alternance négative en sortie, LED allumée en vert : Hors Gel
|
||||||
|
Serial.println("Hors Gel");
|
||||||
|
digitalWrite(ALT_NEG,HIGH);
|
||||||
|
digitalWrite(ALT_POS,LOW);
|
||||||
|
break;
|
||||||
|
case 3 : // demie alternance positive en sortie, LED éteinte : Arrêt
|
||||||
|
Serial.println("Arrêt");
|
||||||
|
digitalWrite(ALT_NEG,LOW);
|
||||||
|
digitalWrite(ALT_POS,HIGH);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Mémorisation dans le fichier
|
||||||
|
Serial.print(F("Status écrit : "));
|
||||||
|
Serial.println(Status);
|
||||||
|
Status_File = LittleFS.open(FP_STATUS, "w+");
|
||||||
|
Status_File.write(Status);
|
||||||
|
Status_File.seek(0, SeekSet);
|
||||||
|
Serial.print(F("Status relu : "));
|
||||||
|
Serial.println(Status_File.read());
|
||||||
|
Status_File.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup OTA Stuff
|
||||||
|
void setupOTA() {
|
||||||
|
// Port defaults to 8266
|
||||||
|
// ArduinoOTA.setPort(8266);
|
||||||
|
|
||||||
|
// Hostname defaults to esp8266-[ChipID]
|
||||||
|
//ArduinoOTA.setHostname("myesp8266");
|
||||||
|
|
||||||
|
// No authentication by default
|
||||||
|
ArduinoOTA.setPassword("12345");
|
||||||
|
|
||||||
|
// Password can be set with it's md5 value as well
|
||||||
|
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||||
|
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||||
|
|
||||||
|
ArduinoOTA.onStart([]() {
|
||||||
|
String type;
|
||||||
|
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||||
|
type = "sketch";
|
||||||
|
} else { // U_FS
|
||||||
|
type = "filesystem";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||||
|
Serial.println("Start updating " + type);
|
||||||
|
});
|
||||||
|
ArduinoOTA.onEnd([]() {
|
||||||
|
Serial.println("\nEnd");
|
||||||
|
});
|
||||||
|
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||||
|
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||||
|
});
|
||||||
|
ArduinoOTA.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup LittleFS
|
||||||
|
void setupLFS() {
|
||||||
|
Serial.println(F("Initializing FS..."));
|
||||||
|
if (LittleFS.begin()) {
|
||||||
|
Serial.println(F("LittleFS system mounted with success"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("An Error has occurred while mounting LittleFS"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all information about LittleFS
|
||||||
|
FSInfo fsInfo;
|
||||||
|
LittleFS.info(fsInfo);
|
||||||
|
|
||||||
|
Serial.println("------------------------------");
|
||||||
|
Serial.println("File system info");
|
||||||
|
Serial.println("------------------------------");
|
||||||
|
|
||||||
|
// Taille de la zone de fichier
|
||||||
|
Serial.print("Total space: ");
|
||||||
|
Serial.print(fsInfo.totalBytes);
|
||||||
|
Serial.println(" byte");
|
||||||
|
|
||||||
|
// Espace total utilise
|
||||||
|
Serial.print("Total space used: ");
|
||||||
|
Serial.print(fsInfo.usedBytes);
|
||||||
|
Serial.println(" byte");
|
||||||
|
|
||||||
|
// Taille d un bloc et page
|
||||||
|
Serial.print("Block size: ");
|
||||||
|
Serial.print(fsInfo.blockSize);
|
||||||
|
Serial.println(" byte");
|
||||||
|
|
||||||
|
Serial.print("Page size: ");
|
||||||
|
Serial.print(fsInfo.totalBytes);
|
||||||
|
Serial.println(" byte");
|
||||||
|
|
||||||
|
Serial.print("Max open files: ");
|
||||||
|
Serial.println(fsInfo.maxOpenFiles);
|
||||||
|
|
||||||
|
// Taille max. d un chemin
|
||||||
|
Serial.print("Max path lenght: ");
|
||||||
|
Serial.println(fsInfo.maxPathLength);
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
Serial.println("------------------------------");
|
||||||
|
Serial.println("List files");
|
||||||
|
Serial.println("------------------------------");
|
||||||
|
// Ouvre le dossier racine | Open folder
|
||||||
|
Dir dir = LittleFS.openDir("/");
|
||||||
|
// Affiche le contenu du dossier racine | Print dir the content
|
||||||
|
while (dir.next()) {
|
||||||
|
// recupere le nom du fichier | get filename
|
||||||
|
Serial.print(dir.fileName());
|
||||||
|
Serial.print(" - ");
|
||||||
|
// et sa taille | and the size
|
||||||
|
if (dir.fileSize()) {
|
||||||
|
File file = dir.openFile("r");
|
||||||
|
Serial.print(file.size());
|
||||||
|
Serial.println(" byte");
|
||||||
|
file.close();
|
||||||
|
} else {
|
||||||
|
File file = dir.openFile("r");
|
||||||
|
if ( file.isDirectory() ) {
|
||||||
|
Serial.println("this is a folder");
|
||||||
|
} else {
|
||||||
|
Serial.println("file is empty");
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fp_from_fs() {
|
||||||
|
unsigned int Num_Cde;
|
||||||
|
|
||||||
|
if (!LittleFS.exists(FP_STATUS)) {
|
||||||
|
Serial.println("Fil Pilot status file does not exists. Create it");
|
||||||
|
Status_File = LittleFS.open(FP_STATUS, "w");
|
||||||
|
Status_File.write(DEFAULT_PILOTE_STATUS);
|
||||||
|
Status_File.close();
|
||||||
|
Pilote(DEFAULT_PILOTE_STATUS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// File should exist so read it
|
||||||
|
Serial.println(F("Status FP from FS : "));
|
||||||
|
Status_File = LittleFS.open(FP_STATUS, "r");
|
||||||
|
Num_Cde = Status_File.read();
|
||||||
|
Status_File.close();
|
||||||
|
Serial.print(F("Status is : "));
|
||||||
|
Serial.println(Num_Cde);
|
||||||
|
|
||||||
|
Pilote(Num_Cde);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("Booting");
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||||
|
Serial.println("Connection Failed! Rebooting...");
|
||||||
|
delay(5000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup OTA
|
||||||
|
setupOTA();
|
||||||
|
|
||||||
|
// Setup LittleFS
|
||||||
|
setupLFS();
|
||||||
|
|
||||||
|
// Alternance -
|
||||||
|
pinMode(ALT_NEG, OUTPUT);
|
||||||
|
// Alternance +
|
||||||
|
pinMode(ALT_POS, OUTPUT);
|
||||||
|
|
||||||
|
// Now retreive FP status from FS
|
||||||
|
fp_from_fs();
|
||||||
|
|
||||||
|
// We are ready
|
||||||
|
Serial.println("Ready");
|
||||||
|
Serial.print("IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Pilote(fp);
|
||||||
|
fp = fp + 1;
|
||||||
|
if (fp >= 4) {
|
||||||
|
fp = 0;
|
||||||
|
}
|
||||||
|
delay(30000);
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
1175
Fil_Pilot/WiFi_Heater_Pilot_v.2.8/WiFi_Heater_Pilot_v.2.8.ino
Normal file
1175
Fil_Pilot/WiFi_Heater_Pilot_v.2.8/WiFi_Heater_Pilot_v.2.8.ino
Normal file
File diff suppressed because it is too large
Load Diff
713
WS3/WS3-ESP32-v2/WS3-ESP32-v2.ino
Normal file
713
WS3/WS3-ESP32-v2/WS3-ESP32-v2.ino
Normal file
@ -0,0 +1,713 @@
|
|||||||
|
// Weather Station 3 from AliExpress / TaoBao
|
||||||
|
//
|
||||||
|
// ESP32-PoE-ISO version.
|
||||||
|
// This version uses ONLY the ethernet interface.
|
||||||
|
//
|
||||||
|
// 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 <DNSServer.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
// MQTT
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <ETH.h>
|
||||||
|
|
||||||
|
// Domoticz MQTT configuration has to be coded here
|
||||||
|
const char *mqtt_server = "portbuild.home.oav.net";
|
||||||
|
#define mqtt_port 1883
|
||||||
|
|
||||||
|
// configuration values
|
||||||
|
const char idx_windir[4]="48";
|
||||||
|
const char idx_temp[4] ="52";
|
||||||
|
const 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];
|
||||||
|
|
||||||
|
// In case of too much MQTT Failure
|
||||||
|
volatile unsigned int mqttfail = 0;
|
||||||
|
#define MAX_MQTT_FAIL 30;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Ethernet stuff
|
||||||
|
#define ETH_CLK_MODE ETH_CLOCK_GPIO17_OUT
|
||||||
|
#define ETH_PHY_POWER 12
|
||||||
|
|
||||||
|
static bool eth_connected = false;
|
||||||
|
|
||||||
|
// Place holder for the packet received
|
||||||
|
String pkt_str = "";
|
||||||
|
|
||||||
|
// Flag for packet OK
|
||||||
|
volatile byte pkt_ok = false;
|
||||||
|
|
||||||
|
//#define WS3 Serial2
|
||||||
|
#define WS3 Serial1
|
||||||
|
|
||||||
|
//WiFiClient espClient; // FIXME
|
||||||
|
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
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiEvent(WiFiEvent_t event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case SYSTEM_EVENT_ETH_START:
|
||||||
|
Serial.println("ETH Started");
|
||||||
|
//set eth hostname here
|
||||||
|
ETH.setHostname("esp32-ethernet");
|
||||||
|
//ETH.setHostname(Mqtt_clientid);
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_CONNECTED:
|
||||||
|
Serial.println("ETH Connected");
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_GOT_IP:
|
||||||
|
Serial.print("ETH MAC: ");
|
||||||
|
Serial.print(ETH.macAddress());
|
||||||
|
Serial.print(", IPv4: ");
|
||||||
|
Serial.print(ETH.localIP());
|
||||||
|
if (ETH.fullDuplex()) {
|
||||||
|
Serial.print(", FULL_DUPLEX");
|
||||||
|
}
|
||||||
|
Serial.print(", ");
|
||||||
|
Serial.print(ETH.linkSpeed());
|
||||||
|
Serial.println("Mbps");
|
||||||
|
eth_connected = true;
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||||
|
Serial.println("ETH Disconnected");
|
||||||
|
eth_connected = false;
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_ETH_STOP:
|
||||||
|
Serial.println("ETH Stopped");
|
||||||
|
eth_connected = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the stuff.
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) ; // wait for Arduino Serial Monitor to open
|
||||||
|
Serial.println("\n\n");
|
||||||
|
Serial.println("Weather Station 3 Adapter by Kiwi");
|
||||||
|
|
||||||
|
// Enable Ethernet
|
||||||
|
// Why this is "WiFi" instead of Eth????
|
||||||
|
WiFi.onEvent(WiFiEvent);
|
||||||
|
ETH.begin();
|
||||||
|
|
||||||
|
//while(!eth_connected);
|
||||||
|
// Wait 30 seconds to allow Cisco switch to be ready
|
||||||
|
Serial.println("Wait for link up and IP address to be ready - 10 sec ...");
|
||||||
|
delay(10*1000);
|
||||||
|
|
||||||
|
// Launch OTA stuff
|
||||||
|
setup_ota();
|
||||||
|
|
||||||
|
// Start the Software Serial for WS3
|
||||||
|
// WS3.begin(WS3_BAUD,SERIAL_8N1, 16,17);
|
||||||
|
// GPIO 17 is used by Ethernet, so use unused IO -> 10
|
||||||
|
//WS3.begin(WS3_BAUD,SERIAL_8N1, 16,10);
|
||||||
|
WS3.begin(WS3_BAUD,SERIAL_8N1, 36,4);
|
||||||
|
|
||||||
|
|
||||||
|
while (!WS3) ; // wait for Arduino Serial Monitor to open
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 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.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 */
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
mqttfail = mqttfail + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
if(eth_connected) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mqttfail > 5) {
|
||||||
|
debugln("MQTT Fails more than 5 times, reconnect");
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
|
if (mqttfail > 30) {
|
||||||
|
debugln("MQTT Fails : reboot");
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
// Weather Station 3 from AliExpress / TaoBao
|
// Weather Station 3 from AliExpress / TaoBao
|
||||||
//
|
//
|
||||||
// Code for ESP32-PoE-ISO
|
// 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/
|
// Highly inspired by https://github.com/kquinsland/ws3-to-esphome-bridge/
|
||||||
//
|
//
|
||||||
@ -58,60 +60,12 @@ SoftwareSerial WS3(RXD2,TXD2);
|
|||||||
WiFiClient espClient;
|
WiFiClient espClient;
|
||||||
PubSubClient client(espClient);
|
PubSubClient client(espClient);
|
||||||
|
|
||||||
// After parsing the string of bytes, we'll have an easier to use struct
|
// ws3_wifi.ino
|
||||||
// TODO: this should be it's own file?
|
void setupSpiffs();
|
||||||
struct WS3Packet {
|
void saveConfigCallback();
|
||||||
|
|
||||||
// The 1st field is "A0000" - Wind direction AD value in real time (0-4096)
|
// ws3_ota.ino
|
||||||
unsigned int wind_dir;
|
void setup_ota();
|
||||||
// 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 the stuff.
|
// Setup the stuff.
|
||||||
@ -221,205 +175,6 @@ void setup() {
|
|||||||
debugln("MQTT started");
|
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)
|
|
||||||
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();
|
|
||||||
|
|
||||||
// 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.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
|
// Print the data over MQTT
|
||||||
void push_weather(WS3Packet* p) {
|
void push_weather(WS3Packet* p) {
|
||||||
String MQPayload;
|
String MQPayload;
|
||||||
@ -486,53 +241,6 @@ void push_weather(WS3Packet* p) {
|
|||||||
sendMQTTPayload(MQPayload);
|
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() {
|
void loop() {
|
||||||
// MQTT
|
// MQTT
|
||||||
if (!client.connected()) {
|
if (!client.connected()) {
|
||||||
|
|||||||
301
WS3/WS3-ESP32/ws3_stuff.ino
Normal file
301
WS3/WS3-ESP32/ws3_stuff.ino
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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)
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 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.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 */
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user