Page 1 of 1

Z-UNO based weather Station (German description)

Posted: 24 Apr 2018 09:43
by mdietinger@gmail.com
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.

Re: Z-UNO based weather Station (German description)

Posted: 26 Apr 2018 23:04
by PoltoS
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?

Re: Z-UNO based weather Station (German description)

Posted: 27 Apr 2018 08:45
by mdietinger@gmail.com
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.

Re: Z-UNO based weather Station (German description)

Posted: 27 Apr 2018 13:13
by petergebruers
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.

Re: Z-UNO based weather Station (German description)

Posted: 30 Apr 2018 10:34
by mdietinger@gmail.com
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;

Re: Z-UNO based weather Station (German description)

Posted: 30 Apr 2018 18:54
by petergebruers
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:

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);
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!

Re: Z-UNO based weather Station (German description)

Posted: 01 May 2018 12:50
by mdietinger@gmail.com
Peter,

thanks, going to give it a try.

Re: Z-UNO based weather Station (German description)

Posted: 01 May 2018 13:26
by petergebruers
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).

Re: Z-UNO based weather Station (German description)

Posted: 02 May 2018 06:56
by mdietinger@gmail.com
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.