Просмотр исходного кода

[FL-2940] WS: add protocol Ambient_Weather (#1960)

* WS: add protocol Ambient_Weather
* WS: fix link
* WS: removing unused code

Co-authored-by: あく <alleteam@gmail.com>
Skorpionm 3 лет назад
Родитель
Сommit
d34ccb38cc

+ 1 - 1
applications/plugins/weather_station/helpers/weather_station_types.h

@@ -3,7 +3,7 @@
 #include <furi.h>
 #include <furi_hal.h>
 
-#define WS_VERSION_APP "0.3.1"
+#define WS_VERSION_APP "0.4"
 #define WS_DEVELOPED "SkorP"
 #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 

+ 1 - 1
applications/plugins/weather_station/protocols/acurite_592txr.c

@@ -4,7 +4,7 @@
 
 /*
  * Help
- * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c
+ * https://github.com/merbanan/rtl_433/blob/master/src/devices/acurite.c
  * 
  * Acurite 592TXR Temperature Humidity sensor decoder
  * Message Type 0x04, 7 bytes

+ 278 - 0
applications/plugins/weather_station/protocols/ambient_weather.c

@@ -0,0 +1,278 @@
+#include "ambient_weather.h"
+#include <lib/toolbox/manchester_decoder.h>
+
+#define TAG "WSProtocolAmbient_Weather"
+
+/*
+ * Help
+ * https://github.com/merbanan/rtl_433/blob/master/src/devices/ambient_weather.c
+ * 
+ * Decode Ambient Weather F007TH, F012TH, TF 30.3208.02, SwitchDoc F016TH.
+ * Devices supported:
+ * - Ambient Weather F007TH Thermo-Hygrometer.
+ * - Ambient Weather F012TH Indoor/Display Thermo-Hygrometer.
+ * - TFA senders 30.3208.02 from the TFA "Klima-Monitor" 30.3054,
+ * - SwitchDoc Labs F016TH.
+ * This decoder handles the 433mhz/868mhz thermo-hygrometers.
+ * The 915mhz (WH*) family of devices use different modulation/encoding.
+ * Byte 0   Byte 1   Byte 2   Byte 3   Byte 4   Byte 5
+ * xxxxMMMM IIIIIIII BCCCTTTT TTTTTTTT HHHHHHHH MMMMMMMM
+ * - x: Unknown 0x04 on F007TH/F012TH
+ * - M: Model Number?, 0x05 on F007TH/F012TH/SwitchDocLabs F016TH
+ * - I: ID byte (8 bits), volatie, changes at power up,
+ * - B: Battery Low
+ * - C: Channel (3 bits 1-8) - F007TH set by Dip switch, F012TH soft setting
+ * - T: Temperature 12 bits - Fahrenheit * 10 + 400
+ * - H: Humidity (8 bits)
+ * - M: Message integrity check LFSR Digest-8, gen 0x98, key 0x3e, init 0x64
+ * 
+ * three repeats without gap
+ * full preamble is 0x00145 (the last bits might not be fixed, e.g. 0x00146)
+ * and on decoding also 0xffd45
+ */
+
+#define AMBIENT_WEATHER_PACKET_HEADER_1 0xFFD440000000000 //0xffd45 .. 0xffd46
+#define AMBIENT_WEATHER_PACKET_HEADER_2 0x001440000000000 //0x00145 .. 0x00146
+#define AMBIENT_WEATHER_PACKET_HEADER_MASK 0xFFFFC0000000000
+
+static const SubGhzBlockConst ws_protocol_ambient_weather_const = {
+    .te_short = 500,
+    .te_long = 1000,
+    .te_delta = 120,
+    .min_count_bit_for_found = 48,
+};
+
+struct WSProtocolDecoderAmbient_Weather {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    WSBlockGeneric generic;
+    ManchesterState manchester_saved_state;
+    uint16_t header_count;
+};
+
+struct WSProtocolEncoderAmbient_Weather {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    WSBlockGeneric generic;
+};
+
+const SubGhzProtocolDecoder ws_protocol_ambient_weather_decoder = {
+    .alloc = ws_protocol_decoder_ambient_weather_alloc,
+    .free = ws_protocol_decoder_ambient_weather_free,
+
+    .feed = ws_protocol_decoder_ambient_weather_feed,
+    .reset = ws_protocol_decoder_ambient_weather_reset,
+
+    .get_hash_data = ws_protocol_decoder_ambient_weather_get_hash_data,
+    .serialize = ws_protocol_decoder_ambient_weather_serialize,
+    .deserialize = ws_protocol_decoder_ambient_weather_deserialize,
+    .get_string = ws_protocol_decoder_ambient_weather_get_string,
+};
+
+const SubGhzProtocolEncoder ws_protocol_ambient_weather_encoder = {
+    .alloc = NULL,
+    .free = NULL,
+
+    .deserialize = NULL,
+    .stop = NULL,
+    .yield = NULL,
+};
+
+const SubGhzProtocol ws_protocol_ambient_weather = {
+    .name = WS_PROTOCOL_AMBIENT_WEATHER_NAME,
+    .type = SubGhzProtocolWeatherStation,
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
+            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
+
+    .decoder = &ws_protocol_ambient_weather_decoder,
+    .encoder = &ws_protocol_ambient_weather_encoder,
+};
+
+void* ws_protocol_decoder_ambient_weather_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    WSProtocolDecoderAmbient_Weather* instance = malloc(sizeof(WSProtocolDecoderAmbient_Weather));
+    instance->base.protocol = &ws_protocol_ambient_weather;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    return instance;
+}
+
+void ws_protocol_decoder_ambient_weather_free(void* context) {
+    furi_assert(context);
+    WSProtocolDecoderAmbient_Weather* instance = context;
+    free(instance);
+}
+
+void ws_protocol_decoder_ambient_weather_reset(void* context) {
+    furi_assert(context);
+    WSProtocolDecoderAmbient_Weather* instance = context;
+    manchester_advance(
+        instance->manchester_saved_state,
+        ManchesterEventReset,
+        &instance->manchester_saved_state,
+        NULL);
+}
+
+static bool ws_protocol_ambient_weather_check_crc(WSProtocolDecoderAmbient_Weather* instance) {
+    uint8_t msg[] = {
+        instance->decoder.decode_data >> 40,
+        instance->decoder.decode_data >> 32,
+        instance->decoder.decode_data >> 24,
+        instance->decoder.decode_data >> 16,
+        instance->decoder.decode_data >> 8};
+
+    uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 5, 0x98, 0x3e) ^ 0x64;
+    return (crc == (uint8_t)(instance->decoder.decode_data & 0xFF));
+}
+
+/**
+ * Analysis of received data
+ * @param instance Pointer to a WSBlockGeneric* instance
+ */
+static void ws_protocol_ambient_weather_remote_controller(WSBlockGeneric* instance) {
+    instance->id = (instance->data >> 32) & 0xFF;
+    instance->battery_low = (instance->data >> 31) & 1;
+    instance->channel = ((instance->data >> 28) & 0x07) + 1;
+    instance->temp = ws_block_generic_fahrenheit_to_celsius(
+        ((float)((instance->data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
+    instance->humidity = (instance->data >> 8) & 0xFF;
+    instance->btn = WS_NO_BTN;
+
+    // ToDo maybe it won't be needed
+    /*
+    Sanity checks to reduce false positives and other bad data
+    Packets with Bad data often pass the MIC check.
+    - humidity > 100 (such as 255) and
+    - temperatures > 140 F (such as 369.5 F and 348.8 F
+    Specs in the F007TH and F012TH manuals state the range is:
+    - Temperature: -40 to 140 F
+    - Humidity: 10 to 99%
+    @todo - sanity check b[0] "model number"
+    - 0x45 - F007TH and F012TH
+    - 0x?5 - SwitchDocLabs F016TH temperature sensor (based on comment b[0] & 0x0f == 5)
+    - ? - TFA 30.3208.02
+    if (instance->humidity < 0 || instance->humidity > 100) {
+        ERROR;
+    }
+
+    if (instance->temp < -40.0 || instance->temp > 140.0) {
+         ERROR;
+    }
+    */
+}
+
+void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    WSProtocolDecoderAmbient_Weather* instance = context;
+
+    ManchesterEvent event = ManchesterEventReset;
+    if(!level) {
+        if(DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_short) <
+           ws_protocol_ambient_weather_const.te_delta) {
+            event = ManchesterEventShortLow;
+        } else if(
+            DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_long) <
+            ws_protocol_ambient_weather_const.te_delta * 2) {
+            event = ManchesterEventLongLow;
+        }
+    } else {
+        if(DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_short) <
+           ws_protocol_ambient_weather_const.te_delta) {
+            event = ManchesterEventShortHigh;
+        } else if(
+            DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_long) <
+            ws_protocol_ambient_weather_const.te_delta * 2) {
+            event = ManchesterEventLongHigh;
+        }
+    }
+    if(event != ManchesterEventReset) {
+        bool data;
+        bool data_ok = manchester_advance(
+            instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
+
+        if(data_ok) {
+            instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;
+        }
+
+        if(((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) ==
+            AMBIENT_WEATHER_PACKET_HEADER_1) ||
+           ((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) ==
+            AMBIENT_WEATHER_PACKET_HEADER_2)) {
+            if(ws_protocol_ambient_weather_check_crc(instance)) {
+                instance->decoder.decode_data = instance->decoder.decode_data;
+                instance->generic.data = instance->decoder.decode_data;
+                instance->generic.data_count_bit =
+                    ws_protocol_ambient_weather_const.min_count_bit_for_found;
+                ws_protocol_ambient_weather_remote_controller(&instance->generic);
+                if(instance->base.callback)
+                    instance->base.callback(&instance->base, instance->base.context);
+                instance->decoder.decode_data = 0;
+                instance->decoder.decode_count_bit = 0;
+            }
+        }
+    } else {
+        instance->decoder.decode_data = 0;
+        instance->decoder.decode_count_bit = 0;
+        manchester_advance(
+            instance->manchester_saved_state,
+            ManchesterEventReset,
+            &instance->manchester_saved_state,
+            NULL);
+    }
+}
+
+uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context) {
+    furi_assert(context);
+    WSProtocolDecoderAmbient_Weather* instance = context;
+    return subghz_protocol_blocks_get_hash_data(
+        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
+}
+
+bool ws_protocol_decoder_ambient_weather_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzRadioPreset* preset) {
+    furi_assert(context);
+    WSProtocolDecoderAmbient_Weather* instance = context;
+    return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
+}
+
+bool ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    WSProtocolDecoderAmbient_Weather* instance = context;
+    bool ret = false;
+    do {
+        if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           ws_protocol_ambient_weather_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        ret = true;
+    } while(false);
+    return ret;
+}
+
+void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output) {
+    furi_assert(context);
+    WSProtocolDecoderAmbient_Weather* instance = context;
+    furi_string_printf(
+        output,
+        "%s %dbit\r\n"
+        "Key:0x%lX%08lX\r\n"
+        "Sn:0x%lX Ch:%d  Bat:%d\r\n"
+        "Temp:%d.%d C Hum:%d%%",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit,
+        (uint32_t)(instance->generic.data >> 32),
+        (uint32_t)(instance->generic.data),
+        instance->generic.id,
+        instance->generic.channel,
+        instance->generic.battery_low,
+        (int16_t)instance->generic.temp,
+        abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))),
+        instance->generic.humidity);
+}

+ 79 - 0
applications/plugins/weather_station/protocols/ambient_weather.h

@@ -0,0 +1,79 @@
+#pragma once
+
+#include <lib/subghz/protocols/base.h>
+
+#include <lib/subghz/blocks/const.h>
+#include <lib/subghz/blocks/decoder.h>
+#include <lib/subghz/blocks/encoder.h>
+#include "ws_generic.h"
+#include <lib/subghz/blocks/math.h>
+
+#define WS_PROTOCOL_AMBIENT_WEATHER_NAME "Ambient_Weather"
+
+typedef struct WSProtocolDecoderAmbient_Weather WSProtocolDecoderAmbient_Weather;
+typedef struct WSProtocolEncoderAmbient_Weather WSProtocolEncoderAmbient_Weather;
+
+extern const SubGhzProtocolDecoder ws_protocol_ambient_weather_decoder;
+extern const SubGhzProtocolEncoder ws_protocol_ambient_weather_encoder;
+extern const SubGhzProtocol ws_protocol_ambient_weather;
+
+/**
+ * Allocate WSProtocolDecoderAmbient_Weather.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return WSProtocolDecoderAmbient_Weather* pointer to a WSProtocolDecoderAmbient_Weather instance
+ */
+void* ws_protocol_decoder_ambient_weather_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free WSProtocolDecoderAmbient_Weather.
+ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance
+ */
+void ws_protocol_decoder_ambient_weather_free(void* context);
+
+/**
+ * Reset decoder WSProtocolDecoderAmbient_Weather.
+ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance
+ */
+void ws_protocol_decoder_ambient_weather_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance
+ * @return hash Hash sum
+ */
+uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context);
+
+/**
+ * Serialize data WSProtocolDecoderAmbient_Weather.
+ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param preset The modulation on which the signal was received, SubGhzRadioPreset
+ * @return true On success
+ */
+bool ws_protocol_decoder_ambient_weather_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzRadioPreset* preset);
+
+/**
+ * Deserialize data WSProtocolDecoderAmbient_Weather.
+ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance
+ * @param output Resulting text
+ */
+void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output);

+ 1 - 1
applications/plugins/weather_station/protocols/gt_wt_03.c

@@ -4,7 +4,7 @@
 
 /*
  * Help
- * https://github.com/merbanan/rtl_433/blob/5f0ff6db624270a4598958ab9dd79bb385ced3ef/src/devices/gt_wt_03.c
+ * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_03.c
  * 
  * 
  * Globaltronics GT-WT-03 sensor on 433.92MHz.

+ 1 - 1
applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c

@@ -4,7 +4,7 @@
 
 /*
  * Help
- * https://github.com/merbanan/rtl_433/blob/7e83cfd27d14247b6c3c81732bfe4a4f9a974d30/src/devices/lacrosse_tx141x.c
+ * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c
  *  
  *     iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u
  * - i: identification; changes on battery switch

+ 1 - 1
applications/plugins/weather_station/protocols/nexus_th.c

@@ -4,7 +4,7 @@
 
 /*
  * Help
- * https://github.com/merbanan/rtl_433/blob/ef2d37cf51e3264d11cde9149ef87de2f0a4d37a/src/devices/nexus.c
+ * https://github.com/merbanan/rtl_433/blob/master/src/devices/nexus.c
  *
  * Nexus sensor protocol with ID, temperature and optional humidity
  * also FreeTec (Pearl) NC-7345 sensors for FreeTec Weatherstation NC-7344,

+ 1 - 0
applications/plugins/weather_station/protocols/protocol_items.c

@@ -9,6 +9,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
     &ws_protocol_lacrosse_tx141thbv2,
     &ws_protocol_oregon2,
     &ws_protocol_acurite_592txr,
+    &ws_protocol_ambient_weather,
 };
 
 const SubGhzProtocolRegistry weather_station_protocol_registry = {

+ 1 - 0
applications/plugins/weather_station/protocols/protocol_items.h

@@ -9,5 +9,6 @@
 #include "lacrosse_tx141thbv2.h"
 #include "oregon2.h"
 #include "acurite_592txr.h"
+#include "ambient_weather.h"
 
 extern const SubGhzProtocolRegistry weather_station_protocol_registry;