Acurite 5in1 minor fixes
[rtl-433.git] / src / devices / oregon_scientific.c
1 #include "rtl_433.h"
2
3 float get_os_temperature(unsigned char *message, unsigned int sensor_id) {
4   // sensor ID included  to support sensors with temp in different position
5   float temp_c = 0;
6   temp_c = (((message[5]>>4)*100)+((message[4]&0x0f)*10) + ((message[4]>>4)&0x0f)) /10.0F;
7   if (message[5] & 0x0f)
8        temp_c = -temp_c;
9   return temp_c;
10 }
11 unsigned int get_os_humidity(unsigned char *message, unsigned int sensor_id) {
12  // sensor ID included to support sensors with temp in different position
13  int humidity = 0;
14     humidity = ((message[6]&0x0f)*10)+(message[6]>>4);
15  return humidity;
16 }
17
18 static int validate_os_checksum(unsigned char *msg, int checksum_nibble_idx) {
19   // Oregon Scientific v2.1 and v3 checksum is a  1 byte  'sum of nibbles' checksum.
20   // with the 2 nibbles of the checksum byte  swapped.
21   int i;
22   unsigned int checksum, sum_of_nibbles=0;
23   for (i=0; i<(checksum_nibble_idx-1);i+=2) {
24     unsigned char val=msg[i>>1];
25         sum_of_nibbles += ((val>>4) + (val &0x0f));
26   }
27   if (checksum_nibble_idx & 1) {
28      sum_of_nibbles += (msg[checksum_nibble_idx>>1]>>4);
29      checksum = (msg[checksum_nibble_idx>>1] & 0x0f) | (msg[(checksum_nibble_idx+1)>>1]&0xf0);
30   } else
31      checksum = (msg[checksum_nibble_idx>>1]>>4) | ((msg[checksum_nibble_idx>>1]&0x0f)<<4);
32   sum_of_nibbles &= 0xff;
33
34   if (sum_of_nibbles == checksum)
35     return 0;
36   else {
37     fprintf(stderr, "Checksum error in Oregon Scientific message.  Expected: %02x  Calculated: %02x\n", checksum, sum_of_nibbles);
38         fprintf(stderr, "Message: "); int i; for (i=0 ;i<((checksum_nibble_idx+4)>>1) ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr, "\n\n");
39         return 1;
40   }
41 }
42
43 static int validate_os_v2_message(unsigned char * msg, int bits_expected, int valid_v2_bits_received,
44                                 int nibbles_in_checksum) {
45   // Oregon scientific v2.1 protocol sends each bit using the complement of the bit, then the bit  for better error checking.  Compare number of valid bits processed vs number expected
46   if (bits_expected == valid_v2_bits_received) {
47     return (validate_os_checksum(msg, nibbles_in_checksum));
48   } else {
49     fprintf(stderr, "Bit validation error on Oregon Scientific message.  Expected %d bits, received error after bit %d \n",        bits_expected, valid_v2_bits_received);
50     fprintf(stderr, "Message: "); int i; for (i=0 ;i<(bits_expected+7)/8 ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr, "\n\n");
51   }
52   return 1;
53 }
54
55 static int oregon_scientific_v2_1_parser(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
56    // Check 2nd and 3rd bytes of stream for possible Oregon Scientific v2.1 sensor data (skip first byte to get past sync/startup bit errors)
57    if ( ((bb[0][1] == 0x55) && (bb[0][2] == 0x55)) ||
58             ((bb[0][1] == 0xAA) && (bb[0][2] == 0xAA))) {
59           int i,j;
60           unsigned char msg[BITBUF_COLS] = {0};
61
62           // Possible  v2.1 Protocol message
63           int num_valid_v2_bits = 0;
64
65           unsigned int sync_test_val = (bb[0][3]<<24) | (bb[0][4]<<16) | (bb[0][5]<<8) | (bb[0][6]);
66           int dest_bit = 0;
67           int pattern_index;
68           // Could be extra/dropped bits in stream.  Look for sync byte at expected position +/- some bits in either direction
69       for(pattern_index=0; pattern_index<8; pattern_index++) {
70         unsigned int mask     = (unsigned int) (0xffff0000>>pattern_index);
71                 unsigned int pattern  = (unsigned int)(0x55990000>>pattern_index);
72         unsigned int pattern2 = (unsigned int)(0xaa990000>>pattern_index);
73
74         //fprintf(stderr, "OS v2.1 sync byte search - test_val=%08x pattern=%08x  mask=%08x\n", sync_test_val, pattern, mask);
75
76             if (((sync_test_val & mask) == pattern) ||
77                     ((sync_test_val & mask) == pattern2)) {
78                   //  Found sync byte - start working on decoding the stream data.
79                   // pattern_index indicates  where sync nibble starts, so now we can find the start of the payload
80               int start_byte = 5 + (pattern_index>>3);
81               int start_bit = pattern_index & 0x07;
82         //fprintf(stderr, "OS v2.1 Sync test val %08x found, starting decode at byte index %d bit %d\n", sync_test_val, start_byte, start_bit);
83               int bits_processed = 0;
84                   unsigned char last_bit_val = 0;
85                   j=start_bit;
86               for (i=start_byte;i<BITBUF_COLS;i++) {
87                 while (j<8) {
88                            if (bits_processed & 0x01) {
89                              unsigned char bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j));
90
91                                  // check if last bit received was the complement of the current bit
92                                  if ((num_valid_v2_bits == 0) && (last_bit_val == bit_val))
93                                    num_valid_v2_bits = bits_processed; // record position of first bit in stream that doesn't verify correctly
94                                  last_bit_val = bit_val;
95
96                              // copy every other bit from source stream to dest packet
97                                  msg[dest_bit>>3] |= (((bb[0][i] & (0x80 >> j)) >> (7-j)) << (7-(dest_bit & 0x07)));
98
99         //fprintf(stderr,"i=%d j=%d dest_bit=%02x bb=%02x msg=%02x\n",i, j, dest_bit, bb[0][i], msg[dest_bit>>3]);
100                                  if ((dest_bit & 0x07) == 0x07) {
101                                     // after assembling each dest byte, flip bits in each nibble to convert from lsb to msb bit ordering
102                                     int k = (dest_bit>>3);
103                     unsigned char indata = msg[k];
104                         // flip the 4 bits in the upper and lower nibbles
105                         msg[k] = ((indata & 0x11) << 3) | ((indata & 0x22) << 1) |
106                                      ((indata & 0x44) >> 1) | ((indata & 0x88) >> 3);
107                             }
108                                  dest_bit++;
109                              }
110                                  else
111                                    last_bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j)); // used for v2.1 bit error detection
112                            bits_processed++;
113                            j++;
114                 }
115                     j=0;
116                   }
117                   break;
118             } //if (sync_test_val...
119       } // for (pattern...
120
121
122     int sensor_id = (msg[0] << 8) | msg[1];
123         if ((sensor_id == 0x1d20) || (sensor_id == 0x1d30))     {
124            if (validate_os_v2_message(msg, 153, num_valid_v2_bits, 15) == 0) {
125          int  channel = ((msg[2] >> 4)&0x0f);
126              if (channel == 4)
127                channel = 3; // sensor 3 channel number is 0x04
128          float temp_c = get_os_temperature(msg, sensor_id);
129                  if (sensor_id == 0x1d20) fprintf(stdout, "SENSOR:TYPE=OREGON_THGR122N,ID=%d,", channel);
130                  else fprintf(stdout, "SENSOR:TYPE=OREGON_THGR968,");
131                  fprintf(stdout, "TEMPERATURE=%3.1f,HUMIDITY=%d%\n", temp_c,get_os_humidity(msg, sensor_id));
132            }
133            return 1;
134     } else if (sensor_id == 0x5d60) {
135            if (validate_os_v2_message(msg, 185, num_valid_v2_bits, 19) == 0) {
136              unsigned int comfort = msg[7] >>4;
137              char *comfort_str="Normal";
138              if      (comfort == 4)   comfort_str = "Comfortable";
139              else if (comfort == 8)   comfort_str = "Dry";
140              else if (comfort == 0xc) comfort_str = "Humid";
141              unsigned int forecast = msg[9]>>4;
142              char *forecast_str="Cloudy";
143              if      (forecast == 3)   forecast_str = "Rainy";
144              else if (forecast == 6)   forecast_str = "Partly Cloudy";
145              else if (forecast == 0xc) forecast_str = "Sunny";
146          float temp_c = get_os_temperature(msg, 0x5d60);
147              fprintf(stdout,"SENSOR:TYPE=OREGON_BHTR968,TEMPERATURE=%3.1f,HUMIDITY=%d,", temp_c, get_os_humidity(msg, 0x5d60));
148              fprintf(stdout, "COMFORT=%s,PRESSURE=%d,FORECAST=%s\n", comfort_str, ((msg[7] & 0x0f) | (msg[8] & 0xf0))+856, forecast_str);
149            }
150            return 1;
151         } else if (sensor_id == 0x2d10) {
152            if (validate_os_v2_message(msg, 161, num_valid_v2_bits, 16) == 0) {
153            float rain_rate = (((msg[4] &0x0f)*100)+((msg[4]>>4)*10) + ((msg[5]>>4)&0x0f)) /10.0F;
154            float total_rain = (((msg[7]&0xf)*10000)+((msg[7]>>4)*1000) + ((msg[6]&0xf)*100)+((msg[6]>>4)*10) + (msg[5]&0xf))/10.0F;
155            fprintf(stdout, "SENSOR:TYPE=OREGON_RGR968,RAINRATE=%2.0f,RAINTOTAL=%3.0f\n", rain_rate, total_rain);
156            }
157            return 1;
158         } else if (sensor_id == 0xec40 && num_valid_v2_bits==153) {
159                 if (  validate_os_v2_message(msg, 153, num_valid_v2_bits, 12) == 0) {
160                         int  channel = ((msg[2] >> 4)&0x0f);
161                         if (channel == 4)
162                                 channel = 3; // sensor 3 channel number is 0x04
163                         float temp_c = get_os_temperature(msg, sensor_id);
164                         if (sensor_id == 0xec40) fprintf(stdout, "SENSOR:TYPE=OREGON_THR228N,ID=%d,", channel);
165                         fprintf(stdout, "TEMPERATURE=%3.1f\n", temp_c);
166                 }
167                 return 1;
168         } else if (sensor_id == 0xec40 && num_valid_v2_bits==129) {
169                 if (  validate_os_v2_message(msg, 129, num_valid_v2_bits, 12) == 0) {
170                         int  channel = ((msg[2] >> 4)&0x0f);
171                         if (channel == 4)
172                                 channel = 3; // sensor 3 channel number is 0x04
173                         int battery_low = (msg[3] >> 2 & 0x01);
174                         unsigned char rolling_code = ((msg[2] << 4)&0xF0) | ((msg[3] >> 4)&0x0F);
175                         float temp_c = get_os_temperature(msg, sensor_id);
176                         if (sensor_id == 0xec40) fprintf(stdout, "SENSOR:TYPE=OREGON_THN132N,ID=%d,BATTERY=%s,CODE=%0X,", channel, battery_low?"LOW":"OK", rolling_code);
177                         fprintf(stdout, "TEMPERATURE=%3.1f\n", temp_c);
178                 }
179                 return 1;
180         } else if (num_valid_v2_bits > 16) {
181 fprintf(stderr, "%d bit message received from unrecognized Oregon Scientific v2.1 sensor with device ID %x.\n", num_valid_v2_bits, sensor_id);
182 fprintf(stderr, "Message: "); for (i=0 ; i<20 ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr,"\n\n");
183     } else {
184 //fprintf(stderr, "\nPossible Oregon Scientific v2.1 message, but sync nibble wasn't found\n"); fprintf(stderr, "Raw Data: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");
185     }
186    } else {
187 //if (bb[0][3] != 0) int i; fprintf(stderr, "\nBadly formatted OS v2.1 message encountered."); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");}
188    }
189    return 0;
190 }
191
192 static int oregon_scientific_v3_parser(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
193
194    // Check stream for possible Oregon Scientific v3 protocol data (skip part of first and last bytes to get past sync/startup bit errors)
195    if ((((bb[0][0]&0xf) == 0x0f) && (bb[0][1] == 0xff) && ((bb[0][2]&0xc0) == 0xc0)) ||
196        (((bb[0][0]&0xf) == 0x00) && (bb[0][1] == 0x00) && ((bb[0][2]&0xc0) == 0x00))) {
197           int i,j;
198           unsigned char msg[BITBUF_COLS] = {0};
199           unsigned int sync_test_val = (bb[0][2]<<24) | (bb[0][3]<<16) | (bb[0][4]<<8);
200           int dest_bit = 0;
201           int pattern_index;
202           // Could be extra/dropped bits in stream.  Look for sync byte at expected position +/- some bits in either direction
203       for(pattern_index=0; pattern_index<16; pattern_index++) {
204         unsigned int     mask = (unsigned int)(0xfff00000>>pattern_index);
205         unsigned int  pattern = (unsigned int)(0xffa00000>>pattern_index);
206         unsigned int pattern2 = (unsigned int)(0xff500000>>pattern_index);
207                 unsigned int pattern3 = (unsigned int)(0x00500000>>pattern_index);
208 //fprintf(stderr, "OS v3 Sync nibble search - test_val=%08x pattern=%08x  mask=%08x\n", sync_test_val, pattern, mask);
209             if (((sync_test_val & mask) == pattern)  ||
210             ((sync_test_val & mask) == pattern2) ||
211             ((sync_test_val & mask) == pattern3)) {
212                   //  Found sync byte - start working on decoding the stream data.
213                   // pattern_index indicates  where sync nibble starts, so now we can find the start of the payload
214               int start_byte = 3 + (pattern_index>>3);
215               int start_bit = (pattern_index+4) & 0x07;
216 //fprintf(stderr, "Oregon Scientific v3 Sync test val %08x ok, starting decode at byte index %d bit %d\n", sync_test_val, start_byte, start_bit);
217           j = start_bit;
218               for (i=start_byte;i<BITBUF_COLS;i++) {
219                 while (j<8) {
220                            unsigned char bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j));
221
222                            // copy every  bit from source stream to dest packet
223                            msg[dest_bit>>3] |= (((bb[0][i] & (0x80 >> j)) >> (7-j)) << (7-(dest_bit & 0x07)));
224
225 //fprintf(stderr,"i=%d j=%d dest_bit=%02x bb=%02x msg=%02x\n",i, j, dest_bit, bb[0][i], msg[dest_bit>>3]);
226                            if ((dest_bit & 0x07) == 0x07) {
227                                   // after assembling each dest byte, flip bits in each nibble to convert from lsb to msb bit ordering
228                                   int k = (dest_bit>>3);
229                   unsigned char indata = msg[k];
230                       // flip the 4 bits in the upper and lower nibbles
231                       msg[k] = ((indata & 0x11) << 3) | ((indata & 0x22) << 1) |
232                                    ((indata & 0x44) >> 1) | ((indata & 0x88) >> 3);
233                          }
234                            dest_bit++;
235                            j++;
236                         }
237                         j=0;
238                }
239                   break;
240                   }
241             }
242
243         if ((msg[0] == 0xf8) && (msg[1] == 0x24))       {
244            if (validate_os_checksum(msg, 15) == 0) {
245              int  channel = ((msg[2] >> 4)&0x0f);
246              float temp_c = get_os_temperature(msg, 0xf824);
247                  int humidity = get_os_humidity(msg, 0xf824);
248                  fprintf(stdout,"SENSOR:TYPE=OREGON_THGR810,ID=%d,TEMPERATURE=%3.1f,HUMIDITY=%d\n", channel, temp_c, humidity);
249            }
250            return 1;
251         } else if ((msg[0] == 0x19) && (msg[1] == 0x84)) {
252            if (validate_os_checksum(msg, 17) == 0) {
253              float gustWindspeed = (msg[11]+msg[10])/100;
254              float quadrant = msg[8]*22.5;
255            fprintf(stdout, "SENSOR:TYPE=OREGON_WGR800,WINDSPEED=%2.0f,WINDDIRECTION=%3.0f\n", gustWindspeed, quadrant);
256            }
257            return 1;
258     } else if ((msg[0] != 0) && (msg[1]!= 0)) { //  sync nibble was found  and some data is present...
259 fprintf(stderr, "Message received from unrecognized Oregon Scientific v3 sensor.\n");
260 fprintf(stderr, "Message: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr, "\n");
261 fprintf(stderr, "    Raw: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");
262     } else if (bb[0][3] != 0) {
263 //fprintf(stderr, "\nPossible Oregon Scientific v3 message, but sync nibble wasn't found\n"); fprintf(stderr, "Raw Data: "); for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n");
264     }
265    }
266    else { // Based on first couple of bytes, either corrupt message or something other than an Oregon Scientific v3 message
267 //if (bb[0][3] != 0) { fprintf(stderr, "\nUnrecognized Msg in v3: "); int i; for (i=0 ; i<BITBUF_COLS ; i++) fprintf(stderr, "%02x ", bb[0][i]); fprintf(stderr,"\n\n"); }
268    }
269    return 0;
270 }
271
272 static int oregon_scientific_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
273  int ret = oregon_scientific_v2_1_parser(bb, bits_per_row);
274  if (ret == 0)
275    ret = oregon_scientific_v3_parser(bb, bits_per_row);
276  return ret;
277 }
278
279 r_device oregon_scientific = {
280     /* .id             = */ 18,
281     /* .name           = */ "Oregon Scientific Weather Sensor",
282     /* .modulation     = */ OOK_MANCHESTER,
283     /* .short_limit    = */ 125,
284     /* .long_limit     = */ 0, // not used
285     /* .reset_limit    = */ 600,
286     /* .json_callback  = */ &oregon_scientific_callback,
287 };