forked from flipperdevices/flipperzero-firmware
-
-
Notifications
You must be signed in to change notification settings - Fork 547
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
387 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,303 @@ | ||
#include "acurite_5n1.h" | ||
|
||
#define TAG "WSProtocolAcurite_5n1" | ||
|
||
/* | ||
* Help | ||
* https://github.com/merbanan/rtl_433/blob/master/src/devices/acurite.c | ||
* | ||
* Acurite 5n1 Wind Speed Temperature Humidity sensor decoder | ||
* Message Type 0x38, 8 bytes | ||
* | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | | ||
* | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | | ||
* | CCII IIII | IIII IIII | pB11 1000 | p??W WWWW | pWWW TTTT | pTTT TTTT | pHHH HHHH | KKKK KKKK | | ||
* - C: Channel 00: C, 10: B, 11: A, (01 is invalid) | ||
* - I: Device ID (14 bits) | ||
* - B: Battery, 1 is battery OK, 0 is battery low | ||
* - M: Message type (6 bits), 0x38 | ||
* - W: Wind Speed (8 bits) | ||
* - T: Temperature Fahrenheit (11 bits), + 400 * 10 | ||
* - H: Relative Humidity (%) (7 bits) | ||
* - K: Checksum (8 bits) | ||
* - p: Parity bit | ||
* Notes: | ||
* - Temperature | ||
* - Encoded as Fahrenheit + 400 * 10 | ||
* - only 11 bits needed for specified range -40 F - 158 F (-40 C to 70 C) | ||
* | ||
*/ | ||
|
||
static const SubGhzBlockConst ws_protocol_acurite_5n1_const = { | ||
.te_short = 200, | ||
.te_long = 400, | ||
.te_delta = 90, | ||
.min_count_bit_for_found = 64, | ||
}; | ||
|
||
struct WSProtocolDecoderAcurite_5n1 { | ||
SubGhzProtocolDecoderBase base; | ||
|
||
SubGhzBlockDecoder decoder; | ||
WSBlockGeneric generic; | ||
|
||
uint16_t header_count; | ||
}; | ||
|
||
struct WSProtocolEncoderAcurite_5n1 { | ||
SubGhzProtocolEncoderBase base; | ||
|
||
SubGhzProtocolBlockEncoder encoder; | ||
WSBlockGeneric generic; | ||
}; | ||
|
||
typedef enum { | ||
Acurite_5n1DecoderStepReset = 0, | ||
Acurite_5n1DecoderStepCheckPreambule, | ||
Acurite_5n1DecoderStepSaveDuration, | ||
Acurite_5n1DecoderStepCheckDuration, | ||
} Acurite_5n1DecoderStep; | ||
|
||
const SubGhzProtocolDecoder ws_protocol_acurite_5n1_decoder = { | ||
.alloc = ws_protocol_decoder_acurite_5n1_alloc, | ||
.free = ws_protocol_decoder_acurite_5n1_free, | ||
|
||
.feed = ws_protocol_decoder_acurite_5n1_feed, | ||
.reset = ws_protocol_decoder_acurite_5n1_reset, | ||
|
||
.get_hash_data = ws_protocol_decoder_acurite_5n1_get_hash_data, | ||
.serialize = ws_protocol_decoder_acurite_5n1_serialize, | ||
.deserialize = ws_protocol_decoder_acurite_5n1_deserialize, | ||
.get_string = ws_protocol_decoder_acurite_5n1_get_string, | ||
}; | ||
|
||
const SubGhzProtocolEncoder ws_protocol_acurite_5n1_encoder = { | ||
.alloc = NULL, | ||
.free = NULL, | ||
|
||
.deserialize = NULL, | ||
.stop = NULL, | ||
.yield = NULL, | ||
}; | ||
|
||
const SubGhzProtocol ws_protocol_acurite_5n1 = { | ||
.name = WS_PROTOCOL_ACURITE_5N1_NAME, | ||
.type = SubGhzProtocolWeatherStation, | ||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | | ||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, | ||
|
||
.decoder = &ws_protocol_acurite_5n1_decoder, | ||
.encoder = &ws_protocol_acurite_5n1_encoder, | ||
}; | ||
|
||
void* ws_protocol_decoder_acurite_5n1_alloc(SubGhzEnvironment* environment) { | ||
UNUSED(environment); | ||
WSProtocolDecoderAcurite_5n1* instance = malloc(sizeof(WSProtocolDecoderAcurite_5n1)); | ||
instance->base.protocol = &ws_protocol_acurite_5n1; | ||
instance->generic.protocol_name = instance->base.protocol->name; | ||
return instance; | ||
} | ||
|
||
void ws_protocol_decoder_acurite_5n1_free(void* context) { | ||
furi_assert(context); | ||
WSProtocolDecoderAcurite_5n1* instance = context; | ||
free(instance); | ||
} | ||
|
||
void ws_protocol_decoder_acurite_5n1_reset(void* context) { | ||
furi_assert(context); | ||
WSProtocolDecoderAcurite_5n1* instance = context; | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepReset; | ||
} | ||
|
||
static bool ws_protocol_acurite_5n1_check_crc(WSProtocolDecoderAcurite_5n1* instance) { | ||
uint8_t msg[] = { | ||
instance->decoder.decode_data >> 56, | ||
instance->decoder.decode_data >> 48, | ||
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}; | ||
|
||
if((subghz_protocol_blocks_add_bytes(msg, 7) == | ||
(uint8_t)(instance->decoder.decode_data & 0xFF)) && | ||
(!subghz_protocol_blocks_parity_bytes(&msg[2], 5))) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
static bool ws_protocol_acurite_5n1_check_message_type(WSProtocolDecoderAcurite_5n1* instance) { | ||
if(((instance->decoder.decode_data >> 40) & 0x3F) == 0x38) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Analysis of received data | ||
* @param instance Pointer to a WSBlockGeneric* instance | ||
*/ | ||
static void ws_protocol_acurite_5n1_remote_controller(WSBlockGeneric* instance) { | ||
uint8_t channel[] = {3, 0, 2, 1}; | ||
uint8_t channel_raw = ((instance->data >> 62) & 0x03); | ||
instance->channel = channel[channel_raw]; | ||
instance->id = (instance->data >> 48) & 0x3FFF; | ||
instance->battery_low = !((instance->data >> 46) & 1); | ||
instance->humidity = (instance->data >> 8) & 0x7F; | ||
|
||
uint16_t temp_raw = ((instance->data >> (24 - 7)) & 0x780) | ((instance->data >> 16) & 0x7F); | ||
instance->temp = locale_fahrenheit_to_celsius(((float)(temp_raw)-400) / 10.0f); | ||
|
||
instance->btn = WS_NO_BTN; | ||
} | ||
|
||
void ws_protocol_decoder_acurite_5n1_feed(void* context, bool level, uint32_t duration) { | ||
furi_assert(context); | ||
WSProtocolDecoderAcurite_5n1* instance = context; | ||
|
||
switch(instance->decoder.parser_step) { | ||
case Acurite_5n1DecoderStepReset: | ||
if((level) && (DURATION_DIFF(duration, ws_protocol_acurite_5n1_const.te_short * 3) < | ||
ws_protocol_acurite_5n1_const.te_delta * 2)) { | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepCheckPreambule; | ||
instance->decoder.te_last = duration; | ||
instance->header_count = 0; | ||
} | ||
break; | ||
|
||
case Acurite_5n1DecoderStepCheckPreambule: | ||
if(level) { | ||
instance->decoder.te_last = duration; | ||
} else { | ||
if((DURATION_DIFF( | ||
instance->decoder.te_last, ws_protocol_acurite_5n1_const.te_short * 3) < | ||
ws_protocol_acurite_5n1_const.te_delta * 2) && | ||
(DURATION_DIFF(duration, ws_protocol_acurite_5n1_const.te_short * 3) < | ||
ws_protocol_acurite_5n1_const.te_delta * 2)) { | ||
//Found preambule | ||
instance->header_count++; | ||
} else if((instance->header_count > 2) && (instance->header_count < 5)) { | ||
if((DURATION_DIFF( | ||
instance->decoder.te_last, ws_protocol_acurite_5n1_const.te_short) < | ||
ws_protocol_acurite_5n1_const.te_delta) && | ||
(DURATION_DIFF(duration, ws_protocol_acurite_5n1_const.te_long) < | ||
ws_protocol_acurite_5n1_const.te_delta)) { | ||
instance->decoder.decode_data = 0; | ||
instance->decoder.decode_count_bit = 0; | ||
subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepSaveDuration; | ||
} else if( | ||
(DURATION_DIFF( | ||
instance->decoder.te_last, ws_protocol_acurite_5n1_const.te_long) < | ||
ws_protocol_acurite_5n1_const.te_delta) && | ||
(DURATION_DIFF(duration, ws_protocol_acurite_5n1_const.te_short) < | ||
ws_protocol_acurite_5n1_const.te_delta)) { | ||
instance->decoder.decode_data = 0; | ||
instance->decoder.decode_count_bit = 0; | ||
subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepSaveDuration; | ||
} else { | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepReset; | ||
} | ||
} else { | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepReset; | ||
} | ||
} | ||
break; | ||
|
||
case Acurite_5n1DecoderStepSaveDuration: | ||
if(level) { | ||
instance->decoder.te_last = duration; | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepCheckDuration; | ||
} else { | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepReset; | ||
} | ||
break; | ||
|
||
case Acurite_5n1DecoderStepCheckDuration: | ||
if(!level) { | ||
if(duration >= ((uint32_t)ws_protocol_acurite_5n1_const.te_short * 5)) { | ||
if((instance->decoder.decode_count_bit == | ||
ws_protocol_acurite_5n1_const.min_count_bit_for_found) && | ||
ws_protocol_acurite_5n1_check_crc(instance) && | ||
ws_protocol_acurite_5n1_check_message_type(instance)) { | ||
instance->generic.data = instance->decoder.decode_data; | ||
instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||
ws_protocol_acurite_5n1_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; | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepReset; | ||
break; | ||
} else if( | ||
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_5n1_const.te_short) < | ||
ws_protocol_acurite_5n1_const.te_delta) && | ||
(DURATION_DIFF(duration, ws_protocol_acurite_5n1_const.te_long) < | ||
ws_protocol_acurite_5n1_const.te_delta)) { | ||
subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepSaveDuration; | ||
} else if( | ||
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_5n1_const.te_long) < | ||
ws_protocol_acurite_5n1_const.te_delta) && | ||
(DURATION_DIFF(duration, ws_protocol_acurite_5n1_const.te_short) < | ||
ws_protocol_acurite_5n1_const.te_delta)) { | ||
subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepSaveDuration; | ||
} else { | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepReset; | ||
} | ||
} else { | ||
instance->decoder.parser_step = Acurite_5n1DecoderStepReset; | ||
} | ||
break; | ||
} | ||
} | ||
|
||
uint8_t ws_protocol_decoder_acurite_5n1_get_hash_data(void* context) { | ||
furi_assert(context); | ||
WSProtocolDecoderAcurite_5n1* instance = context; | ||
return subghz_protocol_blocks_get_hash_data( | ||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||
} | ||
|
||
SubGhzProtocolStatus ws_protocol_decoder_acurite_5n1_serialize( | ||
void* context, | ||
FlipperFormat* flipper_format, | ||
SubGhzRadioPreset* preset) { | ||
furi_assert(context); | ||
WSProtocolDecoderAcurite_5n1* instance = context; | ||
return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||
} | ||
|
||
SubGhzProtocolStatus | ||
ws_protocol_decoder_acurite_5n1_deserialize(void* context, FlipperFormat* flipper_format) { | ||
furi_assert(context); | ||
WSProtocolDecoderAcurite_5n1* instance = context; | ||
return ws_block_generic_deserialize_check_count_bit( | ||
&instance->generic, flipper_format, ws_protocol_acurite_5n1_const.min_count_bit_for_found); | ||
} | ||
|
||
void ws_protocol_decoder_acurite_5n1_get_string(void* context, FuriString* output) { | ||
furi_assert(context); | ||
WSProtocolDecoderAcurite_5n1* 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:%3.1f 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, | ||
(double)instance->generic.temp, | ||
instance->generic.humidity); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#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_ACURITE_5N1_NAME "Acurite 5n1" | ||
|
||
typedef struct WSProtocolDecoderAcurite_5n1 WSProtocolDecoderAcurite_5n1; | ||
typedef struct WSProtocolEncoderAcurite_5n1 WSProtocolEncoderAcurite_5n1; | ||
|
||
extern const SubGhzProtocolDecoder ws_protocol_acurite_5n1_decoder; | ||
extern const SubGhzProtocolEncoder ws_protocol_acurite_5n1_encoder; | ||
extern const SubGhzProtocol ws_protocol_acurite_5n1; | ||
|
||
/** | ||
* Allocate WSProtocolDecoderAcurite_5n1. | ||
* @param environment Pointer to a SubGhzEnvironment instance | ||
* @return WSProtocolDecoderAcurite_5n1* pointer to a WSProtocolDecoderAcurite_5n1 instance | ||
*/ | ||
void* ws_protocol_decoder_acurite_5n1_alloc(SubGhzEnvironment* environment); | ||
|
||
/** | ||
* Free WSProtocolDecoderAcurite_5n1. | ||
* @param context Pointer to a WSProtocolDecoderAcurite_5n1 instance | ||
*/ | ||
void ws_protocol_decoder_acurite_5n1_free(void* context); | ||
|
||
/** | ||
* Reset decoder WSProtocolDecoderAcurite_5n1. | ||
* @param context Pointer to a WSProtocolDecoderAcurite_5n1 instance | ||
*/ | ||
void ws_protocol_decoder_acurite_5n1_reset(void* context); | ||
|
||
/** | ||
* Parse a raw sequence of levels and durations received from the air. | ||
* @param context Pointer to a WSProtocolDecoderAcurite_5n1 instance | ||
* @param level Signal level true-high false-low | ||
* @param duration Duration of this level in, us | ||
*/ | ||
void ws_protocol_decoder_acurite_5n1_feed(void* context, bool level, uint32_t duration); | ||
|
||
/** | ||
* Getting the hash sum of the last randomly received parcel. | ||
* @param context Pointer to a WSProtocolDecoderAcurite_5n1 instance | ||
* @return hash Hash sum | ||
*/ | ||
uint8_t ws_protocol_decoder_acurite_5n1_get_hash_data(void* context); | ||
|
||
/** | ||
* Serialize data WSProtocolDecoderAcurite_5n1. | ||
* @param context Pointer to a WSProtocolDecoderAcurite_5n1 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_acurite_5n1_serialize( | ||
void* context, | ||
FlipperFormat* flipper_format, | ||
SubGhzRadioPreset* preset); | ||
|
||
/** | ||
* Deserialize data WSProtocolDecoderAcurite_5n1. | ||
* @param context Pointer to a WSProtocolDecoderAcurite_5n1 instance | ||
* @param flipper_format Pointer to a FlipperFormat instance | ||
* @return status | ||
*/ | ||
SubGhzProtocolStatus | ||
ws_protocol_decoder_acurite_5n1_deserialize(void* context, FlipperFormat* flipper_format); | ||
|
||
/** | ||
* Getting a textual representation of the received data. | ||
* @param context Pointer to a WSProtocolDecoderAcurite_5n1 instance | ||
* @param output Resulting text | ||
*/ | ||
void ws_protocol_decoder_acurite_5n1_get_string(void* context, FuriString* output); |
Oops, something went wrong.