- Merged with upstream version
[rtl-433.git] / src / devices / alecto.c
1 #include "rtl_433.h"
2
3 /* Documentation also at http://www.tfd.hu/tfdhu/files/wsprotocol/auriol_protocol_v20.pdf
4  * Message Format: (9 nibbles, 36 bits):
5  * Please note that bytes need to be reversed before processing!
6  *
7  * Format for Temperature Humidity
8  *   AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
9  *   RC       Type Temperature___ Humidity Checksum
10  *   A = Rolling Code / Device ID
11  *       Device ID: AAAABBAA BB is used for channel, base channel is 01
12  *       When channel selector is used, channel can be 10 (2) and 11 (3)
13  *   B = Message type (xyyz = temp/humidity if yy <> '11') else wind/rain sensor
14  *       x indicates battery status (0 normal, 1 voltage is below ~2.6 V)
15  *       z 0 indicates regular transmission, 1 indicates requested by pushbutton
16  *   C = Temperature (two's complement)
17  *   D = Humidity BCD format
18  *   E = Checksum
19  *
20  * Format for Rain
21  *   AAAAAAAA BBBB CCCC DDDD DDDD DDDD DDDD EEEE
22  *   RC       Type      Rain                Checksum
23  *   A = Rolling Code /Device ID
24  *   B = Message type (xyyx = NON temp/humidity data if yy = '11')
25  *   C = fixed to 1100
26  *   D = Rain (bitvalue * 0.25 mm)
27  *   E = Checksum
28  *
29  * Format for Windspeed
30  *   AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
31  *   RC       Type                Windspd  Checksum
32  *   A = Rolling Code
33  *   B = Message type (xyyx = NON temp/humidity data if yy = '11')
34  *   C = Fixed to 1000 0000 0000
35  *   D = Windspeed  (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
36  *   E = Checksum
37  *
38  * Format for Winddirection & Windgust
39  *   AAAAAAAA BBBB CCCD DDDD DDDD EEEEEEEE FFFF
40  *   RC       Type      Winddir   Windgust Checksum
41  *   A = Rolling Code
42  *   B = Message type (xyyx = NON temp/humidity data if yy = '11')
43  *   C = Fixed to 111
44  *   D = Wind direction
45  *   E = Windgust (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
46  *   F = Checksum
47  *********************************************************************************************
48  */
49 uint8_t reverse8(uint8_t x) {
50     x = (x & 0xF0) >> 4 | (x & 0x0F) << 4;
51     x = (x & 0xCC) >> 2 | (x & 0x33) << 2;
52     x = (x & 0xAA) >> 1 | (x & 0x55) << 1;
53     return x;
54 }
55
56 uint8_t bcd_decode8(uint8_t x) {
57     return ((x & 0xF0) >> 4) * 10 + (x & 0x0F);
58 }
59
60 static int alectov1_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
61     int temperature_before_dec;
62     int temperature_after_dec;
63     int16_t temp;
64     uint8_t humidity, csum = 0, csum2 = 0;
65     int i;
66     if (bb[1][0] == bb[5][0] && bb[2][0] == bb[6][0] && (bb[1][4] & 0xf) == 0 && (bb[5][4] & 0xf) == 0
67             && (bb[5][0] != 0 && bb[5][1] != 0)) {
68
69         for (i = 0; i < 4; i++) {
70             uint8_t tmp = reverse8(bb[1][i]);
71             csum += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
72
73             tmp = reverse8(bb[5][i]);
74             csum2 += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
75         }
76
77         csum = ((bb[1][1] & 0x7f) == 0x6c) ? (csum + 0x7) : (0xf - csum);
78         csum2 = ((bb[5][1] & 0x7f) == 0x6c) ? (csum2 + 0x7) : (0xf - csum2);
79
80         csum = reverse8((csum & 0xf) << 4);
81         csum2 = reverse8((csum2 & 0xf) << 4);
82         /* Quit if checksup does not work out */
83         if (csum != (bb[1][4] >> 4) || csum2 != (bb[5][4] >> 4)) {
84             fprintf(stderr, "\nAlectoV1 CRC error");
85             return 0;
86         } //Invalid checksum
87
88
89         uint8_t wind = 0;
90
91         if ((bb[1][1] & 0xe0) == 0x60) {
92             wind = ((bb[1][1] & 0xf) == 0xc) ? 0 : 1;
93
94             fprintf(stdout, "SENSOR:TYPE=ALECTO_%s,", wind ? "WIND" : "RAIN_GAUGE");
95             fprintf(stdout, "ID=%d,", reverse8(bb[1][0]));
96             fprintf(stdout, "BUTTON=%d,", bb[1][1]&0x10 ? 1 : 0);
97             fprintf(stdout, "BATTERY=%s,", bb[1][1]&0x80 ? "LOW" : "OK");
98             if (wind) {
99                 int skip = -1;
100                 /* Untested code written according to the specification, may not decode correctly  */
101                 if ((bb[1][1]&0xe) == 0x8 && bb[1][2] == 0) {
102                     skip = 0;
103                 } else if ((bb[1][1]&0xe) == 0xe) {
104                     skip = 4;
105                 } //According to supplied data!
106                 if (skip >= 0) {
107                     double speed = reverse8(bb[1 + skip][3]);
108                     double gust = reverse8(bb[5 + skip][3]);
109                     int direction = (reverse8(bb[5 + skip][2]) << 1) | (bb[5 + skip][1] & 0x1);
110                     fprintf(stdout, "WINDSPEED=%.0f,", speed);
111                     fprintf(stdout, "WINDGUST=%.0f,", gust);
112                     fprintf(stdout, "WINDDIRECTION=%.2i\n", direction);
113                 }
114             } else {
115                 /* Untested code written according to the specification, may not decode correctly  */
116                 double rain_mm = (reverse8(bb[1][2]) + (reverse8(bb[1][3] << 8))) * 0.25;
117                 fprintf(stdout, "RAINFALL=%f\n", rain_mm);
118             }
119         } else if (bb[2][0] == bb[3][0] && bb[3][0] == bb[4][0] && bb[4][0] == bb[5][0] &&
120                 bb[5][0] == bb[6][0] && (bb[3][4] & 0xf) == 0 && (bb[5][4] & 0xf) == 0) {
121             //static char * temp_states[4] = {"stable", "increasing", "decreasing", "invalid"};
122             temp = (int16_t) ((uint16_t) (reverse8(bb[1][1]) >> 4) | (reverse8(bb[1][2]) << 4));
123             if ((temp & 0x800) != 0) {
124                 temp |= 0xf000;
125             }
126             temperature_before_dec = abs(temp / 10);
127             temperature_after_dec = abs(temp % 10);
128             humidity = bcd_decode8(reverse8(bb[1][3]));
129             fprintf(stdout, "SENSOR:TYPE=ALECTO_TEMP,");
130             fprintf(stdout, "ID=%d,", reverse8(bb[1][0]));
131             fprintf(stdout, "CHANNEL== %d,", (bb[1][0] & 0xc) >> 2);
132             fprintf(stdout, "BUTTON=%d,", bb[1][1]&0x10 ? 1 : 0);
133             fprintf(stdout, "BATTERY=%s,", bb[1][1]&0x80 ? "Low" : "OK");
134             fprintf(stdout, "TEMPERATURE=%s%d.%d,", temp < 0 ? "-" : "", temperature_before_dec, temperature_after_dec);
135             fprintf(stdout, "HUMIDITY=%d\n", humidity);
136         }
137         fprintf(stderr, "Checksum      = %01x (calculated %01x)\n", bb[1][4] >> 4, csum);
138
139         fprintf(stderr, "Received Data = %02x %02x %02x %02x %02x\n", bb[1][0], bb[1][1], bb[1][2], bb[1][3], bb[1][4]);
140         if (wind) fprintf(stderr, "Rcvd Data 2   = %02x %02x %02x %02x %02x\n", bb[5][0], bb[5][1], bb[5][2], bb[5][3], bb[5][4]);
141         /*
142          * fprintf(stderr, "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]));
143          */
144         if (debug_output)
145             debug_callback(bb, bits_per_row);
146
147         return 1;
148     }
149     return 0;
150 }
151
152 //Timing based on 250000
153 r_device alectov1 = {
154     /* .id             = */ 13,
155     /* .name           = */ "AlectoV1 Weather Sensor",
156     /* .modulation     = */ OOK_PWM_D,
157     /* .short_limit    = */ 3500 / 4, //875
158     /* .long_limit     = */ 7000 / 4, //1750
159     /* .reset_limit    = */ 15000 / 4, //3750
160     /* .json_callback  = */ &alectov1_callback,
161 };