Added heather algorithm
This commit is contained in:
@@ -67,9 +67,14 @@ const byte SlaveId = 14;
|
||||
* 30004: Temperature (degrees Celcius)
|
||||
* 30005: Rain last hour (l/m2)
|
||||
* 30006: Rain last 24 hours (l/m2)
|
||||
* 30007: Rain since midnight (l/m2)
|
||||
* 30007: Rain since midnight (l/m2) [NOT IMPLEMENTED, always 0]
|
||||
* 30008: Humidity (percent)
|
||||
* 30009: Barometric pressure (hPa)
|
||||
* 30010: Luminosity (W/m2)
|
||||
* 30011: Snow fall [NOT IMPLEMENTED, always 0]
|
||||
* 30012: Raw rainfall counter (mm)
|
||||
* 30013: Temperature pressure sensor (degrees Celsius)
|
||||
* 30014: Status bits 0=heater, 1-15: reserved
|
||||
*
|
||||
*/
|
||||
const int SensorIDIreg = 0;
|
||||
@@ -82,6 +87,18 @@ const int SensorRainLast24Ireg = 6;
|
||||
const int SensorRainSinceMidnightIreg = 7;
|
||||
const int SensorHumidityIreg = 8;
|
||||
const int SensorPressureIreg = 9;
|
||||
const int SensorLuminosityIreg = 10;
|
||||
const int SensorSnowFallIreg = 11;
|
||||
const int SensorRainfallRawIreg = 12;
|
||||
const int SensorTemperatureBackupIreg = 13;
|
||||
const int SensorStatusBitsIreg = 14;
|
||||
|
||||
/* Modbus Registers Offsets (0-9999)
|
||||
* Coils
|
||||
* 0 = Heater algorithm (0 = disable, 1 = enable)
|
||||
*/
|
||||
const int HeaterCoil = 0;
|
||||
|
||||
// RS-485 serial port
|
||||
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
|
||||
const unsigned long Baudrate = 9600;
|
||||
@@ -116,68 +133,153 @@ struct MeasuredData {
|
||||
int RainLast24;
|
||||
int SensorRainSinceMidnight;
|
||||
int Pressure;
|
||||
int Luminosity;
|
||||
int StatusBits = 0;
|
||||
unsigned int RainfallCounter = 0;
|
||||
|
||||
float Temperature;
|
||||
float Humidity;
|
||||
float TemperatureBackup;
|
||||
|
||||
bool HeaterStatus = 0;
|
||||
} MeasuredData;
|
||||
|
||||
// State machine implementing smart heater to prevent saturation of the sensor
|
||||
char HeaterSi7021 (float humidity)
|
||||
{
|
||||
static int state=0;
|
||||
static unsigned long StatemachineTimer=0;
|
||||
bool TempValid=1;
|
||||
bool Heater=0;
|
||||
|
||||
// If Smart heater algorithm is disabled, reset the statemachine forever.
|
||||
// TempValid bit is also forced to 1, but it could be that we just came out of a heater period.
|
||||
// We assume that the client on the other side of the ModBus is smart enough to understand.
|
||||
if (MeasuredData.StatusBits & 0x04)
|
||||
state = 0;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
// Default state: humidity is below 95%
|
||||
case 0:
|
||||
Heater = 0;
|
||||
if (humidity >= 95) {
|
||||
StatemachineTimer = millis();
|
||||
state = 1;
|
||||
}
|
||||
break;
|
||||
// Humidity went above 95%. See if humidity stays above 95% for more than an hour, if so turn on heater
|
||||
case 1:
|
||||
if (humidity >= 95) {
|
||||
if ( (millis() - StatemachineTimer) >= 3.6e+6 ) {
|
||||
Heater = 1;
|
||||
TempValid = 0;
|
||||
StatemachineTimer = millis();
|
||||
state = 2;
|
||||
} else {
|
||||
Heater = 0;
|
||||
}
|
||||
} else {
|
||||
Heater = 0;
|
||||
state = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
// Heater is now on, let the sensor cook for 10 minutes
|
||||
case 2:
|
||||
if ( (millis() - StatemachineTimer) >= 600000 ) {
|
||||
StatemachineTimer = millis();
|
||||
Heater = 0;
|
||||
state = 3;
|
||||
} else {
|
||||
Heater = 1;
|
||||
}
|
||||
TempValid = 0;
|
||||
break;
|
||||
// Heater is now off, let the sensor cool for 10 minutes
|
||||
case 3:
|
||||
if ( (millis() - StatemachineTimer) >= 600000 ) {
|
||||
TempValid = 1; // Sensor cooled, so we can take a valid temperature reading
|
||||
if (humidity >= 95) {
|
||||
// Humidity still above 95%, repeat heating/cooling
|
||||
Heater = 1;
|
||||
StatemachineTimer = millis();
|
||||
state = 2;
|
||||
} else {
|
||||
// Humidity below 95%, reset statemachine
|
||||
Heater = 0;
|
||||
state = 0;
|
||||
}
|
||||
} else {
|
||||
Heater = 0;
|
||||
TempValid = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Heater = 0;
|
||||
state = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return TempValid<<1 | Heater;
|
||||
|
||||
}
|
||||
// Read Si7021 sensor and process data
|
||||
void ReadSi7021 (void)
|
||||
{
|
||||
char result=0x2;
|
||||
|
||||
si7021.triggerMeasurement();
|
||||
si7021.getHumidity(MeasuredData.Humidity);
|
||||
si7021.getTemperature(MeasuredData.Temperature);
|
||||
|
||||
if (MeasuredData.Humidity>100 || MeasuredData.Humidity<0)
|
||||
MeasuredData.Humidity = 100;
|
||||
|
||||
//If humidity is larger than 96% switch on heater to get more acurate measurement and prevent memory offset
|
||||
//Switch off when lower than 94% (hysteresis)
|
||||
if (MeasuredData.Humidity > 96 && !MeasuredData.HeaterStatus) {
|
||||
Serial.print(F("Heater on."));
|
||||
MeasuredData.HeaterStatus = 1;
|
||||
si7021.setHeater(MeasuredData.HeaterStatus);
|
||||
}
|
||||
if (MeasuredData.Humidity < 94 && MeasuredData.HeaterStatus) {
|
||||
Serial.print(F("Heater off."));
|
||||
MeasuredData.HeaterStatus = 0;
|
||||
si7021.setHeater(MeasuredData.HeaterStatus);
|
||||
}
|
||||
|
||||
|
||||
//If humidity is larger than 95% switch on heater to get more acurate measurement and prevent memory offset
|
||||
result = HeaterSi7021(MeasuredData.Humidity);
|
||||
MeasuredData.StatusBits &= 0xFFFC; // Reset heater status bits to zero
|
||||
MeasuredData.StatusBits |= result; // And set the proper bits to one if there are any. The result is we copied the status bits to the register
|
||||
|
||||
// Scale for more decimal positions when converted to integer value for ModBus
|
||||
MeasuredData.Humidity *= 100;
|
||||
MeasuredData.Temperature *= 100;
|
||||
|
||||
// Temperture readings are valid (as the sensor is not heated)
|
||||
if (result & 0x2) {
|
||||
si7021.getTemperature(MeasuredData.Temperature);
|
||||
// Scale for more decimal positions when converted to integer value for ModBus
|
||||
MeasuredData.Temperature *= 100;
|
||||
//Serial.print(F("Valid temp"));
|
||||
}
|
||||
// Statemachine thinks it is time to switch on the heater
|
||||
if (result & 0x1) {
|
||||
//Serial.print(F("Heater on."));
|
||||
MeasuredData.HeaterStatus = 1;
|
||||
|
||||
} else {
|
||||
//Serial.print(F("Heater off."));
|
||||
MeasuredData.HeaterStatus = 0;
|
||||
}
|
||||
si7021.setHeater(MeasuredData.HeaterStatus);
|
||||
|
||||
}
|
||||
|
||||
// Read BMP280
|
||||
void ReadBMP280 (void)
|
||||
{
|
||||
// MeasuredData.Pressure=0;
|
||||
|
||||
bmp280.awaitMeasurement();
|
||||
|
||||
float temperature;
|
||||
bmp280.getTemperature(temperature);
|
||||
|
||||
float pascal;
|
||||
bmp280.getPressure(pascal);
|
||||
|
||||
pascal = (pascal - PRESSURE_OFFSET) / 10; // Convert to hPa
|
||||
|
||||
MeasuredData.Pressure = pascal;
|
||||
|
||||
bmp280.getTemperature(MeasuredData.TemperatureBackup);
|
||||
// Scale for more decimal positions when converted to integer value for ModBus
|
||||
MeasuredData.TemperatureBackup *= 100;
|
||||
|
||||
bmp280.triggerMeasurement();
|
||||
|
||||
// When humidity is high, the heater of the Si7021 is on. This causes the temperature sensor of the humidity sensor to heat up.
|
||||
// Use temperature sensor of BMP280 instead.
|
||||
if (MeasuredData.HeaterStatus) {
|
||||
bmp280.getTemperature(MeasuredData.Temperature);
|
||||
// Scale for more decimal positions when converted to integer value for ModBus
|
||||
MeasuredData.Temperature *= 100;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int MaxOfArray (int array[], unsigned int length)
|
||||
@@ -214,7 +316,8 @@ void ReadSparkfunWeatherStation (void)
|
||||
|
||||
float tmpRegister;
|
||||
|
||||
MeasuredData.WindDirection = weatherMeterKit.getWindDirection();
|
||||
tmpRegister = 10*weatherMeterKit.getWindDirection(); // Use float for conversion to degrees times 10, than put it in integer register for ModBus
|
||||
MeasuredData.WindDirection = tmpRegister;
|
||||
tmpRegister = 100*(weatherMeterKit.getWindSpeed())/3.6; // Use float for conversion to m/s times 100, than put it in integer register for ModBus
|
||||
MeasuredData.WindSpeed = tmpRegister;
|
||||
tmpRegister = 100*weatherMeterKit.getTotalRainfall(); // Use float for conversion to l/m2 times 100, than put it in integer register for ModBus
|
||||
@@ -279,6 +382,9 @@ void ReadSparkfunWeatherStation (void)
|
||||
RainPerHourCounter=0;
|
||||
}
|
||||
RainPerHour[RainPerHourCounter] = MeasuredData.Rain;
|
||||
// Every time before we reset the TotalRainCounter we add the amount to the RawRainCounter.
|
||||
// This 16 bit register will eventually overflow, but 655.35mm of rain fall is a lot!
|
||||
MeasuredData.RainfallCounter += MeasuredData.Rain; // We don't care about the rounding error due to the convertion from float to int
|
||||
weatherMeterKit.resetTotalRainfall();
|
||||
|
||||
// Calculate rain fall in the last 24 hours
|
||||
@@ -320,13 +426,22 @@ void setup() {
|
||||
mb.addIreg (SensorRainSinceMidnightIreg);
|
||||
mb.addIreg (SensorHumidityIreg);
|
||||
mb.addIreg (SensorPressureIreg);
|
||||
mb.addIreg (SensorLuminosityIreg);
|
||||
mb.addIreg (SensorSnowFallIreg);
|
||||
mb.addIreg (SensorRainfallRawIreg);
|
||||
mb.addIreg (SensorTemperatureBackupIreg);
|
||||
mb.addIreg (SensorStatusBitsIreg);
|
||||
|
||||
// Add HeaterCoil register
|
||||
mb.addCoil (HeaterCoil);
|
||||
|
||||
// Set Weather station ID
|
||||
mb.Ireg (SensorIDIreg, 0x5758);
|
||||
// Set unused register to zero
|
||||
mb.Ireg (SensorRainSinceMidnightIreg, 0);
|
||||
mb.Ireg (SensorSnowFallIreg, 0);
|
||||
|
||||
Serial.println(F("Weather station v0.1.0"));
|
||||
Serial.println(F("Weather station v0.2.1"));
|
||||
Serial.println(F("(C)2024 M.T. Konstapel"));
|
||||
Serial.println(F("This project is free and open source"));
|
||||
Serial.println(F("More details: https://meezenest.nl/mees/"));
|
||||
@@ -346,6 +461,14 @@ void setup() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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())
|
||||
@@ -467,11 +590,22 @@ void loop() {
|
||||
mb.Ireg (SensorTemperatureIreg, MeasuredData.Temperature);
|
||||
mb.Ireg (SensorHumidityIreg, MeasuredData.Humidity);
|
||||
mb.Ireg (SensorPressureIreg, MeasuredData.Pressure);
|
||||
mb.Ireg (SensorTemperatureBackupIreg, MeasuredData.TemperatureBackup);
|
||||
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
|
||||
if (mb.Coil (HeaterCoil)) {
|
||||
MeasuredData.StatusBits |= 0x04; // Set bit
|
||||
} else {
|
||||
MeasuredData.StatusBits &= 0x0B; // Reset bit
|
||||
}
|
||||
|
||||
digitalWrite(LED_BUILTIN, LOW); // LED as heartbeat
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user