Added luminosity sensor, more acurate temperature end debounced rainfall meter

master
marcel 21 hours ago
parent aaec2a02ad
commit 92956c2707
  1. 15
      CHANGELOG.md
  2. BIN
      build-doc/images/dfrobot-gravity-ip68-waterproof-ambient-light-sensor-1-65535lx-i2c.png
  3. BIN
      build-doc/images/dfrobot-gravity-ip68-waterproof-ambient-light-sensor-wiring.png
  4. 93
      build-doc/weather_station.html
  5. 77
      build-doc/weather_station.md
  6. BIN
      build-doc/weather_station.pdf
  7. 113
      firmware/weather_station.ino

@ -88,6 +88,8 @@ In rare cases, the humidity value could creep up above 15% (when the actual humi
## [0.3.0] - 2024-05-02
This is the latest release from 2024.
### Added
Support for HYT-221 humidity sensor
@ -95,3 +97,16 @@ Support for HYT-221 humidity sensor
### Removed
Support for si7021 humidity sensor (this sensor was not suited for outdoor measurments)
## [0.3.1] - 2025-01-14
### Changed
- Cleanup of code
- Debounce rainfall meter from 100ms to 250mms
- Main temperature and backup temperature registers switched: the sensor on the HYT221 heats up by the circuitry and is therefore about 1.5 degrees above ambient temperature. The BMP280 does not heat up.
### Added
- Debug messages can be switched on and off with the _DEBUG_ variable
- SEN0562 luminosity sensor

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<meta name="author" content="M.T. Konstapel" />
<meta name="dcterms.date" content="2024-05-02" />
<meta name="dcterms.date" content="2025-01-14" />
<title>Weather station</title>
<link rel="stylesheet" href="./css/mvp.css" />
<style type="text/css">
@ -56,6 +56,8 @@
<li><a href="#problems-i-encounter-after-four-months-of-use"
id="toc-problems-i-encounter-after-four-months-of-use">Problems
I encounter after four months of use</a></li>
<li><a href="#additions"
id="toc-additions">Additions</a></li>
<li><a href="#software-dependencies"
id="toc-software-dependencies">Software
dependencies</a></li>
@ -78,7 +80,7 @@
<h1 class="title">Weather station</h1>
<p class="subtitle">with ModBus RTU interface</p>
<p class="author">M.T. Konstapel</p>
<p class="date">2024-05-02</p>
<p class="date">2025-01-14</p>
<p><a href="./weather_station.pdf"><i>PDF version</i></a></p>
</header>
<main>
@ -873,6 +875,7 @@ Address : 14</code></pre>
src="./images/weather_station_schematic.svg" alt="Schematic" /></a></p>
<h1 id="problems-i-encounter-after-four-months-of-use">Problems I
encounter after four months of use</h1>
<h2 id="humidity-sensor">Humidity sensor</h2>
<p>The Si7021 humidity sensor is not made for outdoor use. The datasheet
is clear about that. But a lot of people use this cheap sensor for
weather stations anyway. So I choose this sensor for my design. But that
@ -894,6 +897,88 @@ price: it is ten times more expensive than the Si7021.</p>
<p>The sensor can be controlled via the I2C bus, so implementing the new
sensor in the firmware was very easy. From version 0.3.0 onward, this
sensor is used. The Si7021 is removed from the code.</p>
<h2 id="pressure-sensor">Pressure sensor</h2>
<p>After a year the BMP280 pressure sensor started to give odd pressure
values. The pressure was around 700hPa and followed the temperature
curve. The temperature sensor still worked fine. After replacing the
BMP280 the barometric values returned to normal. I suspect that it
malfunctioned because of the perpetual fog we had for about two months.
The sensor was mounted in the Garni RS1 Passive Radiation Shield. Rain
could not enter the shield, but fog could.</p>
<h1 id="additions">Additions</h1>
<h2 id="luminosity-sensor">Luminosity sensor</h2>
<p>From version 0.3.1 onward, the weather station has an ambient light
sensor. It is an SEN0562 from DFRobot and it outputs the light intensity
in Lux. It is a 5 Volt only device, so it should be connected to the 5
Volt I2C bus.</p>
<figure>
<img
src="./images/dfrobot-gravity-ip68-waterproof-ambient-light-sensor-1-65535lx-i2c.png"
alt="The SEN0562 ambient light sensor" />
<figcaption aria-hidden="true">The SEN0562 ambient light
sensor</figcaption>
</figure>
<figure>
<img
src="./images/dfrobot-gravity-ip68-waterproof-ambient-light-sensor-wiring.png"
alt="Connection of the I2C bus" />
<figcaption aria-hidden="true">Connection of the I2C bus</figcaption>
</figure>
<p>The sensor has the following specifications:</p>
<ul>
<li>Supply Voltage: 5V</li>
<li>Operating Current: 1µA</li>
<li>Detection Range: 1 - 65535lx</li>
<li>Accuracy: 1.2lx</li>
<li>Communication Mode: I2C</li>
<li>Operating Temperature: -40 to 85°C/-40 to 185℉</li>
<li>Waterproof Rating: IP68</li>
<li>Thread Length: 10mm</li>
<li>Cutout Size: 26mm</li>
<li>Wrench Size: 31mm</li>
<li>Cable Diameter: 3mm</li>
<li>Wire Length: 1m</li>
</ul>
<p>Sample code:</p>
<pre><code>#include &quot;Wire.h&quot;
#define address 0x23 //I2C address 0x23
void setup()
{
Serial.begin(9600);
Wire.begin();
}
uint8_t buf[4] = {0};
uint16_t data, data1;
float Lux;
void loop()
{
readReg(0x10, buf, 2); //Register address 0x10
data = buf[0] &lt;&lt; 8 | buf[1];
Lux = (((float)data )/1.2);
Serial.print(&quot;LUX:&quot;);
Serial.print(Lux);
Serial.print(&quot;lx&quot;);
Serial.print(&quot;\n&quot;);
delay(500);
}
uint8_t readReg(uint8_t reg, const void* pBuf, size_t size)
{
if (pBuf == NULL) {
Serial.println(&quot;pBuf ERROR!! : null pointer&quot;);
}
uint8_t * _pBuf = (uint8_t *)pBuf;
Wire.beginTransmission(address);
Wire.write(&amp;reg, 1);
if ( Wire.endTransmission() != 0) {
return 0;
}
delay(20);
Wire.requestFrom(address, (uint8_t) size);
for (uint16_t i = 0; i &lt; size; i++) {
_pBuf[i] = Wire.read();
}
return size;
}</code></pre>
<!---
# Bill of materials
@ -914,7 +999,7 @@ sensor is used. The Si7021 is removed from the code.</p>
</ul>
<p>Libraries are included with the source code of this project</p>
<h1 id="license">License</h1>
<p>Copyright (C) 2023, 2024 M.T. Konstapel</p>
<p>Copyright (C) 2023-2025 M.T. Konstapel</p>
<p><a
href="https://meezenest.nl/mees/">https://meezenest.nl/mees/</a></p>
<p>The software is published as open-source software (GPL). The hardware
@ -932,7 +1017,7 @@ option) any later version.</p>
</main>
<footer>
<p>&copy;
2024-05-02
2025-01-14
M.T. Konstapel
<a href="https://meezenest.nl/mees/">https://meezenest.nl/mees/</a>
</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.

@ -2,7 +2,7 @@
title: Weather station
subtitle: with ModBus RTU interface
author: M.T. Konstapel
date: 2024-05-02
date: 2025-01-14
website: https://meezenest.nl/mees/
page_back: https://meezenest.nl/mees/weather_station.html
logo: ./images/mees_logo.svg
@ -394,10 +394,83 @@ As a housing for the prototype, I used an old beehive. These are weatherproof an
# Problems I encounter after four months of use
## Humidity sensor
The Si7021 humidity sensor is not made for outdoor use. The datasheet is clear about that. But a lot of people use this cheap sensor for weather stations anyway. So I choose this sensor for my design. But that was not smart. After an initial time without any problems, the sensor started to saturate. This happened during a very wet and mild winter we had. My first solution was to utilize the build in heater to drive of the moisture. That worked, but only for a short period. The heater blew up and the sensor started to report humidity levels above 100% and because of a bug in the firmware of the sensor, the humidity register wrapped around and the sensor reported humidity levels of 0-30%. I tried to find a software solution, which worked for a while, but the sensor deteriorated even more. To the point of being totally useless. So I searched for another, better sensor. And I found the HYT-221 from Innovative Sensor Technology. This sensor is designed to work outdoors and can even be used in saunas, where the humidity levels are always high and the air is condensating. The datasheet specifically mentions outdoor weather stations as an application. The only downside is its price: it is ten times more expensive than the Si7021.
The sensor can be controlled via the I2C bus, so implementing the new sensor in the firmware was very easy. From version 0.3.0 onward, this sensor is used. The Si7021 is removed from the code.
## Pressure sensor
After a year the BMP280 pressure sensor started to give odd pressure values. The pressure was around 700hPa and followed the temperature curve. The temperature sensor still worked fine. After replacing the BMP280 the barometric values returned to normal. I suspect that it malfunctioned because of the perpetual fog we had for about two months. The sensor was mounted in the Garni RS1 Passive Radiation Shield. Rain could not enter the shield, but fog could.
# Additions
## Luminosity sensor
From version 0.3.1 onward, the weather station has an ambient light sensor. It is an SEN0562 from DFRobot and it outputs the light intensity in Lux. It is a 5 Volt only device, so it should be connected to the 5 Volt I2C bus.
![The SEN0562 ambient light sensor](./images/dfrobot-gravity-ip68-waterproof-ambient-light-sensor-1-65535lx-i2c.png)
![Connection of the I2C bus](./images/dfrobot-gravity-ip68-waterproof-ambient-light-sensor-wiring.png)
The sensor has the following specifications:
- Supply Voltage: 5V
- Operating Current: 1µA
- Detection Range: 1 - 65535lx
- Accuracy: 1.2lx
- Communication Mode: I2C
- Operating Temperature: -40 to 85°C/-40 to 185℉
- Waterproof Rating: IP68
- Thread Length: 10mm
- Cutout Size: 26mm
- Wrench Size: 31mm
- Cable Diameter: 3mm
- Wire Length: 1m
Sample code:
#include "Wire.h"
#define address 0x23 //I2C address 0x23
void setup()
{
Serial.begin(9600);
Wire.begin();
}
uint8_t buf[4] = {0};
uint16_t data, data1;
float Lux;
void loop()
{
readReg(0x10, buf, 2); //Register address 0x10
data = buf[0] << 8 | buf[1];
Lux = (((float)data )/1.2);
Serial.print("LUX:");
Serial.print(Lux);
Serial.print("lx");
Serial.print("\n");
delay(500);
}
uint8_t readReg(uint8_t reg, const void* pBuf, size_t size)
{
if (pBuf == NULL) {
Serial.println("pBuf ERROR!! : null pointer");
}
uint8_t * _pBuf = (uint8_t *)pBuf;
Wire.beginTransmission(address);
Wire.write(&reg, 1);
if ( Wire.endTransmission() != 0) {
return 0;
}
delay(20);
Wire.requestFrom(address, (uint8_t) size);
for (uint16_t i = 0; i < size; i++) {
_pBuf[i] = Wire.read();
}
return size;
}
<!---
# Bill of materials
@ -421,7 +494,7 @@ Libraries are included with the source code of this project
# License
Copyright (C) 2023, 2024 M.T. Konstapel
Copyright (C) 2023-2025 M.T. Konstapel
[https://meezenest.nl/mees/](https://meezenest.nl/mees/)

Binary file not shown.

@ -10,7 +10,7 @@
* BLINK : I2C ERROR
* FLASH : Heartbeat
*
* Copyright (C) 2023, 2024 M.T. Konstapel https://meezenest.nl/mees
* Copyright (C) 2023-2025 M.T. Konstapel https://meezenest.nl/mees
*
* This file is part of weather_station
*
@ -32,12 +32,23 @@
* - Changed some variables to the propper standard (uint8_t, uint16_t, etc.)
* - SparkFun wind interrupt now calculates over 3 seconds in stead of 1 second (KNMI standard)
*
* 2-24-05-02: - Removed cope for si7021
* 2024-05-02: - Removed code for si7021
* - Added code for HYT221 humidity sensor
*
* 2025-01-13: - Cleanup of code
* - Debounce rainfall meter from 100ms to 250mms
* - Debug messages can be switched on and off with the _DEBUG_ variable
* - Main temperature and backup temperature registers switched: the sensor on the HYT221 heats up by
* the circuitry and is therefore about 1.5 degrees above ambient temperature. The BMP280 does not heat up.
* - Added SEN0562 luminosity sensor
*
* See CHANGELOG.md
*
**********************************************************************************/
// When set to 1 debug messages are send to the serial port, set it to 0 for production code!
#define _DEBUG_ 0
#include <ModbusSerial.h>
#include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
@ -48,10 +59,13 @@
//Temperature and humidity sensor
#define HYT_ADDR 0x28 // I2C address of the HYT 221, 271, 371 and most likely the rest of the family
// Light meter
#define SEN0562_ADDR 0x23 //I2C address of Gravity SEN0562 light meter
// Pressure sensor
#include "i2c_BMP280.h"
BMP280 bmp280;
float PRESSURE_OFFSET = 210; // Calibration of BMP280: offset in Pascal
float PRESSURE_OFFSET = -100; // Calibration of BMP280: offset in Pascal
/**************************/
/* Configurable variables */
@ -106,8 +120,7 @@ const int SensorStatusBitsIreg = 14;
* Coils
* 0 = Heater algorithm (0 = disable, 1 = enable)
*/
const int HeaterCoil = 0;
float HUMIDITY_THRESHOLD = 92.0;
const int HeaterCoil = 0; // Legacy register, not used anymore.
// RS-485 serial port
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
@ -143,13 +156,13 @@ struct MeasuredData {
uint16_t RainLast24;
uint16_t SensorRainSinceMidnight;
uint16_t Pressure;
uint16_t Luminosity;
uint16_t StatusBits = 0;
uint16_t RainfallCounter = 0;
float Temperature;
float TemperatureHygrometer;
float Humidity;
float TemperatureBackup;
float TemperatureBarometer;
float Luminosity;
bool HeaterStatus = 0;
} MeasuredData;
@ -188,17 +201,55 @@ void ReadHYT221 (void)
temperature = 165.0 / pow(2,14) * rawTemperature - 40;
// Scale for more decimal positions when converted to integer value for ModBus
MeasuredData.Temperature = 100 * temperature;
MeasuredData.TemperatureHygrometer = 100 * temperature;
//Serial.print(MeasuredData.Humidity);
//Serial.print("% - Temperature: ");
//Serial.println(MeasuredData.Temperature);
if (_DEBUG_) {
Serial.print("HYT221 humidity: ");
Serial.print(MeasuredData.Humidity/100);
Serial.print("% - Temperature: ");
Serial.println(MeasuredData.TemperatureHygrometer/100);
}
}
else {
Serial.println("Not enough bytes available on wire.");
}
}
uint8_t ReadSEN0562_register(uint8_t reg, const void* pBuf)
{
if (pBuf == NULL) {
Serial.println("pBuf ERROR!! : null pointer");
}
uint8_t * _pBuf = (uint8_t *)pBuf;
Wire.beginTransmission(SEN0562_ADDR);
Wire.write(&reg, 1);
if ( Wire.endTransmission() != 0) {
return 0;
}
delay(20);
Wire.requestFrom(SEN0562_ADDR, 2);
for (uint16_t i = 0; i < 2; i++) {
_pBuf[i] = Wire.read();
}
return 2;
}
void ReadSEN0562()
{
uint8_t buf[4] = {0};
uint16_t data, data1;
ReadSEN0562_register(0x10, buf); //Register address 0x10
data = buf[0] << 8 | buf[1];
MeasuredData.Luminosity = (((float)data )/1.2)*100;
if (_DEBUG_) {
Serial.print("SEN0562 light intensity: ");
Serial.print(MeasuredData.Luminosity/100);
Serial.println(" Lux");
}
}
// Read BMP280
void ReadBMP280 (void)
{
@ -209,12 +260,18 @@ void ReadBMP280 (void)
pascal = (pascal - PRESSURE_OFFSET) / 10; // Convert to hPa
MeasuredData.Pressure = pascal;
bmp280.getTemperature(MeasuredData.TemperatureBackup);
bmp280.getTemperature(MeasuredData.TemperatureBarometer);
// Scale for more decimal positions when converted to integer value for ModBus
MeasuredData.TemperatureBackup *= 100;
MeasuredData.TemperatureBarometer *= 100;
bmp280.triggerMeasurement();
if (_DEBUG_) {
Serial.print("BMP280 pressure: ");
Serial.print(MeasuredData.Pressure);
Serial.print("hPa - Temperature: ");
Serial.println(MeasuredData.TemperatureBarometer/100);
}
}
int MaxOfArray (int array[], uint16_t length)
@ -382,19 +439,11 @@ void setup() {
mb.Ireg (SensorRainSinceMidnightIreg, 0);
mb.Ireg (SensorSnowFallIreg, 0);
Serial.println(F("Weather station v0.3.0"));
Serial.println(F("(C)2024 M.T. Konstapel"));
Serial.println(F("Weather station v0.3.1"));
Serial.println(F("(C)2024-2025 M.T. Konstapel"));
Serial.println(F("This project is free and open source"));
Serial.println(F("More details: https://meezenest.nl/mees/"));
// The standard library of the Si7021 sets the heater element to the default 3.1mA, but we want the full power
// We could alter the library, but than we break compatibility. So for this one time we do a raw-write to the
// heater register.
const uint8_t SI7021_I2C_ADDRESS =(0x40);
const uint8_t SI7021_CMD_WRITE_HEATER_CONTROL_REG =(0x51);
const uint8_t SI7021_HEATER_FULL_BLAST =(0x0F); // Set heater to 94mA
i2c.writeByte(SI7021_I2C_ADDRESS, SI7021_CMD_WRITE_HEATER_CONTROL_REG, SI7021_HEATER_FULL_BLAST);
// Initialize BMP280 pressure sensor
Serial.print(F("Pressure sensor BMP280 "));
if (bmp280.initialize())
@ -462,7 +511,7 @@ void setup() {
// The rainfall detector switch can sometimes bounce, causing multiple extra
// triggers. This input is debounced by ignoring extra triggers within a
// time window, which defaults to 100ms
calibrationParams.minMillisPerRainfall = 100;
calibrationParams.minMillisPerRainfall = 250;
// The anemometer contains a switch that opens and closes as it spins. The
// rate at which the switch closes depends on the wind speed. The datasheet
@ -503,6 +552,8 @@ void loop() {
// Read temperature and humidity
ReadHYT221();
ReadSEN0562();
// Read pressure and temperature
ReadBMP280();
@ -515,19 +566,15 @@ void loop() {
mb.Ireg (SensorWindGustIreg, MeasuredData.WindGust);
mb.Ireg (SensorRainIreg, MeasuredData.Rain);
mb.Ireg (SensorRainLast24Ireg, MeasuredData.RainLast24);
mb.Ireg (SensorTemperatureIreg, MeasuredData.Temperature);
mb.Ireg (SensorTemperatureIreg, MeasuredData.TemperatureBarometer);
mb.Ireg (SensorHumidityIreg, MeasuredData.Humidity);
mb.Ireg (SensorPressureIreg, MeasuredData.Pressure);
mb.Ireg (SensorTemperatureBackupIreg, MeasuredData.TemperatureBackup);
mb.Ireg (SensorTemperatureBackupIreg, MeasuredData.TemperatureHygrometer);
mb.Ireg (SensorLuminosityIreg, MeasuredData.Luminosity);
mb.Ireg (SensorRainfallRawIreg, MeasuredData.RainfallCounter);
mb.Ireg (SensorStatusBitsIreg, MeasuredData.StatusBits);
// Debug wind vane
//Serial.print(F("\n Measured ADC: "));
//Serial.print(analogRead(windDirectionPin));
// enable or disable smart heater
// enable or disable smart heater (legacy code, does not do anything except setting the status bit)
if (mb.Coil (HeaterCoil)) {
MeasuredData.StatusBits |= 0x04; // Set bit
} else {

Loading…
Cancel
Save