Bugfixes
[rtl-433.git] / src / devices / lacrossews.c
1 /* LaCrosse WS-2310 433 Mhz Weather Station
2         Packet Format is 53 bits/ 13 nibbles
3
4          bits   nibble
5          0- 3   0 - 0000
6          4- 7   1 - 1001
7          8-11   2 - Type  GPTT  G=0, P=Parity, Gust=Gust, TT=Type  GTT 000=Temp, 001=Humidity, 010=Rain, 011=Wind, 111-Gust
8         12-15   3 - ID High
9         16-19   4 - ID Low
10         20-23   5 - Data Types  GWRH  G=Gust Sent, W=Wind Sent, R=Rain Sent, H=Humidity Sent
11         24-27   6 - Parity TUU? T=Temp Sent, UU=Next Update, 00=8 seconds, 01=32 seconds, 10=?, 11=128 seconds, ?=?
12         28-31   7 - Value1
13         32-35   8 - Value2
14         36-39   9 - Value3
15         40-43   10 - ~Value1
16         44-47   11 - ~Value2
17         48-51   12 - Check Sum = Nibble sum of nibbles 0-11
18  */
19
20 #include "rtl_433.h"
21 #include "util.h"
22 #include "data.h"
23
24 #define LACROSSE_WS_BITLEN      52
25
26 static int lacrossews_detect(uint8_t *pRow, uint8_t *msg_nybbles, int16_t rowlen) {
27         int i;
28         uint8_t rbyte_no, rbit_no, mnybble_no, mbit_no;
29         uint8_t bit, checksum = 0, parity = 0;
30         char time_str[LOCAL_TIME_BUFLEN];
31
32         // Weather Station 2310 Packets
33         if (rowlen == LACROSSE_WS_BITLEN && pRow[0] == 0x09) {
34
35                 for (i = 0; i < (LACROSSE_WS_BITLEN / 4); i++) {
36                         msg_nybbles[i] = 0;
37                 }
38
39                 // Move nybbles into a byte array
40                 // Compute parity and checksum at the same time.
41                 for (i = 0; i < LACROSSE_WS_BITLEN; i++) {
42                         rbyte_no = i / 8;
43                         rbit_no = 7 - (i % 8);
44                         mnybble_no = i / 4;
45                         mbit_no = 3 - (i % 4);
46                         bit = (pRow[rbyte_no] & (1 << rbit_no)) ? 1 : 0;
47                         msg_nybbles[mnybble_no] |= (bit << mbit_no);
48                         if(i == 9 || (i >= 27 && i <= 39))
49                                 parity += bit;
50                 }
51
52                 for (i = 0; i < 12; i++) {
53                         checksum = (checksum + msg_nybbles[i]) & 0x0F;
54                 }
55
56                 if( msg_nybbles[0] == 0x0 &&
57                         msg_nybbles[1] == 0x9 &&
58                         msg_nybbles[7] == (~msg_nybbles[10] & 0xF) &&
59                         msg_nybbles[8] == (~msg_nybbles[11] & 0xF) &&
60                         (parity & 0x1) == 0x1 &&
61                         checksum == msg_nybbles[12])
62                         return 1;
63                 else {
64                         local_time_str(0, time_str);
65             if (debug_output) {
66                         fprintf(stdout,
67                                 "%s LaCrosse Packet Validation Failed error: Checksum Comp. %d != Recv. %d, Parity %d\n",
68                                 time_str, checksum, msg_nybbles[12], parity);
69                         for (i = 0; i < (LACROSSE_WS_BITLEN / 4); i++) {
70                                 fprintf(stderr, "%X", msg_nybbles[i]);
71                         }
72                         fprintf(stderr, "\n");
73             }
74                         return 0;
75                 }
76         }
77
78         return 0;
79 }
80
81 static int lacrossews_callback(bitbuffer_t *bitbuffer) {
82         bitrow_t *bb = bitbuffer->bb;
83
84         int m;
85         int events = 0;
86         uint8_t msg_nybbles[(LACROSSE_WS_BITLEN / 4)];
87         uint8_t ws_id, msg_type, sensor_id, msg_data, msg_unknown, msg_checksum;
88         int msg_value_bcd, msg_value_bcd2, msg_value_bin;
89         float temp_c, temp_f, wind_dir, wind_spd, rain_mm, rain_in;
90         char time_str[LOCAL_TIME_BUFLEN], *wind_key, *wind_label;
91         data_t *data;
92
93         for (m = 0; m < BITBUF_ROWS; m++) {
94                 // break out the message nybbles into separate bytes
95                 if (lacrossews_detect(bb[m], msg_nybbles, bitbuffer->bits_per_row[m])) {
96
97                         ws_id = (msg_nybbles[0] << 4) + msg_nybbles[1];
98                         msg_type = ((msg_nybbles[2] >> 1) & 0x4) + (msg_nybbles[2] & 0x3);
99                         sensor_id = (msg_nybbles[3] << 4) + msg_nybbles[4];
100                         msg_data = (msg_nybbles[5] << 1) + (msg_nybbles[6] >> 3);
101                         msg_unknown = msg_nybbles[6] & 0x01;
102                         msg_value_bcd = msg_nybbles[7] * 100 + msg_nybbles[8] * 10 + msg_nybbles[9];
103                         msg_value_bcd2 = msg_nybbles[7] * 10 + msg_nybbles[8];
104                         msg_value_bin = (msg_nybbles[7] * 256 + msg_nybbles[8] * 16 + msg_nybbles[9]);
105                         msg_checksum = msg_nybbles[12];
106
107                         local_time_str(0, time_str);
108
109                         if (debug_output)
110                                 fprintf(stderr, "%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X   ",
111                                                                         msg_nybbles[0], msg_nybbles[1], msg_nybbles[2], msg_nybbles[3],
112                                                                         msg_nybbles[4], msg_nybbles[5], msg_nybbles[6], msg_nybbles[7],
113                                                                         msg_nybbles[8], msg_nybbles[9], msg_nybbles[10], msg_nybbles[11],
114                                                                         msg_nybbles[12]);
115
116                         switch (msg_type) {
117                         // Temperature
118                         case 0:
119                                 temp_c = (msg_value_bcd - 300.0) / 10.0;
120                                 data = data_make("time",          "",             DATA_STRING, time_str,
121                                                                                                         "model",         "",            DATA_STRING, "LaCrosse WS",
122                                                                                                         "ws_id",         "",            DATA_INT, ws_id,
123                                                                                                         "id",            "",            DATA_INT, sensor_id,
124                                                                                                         "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c,
125                                                                                                         NULL);
126                                 data_acquired_handler(data);
127                                 events++;
128
129                                 break;
130                         // Humidity
131                         case 1:
132                                 if(msg_nybbles[7] == 0xA && msg_nybbles[8] == 0xA)
133                                         fprintf(stderr, "%s LaCrosse WS %02X-%02X: Humidity Error\n",
134                                                 time_str, ws_id, sensor_id);
135                                 else {
136                                         data = data_make("time",          "",            DATA_STRING, time_str,
137                                                                                                                 "model",         "",            DATA_STRING, "LaCrosse WS",
138                                                                                                                 "ws_id",         "",            DATA_INT, ws_id,
139                                                                                                                 "id",            "",            DATA_INT, sensor_id,
140                                                                                                                 "humidity",      "Humidity",    DATA_INT, msg_value_bcd2,
141                                                                                                                 NULL);
142                                         data_acquired_handler(data);
143                                         events++;
144                                 }
145                                 break;
146                         // Rain
147                         case 2:
148                                 rain_mm = 0.5180 * msg_value_bin;
149                                 data = data_make("time",          "",           DATA_STRING, time_str,
150                                                                                                         "model",          "",           DATA_STRING, "LaCrosse WS",
151                                                                                                         "ws_id",          "",           DATA_INT, ws_id,
152                                                                                                         "id",             "",           DATA_INT, sensor_id,
153                                                                                                         "rainfall_mm",    "Rainfall",   DATA_FORMAT, "%3.2f mm", DATA_DOUBLE, rain_mm, NULL);
154                                 data_acquired_handler(data);
155                                 events++;
156                                 break;
157                         // Wind
158                         case 3:
159                         // Gust
160                         case 7:
161                                 wind_dir = msg_nybbles[9] * 22.5;
162                                 wind_spd = (msg_nybbles[7] * 16 + msg_nybbles[8])/ 10.0;
163                                 if(msg_nybbles[7] == 0xF && msg_nybbles[8] == 0xE) {
164                     if (debug_output) {
165                                         fprintf(stderr, "%s LaCrosse WS %02X-%02X: %s Not Connected\n",
166                                                 time_str, ws_id, sensor_id, msg_type == 3 ? "Wind":"Gust");
167                     }
168                 } else {
169                                         wind_key = msg_type == 3 ? "wind_speed_ms":"gust_speed_ms";
170                                         wind_label = msg_type == 3 ? "Wind speed":"Gust speed";
171                                         data = data_make("time",          "",           DATA_STRING, time_str,
172                                                                                                                 "model",          "",           DATA_STRING, "LaCrosse WS",
173                                                                                                                 "ws_id",          "",           DATA_INT, ws_id,
174                                                                                                                 "id",             "",           DATA_INT, sensor_id,
175                                                                                                                 wind_key,         wind_label,   DATA_FORMAT, "%3.1f m/s", DATA_DOUBLE, wind_spd,
176                                                                                                                 "wind_direction", "Direction",  DATA_DOUBLE, wind_dir, NULL);
177                                         data_acquired_handler(data);
178                                         events++;
179                                 }
180                                 break;
181                         default:
182                 if (debug_output) {
183                                 fprintf(stderr,
184                                         "%s LaCrosse WS %02X-%02X: Unknown data type %d, bcd %d bin %d\n",
185                                         time_str, ws_id, sensor_id, msg_type, msg_value_bcd, msg_value_bin);
186                 }
187                                 events++;
188                         }
189                 }
190         }
191
192         return events;
193 }
194
195 static char *output_fields[] = {
196                 "time",
197                 "model",
198                 "ws_id",
199                 "id",
200                 "temperature_C",
201                 "humidity",
202                 "rainfall_mm",
203                 "wind_speed_ms",
204                 "gust_speed_ms",
205                 "wind_direction",
206                 NULL
207 };
208
209 r_device lacrossews = {
210  .name           = "LaCrosse WS-2310 Weather Station",
211  .modulation     = OOK_PULSE_PWM_RAW,
212  .short_limit    = 952,
213  .long_limit     = 3000,
214  .reset_limit    = 8000,
215  .json_callback  = &lacrossews_callback,
216  .disabled       = 0,
217  .demod_arg      = 0,
218  .fields = output_fields
219 };