Hello everyone.
I built a weather station with an ESP32 and inspired by the Solar Powered WiFi Weather Station V3.0 project : https://www.instructables.com/Solar-Powered-WiFi-Weather-Station-V30/
My project uses sensors and the Sparkfun Weather Meter Kit : https://www.sparkfun.com/products/15901
All data are sent to my web site (http://meteobarsurloup.fpvracer.fr) to display gauges and histograms of sensor values.
My sketch is here:
//======================================================================================//
// //
// Station météo V1.0 Firmware //
// //
// Développé par Steve Pirchi, Dernière mise à jour : 06.07.2021 //
// //
//======================================================================================//
//=================== Required Libraries ==========================================
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "Adafruit_VEML6070.h"
#include <BH1750.h>
#include "Adafruit_CCS811.h"
#include <OneWire.h>
#include <DallasTemperature.h>
//=================== Pin assignment definitions ==========================================
// rain & wind sensor
#define WIND_SPD_PIN 14
#define RAIN_PIN 25
#define WIND_DIR_PIN 35
// GPIO where the DS18B20 is connected to
const int oneWireBus = 4;
// UV sensor
Adafruit_VEML6070 uv = Adafruit_VEML6070();
// Light sensor
BH1750 lightMeter(0x23);
// BME280 sensor
Adafruit_BME280 bme;
// CJMCU 8118 sensor
Adafruit_CCS811 ccs;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
//========================= Declaring Variables and Constants ==================================
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTime = 0;
// Timer set to 10 minutes (600000)
//unsigned long timerDelay = 600000;
// Set timer to 5 minutes (300000)
unsigned long timerDelay = 300000;
// Variables used in calculating the windspeed
volatile unsigned long timeSinceLastTick = 0;
volatile unsigned long lastTick = 0;
float windSpeed;
// Variables used in calculating the wind direction
int vin;
String windDir = "";
// Variables and constants used in tracking rainfall
#define S_IN_DAY 86400
#define S_IN_HR 3600
#define NO_RAIN_SAMPLES 2000
volatile long rainTickList[NO_RAIN_SAMPLES];
volatile int rainTickIndex = 0;
volatile int rainTicks = 0;
int rainLastDay = 0;
int rainLastHour = 0;
int rainLastHourStart = 0;
int rainLastDayStart = 0;
long secsClock = 0;
//========================= Sea level pressure ==================================
// sea level pressure from your location
#define SEALEVELPRESSURE_HPA (1020.6)
//========================= Variables for wifi server setup =============================
// Wifi credentials
const char* ssid = "NETGEAR78";
const char* password = "mypassword";
//Your Domain name with URL path or IP address with path
const char* serverName = "http://meteobarsurloup.fpvracer.fr/esp-post-data.php";
// Keep this API Key value to be compatible with the PHP code provided in the project page.
// If you change the apiKeyValue value, the PHP file /esp-post-data.php also needs to have the same key
String apiKeyValue = "tPmAT5Ab3j7F9";
String sensorName = "BME280";
String sensorLocation = "Le Bar sur Loup";
//========================= Setup Function ================================================
void setup() {
Serial.begin(115200);
delay(25);
Serial.println("\nWeather station powered on.\n");
// start wifi
WiFi.begin(ssid, password);
Serial.println("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
// start bme280 sensor
bool status = bme.begin(0x77);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring or change I2C address!");
while (1);
}
Serial.println("Timer set to 30 seconds (timerDelay variable), it will take 30 seconds before publishing the first reading.");
// start UV sensor
uv.begin(VEML6070_1_T); // pass in the integration time constant
// start Light Meter sensor
lightMeter.begin();
// start CCS8111 sensor
if (!ccs.begin()) {
Serial.println("Failed to start sensor! Please check your wiring.");
while (1);
}
//calibrate temperature sensor
while (!ccs.available());
float temp = ccs.calculateTemperature();
ccs.setTempOffset(temp - 25.0);
// Start the DS18B20 sensor
sensors.begin();
// Wind speed sensor setup. The windspeed is calculated according to the number
// of ticks per second. Timestamps are captured in the interrupt, and then converted
// into mph.
pinMode(WIND_SPD_PIN, INPUT); // Wind speed sensor
attachInterrupt(digitalPinToInterrupt(WIND_SPD_PIN), windTick, FALLING);
// Rain sesnor setup. Rainfall is tracked by ticks per second, and timestamps of
// ticks are tracked so rainfall can be "aged" (i.e., rain per hour, per day, etc)
pinMode(RAIN_PIN, INPUT); // Rain sensor
attachInterrupt(digitalPinToInterrupt(RAIN_PIN), rainTick, FALLING);
// Zero out the timestamp array.
for (int i = 0; i < NO_RAIN_SAMPLES; i++) rainTickList[i] = 0;
}
//================================ Loop Function ==============================================
void loop() {
Read_Sensors_Data(); // Read all the Sensors
Send_Data(); // send data to web server
// printdata(); // Print all the sensors data on the serial monitor
}
void Send_Data()
{
//================================ Wifi function ==============================================
//Send an HTTP POST request every 5 minutes
if ((millis() - lastTime) > timerDelay) {
//Check WiFi connection status
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(serverName);
// Specify content-type header
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
float temperatureC = sensors.getTempCByIndex(0);
// Prepare your HTTP POST request data
// value1 = bme280 temperature
// value2 = bme280 humidity
// value3 = bme280 pressure
// value4 = UV sensor
// value5 = light sensor
// value6 = CCS8111 CO2 sensor
// value7 = CCS8111 TVOC sensor
// value8 = DS18B20 temperature sensor
// value9 = rain meter
// value10 = windspeed meter
// value 11 = windir sensor
String httpRequestData = "api_key=" + apiKeyValue + "&sensor=" + sensorName
+ "&location=" + sensorLocation + "&value1=" + String(bme.readTemperature() - 1.1)
+ "&value2=" + String(bme.readHumidity()) + "&value3=" + String((bme.readPressure() / 100.0F) + 24.5) + "&value4=" + String(uv.readUV()) + "&value5=" + String(lightMeter.readLightLevel()) + "&value6=" + String(ccs.geteCO2()) + "&value7=" + String(ccs.getTVOC()) + "&value8=" + String(temperatureC) + "&value9=" + String(float(rainLastHour) * 0.011, 3) + "&value10=" + String(windSpeed * 2.4 * 1.6) + "&value11=" + String(windDir) + "";
Serial.print("httpRequestData: ");
Serial.println(httpRequestData);
// You can comment the httpRequestData variable above
// then, use the httpRequestData variable below (for testing purposes without the BME280 sensor)
//String httpRequestData = "api_key=tPmAT5Ab3j7F9&sensor=BME280&location=Office&value1=24.75&value2=49.54&value3=1005.14";
// Send HTTP POST request
int httpResponseCode = http.POST(httpRequestData);
// If you need an HTTP request with a content type: text/plain
//http.addHeader("Content-Type", "text/plain");
//int httpResponseCode = http.POST("Hello, World!");
// If you need an HTTP request with a content type: application/json, use the following:
//http.addHeader("Content-Type", "application/json");
//int httpResponseCode = http.POST("{\"value1\":\"19\",\"value2\":\"67\",\"value3\":\"78\"}");
if (httpResponseCode > 0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
}
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
// Free resources
http.end();
}
else {
Serial.println("WiFi Disconnected");
}
lastTime = millis();
}
}
//================================ Read Sensors ==============================================
void Read_Sensors_Data() {
//================================ CCS8111 sensor ==============================================
// Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
sensors.requestTemperatures();
//================================ Sparfun sensors ==============================================
// Read Weather Meters Datas ( Wind Speed, Rain Fall and Wind Direction )
static unsigned long outLoopTimer = 0;
static unsigned long wundergroundUpdateTimer = 0;
static unsigned long clockTimer = 0;
static unsigned long tempMSClock = 0;
// Create a seconds clock based on the millis() count. We use this
// to track rainfall by the second. We've done this because the millis()
// count overflows eventually, in a way that makes tracking time stamps
// very difficult.
tempMSClock += millis() - clockTimer;
clockTimer = millis();
while (tempMSClock >= 1000)
{
secsClock++;
tempMSClock -= 1000;
}
// This is a once-per-second timer that calculates and prints off various
// values from the sensors attached to the system.
if (millis() - outLoopTimer >= 2000)
{
outLoopTimer = millis();
// Windspeed calculation, in mph. timeSinceLastTick gets updated by an
// interrupt when ticks come in from the wind speed sensor.
if (timeSinceLastTick != 0) windSpeed = 1000.0 / timeSinceLastTick;
// Calculate the wind direction and display it as a string.
windDirCalc();
rainLastHour = 0;
rainLastDay = 0;
// If there are any captured rain sensor ticks...
if (rainTicks > 0)
{
// Start at the end of the list. rainTickIndex will always be one greater
// than the number of captured samples.
int i = rainTickIndex - 1;
// Iterate over the list and count up the number of samples that have been
// captured with time stamps in the last hour.
while ((rainTickList[i] >= secsClock - S_IN_HR) && rainTickList[i] != 0)
{
i--;
if (i < 0) i = NO_RAIN_SAMPLES - 1;
rainLastHour++;
}
// Repeat the process, this time over days.
i = rainTickIndex - 1;
while ((rainTickList[i] >= secsClock - S_IN_DAY) && rainTickList[i] != 0)
{
i--;
if (i < 0) i = NO_RAIN_SAMPLES - 1;
rainLastDay++;
}
rainLastDayStart = i;
}
}
}
// Keep track of when the last tick came in on the wind sensor.
void windTick(void)
{
timeSinceLastTick = millis() - lastTick;
lastTick = millis();
}
// Capture timestamp of when the rain sensor got tripped.
void rainTick(void)
{
rainTickList[rainTickIndex++] = secsClock;
if (rainTickIndex == NO_RAIN_SAMPLES) rainTickIndex = 0;
rainTicks++;
}
// reading wind direction
void windDirCalc()
{
vin = analogRead(WIND_DIR_PIN);
if (vin < 150) windDir = "202.5";
else if (vin < 300) windDir = "180";
else if (vin < 400) windDir = "247.5";
else if (vin < 600) windDir = "225";
else if (vin < 900) windDir = "292.5";
else if (vin < 1100) windDir = "270";
else if (vin < 1500) windDir = "112.5";
else if (vin < 1700) windDir = "135";
else if (vin < 2250) windDir = "337.5";
else if (vin < 2350) windDir = "315";
else if (vin < 2700) windDir = "67.5";
else if (vin < 3000) windDir = "90";
else if (vin < 3200) windDir = "22.5";
else if (vin < 3400) windDir = "45";
else if (vin < 4000) windDir = "0";
else windDir = "0";
}
//================================ Print data function ==============================================
void printdata() {
float temperatureC = sensors.getTempCByIndex(0);
// print values from sensor
Serial.println("*************************************************");
Serial.println("BME 280 TEST");
Serial.print("Temperature: ");
Serial.println(bme.readTemperature() - 1.1);
Serial.print("Humidite: ");
Serial.println(bme.readHumidity());
Serial.print("Pression atmospherique: ");
// calibration altitude(+24.5)
Serial.println((bme.readPressure() / 100.0F) + 24.5);
Serial.println("--------------------------------");
Serial.println("VEML6070 TEST");
Serial.print("UV light level: ");
Serial.println(uv.readUV());
Serial.println("--------------------------------");
Serial.println("BH1750 TEST");
Serial.print("Light level: ");
Serial.println(lightMeter.readLightLevel());
Serial.println("--------------------------------");
Serial.println("CJMCU 8118 TEST");
if (ccs.available()) {
float temp = ccs.calculateTemperature();
if (!ccs.readData()) {
Serial.print("eCO2: ");
Serial.println(ccs.geteCO2());
Serial.print("TVOC: ");
Serial.println(ccs.getTVOC());
Serial.print("ppb Temp:");
Serial.println(temp);
}
else {
Serial.println("ERROR!");
while (1);
}
}
Serial.println("--------------------------------");
Serial.println("DS18B20 TEST");
Serial.print("Temperature: ");
Serial.print(temperatureC);
Serial.println("ºC");
Serial.println("Winspeed TEST");
Serial.print("Windspeed: "); Serial.print(windSpeed * 2.4 * 1.6); Serial.println(" km/h");
Serial.println("Winsdir TEST");
Serial.print("Wind dir: "); Serial.print(" "); Serial.println(windDir);
Serial.println("Rainfall TEST");
Serial.print("Rainfall last hour: "); Serial.println(float(rainLastHour) * 0.011, 3);
Serial.println("*************************************************");
}
//=============================End of the Program =================================
The main problem is that the sketch crashes after 10 hours, the sensor values are no longer sent to my web site.
I suspect there is a problem with functions of the Rain Fall Sensor or Anemometer and after some code changes I have not fixed this problem.
Another problem is sometimes that the wind speed gives randomely a very high value (3840 km / h) and the rain gauge shows rain when it is not raining ...
Hope someone will see my code and help me.