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!
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
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')
28 * D = Rain (bitvalue * 0.25 mm)
31 * Format for Windspeed
32 * AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
33 * RC Type Windspd Checksum
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)
40 * Format for Winddirection & Windgust
41 * AAAAAAAA BBBB CCCD DDDD DDDD EEEEEEEE FFFF
42 * RC Type Winddir Windgust Checksum
44 * B = Message type (xyyx = NON temp/humidity data if yy = '11')
47 * E = Windgust (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
49 *********************************************************************************************
52 uint8_t bcd_decode8(uint8_t x) {
53 return ((x & 0xF0) >> 4) * 10 + (x & 0x0F);
56 static int alectov1_callback(bitbuffer_t *bitbuffer) {
57 bitrow_t *bb = bitbuffer->bb;
58 int temperature_before_dec;
59 int temperature_after_dec;
61 uint8_t humidity, csum = 0, csum2 = 0;
65 char time_str[LOCAL_TIME_BUFLEN];
66 unsigned bits = bitbuffer->bits_per_row[1];
71 local_time_str(0, time_str);
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)) {
76 for (i = 0; i < 4; i++) {
77 uint8_t tmp = reverse8(bb[1][i]);
78 csum += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
80 tmp = reverse8(bb[5][i]);
81 csum2 += (tmp & 0xf) + ((tmp & 0xf0) >> 4);
84 csum = ((bb[1][1] & 0x7f) == 0x6c) ? (csum + 0x7) : (0xf - csum);
85 csum2 = ((bb[5][1] & 0x7f) == 0x6c) ? (csum2 + 0x7) : (0xf - csum2);
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");
94 "%s AlectoV1 Checksum/Parity error\n",
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;
106 if ((bb[1][1] & 0xe0) == 0x60) {
107 wind = ((bb[1][1] & 0xf) == 0xc) ? 0 : 1;
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);
117 /* Untested code written according to the specification, may not decode correctly */
118 if ((bb[1][1]&0xe) == 0x8 && bb[1][2] == 0) {
120 } else if ((bb[1][1]&0xe) == 0xe) {
122 } //According to supplied data!
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);
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,
137 data_acquired_handler(data);
141 double rain_mm = ((reverse8(bb[1][3]) << 8)+reverse8(bb[1][2])) * 0.25F;
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,
150 data_acquired_handler(data);
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) {
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
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,
170 data_acquired_handler(data);
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]);
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]));
185 static char *output_fields[] = {
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,
206 .reset_limit = 10000,
207 .json_callback = &alectov1_callback,
210 .fields = output_fields