Bonjour, je travaille toujours sur mon projet de station météo, et je remarque que mon wemos d1 mini redémarre de façon aléatoire (de qqes minutes à qqes heures) avec ce message Exception (29).
Après des recherches sur google il semblerait que le module essaye d'écrire dans une zone interdite mais je ne vois pas où dans mon sketch, voici le code principal :
/*
Station météo à base d'un ESP8266, et envoi des données
vers Influxdb, Weathercloud et Wunderground.
Création Dominique PAUL.
Adaptée par Anthony&Philippe
Version 3_2 : ajout de l'envoi de l'angle de direction du vent dans influxdb
envoi des données pluie valeur = 0 pour avoir prévision météo dans Weathercloud
Version 3_3 : correction de bugs dans weathercloud ex wchill et heat en cas de non vent ou de chaleur
envoi des données vers wunderground
Version 3_4 : correction du bug sur wunderground pour dew
envoi du RSSI wifi sur Influxdb
Version 3_5 : séparation de Weathercloud et wunderground pour des envois séparés.
affichage du Rssi Wifi dans le voltage weathercloud et Influxdb.
Version 3_5_5 : ajout d'un pluviomètre et envoi des données sur WC, WU et Influxdb
mise en eeprom de la valeur pluieJour en cas de panne de courant ou redémarrage
réglage heure hiver et été dans wifi.cpp
Version 3_5_6 : essai d'envoi des données du pluvio toutes les 6 minutes au lieu de 2
pour economiser de l'eeprom
3_5_61 : suppression du fifo dans pluvio.cpp
Version 3_5_7 : essai d'une autre librairie pour OTA et avoir un serveur web
incrémentation du nombre de lecture eeprom pour debug
ajout de conditions pour remise à zéro de pluieJour dans pluvio.cpp
essai de calibration du pluvio avec un godet à 0,0931
Version 3_5_71 : essai de raincount24h++ pour la valeur de pluieJour et suppression des écritures dans eeprom
ajout d'un module horloge et sd card DS1307 pour obtenir date et heure sans NTP, sans Wifi et en cas de
coupure de courant et écriture valeur pluieJour sur carte SD.
ajout d'une page web pour redémarrer 192.168.1.../restart.
ajout d'une page web pour réinitialiser le BME280 192.168.1.xxx/bmereset
ajout de myTime dans vent.cpp pour duree de connexion de la station sur influxdb
Version 3_5_72_2209_2023 : ajout d'un capteur UV VEML6075
essai envois d'email.
Version 3_7 : utilisation de la nvram du ds1307 pour enregistrer valeurs pluieJour et jourCourant, et rafaleMaxi et suppression du module carte SD.
Dépot Github : https://github.com/DomoticDIY/.........
Chaine YouTube du Tuto Vidéo : https://www.youtube.com/c/DomoticDIY
Bibliothéques nécessaires, et version utilisées :
- pubsubclient v2.7.0 : https://github.com/knolleary/pubsubclient
- ArduinoJson v6.15.0 : https://github.com/bblanchon/ArduinoJson
- ESP8266Wifi v1.0.0
- Adafruit unified sensor v 1.1.2
- Adafruit BME280 v2.0.2
- Sparkfun VEML6075 library v2.2.0
- NTPClient by Fabrice Weinberg v3.2.0
- CircularBuffer by AgileWare v1.3.3
Connexions sur Wemos D1 mini :
- pluviomètre sur D5
- anemomètre sur D6
- girouette sur A0
- carte LEXTRONIC 3V sur 3V3
- carte LEXTRONIC GND sur G
- bme 280 SDA sur D2
- bme 280 SCL sur D1
- bme 280 GND sur GND LEXTRONIC
- bme 280 3V3 sur 3V3 LEXTRONIC
- veml6075 SDA sur D2
- veml6075 SCL sur D1
- veml6075 GND sur GND LEXTRONIC
- veml6075 3V3 sur 3V3 LEXTRONIC
- DS1307 SDA sur D2 Horloge
- DS1307 SCL sur D1 Horloge
- DS1307 GND sur... Horloge
- DS1307 3V3 sur .... Horloge
Gestionnaire de carte ESP8266 version 3.1.2
Type de carte : generic ESP8266 module
Flash mode 'DOUT'
Flash size : 4MB (FS: none)(OTA :1019 kb)
*/
#include "connect.h"
#include "wifi.h"
#include "bme280.h"
#include "vent.h"
#include "pluvio.h"
#include "veml6075.h"
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ESP_Mail_Client.h>
#include <AsyncElegantOTA.h>
#include <SPI.h>
#include <Wire.h> // I2C library
#include "I2Cbus.h" // I2Cbus library
#include <RTClib.h>
// Variables et constantes utilisateur :
#define INTERO_CAPTEURS 300 // 5m (300s) : Valeur de l'intervalle en secondes entre 2 relevés des capteurs (BME280 et VEML6075)et envoi sur Influxdb.
unsigned long t_lastActionPluvio = 0; // enregistre le Time de la dernière intérogation des capteurs Pluviomètrie.
#define INTERO_PLUVIO 300 // Valeur de l'intervale (300 ou 5 mn) en secondes entre 2 relevés des capteurs Pluviomètrie.
#define INTERO_VENT 30 // 1/2 min (30s) Valeur de l'intervalle en secondes entre 2 relevés des capteurs Vent et envoi sur Influxdb.
unsigned long t_lastWeatherCloud = 0;
#define INTERVAL_WEATHERCLOUD 300 // 5 min (300s) d'intervalle pour les requêtes à WeatherCloud
unsigned long t_lastWunderGround = 0;
#define INTERVAL_WUNDERGROUND 30 // 1/2 min (30s) d'intervalle pour les requêtes à Wunderground
unsigned long t_lastActionCapteur = 0; // enregistre le Time de la dernière intérogation des capteurs.
unsigned long t_lastActionVent = 0; // enregistre le Time de la dernière intérogation des capteurs vent.
unsigned long t_lastRafaleVent = 0; // Enregistre le Time du dernier relevé de Rafale de vent.
int i2cbusstatus;
AsyncWebServer server3(80);
SMTPSession smtp;
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN
Serial.begin(115200); // On initialise la vitesse de transmission de la console.
delay(500);
setup_wifi(); // Connexion au Wifi
init_capteurs(); // Initialisation des Capteurs de relevés
//------------------------------------------------------------------------//
server3.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(200, "text/plain", "HTTP serveur OTA Station Essai version 3_7_06112023.");
});
//-------------------------------------------------------------------------//
server3.on("/heurehiver", HTTP_GET, [](AsyncWebServerRequest * request) {
RTC_DS1307 rtc;
rtc.begin();
DateTime now = rtc.now();
Serial.println(F("Le module RTC est actif !"));
/*int hour = now.hour();
hour = hour - 1;
Serial.print("Nouvelle heure d'hiver:");
Serial.println(hour);*/
//rtc.adjust(DateTime(2023, 6, 18, 13, 59, 0));
rtc.adjust(DateTime(now.year(), now.month(), now.day(), (now.hour() - 1), now.minute(), now.second()));
request->send(200, "text/plain", "Heure modifiee -1H Hiver.");
delay(1000);
});
//---------------------------------------------------------------------------//
server3.on("/heurete", HTTP_GET, [](AsyncWebServerRequest * request) {
RTC_DS1307 rtc;
rtc.begin();
DateTime now = rtc.now();
Serial.println(F("Le module RTC est actif !"));
/*int hour = now.hour();
hour = hour + 1;
Serial.print("Nouvelle heure d'été:");
Serial.println(hour);*/
//rtc.adjust(DateTime(2023, 6, 18, 13, 59, 0));
rtc.adjust(DateTime(now.year(), now.month(), now.day(), (now.hour() + 1), now.minute(), now.second()));
request->send(200, "text/plain", "Heure modifiee +1H Ete.");
delay(1000);
});
//-----------------------------------------------------------------------//
server3.on("/rtcajust", HTTP_GET, [](AsyncWebServerRequest * request) {
RTC_DS1307 rtc;
rtc.begin();
DateTime now = rtc.now();
Serial.println(F("L'horloge du module RTC est active !"));
//rtc.adjust(DateTime(2023, 6, 18, 13, 59, 0));
rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), 0, 0));
request->send(200, "text/plain", "RTC mise à la minute !");
delay(1000);
});
//-----------------------------------------------------------------------//
server3.on("/i2creset", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(200, "text/plain", "Réinitialisation du bus I2C !");
Serial.println("Réinitialisation du bus I2C");
//init_bme(); // Initialisation de BME280.
// Clear bus before Wire.begin()
i2cbusstatus = I2Cbus_clear(SDA, SCL);
Serial.println( I2Cbus_statusstr(i2cbusstatus) );
// Enable I2C
Wire.begin(SDA, SCL); // For ESP8266 NodeMCU boards [VDD to 3V3, GND to GND, SDA to D2, SCL to D1]
//Wire.begin(); // Arduino pro mini or Nano [VDD to VCC/3V3, GND to GND, SDA to A4, SCL to A5]
Serial.println("Effectué !");
init_capteurs(); // Réinitialisation des Capteurs de relevés
});
//----------------------------------------------------------------------//
server3.on("/restart", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncResponseStream *response = request->beginResponseStream("text/html");
response->addHeader("Server", "ESP Async Web Server");
response->printf("<!DOCTYPE html><html lang='en'>");
response->printf("<head>");
response->printf("<meta charset='UTF-8'>");
response->printf("<title>Restart</title>");
response->printf("<script> function confirmRestart() {const req = new XMLHttpRequest();req.open('GET', '/confirmRestart');req.send();}</script>");
response->printf("</head>");
response->printf("<body><button onclick='confirmRestart()'>Confirm restart</button></body>");
response->printf("</html>");
request->send(response);
});
server3.on("/confirmRestart", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(200, "text/plain", "RESTART OK");
delay(1000);
ESP.reset();
});
//-----------------------------------------------------------------------------//
AsyncElegantOTA.begin(&server3); // Start ElegantOTA
server3.begin();
Serial.println("HTTP serveur OTA démarré version 3_7_06112023");
Serial.print("Addresse IP : ");
Serial.println(WiFi.localIP());
Serial.println("Waiting for NTP server time reading");
// configTime(2 * 3600, 0, "pool.ntp.org", "time.nist.gov"); // heure été
configTime(1 * 3600, 0, "pool.ntp.org", "time.nist.gov"); // heure hiver
while (time(nullptr) < ESP_MAIL_CLIENT_VALID_TS)
{
delay(100);
}
MailClient.networkReconnect(true);
Session_Config config;
config.server.host_name = SMTP_HOST;
config.server.port = SMTP_PORT;
config.login.email = AUTHOR_EMAIL;
config.login.password = AUTHOR_PASSWORD;
config.login.user_domain = F("127.0.0.1");
SMTP_Message message;
// Set the message headers
message.sender.name = F("Station Météo Lamartine");
message.sender.email = AUTHOR_EMAIL;
message.addRecipient(F("Someone"), RECIPIENT_EMAIL);
// The time format of timestamp to inject into subject or content as using in strftime C++ function
message.timestamp.tag = "#esp_mail_current_time";
// The tag that will be replaced with current timestamp
message.timestamp.format = "%B %d, %Y %H:%M:%S";
message.subject = F("Test sending plain text Email (#esp_mail_current_time)");
// message.subject = F("Démarrage ou Redémarrage Station Météo3 Email (#esp_mail_current_time)");
message.text.content = "La Station Météo3 vient de re(démarrer) ! !\n\nEnvoyé le : #esp_mail_current_time";
// message.text.content = "La Station Météo vient de re(démarrer) !";
if (!smtp.connect(&config))
{
MailClient.printf("Connection erreur, Status Code: %d, Erreur Code: %d, Raison: %s\n", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
return;
}
if (!smtp.isLoggedIn())
{
Serial.println("Pas connecté au serveur SMTP.");
}
else
{
if (smtp.isAuthenticated())
Serial.println("Authentification réussie.");
else
Serial.println("Connecté sans autorisation.");
}
if (!MailClient.sendMail(&smtp, &message)) {
MailClient.printf("Erreur, Status Code: %d, Erreur Code: %d, Raison: %s\n", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
} else
{
Serial.println("Message bien envoyé !");
}
}
void loop() {
unsigned long currentMillis = millis(); // On enregistre le Time courant
if ((WiFi.status() != WL_CONNECTED) && (currentMillis > 300000)) {
Serial.println("Essai de reconnexion wifi ..redémarrage du Wemos !");
ESP.restart();
}
//----------------------------------------------------------------------------------//
// On vérifie si l'intervale d'envoi des informations "Vent" sont atteintes. (120s) //
//----------------------------------------------------------------------------------//
if (currentMillis - t_lastActionVent >= INTERO_VENT * 1000) {
t_lastActionVent = currentMillis; // On met à jour la valeur du dernier traitement à maintenant.
// Si il est actif, on envoi les info de vent.
#ifdef VENT_H
setInfo_vent(); // Envoi des données de vent à Influxdb
#endif
}
// On reléve la rafale de vent des 5 dernières secondes.
if (millis() - t_lastRafaleVent >= (5 * 1000)) {
// On met à jour la valeur du dernier traitement de Rafale de vent à maintenant.
t_lastRafaleVent = millis();
// S'il est actif, On a atteint l'interval souhaité, on exécute le traitement Vent.
#ifdef VENT_H
getRafale();
#endif
}
//--------------------------------------------------------------------------------//
// On vérifie si l'intervale d'envoi des informations pluie sont atteintes. (120s)//
//--------------------------------------------------------------------------------//
if (currentMillis - t_lastActionPluvio >= INTERO_PLUVIO * 1000) {
// On met à jour la valeur du dernier traitement à maintenant.
t_lastActionPluvio = currentMillis;
// Si il est actif, on envoi les info de pluviometre.
#ifdef PLUVIO_H
getDataPluvio(); // Envoi des données de pluviomètrie à InfluxDb
#endif
}
//---------------------------------------------------------------------//
// On vérifie si l'intervale de relevé des capteurs est atteint. (60s)-//
//---------------------------------------------------------------------//
if (currentMillis - t_lastActionCapteur >= INTERO_CAPTEURS * 1000) {
// On met à jour la valeur du dernier traitement à maintenant.
t_lastActionCapteur = currentMillis;
#ifdef BME280_H
sendDataBME280(getWindSpeed()); // Relevé des données du BME280, et envoi à Influxdb
#endif
#ifdef VEML6075_H
getDataVEML6075(); // Relevé des données du VEML6075, et envoi.
#endif
}
if (currentMillis - t_lastWunderGround >= INTERVAL_WUNDERGROUND * 1000) {
// On met à jour la valeur du dernier traitement à maintenant.
t_lastWunderGround = currentMillis;
#if defined(VENT_H) && defined(BME280_H)
sendDataToWunderground();
#endif
}
if (currentMillis - t_lastWeatherCloud >= INTERVAL_WEATHERCLOUD * 1000) {
// On met à jour la valeur du dernier traitement à maintenant.
t_lastWeatherCloud = currentMillis;
#if defined(VENT_H) && defined(BME280_H)
sendDataToWeatherCloud();
#endif
}
//ESP.wdtFeed();
//delay(2000);
} // fin de loop
//******************************//
// INITIALISATION des Capteurs //
// *****************************//
void init_capteurs() {
#ifdef BME280_H
init_bme(); // Initialisation de BME280.
#endif
#ifdef VEML6075_H
init_veml(); // Initialisation du VEML6075.
#endif
#ifdef PLUVIO_H
init_pluvio(); // Initialisation de l'interruption du capteur de pluviomètrie.
#endif
#ifdef VENT_H
init_vent(); // Initialisation Girouette et anémomêtre.
#endif
}
//*********************************//
// ENVOI des infos à WeatherCloud //
//*********************************//
void sendDataToWeatherCloud() {
digitalWrite(LED_BUILTIN, LOW); // Turn the LED on
float temp = getTemperature() - 2; // Attention correction température
float hum = getHumidity();
float bar = getPressure();
float windSpeed = getWindSpeed();
int windDirAngle = getWindDir();
float windRafale = getWindRafale();
float total_h = getPluieHeure();
float total_j = getPluieJour();
Serial.print("pluie heure debut WC:");
Serial.println(total_h);
Serial.print("pluie jour debut WC:");
Serial.println(total_j);
float heatIndex;
int heat, dew;
//-----------------------------------------------------------------------------------------------//
//-------------- CALCUL DU POINT DE ROSEE -(formule de Heinrich Gustav Magnus-Tetens)------------//
//-----------------------------------------------------------------------------------------------//
double gamma = log(hum / 100) + ((17.62 * temp) / (243.5 + temp));
double dewPoint = (243.5 * gamma / (17.62 - gamma));
dew = int(dewPoint * 10);
//-------------------------------------------------------------//
//--------------- CALCUL DE L'INDEX DE CHALEUR ----------------//
//-------------------------------------------------------------//
#define c1 -8.78469475556
#define c2 1.61139411
#define c3 2.33854883889
#define c4 -0.14611605
#define c5 -0.012308094
#define c6 -0.0164248277778
#define c7 0.002211732
#define c8 0.00072546
#define c9 -0.000003582
heatIndex = c1 + (c2 * temp) + (c3 * hum) + (c4 * temp * hum) + (c5 * sq(temp)) + (c6 * sq(hum)) + (c7 * sq(temp) * hum) + (c8 * temp * sq(hum)) + (c9 * sq(temp) * sq(hum));
heat = int(heatIndex * 10);
if (temp < 27) heat = temp * 10;
//----------------------------------------------------------------------//
//-------------------- CALCUL DE LA TEMPERATURE RESSENTIE --------------//
//----------------------------------------------------------------------//
float kph, windChill;
int wchill;
kph = windSpeed;
Serial.println("Wind speed measurement complete.\n");
Serial.println(kph);
//R = 13,12 + 0,6215 T + (0,3965 T - 11,37) V16 où T est température ambiante exprimée en °C et V est la vitesse du vent exprimée en km/h.
windChill = (13.12 + (0.62 * temp) - (11.37 * (pow(kph, 0.16))) + (0.39 * temp * (pow(kph, 0.16))));
wchill = int(windChill * 10);
if (kph < 4.8) wchill = temp * 10;
if (temp > 27) wchill = temp * 10;
//*
//-------------ENVOI SUR WEATHERCLOUD-----------------------------------//
WiFiClient client;
Serial.println("Initializing data push to Weathercloud.");
if (!client.connect(server, 80)) {
Serial.println("Connecting to Weatherloud failed.\n");
return;
}
client.print("GET /v01/set");
client.print("/wid/"); client.print(ID);
client.print("/key/"); client.print(Key);
client.print("/softwareid/"); client.print("d50a5c965d45");
client.print("/temp/"); client.print(temp * 10);
client.print("/hum/"); client.print(hum);
client.print("/bar/"); client.print(bar * 10);
client.print("/dew/"); client.print(dew);
Serial.println(dew);
client.print("/heat/"); client.print(heat);
Serial.println(heat);
int dBm2 = 0;
dBm2 = abs(WiFi.RSSI() * 10);
Serial.print("Valeur du RSSI en dB-2: ");
client.print("/et/"); client.print(dBm2);
client.print("/battery/"); client.print(dBm2 / 10);
Serial.println(dBm2);
//int rain = 0;
//int rainrate = 0;
client.print("/rain/"); client.print(total_j * 10); // quantité de pluie dans une journée
client.print("/rainrate/"); client.print(total_h * 10); // quantité de pluie dans une heure
Serial.print("Valeur de pluie heure envoyee a WC :");
Serial.println(total_h * 10);
Serial.print("Valeur de pluie jour envoyee a WC :");
Serial.println(total_j * 10);
client.print("/chill/"); client.print(wchill);
Serial.println(wchill);
client.print("/wspd/"); client.print(metersPerSecond(windSpeed) * 10);
client.print("/wdir/"); client.print(windDirAngle);
client.print("/wspdhi/"); client.print(metersPerSecond(windRafale) * 10);
float uv = getIndexUv();
Serial.print("Index UV envoyé à WC");
Serial.println (uv);
float solarrad = getRaySolar();
Serial.print("Rayonnement envoyé à WC");
Serial.println(solarrad);
client.print("/uvi/"); client.print(uv * 10); // index uv x10 [0,160] 8.0->80
client.print("/solarrad/"); client.print(solarrad); // watts/m2 x10 [0,20000] 1050.0->10500
client.println("/ HTTP/1.1");
client.println("Host: 188.164.196.110");
client.println("Connection: close");
client.println();
Serial.println("Données envoyées vers Weathercloud avec succès.\n");
digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off
}
//---------------------------------------------------------------------//
//------------------ Envoi des données sur Wunderground ---------------//
//---------------------------------------------------------------------//
void sendDataToWunderground() {
digitalWrite(LED_BUILTIN, LOW); // Turn the LED on
float temp2 = getTemperature() - 2; // Attention correction température
float hum2 = getHumidity();
float bar2 = getPressure();
float windSpeed2 = getWindSpeed();
int windDirAngle2 = getWindDir();
float windRafale2 = getWindRafale();
float total_h2 = getPluieHeure();
//float total_h2 = 10;
float total_j2 = getPluieJour();
//float total_j2 = 20;
//-----------------------------------------------------------------------------------------------//
//-------------- CALCUL DU POINT DE ROSEE -(formule de Heinrich Gustav Magnus-Tetens)------------//
//-----------------------------------------------------------------------------------------------//
int dew2;
double gamma = log(hum2 / 100) + ((17.62 * temp2) / (243.5 + temp2));
double dewPoint2 = (243.5 * gamma / (17.62 - gamma));
dew2 = int(dewPoint2 * 10);
Serial.print("Connecting to ");
Serial.println(server2);
WiFiClient client;
if (client.connect(server2, 80)) {
Serial.print("connected to ");
Serial.println(client.remoteIP());
delay(1000);
} else {
Serial.println("connection failed");
}
client.print(WEBPAGE);
client.print("ID=");
client.print(ID2);
client.print("&PASSWORD=");
client.print(Key2);
client.print("&dateutc=now");
// client.print(CalDirection);
client.print("&tempf=");
//temp2 = 4;
client.print((temp2 * 9 / 5) + 32);
client.print("&dewptf=");
//dew2 = 1;
client.print(((dew2 / 10) * 9 / 5) + 32);
client.print("&winddir=");
//windDirAngle2 = 270;
client.print(windDirAngle2);
client.print("&windspeedmph=");
//windSpeed2 = 10;
client.print(windSpeed2 * 0.621371);
client.print("&windgustmph=");
//windRafale2 = 15;
client.print(windRafale2 * 0.621371);
float uv2 = getIndexUv();
float solarrad2 = getRaySolar();
client.print("&rainin=");
client.print (total_h2 / 25.4); // attention à mettre en pouces (inches) !
client.print("&dailyrainin=");
client.print (total_j2 / 25.4); // attention à mettre en pouces (inches) !
client.print("&UV=");
client.print (uv2);
client.print("&solarradiation=");
client.print (solarrad2 / 10);
client.print("&humidity=");
//hum2 = 98;
client.print(hum2);
client.print("&baromin=");
//bar2 = 1004;
client.print(bar2 / 33.863);
client.print("&softwaretype=Wemos_D1_Mini_V3_7_06112023&action=updateraw&realtime=1&rtfreq=30");
client.print("/ HTTP/1.1\r\nHost: rtupdate.wunderground.com:80\r\nConnection: close\r\n\r\n");
Serial.println(" ");
delay(1000);
digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off
}
float metersPerSecond(float kilometersPerHour) {
return round((kilometersPerHour * 1000 / 3600) * 10) / 10; // arrondi à 1 décimale
}
Je ne sais pas comment orienter mes recherches, merci de vos réponses par avance.
Bien cordialement
Philippe



