Редизайн на основе текущей ветки мейнстрима + новые устройства.
[rtl-433.git] / src / devices / lacrossews.c
diff --git a/src/devices/lacrossews.c b/src/devices/lacrossews.c
new file mode 100755 (executable)
index 0000000..d4c718f
--- /dev/null
@@ -0,0 +1,219 @@
+/* LaCrosse WS-2310 433 Mhz Weather Station
+       Packet Format is 53 bits/ 13 nibbles
+
+        bits   nibble
+        0- 3   0 - 0000
+        4- 7   1 - 1001
+        8-11   2 - Type  GPTT  G=0, P=Parity, Gust=Gust, TT=Type  GTT 000=Temp, 001=Humidity, 010=Rain, 011=Wind, 111-Gust
+       12-15   3 - ID High
+       16-19   4 - ID Low
+       20-23   5 - Data Types  GWRH  G=Gust Sent, W=Wind Sent, R=Rain Sent, H=Humidity Sent
+       24-27   6 - Parity TUU? T=Temp Sent, UU=Next Update, 00=8 seconds, 01=32 seconds, 10=?, 11=128 seconds, ?=?
+       28-31   7 - Value1
+       32-35   8 - Value2
+       36-39   9 - Value3
+       40-43   10 - ~Value1
+       44-47   11 - ~Value2
+       48-51   12 - Check Sum = Nibble sum of nibbles 0-11
+ */
+
+#include "rtl_433.h"
+#include "util.h"
+#include "data.h"
+
+#define LACROSSE_WS_BITLEN     52
+
+static int lacrossews_detect(uint8_t *pRow, uint8_t *msg_nybbles, int16_t rowlen) {
+       int i;
+       uint8_t rbyte_no, rbit_no, mnybble_no, mbit_no;
+       uint8_t bit, checksum = 0, parity = 0;
+       char time_str[LOCAL_TIME_BUFLEN];
+
+       // Weather Station 2310 Packets
+       if (rowlen == LACROSSE_WS_BITLEN && pRow[0] == 0x09) {
+
+               for (i = 0; i < (LACROSSE_WS_BITLEN / 4); i++) {
+                       msg_nybbles[i] = 0;
+               }
+
+               // Move nybbles into a byte array
+               // Compute parity and checksum at the same time.
+               for (i = 0; i < LACROSSE_WS_BITLEN; i++) {
+                       rbyte_no = i / 8;
+                       rbit_no = 7 - (i % 8);
+                       mnybble_no = i / 4;
+                       mbit_no = 3 - (i % 4);
+                       bit = (pRow[rbyte_no] & (1 << rbit_no)) ? 1 : 0;
+                       msg_nybbles[mnybble_no] |= (bit << mbit_no);
+                       if(i == 9 || (i >= 27 && i <= 39))
+                               parity += bit;
+               }
+
+               for (i = 0; i < 12; i++) {
+                       checksum = (checksum + msg_nybbles[i]) & 0x0F;
+               }
+
+               if( msg_nybbles[0] == 0x0 &&
+                       msg_nybbles[1] == 0x9 &&
+                       msg_nybbles[7] == (~msg_nybbles[10] & 0xF) &&
+                       msg_nybbles[8] == (~msg_nybbles[11] & 0xF) &&
+                       (parity & 0x1) == 0x1 &&
+                       checksum == msg_nybbles[12])
+                       return 1;
+               else {
+                       local_time_str(0, time_str);
+            if (debug_output) {
+                       fprintf(stdout,
+                               "%s LaCrosse Packet Validation Failed error: Checksum Comp. %d != Recv. %d, Parity %d\n",
+                               time_str, checksum, msg_nybbles[12], parity);
+                       for (i = 0; i < (LACROSSE_WS_BITLEN / 4); i++) {
+                               fprintf(stderr, "%X", msg_nybbles[i]);
+                       }
+                       fprintf(stderr, "\n");
+            }
+                       return 0;
+               }
+       }
+
+       return 0;
+}
+
+static int lacrossews_callback(bitbuffer_t *bitbuffer) {
+       bitrow_t *bb = bitbuffer->bb;
+
+       int m;
+       int events = 0;
+       uint8_t msg_nybbles[(LACROSSE_WS_BITLEN / 4)];
+       uint8_t ws_id, msg_type, sensor_id, msg_data, msg_unknown, msg_checksum;
+       int msg_value_bcd, msg_value_bcd2, msg_value_bin;
+       float temp_c, temp_f, wind_dir, wind_spd, rain_mm, rain_in;
+       char time_str[LOCAL_TIME_BUFLEN], *wind_key, *wind_label;
+       data_t *data;
+
+       for (m = 0; m < BITBUF_ROWS; m++) {
+               // break out the message nybbles into separate bytes
+               if (lacrossews_detect(bb[m], msg_nybbles, bitbuffer->bits_per_row[m])) {
+
+                       ws_id = (msg_nybbles[0] << 4) + msg_nybbles[1];
+                       msg_type = ((msg_nybbles[2] >> 1) & 0x4) + (msg_nybbles[2] & 0x3);
+                       sensor_id = (msg_nybbles[3] << 4) + msg_nybbles[4];
+                       msg_data = (msg_nybbles[5] << 1) + (msg_nybbles[6] >> 3);
+                       msg_unknown = msg_nybbles[6] & 0x01;
+                       msg_value_bcd = msg_nybbles[7] * 100 + msg_nybbles[8] * 10 + msg_nybbles[9];
+                       msg_value_bcd2 = msg_nybbles[7] * 10 + msg_nybbles[8];
+                       msg_value_bin = (msg_nybbles[7] * 256 + msg_nybbles[8] * 16 + msg_nybbles[9]);
+                       msg_checksum = msg_nybbles[12];
+
+                       local_time_str(0, time_str);
+
+                       if (debug_output)
+                               fprintf(stderr, "%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X   ",
+                                                                       msg_nybbles[0], msg_nybbles[1], msg_nybbles[2], msg_nybbles[3],
+                                                                       msg_nybbles[4], msg_nybbles[5], msg_nybbles[6], msg_nybbles[7],
+                                                                       msg_nybbles[8], msg_nybbles[9], msg_nybbles[10], msg_nybbles[11],
+                                                                       msg_nybbles[12]);
+
+                       switch (msg_type) {
+                       // Temperature
+                       case 0:
+                               temp_c = (msg_value_bcd - 300.0) / 10.0;
+                               data = data_make("time",          "",             DATA_STRING, time_str,
+                                                                                                       "model",         "",            DATA_STRING, "LaCrosse WS",
+                                                                                                       "ws_id",         "",            DATA_INT, ws_id,
+                                                                                                       "id",            "",            DATA_INT, sensor_id,
+                                                                                                       "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c,
+                                                                                                       NULL);
+                               data_acquired_handler(data);
+                               events++;
+
+                               break;
+                       // Humidity
+                       case 1:
+                               if(msg_nybbles[7] == 0xA && msg_nybbles[8] == 0xA)
+                                       fprintf(stderr, "%s LaCrosse WS %02X-%02X: Humidity Error\n",
+                                               time_str, ws_id, sensor_id);
+                               else {
+                                       data = data_make("time",          "",            DATA_STRING, time_str,
+                                                                                                               "model",         "",            DATA_STRING, "LaCrosse WS",
+                                                                                                               "ws_id",         "",            DATA_INT, ws_id,
+                                                                                                               "id",            "",            DATA_INT, sensor_id,
+                                                                                                               "humidity",      "Humidity",    DATA_INT, msg_value_bcd2,
+                                                                                                               NULL);
+                                       data_acquired_handler(data);
+                                       events++;
+                               }
+                               break;
+                       // Rain
+                       case 2:
+                               rain_mm = 0.5180 * msg_value_bin;
+                               data = data_make("time",          "",           DATA_STRING, time_str,
+                                                                                                       "model",          "",           DATA_STRING, "LaCrosse WS",
+                                                                                                       "ws_id",          "",           DATA_INT, ws_id,
+                                                                                                       "id",             "",           DATA_INT, sensor_id,
+                                                                                                       "rainfall_mm",    "Rainfall",   DATA_FORMAT, "%3.2f mm", DATA_DOUBLE, rain_mm, NULL);
+                               data_acquired_handler(data);
+                               events++;
+                               break;
+                       // Wind
+                       case 3:
+                       // Gust
+                       case 7:
+                               wind_dir = msg_nybbles[9] * 22.5;
+                               wind_spd = (msg_nybbles[7] * 16 + msg_nybbles[8])/ 10.0;
+                               if(msg_nybbles[7] == 0xF && msg_nybbles[8] == 0xE) {
+                    if (debug_output) {
+                                       fprintf(stderr, "%s LaCrosse WS %02X-%02X: %s Not Connected\n",
+                                               time_str, ws_id, sensor_id, msg_type == 3 ? "Wind":"Gust");
+                    }
+                } else {
+                                       wind_key = msg_type == 3 ? "wind_speed_ms":"gust_speed_ms";
+                                       wind_label = msg_type == 3 ? "Wind speed":"Gust speed";
+                                       data = data_make("time",          "",           DATA_STRING, time_str,
+                                                                                                               "model",          "",           DATA_STRING, "LaCrosse WS",
+                                                                                                               "ws_id",          "",           DATA_INT, ws_id,
+                                                                                                               "id",             "",           DATA_INT, sensor_id,
+                                                                                                               wind_key,         wind_label,   DATA_FORMAT, "%3.1f m/s", DATA_DOUBLE, wind_spd,
+                                                                                                               "wind_direction", "Direction",  DATA_DOUBLE, wind_dir, NULL);
+                                       data_acquired_handler(data);
+                                       events++;
+                               }
+                               break;
+                       default:
+                if (debug_output) {
+                               fprintf(stderr,
+                                       "%s LaCrosse WS %02X-%02X: Unknown data type %d, bcd %d bin %d\n",
+                                       time_str, ws_id, sensor_id, msg_type, msg_value_bcd, msg_value_bin);
+                }
+                               events++;
+                       }
+               }
+       }
+
+       return events;
+}
+
+static char *output_fields[] = {
+               "time",
+               "model",
+               "ws_id",
+               "id",
+               "temperature_C",
+               "humidity",
+               "rainfall_mm",
+               "wind_speed_ms",
+               "gust_speed_ms",
+               "wind_direction",
+               NULL
+};
+
+r_device lacrossews = {
+ .name           = "LaCrosse WS-2310 Weather Station",
+ .modulation     = OOK_PULSE_PWM_RAW,
+ .short_limit    = 952,
+ .long_limit     = 3000,
+ .reset_limit    = 8000,
+ .json_callback  = &lacrossews_callback,
+ .disabled       = 0,
+ .demod_arg      = 0,
+ .fields = output_fields
+};