3 float get_os_temperature(unsigned char *message, unsigned int sensor_id) {
4 // sensor ID included to support sensors with temp in different position
6 temp_c = (((message[5]>>4)*100)+((message[4]&0x0f)*10) + ((message[4]>>4)&0x0f)) /10.0F;
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
14 humidity = ((message[6]&0x0f)*10)+(message[6]>>4);
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.
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));
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);
31 checksum = (msg[checksum_nibble_idx>>1]>>4) | ((msg[checksum_nibble_idx>>1]&0x0f)<<4);
32 sum_of_nibbles &= 0xff;
34 if (sum_of_nibbles == checksum)
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");
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));
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");
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))) {
60 unsigned char msg[BITBUF_COLS] = {0};
62 // Possible v2.1 Protocol message
63 int num_valid_v2_bits = 0;
65 unsigned int sync_test_val = (bb[0][3]<<24) | (bb[0][4]<<16) | (bb[0][5]<<8) | (bb[0][6]);
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);
74 //fprintf(stderr, "OS v2.1 sync byte search - test_val=%08x pattern=%08x mask=%08x\n", sync_test_val, pattern, mask);
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;
86 for (i=start_byte;i<BITBUF_COLS;i++) {
88 if (bits_processed & 0x01) {
89 unsigned char bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j));
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;
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)));
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);
111 last_bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j)); // used for v2.1 bit error detection
118 } //if (sync_test_val...
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);
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(stderr, "Weather Sensor THGR122N Channel %d ", channel);
130 else fprintf(stderr, "Weather Sensor THGR968 Outdoor ");
131 fprintf(stderr, "Temp: %3.1f°C %3.1f°F Humidity: %d%%\n", temp_c, ((temp_c*9)/5)+32,get_os_humidity(msg, sensor_id));
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(stderr,"Weather Sensor BHTR968 Indoor Temp: %3.1f°C %3.1f°F Humidity: %d%%", temp_c, ((temp_c*9)/5)+32, get_os_humidity(msg, 0x5d60));
148 fprintf(stderr, " (%s) Pressure: %dmbar (%s)\n", comfort_str, ((msg[7] & 0x0f) | (msg[8] & 0xf0))+856, forecast_str);
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(stderr, "Weather Sensor RGR968 Rain Gauge Rain Rate: %2.0fmm/hr Total Rain %3.0fmm\n", rain_rate, total_rain);
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);
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(stderr, "Thermo Sensor THR228N Channel %d ", channel);
165 fprintf(stderr, "Temp: %3.1f°C %3.1f°F\n", temp_c, ((temp_c*9)/5)+32);
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);
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(stderr, "Thermo Sensor THN132N, Channel %d, Battery: %s, Rolling-code 0x%0X, ", channel, battery_low?"Low":"Ok", rolling_code);
177 fprintf(stderr, "Temp: %3.1f°C %3.1f°F\n", temp_c, ((temp_c*9)/5)+32);
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");
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");
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");}
192 static int oregon_scientific_v3_parser(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
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))) {
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);
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);
218 for (i=start_byte;i<BITBUF_COLS;i++) {
220 unsigned char bit_val = ((bb[0][i] & (0x80 >> j)) >> (7-j));
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)));
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);
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(stderr,"Weather Sensor THGR810 Channel %d Temp: %3.1f°C %3.1f°F Humidity: %d%%\n", channel, temp_c, ((temp_c*9)/5)+32, humidity);
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(stderr, "Weather Sensor WGR800 Wind Gauge Gust Wind Speed : %2.0f m/s Wind direction %3.0f dgrs\n", gustWindspeed, quadrant);
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");
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"); }
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);
275 ret = oregon_scientific_v3_parser(bb, bits_per_row);
279 r_device oregon_scientific = {
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,