Skip to content

Commit bfba270

Browse files
authored
Add support for Emax Weather Sensor, improves Altronics X7064 sensor (#2300)
1 parent 73061ff commit bfba270

File tree

4 files changed

+241
-136
lines changed

4 files changed

+241
-136
lines changed

include/rtl_433_devices.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@
222222
DECL(tpms_renault_0435r) \
223223
DECL(fineoffset_ws80) \
224224
DECL(emos_e6016) \
225-
DECL(altronics_7064) \
225+
DECL(emax) \
226226
DECL(ant_antplus) \
227227
DECL(emos_e6016_rain) \
228228
DECL(hcs200_fsk) \

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ add_library(r_433 STATIC
4949
devices/acurite_01185m.c
5050
devices/akhan_100F14.c
5151
devices/alecto.c
52-
devices/altronics_x7064.c
5352
devices/ambient_weather.c
5453
devices/ambientweather_tx8300.c
5554
devices/ambientweather_wh31e.c
@@ -91,6 +90,7 @@ add_library(r_433 STATIC
9190
devices/efth800.c
9291
devices/elro_db286a.c
9392
devices/elv.c
93+
devices/emax.c
9494
devices/emontx.c
9595
devices/emos_e6016.c
9696
devices/emos_e6016_rain.c

src/devices/altronics_x7064.c

Lines changed: 0 additions & 134 deletions
This file was deleted.

src/devices/emax.c

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/** @file
2+
First version was for Altronics X7064 temperature and humidity sensor.
3+
Then updated by Profboc75 with Optex 990040 (Emax full Weather station rain gauge/wind speed/wind direction ... ref EM3390W6 )
4+
5+
Copyright (C) 2022 Christian W. Zuckschwerdt <[email protected]>
6+
based on protocol decoding by Thomas White
7+
8+
This program is free software; you can redistribute it and/or modify
9+
it under the terms of the GNU General Public License as published by
10+
the Free Software Foundation; either version 2 of the License, or
11+
(at your option) any later version.
12+
*/
13+
14+
#include "decoder.h"
15+
16+
/**
17+
Fuzhou Emax Electronic W6 Professional Weather Station.
18+
Rebrand and devices decoded :
19+
- Emax W6 / WEC-W6 / 3390TX W6 / EM3390W6
20+
- Altronics x7063/4
21+
- Optex 990040 / 990050 / 990051 / SM-040
22+
- Infactory FWS-1200
23+
- Newentor Q9
24+
- Otio Weather Station Pro La Surprenante 810025
25+
- Orium Pro Atlanta 13093, Helios 13123
26+
- Protmex PT3390A
27+
- Jula Marquant 014331 weather station /014332 temp hum sensor
28+
29+
S.a. issue #2000 #2299 #2326 PR #2300
30+
31+
- Likely a rebranded device, sold by Altronics
32+
- Data length is 32 bytes with a preamble of 10 bytes (33 bytes for Rain/Wind Station)
33+
34+
Data Layout:
35+
36+
// That fits nicely: aaa16e95 a3 8a ae 2d is channel 1, id 6e95, temp 38e (=910, 1 F, -17.2 C), hum 2d (=45).
37+
38+
Temp/Hum Sensor :
39+
AA AC II IB AT TA AT HH AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA SS
40+
41+
default empty = 0xAA
42+
43+
- K: (4 bit) Kind of device, = A if Temp/Hum Sensor or = 0 if Weather Rain/Wind station
44+
- C: (4 bit) channel ( = 4 for Weather Rain/wind station)
45+
- I: (12 bit) ID
46+
- B: (4 bit) BP01: battery low, pairing button, 0, 1
47+
- T: (12 bit) temperature in F, offset 900, scale 10
48+
- H: (8 bit) humidity %
49+
- A: (4 bit) fixed values of 0xA
50+
- S: (8 bit) checksum
51+
52+
Raw data:
53+
54+
FF FF AA AA AA AA AA CA CA 54
55+
AA A1 6E 95 A6 BA A5 3B AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA D4
56+
AA 00 0
57+
58+
Format string:
59+
60+
12h CH:4h ID:12h FLAGS:4b TEMP:4x4h4h4x4x4h HUM:8d 184h CHKSUM:8h 8x
61+
62+
Decoded example:
63+
64+
aaa CH:1 ID:6e9 FLAGS:0101 TEMP:6b5 HUM:059 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa CHKSUM:d4 000
65+
66+
67+
Emax EM3390W6 Rain / Wind speed / Wind Direction / Temp / Hum / UV / Lux
68+
69+
Weather Rain/Wind station : hummidity not at same byte position.
70+
AA 04 II IB 0T TT HH 0W WW 0D DD RR RR 0U LL LL 04 05 06 07 08 09 10 11 12 13 14 15 16 17 xx SS yy
71+
72+
default empty/null = 0x01 => value = 0
73+
74+
- K: (4 bit) Kind of device, = A if Temp/Hum Sensor or = 0 if Weather Rain/Wind station
75+
- C: (4 bit) channel ( = 4 for Weather Rain/wind station)
76+
- I: (12 bit) ID
77+
- B: (4 bit) BP01: battery low, pairing button, 0, 1
78+
- T: (12 bit) temperature in F, offset 900, scale 10
79+
- H: (8 bit) humidity %
80+
- R: (16) Rain
81+
- W: (12) Wind speed
82+
- D: (9 bit) Wind Direction
83+
- U: (5 bit) UV index
84+
- L: (1 + 15 bit) Lux value, if first bit = 1 , then x 10 the rest.
85+
- A: (4 bit) fixed values of 0xA
86+
- 0: (4 bit) fixed values of 0x0
87+
- xx: incremental value each tx
88+
- yy: incremental value each tx yy = xx + 1
89+
- S: (8 bit) checksum
90+
91+
Raw Data:
92+
93+
ff ff 80 00 aa aa aa aa aa ca ca 54
94+
aa 04 59 41 06 1f 42 01 01 01 81 01 16 01 01 01 04 05 06 07 08 09 10 11 12 13 14 15 16 17 9d ad 9e
95+
0000
96+
97+
Format string:
98+
99+
8h K:4h CH:4h ID:12h Flags:4b 4h Temp:12h Hum:8h 4h Wind:12h 4h Direction: 12h Rain: 16h 4h UV:4h Lux:16h 112h xx:8d CHKSUM:8h
100+
101+
Decoded example:
102+
103+
aa KD:0 CH:4 ID:594 FLAGS:0001 0 TEMP:61f (66.7F) HUM:42 (66%) Wind: 101 ( = 000 * 0.2 = 0 kmh) 0 Direction: 181 ( = 080 = 128°) Rain: 0116 ( 0015 * 0.2 = 4.2 mm) 0 UV: 1 (0 UV) Lux: 0101 (0 Lux) 04 05 ...16 17 xx:9d CHKSUM:ad yy:9e
104+
105+
*/
106+
107+
static int emax_decode(r_device *decoder, bitbuffer_t *bitbuffer)
108+
{
109+
// full preamble is ffffaaaaaaaaaacaca54
110+
uint8_t const preamble_pattern[] = {0xaa, 0xaa, 0xca, 0xca, 0x54};
111+
112+
int ret = 0;
113+
for (unsigned row = 0; row < bitbuffer->num_rows; ++row) {
114+
unsigned pos = bitbuffer_search(bitbuffer, row, 0, preamble_pattern, sizeof(preamble_pattern) * 8);
115+
116+
if (pos >= bitbuffer->bits_per_row[row]) {
117+
decoder_log(decoder, 2, __func__, "Preamble not found");
118+
ret = DECODE_ABORT_EARLY;
119+
continue;
120+
}
121+
decoder_logf(decoder, 2, __func__, "Found row: %d", row);
122+
123+
pos += sizeof(preamble_pattern) * 8;
124+
// we expect 32 bytes
125+
if (pos + 32 * 8 > bitbuffer->bits_per_row[row]) {
126+
decoder_log(decoder, 2, __func__, "Length check fail");
127+
ret = DECODE_ABORT_LENGTH;
128+
continue;
129+
}
130+
uint8_t b[32] = {0};
131+
bitbuffer_extract_bytes(bitbuffer, row, pos, b, sizeof(b) * 8);
132+
133+
// verify checksum
134+
if ((add_bytes(b, 31) & 0xff) != b[31]) {
135+
decoder_log(decoder, 2, __func__, "Checksum fail");
136+
ret = DECODE_FAIL_MIC;
137+
continue;
138+
}
139+
140+
int channel = (b[1] & 0x0f);
141+
int kind = ((b[1] & 0xf0) >> 4);
142+
int id = (b[2] << 4) | (b[3] >> 4);
143+
int battery_low = (b[3] & 0x08);
144+
int pairing = (b[3] & 0x04);
145+
146+
// depend if external temp/hum sensor or Weather rain/wind station the values are not decode the same
147+
148+
if (kind != 0) { // if not Rain/Wind ... sensor
149+
150+
int temp_raw = ((b[4] & 0x0f) << 8) | (b[5] & 0xf0) | (b[6] & 0x0f); // weird format
151+
float temp_f = (temp_raw - 900) * 0.1f;
152+
int humidity = b[7];
153+
154+
/* clang-format off */
155+
data_t *data = data_make(
156+
"model", "", DATA_STRING, "Altronics-X7064",
157+
"id", "", DATA_FORMAT, "%03x", DATA_INT, id,
158+
"channel", "Channel", DATA_INT, channel,
159+
"battery_ok", "Battery_OK", DATA_INT, !battery_low,
160+
"temperature_F", "Temperature_F", DATA_FORMAT, "%.1f", DATA_DOUBLE, temp_f,
161+
"humidity", "Humidity", DATA_FORMAT, "%u", DATA_INT, humidity,
162+
"pairing", "Pairing?", DATA_COND, pairing, DATA_INT, !!pairing,
163+
"mic", "Integrity", DATA_STRING, "CHECKSUM",
164+
NULL);
165+
/* clang-format on */
166+
167+
decoder_output_data(decoder, data);
168+
return 1;
169+
}
170+
else { // if Rain/Wind sensor
171+
172+
int temp_raw = ((b[4] & 0x0f) << 8) | (b[5]); // weird format
173+
float temp_f = (temp_raw - 900) * 0.1f;
174+
int humidity = b[6];
175+
int wind_raw = ((b[7] - 1) << 8 ) | (b[8] -1); // all default is 01 so need to remove 1 from all bytes.
176+
float speed_kmh = wind_raw * 0.2f;
177+
int direction_deg = (((b[9] & 0x0f) - 1) << 8) | (b[10] - 1);
178+
int rain_raw = ((b[11] - 1) << 8 ) | (b[12] -1);
179+
float rain_mm = rain_raw * 0.2f;
180+
int uv_index = (b[13] & 0x1f) - 1;
181+
int lux_multi = (((b[14] - 1) & 0x80) >> 7);
182+
int light_lux = ((b[14] & 0x7f) - 1) << 8 | (b[15] - 1);
183+
if (lux_multi == 1) {
184+
light_lux = light_lux * 10;
185+
}
186+
187+
/* clang-format off */
188+
data_t *data = data_make(
189+
"model", "", DATA_STRING, "Emax-W6",
190+
"id", "", DATA_FORMAT, "%03x", DATA_INT, id,
191+
"channel", "Channel", DATA_INT, channel,
192+
"battery_ok", "Battery_OK", DATA_INT, !battery_low,
193+
"temperature_F", "Temperature_F", DATA_FORMAT, "%.1f", DATA_DOUBLE, temp_f,
194+
"humidity", "Humidity", DATA_FORMAT, "%u", DATA_INT, humidity,
195+
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh,
196+
"wind_dir_deg", "Wind Direction", DATA_INT, direction_deg,
197+
"rain_mm", "Total rainfall", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm,
198+
"uv", "UV Index", DATA_FORMAT, "%u", DATA_INT, uv_index,
199+
"light_lux", "Lux", DATA_FORMAT, "%u", DATA_INT, light_lux,
200+
"pairing", "Pairing?", DATA_COND, pairing, DATA_INT, !!pairing,
201+
"mic", "Integrity", DATA_STRING, "CHECKSUM",
202+
NULL);
203+
/* clang-format on */
204+
205+
decoder_output_data(decoder, data);
206+
return 1;
207+
208+
}
209+
}
210+
return ret;
211+
}
212+
213+
static char *output_fields[] = {
214+
"model",
215+
"id",
216+
"channel",
217+
"battery_ok",
218+
"temperature_F",
219+
"humidity",
220+
"wind_avg_km_h",
221+
"rain_mm",
222+
"wind_dir_deg",
223+
"uv",
224+
"light_lux",
225+
"pairing",
226+
"mic",
227+
NULL,
228+
};
229+
230+
r_device emax = {
231+
.name = "Emax W6, rebrand Altronics x7063/4, Optex 990040/50/51, Orium 13093/13123, Infactory FWS-1200, Newentor Q9, Otio 810025, Protmex PT3390A, Jula Marquant 014331/32, Weather Station or temperature/humidity sensor",
232+
.modulation = FSK_PULSE_PCM,
233+
.short_width = 90,
234+
.long_width = 90,
235+
.gap_limit = 1200,
236+
.reset_limit = 9000,
237+
.decode_fn = &emax_decode,
238+
.fields = output_fields,
239+
};

0 commit comments

Comments
 (0)