[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>
This commit is contained in:
		
							parent
							
								
									c417d467f7
								
							
						
					
					
						commit
						0652830c51
					
				| @ -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"
 | ||||
| 
 | ||||
|  | ||||
| @ -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
									
								
								applications/plugins/weather_station/protocols/ambient_weather.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								applications/plugins/weather_station/protocols/ambient_weather.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
| } | ||||
| @ -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); | ||||
| @ -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. | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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 = { | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Skorpionm
						Skorpionm