1 /* LaCrosse WS-2310 433 Mhz Weather Station
2 Packet Format is 53 bits/ 13 nibbles
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
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, ?=?
17 48-51 12 - Check Sum = Nibble sum of nibbles 0-11
24 #define LACROSSE_WS_BITLEN 52
26 static int lacrossews_detect(uint8_t *pRow, uint8_t *msg_nybbles, int16_t rowlen) {
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];
32 // Weather Station 2310 Packets
33 if (rowlen == LACROSSE_WS_BITLEN && pRow[0] == 0x09) {
35 for (i = 0; i < (LACROSSE_WS_BITLEN / 4); i++) {
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++) {
43 rbit_no = 7 - (i % 8);
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))
52 for (i = 0; i < 12; i++) {
53 checksum = (checksum + msg_nybbles[i]) & 0x0F;
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])
64 local_time_str(0, time_str);
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]);
72 fprintf(stderr, "\n");
81 static int lacrossews_callback(bitbuffer_t *bitbuffer) {
82 bitrow_t *bb = bitbuffer->bb;
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;
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])) {
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];
107 local_time_str(0, time_str);
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],
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,
126 data_acquired_handler(data);
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);
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,
142 data_acquired_handler(data);
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);
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) {
165 fprintf(stderr, "%s LaCrosse WS %02X-%02X: %s Not Connected\n",
166 time_str, ws_id, sensor_id, msg_type == 3 ? "Wind":"Gust");
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);
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);
195 static char *output_fields[] = {
209 r_device lacrossews = {
210 .name = "LaCrosse WS-2310 Weather Station",
211 .modulation = OOK_PULSE_PWM_RAW,
215 .json_callback = &lacrossews_callback,
218 .fields = output_fields