Редизайн на основе текущей ветки мейнстрима + новые устройства.
[rtl-433.git] / src / devices / acurite.c
old mode 100644 (file)
new mode 100755 (executable)
index c5b50ae..f2bdc62
+/*
+ * Acurite weather stations and temperature / humidity sensors
+ *
+ * Copyright (c) 2015, Jens Jenson, Helge Weissig, David Ray Thompson, Robert Terzi
+ *
+ * Devices decoded:
+ * - 5-n-1 weather sensor, Model; VN1TXC, 06004RM
+ * - 5-n-1 pro weather sensor, Model: 06014RM
+ * - 896 Rain gauge, Model: 00896
+ * - 592TXR / 06002RM Tower sensor (temperature and humidity)
+ * - 609TXC "TH" temperature and humidity sensor (609A1TX)
+ * - Acurite 986 Refrigerator / Freezer Thermometer
+ * - Acurite 606TX temperature sesor
+ * - Acurite 6045M Lightning Detector (Work in Progress)
+ */
+
+
 #include "rtl_433.h"
 #include "rtl_433.h"
+#include "util.h"
+#include "pulse_demod.h"
+#include "data.h"
 
 // ** Acurite 5n1 functions **
 
 
 // ** Acurite 5n1 functions **
 
-const float acurite_winddirections[] =
-    { 337.5, 315.0, 292.5, 270.0, 247.5, 225.0, 202.5, 180,
-      157.5, 135.0, 112.5, 90.0, 67.5, 45.0, 22.5, 0.0 };
+#define ACURITE_TXR_BITLEN             56
+#define ACURITE_5N1_BITLEN             64
+#define ACURITE_6045_BITLEN            72
+
+// ** Acurite known message types
+#define ACURITE_MSGTYPE_WINDSPEED_WINDDIR_RAINFALL  0x31
+#define ACURITE_MSGTYPE_WINDSPEED_TEMP_HUMIDITY     0x38
+
+static char time_str[LOCAL_TIME_BUFLEN];
+
+
+// Acurite 5n1 Wind direction values.
+// There are seem to be conflicting decodings.
+// It is possible there there are different versions
+// of the 5n1 station that report differently.
+//
+// The original implementation used by the 5n1 device type
+// here seems to have a straight linear/cicular mapping.
+//
+// The newer 5n1 mapping seems to just jump around with no clear
+// meaning, but does map to the values sent by Acurite's
+// only Acu-Link Internet Bridge and physical console 1512.
+// This is may be a modified/non-standard Gray Code.
+
+// Mapping 5n1 raw RF wind direction values to aculink's values
+//    RF, AcuLink
+//     0,  6,   NW,  315.0
+//     1,  8,  WSW,  247.5
+//     2,  2,  WNW,  292.5
+//     3,  0,    W,  270.0
+//     4,  4,  NNW,  337.5
+//     5,  A,   SW,  225.0
+//     6,  5,    N,    0.0
+//     7,  E,  SSW,  202.5
+//     8,  1,  ENE,   67.5
+//     9,  F,   SE,  135.0
+//     A,  9,    E,   90.0
+//     B,  B,  ESE,  112.5
+//     C,  3,   NE,   45.0
+//     D,  D,  SSE,  157.0
+//     E,  7,  NNE,   22.5
+//     F,  C,    S,  180.0
+
+// From draythomp/Desert-home-rtl_433
+// matches acu-link internet bridge values
+// The mapping isn't circular, it jumps around.
+char * acurite_5n1_winddirection_str[] =
+    {"NW",  // 0  315
+     "WSW", // 1  247.5
+     "WNW", // 2  292.5
+     "W",   // 3  270
+     "NNW", // 4  337.5
+     "SW",  // 5  225
+     "N",   // 6  0
+     "SSW", // 7  202.5
+     "ENE", // 8  67.5
+     "SE",  // 9  135
+     "E",   // 10 90
+     "ESE", // 11 112.5
+     "NE",  // 12 45
+     "SSE", // 13 157.5
+     "NNE", // 14 22.5
+     "S"};  // 15 180
+
+
+const float acurite_5n1_winddirections[] =
+    { 315.0, // 0 - NW
+      247.5, // 1 - WSW
+      292.5, // 2 - WNW
+      270.0, // 3 - W
+      337.5, // 4 - NNW
+      225.0, // 5 - SW
+      0.0,   // 6 - N
+      202.5, // 7 - SSW
+      67.5,  // 8 - ENE
+      135.0, // 9 - SE
+      90.0,  // a - E
+      112.5, // b - ESE
+      45.0,  // c - NE
+      157.5, // d - SSE
+      22.5,  // e - NNE
+      180.0, // f - S
+    };
+
+
+// 5n1 keep state for how much rain has been seen so far
+static int acurite_5n1raincounter = 0;  // for 5n1 decoder
+static int acurite_5n1t_raincounter = 0;  // for combined 5n1/TXR decoder
 
 
-static int acurite_raincounter = 0;
 
 
-static int acurite_crc(uint8_t row[BITBUF_COLS], int cols) {
+static int acurite_checksum(uint8_t row[BITBUF_COLS], int cols) {
     // sum of first n-1 bytes modulo 256 should equal nth byte
     // sum of first n-1 bytes modulo 256 should equal nth byte
+    // also disregard a row of all zeros
     int i;
     int sum = 0;
     for ( i=0; i < cols; i++)
         sum += row[i];
     int i;
     int sum = 0;
     for ( i=0; i < cols; i++)
         sum += row[i];
-    if ( sum % 256 == row[cols] )
+    if (sum != 0 && (sum % 256 == row[cols]))
         return 1;
     else
         return 0;
 }
 
         return 1;
     else
         return 0;
 }
 
-static int acurite_detect(uint8_t *pRow) {
-    int i;
-    if ( pRow[0] != 0x00 ) {
-        // invert bits due to wierd issue
-        for (i = 0; i < 8; i++)
-            pRow[i] = ~pRow[i] & 0xFF;
-        pRow[0] |= pRow[8];  // fix first byte that has mashed leading bit
-
-        if (acurite_crc(pRow, 7))
-            return 1;  // passes crc check
-    }
-    return 0;
-}
-
+// Temperature encoding for 5-n-1 sensor and possibly others
 static float acurite_getTemp (uint8_t highbyte, uint8_t lowbyte) {
     // range -40 to 158 F
     int highbits = (highbyte & 0x0F) << 7 ;
     int lowbits = lowbyte & 0x7F;
     int rawtemp = highbits | lowbits;
 static float acurite_getTemp (uint8_t highbyte, uint8_t lowbyte) {
     // range -40 to 158 F
     int highbits = (highbyte & 0x0F) << 7 ;
     int lowbits = lowbyte & 0x7F;
     int rawtemp = highbits | lowbits;
-    float temp = (rawtemp - 400) / 10.0;
-    return temp;
+    float temp_F = (rawtemp - 400) / 10.0;
+    return temp_F;
 }
 
 }
 
-static int acurite_getWindSpeed (uint8_t highbyte, uint8_t lowbyte) {
+static float acurite_getWindSpeed_kph (uint8_t highbyte, uint8_t lowbyte) {
     // range: 0 to 159 kph
     // range: 0 to 159 kph
-       // TODO: sensor does not seem to be in kph, e.g.,
-       // a value of 49 here was registered as 41 kph on base unit
-       // value could be rpm, etc which may need (polynomial) scaling factor??
+    // raw number is cup rotations per 4 seconds
+    // http://www.wxforum.net/index.php?topic=27244.0 (found from weewx driver)
        int highbits = ( highbyte & 0x1F) << 3;
     int lowbits = ( lowbyte & 0x70 ) >> 4;
        int highbits = ( highbyte & 0x1F) << 3;
     int lowbits = ( lowbyte & 0x70 ) >> 4;
-    int speed = highbits | lowbits;
-    return speed;
-}
-
-static float acurite_getWindDirection (uint8_t byte) {
-    // 16 compass points, ccw from (NNW) to 15 (N)
-    int direction = byte & 0x0F;
-    return acurite_winddirections[direction];
+    int rawspeed = highbits | lowbits;
+    float speed_kph = 0;
+    if (rawspeed > 0) {
+        speed_kph = rawspeed * 0.8278 + 1.0;
+    }
+    return speed_kph;
 }
 
 static int acurite_getHumidity (uint8_t byte) {
 }
 
 static int acurite_getHumidity (uint8_t byte) {
@@ -72,134 +161,928 @@ static int acurite_getRainfallCounter (uint8_t hibyte, uint8_t lobyte) {
     return raincounter;
 }
 
     return raincounter;
 }
 
-static int acurite5n1_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],int16_t bits_per_row[BITBUF_ROWS]) {
-    // acurite 5n1 weather sensor decoding for rtl_433
-    // Jens Jensen 2014
-    int i;
-    uint8_t *buf = NULL;
-    // run through rows til we find one with good crc (brute force)
-    for (i=0; i < BITBUF_ROWS; i++) {
-        if (acurite_detect(bb[i])) {
-            buf = bb[i];
-            break; // done
-        }
-    }
+// The high 2 bits of byte zero are the channel (bits 7,6)
+//  00 = C
+//  10 = B
+//  11 = A
+static char chLetter[4] = {'C','E','B','A'}; // 'E' stands for error
 
 
-    if (buf) {
-        // decode packet here
-        if (debug_output) {
-            for (i=0; i < 8; i++)
-                fprintf(stderr, "%02X ", buf[i]);
-            fprintf(stderr, "CRC OK\n");
-        }
+static char acurite_getChannel(uint8_t byte){
+    int channel = (byte & 0xC0) >> 6;
+    return chLetter[channel];
+}
 
 
-        if ((buf[2] & 0x0F) == 1) {
-            // wind speed, wind direction, rainfall
-            fprintf(stdout, "SENSOR:TYPE=ACURITE_5IN1,");
+// 5-n-1 sensor ID is the last 12 bits of byte 0 & 1
+// byte 0     | byte 1
+// CC RR IIII | IIII IIII
+//
+static uint16_t acurite_5n1_getSensorId(uint8_t hibyte, uint8_t lobyte){
+    return ((hibyte & 0x0f) << 8) | lobyte;
+}
 
 
-            float rainfall = 0.00;
-            int raincounter = acurite_getRainfallCounter(buf[5], buf[6]);
-            if (acurite_raincounter > 0) {
-                // track rainfall difference after first run
-                rainfall = ( raincounter - acurite_raincounter ) * 0.01;
-            } else {
-                // capture starting counter
-                acurite_raincounter = raincounter;
-            }
 
 
-            fprintf(stdout, "WINDSPEED=%d,",
-                acurite_getWindSpeed(buf[3], buf[4]));
-            fprintf(stdout, "WINDDIRECTION=%0.1f,",
-                acurite_getWindDirection(buf[4]));
-            fprintf(stdout, "RAINGAUGE=%0.2f\n", rainfall);
-
-        } else if ((buf[2] & 0x0F) == 8) {
-            fprintf(stdout, "SENSOR:TYPE=ACURITE_5IN1,");
-            // wind speed, temp, RH
-            fprintf(stdout, "WINDSPEED=%d,",
-                acurite_getWindSpeed(buf[3], buf[4]));
-            fprintf(stdout, "TEMPERATURE=%2.1f,",
-                acurite_getTemp(buf[4], buf[5]));
-            fprintf(stdout, "HUMIDITY=%d\n",
-                acurite_getHumidity(buf[6]));
-        }
-    } else {
-       return 0;
-    }
+// The sensor sends the same data three times, each of these have
+// an indicator of which one of the three it is. This means the
+// checksum and first byte will be different for each one.
+// The bits 5,4 of byte 0 indicate which copy of the 65 bit data string
+//  00 = first copy
+//  01 = second copy
+//  10 = third copy
+//  1100 xxxx  = channel A 1st copy
+//  1101 xxxx  = channel A 2nd copy
+//  1110 xxxx  = channel A 3rd copy
+static int acurite_5n1_getMessageCaught(uint8_t byte){
+    return (byte & 0x30) >> 4;
+}
 
 
-    if (debug_output)
-       debug_callback(bb, bits_per_row);
 
 
-    return 1;
+// So far, all that's known about the battery is that the
+// third byte, high nibble has two values.xo 0xb0=low and 0x70=OK
+// so this routine just returns the nibble shifted to make a byte
+// for more work as time goes by
+//
+// Battery status appears to be the 7th bit 0x40. 1 = normal, 0 = low
+// The 8th bit appears to be parity.
+// @todo - determine if the 5th & 6th bits (0x30) are status bits or
+//         part of the message type. So far these appear to always be 1
+static int acurite_5n1_getBatteryLevel(uint8_t byte){
+    return (byte & 0x40) >> 6;
 }
 
 }
 
-static int acurite_rain_gauge_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
-    // 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
+
+static int acurite_rain_gauge_callback(bitbuffer_t *bitbuffer) {
+       bitrow_t *bb = bitbuffer->bb;
+   // 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
     if ((bb[0][0] != 0) && (bb[0][1] != 0) && (bb[0][2]!=0) && (bb[0][3] == 0) && (bb[0][4] == 0)) {
            float total_rain = ((bb[0][1]&0xf)<<8)+ bb[0][2];
                total_rain /= 2; // Sensor reports number of bucket tips.  Each bucket tip is .5mm
     if ((bb[0][0] != 0) && (bb[0][1] != 0) && (bb[0][2]!=0) && (bb[0][3] == 0) && (bb[0][4] == 0)) {
            float total_rain = ((bb[0][1]&0xf)<<8)+ bb[0][2];
                total_rain /= 2; // Sensor reports number of bucket tips.  Each bucket tip is .5mm
-               fprintf(stdout, "SENSOR:TYPE=ACURITE_RAIN_GAUGE,RAIN=%2.1f\n", total_rain);
-               fprintf(stderr, "Raw Message: %02x %02x %02x %02x %02x\n",bb[0][0],bb[0][1],bb[0][2],bb[0][3],bb[0][4]);
+        fprintf(stdout, "AcuRite Rain Gauge Total Rain is %2.1fmm\n", total_rain);
+               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]);
         return 1;
     }
     return 0;
 }
 
         return 1;
     }
     return 0;
 }
 
-static int acurite_th_detect(uint8_t *buf){
-    if(buf[5] != 0) return 0;
-    uint8_t sum = (buf[0] + buf[1] + buf[2] + buf[3]) & 0xff;
-    if(sum == 0) return 0;
-    return sum == buf[4];
-}
+
+// Acurite 609TXC
+// Temperature in Celsius is encoded as a 12 bit integer value
+// multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2)
+// negative values are handled by treating it temporarily
+// as a 16 bit value to put the sign bit in a usable place.
+//
 static float acurite_th_temperature(uint8_t *s){
     uint16_t shifted = (((s[1] & 0x0f) << 8) | s[2]) << 4; // Logical left shift
     return (((int16_t)shifted) >> 4) / 10.0; // Arithmetic right shift
 }
 static float acurite_th_temperature(uint8_t *s){
     uint16_t shifted = (((s[1] & 0x0f) << 8) | s[2]) << 4; // Logical left shift
     return (((int16_t)shifted) >> 4) / 10.0; // Arithmetic right shift
 }
-static int acurite_th_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
-    uint8_t *buf = NULL;
-    int i;
-    for(i = 0; i < BITBUF_ROWS; i++){
-       if(acurite_th_detect(bb[i])){
-            buf = bb[i];
-            break;
+
+// Acurite 609 Temperature and Humidity Sensor
+// 5 byte messages
+// II ST TT HH CC
+// II - ID byte, changes at each power up
+// S - Status bitmask, normally 0x2,
+//     0xa - battery low (bit 0x80)
+// TTT - Temp in Celsius * 10, 12 bit with complement.
+// HH - Humidity
+// CC - Checksum
+//
+// @todo - see if the 3rd nybble is battery/status
+//
+static int acurite_th_callback(bitbuffer_t *bitbuf) {
+    uint8_t *bb = NULL;
+    int cksum, battery_low, valid = 0;
+    float tempc;
+    uint8_t humidity, id, status;
+    data_t *data;
+
+    local_time_str(0, time_str);
+
+    for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
+        if (bitbuf->bits_per_row[brow] != 40) {
+           continue;
+       }
+
+       bb = bitbuf->bb[brow];
+
+       cksum = (bb[0] + bb[1] + bb[2] + bb[3]);
+
+       if (cksum == 0 || ((cksum & 0xff) != bb[4])) {
+           continue;
+       }
+
+       tempc = acurite_th_temperature(bb);
+       id = bb[0];
+       status = (bb[1] & 0xf0) >> 4;
+       battery_low = status & 0x8;
+       humidity = bb[3];
+
+       data = data_make(
+                    "time",            "",             DATA_STRING,    time_str,
+                    "model",           "",             DATA_STRING,    "Acurite 609TXC Sensor",
+                    "id",              "",             DATA_INT,       id,
+                    "battery",         "",             DATA_STRING,    battery_low ? "LOW" : "OK",
+                    "status",          "",             DATA_INT,       status,
+                    "temperature_C",   "Temperature",  DATA_FORMAT,    "%.1f C", DATA_DOUBLE, tempc,
+                    "humidity",        "Humidity",     DATA_INT,       humidity,
+                    NULL);
+
+       data_acquired_handler(data);
+       valid++;
+    }
+
+    if (valid)
+        return 1;
+
+    return 0;
+}
+
+// Tower sensor ID is the last 14 bits of byte 0 & 1
+// byte 0    | byte 1
+// CCII IIII | IIII IIII
+//
+static uint16_t acurite_txr_getSensorId(uint8_t hibyte, uint8_t lobyte){
+    return ((hibyte & 0x3f) << 8) | lobyte;
+}
+
+
+// temperature encoding used by "tower" sensors 592txr
+// 14 bits available after removing both parity bits.
+// 11 bits needed for specified range -40 C to 70 C (-40 F - 158 F)
+// range -100 C to 1538.4 C
+static float acurite_txr_getTemp (uint8_t highbyte, uint8_t lowbyte) {
+    int rawtemp = ((highbyte & 0x7F) << 7) | (lowbyte & 0x7F);
+    float temp = rawtemp / 10.0 - 100;
+    return temp;
+}
+
+
+/*
+ * Acurite 06045 Lightning sensor Temperature encoding
+ * 12 bits of temperature after removing parity and status bits.
+ * Message native format appears to be in 1/10 of a degree Fahrenheit
+ * Device Specification: -40 to 158 F  / -40 to 70 C
+ * Available range given encoding with 12 bits -150.0 F to +259.6 F
+ */
+static float acurite_6045_getTemp (uint8_t highbyte, uint8_t lowbyte) {
+    int rawtemp = ((highbyte & 0x1F) << 7) | (lowbyte & 0x7F);
+    float temp = (rawtemp - 1500) / 10.0;
+    return temp;
+}
+
+/*
+ * Acurite 06045m Lightning Sensor decoding.
+ *
+ * Specs:
+ * - lightning strike count
+ * - extimated distance to front of storm, up to 25 miles / 40 km
+ * - Temperature -40 to 158 F / -40 to 70 C
+ * - Humidity 1 - 99% RH
+ *
+ * Status Information sent per 06047M/01021 display
+ * - (RF) interference (preventing lightning detection)
+ * - low battery
+ *
+ *
+ * Message format
+ * --------------
+ * Somewhat similar to 592TXR and 5-n-1 weather stations
+ * Same pulse characteristics. checksum, and parity checking on data bytes.
+ *
+ * 0   1   2   3   4   5   6   7   8
+ * CI? II  II  HH  ST  TT  LL  DD? KK
+ *
+ * C = Channel
+ * I = ID
+ * H = Humidity
+ * S = Status/Message type/Temperature MSB.
+ * T = Temperature
+ * D = Lightning distanace and status bits?
+ * L = Lightning strike count.
+ * K = Checksum
+ *
+ * Byte 0 - channel number A/B/C
+ * - Channel in 2 most significant bits - A: 0xC, B: 0x8, C: 00
+ * - TBD: lower 6 bits, ID or unused?
+ *
+ * Bytes 1 & 2 - ID, all 8 bits, no parity.
+ *
+ * Byte 3 - Humidity (7 bits + parity bit)
+ *
+ * Byte 4 - Status (2 bits) and Temperature MSB (5 bits)
+ * - Bitmask PSSTTTTT  (P = Parity, S = Status, T = Temperature)
+ * - 0x40 - Transmitting every 8 seconds (lightning possibly detected)
+ *          normal, off, transmits every 24 seconds
+ * - 0x20 - TBD: normally off, On is possibly low battery?
+ * - 0x1F - Temperature MSB (5 bits)
+ *
+ * Byte 5 - Temperature LSB (7 bits, 8th is parity)
+ *
+ * Byte 6 - Lightning Strike count (7 bits, 8th is parity)
+ * - Stored in EEPROM or something non-volatile)
+ * - Wraps at 127
+ *
+ * Byte 7 - Lightning Distance (5 bits) and status bits (2 bits)  (?)
+ * - Bits PSSDDDDD  (P = Parity, S = Status, D = Distance
+ * - 5 lower bits is distance in unit? (miles? km?) to edge of storm (theory)
+ * - Bit 0x20: (RF) interference / strong RFI detected (to be verified)
+ * - Bit 0x40: TBD, possible activity?
+ * - distance = 0x1f: possible invalid value indication (value at power up)
+ * - Note: Distance sometimes goes to 0 right after strike counter increment
+ *         status bits might indicate validifity of distance.
+ *
+ * Byte 8 - checksum. 8 bits, no parity.
+ *
+ * @todo - Get lightning/distance to front of storm to match display
+ * @todo - Low battery, figure out encoding
+ * @todo - figure out remaining status bits and how to report
+ * @todo - convert to data make once decoding is stable
+ */
+static int acurite_6045_decode (bitrow_t bb, int browlen) {
+    int valid = 0;
+    float tempf;
+    uint8_t humidity, message_type, l_status;
+    char channel, *wind_dirstr = "";
+    char channel_str[2];
+    uint16_t sensor_id;
+    uint8_t strike_count, strike_distance;
+
+    channel = acurite_getChannel(bb[0]);  // same as TXR
+    sensor_id = (bb[1] << 8) | bb[2];     // TBD 16 bits or 20?
+    humidity = acurite_getHumidity(bb[3]);  // same as TXR
+    message_type = (bb[4] & 0x60) >> 5;  // status bits: 0x2 8 second xmit, 0x1 - TBD batttery?
+    tempf = acurite_6045_getTemp(bb[4], bb[5]);
+    strike_count = bb[6] & 0x7f;
+    strike_distance = bb[7] & 0x1f;
+    l_status = (bb[7] & 0x60) >> 5;
+
+    printf("%s Acurite lightning 0x%04X Ch %c Msg Type 0x%02x: %.1f F %d %% RH Strikes %d Distance %d L_status 0x%02x -",
+          time_str, sensor_id, channel, message_type, tempf, humidity, strike_count, strike_distance, l_status);
+
+    // FIXME Temporarily dump raw message data until the
+    // decoding improves.  Includes parity indicator(*).
+    for (int i=0; i < browlen; i++) {
+       char pc;
+       pc = byteParity(bb[i]) == 0 ? ' ' : '*';
+       fprintf(stdout, " %02x%c", bb[i], pc);
+    }
+    printf("\n");
+
+    valid++;
+    return(valid);
+}
+
+
+/*
+ * This callback handles several Acurite devices that use a very
+ * similar RF encoding and data format:
+ *:
+ * - 592TXR temperature and humidity sensor
+ * - 5-n-1 weather station
+ * - 6045M Lightning Detectur with Temperature and Humidity
+ */
+static int acurite_txr_callback(bitbuffer_t *bitbuf) {
+    int browlen, valid = 0;
+    uint8_t *bb;
+    float tempc, tempf, wind_dird, rainfall = 0.0, wind_speed, wind_speedmph;
+    uint8_t humidity, sensor_status, sequence_num, message_type;
+    char channel, *wind_dirstr = "";
+    char channel_str[2];
+    uint16_t sensor_id;
+    int raincounter, temp, battery_low;
+    uint8_t strike_count, strike_distance;
+    data_t *data;
+
+
+    local_time_str(0, time_str);
+
+    if (debug_output > 1) {
+        fprintf(stderr,"acurite_txr\n");
+        bitbuffer_print(bitbuf);
+    }
+
+    for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
+       browlen = (bitbuf->bits_per_row[brow] + 7)/8;
+       bb = bitbuf->bb[brow];
+
+       if (debug_output > 1)
+           fprintf(stderr,"acurite_txr: row %d bits %d, bytes %d \n", brow, bitbuf->bits_per_row[brow], browlen);
+
+       if ((bitbuf->bits_per_row[brow] < ACURITE_TXR_BITLEN ||
+            bitbuf->bits_per_row[brow] > ACURITE_5N1_BITLEN + 1) &&
+           bitbuf->bits_per_row[brow] != ACURITE_6045_BITLEN) {
+           if (debug_output > 1 && bitbuf->bits_per_row[brow] > 16)
+               fprintf(stderr,"acurite_txr: skipping wrong len\n");
+           continue;
+       }
+
+       // There will be 1 extra false zero bit added by the demod.
+       // this forces an extra zero byte to be added
+       if (bb[browlen - 1] == 0)
+           browlen--;
+
+       if (!acurite_checksum(bb,browlen - 1)) {
+           if (debug_output) {
+               fprintf(stderr, "%s Acurite bad checksum:", time_str);
+               for (uint8_t i = 0; i < browlen; i++)
+                   fprintf(stderr," 0x%02x",bb[i]);
+               fprintf(stderr,"\n");
+           }
+           continue;
+       }
+
+       if (debug_output) {
+           fprintf(stderr, "acurite_txr Parity: ");
+           for (uint8_t i = 0; i < browlen; i++) {
+               fprintf(stderr,"%d",byteParity(bb[i]));
+           }
+           fprintf(stderr,"\n");
+       }
+
+
+       // tower sensor messages are 7 bytes.
+       // @todo - see if there is a type in the message that
+       // can be used instead of length to determine type
+       if (browlen == ACURITE_TXR_BITLEN / 8) {
+           channel = acurite_getChannel(bb[0]);
+           sensor_id = acurite_txr_getSensorId(bb[0],bb[1]);
+           sensor_status = bb[2]; // @todo, uses parity? & 0x07f
+           humidity = acurite_getHumidity(bb[3]);
+           tempc = acurite_txr_getTemp(bb[4], bb[5]);
+            sprintf(channel_str, "%c", channel);
+            battery_low = sensor_status >>7;
+
+            data = data_make(
+                    "time",                    "",             DATA_STRING,    time_str,
+                    "model",                   "",             DATA_STRING,    "Acurite tower sensor",
+                    "id",                      "",             DATA_INT,       sensor_id,
+                    "channel",                 "",             DATA_STRING,    &channel_str,
+                    "temperature_C",   "Temperature",  DATA_FORMAT,    "%.1f C", DATA_DOUBLE, tempc,
+                    "humidity",         "Humidity",    DATA_INT,       humidity,
+                    "battery",          "Battery",     DATA_INT,       battery_low,
+                    "status",          "",             DATA_INT,       sensor_status,
+                    NULL);
+
+            data_acquired_handler(data);
+            valid++;
+       }
+
+       // The 5-n-1 weather sensor messages are 8 bytes.
+       if (browlen == ACURITE_5N1_BITLEN / 8) {
+        if (debug_output) {
+            fprintf(stderr, "Acurite 5n1 raw msg: %02X %02X %02X %02X %02X %02X %02X %02X\n",
+                bb[0], bb[1], bb[2], bb[3], bb[4], bb[5], bb[6], bb[7]);
         }
         }
+           channel = acurite_getChannel(bb[0]);
+        sprintf(channel_str, "%c", channel);        
+           sensor_id = acurite_5n1_getSensorId(bb[0],bb[1]);
+           sequence_num = acurite_5n1_getMessageCaught(bb[0]);
+           message_type = bb[2] & 0x3f;
+        battery_low = (bb[2] & 0x40) >> 6;
+
+           if (message_type == ACURITE_MSGTYPE_WINDSPEED_WINDDIR_RAINFALL) {
+            // Wind speed, wind direction, and rain fall
+            wind_speed = acurite_getWindSpeed_kph(bb[3], bb[4]);
+            wind_speedmph = kmph2mph(wind_speed);
+            wind_dird = acurite_5n1_winddirections[bb[4] & 0x0f];
+            wind_dirstr = acurite_5n1_winddirection_str[bb[4] & 0x0f];
+            raincounter = acurite_getRainfallCounter(bb[5], bb[6]);
+            if (acurite_5n1t_raincounter > 0) {
+                // track rainfall difference after first run
+                // FIXME when converting to structured output, just output
+                // the reading, let consumer track state/wrap around, etc. 
+                rainfall = ( raincounter - acurite_5n1t_raincounter ) * 0.01;
+                if (raincounter < acurite_5n1t_raincounter) {
+                    fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, rain counter reset or wrapped around (old %d, new %d)\n",
+                        time_str, sensor_id, channel, acurite_5n1t_raincounter, raincounter);
+                    acurite_5n1t_raincounter = raincounter;
+                }
+            } else {
+                // capture starting counter
+                acurite_5n1t_raincounter = raincounter;
+                fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, Total rain fall since last reset: %0.2f\n",
+                time_str, sensor_id, channel, raincounter * 0.01);
+            }
+                
+            data = data_make(
+                "time",         "",   DATA_STRING,    time_str,
+                "model",        "",   DATA_STRING,    "Acurite 5n1 sensor",
+                "sensor_id",    NULL,   DATA_FORMAT,    "0x%02X",   DATA_INT,       sensor_id,   
+                "channel",      NULL,   DATA_STRING,    &channel_str,
+                "sequence_num",  NULL,   DATA_INT,      sequence_num,
+                "battery",      NULL,   DATA_STRING,    battery_low ? "OK" : "LOW",
+                "message_type", NULL,   DATA_INT,       message_type,
+                "wind_speed",   NULL,   DATA_FORMAT,    "%.1f mph", DATA_DOUBLE,     wind_speedmph,
+                "wind_dir_deg", NULL,   DATA_FORMAT,    "%.1f", DATA_DOUBLE,    wind_dird,
+                "wind_dir",     NULL,   DATA_STRING,    wind_dirstr,
+                "rainfall_accumulation",     NULL,   DATA_FORMAT,    "%.2f in", DATA_DOUBLE,    rainfall,
+                "raincounter_raw",  NULL,   DATA_INT,   raincounter,
+                NULL);
+
+            data_acquired_handler(data);
+
+           } else if (message_type == ACURITE_MSGTYPE_WINDSPEED_TEMP_HUMIDITY) {
+            // Wind speed, temperature and humidity
+            wind_speed = acurite_getWindSpeed_kph(bb[3], bb[4]);
+            wind_speedmph = kmph2mph(wind_speed);
+            tempf = acurite_getTemp(bb[4], bb[5]);
+            tempc = fahrenheit2celsius(tempf);
+            humidity = acurite_getHumidity(bb[6]);
+
+            data = data_make(
+                "time",         "",   DATA_STRING,    time_str,
+                "model",        "",   DATA_STRING,    "Acurite 5n1 sensor",
+                "sensor_id",    NULL,   DATA_FORMAT,    "0x%02X",   DATA_INT,       sensor_id,   
+                "channel",      NULL,   DATA_STRING,    &channel_str,
+                "sequence_num",  NULL,   DATA_INT,      sequence_num,
+                "battery",      NULL,   DATA_STRING,    battery_low ? "OK" : "LOW",
+                "message_type", NULL,   DATA_INT,       message_type,
+                "wind_speed",   NULL,   DATA_FORMAT,    "%.1f mph", DATA_DOUBLE,     wind_speedmph,
+                "temperature_F",       "temperature",  DATA_FORMAT,    "%.1f F", DATA_DOUBLE,    tempf,
+                "humidity",     NULL,  DATA_FORMAT,    "%d",   DATA_INT,   humidity,
+                NULL);
+            data_acquired_handler(data);
+            
+           } else {
+            fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, Status %02X, Unknown message type 0x%02x\n",
+                time_str, sensor_id, channel, bb[3], message_type);
+           }
+       }
+
+       if (browlen == ACURITE_6045_BITLEN / 8) {
+           // @todo check parity and reject if invalid
+           valid += acurite_6045_decode(bb, browlen);
+       }
+
     }
     }
-    if(buf){
-        fprintf(stdout, "SENSOR:TYPE=ACURITE_TEMP,");
-        fprintf(stdout, "TEMPERATURE=%.1f,", acurite_th_temperature(buf));
-        fprintf(stdout, "HUMIDITY=%d\n", buf[3]);
+
+    if (valid)
         return 1;
         return 1;
+
+    return 0;
+}
+
+
+/*
+ * Acurite 00986 Refrigerator / Freezer Thermometer
+ *
+ * Includes two sensors and a display, labeled 1 and 2,
+ * by default 1 - Refridgerator, 2 - Freezer
+ *
+ * PPM, 5 bytes, sent twice, no gap between repeaters
+ * start/sync pulses two short, with short gaps, followed by
+ * 4 long pulse/gaps.
+ *
+ * @todo, the 2 short sync pulses get confused as data.
+ *
+ * Data Format - 5 bytes, sent LSB first, reversed
+ *
+ * TT II II SS CC
+ *
+ * T - Temperature in Fahrenehit, integer, MSB = sign.
+ *     Encoding is "Sign and magnitude"
+ * I - 16 bit sensor ID
+ *     changes at each power up
+ * S - status/sensor type
+ *     0x01 = Sensor 2
+ *     0x02 = low battery
+ * C = CRC (CRC-8 poly 0x07, little-endian)
+ *
+ * @todo
+ * - needs new PPM demod that can separate out the short
+ *   start/sync pulses which confuse things and cause
+ *   one data bit to be lost in the check value.
+ * - low battery detection
+ *
+ */
+
+static int acurite_986_callback(bitbuffer_t *bitbuf) {
+    int browlen;
+    uint8_t *bb, sensor_num, status, crc, crcc;
+    uint8_t br[8];
+    int8_t tempf; // Raw Temp is 8 bit signed Fahrenheit
+    float tempc;
+    uint16_t sensor_id, valid_cnt = 0;
+    char sensor_type;
+
+    local_time_str(0, time_str);
+
+    if (debug_output > 1) {
+        fprintf(stderr,"acurite_986\n");
+        bitbuffer_print(bitbuf);
     }
 
     }
 
+    for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
+       browlen = (bitbuf->bits_per_row[brow] + 7)/8;
+       bb = bitbuf->bb[brow];
+
+       if (debug_output > 1)
+           fprintf(stderr,"acurite_986: row %d bits %d, bytes %d \n", brow, bitbuf->bits_per_row[brow], browlen);
+
+       if (bitbuf->bits_per_row[brow] < 39 ||
+           bitbuf->bits_per_row[brow] > 43 ) {
+           if (debug_output > 1 && bitbuf->bits_per_row[brow] > 16)
+               fprintf(stderr,"acurite_986: skipping wrong len\n");
+           continue;
+       }
+
+       // Reduce false positives
+       // may eliminate these with a beter PPM (precise?) demod.
+       if ((bb[0] == 0xff && bb[1] == 0xff && bb[2] == 0xff) ||
+          (bb[0] == 0x00 && bb[1] == 0x00 && bb[2] == 0x00)) {
+           continue;
+       }
+
+       // There will be 1 extra false zero bit added by the demod.
+       // this forces an extra zero byte to be added
+       if (browlen > 5 && bb[browlen - 1] == 0)
+           browlen--;
+
+       // Reverse the bits
+       for (uint8_t i = 0; i < browlen; i++)
+           br[i] = reverse8(bb[i]);
+
+       if (debug_output > 0) {
+           fprintf(stderr,"Acurite 986 reversed: ");
+           for (uint8_t i = 0; i < browlen; i++)
+               fprintf(stderr," %02x",br[i]);
+           fprintf(stderr,"\n");
+       }
+
+       tempf = br[0];
+       sensor_id = (br[1] << 8) + br[2];
+       status = br[3];
+       sensor_num = (status & 0x01) + 1;
+       status = status >> 1;
+       // By default Sensor 1 is the Freezer, 2 Refrigerator
+       sensor_type = sensor_num == 2 ? 'F' : 'R';
+       crc = br[4];
+
+       if ((crcc = crc8le(br, 5, 0x07, 0)) != 0) {
+           // XXX make debug
+           if (debug_output) {
+               fprintf(stderr,"%s Acurite 986 sensor bad CRC: %02x -",
+                       time_str, crc8le(br, 4, 0x07, 0));
+               for (uint8_t i = 0; i < browlen; i++)
+                   fprintf(stderr," %02x", br[i]);
+               fprintf(stderr,"\n");
+           }
+           continue;
+       }
+
+       if ((status & 1) == 1) {
+           fprintf(stderr, "%s Acurite 986 sensor 0x%04x - %d%c: low battery, status %02x\n",
+                   time_str, sensor_id, sensor_num, sensor_type, status);
+       }
+
+       // catch any status bits that haven't been decoded yet
+       if ((status & 0xFE) != 0) {
+           fprintf(stderr, "%s Acurite 986 sensor 0x%04x - %d%c: Unexpected status %02x\n",
+                   time_str, sensor_id, sensor_num, sensor_type, status);
+       }
+
+       if (tempf & 0x80) {
+           tempf = (tempf & 0x7f) * -1;
+       }
+       tempc = fahrenheit2celsius(tempf);
+
+
+       printf("%s Acurite 986 sensor 0x%04x - %d%c: %3.1f C %d F\n",
+              time_str, sensor_id, sensor_num, sensor_type,
+              tempc, tempf);
+
+       valid_cnt++;
+
+    }
+
+    if (valid_cnt)
+       return 1;
+
+    return 0;
+}
+
+// Checksum code from
+// https://eclecticmusingsofachaoticmind.wordpress.com/2015/01/21/home-automation-temperature-sensors/
+// with modifications listed in
+// http://www.osengr.org/WxShield/Downloads/Weather-Sensor-RF-Protocols.pdf
+//
+// This is the same algorithm as used in ambient_weather.c
+//
+uint8_t Checksum(int length, uint8_t *buff) {
+  uint8_t mask = 0xd3;
+  uint8_t checksum = 0x00;
+  uint8_t data;
+  int byteCnt;
+
+  for (byteCnt = 0; byteCnt < length; byteCnt++) {
+    int bitCnt;
+    data = buff[byteCnt];
+
+    for (bitCnt = 7; bitCnt >= 0; bitCnt--) {
+      uint8_t bit;
+
+      // Rotate mask right
+      bit = mask & 1;
+      mask = (mask >> 1) | (mask << 7);
+      if (bit) {
+        mask ^= 0x18;
+      }
+
+      // XOR mask into checksum if data bit is 1
+      if (data & 0x80) {
+        checksum ^= mask;
+      }
+      data <<= 1;
+    }
+  }
+  return checksum;
+}
+
+
+static int acurite_606_callback(bitbuffer_t *bitbuf) {
+    data_t *data;
+    bitrow_t *bb = bitbuf->bb;
+    float temperature; // temperature in C
+    int16_t temp;      // temperature as read from the data packet
+    int battery;        // the battery status: 1 is good, 0 is low
+    int8_t sensor_id;  // the sensor ID - basically a random number that gets reset whenever the battery is removed
+
+
+    local_time_str(0, time_str);
+
+    if (debug_output > 1) {
+        fprintf(stderr,"acurite_606\n");
+        bitbuffer_print(bitbuf);
+    }
+
+    // throw out all blank messages
+    if (bb[1][0] == 0 && bb[1][1] == 0 && bb[1][2] == 0 && bb[1][3] == 0)
+      return 0;
+
+    // do some basic checking to make sure we have a valid data record
+    if ((bb[0][0] == 0) && (bb[1][4] == 0)) {                                  //This test may need some more scrutiny...
+        // calculate the checksum and only continue if we have a maching checksum
+        uint8_t chk = Checksum(3, &bb[1][0]);
+
+        if (chk == bb[1][3]) {
+           // Processing the temperature: 
+            // Upper 4 bits are stored in nibble 1, lower 8 bits are stored in nibble 2
+            // upper 4 bits of nibble 1 are reserved for other usages (e.g. battery status)
+           temp = (int16_t)((uint16_t)(bb[1][1] << 12) | (bb[1][2] << 4));
+           temp = temp >> 4;
+
+           temperature = temp / 10.0;
+           sensor_id = bb[1][0];
+           battery = (bb[1][1] & 0x80) >> 7;
+
+           data = data_make("time",          "",            DATA_STRING, time_str,
+                             "model",         "",            DATA_STRING, "Acurite 606TX Sensor",
+                             "id",            "",            DATA_INT, sensor_id,
+                            "battery",       "Battery",     DATA_STRING, battery ? "OK" : "LOW",
+                             "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature,
+                             NULL);
+           data_acquired_handler(data);
+            return 1;
+       }
+    }
+
+    return 0;
+}
+
+
+static int acurite_00275rm_callback(bitbuffer_t *bitbuf) {
+    int crc, battery_low, id, model, valid = 0;
+    uint8_t *bb;
+    data_t *data;
+    char *model1 = "00275rm", *model2 = "00276rm";
+    float tempc, ptempc;
+    uint8_t probe, humidity, phumidity, water;
+    uint8_t signal[3][11];  //  Hold three copies of the signal
+    int     nsignal = 0;
+
+    local_time_str(0, time_str);
+
+    if (debug_output > 1) {
+        fprintf(stderr,"acurite_00275rm\n");
+        bitbuffer_print(bitbuf);
+    }
+
+    //  This sensor repeats signal three times.  Store each copy.
+    for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
+        if (bitbuf->bits_per_row[brow] != 88) continue;
+        if (nsignal>=3) continue;
+        memcpy(signal[nsignal], bitbuf->bb[brow], 11);
+        if (debug_output) {
+            fprintf(stderr,"acurite_00275rm: ");
+            for (int i=0; i<11; i++) fprintf(stderr," %02x",signal[nsignal][i]);
+            fprintf(stderr,"\n");
+        }
+        nsignal++;
+    }
+
+    //  All three signals were found
+    if (nsignal==3) {
+        //  Combine signal copies so that majority bit count wins
+        for (int i=0; i<11; i++) {
+            signal[0][i] = 
+                (signal[0][i] & signal[1][i]) |
+                (signal[1][i] & signal[2][i]) |
+                (signal[2][i] & signal[0][i]);
+        }
+        // CRC check fails?
+        if ((crc=crc16(&(signal[0][0]), 11/*len*/, 0xb2/*poly*/, 0xd0/*seed*/)) != 0) {
+            if (debug_output) {
+                fprintf(stderr,"%s Acurite 00275rm sensor bad CRC: %02x -",
+                    time_str, crc);
+                for (uint8_t i = 0; i < 11; i++)
+                    fprintf(stderr," %02x", signal[0][i]);
+                fprintf(stderr,"\n");
+            }
+        // CRC is OK
+        } else {
+            //  Decode the combined signal
+            id = (signal[0][0]<<16) | (signal[0][1]<<8) | signal[0][3];
+            battery_low = (signal[0][2] & 0x40)==0;
+            model       = (signal[0][2] & 1);
+            tempc       = 0.1 * ( (signal[0][4]<<4) | (signal[0][5]>>4) ) - 100;
+            probe       = signal[0][5] & 3;
+            humidity    = ((signal[0][6] & 0x1f) << 2) | (signal[0][7] >> 6);
+            //  No probe
+            if (probe==0) {
+                data = data_make(
+                    "time",            "",             DATA_STRING,    time_str,
+                    "model",           "",             DATA_STRING,    model ? model1 : model2,
+                    "probe",           "",             DATA_INT,       probe,
+                    "id",              "",             DATA_INT,       id,
+                    "battery",         "",             DATA_STRING,    battery_low ? "LOW" : "OK",
+                    "temperature_C",   "Celcius",      DATA_FORMAT,    "%.1f C",  DATA_DOUBLE, tempc,
+                    "humidity",        "Humidity",     DATA_INT,       humidity,
+                    "crc",             "",             DATA_STRING,    "ok",
+
+                    NULL);
+            //  Water probe (detects water leak)
+            } else if (probe==1) {
+                water = (signal[0][7] & 0x0f) == 15;
+                data = data_make(
+                    "time",            "",             DATA_STRING,    time_str,
+                    "model",           "",             DATA_STRING,    model ? model1 : model2,
+                    "probe",           "",             DATA_INT,       probe,
+                    "id",              "",             DATA_INT,       id,
+                    "battery",         "",             DATA_STRING,    battery_low ? "LOW" : "OK",
+                    "temperature_C",   "Celcius",      DATA_FORMAT,    "%.1f C",  DATA_DOUBLE, tempc,
+                    "humidity",        "Humidity",     DATA_INT,       humidity,
+                    "water",           "",             DATA_INT,       water,
+                    "crc",             "",             DATA_STRING,    "ok",
+                    NULL);
+            //  Soil probe (detects temperature)
+            } else if (probe==2) {
+                ptempc    = 0.1 * ( ((0x0f&signal[0][7])<<8) | signal[0][8] ) - 100; 
+                data = data_make(
+                    "time",            "",             DATA_STRING,    time_str,
+                    "model",           "",             DATA_STRING,    model ? model1 : model2,
+                    "probe",           "",             DATA_INT,       probe,
+                    "id",              "",             DATA_INT,       id,
+                    "battery",         "",             DATA_STRING,    battery_low ? "LOW" : "OK",
+                    "temperature_C",   "Celcius",      DATA_FORMAT,    "%.1f C",  DATA_DOUBLE, tempc,
+                    "humidity",        "Humidity",     DATA_INT,       humidity,
+                    "ptemperature_C",  "Celcius",      DATA_FORMAT,    "%.1f C",  DATA_DOUBLE, ptempc,
+                    "crc",             "",             DATA_STRING,    "ok",
+                    NULL);
+            //  Spot probe (detects temperature and humidity)
+            } else if (probe==3) {
+                ptempc    = 0.1 * ( ((0x0f&signal[0][7])<<8) | signal[0][8] ) - 100; 
+                phumidity = signal[0][9] & 0x7f;
+                data = data_make(
+                    "time",            "",             DATA_STRING,    time_str,
+                    "model",           "",             DATA_STRING,    model ? model1 : model2,
+                    "probe",           "",             DATA_INT,       probe,
+                    "id",              "",             DATA_INT,       id,
+                    "battery",         "",             DATA_STRING,    battery_low ? "LOW" : "OK",
+                    "temperature_C",   "Celcius",      DATA_FORMAT,    "%.1f C",  DATA_DOUBLE, tempc,
+                    "humidity",        "Humidity",     DATA_INT,       humidity,
+                    "ptemperature_C",  "Celcius",      DATA_FORMAT,    "%.1f C",  DATA_DOUBLE, ptempc,
+                    "phumidity",       "Humidity",     DATA_INT,       phumidity,
+                    "crc",             "",             DATA_STRING,    "ok",
+                    NULL);
+            }
+            data_acquired_handler(data);
+            valid=1;
+        }
+    }
+    if (valid) return 1;
     return 0;
 }
 
     return 0;
 }
 
-r_device acurite5n1 = {
-    /* .id             = */ 10,
-    /* .name           = */ "Acurite 5n1 Weather Station",
-    /* .modulation     = */ OOK_PWM_P,
-    /* .short_limit    = */ 70,
-    /* .long_limit     = */ 240,
-    /* .reset_limit    = */ 21000,
-    /* .json_callback  = */ &acurite5n1_callback,
-};
 
 r_device acurite_rain_gauge = {
 
 r_device acurite_rain_gauge = {
-    /* .id             = */ 11,
-    /* .name           = */ "Acurite 896 Rain Gauge",
-    /* .modulation     = */ OOK_PWM_D,
-    /* .short_limit    = */ 1744/4,
-    /* .long_limit     = */ 3500/4,
-    /* .reset_limit    = */ 5000/4,
-    /* .json_callback  = */ &acurite_rain_gauge_callback,
+    .name           = "Acurite 896 Rain Gauge",
+    .modulation     = OOK_PULSE_PPM_RAW,
+    .short_limit    = 1744,
+    .long_limit     = 3500,
+    .reset_limit    = 5000,
+    .json_callback  = &acurite_rain_gauge_callback,
+// Disabled by default due to false positives on oregon scientific v1 protocol see issue #353
+    .disabled       = 1,
+    .demod_arg      = 0,
 };
 
 };
 
+
 r_device acurite_th = {
 r_device acurite_th = {
-    /* .id             = */ 12,
-    /* .name           = */ "Acurite Temperature and Humidity Sensor",
-    /* .modulation     = */ OOK_PWM_D,
-    /* .short_limit    = */ 300,
-    /* .long_limit     = */ 550,
-    /* .reset_limit    = */ 2500,
-    /* .json_callback  = */ &acurite_th_callback,
+    .name           = "Acurite 609TXC Temperature and Humidity Sensor",
+    .modulation     = OOK_PULSE_PPM_RAW,
+    .short_limit    = 1200,
+    .long_limit     = 3000,
+    .reset_limit    = 10000,
+    .json_callback  = &acurite_th_callback,
+    .disabled       = 0,
+    .demod_arg      = 0,
+};
+
+/*
+ * For Acurite 592 TXR Temp/Mumidity, but
+ * Should match Acurite 592TX, 5-n-1, etc.
+ *
+ *
+ * @todo, convert to use precise demodulator, after adding a flag
+ *        to set "polarity" to flip short bits = 0 vs. 1.
+ */
+
+r_device acurite_txr = {
+    .name           = "Acurite 592TXR Temp/Humidity, 5n1 Weather Station, 6045 Lightning",
+    .modulation     = OOK_PULSE_PWM_TERNARY,
+    .short_limit    = 320,
+    .long_limit     = 520,
+    .reset_limit    = 4000,
+    .json_callback  = &acurite_txr_callback,
+    .disabled       = 1,
+    .demod_arg      = 2,
+};
+
+// @todo, find a set of values that will work reasonably
+// with a range of signal levels
+//
+// PWM_Precise_Parameters pwm_precise_param_acurite_txr = {
+//     .pulse_tolerance        = 50,
+//     .pulse_sync_width       = 170,
+// };
+
+//r_device acurite_txr = {
+//    .name           = "Acurite 592TXR Temp/Humidity sensor",
+//    .modulation     = OOK_PULSE_PWM_PRECISE,
+//    .short_limit    = 440,
+//    .long_limit     = 260,
+//    .reset_limit    = 4000,
+//    .json_callback  = &acurite_txr_callback,
+//    .disabled       = 0,
+//    .demod_arg      = (unsigned long)&pwm_precise_param_acurite_txr,
+//};
+
+
+/*
+ * Acurite 00986 Refrigerator / Freezer Thermometer
+ *
+ * Temperature only, Pulse Position
+ *
+ * 4 x 400 sample (150 uS) start/sync pulses
+ * 40 (42) 50 (20 uS)  (sample data pulses)
+ * short gap approx 130 samples
+ * long gap approx 220 samples
+ *
+ */
+r_device acurite_986 = {
+    .name           = "Acurite 986 Refrigerator / Freezer Thermometer",
+    .modulation     = OOK_PULSE_PPM_RAW,
+    .short_limit    = 720,   // Threshold between short and long gap
+    .long_limit     = 1280,
+    .reset_limit    = 4000,
+    .json_callback  = &acurite_986_callback,
+    .disabled       = 1,
+    .demod_arg      = 2,
+};
+
+/*
+ * Acurite 00606TX Tower Sensor
+ *
+ * Temperature only
+ *
+ */
+r_device acurite_606 = {
+    .name           = "Acurite 606TX Temperature Sensor",
+    .modulation     = OOK_PULSE_PPM_RAW,
+    .short_limit    = 3500,
+    .long_limit     = 7000,
+    .reset_limit    = 10000,
+    .json_callback  = &acurite_606_callback,
+    .disabled       = 0,
+    .demod_arg      = 0,
+};
+
+r_device acurite_00275rm = {
+    .name           = "Acurite 00275rm,00276rm Temp/Humidity with optional probe",
+    .modulation     = OOK_PULSE_PWM_TERNARY,
+    .short_limit    = 320,  // = 4* 80,  80  is reported by -G option
+    .long_limit     = 520,  // = 4*130, 130  "
+  //  .reset_limit    = 608,  // = 4*152, 152  "
+    .reset_limit    = 708,  // = 4*152, 152  "
+    .json_callback  = &acurite_00275rm_callback,
+    .disabled       = 0,
+    .demod_arg      = 2,
 };
 };