Bugfixes
[rtl-433.git] / src / devices / alecto.c
1 #include "rtl_433.h"
2 #include "data.h"
3 #include "util.h"
4
5 /* Documentation also at http://www.tfd.hu/tfdhu/files/wsprotocol/auriol_protocol_v20.pdf
6  * Message Format: (9 nibbles, 36 bits):
7  * Please note that bytes need to be reversed before processing!
8  *
9  * Format for Temperature Humidity
10  *   AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
11  *   RC       Type Temperature___ Humidity Checksum
12  *   A = Rolling Code / Device ID
13  *       Device ID: AAAABBAA BB is used for channel, base channel is 01
14  *       When channel selector is used, channel can be 10 (2) and 11 (3)
15  *   B = Message type (xyyz = temp/humidity if yy <> '11') else wind/rain sensor
16  *       x indicates battery status (0 normal, 1 voltage is below ~2.6 V)
17  *       z 0 indicates regular transmission, 1 indicates requested by pushbutton
18  *   C = Temperature (two's complement)
19  *   D = Humidity BCD format
20  *   E = Checksum
21  *
22  * Format for Rain
23  *   AAAAAAAA BBBB CCCC DDDD DDDD DDDD DDDD EEEE
24  *   RC       Type      Rain                Checksum
25  *   A = Rolling Code /Device ID
26  *   B = Message type (xyyx = NON temp/humidity data if yy = '11')
27  *   C = fixed to 1100
28  *   D = Rain (bitvalue * 0.25 mm)
29  *   E = Checksum
30  *
31  * Format for Windspeed
32  *   AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
33  *   RC       Type                Windspd  Checksum
34  *   A = Rolling Code
35  *   B = Message type (xyyx = NON temp/humidity data if yy = '11')
36  *   C = Fixed to 1000 0000 0000
37  *   D = Windspeed  (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
38  *   E = Checksum
39  *
40  * Format for Winddirection & Windgust
41  *   AAAAAAAA BBBB CCCD DDDD DDDD EEEEEEEE FFFF
42  *   RC       Type      Winddir   Windgust Checksum
43  *   A = Rolling Code
44  *   B = Message type (xyyx = NON temp/humidity data if yy = '11')
45  *   C = Fixed to 111
46  *   D = Wind direction
47  *   E = Windgust (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
48  *   F = Checksum
49  *********************************************************************************************
50  */
51
52 uint8_t bcd_decode8(uint8_t x) {
53     return ((x & 0xF0) >> 4) * 10 + (x & 0x0F);
54 }
55
56 static int alectov1_callback(bitbuffer_t *bitbuffer) {
57     bitrow_t *bb = bitbuffer->bb;
58     int temperature_before_dec;
59     int temperature_after_dec;
60     int16_t temp;
61     uint8_t humidity, csum = 0, csum2 = 0;
62     int i;
63
64     data_t *data;
65     char time_str[LOCAL_TIME_BUFLEN];
66     unsigned bits = bitbuffer->bits_per_row[1];
67
68     if (bits != 36)
69         return 0;
70
71     local_time_str(0, time_str);
72
73     if (bb[1][0] == bb[5][0] && bb[2][0] == bb[6][0] && (bb[1][4] & 0xf) == 0 && (bb[5][4] & 0xf) == 0
74             && (bb[5][0] != 0 && bb[5][1] != 0)) {
75
76         for (i = 0; i < 4; i++) {
77             uint8_t tmp = reverse8(bb[1][i]);
78             csum += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
79
80             tmp = reverse8(bb[5][i]);
81             csum2 += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
82         }
83
84         csum = ((bb[1][1] & 0x7f) == 0x6c) ? (csum + 0x7) : (0xf - csum);
85         csum2 = ((bb[5][1] & 0x7f) == 0x6c) ? (csum2 + 0x7) : (0xf - csum2);
86
87         csum = reverse8((csum & 0xf) << 4);
88         csum2 = reverse8((csum2 & 0xf) << 4);
89         /* Quit if checksup does not work out */
90         if (csum != (bb[1][4] >> 4) || csum2 != (bb[5][4] >> 4)) {
91             //fprintf(stdout, "\nAlectoV1 CRC error");
92             if(debug_output) {
93                 fprintf(stderr,
94                 "%s AlectoV1 Checksum/Parity error\n",
95                 time_str);
96             }
97             return 0;
98         } //Invalid checksum
99
100
101         uint8_t wind = 0;
102         uint8_t channel = (bb[1][0] & 0xc) >> 2;
103         uint8_t sensor_id = reverse8(bb[1][0]);
104         uint8_t battery_low = bb[1][1]&0x80;
105
106         if ((bb[1][1] & 0xe0) == 0x60) {
107             wind = ((bb[1][1] & 0xf) == 0xc) ? 0 : 1;
108
109             //left out data (not needed):
110             //bb[1][1]&0x10 ? "timed event":"Button generated ");
111             //fprintf(stdout, "Protocol      = AlectoV1 bpr1: %d bpr2: %d\n", bits_per_row[1], bits_per_row[5]);
112             //fprintf(stdout, "Button        = %d\n", bb[1][1]&0x10 ? 1 : 0);
113  
114             if (wind) {
115                 // Wind sensor
116                 int skip = -1;
117                 /* Untested code written according to the specification, may not decode correctly  */
118                 if ((bb[1][1]&0xe) == 0x8 && bb[1][2] == 0) {
119                     skip = 0;
120                 } else if ((bb[1][1]&0xe) == 0xe) {
121                     skip = 4;
122                 } //According to supplied data!
123                 if (skip >= 0) {
124                     double speed = reverse8(bb[1 + skip][3]);
125                     double gust = reverse8(bb[5 + skip][3]);
126                     int direction = (reverse8(bb[5 + skip][2]) << 1) | (bb[5 + skip][1] & 0x1);
127
128                         data = data_make("time",          "",           DATA_STRING, time_str,
129                                                                         "model",          "",           DATA_STRING, "AlectoV1 Wind Sensor",
130                                                                         "id",             "House Code", DATA_INT,    sensor_id,
131                                                                         "channel",        "Channel",    DATA_INT,    channel,
132                                                                         "battery",        "Battery",    DATA_STRING, battery_low ? "LOW" : "OK",
133                                                                         "wind_speed",     "Wind speed", DATA_FORMAT, "%.2f m/s", DATA_DOUBLE, speed * 0.2F,
134                                                                         "wind_gust",      "Wind gust",  DATA_FORMAT, "%.2f m/s", DATA_DOUBLE, gust * 0.2F,
135                                                                         "wind_direction", "Direction",  DATA_INT,    direction,
136                                                                         NULL);
137                                 data_acquired_handler(data);
138                 }
139             } else {
140                 // Rain sensor
141                 double rain_mm = ((reverse8(bb[1][3]) << 8)+reverse8(bb[1][2])) * 0.25F;
142
143                 data = data_make("time",         "",           DATA_STRING, time_str,
144                                                                 "model",         "",           DATA_STRING, "AlectoV1 Rain Sensor",
145                                                                 "id",            "House Code", DATA_INT,    sensor_id,
146                                                                 "channel",       "Channel",    DATA_INT,    channel,
147                                                                 "battery",       "Battery",    DATA_STRING, battery_low ? "LOW" : "OK",
148                                                             "rain_total",    "Total Rain", DATA_FORMAT, "%.02f mm", DATA_DOUBLE, rain_mm,
149                                                             NULL);
150                             data_acquired_handler(data);
151             }
152         } else if (bb[2][0] == bb[3][0] && bb[3][0] == bb[4][0] && bb[4][0] == bb[5][0] &&
153                 bb[5][0] == bb[6][0] && (bb[3][4] & 0xf) == 0 && (bb[5][4] & 0xf) == 0) {
154             //static char * temp_states[4] = {"stable", "increasing", "decreasing", "invalid"};
155             temp = (int16_t) ((uint16_t) (reverse8(bb[1][1]) >> 4) | (reverse8(bb[1][2]) << 4));
156             if ((temp & 0x800) != 0) {
157                 temp |= 0xf000;
158             }
159             humidity = bcd_decode8(reverse8(bb[1][3]));
160             if (humidity>100) return 0;//extra detection false positive!! prologue is also 36bits and sometimes detected as alecto            
161
162             data = data_make("time",         "",            DATA_STRING, time_str,
163                                                         "model",         "",            DATA_STRING, "AlectoV1 Temperature Sensor",
164                                                         "id",            "House Code",  DATA_INT,    sensor_id,
165                                                         "channel",       "Channel",     DATA_INT,    channel,
166                                                         "battery",       "Battery",     DATA_STRING, battery_low ? "LOW" : "OK",
167                                                         "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, (float) temp / 10.0F,
168                                                         "humidity",      "Humidity",    DATA_FORMAT, "%u %%",   DATA_INT, humidity,
169                                                         NULL);
170                         data_acquired_handler(data);
171         }        
172         if (debug_output){
173            fprintf(stdout, "Checksum      = %01x (calculated %01x)\n", bb[1][4] >> 4, csum);
174            fprintf(stdout, "Received Data = %02x %02x %02x %02x %02x\n", bb[1][0], bb[1][1], bb[1][2], bb[1][3], bb[1][4]);
175            if (wind) fprintf(stdout, "Rcvd Data 2   = %02x %02x %02x %02x %02x\n", bb[5][0], bb[5][1], bb[5][2], bb[5][3], bb[5][4]);
176          /*
177          * fprintf(stdout, "L2M: %02x %02x %02x %02x %02x\n",reverse8(bb[1][0]),reverse8(bb[1][1]),reverse8(bb[1][2]),reverse8(bb[1][3]),reverse8(bb[1][4]));
178          */
179         }
180         return 1;
181     }
182     return 0;
183 }
184
185 static char *output_fields[] = {
186         "time",
187         "model",
188         "id",
189         "channel",
190         "battery",
191         "temperature_C",
192         "humidity",
193         "rain_total",
194         "wind_speed",
195         "wind_gust",
196         "wind_direction",
197         NULL
198 };
199
200 //Timing based on 250000
201 r_device alectov1 = {
202     .name           = "AlectoV1 Weather Sensor (Alecto WS3500 WS4500 Ventus W155/W044 Oregon)",
203     .modulation     = OOK_PULSE_PPM_RAW,
204     .short_limit    = 3500,
205     .long_limit     = 7000,
206     .reset_limit    = 10000,
207     .json_callback  = &alectov1_callback,
208     .disabled       = 0,
209     .demod_arg      = 0,
210     .fields         = output_fields
211 };