Skip to content

Commit cbeb65c

Browse files
committed
1 parent dda9b40 commit cbeb65c

File tree

5 files changed

+386
-20
lines changed

5 files changed

+386
-20
lines changed

base_pack/weather_station/application.fam

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ App(
77
requires=["gui"],
88
stack_size=4 * 1024,
99
fap_description="Receive weather data from a wide range of supported Sub-1GHz remote sensor",
10-
fap_version="1.2",
10+
fap_version="1.3",
1111
fap_icon="weather_station_10px.png",
1212
fap_category="Sub-GHz",
1313
fap_icon_assets="images",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
#include "kedsum_th.h"
2+
3+
#define TAG "WSProtocolKedsumTH"
4+
5+
/*
6+
* Help
7+
* https://github.com/merbanan/rtl_433/blob/master/src/devices/kedsum.c
8+
*
9+
* Frame structure:
10+
*
11+
* Byte: 0 1 2 3 4
12+
* Nibble: 1 2 3 4 5 6 7 8 9 10
13+
* Type: 00 IIIIIIII BBCC++++ ttttTTTT hhhhHHHH FFFFXXXX
14+
*
15+
* - I: unique id. changes on powercycle
16+
* - B: Battery state 10 = Ok, 01 = weak, 00 = bad
17+
* - C: channel, 00 = ch1, 10=ch3
18+
* - + low temp nibble
19+
* - t: med temp nibble
20+
* - T: high temp nibble
21+
* - h: humidity low nibble
22+
* - H: humidity high nibble
23+
* - F: flags
24+
* - X: CRC-4 poly 0x3 init 0x0 xor last 4 bits
25+
*/
26+
27+
static const SubGhzBlockConst ws_protocol_kedsum_th_const = {
28+
.te_short = 500,
29+
.te_long = 2000,
30+
.te_delta = 150,
31+
.min_count_bit_for_found = 42,
32+
};
33+
34+
struct WSProtocolDecoderKedsumTH {
35+
SubGhzProtocolDecoderBase base;
36+
37+
SubGhzBlockDecoder decoder;
38+
WSBlockGeneric generic;
39+
40+
uint16_t header_count;
41+
};
42+
43+
struct WSProtocolEncoderKedsumTH {
44+
SubGhzProtocolEncoderBase base;
45+
46+
SubGhzProtocolBlockEncoder encoder;
47+
WSBlockGeneric generic;
48+
};
49+
50+
typedef enum {
51+
KedsumTHDecoderStepReset = 0,
52+
KedsumTHDecoderStepCheckPreambule,
53+
KedsumTHDecoderStepSaveDuration,
54+
KedsumTHDecoderStepCheckDuration,
55+
} KedsumTHDecoderStep;
56+
57+
const SubGhzProtocolDecoder ws_protocol_kedsum_th_decoder = {
58+
.alloc = ws_protocol_decoder_kedsum_th_alloc,
59+
.free = ws_protocol_decoder_kedsum_th_free,
60+
61+
.feed = ws_protocol_decoder_kedsum_th_feed,
62+
.reset = ws_protocol_decoder_kedsum_th_reset,
63+
64+
.get_hash_data = ws_protocol_decoder_kedsum_th_get_hash_data,
65+
.serialize = ws_protocol_decoder_kedsum_th_serialize,
66+
.deserialize = ws_protocol_decoder_kedsum_th_deserialize,
67+
.get_string = ws_protocol_decoder_kedsum_th_get_string,
68+
};
69+
70+
const SubGhzProtocolEncoder ws_protocol_kedsum_th_encoder = {
71+
.alloc = NULL,
72+
.free = NULL,
73+
74+
.deserialize = NULL,
75+
.stop = NULL,
76+
.yield = NULL,
77+
};
78+
79+
const SubGhzProtocol ws_protocol_kedsum_th = {
80+
.name = WS_PROTOCOL_KEDSUM_TH_NAME,
81+
.type = SubGhzProtocolWeatherStation,
82+
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
83+
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
84+
85+
.decoder = &ws_protocol_kedsum_th_decoder,
86+
.encoder = &ws_protocol_kedsum_th_encoder,
87+
};
88+
89+
void* ws_protocol_decoder_kedsum_th_alloc(SubGhzEnvironment* environment) {
90+
UNUSED(environment);
91+
WSProtocolDecoderKedsumTH* instance = malloc(sizeof(WSProtocolDecoderKedsumTH));
92+
instance->base.protocol = &ws_protocol_kedsum_th;
93+
instance->generic.protocol_name = instance->base.protocol->name;
94+
return instance;
95+
}
96+
97+
void ws_protocol_decoder_kedsum_th_free(void* context) {
98+
furi_assert(context);
99+
WSProtocolDecoderKedsumTH* instance = context;
100+
free(instance);
101+
}
102+
103+
void ws_protocol_decoder_kedsum_th_reset(void* context) {
104+
furi_assert(context);
105+
WSProtocolDecoderKedsumTH* instance = context;
106+
instance->decoder.parser_step = KedsumTHDecoderStepReset;
107+
}
108+
109+
static bool ws_protocol_kedsum_th_check_crc(WSProtocolDecoderKedsumTH* instance) {
110+
uint8_t msg[] = {
111+
instance->decoder.decode_data >> 32,
112+
instance->decoder.decode_data >> 24,
113+
instance->decoder.decode_data >> 16,
114+
instance->decoder.decode_data >> 8,
115+
instance->decoder.decode_data};
116+
117+
uint8_t crc =
118+
subghz_protocol_blocks_crc4(msg, 4, 0x03, 0); // CRC-4 poly 0x3 init 0x0 xor last 4 bits
119+
crc ^= msg[4] >> 4; // last nibble is only XORed
120+
return (crc == (msg[4] & 0x0F));
121+
}
122+
123+
/**
124+
* Analysis of received data
125+
* @param instance Pointer to a WSBlockGeneric* instance
126+
*/
127+
static void ws_protocol_kedsum_th_remote_controller(WSBlockGeneric* instance) {
128+
instance->id = instance->data >> 32;
129+
if((instance->data >> 30) & 0x3) {
130+
instance->battery_low = 0;
131+
} else {
132+
instance->battery_low = 1;
133+
}
134+
instance->channel = ((instance->data >> 28) & 0x3) + 1;
135+
instance->btn = WS_NO_BTN;
136+
uint16_t temp_raw = ((instance->data >> 16) & 0x0f) << 8 |
137+
((instance->data >> 20) & 0x0f) << 4 | ((instance->data >> 24) & 0x0f);
138+
instance->temp = locale_fahrenheit_to_celsius(((float)temp_raw - 900.0f) / 10.0f);
139+
instance->humidity = ((instance->data >> 8) & 0x0f) << 4 | ((instance->data >> 12) & 0x0f);
140+
}
141+
142+
void ws_protocol_decoder_kedsum_th_feed(void* context, bool level, uint32_t duration) {
143+
furi_assert(context);
144+
WSProtocolDecoderKedsumTH* instance = context;
145+
146+
switch(instance->decoder.parser_step) {
147+
case KedsumTHDecoderStepReset:
148+
if((level) && (DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_short) <
149+
ws_protocol_kedsum_th_const.te_delta)) {
150+
instance->decoder.parser_step = KedsumTHDecoderStepCheckPreambule;
151+
instance->decoder.te_last = duration;
152+
instance->header_count = 0;
153+
}
154+
break;
155+
156+
case KedsumTHDecoderStepCheckPreambule:
157+
if(level) {
158+
instance->decoder.te_last = duration;
159+
} else {
160+
if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) <
161+
ws_protocol_kedsum_th_const.te_delta) &&
162+
(DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 4) <
163+
ws_protocol_kedsum_th_const.te_delta * 4)) {
164+
//Found preambule
165+
instance->header_count++;
166+
} else if(
167+
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) <
168+
ws_protocol_kedsum_th_const.te_delta) &&
169+
(duration < (ws_protocol_kedsum_th_const.te_long * 2 +
170+
ws_protocol_kedsum_th_const.te_delta * 2))) {
171+
//Found syncPrefix
172+
if(instance->header_count > 0) {
173+
instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration;
174+
instance->decoder.decode_data = 0;
175+
instance->decoder.decode_count_bit = 0;
176+
if((DURATION_DIFF(
177+
instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) <
178+
ws_protocol_kedsum_th_const.te_delta) &&
179+
(DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long) <
180+
ws_protocol_kedsum_th_const.te_delta * 2)) {
181+
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
182+
instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration;
183+
} else if(
184+
(DURATION_DIFF(
185+
instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) <
186+
ws_protocol_kedsum_th_const.te_delta) &&
187+
(DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 2) <
188+
ws_protocol_kedsum_th_const.te_delta * 4)) {
189+
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
190+
instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration;
191+
} else {
192+
instance->decoder.parser_step = KedsumTHDecoderStepReset;
193+
}
194+
}
195+
} else {
196+
instance->decoder.parser_step = KedsumTHDecoderStepReset;
197+
}
198+
}
199+
break;
200+
201+
case KedsumTHDecoderStepSaveDuration:
202+
if(level) {
203+
instance->decoder.te_last = duration;
204+
instance->decoder.parser_step = KedsumTHDecoderStepCheckDuration;
205+
} else {
206+
instance->decoder.parser_step = KedsumTHDecoderStepReset;
207+
}
208+
break;
209+
210+
case KedsumTHDecoderStepCheckDuration:
211+
if(!level) {
212+
if(DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 4) <
213+
ws_protocol_kedsum_th_const.te_delta * 4) {
214+
//Found syncPostfix
215+
if((instance->decoder.decode_count_bit ==
216+
ws_protocol_kedsum_th_const.min_count_bit_for_found) &&
217+
ws_protocol_kedsum_th_check_crc(instance)) {
218+
instance->generic.data = instance->decoder.decode_data;
219+
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
220+
ws_protocol_kedsum_th_remote_controller(&instance->generic);
221+
if(instance->base.callback)
222+
instance->base.callback(&instance->base, instance->base.context);
223+
}
224+
instance->decoder.decode_data = 0;
225+
instance->decoder.decode_count_bit = 0;
226+
instance->decoder.parser_step = KedsumTHDecoderStepReset;
227+
break;
228+
} else if(
229+
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) <
230+
ws_protocol_kedsum_th_const.te_delta) &&
231+
(DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long) <
232+
ws_protocol_kedsum_th_const.te_delta * 2)) {
233+
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
234+
instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration;
235+
} else if(
236+
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) <
237+
ws_protocol_kedsum_th_const.te_delta) &&
238+
(DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 2) <
239+
ws_protocol_kedsum_th_const.te_delta * 4)) {
240+
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
241+
instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration;
242+
} else {
243+
instance->decoder.parser_step = KedsumTHDecoderStepReset;
244+
}
245+
} else {
246+
instance->decoder.parser_step = KedsumTHDecoderStepReset;
247+
}
248+
break;
249+
}
250+
}
251+
252+
uint8_t ws_protocol_decoder_kedsum_th_get_hash_data(void* context) {
253+
furi_assert(context);
254+
WSProtocolDecoderKedsumTH* instance = context;
255+
return subghz_protocol_blocks_get_hash_data(
256+
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
257+
}
258+
259+
SubGhzProtocolStatus ws_protocol_decoder_kedsum_th_serialize(
260+
void* context,
261+
FlipperFormat* flipper_format,
262+
SubGhzRadioPreset* preset) {
263+
furi_assert(context);
264+
WSProtocolDecoderKedsumTH* instance = context;
265+
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
266+
}
267+
268+
SubGhzProtocolStatus
269+
ws_protocol_decoder_kedsum_th_deserialize(void* context, FlipperFormat* flipper_format) {
270+
furi_assert(context);
271+
WSProtocolDecoderKedsumTH* instance = context;
272+
return ws_block_generic_deserialize_check_count_bit(
273+
&instance->generic, flipper_format, ws_protocol_kedsum_th_const.min_count_bit_for_found);
274+
}
275+
276+
void ws_protocol_decoder_kedsum_th_get_string(void* context, FuriString* output) {
277+
furi_assert(context);
278+
WSProtocolDecoderKedsumTH* instance = context;
279+
furi_string_printf(
280+
output,
281+
"%s %dbit\r\n"
282+
"Key:0x%lX%08lX\r\n"
283+
"Sn:0x%lX Ch:%d Bat:%d\r\n"
284+
"Temp:%3.1f C Hum:%d%%",
285+
instance->generic.protocol_name,
286+
instance->generic.data_count_bit,
287+
(uint32_t)(instance->generic.data >> 32),
288+
(uint32_t)(instance->generic.data),
289+
instance->generic.id,
290+
instance->generic.channel,
291+
instance->generic.battery_low,
292+
(double)instance->generic.temp,
293+
instance->generic.humidity);
294+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#pragma once
2+
3+
#include <lib/subghz/protocols/base.h>
4+
5+
#include <lib/subghz/blocks/const.h>
6+
#include <lib/subghz/blocks/decoder.h>
7+
#include <lib/subghz/blocks/encoder.h>
8+
#include "ws_generic.h"
9+
#include <lib/subghz/blocks/math.h>
10+
11+
#define WS_PROTOCOL_KEDSUM_TH_NAME "Kedsum-TH"
12+
13+
typedef struct WSProtocolDecoderKedsumTH WSProtocolDecoderKedsumTH;
14+
typedef struct WSProtocolEncoderKedsumTH WSProtocolEncoderKedsumTH;
15+
16+
extern const SubGhzProtocolDecoder ws_protocol_kedsum_th_decoder;
17+
extern const SubGhzProtocolEncoder ws_protocol_kedsum_th_encoder;
18+
extern const SubGhzProtocol ws_protocol_kedsum_th;
19+
20+
/**
21+
* Allocate WSProtocolDecoderKedsumTH.
22+
* @param environment Pointer to a SubGhzEnvironment instance
23+
* @return WSProtocolDecoderKedsumTH* pointer to a WSProtocolDecoderKedsumTH instance
24+
*/
25+
void* ws_protocol_decoder_kedsum_th_alloc(SubGhzEnvironment* environment);
26+
27+
/**
28+
* Free WSProtocolDecoderKedsumTH.
29+
* @param context Pointer to a WSProtocolDecoderKedsumTH instance
30+
*/
31+
void ws_protocol_decoder_kedsum_th_free(void* context);
32+
33+
/**
34+
* Reset decoder WSProtocolDecoderKedsumTH.
35+
* @param context Pointer to a WSProtocolDecoderKedsumTH instance
36+
*/
37+
void ws_protocol_decoder_kedsum_th_reset(void* context);
38+
39+
/**
40+
* Parse a raw sequence of levels and durations received from the air.
41+
* @param context Pointer to a WSProtocolDecoderKedsumTH instance
42+
* @param level Signal level true-high false-low
43+
* @param duration Duration of this level in, us
44+
*/
45+
void ws_protocol_decoder_kedsum_th_feed(void* context, bool level, uint32_t duration);
46+
47+
/**
48+
* Getting the hash sum of the last randomly received parcel.
49+
* @param context Pointer to a WSProtocolDecoderKedsumTH instance
50+
* @return hash Hash sum
51+
*/
52+
uint8_t ws_protocol_decoder_kedsum_th_get_hash_data(void* context);
53+
54+
/**
55+
* Serialize data WSProtocolDecoderKedsumTH.
56+
* @param context Pointer to a WSProtocolDecoderKedsumTH instance
57+
* @param flipper_format Pointer to a FlipperFormat instance
58+
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
59+
* @return status
60+
*/
61+
SubGhzProtocolStatus ws_protocol_decoder_kedsum_th_serialize(
62+
void* context,
63+
FlipperFormat* flipper_format,
64+
SubGhzRadioPreset* preset);
65+
66+
/**
67+
* Deserialize data WSProtocolDecoderKedsumTH.
68+
* @param context Pointer to a WSProtocolDecoderKedsumTH instance
69+
* @param flipper_format Pointer to a FlipperFormat instance
70+
* @return status
71+
*/
72+
SubGhzProtocolStatus
73+
ws_protocol_decoder_kedsum_th_deserialize(void* context, FlipperFormat* flipper_format);
74+
75+
/**
76+
* Getting a textual representation of the received data.
77+
* @param context Pointer to a WSProtocolDecoderKedsumTH instance
78+
* @param output Resulting text
79+
*/
80+
void ws_protocol_decoder_kedsum_th_get_string(void* context, FuriString* output);

0 commit comments

Comments
 (0)