2 * Acurite weather stations and temperature / humidity sensors
4 * Copyright (c) 2015, Jens Jenson, Helge Weissig, David Ray Thompson, Robert Terzi
7 * - 5-n-1 weather sensor, Model; VN1TXC, 06004RM
8 * - 5-n-1 pro weather sensor, Model: 06014RM
9 * - 896 Rain gauge, Model: 00896
10 * - 592TXR / 06002RM Tower sensor (temperature and humidity)
11 * - 609TXC "TH" temperature and humidity sensor (609A1TX)
12 * - Acurite 986 Refrigerator / Freezer Thermometer
13 * - Acurite 606TX temperature sesor
14 * - Acurite 6045M Lightning Detector (Work in Progress)
20 #include "pulse_demod.h"
23 // ** Acurite 5n1 functions **
25 #define ACURITE_TXR_BITLEN 56
26 #define ACURITE_5N1_BITLEN 64
27 #define ACURITE_6045_BITLEN 72
29 // ** Acurite known message types
30 #define ACURITE_MSGTYPE_WINDSPEED_WINDDIR_RAINFALL 0x31
31 #define ACURITE_MSGTYPE_WINDSPEED_TEMP_HUMIDITY 0x38
33 static char time_str[LOCAL_TIME_BUFLEN];
36 // Acurite 5n1 Wind direction values.
37 // There are seem to be conflicting decodings.
38 // It is possible there there are different versions
39 // of the 5n1 station that report differently.
41 // The original implementation used by the 5n1 device type
42 // here seems to have a straight linear/cicular mapping.
44 // The newer 5n1 mapping seems to just jump around with no clear
45 // meaning, but does map to the values sent by Acurite's
46 // only Acu-Link Internet Bridge and physical console 1512.
47 // This is may be a modified/non-standard Gray Code.
49 // Mapping 5n1 raw RF wind direction values to aculink's values
68 // From draythomp/Desert-home-rtl_433
69 // matches acu-link internet bridge values
70 // The mapping isn't circular, it jumps around.
71 char * acurite_5n1_winddirection_str[] =
90 const float acurite_5n1_winddirections[] =
110 // 5n1 keep state for how much rain has been seen so far
111 static int acurite_5n1raincounter = 0; // for 5n1 decoder
112 static int acurite_5n1t_raincounter = 0; // for combined 5n1/TXR decoder
115 static int acurite_checksum(uint8_t row[BITBUF_COLS], int cols) {
116 // sum of first n-1 bytes modulo 256 should equal nth byte
117 // also disregard a row of all zeros
120 for ( i=0; i < cols; i++)
122 if (sum != 0 && (sum % 256 == row[cols]))
128 // Temperature encoding for 5-n-1 sensor and possibly others
129 static float acurite_getTemp (uint8_t highbyte, uint8_t lowbyte) {
130 // range -40 to 158 F
131 int highbits = (highbyte & 0x0F) << 7 ;
132 int lowbits = lowbyte & 0x7F;
133 int rawtemp = highbits | lowbits;
134 float temp_F = (rawtemp - 400) / 10.0;
138 static float acurite_getWindSpeed_kph (uint8_t highbyte, uint8_t lowbyte) {
139 // range: 0 to 159 kph
140 // raw number is cup rotations per 4 seconds
141 // http://www.wxforum.net/index.php?topic=27244.0 (found from weewx driver)
142 int highbits = ( highbyte & 0x1F) << 3;
143 int lowbits = ( lowbyte & 0x70 ) >> 4;
144 int rawspeed = highbits | lowbits;
147 speed_kph = rawspeed * 0.8278 + 1.0;
152 static int acurite_getHumidity (uint8_t byte) {
153 // range: 1 to 99 %RH
154 int humidity = byte & 0x7F;
158 static int acurite_getRainfallCounter (uint8_t hibyte, uint8_t lobyte) {
159 // range: 0 to 99.99 in, 0.01 in incr., rolling counter?
160 int raincounter = ((hibyte & 0x7f) << 7) | (lobyte & 0x7F);
164 // The high 2 bits of byte zero are the channel (bits 7,6)
168 static char chLetter[4] = {'C','E','B','A'}; // 'E' stands for error
170 static char acurite_getChannel(uint8_t byte){
171 int channel = (byte & 0xC0) >> 6;
172 return chLetter[channel];
175 // 5-n-1 sensor ID is the last 12 bits of byte 0 & 1
177 // CC RR IIII | IIII IIII
179 static uint16_t acurite_5n1_getSensorId(uint8_t hibyte, uint8_t lobyte){
180 return ((hibyte & 0x0f) << 8) | lobyte;
184 // The sensor sends the same data three times, each of these have
185 // an indicator of which one of the three it is. This means the
186 // checksum and first byte will be different for each one.
187 // The bits 5,4 of byte 0 indicate which copy of the 65 bit data string
191 // 1100 xxxx = channel A 1st copy
192 // 1101 xxxx = channel A 2nd copy
193 // 1110 xxxx = channel A 3rd copy
194 static int acurite_5n1_getMessageCaught(uint8_t byte){
195 return (byte & 0x30) >> 4;
199 // So far, all that's known about the battery is that the
200 // third byte, high nibble has two values.xo 0xb0=low and 0x70=OK
201 // so this routine just returns the nibble shifted to make a byte
202 // for more work as time goes by
204 // Battery status appears to be the 7th bit 0x40. 1 = normal, 0 = low
205 // The 8th bit appears to be parity.
206 // @todo - determine if the 5th & 6th bits (0x30) are status bits or
207 // part of the message type. So far these appear to always be 1
208 static int acurite_5n1_getBatteryLevel(uint8_t byte){
209 return (byte & 0x40) >> 6;
213 static int acurite_rain_gauge_callback(bitbuffer_t *bitbuffer) {
214 bitrow_t *bb = bitbuffer->bb;
215 // This needs more validation to positively identify correct sensor type, but it basically works if message is really from acurite raingauge and it doesn't have any errors
216 if ((bb[0][0] != 0) && (bb[0][1] != 0) && (bb[0][2]!=0) && (bb[0][3] == 0) && (bb[0][4] == 0)) {
217 float total_rain = ((bb[0][1]&0xf)<<8)+ bb[0][2];
218 total_rain /= 2; // Sensor reports number of bucket tips. Each bucket tip is .5mm
219 fprintf(stdout, "AcuRite Rain Gauge Total Rain is %2.1fmm\n", total_rain);
220 fprintf(stdout, "Raw Message: %02x %02x %02x %02x %02x\n",bb[0][0],bb[0][1],bb[0][2],bb[0][3],bb[0][4]);
228 // Temperature in Celsius is encoded as a 12 bit integer value
229 // multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2)
230 // negative values are handled by treating it temporarily
231 // as a 16 bit value to put the sign bit in a usable place.
233 static float acurite_th_temperature(uint8_t *s){
234 uint16_t shifted = (((s[1] & 0x0f) << 8) | s[2]) << 4; // Logical left shift
235 return (((int16_t)shifted) >> 4) / 10.0; // Arithmetic right shift
238 // Acurite 609 Temperature and Humidity Sensor
241 // II - ID byte, changes at each power up
242 // S - Status bitmask, normally 0x2,
243 // 0xa - battery low (bit 0x80)
244 // TTT - Temp in Celsius * 10, 12 bit with complement.
248 // @todo - see if the 3rd nybble is battery/status
250 static int acurite_th_callback(bitbuffer_t *bitbuf) {
252 int cksum, battery_low, valid = 0;
254 uint8_t humidity, id, status;
257 local_time_str(0, time_str);
259 for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
260 if (bitbuf->bits_per_row[brow] != 40) {
264 bb = bitbuf->bb[brow];
266 cksum = (bb[0] + bb[1] + bb[2] + bb[3]);
268 if (cksum == 0 || ((cksum & 0xff) != bb[4])) {
272 tempc = acurite_th_temperature(bb);
274 status = (bb[1] & 0xf0) >> 4;
275 battery_low = status & 0x8;
279 "time", "", DATA_STRING, time_str,
280 "model", "", DATA_STRING, "Acurite 609TXC Sensor",
281 "id", "", DATA_INT, id,
282 "battery", "", DATA_STRING, battery_low ? "LOW" : "OK",
283 "status", "", DATA_INT, status,
284 "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc,
285 "humidity", "Humidity", DATA_INT, humidity,
288 data_acquired_handler(data);
298 // Tower sensor ID is the last 14 bits of byte 0 & 1
300 // CCII IIII | IIII IIII
302 static uint16_t acurite_txr_getSensorId(uint8_t hibyte, uint8_t lobyte){
303 return ((hibyte & 0x3f) << 8) | lobyte;
307 // temperature encoding used by "tower" sensors 592txr
308 // 14 bits available after removing both parity bits.
309 // 11 bits needed for specified range -40 C to 70 C (-40 F - 158 F)
310 // range -100 C to 1538.4 C
311 static float acurite_txr_getTemp (uint8_t highbyte, uint8_t lowbyte) {
312 int rawtemp = ((highbyte & 0x7F) << 7) | (lowbyte & 0x7F);
313 float temp = rawtemp / 10.0 - 100;
319 * Acurite 06045 Lightning sensor Temperature encoding
320 * 12 bits of temperature after removing parity and status bits.
321 * Message native format appears to be in 1/10 of a degree Fahrenheit
322 * Device Specification: -40 to 158 F / -40 to 70 C
323 * Available range given encoding with 12 bits -150.0 F to +259.6 F
325 static float acurite_6045_getTemp (uint8_t highbyte, uint8_t lowbyte) {
326 int rawtemp = ((highbyte & 0x1F) << 7) | (lowbyte & 0x7F);
327 float temp = (rawtemp - 1500) / 10.0;
332 * Acurite 06045m Lightning Sensor decoding.
335 * - lightning strike count
336 * - extimated distance to front of storm, up to 25 miles / 40 km
337 * - Temperature -40 to 158 F / -40 to 70 C
338 * - Humidity 1 - 99% RH
340 * Status Information sent per 06047M/01021 display
341 * - (RF) interference (preventing lightning detection)
347 * Somewhat similar to 592TXR and 5-n-1 weather stations
348 * Same pulse characteristics. checksum, and parity checking on data bytes.
351 * CI? II II HH ST TT LL DD? KK
356 * S = Status/Message type/Temperature MSB.
358 * D = Lightning distanace and status bits?
359 * L = Lightning strike count.
362 * Byte 0 - channel number A/B/C
363 * - Channel in 2 most significant bits - A: 0xC, B: 0x8, C: 00
364 * - TBD: lower 6 bits, ID or unused?
366 * Bytes 1 & 2 - ID, all 8 bits, no parity.
368 * Byte 3 - Humidity (7 bits + parity bit)
370 * Byte 4 - Status (2 bits) and Temperature MSB (5 bits)
371 * - Bitmask PSSTTTTT (P = Parity, S = Status, T = Temperature)
372 * - 0x40 - Transmitting every 8 seconds (lightning possibly detected)
373 * normal, off, transmits every 24 seconds
374 * - 0x20 - TBD: normally off, On is possibly low battery?
375 * - 0x1F - Temperature MSB (5 bits)
377 * Byte 5 - Temperature LSB (7 bits, 8th is parity)
379 * Byte 6 - Lightning Strike count (7 bits, 8th is parity)
380 * - Stored in EEPROM or something non-volatile)
383 * Byte 7 - Lightning Distance (5 bits) and status bits (2 bits) (?)
384 * - Bits PSSDDDDD (P = Parity, S = Status, D = Distance
385 * - 5 lower bits is distance in unit? (miles? km?) to edge of storm (theory)
386 * - Bit 0x20: (RF) interference / strong RFI detected (to be verified)
387 * - Bit 0x40: TBD, possible activity?
388 * - distance = 0x1f: possible invalid value indication (value at power up)
389 * - Note: Distance sometimes goes to 0 right after strike counter increment
390 * status bits might indicate validifity of distance.
392 * Byte 8 - checksum. 8 bits, no parity.
394 * @todo - Get lightning/distance to front of storm to match display
395 * @todo - Low battery, figure out encoding
396 * @todo - figure out remaining status bits and how to report
397 * @todo - convert to data make once decoding is stable
399 static int acurite_6045_decode (bitrow_t bb, int browlen) {
402 uint8_t humidity, message_type, l_status;
403 char channel, *wind_dirstr = "";
406 uint8_t strike_count, strike_distance;
408 channel = acurite_getChannel(bb[0]); // same as TXR
409 sensor_id = (bb[1] << 8) | bb[2]; // TBD 16 bits or 20?
410 humidity = acurite_getHumidity(bb[3]); // same as TXR
411 message_type = (bb[4] & 0x60) >> 5; // status bits: 0x2 8 second xmit, 0x1 - TBD batttery?
412 tempf = acurite_6045_getTemp(bb[4], bb[5]);
413 strike_count = bb[6] & 0x7f;
414 strike_distance = bb[7] & 0x1f;
415 l_status = (bb[7] & 0x60) >> 5;
417 printf("%s Acurite lightning 0x%04X Ch %c Msg Type 0x%02x: %.1f F %d %% RH Strikes %d Distance %d L_status 0x%02x -",
418 time_str, sensor_id, channel, message_type, tempf, humidity, strike_count, strike_distance, l_status);
420 // FIXME Temporarily dump raw message data until the
421 // decoding improves. Includes parity indicator(*).
422 for (int i=0; i < browlen; i++) {
424 pc = byteParity(bb[i]) == 0 ? ' ' : '*';
425 fprintf(stdout, " %02x%c", bb[i], pc);
435 * This callback handles several Acurite devices that use a very
436 * similar RF encoding and data format:
438 * - 592TXR temperature and humidity sensor
439 * - 5-n-1 weather station
440 * - 6045M Lightning Detectur with Temperature and Humidity
442 static int acurite_txr_callback(bitbuffer_t *bitbuf) {
443 int browlen, valid = 0;
445 float tempc, tempf, wind_dird, rainfall = 0.0, wind_speed, wind_speedmph;
446 uint8_t humidity, sensor_status, sequence_num, message_type;
447 char channel, *wind_dirstr = "";
450 int raincounter, temp, battery_low;
451 uint8_t strike_count, strike_distance;
455 local_time_str(0, time_str);
457 if (debug_output > 1) {
458 fprintf(stderr,"acurite_txr\n");
459 bitbuffer_print(bitbuf);
462 for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
463 browlen = (bitbuf->bits_per_row[brow] + 7)/8;
464 bb = bitbuf->bb[brow];
466 if (debug_output > 1)
467 fprintf(stderr,"acurite_txr: row %d bits %d, bytes %d \n", brow, bitbuf->bits_per_row[brow], browlen);
469 if ((bitbuf->bits_per_row[brow] < ACURITE_TXR_BITLEN ||
470 bitbuf->bits_per_row[brow] > ACURITE_5N1_BITLEN + 1) &&
471 bitbuf->bits_per_row[brow] != ACURITE_6045_BITLEN) {
472 if (debug_output > 1 && bitbuf->bits_per_row[brow] > 16)
473 fprintf(stderr,"acurite_txr: skipping wrong len\n");
477 // There will be 1 extra false zero bit added by the demod.
478 // this forces an extra zero byte to be added
479 if (bb[browlen - 1] == 0)
482 if (!acurite_checksum(bb,browlen - 1)) {
484 fprintf(stderr, "%s Acurite bad checksum:", time_str);
485 for (uint8_t i = 0; i < browlen; i++)
486 fprintf(stderr," 0x%02x",bb[i]);
487 fprintf(stderr,"\n");
493 fprintf(stderr, "acurite_txr Parity: ");
494 for (uint8_t i = 0; i < browlen; i++) {
495 fprintf(stderr,"%d",byteParity(bb[i]));
497 fprintf(stderr,"\n");
501 // tower sensor messages are 7 bytes.
502 // @todo - see if there is a type in the message that
503 // can be used instead of length to determine type
504 if (browlen == ACURITE_TXR_BITLEN / 8) {
505 channel = acurite_getChannel(bb[0]);
506 sensor_id = acurite_txr_getSensorId(bb[0],bb[1]);
507 sensor_status = bb[2]; // @todo, uses parity? & 0x07f
508 humidity = acurite_getHumidity(bb[3]);
509 tempc = acurite_txr_getTemp(bb[4], bb[5]);
510 sprintf(channel_str, "%c", channel);
511 battery_low = sensor_status >>7;
514 "time", "", DATA_STRING, time_str,
515 "model", "", DATA_STRING, "Acurite tower sensor",
516 "id", "", DATA_INT, sensor_id,
517 "channel", "", DATA_STRING, &channel_str,
518 "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc,
519 "humidity", "Humidity", DATA_INT, humidity,
520 "battery", "Battery", DATA_INT, battery_low,
521 "status", "", DATA_INT, sensor_status,
524 data_acquired_handler(data);
528 // The 5-n-1 weather sensor messages are 8 bytes.
529 if (browlen == ACURITE_5N1_BITLEN / 8) {
531 fprintf(stderr, "Acurite 5n1 raw msg: %02X %02X %02X %02X %02X %02X %02X %02X\n",
532 bb[0], bb[1], bb[2], bb[3], bb[4], bb[5], bb[6], bb[7]);
534 channel = acurite_getChannel(bb[0]);
535 sprintf(channel_str, "%c", channel);
536 sensor_id = acurite_5n1_getSensorId(bb[0],bb[1]);
537 sequence_num = acurite_5n1_getMessageCaught(bb[0]);
538 message_type = bb[2] & 0x3f;
539 battery_low = (bb[2] & 0x40) >> 6;
541 if (message_type == ACURITE_MSGTYPE_WINDSPEED_WINDDIR_RAINFALL) {
542 // Wind speed, wind direction, and rain fall
543 wind_speed = acurite_getWindSpeed_kph(bb[3], bb[4]);
544 wind_speedmph = kmph2mph(wind_speed);
545 wind_dird = acurite_5n1_winddirections[bb[4] & 0x0f];
546 wind_dirstr = acurite_5n1_winddirection_str[bb[4] & 0x0f];
547 raincounter = acurite_getRainfallCounter(bb[5], bb[6]);
548 if (acurite_5n1t_raincounter > 0) {
549 // track rainfall difference after first run
550 // FIXME when converting to structured output, just output
551 // the reading, let consumer track state/wrap around, etc.
552 rainfall = ( raincounter - acurite_5n1t_raincounter ) * 0.01;
553 if (raincounter < acurite_5n1t_raincounter) {
554 fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, rain counter reset or wrapped around (old %d, new %d)\n",
555 time_str, sensor_id, channel, acurite_5n1t_raincounter, raincounter);
556 acurite_5n1t_raincounter = raincounter;
559 // capture starting counter
560 acurite_5n1t_raincounter = raincounter;
561 fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, Total rain fall since last reset: %0.2f\n",
562 time_str, sensor_id, channel, raincounter * 0.01);
566 "time", "", DATA_STRING, time_str,
567 "model", "", DATA_STRING, "Acurite 5n1 sensor",
568 "sensor_id", NULL, DATA_FORMAT, "0x%02X", DATA_INT, sensor_id,
569 "channel", NULL, DATA_STRING, &channel_str,
570 "sequence_num", NULL, DATA_INT, sequence_num,
571 "battery", NULL, DATA_STRING, battery_low ? "OK" : "LOW",
572 "message_type", NULL, DATA_INT, message_type,
573 "wind_speed", NULL, DATA_FORMAT, "%.1f mph", DATA_DOUBLE, wind_speedmph,
574 "wind_dir_deg", NULL, DATA_FORMAT, "%.1f", DATA_DOUBLE, wind_dird,
575 "wind_dir", NULL, DATA_STRING, wind_dirstr,
576 "rainfall_accumulation", NULL, DATA_FORMAT, "%.2f in", DATA_DOUBLE, rainfall,
577 "raincounter_raw", NULL, DATA_INT, raincounter,
580 data_acquired_handler(data);
582 } else if (message_type == ACURITE_MSGTYPE_WINDSPEED_TEMP_HUMIDITY) {
583 // Wind speed, temperature and humidity
584 wind_speed = acurite_getWindSpeed_kph(bb[3], bb[4]);
585 wind_speedmph = kmph2mph(wind_speed);
586 tempf = acurite_getTemp(bb[4], bb[5]);
587 tempc = fahrenheit2celsius(tempf);
588 humidity = acurite_getHumidity(bb[6]);
591 "time", "", DATA_STRING, time_str,
592 "model", "", DATA_STRING, "Acurite 5n1 sensor",
593 "sensor_id", NULL, DATA_FORMAT, "0x%02X", DATA_INT, sensor_id,
594 "channel", NULL, DATA_STRING, &channel_str,
595 "sequence_num", NULL, DATA_INT, sequence_num,
596 "battery", NULL, DATA_STRING, battery_low ? "OK" : "LOW",
597 "message_type", NULL, DATA_INT, message_type,
598 "wind_speed", NULL, DATA_FORMAT, "%.1f mph", DATA_DOUBLE, wind_speedmph,
599 "temperature_F", "temperature", DATA_FORMAT, "%.1f F", DATA_DOUBLE, tempf,
600 "humidity", NULL, DATA_FORMAT, "%d", DATA_INT, humidity,
602 data_acquired_handler(data);
605 fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, Status %02X, Unknown message type 0x%02x\n",
606 time_str, sensor_id, channel, bb[3], message_type);
610 if (browlen == ACURITE_6045_BITLEN / 8) {
611 // @todo check parity and reject if invalid
612 valid += acurite_6045_decode(bb, browlen);
625 * Acurite 00986 Refrigerator / Freezer Thermometer
627 * Includes two sensors and a display, labeled 1 and 2,
628 * by default 1 - Refridgerator, 2 - Freezer
630 * PPM, 5 bytes, sent twice, no gap between repeaters
631 * start/sync pulses two short, with short gaps, followed by
634 * @todo, the 2 short sync pulses get confused as data.
636 * Data Format - 5 bytes, sent LSB first, reversed
640 * T - Temperature in Fahrenehit, integer, MSB = sign.
641 * Encoding is "Sign and magnitude"
642 * I - 16 bit sensor ID
643 * changes at each power up
644 * S - status/sensor type
647 * C = CRC (CRC-8 poly 0x07, little-endian)
650 * - needs new PPM demod that can separate out the short
651 * start/sync pulses which confuse things and cause
652 * one data bit to be lost in the check value.
653 * - low battery detection
657 static int acurite_986_callback(bitbuffer_t *bitbuf) {
659 uint8_t *bb, sensor_num, status, crc, crcc;
661 int8_t tempf; // Raw Temp is 8 bit signed Fahrenheit
663 uint16_t sensor_id, valid_cnt = 0;
666 local_time_str(0, time_str);
668 if (debug_output > 1) {
669 fprintf(stderr,"acurite_986\n");
670 bitbuffer_print(bitbuf);
673 for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
674 browlen = (bitbuf->bits_per_row[brow] + 7)/8;
675 bb = bitbuf->bb[brow];
677 if (debug_output > 1)
678 fprintf(stderr,"acurite_986: row %d bits %d, bytes %d \n", brow, bitbuf->bits_per_row[brow], browlen);
680 if (bitbuf->bits_per_row[brow] < 39 ||
681 bitbuf->bits_per_row[brow] > 43 ) {
682 if (debug_output > 1 && bitbuf->bits_per_row[brow] > 16)
683 fprintf(stderr,"acurite_986: skipping wrong len\n");
687 // Reduce false positives
688 // may eliminate these with a beter PPM (precise?) demod.
689 if ((bb[0] == 0xff && bb[1] == 0xff && bb[2] == 0xff) ||
690 (bb[0] == 0x00 && bb[1] == 0x00 && bb[2] == 0x00)) {
694 // There will be 1 extra false zero bit added by the demod.
695 // this forces an extra zero byte to be added
696 if (browlen > 5 && bb[browlen - 1] == 0)
700 for (uint8_t i = 0; i < browlen; i++)
701 br[i] = reverse8(bb[i]);
703 if (debug_output > 0) {
704 fprintf(stderr,"Acurite 986 reversed: ");
705 for (uint8_t i = 0; i < browlen; i++)
706 fprintf(stderr," %02x",br[i]);
707 fprintf(stderr,"\n");
711 sensor_id = (br[1] << 8) + br[2];
713 sensor_num = (status & 0x01) + 1;
714 status = status >> 1;
715 // By default Sensor 1 is the Freezer, 2 Refrigerator
716 sensor_type = sensor_num == 2 ? 'F' : 'R';
719 if ((crcc = crc8le(br, 5, 0x07, 0)) != 0) {
722 fprintf(stderr,"%s Acurite 986 sensor bad CRC: %02x -",
723 time_str, crc8le(br, 4, 0x07, 0));
724 for (uint8_t i = 0; i < browlen; i++)
725 fprintf(stderr," %02x", br[i]);
726 fprintf(stderr,"\n");
731 if ((status & 1) == 1) {
732 fprintf(stderr, "%s Acurite 986 sensor 0x%04x - %d%c: low battery, status %02x\n",
733 time_str, sensor_id, sensor_num, sensor_type, status);
736 // catch any status bits that haven't been decoded yet
737 if ((status & 0xFE) != 0) {
738 fprintf(stderr, "%s Acurite 986 sensor 0x%04x - %d%c: Unexpected status %02x\n",
739 time_str, sensor_id, sensor_num, sensor_type, status);
743 tempf = (tempf & 0x7f) * -1;
745 tempc = fahrenheit2celsius(tempf);
748 printf("%s Acurite 986 sensor 0x%04x - %d%c: %3.1f C %d F\n",
749 time_str, sensor_id, sensor_num, sensor_type,
762 // Checksum code from
763 // https://eclecticmusingsofachaoticmind.wordpress.com/2015/01/21/home-automation-temperature-sensors/
764 // with modifications listed in
765 // http://www.osengr.org/WxShield/Downloads/Weather-Sensor-RF-Protocols.pdf
767 // This is the same algorithm as used in ambient_weather.c
769 uint8_t Checksum(int length, uint8_t *buff) {
771 uint8_t checksum = 0x00;
775 for (byteCnt = 0; byteCnt < length; byteCnt++) {
777 data = buff[byteCnt];
779 for (bitCnt = 7; bitCnt >= 0; bitCnt--) {
784 mask = (mask >> 1) | (mask << 7);
789 // XOR mask into checksum if data bit is 1
800 static int acurite_606_callback(bitbuffer_t *bitbuf) {
802 bitrow_t *bb = bitbuf->bb;
803 float temperature; // temperature in C
804 int16_t temp; // temperature as read from the data packet
805 int battery; // the battery status: 1 is good, 0 is low
806 int8_t sensor_id; // the sensor ID - basically a random number that gets reset whenever the battery is removed
809 local_time_str(0, time_str);
811 if (debug_output > 1) {
812 fprintf(stderr,"acurite_606\n");
813 bitbuffer_print(bitbuf);
816 // throw out all blank messages
817 if (bb[1][0] == 0 && bb[1][1] == 0 && bb[1][2] == 0 && bb[1][3] == 0)
820 // do some basic checking to make sure we have a valid data record
821 if ((bb[0][0] == 0) && (bb[1][4] == 0)) { //This test may need some more scrutiny...
822 // calculate the checksum and only continue if we have a maching checksum
823 uint8_t chk = Checksum(3, &bb[1][0]);
825 if (chk == bb[1][3]) {
826 // Processing the temperature:
827 // Upper 4 bits are stored in nibble 1, lower 8 bits are stored in nibble 2
828 // upper 4 bits of nibble 1 are reserved for other usages (e.g. battery status)
829 temp = (int16_t)((uint16_t)(bb[1][1] << 12) | (bb[1][2] << 4));
832 temperature = temp / 10.0;
833 sensor_id = bb[1][0];
834 battery = (bb[1][1] & 0x80) >> 7;
836 data = data_make("time", "", DATA_STRING, time_str,
837 "model", "", DATA_STRING, "Acurite 606TX Sensor",
838 "id", "", DATA_INT, sensor_id,
839 "battery", "Battery", DATA_STRING, battery ? "OK" : "LOW",
840 "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature,
842 data_acquired_handler(data);
851 static int acurite_00275rm_callback(bitbuffer_t *bitbuf) {
852 int crc, battery_low, id, model, valid = 0;
855 char *model1 = "00275rm", *model2 = "00276rm";
857 uint8_t probe, humidity, phumidity, water;
858 uint8_t signal[3][11]; // Hold three copies of the signal
861 local_time_str(0, time_str);
863 if (debug_output > 1) {
864 fprintf(stderr,"acurite_00275rm\n");
865 bitbuffer_print(bitbuf);
868 // This sensor repeats signal three times. Store each copy.
869 for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
870 if (bitbuf->bits_per_row[brow] != 88) continue;
871 if (nsignal>=3) continue;
872 memcpy(signal[nsignal], bitbuf->bb[brow], 11);
874 fprintf(stderr,"acurite_00275rm: ");
875 for (int i=0; i<11; i++) fprintf(stderr," %02x",signal[nsignal][i]);
876 fprintf(stderr,"\n");
881 // All three signals were found
883 // Combine signal copies so that majority bit count wins
884 for (int i=0; i<11; i++) {
886 (signal[0][i] & signal[1][i]) |
887 (signal[1][i] & signal[2][i]) |
888 (signal[2][i] & signal[0][i]);
891 if ((crc=crc16(&(signal[0][0]), 11/*len*/, 0xb2/*poly*/, 0xd0/*seed*/)) != 0) {
893 fprintf(stderr,"%s Acurite 00275rm sensor bad CRC: %02x -",
895 for (uint8_t i = 0; i < 11; i++)
896 fprintf(stderr," %02x", signal[0][i]);
897 fprintf(stderr,"\n");
901 // Decode the combined signal
902 id = (signal[0][0]<<16) | (signal[0][1]<<8) | signal[0][3];
903 battery_low = (signal[0][2] & 0x40)==0;
904 model = (signal[0][2] & 1);
905 tempc = 0.1 * ( (signal[0][4]<<4) | (signal[0][5]>>4) ) - 100;
906 probe = signal[0][5] & 3;
907 humidity = ((signal[0][6] & 0x1f) << 2) | (signal[0][7] >> 6);
911 "time", "", DATA_STRING, time_str,
912 "model", "", DATA_STRING, model ? model1 : model2,
913 "probe", "", DATA_INT, probe,
914 "id", "", DATA_INT, id,
915 "battery", "", DATA_STRING, battery_low ? "LOW" : "OK",
916 "temperature_C", "Celcius", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc,
917 "humidity", "Humidity", DATA_INT, humidity,
918 "crc", "", DATA_STRING, "ok",
921 // Water probe (detects water leak)
922 } else if (probe==1) {
923 water = (signal[0][7] & 0x0f) == 15;
925 "time", "", DATA_STRING, time_str,
926 "model", "", DATA_STRING, model ? model1 : model2,
927 "probe", "", DATA_INT, probe,
928 "id", "", DATA_INT, id,
929 "battery", "", DATA_STRING, battery_low ? "LOW" : "OK",
930 "temperature_C", "Celcius", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc,
931 "humidity", "Humidity", DATA_INT, humidity,
932 "water", "", DATA_INT, water,
933 "crc", "", DATA_STRING, "ok",
935 // Soil probe (detects temperature)
936 } else if (probe==2) {
937 ptempc = 0.1 * ( ((0x0f&signal[0][7])<<8) | signal[0][8] ) - 100;
939 "time", "", DATA_STRING, time_str,
940 "model", "", DATA_STRING, model ? model1 : model2,
941 "probe", "", DATA_INT, probe,
942 "id", "", DATA_INT, id,
943 "battery", "", DATA_STRING, battery_low ? "LOW" : "OK",
944 "temperature_C", "Celcius", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc,
945 "humidity", "Humidity", DATA_INT, humidity,
946 "ptemperature_C", "Celcius", DATA_FORMAT, "%.1f C", DATA_DOUBLE, ptempc,
947 "crc", "", DATA_STRING, "ok",
949 // Spot probe (detects temperature and humidity)
950 } else if (probe==3) {
951 ptempc = 0.1 * ( ((0x0f&signal[0][7])<<8) | signal[0][8] ) - 100;
952 phumidity = signal[0][9] & 0x7f;
954 "time", "", DATA_STRING, time_str,
955 "model", "", DATA_STRING, model ? model1 : model2,
956 "probe", "", DATA_INT, probe,
957 "id", "", DATA_INT, id,
958 "battery", "", DATA_STRING, battery_low ? "LOW" : "OK",
959 "temperature_C", "Celcius", DATA_FORMAT, "%.1f C", DATA_DOUBLE, tempc,
960 "humidity", "Humidity", DATA_INT, humidity,
961 "ptemperature_C", "Celcius", DATA_FORMAT, "%.1f C", DATA_DOUBLE, ptempc,
962 "phumidity", "Humidity", DATA_INT, phumidity,
963 "crc", "", DATA_STRING, "ok",
966 data_acquired_handler(data);
975 r_device acurite_rain_gauge = {
976 .name = "Acurite 896 Rain Gauge",
977 .modulation = OOK_PULSE_PPM_RAW,
981 .json_callback = &acurite_rain_gauge_callback,
982 // Disabled by default due to false positives on oregon scientific v1 protocol see issue #353
988 r_device acurite_th = {
989 .name = "Acurite 609TXC Temperature and Humidity Sensor",
990 .modulation = OOK_PULSE_PPM_RAW,
993 .reset_limit = 10000,
994 .json_callback = &acurite_th_callback,
1000 * For Acurite 592 TXR Temp/Mumidity, but
1001 * Should match Acurite 592TX, 5-n-1, etc.
1004 * @todo, convert to use precise demodulator, after adding a flag
1005 * to set "polarity" to flip short bits = 0 vs. 1.
1008 r_device acurite_txr = {
1009 .name = "Acurite 592TXR Temp/Humidity, 5n1 Weather Station, 6045 Lightning",
1010 .modulation = OOK_PULSE_PWM_TERNARY,
1013 .reset_limit = 4000,
1014 .json_callback = &acurite_txr_callback,
1019 // @todo, find a set of values that will work reasonably
1020 // with a range of signal levels
1022 // PWM_Precise_Parameters pwm_precise_param_acurite_txr = {
1023 // .pulse_tolerance = 50,
1024 // .pulse_sync_width = 170,
1027 //r_device acurite_txr = {
1028 // .name = "Acurite 592TXR Temp/Humidity sensor",
1029 // .modulation = OOK_PULSE_PWM_PRECISE,
1030 // .short_limit = 440,
1031 // .long_limit = 260,
1032 // .reset_limit = 4000,
1033 // .json_callback = &acurite_txr_callback,
1035 // .demod_arg = (unsigned long)&pwm_precise_param_acurite_txr,
1040 * Acurite 00986 Refrigerator / Freezer Thermometer
1042 * Temperature only, Pulse Position
1044 * 4 x 400 sample (150 uS) start/sync pulses
1045 * 40 (42) 50 (20 uS) (sample data pulses)
1046 * short gap approx 130 samples
1047 * long gap approx 220 samples
1050 r_device acurite_986 = {
1051 .name = "Acurite 986 Refrigerator / Freezer Thermometer",
1052 .modulation = OOK_PULSE_PPM_RAW,
1053 .short_limit = 720, // Threshold between short and long gap
1055 .reset_limit = 4000,
1056 .json_callback = &acurite_986_callback,
1062 * Acurite 00606TX Tower Sensor
1067 r_device acurite_606 = {
1068 .name = "Acurite 606TX Temperature Sensor",
1069 .modulation = OOK_PULSE_PPM_RAW,
1070 .short_limit = 3500,
1072 .reset_limit = 10000,
1073 .json_callback = &acurite_606_callback,
1078 r_device acurite_00275rm = {
1079 .name = "Acurite 00275rm,00276rm Temp/Humidity with optional probe",
1080 .modulation = OOK_PULSE_PWM_TERNARY,
1081 .short_limit = 320, // = 4* 80, 80 is reported by -G option
1082 .long_limit = 520, // = 4*130, 130 "
1083 // .reset_limit = 608, // = 4*152, 152 "
1084 .reset_limit = 708, // = 4*152, 152 "
1085 .json_callback = &acurite_00275rm_callback,