From 7e09f4b774cf6fa1bce3b3a70f4f4e5dbfaed749 Mon Sep 17 00:00:00 2001 From: m7i-org <64315540+m7i-org@users.noreply.github.com> Date: Sun, 20 Oct 2024 01:27:03 -0400 Subject: [PATCH] Sub-GHz: Added Vauno-EN8822 weather station (#262) * feat(subghz): Added Vauno-EN8822 weather station * Update changelog --------- Co-authored-by: Willy-JL <49810075+Willy-JL@users.noreply.github.com> --- CHANGELOG.md | 1 + .../unit_tests/subghz/vauno_en8822c.sub | 16 ++ .../unit_tests/subghz/vauno_en8822c_raw.sub | 6 + .../unit_tests/tests/subghz/subghz_test.c | 8 + lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + lib/subghz/protocols/vauno_en8822c.c | 258 ++++++++++++++++++ lib/subghz/protocols/vauno_en8822c.h | 80 ++++++ 8 files changed, 371 insertions(+) create mode 100644 applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c.sub create mode 100644 applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c_raw.sub create mode 100644 lib/subghz/protocols/vauno_en8822c.c create mode 100644 lib/subghz/protocols/vauno_en8822c.h diff --git a/CHANGELOG.md b/CHANGELOG.md index bdeb6e0c7..23fca96d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - Sub-GHz: - Show satellites count with an icon (#215 by @m7i-org) - Add Bresser 3CH weather station protocol (#217 #245 by @m7i-org) + - Add Vauno-EN8822 weather station protocol (#262 by @m7i-org) - UL: Add Marantec24 protocol (static 24 bit) with add manually (by @xMasterX) - UL: Add GangQi protocol (static 34 bit) with button parsing and add manually (by @xMasterX & @Skorpionm) - UL: Add Hollarm protocol (static 42 bit) with button parsing and add manually (by @xMasterX & @Skorpionm) diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c.sub new file mode 100644 index 000000000..28751fe2f --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c.sub @@ -0,0 +1,16 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Lat: 0.000000 +Lon: 0.000000 +Protocol: Vauno-EN8822C +Id: 64 +Bit: 42 +Data: 00 00 01 00 02 16 88 1D +Batt: 0 +Hum: 81 +Ts: 1728836876 +Ch: 0 +Btn: 255 +Temp: 13.300000 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c_raw.sub new file mode 100644 index 000000000..86e51ebe7 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/vauno_en8822c_raw.sub @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Protocol: RAW +RAW_Data: 588 -4136 546 -4150 578 -4158 546 -2096 578 -2092 592 -2068 592 -4126 578 -4144 570 -2064 580 -2092 594 -2102 564 -2168 582 -2078 582 -2112 544 -2102 572 -2096 572 -4130 574 -2114 552 -2090 594 -2064 590 -2104 560 -2094 566 -2090 564 -2092 580 -2098 566 -2096 570 -2164 578 -2090 546 -4140 578 -2104 568 -4138 578 -2078 550 -2086 594 -9092 592 -61848 200 -17582 66 -5072 130 -11038 100 -60 164 -8288 166 -2374 302 -94 166 -300 66 -18706 96 -25730 192 -66 158 -29578 64 -99962 128 -99858 96 -99908 130 -9892 164 -96 98 -89580 162 -106 76 -11000 196 -88450 506 -3900 522 -1900 522 -3878 500 -1936 482 -1958 488 -1938 500 -3906 462 -1966 482 -1950 500 -1924 502 -1958 474 -1940 480 -1960 520 -3858 512 -3878 524 -3888 464 -1968 482 -3910 478 -7792 518 -1912 502 -7802 478 -1962 476 -3902 510 -1938 500 -1938 480 -1954 504 -1936 488 -1954 476 -1960 476 -1948 478 -1964 466 -1994 446 -1970 464 -1972 480 -1954 474 -1954 482 -1970 474 -3934 446 -1958 466 -1978 480 -1956 468 -1960 472 -3900 516 -1948 478 -3908 494 -3878 508 -1938 502 -3896 482 -1950 480 -1938 514 -1924 516 -3884 484 -1940 502 -1942 480 -1958 502 -1930 506 -1938 480 -1934 516 -3860 510 -3912 488 -3888 504 -1940 486 -3876 506 -7800 526 -1928 476 -7790 512 -1942 508 -3876 486 -1918 518 -1962 482 -1954 482 -1930 498 -1958 470 -1936 490 -1962 484 -1960 476 -1932 520 -1938 490 -1928 508 -1952 478 -1940 480 -1958 500 -3892 498 -1930 484 -1960 474 -1936 496 -1934 514 -3890 508 -1906 522 -3882 478 -3916 484 -1932 506 -3888 512 -1928 502 -1928 502 -1938 492 -3920 490 -1924 510 -1928 498 -1934 480 -1954 488 -1972 462 -1964 478 -3896 484 -3902 476 -3912 484 -1944 476 -3916 498 -7784 510 -1930 504 -7796 498 -1936 518 -3864 492 -1964 482 -1950 474 -1962 470 -1954 490 -1960 478 -1952 482 -1934 520 -1934 482 -1948 482 -1944 486 -1970 448 -1976 500 -1936 480 -1964 474 -3902 484 -1960 480 -1952 482 -1962 474 -1950 486 -3914 448 -1984 450 -3916 520 -3872 490 -1942 480 -3938 448 -1970 464 -1972 482 -1964 484 -3910 484 -1938 476 -1950 516 -1926 470 -1970 514 -1906 500 -1950 506 -3876 510 -3850 524 -3896 486 -1934 518 -3870 506 -7798 512 -1912 512 -7798 516 -1918 478 -3924 458 -1956 472 -1954 506 -1936 512 -1930 474 -1968 482 -1930 504 -1940 486 -1958 482 -1960 486 -1936 486 -1936 494 -1934 516 -1912 510 -1962 474 -3882 504 -1956 478 -1938 510 -1926 500 -1924 514 -3920 450 -1970 478 -3904 488 -3896 502 -1942 486 -3912 476 -1926 508 -1940 486 -1962 474 -3920 484 -1930 512 -1944 470 -1970 470 -1948 502 -1936 478 -1962 482 -3914 484 -3878 502 -3906 472 -1968 488 -3894 498 -7774 496 -1948 diff --git a/applications/debug/unit_tests/tests/subghz/subghz_test.c b/applications/debug/unit_tests/tests/subghz/subghz_test.c index 8f1471089..f2e48e079 100644 --- a/applications/debug/unit_tests/tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/tests/subghz/subghz_test.c @@ -691,6 +691,13 @@ MU_TEST(subghz_decoder_bresser_3ch_v0_test) { "Test decoder " WS_PROTOCOL_BRESSER_3CH_NAME " v0 error\r\n"); } +MU_TEST(subghz_decoder_vauno_en8822c_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/vauno_en8822c.sub"), WS_PROTOCOL_VAUNO_EN8822C_NAME), + "Test decoder " WS_PROTOCOL_VAUNO_EN8822C_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -916,6 +923,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_solight_te44_test); MU_RUN_TEST(subghz_decoder_bresser_3ch_v1_test); MU_RUN_TEST(subghz_decoder_bresser_3ch_v0_test); + MU_RUN_TEST(subghz_decoder_vauno_en8822c_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index c6a1c08f5..3ebb8b4f7 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -67,6 +67,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &ws_protocol_kedsum_th, &ws_protocol_emose601x, &ws_protocol_acurite_5n1, + &ws_protocol_vauno_en8822c, &subghz_protocol_pocsag, &tpms_protocol_schrader_gg4, &subghz_protocol_bin_raw, diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 48e72cc44..8fbbc9a82 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -68,6 +68,7 @@ #include "acurite_5n1.h" #include "solight_te44.h" #include "bresser_3ch.h" +#include "vauno_en8822c.h" #include "pocsag.h" #include "schrader_gg4.h" #include "bin_raw.h" diff --git a/lib/subghz/protocols/vauno_en8822c.c b/lib/subghz/protocols/vauno_en8822c.c new file mode 100644 index 000000000..e80ec2f84 --- /dev/null +++ b/lib/subghz/protocols/vauno_en8822c.c @@ -0,0 +1,258 @@ +#include "vauno_en8822c.h" +#include "furi/core/log.h" +#define TAG "WSProtocolVaunoEN8822C" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/vauno_en8822c.c + * + * Vauno EN8822C sensor on 433.92MHz. + * + * Largely the same as Esperanza EWS, s3318p. + * @sa esperanza_ews.c s3318p.c + + * List of known supported devices: + * - Vauno EN8822C-1 + * - FUZHOU ESUN ELECTRONIC outdoor T21 sensor + * + * Frame structure (42 bits): + * + * Byte: 0 1 2 3 4 + * Nibble: 1 2 3 4 5 6 7 8 9 10 11 + * Type: IIIIIIII B?CCTTTT TTTTTTTT HHHHHHHF FFFBXXXX XX + * + * - I: Random device ID + * - C: Channel (1-3) + * - T: Temperature (Little-endian) + * - H: Humidity (Little-endian) + * - F: Flags (unknown) + * - B: Battery (1=low voltage ~<2.5V) + * - X: Checksum (6 bit nibble sum) + * + * Sample Data: + * + * [00] {42} af 0f a2 7c 01 c0 : 10101111 00001111 10100010 01111100 00000001 11 + * + * - Sensor ID = 175 = 0xaf + * - Channel = 0 + * - temp = -93 = 0x111110100010 + * - TemperatureC = -9.3 + * - hum = 62% = 0x0111110 + * + * Copyright (C) 2022 Jamie Barron + * + * @m7i-org - because the ether is wavy + * + */ + +static const SubGhzBlockConst ws_protocol_vauno_en8822c_const = { + .te_short = 500, + .te_long = 1940, + .te_delta = 150, + .min_count_bit_for_found = 42, +}; + +struct WSProtocolDecoderVaunoEN8822C { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderVaunoEN8822C { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +const SubGhzProtocolDecoder ws_protocol_vauno_en8822c_decoder = { + .alloc = ws_protocol_decoder_vauno_en8822c_alloc, + .free = ws_protocol_decoder_vauno_en8822c_free, + + .feed = ws_protocol_decoder_vauno_en8822c_feed, + .reset = ws_protocol_decoder_vauno_en8822c_reset, + + .get_hash_data = NULL, + .get_hash_data_long = ws_protocol_decoder_vauno_en8822c_get_hash_data, + .serialize = ws_protocol_decoder_vauno_en8822c_serialize, + .deserialize = ws_protocol_decoder_vauno_en8822c_deserialize, + .get_string = ws_protocol_decoder_vauno_en8822c_get_string, + .get_string_brief = NULL, +}; + +const SubGhzProtocolEncoder ws_protocol_vauno_en8822c_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_vauno_en8822c = { + .name = WS_PROTOCOL_VAUNO_EN8822C_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save, + .filter = SubGhzProtocolFilter_Weather, + .decoder = &ws_protocol_vauno_en8822c_decoder, + .encoder = &ws_protocol_vauno_en8822c_encoder, +}; + +typedef enum { + VaunoEN8822CDecoderStepReset = 0, + VaunoEN8822CDecoderStepSaveDuration, + VaunoEN8822CDecoderStepCheckDuration, +} VaunoEN8822CDecoderStep; + +void* ws_protocol_decoder_vauno_en8822c_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderVaunoEN8822C* instance = malloc(sizeof(WSProtocolDecoderVaunoEN8822C)); + instance->base.protocol = &ws_protocol_vauno_en8822c; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_vauno_en8822c_free(void* context) { + furi_assert(context); + WSProtocolDecoderVaunoEN8822C* instance = context; + free(instance); +} + +void ws_protocol_decoder_vauno_en8822c_reset(void* context) { + furi_assert(context); + WSProtocolDecoderVaunoEN8822C* instance = context; + instance->decoder.parser_step = VaunoEN8822CDecoderStepReset; +} + +static bool ws_protocol_vauno_en8822c_check(WSProtocolDecoderVaunoEN8822C* instance) { + if(!instance->decoder.decode_data) return false; + + // The sum of all nibbles should match the last 6 bits + uint8_t sum = 0; + for(uint8_t i = 6; i <= 38; i += 4) { + sum += ((instance->decoder.decode_data >> i) & 0x0f); + } + + return sum != 0 && (sum & 0x3f) == (instance->decoder.decode_data & 0x3f); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_vauno_en8822c_extract_data(WSBlockGeneric* instance) { + instance->id = (instance->data >> 34) & 0xff; + instance->battery_low = (instance->data >> 33) & 0x01; + instance->channel = ((instance->data >> 30) & 0x03); + + int16_t temp = (instance->data >> 18) & 0x0fff; + /* Handle signed data */ + if(temp & 0x0800) { + temp |= 0xf000; + } + instance->temp = (float)temp / 10.0; + + instance->humidity = (instance->data >> 11) & 0x7f; + instance->btn = WS_NO_BTN; +} + +void ws_protocol_decoder_vauno_en8822c_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderVaunoEN8822C* instance = context; + + switch(instance->decoder.parser_step) { + case VaunoEN8822CDecoderStepReset: + if((!level) && DURATION_DIFF(duration, ws_protocol_vauno_en8822c_const.te_long * 4) < + ws_protocol_vauno_en8822c_const.te_delta) { + instance->decoder.parser_step = VaunoEN8822CDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case VaunoEN8822CDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = VaunoEN8822CDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = VaunoEN8822CDecoderStepReset; + } + break; + + case VaunoEN8822CDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_vauno_en8822c_const.te_short) < + ws_protocol_vauno_en8822c_const.te_delta) { + if(DURATION_DIFF(duration, ws_protocol_vauno_en8822c_const.te_long * 2) < + ws_protocol_vauno_en8822c_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = VaunoEN8822CDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_vauno_en8822c_const.te_long) < + ws_protocol_vauno_en8822c_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = VaunoEN8822CDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_vauno_en8822c_const.te_long * 4) < + ws_protocol_vauno_en8822c_const.te_delta) { + instance->decoder.parser_step = VaunoEN8822CDecoderStepReset; + if(instance->decoder.decode_count_bit == + ws_protocol_vauno_en8822c_const.min_count_bit_for_found && + ws_protocol_vauno_en8822c_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_vauno_en8822c_extract_data(&instance->generic); + + if(instance->base.callback) { + instance->base.callback(&instance->base, instance->base.context); + } + } else if(instance->decoder.decode_count_bit == 1) { + instance->decoder.parser_step = VaunoEN8822CDecoderStepSaveDuration; + } + + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else + instance->decoder.parser_step = VaunoEN8822CDecoderStepReset; + } else + instance->decoder.parser_step = VaunoEN8822CDecoderStepReset; + } else + instance->decoder.parser_step = VaunoEN8822CDecoderStepReset; + break; + } +} + +uint32_t ws_protocol_decoder_vauno_en8822c_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderVaunoEN8822C* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_vauno_en8822c_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderVaunoEN8822C* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_vauno_en8822c_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderVaunoEN8822C* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_vauno_en8822c_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_vauno_en8822c_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderVaunoEN8822C* instance = context; + ws_block_generic_get_string(&instance->generic, output); +} diff --git a/lib/subghz/protocols/vauno_en8822c.h b/lib/subghz/protocols/vauno_en8822c.h new file mode 100644 index 000000000..5c61d32d6 --- /dev/null +++ b/lib/subghz/protocols/vauno_en8822c.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_VAUNO_EN8822C_NAME "Vauno-EN8822C" + +typedef struct WSProtocolDecoderVaunoEN8822C WSProtocolDecoderVaunoEN8822C; +typedef struct WSProtocolEncoderVaunoEN8822C WSProtocolEncoderVaunoEN8822C; + +extern const SubGhzProtocolDecoder ws_protocol_vauno_en8822c_decoder; +extern const SubGhzProtocolEncoder ws_protocol_vauno_en8822c_encoder; +extern const SubGhzProtocol ws_protocol_vauno_en8822c; + +/** + * Allocate WSProtocolDecoderVaunoEN8822C. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderVaunoEN8822C* pointer to a WSProtocolDecoderVaunoEN8822C instance + */ +void* ws_protocol_decoder_vauno_en8822c_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderVaunoEN8822C. + * @param context Pointer to a WSProtocolDecoderVaunoEN8822C instance + */ +void ws_protocol_decoder_vauno_en8822c_free(void* context); + +/** + * Reset decoder WSProtocolDecoderVaunoEN8822C. + * @param context Pointer to a WSProtocolDecoderVaunoEN8822C instance + */ +void ws_protocol_decoder_vauno_en8822c_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderVaunoEN8822C instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_vauno_en8822c_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderVaunoEN8822C instance + * @return hash Hash sum + */ +uint32_t ws_protocol_decoder_vauno_en8822c_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderVaunoEN8822C. + * @param context Pointer to a WSProtocolDecoderVaunoEN8822C instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_vauno_en8822c_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderVaunoEN8822C. + * @param context Pointer to a WSProtocolDecoderVaunoEN8822C instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_vauno_en8822c_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderVaunoEN8822C instance + * @param output Resulting text + */ +void ws_protocol_decoder_vauno_en8822c_get_string(void* context, FuriString* output);