Acurite 5in1 minor fixes
[rtl-433.git] / src / devices / lacrosse.c
1 /* LaCrosse TX Temperature and Humidity Sensors
2  * Tested: TX-7U and TX-6U (Temperature only)
3  *
4  * Not Tested but should work: TX-3, TX-4
5  *
6  * Protocol Documentation: http://www.f6fbb.org/domo/sensors/tx3_th.php
7  *
8  * Message is 44 bits, 11 x 4 bit nybbles:
9  *
10  * [00] [cnt = 10] [type] [addr] [addr + parity] [v1] [v2] [v3] [iv1] [iv2] [check]
11  *
12  * Notes:
13  * - Zero Pulses are longer (1,400 uS High, 1,000 uS Low) = 2,400 uS
14  * - One Pulses are shorter (  550 uS High, 1,000 uS Low) = 1,600 uS
15  * - Sensor id changes when the battery is changed
16  * - Values are BCD with one decimal place: vvv = 12.3
17  * - Value is repeated integer only iv = 12
18  * - Temperature is in Celsius with 50.0 added (to handle negative values)
19  * - There is a 4 bit checksum and a parity bit covering the three digit value
20  * - Parity check for TX-3 and TX-4 might be different.
21  * - Msg sent with one repeat after 30 mS
22  * - Temperature and humidity are sent as separate messages
23  * - Frequency for each sensor may be could be off by as much as 50-75 khz
24  */
25
26 #include "rtl_433.h"
27
28 // buffer to hold localized timestamp YYYY-MM-DD HH:MM:SS
29 #define LOCAL_TIME_BUFLEN       32
30
31 void local_time_str(time_t time_secs, char *buf) {
32         time_t etime;
33         struct tm *tm_info;
34
35         if (time_secs == 0) {
36                 time(&etime);
37         } else {
38                 etime = time_secs;
39         }
40
41         tm_info = localtime(&etime);
42
43         strftime(buf, LOCAL_TIME_BUFLEN, "%Y-%m-%d %H:%M:%S", tm_info);
44 }
45
46 // Check for a valid LaCrosse Packet
47 //
48 // written for the version of pwm_p_decode() (OOK_PWM_P)
49 // pulse width detector with two anomalys:
50 // 1. bits are inverted
51 // 2. The first bit is discarded as a start bit
52 //
53 // If a fixed pulse width decoder is used this
54 // routine will need to be changed.
55 static int lacrossetx_detect(uint8_t *pRow, uint8_t *msg_nybbles) {
56         int i;
57         uint8_t rbyte_no, rbit_no, mnybble_no, mbit_no;
58         uint8_t bit, checksum, parity_bit, parity = 0;
59
60         // Actual Packet should start with 0x0A and be 6 bytes
61         // actual message is 44 bit, 11 x 4 bit nybbles.
62         if ((pRow[0] & 0xFE) == 0x14 && pRow[6] == 0 && pRow[7] == 0) {
63
64                 for (i = 0; i < 11; i++) {
65                         msg_nybbles[i] = 0;
66                 }
67
68                 // Move nybbles into a byte array
69                 // shifted by one to compensate for loss of first bit.
70                 for (i = 0; i < 43; i++) {
71                         rbyte_no = i / 8;
72                         rbit_no = 7 - (i % 8);
73                         mnybble_no = (i + 1) / 4;
74                         mbit_no = 3 - ((i + 1) % 4);
75                         bit = (pRow[rbyte_no] & (1 << rbit_no)) ? 1 : 0;
76                         msg_nybbles[mnybble_no] |= (bit << mbit_no);
77
78                         // Check parity on three bytes of data value
79                         // TX3U might calculate parity on all data including
80                         // sensor id and redundant integer data
81                         if (mnybble_no > 4 && mnybble_no < 8) {
82                                 parity += bit;
83                         }
84
85                         //          fprintf(stderr, "recv: [%d/%d] %d -> msg [%d/%d] %02x, Parity: %d %s\n", rbyte_no, rbit_no,
86                         //                  bit, mnybble_no, mbit_no, msg_nybbles[mnybble_no], parity,
87                         //                  ( mbit_no == 0 ) ? "\n" : "" );
88                 }
89
90                 parity_bit = msg_nybbles[4] & 0x01;
91                 parity += parity_bit;
92
93                 // Validate Checksum (4 bits in last nybble)
94                 checksum = 0;
95                 for (i = 0; i < 10; i++) {
96                         checksum = (checksum + msg_nybbles[i]) & 0x0F;
97                 }
98
99                 // fprintf(stderr,"Parity: %d, parity bit %d, Good %d\n", parity, parity_bit, parity % 2);
100
101                 if (checksum == msg_nybbles[10] && (parity % 2 == 0)) {
102                         return 1;
103                 } else {
104                         fprintf(stderr,
105                                         "LaCrosse Checksum/Parity error: %d != %d, Parity %d\n",
106                                         checksum, msg_nybbles[10], parity);
107                         return 0;
108                 }
109         }
110
111         return 0;
112 }
113
114 // LaCrosse TX-6u, TX-7u,  Temperature and Humidity Sensors
115 // Temperature and Humidity are sent in different messages bursts.
116 static int lacrossetx_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS],
117                 int16_t bits_per_row[BITBUF_ROWS]) {
118
119         int i, m, valid = 0;
120         uint8_t *buf;
121         uint8_t msg_nybbles[11];
122         uint8_t sensor_id, msg_type, msg_len, msg_parity, msg_checksum;
123         int msg_value_int;
124         float msg_value = 0, temp_c = 0, temp_f = 0;
125         time_t time_now;
126         char time_str[25];
127
128         static float last_msg_value = 0.0;
129         static uint8_t last_sensor_id = 0;
130         static uint8_t last_msg_type = 0;
131         static time_t last_msg_time = 0;
132
133         for (m = 0; m < BITBUF_ROWS; m++) {
134                 valid = 0;
135                 if (lacrossetx_detect(bb[m], msg_nybbles)) {
136
137                         msg_len = msg_nybbles[1];
138                         msg_type = msg_nybbles[2];
139                         sensor_id = (msg_nybbles[3] << 3) + (msg_nybbles[4] >> 1);
140                         msg_parity = msg_nybbles[4] & 0x01;
141                         msg_value = msg_nybbles[5] * 10 + msg_nybbles[6]
142                                         + msg_nybbles[7] / 10.0;
143                         msg_value_int = msg_nybbles[8] * 10 + msg_nybbles[9];
144                         msg_checksum = msg_nybbles[10];
145
146                         time(&time_now);
147
148                         // suppress duplicates
149                         if (sensor_id == last_sensor_id && msg_type == last_msg_type
150                                         && last_msg_value == msg_value
151                                         && time_now - last_msg_time < 50) {
152                                 continue;
153                         }
154
155                         local_time_str(time_now, time_str);
156
157                         switch (msg_type) {
158                         case 0x00:
159                                 temp_c = msg_value - 50.0;
160                                 temp_f = temp_c * 1.8 + 32;
161                                 printf("SENSOR:TYPE=LACROSSE_TX,TIME=%s,ID=%02x,TEMPERATURE=%3.1f\n",
162                                                 time_str, sensor_id, temp_c);
163                                 break;
164
165                         case 0x0E:
166                                 printf("SENSOR:TYPE=LACROSSE_TX,TIME=%s,ID=%02x,HUMIDITY=%3.1f\n",
167                                                 time_str, sensor_id, msg_value);
168                                 break;
169
170                         default:
171                                 fprintf(stderr,
172                                                 "%s LaCrosse Sensor %02x: Unknown Reading % 3.1f (%d)\n",
173                                                 time_str, sensor_id, msg_value, msg_value_int);
174                         }
175
176                         time(&last_msg_time);
177                         last_msg_value = msg_value;
178                         last_msg_type = msg_type;
179                         last_sensor_id = sensor_id;
180
181                 } else {
182                         return 0;
183                 }
184         }
185
186         if (debug_output)
187                 debug_callback(bb, bits_per_row);
188         return 1;
189 }
190
191 r_device lacrossetx = {
192 /* .id             = */15,
193 /* .name           = */"LaCrosse TX Temperature / Humidity Sensor",
194 /* .modulation     = */OOK_PWM_P,
195 /* .short_limit    = */238,
196 /* .long_limit     = */750,
197 /* .reset_limit    = */8000,
198 /* .json_callback  = */&lacrossetx_callback, };