Over the last couple of months i build a weather station using Z-UNO.
There are two articles describing the solution and integration into Fibaro HC2(German)
https://www.siio.de/z-wave-wetterstatio ... uno-hw-sw/
https://www.siio.de/z-wave-wetterstatio ... fibaro-vd/
The solution is working great.
I have no plans to translate the entire article, but are happy to answer questions and provide basic support.
Z-UNO based weather Station (German description)
-
- Posts: 39
- Joined: 12 Aug 2016 12:08
Re: Z-UNO based weather Station (German description)
Great work! Don't stop
Is it ok if we translate it and publish on our web site with an active link to your blog?
Is it ok if we translate it and publish on our web site with an active link to your blog?
-
- Posts: 39
- Joined: 12 Aug 2016 12:08
Re: Z-UNO based weather Station (German description)
Polto, sure it is fine if post it on your web site.
Actually that is not my blog, just wrote a guest report.
The Z-UNO code is poorly commented at the moment, I can add some comments to explain a bit better the logic used.
If you need any support let me know.
One challeng I have is the BH1750 only giving me a max of 32k Lux.
If light level goes above 32k, then the measurements don't get transmitted.
In any case I ordered a MAX 44007, which should work up to 104k Lux and will test when received.
Actually that is not my blog, just wrote a guest report.
The Z-UNO code is poorly commented at the moment, I can add some comments to explain a bit better the logic used.
If you need any support let me know.
One challeng I have is the BH1750 only giving me a max of 32k Lux.
If light level goes above 32k, then the measurements don't get transmitted.
In any case I ordered a MAX 44007, which should work up to 104k Lux and will test when received.
-
- Posts: 255
- Joined: 26 Jul 2015 17:29
Re: Z-UNO based weather Station (German description)
Regarding BH1750: it supports 0.5, 1 and 4 Lux resolution... With 4 Lux resolution you should be able to get higher values than 32k, but then it is not very sensitive in dark light. So Ideally, you'd have to measure at 4 Lux resolution, then if it is below 16.000 switch to 1 Lux resolution. I am not at home, I can't test it. Maybe next week.
Meet me here: https://forum.fibaro.com/index.php?/pro ... rgebruers/ or here: memberlist.php?mode=viewprofile&u=564463
-
- Posts: 39
- Joined: 12 Aug 2016 12:08
Re: Z-UNO based weather Station (German description)
better comented arduino code:
Code: Select all
//PIN18-rain (3V3-2K2-PIN18-100nF-GND)
//PIN4 ADC1
//PIN9-SCL (2K2 pullup to 3V3)
//PIN10-SDA (2K2 pullup to 3V3)
//GND-Ground
//3V3-3Volts</pre>
//WIND RJ11:
//*
//2-3V3(Yellow)
//3-PIN4-Winddirection(Green)
//4-GND(Red)
//5-PIN17-Windspeed(Black)
//*
//RAIN RJ11:
//*
//*
//3-PIN18-Rainsensor
//4-GND
//*
//*
//I2C Temperature & Light RJ11:
//*
//2-3V3
//3-PIN9-SCL
//4-GND
//5-PIN10-SDA
//*
#include <Wire.h>
#include <math.h>
#include "EEPROM.h"
#define EEPROM_ADDR 0x800 // EEPROM address
#define EEPROM_UPDATE_INTERVAL 120000 // Delayed EEPROM writing to minimize writting attempts
#define HYT_ADDR 0x28 // I2C address of the HYT 939
#define BH1750_ADDR 0x23 // I2C address of the BH1750
#define BH1750_MODE 0x20 // Mode of the BH1750
#define wind2_factor 0.50292 // m/s per pulse in 2 seconds
#define rain_factor 0.2 // mm per pulse
ZUNO_SETUP_ISR_INT0(int0_handler); // Davis Windsensor
ZUNO_SETUP_ISR_INT1(int1_handler); // Davis Regensensor
ZUNO_SETUP_ISR_GPTIMER(gpt_handler); // 2" Timer (2" & 1 Minute measurements)
struct meter_data {
dword rain_total;
byte crc8;
};
meter_data my_meter_data;
struct sensor_data {
int temperature = 0;
unsigned int humidity = 0;
unsigned int wind_speed = 0;
unsigned int wind_gust = 0;
unsigned int wind_direction = 0;
unsigned int rain_rate = 0;
dword light = 0;
};
sensor_data my_sensor;
long last_write_EEPROM_millis = 0;
unsigned int wind_pulses = 0;
unsigned int wind_pulses60 = 0;
unsigned int wind_boe60 = 0;
unsigned int rain_pulses = 0;
unsigned int rain_pulses300 = 0;
byte data_updated = FALSE;
byte GPT_2sec = 0;
byte count_2sec = 0;
byte minute_Count = 0;
byte hour_Count = 0;
byte RR[12];
ZUNO_SETUP_CHANNELS( // set up channels
ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_TEMPERATURE,
SENSOR_MULTILEVEL_SCALE_CELSIUS,
SENSOR_MULTILEVEL_SIZE_TWO_BYTES,
SENSOR_MULTILEVEL_PRECISION_TWO_DECIMALS,
getterTemperature), // Temperature Sensor
ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_RELATIVE_HUMIDITY,
SENSOR_MULTILEVEL_SCALE_PERCENTAGE_VALUE,
SENSOR_MULTILEVEL_SIZE_TWO_BYTES,
SENSOR_MULTILEVEL_PRECISION_TWO_DECIMALS,
getterHumidity) , // Humidity sensor
ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_VELOCITY,
SENSOR_MULTILEVEL_SCALE_METERS_PER_SECOND,
SENSOR_MULTILEVEL_SIZE_TWO_BYTES,
SENSOR_MULTILEVEL_PRECISION_TWO_DECIMALS,
getterVelocity), // Wind sensor avg speed
ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_VELOCITY,
SENSOR_MULTILEVEL_SCALE_METERS_PER_SECOND,
SENSOR_MULTILEVEL_SIZE_TWO_BYTES,
SENSOR_MULTILEVEL_PRECISION_TWO_DECIMALS,
getterVelocityBoe), // Wind sensor gusts
ZUNO_SENSOR_MULTILEVEL_ANGLE_POSITION (getterDirection), // Wind direction
ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_RAIN_RATE,
SENSOR_MULTILEVEL_SCALE_MILLIMETERS_PER_HOUR,
SENSOR_MULTILEVEL_SIZE_TWO_BYTES,
SENSOR_MULTILEVEL_PRECISION_ONE_DECIMAL,
getterRainRate), // Rain rate
ZUNO_METER(ZUNO_METER_TYPE_WATER,
METER_RESET_ENABLE,
ZUNO_METER_WATER_SCALE_PULSECOUNT,
METER_SIZE_FOUR_BYTES,
METER_PRECISION_ONE_DECIMAL,
getterRain, // Rain total
resetterRain),
ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_LUMINANCE,
SENSOR_MULTILEVEL_SCALE_LUX,
SENSOR_MULTILEVEL_SIZE_FOUR_BYTES,
SENSOR_MULTILEVEL_PRECISION_ZERO_DECIMALS,
GetterLightLux), // Light sensor
ZUNO_SENSOR_MULTILEVEL_ANGLE_POSITION (getterCount) // Keep alive counter
);
void setup() {
zunoExtIntMode(ZUNO_EXT_INT0, RISING); // setup wind sensor
zunoExtIntMode(ZUNO_EXT_INT1, RISING); // setup rain counter
zunoGPTInit(ZUNO_GPT_SCALE1024|ZUNO_GPT_CYCLIC); // setip timer
zunoGPTSet(62500); // 2 seconds Timer
zunoGPTEnable(1);
EEPROM.get(EEPROM_ADDR, &my_meter_data, sizeof(meter_data)); // reset raincounter if CRC fail
if (my_crc8((byte*)&my_meter_data, sizeof(meter_data) - 1) != my_meter_data.crc8) {
my_meter_data.rain_total = 0;
update_meter_data();
}
}
void int0_handler() // PIN17-wind sensor interupt
{
wind_pulses++;
}
void int1_handler() // PIN18-rain sensor interupt
{
rain_pulses++;
}
void gpt_handler() // 2" Timer
{
GPT_2sec++;
}
void loop() {
if (count_2sec != GPT_2sec) { // only process routine if 2" counter changed
count_2sec = GPT_2sec;
if (wind_pulses > wind_boe60) { // check for last 2" wind speed
my_sensor.wind_direction = map(analogRead(A1), 0, 1024, 0, 100); // PIN4- wind direction 0-100 in 3.6° steps 100=N
wind_boe60 = wind_pulses ; // update wind gust if wind was higher then last value
}
wind_pulses60 = (wind_pulses60 + wind_pulses); // add up wind speed by last 2" pulses
wind_pulses = 0;
if (rain_pulses > 0) { // check if rain counter increased
my_meter_data.rain_total = my_meter_data.rain_total + 1; // Debounce, 2 or more pulses not possible in 2 ", increase total rain counter
rain_pulses300 = rain_pulses300 +1; // increase rain rate counter
rain_pulses = 0;
data_updated = true;
}
if (count_2sec > 29) { // 1 minute counter check
minute_Count++;
if (minute_Count > 4) { // reset keep alive counter
minute_Count = 0;
hour_Count++; // cyclic 60 minutes counter for rain rate
if (hour_Count > 11) { hour_Count = 0; }
RR[hour_Count] = rain_pulses300; // add rain counter last minute to 60 minutes rain rate counter
rain_pulses300 = 0;
for (byte i = 0; i < 12; i++) {
rain_pulses300 = rain_pulses300 +RR[i] ;
}
my_sensor.rain_rate = rain_pulses300 ;
rain_pulses300 = 0;
}
my_sensor.wind_speed = wind_pulses60 * wind2_factor / 30 * 100; //1 minute average wind speed measurement in m/s
my_sensor.wind_gust = wind_boe60 * wind2_factor * 100; //1 minute max. 2" gust in m/s
wind_boe60 = 0;
wind_pulses60 = 0;
GPT_2sec = 0;
readSensor();
}
}
if (data_updated && (millis() - last_write_EEPROM_millis) > EEPROM_UPDATE_INTERVAL) { // To save EEPROM from a lot of r/w operation write it once in EEPROM_UPDATE_INTERVAL if data was updated
update_meter_data();
data_updated = false;
last_write_EEPROM_millis = millis();
}
delay(100);
}
void readSensor () { //read light sensor
dword lightLux;
Wire.begin();
Wire.beginTransmission(BH1750_ADDR);
Wire.write(BH1750_MODE);
Wire.endTransmission();
delay(500);
Wire.requestFrom(BH1750_ADDR, 2);
if(Wire.available() == 2) {
((byte *)&lightLux)[3] = 0;
((byte *)&lightLux)[2] = 0;
((byte *)&lightLux)[1] = Wire.read();
((byte *)&lightLux)[0] = Wire.read();
my_sensor.light = lightLux / 1.2; // light value
}
delay(500); // read temperature and humidity sensor
int iData[4];
Wire.beginTransmission(HYT_ADDR);
Wire.write(0x80);
Wire.endTransmission();
delay(500);
Wire.requestFrom(HYT_ADDR, 4);
if(Wire.available() == 4) {
iData[1] = Wire.read();
iData[2] = Wire.read();
iData[3] = Wire.read();
iData[4] = Wire.read();
int rawHumidity = iData[1] << 8 | iData[2];
rawHumidity = (rawHumidity &= 0x3FFF);
my_sensor.humidity = 100.0 / pow(2,14) * rawHumidity*100; // humidity value
iData[4] = (iData[4] >> 2);
int rawTemperature = iData[3] << 6 | iData[4];
my_sensor.temperature = (165.0 / pow(2,14) * rawTemperature - 40)*100; // temperature value
}
zunoSendReport(1); // submit all values one a minute
zunoSendReport(2);
zunoSendReport(3);
zunoSendReport(4);
zunoSendReport(5);
zunoSendReport(6);
zunoSendReport(7);
zunoSendReport(8);
zunoSendReport(9);
}
byte my_crc8(byte * data, byte count) {
byte result = 0xDF;
while(count--) {
result ^= *data;
data++;
}
return result;
}
void update_meter_data() {
my_meter_data.crc8 = my_crc8((byte*)&my_meter_data, sizeof(meter_data) - 1);
EEPROM.put(EEPROM_ADDR, &my_meter_data, sizeof(meter_data));
}
word getterTemperature() {
return my_sensor.temperature;
}
word getterHumidity() {
return my_sensor.humidity;
}
word getterVelocity() {
return my_sensor.wind_speed;
}
word getterVelocityBoe() {
return my_sensor.wind_gust;
}
word getterDirection() {
return my_sensor.wind_direction;
}
word getterRainRate() {
return my_sensor.rain_rate * rain_factor * 10;
}
word getterCount() {
return minute_Count;
}
void resetterRain(byte v) {
my_meter_data.rain_total = 0;
update_meter_data();
}
dword getterRain(void) {
return my_meter_data.rain_total * rain_factor * 10;
}
dword GetterLightLux() {
return my_sensor.light;
-
- Posts: 255
- Joined: 26 Jul 2015 17:29
Re: Z-UNO based weather Station (German description)
Thanks for the update...
I have not tried this yet, but I have found how to get 0.11 Lux in the lowest range and 100.000 Lux high range.
The trick is, to modify the "measurement time registers" of the BH1750
https://gist.github.com/Koepel/b70f81c7 ... b9fe56d50e
Since you mentioned the high range, I'll copy paste what he says about that range:
I think he is wrong about the BH1750_factor but that is not too important, I guess you'll use the sensor onder some sort of semi-transparent window, so you'll have to calibtrate that factor anyway.
Hope this helps!
I have not tried this yet, but I have found how to get 0.11 Lux in the lowest range and 100.000 Lux high range.
The trick is, to modify the "measurement time registers" of the BH1750
https://gist.github.com/Koepel/b70f81c7 ... b9fe56d50e
Since you mentioned the high range, I'll copy paste what he says about that range:
Code: Select all
// High resultion with minimum measurement time
// ----------------------------------------
// Extra range for bright light up to 100000 lux
// The calculation could result into a lux value up to 120 klx (120000 lux).
// Normal High Resolution mode, with minimal time measurement (31 is minimum for MTregister).
write8( BH1750_POWER_ON);
write8( mt_H( 31)); // lowest value of measurement time
write8( mt_L( 31));
write8( BH1750_ONE_TIME_H_RES_MODE);
delay( 81); // 180 * 31 / 69
uint16_t c = read16();
float lux_light = (float) c / BH1750_factor * ( 69.0 / 31.0 ); // BH1750_factor is usually 1.2
Serial.print(F( "100k range: "));
Serial.println( lux_light);
Hope this helps!
Meet me here: https://forum.fibaro.com/index.php?/pro ... rgebruers/ or here: memberlist.php?mode=viewprofile&u=564463
-
- Posts: 39
- Joined: 12 Aug 2016 12:08
Re: Z-UNO based weather Station (German description)
Peter,
thanks, going to give it a try.
thanks, going to give it a try.
-
- Posts: 255
- Joined: 26 Jul 2015 17:29
Re: Z-UNO based weather Station (German description)
Good luck. I did a quick test, and I have found 2 issues, not related to that solution. The first is you have to use "long", not DWORD for the getter. Otherwise Z-Uno reports only lower 16 bits. The second, I was unable to solve: I have reasons to believe HC2 does not display values above 32767... I used Zniffer to verify this and I have sent a PM to the developers. Your Z-Uno debug printing should be fine (values with .11 resolution, and values above 32k).
Meet me here: https://forum.fibaro.com/index.php?/pro ... rgebruers/ or here: memberlist.php?mode=viewprofile&u=564463
-
- Posts: 39
- Joined: 12 Aug 2016 12:08
Re: Z-UNO based weather Station (German description)
Thanks for your testing.
HC2 indeed seems to have issues with values >32k.
Initially I used long, 32k was limitation as well, but from time to time a neighbouring channel showed spikes of 32k. Could be when value got over 64k. For the time being I will devide the result by 4 and apply correction on HC2 virtual device. Will impact sensitivity, but I anyway translate LUX to solar radiation and there the lower values are of less interest.
HC2 indeed seems to have issues with values >32k.
Initially I used long, 32k was limitation as well, but from time to time a neighbouring channel showed spikes of 32k. Could be when value got over 64k. For the time being I will devide the result by 4 and apply correction on HC2 virtual device. Will impact sensitivity, but I anyway translate LUX to solar radiation and there the lower values are of less interest.