Обработка сообщений MQTT в формате rtl_433
[weathermon.git] / Weather_WH2 / Weather_WH2.ino
1 /*
2   Updated code for receiving data from WH2 weather station
3   This code implements timeouts to make decoding more robust
4   Decodes received packets and writes a summary of each packet to the Arduino's
5   serial port
6   Created by Luc Small on 19 July 2013.
7   Released into the public domain.
8 */
9
10 #include <dht.h>
11 #include <Wire.h>
12 #include <BMP085.h>
13
14 // DHT11 and BMP085 wired sensors
15 dht DHT;
16 BMP085 bmp;
17
18 // Humidity sensor at pin 4
19 #define DHT11PIN 5
20 int sensorPin = 0;
21 #define DEBUG
22
23 // LED pins
24 #define REDPIN 11
25 #define GREENPIN 12
26
27 // Read data from 433MHz receiver on digital pin 3
28 #define RF_IN 4
29 // For better efficiency, the port is read directly
30 // the following two lines should be changed appropriately
31 // if the line above is changed.
32 #define RF_IN_RAW PIND4
33 #define RF_IN_PIN PIND
34
35 // Port that is hooked to LED to indicate a packet has been received
36
37 #define COUNTER_RATE 3200-1 // 16,000,000Hz / 3200 = 5000 interrupts per second, ie. 200us between interrupts
38 // 1 is indicated by 500uS pulse
39 // wh2_accept from 2 = 400us to 3 = 600us
40 #define IS_HI_PULSE(interval)   (interval >= 2 && interval <= 3)
41 // 0 is indicated by ~1500us pulse
42 // wh2_accept from 7 = 1400us to 8 = 1600us
43 #define IS_LOW_PULSE(interval)  (interval >= 7 && interval <= 8)
44 // worst case packet length
45 // 6 bytes x 8 bits x (1.5 + 1) = 120ms; 120ms = 200us x 600
46 #define HAS_TIMED_OUT(interval) (interval > 600)
47 // we expect 1ms of idle time between pulses
48 // so if our pulse hasn't arrived by 1.2ms, reset the wh2_packet_state machine
49 // 6 x 200us = 1.2ms
50 #define IDLE_HAS_TIMED_OUT(interval) (interval > 6)
51 // our expected pulse should arrive after 1ms
52 // we'll wh2_accept it if it arrives after
53 // 4 x 200us = 800us
54 #define IDLE_PERIOD_DONE(interval) (interval >= 4)
55 // Shorthand for tests
56 //#define RF_HI (digitalRead(RF_IN) == HIGH)
57 //#define RF_LOW (digitalRead(RF_IN) == LOW)
58 #define RF_HI (bit_is_set(RF_IN_PIN, RF_IN_RAW))
59 #define RF_LOW (bit_is_clear(RF_IN_PIN, RF_IN_RAW))
60
61 // wh2_flags 
62 #define GOT_PULSE 0x01
63 #define LOGIC_HI  0x02
64 volatile byte wh2_flags = 0;
65 volatile byte wh2_packet_state = 0;
66 volatile int wh2_timeout = 0;
67 byte wh2_packet[5];
68 byte wh2_calculated_crc;
69
70
71 #ifdef DEBUG
72 byte printed = 0;
73 #endif
74
75 ISR(TIMER1_COMPA_vect)
76 {
77   static byte sampling_state = 0;
78   static byte count;   
79   static boolean was_low = false; 
80     
81   switch(sampling_state) {
82     case 0: // waiting
83       wh2_packet_state = 0;
84       if (RF_HI) {
85         if (was_low) {
86           count = 0;
87           sampling_state = 1;
88           was_low = false;
89         }
90       } else {
91         was_low = true;  
92       }
93       break;
94     case 1: // acquiring first pulse
95       count++;
96       // end of first pulse
97       if (RF_LOW) {
98         if (IS_HI_PULSE(count)) {
99           wh2_flags = GOT_PULSE | LOGIC_HI;
100           sampling_state = 2;
101           count = 0;        
102         } else if (IS_LOW_PULSE(count)) {
103           wh2_flags = GOT_PULSE; // logic low
104           sampling_state = 2;
105           count = 0;      
106         } else {
107           sampling_state = 0;
108         }    
109       }   
110       break;
111     case 2: // observe 1ms of idle time
112       count++;
113       if (RF_HI) {
114          if (IDLE_HAS_TIMED_OUT(count)) {
115            sampling_state = 0;
116          } else if (IDLE_PERIOD_DONE(count)) {
117            sampling_state = 1;
118            count = 0;
119          }
120       }     
121       break;     
122   }
123   
124   if (wh2_timeout > 0) {
125     wh2_timeout++; 
126     if (HAS_TIMED_OUT(wh2_timeout)) {
127       wh2_packet_state = 0;
128       wh2_timeout = 0;
129 #ifdef DEBUG
130       if (printed) {
131         Serial1.println();
132         printed=0;
133       }
134 #endif
135     }
136   }
137 }
138
139 void setup() {
140
141   Serial1.begin(115200); // Set the baud.
142  
143    // Wait for U-boot to finish startup.  Consume all bytes until we are done.
144   do {
145      while (Serial1.available() > 0) {
146         Serial1.read();
147      }
148    
149     delay(1000);
150   } while (Serial1.available()>0);  
151
152   Serial1.begin(57600);
153   Serial1.println();
154   Serial1.println("STATUS:STARTING");
155
156   bmp.begin();
157   
158   pinMode(REDPIN,OUTPUT);
159   pinMode(GREENPIN,OUTPUT);  
160
161   pinMode(RF_IN, INPUT);
162   digitalWrite(RF_IN,HIGH);
163   
164   TCCR1A = 0x00;
165   TCCR1B = 0x09;
166   TCCR1C = 0x00;
167   OCR1A = COUNTER_RATE; 
168   TIMSK1 = 0x02;
169   
170   // enable interrupts
171   sei();
172 }
173
174 unsigned long previousMillis = 0;
175 unsigned long indoor_interval = 60000;
176 unsigned long outdoor_interval = 45000;
177 unsigned long previousIndoor = 0;
178 unsigned long previousOutdoor = 0;
179
180 float temperature;
181 float pressure;
182
183 void loop() {
184   unsigned long now;
185   int gas = 0;
186   byte i;
187
188   now = millis();
189
190   if (wh2_flags) {
191     if (wh2_accept()) {
192       // calculate the CRC
193       wh2_calculate_crc();
194       
195       if (wh2_valid()) {
196         
197         Serial1.println();
198         Serial1.print("SENSOR:TYPE=OUTDOOR,");
199         
200         Serial1.print("ID=");
201         Serial1.print(wh2_sensor_id(), HEX);
202       
203         Serial1.print(",HUMIDITY=");
204         Serial1.print(wh2_humidity(), DEC);
205         
206         Serial1.print(",TEMPERATURE=");
207         Serial1.println(format_temp(wh2_temperature()));
208         
209         previousOutdoor = now;
210         digitalWrite(REDPIN,HIGH);
211         
212       } else {
213       
214         Serial1.println();
215         Serial1.println("ERROR:OUTDOOR");
216         previousOutdoor = now;
217         digitalWrite(REDPIN,LOW);
218         
219       }  
220
221    }
222    wh2_flags = 0x00; 
223   }
224
225   if ((unsigned long)(now - previousMillis) >= indoor_interval) {
226
227     previousMillis = now;
228  
229     int chk = DHT.read11(DHT11PIN);
230
231     if (chk==0) {
232      
233       Serial1.println();
234       Serial1.print("SENSOR:TYPE=INDOOR,");
235       Serial1.print("HUMIDITY=");
236       Serial1.print(DHT.humidity);
237       Serial1.print(",TEMPERATURE=");
238       Serial1.print(DHT.temperature);
239
240       previousIndoor = now;
241       digitalWrite(GREENPIN,HIGH);
242
243
244     } else {
245         
246       Serial1.println();
247       Serial1.println("ERROR:INDOOR");
248       previousIndoor = now;
249       digitalWrite(GREENPIN,LOW);
250         
251     } 
252
253     pressure=bmp.readPressure();
254     temperature=bmp.readTemperature();
255     Serial1.println();
256     Serial1.print("SENSOR:TYPE=BARO,");
257     Serial1.print("PRESSURE=");
258     Serial1.print(pressure);
259     Serial1.print(",TEMPERATURE=");
260     Serial1.println(temperature);
261
262     gas = analogRead(sensorPin); // Получаем значения из датчика
263     Serial1.print("SENSOR:TYPE=GAS,VALUE=");
264     Serial1.println(gas); // Пишем в серийный порт
265
266   }
267   
268   if ((unsigned long)(now - previousIndoor) > indoor_interval*10) {
269
270       Serial1.println();
271       Serial1.println("ERROR:INDOOR TIMEOUT");
272       previousIndoor = now;
273       digitalWrite(GREENPIN,LOW);
274     
275   }
276
277   if ((unsigned long)(now - previousOutdoor) > outdoor_interval*10) {
278
279       Serial1.println();
280       Serial1.println("ERROR:OUTDOOR TIMEOUT");
281       previousOutdoor = now;
282       digitalWrite(REDPIN,LOW);
283     
284   }
285
286
287 }
288
289
290 // processes new pulse
291 boolean wh2_accept()
292 {
293   static byte packet_no, bit_no, history;
294
295   // reset if in initial wh2_packet_state
296   if(wh2_packet_state == 0) {
297      // should history be 0, does it matter?
298     history = 0xFF;
299     wh2_packet_state = 1;
300     // enable wh2_timeout
301     wh2_timeout = 1;
302   } // fall thru to wh2_packet_state one
303  
304   // acquire preamble
305   if (wh2_packet_state == 1) {
306      // shift history right and store new value
307      history <<= 1;
308      // store a 1 if required (right shift along will store a 0)
309      if (wh2_flags & LOGIC_HI) {
310        history |= 0x01;
311      }
312      // check if we have a valid start of frame
313      // xxxxx110
314      if ((history & B00000111) == B00000110) {
315        // need to clear packet, and counters
316        packet_no = 0;
317        // start at 1 becuase only need to acquire 7 bits for first packet byte.
318        bit_no = 1;
319        wh2_packet[0] = wh2_packet[1] = wh2_packet[2] = wh2_packet[3] = wh2_packet[4] = 0;
320        // we've acquired the preamble
321        wh2_packet_state = 2;
322     }
323     return false;
324   }
325   // acquire packet
326   if (wh2_packet_state == 2) {
327
328     wh2_packet[packet_no] <<= 1;
329     if (wh2_flags & LOGIC_HI) {
330       wh2_packet[packet_no] |= 0x01;
331 #ifdef DEBUG
332       Serial1.print('1');
333       printed=1;
334     } else {
335       Serial1.print('0');
336       printed=1; 
337 #endif
338     }
339
340     bit_no ++;
341     if(bit_no > 7) {
342       bit_no = 0;
343       packet_no ++;
344     }
345
346     if (packet_no > 4) {
347       // start the sampling process from scratch
348       wh2_packet_state = 0;
349       // clear wh2_timeout
350       wh2_timeout = 0;
351       return true;
352     }
353   }
354   return false;
355 }
356
357
358 void wh2_calculate_crc()
359 {
360   wh2_calculated_crc = crc8(wh2_packet, 4);
361 }
362
363 bool wh2_valid()
364 {
365   return (wh2_calculated_crc == wh2_packet[4]);
366 }
367
368 int wh2_sensor_id()
369 {
370   return (wh2_packet[0] << 4) + (wh2_packet[1] >> 4);
371 }
372
373 byte wh2_humidity()
374 {
375   return wh2_packet[3];
376 }
377
378 /* Temperature in deci-degrees. e.g. 251 = 25.1 */
379 int wh2_temperature()
380 {
381   int temperature;
382   temperature = ((wh2_packet[1] & B00000111) << 8) + wh2_packet[2];
383   // make negative
384   if (wh2_packet[1] & B00001000) {
385     temperature = -temperature;
386   }
387   return temperature;
388 }
389
390 String format_temp(int temperature)
391 {
392   byte whole, partial;
393   String s;
394   s = String();
395   if (temperature<0) {
396     temperature = -temperature;
397     s += String('-');
398   }
399        
400   whole = temperature / 10;
401   partial = temperature - (whole*10);
402
403   s += String(whole, DEC);
404   s += '.';
405   s += String(partial, DEC);
406
407   return s;
408
409 }
410
411 uint8_t crc8( uint8_t *addr, uint8_t len)
412 {
413   uint8_t crc = 0;
414
415   // Indicated changes are from reference CRC-8 function in OneWire library
416   while (len--) {
417     uint8_t inbyte = *addr++;
418     for (uint8_t i = 8; i; i--) {
419       uint8_t mix = (crc ^ inbyte) & 0x80; // changed from & 0x01
420       crc <<= 1; // changed from right shift
421       if (mix) crc ^= 0x31;// changed from 0x8C;
422       inbyte <<= 1; // changed from right shift
423     }
424   }
425   return crc;
426 }
427
428
429
430
431
432
433
434
435