mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 15:04:19 +00:00
[FL-2904, FL-2900, FL-2890] WS: add app WeatherStation (#1833)
* WeatherStation: start * SubGhz: rename protocol magellen -> magellan * WeatherStation: err Unresolved symbols: {'subghz_protocol_decoder_base_get_string'} * WeatherStation: fix Unresolved symbols: {'subghz_protocol_decoder_base_get_string'} * Subghz: add set protocol_items * WeatherStation: adding your protocols * WS: add Infactory protocol * WS: add history * WS: add setting * WS: add lock * WS: add hopper frequency * WS: fix history * WS fix string_t -> FuriString* * WS: add images * WS: history record update when receiving data from the sensor again * WS: add receiver info, delete extra code * WS: add protocol ThermoPRO_TX4 * [FL-2900] SubGhz: Move icons in Sub-GHz * WS: add Notification * [FL-2890] SubGhz: Rename *_user files in resources to _user.example * WS: add about scene * WS: removing redundant code * WS: add protocol Nexus-TH * WS: add protocol GT_WT03 * WS: fix notification and rename "Weather Station" -> "Read Weather Station" * SubGhz: partial unit tests fix * SubGhz: fix unit_test * SubGhz: remove dead code * SubGhz: rename SubGhzPresetDefinition into SubGhzRadioPreset, cleanup subghz types. Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
79c3040629
commit
9a9abd59e9
143 changed files with 4677 additions and 458 deletions
|
@ -5,7 +5,7 @@
|
||||||
#include <lib/subghz/transmitter.h>
|
#include <lib/subghz/transmitter.h>
|
||||||
#include <lib/subghz/subghz_keystore.h>
|
#include <lib/subghz/subghz_keystore.h>
|
||||||
#include <lib/subghz/subghz_file_encoder_worker.h>
|
#include <lib/subghz/subghz_file_encoder_worker.h>
|
||||||
#include <lib/subghz/protocols/registry.h>
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
#include <flipper_format/flipper_format_i.h>
|
#include <flipper_format/flipper_format_i.h>
|
||||||
|
|
||||||
#define TAG "SubGhz TEST"
|
#define TAG "SubGhz TEST"
|
||||||
|
@ -43,6 +43,8 @@ static void subghz_test_init(void) {
|
||||||
environment_handler, CAME_ATOMO_DIR_NAME);
|
environment_handler, CAME_ATOMO_DIR_NAME);
|
||||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
environment_handler, NICE_FLOR_S_DIR_NAME);
|
environment_handler, NICE_FLOR_S_DIR_NAME);
|
||||||
|
subghz_environment_set_protocol_registry(
|
||||||
|
environment_handler, (void*)&subghz_protocol_registry);
|
||||||
|
|
||||||
receiver_handler = subghz_receiver_alloc_init(environment_handler);
|
receiver_handler = subghz_receiver_alloc_init(environment_handler);
|
||||||
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
|
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
|
||||||
|
@ -413,11 +415,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) {
|
||||||
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_decoder_magellen_test) {
|
MU_TEST(subghz_decoder_magellan_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
subghz_decoder_test(
|
subghz_decoder_test(
|
||||||
EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME),
|
EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME),
|
||||||
"Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_decoder_intertechno_v3_test) {
|
MU_TEST(subghz_decoder_intertechno_v3_test) {
|
||||||
|
@ -545,10 +547,10 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) {
|
||||||
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_encoder_magellen_test) {
|
MU_TEST(subghz_encoder_magellan_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")),
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")),
|
||||||
"Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
"Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_encoder_intertechno_v3_test) {
|
MU_TEST(subghz_encoder_intertechno_v3_test) {
|
||||||
|
@ -600,7 +602,7 @@ MU_TEST_SUITE(subghz) {
|
||||||
MU_RUN_TEST(subghz_decoder_doitrand_test);
|
MU_RUN_TEST(subghz_decoder_doitrand_test);
|
||||||
MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
|
MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
|
||||||
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
|
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
|
||||||
MU_RUN_TEST(subghz_decoder_magellen_test);
|
MU_RUN_TEST(subghz_decoder_magellan_test);
|
||||||
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
||||||
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
||||||
MU_RUN_TEST(subghz_decoder_oregon2_test);
|
MU_RUN_TEST(subghz_decoder_oregon2_test);
|
||||||
|
@ -622,7 +624,7 @@ MU_TEST_SUITE(subghz) {
|
||||||
MU_RUN_TEST(subghz_encoder_doitrand_test);
|
MU_RUN_TEST(subghz_encoder_doitrand_test);
|
||||||
MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
|
MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
|
||||||
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
|
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
|
||||||
MU_RUN_TEST(subghz_encoder_magellen_test);
|
MU_RUN_TEST(subghz_encoder_magellan_test);
|
||||||
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
|
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
|
||||||
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ void lfrfid_scene_raw_info_on_enter(void* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
|
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
|
||||||
//string_clear(tmp_string);
|
//furi_string_free(tmp_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) {
|
bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
|
|
@ -69,12 +69,3 @@ typedef enum {
|
||||||
SubGhzViewIdTestCarrier,
|
SubGhzViewIdTestCarrier,
|
||||||
SubGhzViewIdTestPacket,
|
SubGhzViewIdTestPacket,
|
||||||
} SubGhzViewId;
|
} SubGhzViewId;
|
||||||
|
|
||||||
struct SubGhzPresetDefinition {
|
|
||||||
FuriString* name;
|
|
||||||
uint32_t frequency;
|
|
||||||
uint8_t* data;
|
|
||||||
size_t data_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct SubGhzPresetDefinition SubGhzPresetDefinition;
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||||
subghz->txrx->decoder_result,
|
subghz->txrx->decoder_result,
|
||||||
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
|
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
|
||||||
|
|
||||||
SubGhzPresetDefinition* preset =
|
SubGhzRadioPreset* preset =
|
||||||
subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
|
subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
|
||||||
subghz_preset_init(
|
subghz_preset_init(
|
||||||
subghz,
|
subghz,
|
||||||
furi_string_get_cstr(preset->name),
|
furi_string_get_cstr(preset->name),
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
#include <flipper_format/flipper_format_i.h>
|
#include <flipper_format/flipper_format_i.h>
|
||||||
#include <lib/toolbox/stream/stream.h>
|
#include <lib/toolbox/stream/stream.h>
|
||||||
#include <lib/subghz/protocols/registry.h>
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
|
||||||
#define TAG "SubGhzSetType"
|
#define TAG "SubGhzSetType"
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "subghz/types.h"
|
#include "subghz/types.h"
|
||||||
#include "subghz_i.h"
|
#include "subghz_i.h"
|
||||||
#include <lib/toolbox/path.h>
|
#include <lib/toolbox/path.h>
|
||||||
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
|
||||||
bool subghz_custom_event_callback(void* context, uint32_t event) {
|
bool subghz_custom_event_callback(void* context, uint32_t event) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
@ -169,7 +170,7 @@ SubGhz* subghz_alloc() {
|
||||||
//init Worker & Protocol & History & KeyBoard
|
//init Worker & Protocol & History & KeyBoard
|
||||||
subghz->lock = SubGhzLockOff;
|
subghz->lock = SubGhzLockOff;
|
||||||
subghz->txrx = malloc(sizeof(SubGhzTxRx));
|
subghz->txrx = malloc(sizeof(SubGhzTxRx));
|
||||||
subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition));
|
subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||||
subghz->txrx->preset->name = furi_string_alloc();
|
subghz->txrx->preset->name = furi_string_alloc();
|
||||||
subghz_preset_init(
|
subghz_preset_init(
|
||||||
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
|
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
|
||||||
|
@ -186,6 +187,8 @@ SubGhz* subghz_alloc() {
|
||||||
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
|
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
|
||||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
|
subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
|
||||||
|
subghz_environment_set_protocol_registry(
|
||||||
|
subghz->txrx->environment, (void*)&subghz_protocol_registry);
|
||||||
subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
|
subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
|
||||||
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <lib/subghz/receiver.h>
|
#include <lib/subghz/receiver.h>
|
||||||
#include <lib/subghz/transmitter.h>
|
#include <lib/subghz/transmitter.h>
|
||||||
#include <lib/subghz/subghz_file_encoder_worker.h>
|
#include <lib/subghz/subghz_file_encoder_worker.h>
|
||||||
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
|
||||||
#include "helpers/subghz_chat.h"
|
#include "helpers/subghz_chat.h"
|
||||||
|
|
||||||
|
@ -164,6 +165,7 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
|
||||||
stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));
|
stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));
|
||||||
|
|
||||||
SubGhzEnvironment* environment = subghz_environment_alloc();
|
SubGhzEnvironment* environment = subghz_environment_alloc();
|
||||||
|
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
||||||
|
|
||||||
SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton");
|
SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton");
|
||||||
subghz_transmitter_deserialize(transmitter, flipper_format);
|
subghz_transmitter_deserialize(transmitter, flipper_format);
|
||||||
|
@ -257,6 +259,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
|
||||||
environment, EXT_PATH("subghz/assets/came_atomo"));
|
environment, EXT_PATH("subghz/assets/came_atomo"));
|
||||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
environment, EXT_PATH("subghz/assets/nice_flor_s"));
|
environment, EXT_PATH("subghz/assets/nice_flor_s"));
|
||||||
|
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
||||||
|
|
||||||
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
|
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
|
||||||
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
|
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
|
||||||
|
@ -376,6 +379,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
|
||||||
environment, EXT_PATH("subghz/assets/came_atomo"));
|
environment, EXT_PATH("subghz/assets/came_atomo"));
|
||||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
environment, EXT_PATH("subghz/assets/nice_flor_s"));
|
environment, EXT_PATH("subghz/assets/nice_flor_s"));
|
||||||
|
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
||||||
|
|
||||||
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
|
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
|
||||||
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
|
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
|
||||||
|
|
|
@ -11,7 +11,7 @@ typedef struct {
|
||||||
FuriString* item_str;
|
FuriString* item_str;
|
||||||
FlipperFormat* flipper_string;
|
FlipperFormat* flipper_string;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
SubGhzPresetDefinition* preset;
|
SubGhzRadioPreset* preset;
|
||||||
} SubGhzHistoryItem;
|
} SubGhzHistoryItem;
|
||||||
|
|
||||||
ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST)
|
ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST)
|
||||||
|
@ -60,7 +60,7 @@ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) {
|
||||||
return item->preset->frequency;
|
return item->preset->frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) {
|
SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
|
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
|
||||||
return item->preset;
|
return item->preset;
|
||||||
|
@ -138,7 +138,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp
|
||||||
bool subghz_history_add_to_history(
|
bool subghz_history_add_to_history(
|
||||||
SubGhzHistory* instance,
|
SubGhzHistory* instance,
|
||||||
void* context,
|
void* context,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ bool subghz_history_add_to_history(
|
||||||
FuriString* text;
|
FuriString* text;
|
||||||
text = furi_string_alloc();
|
text = furi_string_alloc();
|
||||||
SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data);
|
SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data);
|
||||||
item->preset = malloc(sizeof(SubGhzPresetDefinition));
|
item->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||||
item->type = decoder_base->protocol->type;
|
item->type = decoder_base->protocol->type;
|
||||||
item->preset->frequency = preset->frequency;
|
item->preset->frequency = preset->frequency;
|
||||||
item->preset->name = furi_string_alloc();
|
item->preset->name = furi_string_alloc();
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <lib/flipper_format/flipper_format.h>
|
#include <lib/flipper_format/flipper_format.h>
|
||||||
#include "helpers/subghz_types.h"
|
#include <lib/subghz/types.h>
|
||||||
|
|
||||||
typedef struct SubGhzHistory SubGhzHistory;
|
typedef struct SubGhzHistory SubGhzHistory;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance);
|
||||||
*/
|
*/
|
||||||
uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx);
|
uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx);
|
||||||
|
|
||||||
SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx);
|
SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx);
|
||||||
|
|
||||||
/** Get preset to history[idx]
|
/** Get preset to history[idx]
|
||||||
*
|
*
|
||||||
|
@ -88,13 +88,13 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* out
|
||||||
*
|
*
|
||||||
* @param instance - SubGhzHistory instance
|
* @param instance - SubGhzHistory instance
|
||||||
* @param context - SubGhzProtocolCommon context
|
* @param context - SubGhzProtocolCommon context
|
||||||
* @param preset - SubGhzPresetDefinition preset
|
* @param preset - SubGhzRadioPreset preset
|
||||||
* @return bool;
|
* @return bool;
|
||||||
*/
|
*/
|
||||||
bool subghz_history_add_to_history(
|
bool subghz_history_add_to_history(
|
||||||
SubGhzHistory* instance,
|
SubGhzHistory* instance,
|
||||||
void* context,
|
void* context,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
|
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "helpers/subghz_types.h"
|
#include "helpers/subghz_types.h"
|
||||||
|
#include <lib/subghz/types.h>
|
||||||
#include "subghz.h"
|
#include "subghz.h"
|
||||||
#include "views/receiver.h"
|
#include "views/receiver.h"
|
||||||
#include "views/transmitter.h"
|
#include "views/transmitter.h"
|
||||||
|
@ -11,8 +12,6 @@
|
||||||
#include "views/subghz_test_carrier.h"
|
#include "views/subghz_test_carrier.h"
|
||||||
#include "views/subghz_test_packet.h"
|
#include "views/subghz_test_packet.h"
|
||||||
|
|
||||||
// #include <furi.h>
|
|
||||||
// #include <furi_hal.h>
|
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
|
@ -24,14 +23,12 @@
|
||||||
#include <gui/modules/widget.h>
|
#include <gui/modules/widget.h>
|
||||||
|
|
||||||
#include <subghz/scenes/subghz_scene.h>
|
#include <subghz/scenes/subghz_scene.h>
|
||||||
|
|
||||||
#include <lib/subghz/subghz_worker.h>
|
#include <lib/subghz/subghz_worker.h>
|
||||||
|
#include <lib/subghz/subghz_setting.h>
|
||||||
#include <lib/subghz/receiver.h>
|
#include <lib/subghz/receiver.h>
|
||||||
#include <lib/subghz/transmitter.h>
|
#include <lib/subghz/transmitter.h>
|
||||||
|
|
||||||
#include "subghz_history.h"
|
#include "subghz_history.h"
|
||||||
#include "subghz_setting.h"
|
|
||||||
|
|
||||||
#include <gui/modules/variable_item_list.h>
|
#include <gui/modules/variable_item_list.h>
|
||||||
#include <lib/toolbox/path.h>
|
#include <lib/toolbox/path.h>
|
||||||
|
@ -49,7 +46,7 @@ struct SubGhzTxRx {
|
||||||
SubGhzProtocolDecoderBase* decoder_result;
|
SubGhzProtocolDecoderBase* decoder_result;
|
||||||
FlipperFormat* fff_data;
|
FlipperFormat* fff_data;
|
||||||
|
|
||||||
SubGhzPresetDefinition* preset;
|
SubGhzRadioPreset* preset;
|
||||||
SubGhzHistory* history;
|
SubGhzHistory* history;
|
||||||
uint16_t idx_menu_chosen;
|
uint16_t idx_menu_chosen;
|
||||||
SubGhzTxRxState txrx_state;
|
SubGhzTxRxState txrx_state;
|
||||||
|
|
|
@ -192,7 +192,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
|
||||||
} else {
|
} else {
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
}
|
}
|
||||||
canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
||||||
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));
|
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));
|
||||||
furi_string_reset(str_buff);
|
furi_string_reset(str_buff);
|
||||||
}
|
}
|
||||||
|
|
13
applications/plugins/weather_station/application.fam
Normal file
13
applications/plugins/weather_station/application.fam
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
App(
|
||||||
|
appid="weather_station",
|
||||||
|
name="Weather Station",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="weather_station_app",
|
||||||
|
cdefines=["APP_WEATHER_STATION"],
|
||||||
|
requires=["gui"],
|
||||||
|
stack_size=4 * 1024,
|
||||||
|
order=50,
|
||||||
|
fap_icon="weather_station_10px.png",
|
||||||
|
fap_category="Tools",
|
||||||
|
fap_icon_assets="images",
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
//WSCustomEvent
|
||||||
|
WSCustomEventStartId = 100,
|
||||||
|
|
||||||
|
WSCustomEventSceneSettingLock,
|
||||||
|
|
||||||
|
WSCustomEventViewReceiverOK,
|
||||||
|
WSCustomEventViewReceiverConfig,
|
||||||
|
WSCustomEventViewReceiverBack,
|
||||||
|
WSCustomEventViewReceiverOffDisplay,
|
||||||
|
WSCustomEventViewReceiverUnlock,
|
||||||
|
} WSCustomEvent;
|
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#define WS_VERSION_APP "0.1"
|
||||||
|
#define WS_DEVELOPED "SkorP"
|
||||||
|
#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
|
||||||
|
|
||||||
|
#define WS_KEY_FILE_VERSION 1
|
||||||
|
#define WS_KEY_FILE_TYPE "Flipper Weather Station Key File"
|
||||||
|
|
||||||
|
/** WSRxKeyState state */
|
||||||
|
typedef enum {
|
||||||
|
WSRxKeyStateIDLE,
|
||||||
|
WSRxKeyStateBack,
|
||||||
|
WSRxKeyStateStart,
|
||||||
|
WSRxKeyStateAddKey,
|
||||||
|
} WSRxKeyState;
|
||||||
|
|
||||||
|
/** WSHopperState state */
|
||||||
|
typedef enum {
|
||||||
|
WSHopperStateOFF,
|
||||||
|
WSHopperStateRunnig,
|
||||||
|
WSHopperStatePause,
|
||||||
|
WSHopperStateRSSITimeOut,
|
||||||
|
} WSHopperState;
|
||||||
|
|
||||||
|
/** WSLock */
|
||||||
|
typedef enum {
|
||||||
|
WSLockOff,
|
||||||
|
WSLockOn,
|
||||||
|
} WSLock;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WeatherStationViewVariableItemList,
|
||||||
|
WeatherStationViewSubmenu,
|
||||||
|
WeatherStationViewReceiver,
|
||||||
|
WeatherStationViewReceiverInfo,
|
||||||
|
WeatherStationViewWidget,
|
||||||
|
} WeatherStationView;
|
||||||
|
|
||||||
|
/** WeatherStationTxRx state */
|
||||||
|
typedef enum {
|
||||||
|
WSTxRxStateIDLE,
|
||||||
|
WSTxRxStateRx,
|
||||||
|
WSTxRxStateTx,
|
||||||
|
WSTxRxStateSleep,
|
||||||
|
} WSTxRxState;
|
BIN
applications/plugins/weather_station/images/Humid_10x15.png
Normal file
BIN
applications/plugins/weather_station/images/Humid_10x15.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/weather_station/images/Therm_7x16.png
Normal file
BIN
applications/plugins/weather_station/images/Therm_7x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/weather_station/images/station_icon.png
Normal file
BIN
applications/plugins/weather_station/images/station_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
341
applications/plugins/weather_station/protocols/gt_wt_03.c
Normal file
341
applications/plugins/weather_station/protocols/gt_wt_03.c
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
#include "gt_wt_03.h"
|
||||||
|
|
||||||
|
#define TAG "WSProtocolGT_WT03"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Help
|
||||||
|
* https://github.com/merbanan/rtl_433/blob/5f0ff6db624270a4598958ab9dd79bb385ced3ef/src/devices/gt_wt_03.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Globaltronics GT-WT-03 sensor on 433.92MHz.
|
||||||
|
* The 01-set sensor has 60 ms packet gap with 10 repeats.
|
||||||
|
* The 02-set sensor has no packet gap with 23 repeats.
|
||||||
|
* Example:
|
||||||
|
* {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes ]
|
||||||
|
* {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes Batt-Changed ]
|
||||||
|
* {41} 17 cf fe fa ea 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-No Batt-Changed ]
|
||||||
|
* {41} 01 cf 6f 11 b2 80 [ S2 C2 23,8 C 74.8 F 48% Bat-LOW Manual-No ]
|
||||||
|
* {41} 01 c8 d0 2b 76 80 [ S2 C3 -4,4 C 24.1 F 55% Bat-Good Manual-No Batt-Changed ]
|
||||||
|
* Format string:
|
||||||
|
* ID:8h HUM:8d B:b M:b C:2d TEMP:12d CHK:8h 1x
|
||||||
|
* Data layout:
|
||||||
|
* TYP IIIIIIII HHHHHHHH BMCCTTTT TTTTTTTT XXXXXXXX
|
||||||
|
* - I: Random Device Code: changes with battery reset
|
||||||
|
* - H: Humidity: 8 Bit 00-99, Display LL=10%, Display HH=110% (Range 20-95%)
|
||||||
|
* - B: Battery: 0=OK 1=LOW
|
||||||
|
* - M: Manual Send Button Pressed: 0=not pressed, 1=pressed
|
||||||
|
* - C: Channel: 00=CH1, 01=CH2, 10=CH3
|
||||||
|
* - T: Temperature: 12 Bit 2's complement, scaled by 10, range-50.0 C (-50.1 shown as Lo) to +70.0 C (+70.1 C is shown as Hi)
|
||||||
|
* - X: Checksum, xor shifting key per byte
|
||||||
|
* Humidity:
|
||||||
|
* - the working range is 20-95 %
|
||||||
|
* - if "LL" in display view it sends 10 %
|
||||||
|
* - if "HH" in display view it sends 110%
|
||||||
|
* Checksum:
|
||||||
|
* Per byte xor the key for each 1-bit, shift per bit. Key list per bit, starting at MSB:
|
||||||
|
* - 0x00 [07]
|
||||||
|
* - 0x80 [06]
|
||||||
|
* - 0x40 [05]
|
||||||
|
* - 0x20 [04]
|
||||||
|
* - 0x10 [03]
|
||||||
|
* - 0x88 [02]
|
||||||
|
* - 0xc4 [01]
|
||||||
|
* - 0x62 [00]
|
||||||
|
* Note: this can also be seen as lower byte of a Galois/Fibonacci LFSR-16, gen 0x00, init 0x3100 (or 0x62 if reversed) resetting at every byte.
|
||||||
|
* Battery voltages:
|
||||||
|
* - U=<2,65V +- ~5% Battery indicator
|
||||||
|
* - U=>2.10C +- 5% plausible readings
|
||||||
|
* - U=2,00V +- ~5% Temperature offset -5°C Humidity offset unknown
|
||||||
|
* - U=<1,95V +- ~5% does not initialize anymore
|
||||||
|
* - U=1,90V +- 5% temperature offset -15°C
|
||||||
|
* - U=1,80V +- 5% Display is showing refresh pattern
|
||||||
|
* - U=1.75V +- ~5% TX causes cut out
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const SubGhzBlockConst ws_protocol_gt_wt_03_const = {
|
||||||
|
.te_short = 285,
|
||||||
|
.te_long = 570,
|
||||||
|
.te_delta = 120,
|
||||||
|
.min_count_bit_for_found = 41,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolDecoderGT_WT03 {
|
||||||
|
SubGhzProtocolDecoderBase base;
|
||||||
|
|
||||||
|
SubGhzBlockDecoder decoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
|
||||||
|
uint16_t header_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolEncoderGT_WT03 {
|
||||||
|
SubGhzProtocolEncoderBase base;
|
||||||
|
|
||||||
|
SubGhzProtocolBlockEncoder encoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GT_WT03DecoderStepReset = 0,
|
||||||
|
GT_WT03DecoderStepCheckPreambule,
|
||||||
|
GT_WT03DecoderStepSaveDuration,
|
||||||
|
GT_WT03DecoderStepCheckDuration,
|
||||||
|
} GT_WT03DecoderStep;
|
||||||
|
|
||||||
|
const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder = {
|
||||||
|
.alloc = ws_protocol_decoder_gt_wt_03_alloc,
|
||||||
|
.free = ws_protocol_decoder_gt_wt_03_free,
|
||||||
|
|
||||||
|
.feed = ws_protocol_decoder_gt_wt_03_feed,
|
||||||
|
.reset = ws_protocol_decoder_gt_wt_03_reset,
|
||||||
|
|
||||||
|
.get_hash_data = ws_protocol_decoder_gt_wt_03_get_hash_data,
|
||||||
|
.serialize = ws_protocol_decoder_gt_wt_03_serialize,
|
||||||
|
.deserialize = ws_protocol_decoder_gt_wt_03_deserialize,
|
||||||
|
.get_string = ws_protocol_decoder_gt_wt_03_get_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder = {
|
||||||
|
.alloc = NULL,
|
||||||
|
.free = NULL,
|
||||||
|
|
||||||
|
.deserialize = NULL,
|
||||||
|
.stop = NULL,
|
||||||
|
.yield = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocol ws_protocol_gt_wt_03 = {
|
||||||
|
.name = WS_PROTOCOL_GT_WT_03_NAME,
|
||||||
|
.type = SubGhzProtocolWeatherStation,
|
||||||
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||||
|
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||||
|
|
||||||
|
.decoder = &ws_protocol_gt_wt_03_decoder,
|
||||||
|
.encoder = &ws_protocol_gt_wt_03_encoder,
|
||||||
|
};
|
||||||
|
|
||||||
|
void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment) {
|
||||||
|
UNUSED(environment);
|
||||||
|
WSProtocolDecoderGT_WT03* instance = malloc(sizeof(WSProtocolDecoderGT_WT03));
|
||||||
|
instance->base.protocol = &ws_protocol_gt_wt_03;
|
||||||
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_gt_wt_03_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderGT_WT03* instance = context;
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_gt_wt_03_reset(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderGT_WT03* instance = context;
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ws_protocol_gt_wt_03_check_crc(WSProtocolDecoderGT_WT03* instance) {
|
||||||
|
uint8_t msg[] = {
|
||||||
|
instance->decoder.decode_data >> 33,
|
||||||
|
instance->decoder.decode_data >> 25,
|
||||||
|
instance->decoder.decode_data >> 17,
|
||||||
|
instance->decoder.decode_data >> 9};
|
||||||
|
|
||||||
|
uint8_t sum = 0;
|
||||||
|
for(unsigned k = 0; k < sizeof(msg); ++k) {
|
||||||
|
uint8_t data = msg[k];
|
||||||
|
uint16_t key = 0x3100;
|
||||||
|
for(int i = 7; i >= 0; --i) {
|
||||||
|
// XOR key into sum if data bit is set
|
||||||
|
if((data >> i) & 1) sum ^= key & 0xff;
|
||||||
|
// roll the key right
|
||||||
|
key = (key >> 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ((sum ^ (uint8_t)((instance->decoder.decode_data >> 1) & 0xFF)) == 0x2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analysis of received data
|
||||||
|
* @param instance Pointer to a WSBlockGeneric* instance
|
||||||
|
*/
|
||||||
|
static void ws_protocol_gt_wt_03_remote_controller(WSBlockGeneric* instance) {
|
||||||
|
instance->id = instance->data >> 33;
|
||||||
|
instance->humidity = (instance->data >> 25) & 0xFF;
|
||||||
|
|
||||||
|
if(instance->humidity <= 10) { // actually the sensors sends 10 below working range of 20%
|
||||||
|
instance->humidity = 0;
|
||||||
|
} else if(instance->humidity > 95) { // actually the sensors sends 110 above working range of 90%
|
||||||
|
instance->humidity = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->battery_low = (instance->data >> 24) & 1;
|
||||||
|
instance->btn = (instance->data >> 23) & 1;
|
||||||
|
instance->channel = ((instance->data >> 21) & 0x03) + 1;
|
||||||
|
|
||||||
|
if(!((instance->data >> 20) & 1)) {
|
||||||
|
instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f;
|
||||||
|
} else {
|
||||||
|
instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderGT_WT03* instance = context;
|
||||||
|
|
||||||
|
switch(instance->decoder.parser_step) {
|
||||||
|
case GT_WT03DecoderStepReset:
|
||||||
|
if((level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta * 2)) {
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule;
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
instance->header_count = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GT_WT03DecoderStepCheckPreambule:
|
||||||
|
if(level) {
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
} else {
|
||||||
|
if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta * 2) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta * 2)) {
|
||||||
|
//Found preambule
|
||||||
|
instance->header_count++;
|
||||||
|
} else if(instance->header_count == 4) {
|
||||||
|
if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) <
|
||||||
|
ws_protocol_gt_wt_03_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 = GT_WT03DecoderStepSaveDuration;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) <
|
||||||
|
ws_protocol_gt_wt_03_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 = GT_WT03DecoderStepSaveDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepReset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepReset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GT_WT03DecoderStepSaveDuration:
|
||||||
|
if(level) {
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepCheckDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GT_WT03DecoderStepCheckDuration:
|
||||||
|
if(!level) {
|
||||||
|
if(((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta * 2) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta * 2))) {
|
||||||
|
if((instance->decoder.decode_count_bit ==
|
||||||
|
ws_protocol_gt_wt_03_const.min_count_bit_for_found) &&
|
||||||
|
ws_protocol_gt_wt_03_check_crc(instance)) {
|
||||||
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||||
|
ws_protocol_gt_wt_03_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->header_count = 1;
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule;
|
||||||
|
break;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) <
|
||||||
|
ws_protocol_gt_wt_03_const.te_delta)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepReset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = GT_WT03DecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderGT_WT03* instance = context;
|
||||||
|
return subghz_protocol_blocks_get_hash_data(
|
||||||
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_gt_wt_03_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderGT_WT03* instance = context;
|
||||||
|
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderGT_WT03* instance = context;
|
||||||
|
bool ret = false;
|
||||||
|
do {
|
||||||
|
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(instance->generic.data_count_bit !=
|
||||||
|
ws_protocol_gt_wt_03_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_gt_wt_03_get_string(void* context, FuriString* output) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderGT_WT03* 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
applications/plugins/weather_station/protocols/gt_wt_03.h
Normal file
79
applications/plugins/weather_station/protocols/gt_wt_03.h
Normal file
|
@ -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_GT_WT_03_NAME "GT-WT03"
|
||||||
|
|
||||||
|
typedef struct WSProtocolDecoderGT_WT03 WSProtocolDecoderGT_WT03;
|
||||||
|
typedef struct WSProtocolEncoderGT_WT03 WSProtocolEncoderGT_WT03;
|
||||||
|
|
||||||
|
extern const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder;
|
||||||
|
extern const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder;
|
||||||
|
extern const SubGhzProtocol ws_protocol_gt_wt_03;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate WSProtocolDecoderGT_WT03.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return WSProtocolDecoderGT_WT03* pointer to a WSProtocolDecoderGT_WT03 instance
|
||||||
|
*/
|
||||||
|
void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free WSProtocolDecoderGT_WT03.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderGT_WT03 instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_gt_wt_03_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset decoder WSProtocolDecoderGT_WT03.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderGT_WT03 instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_gt_wt_03_reset(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a raw sequence of levels and durations received from the air.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderGT_WT03 instance
|
||||||
|
* @param level Signal level true-high false-low
|
||||||
|
* @param duration Duration of this level in, us
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the hash sum of the last randomly received parcel.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderGT_WT03 instance
|
||||||
|
* @return hash Hash sum
|
||||||
|
*/
|
||||||
|
uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data WSProtocolDecoderGT_WT03.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderGT_WT03 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_gt_wt_03_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data WSProtocolDecoderGT_WT03.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderGT_WT03 instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting a textual representation of the received data.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderGT_WT03 instance
|
||||||
|
* @param output Resulting text
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output);
|
296
applications/plugins/weather_station/protocols/infactory.c
Normal file
296
applications/plugins/weather_station/protocols/infactory.c
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
#include "infactory.h"
|
||||||
|
|
||||||
|
#define TAG "WSProtocolInfactory"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Help
|
||||||
|
* https://github.com/merbanan/rtl_433/blob/master/src/devices/infactory.c
|
||||||
|
*
|
||||||
|
* Analysis using Genuino (see http://gitlab.com/hp-uno, e.g. uno_log_433):
|
||||||
|
* Observed On-Off-Key (OOK) data pattern:
|
||||||
|
* preamble syncPrefix data...(40 bit) syncPostfix
|
||||||
|
* HHLL HHLL HHLL HHLL HLLLLLLLLLLLLLLLL (HLLLL HLLLLLLLL HLLLL HLLLLLLLL ....) HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||||
|
* Breakdown:
|
||||||
|
* - four preamble pairs '1'/'0' each with a length of ca. 1000us
|
||||||
|
* - syncPre, syncPost, data0, data1 have a '1' start pulse of ca. 500us
|
||||||
|
* - syncPre pulse before dataPtr has a '0' pulse length of ca. 8000us
|
||||||
|
* - data0 (0-bits) have then a '0' pulse length of ca. 2000us
|
||||||
|
* - data1 (1-bits) have then a '0' pulse length of ca. 4000us
|
||||||
|
* - syncPost after dataPtr has a '0' pulse length of ca. 16000us
|
||||||
|
* This analysis is the reason for the new r_device definitions below.
|
||||||
|
* NB: pulse_slicer_ppm does not use .gap_limit if .tolerance is set.
|
||||||
|
*
|
||||||
|
* Outdoor sensor, transmits temperature and humidity data
|
||||||
|
* - inFactory NC-3982-913/NX-5817-902, Pearl (for FWS-686 station)
|
||||||
|
* - nor-tec 73383 (weather station + sensor), Schou Company AS, Denmark
|
||||||
|
* - DAY 73365 (weather station + sensor), Schou Company AS, Denmark
|
||||||
|
* Known brand names: inFactory, nor-tec, GreenBlue, DAY. Manufacturer in China.
|
||||||
|
* Transmissions includes an id. Every 60 seconds the sensor transmits 6 packets:
|
||||||
|
* 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 | 0110 0001
|
||||||
|
* iiii iiii | cccc ub?? | tttt tttt | tttt hhhh | hhhh ??nn
|
||||||
|
* - i: identification; changes on battery switch
|
||||||
|
* - c: CRC-4; CCITT checksum, see below for computation specifics
|
||||||
|
* - u: unknown; (sometimes set at power-on, but not always)
|
||||||
|
* - b: battery low; flag to indicate low battery voltage
|
||||||
|
* - h: Humidity; BCD-encoded, each nibble is one digit, 'A0' means 100%rH
|
||||||
|
* - t: Temperature; in °F as binary number with one decimal place + 90 °F offset
|
||||||
|
* - n: Channel; Channel number 1 - 3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const SubGhzBlockConst ws_protocol_infactory_const = {
|
||||||
|
.te_short = 500,
|
||||||
|
.te_long = 2000,
|
||||||
|
.te_delta = 150,
|
||||||
|
.min_count_bit_for_found = 40,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolDecoderInfactory {
|
||||||
|
SubGhzProtocolDecoderBase base;
|
||||||
|
|
||||||
|
SubGhzBlockDecoder decoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
|
||||||
|
uint16_t header_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolEncoderInfactory {
|
||||||
|
SubGhzProtocolEncoderBase base;
|
||||||
|
|
||||||
|
SubGhzProtocolBlockEncoder encoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
InfactoryDecoderStepReset = 0,
|
||||||
|
InfactoryDecoderStepCheckPreambule,
|
||||||
|
InfactoryDecoderStepSaveDuration,
|
||||||
|
InfactoryDecoderStepCheckDuration,
|
||||||
|
} InfactoryDecoderStep;
|
||||||
|
|
||||||
|
const SubGhzProtocolDecoder ws_protocol_infactory_decoder = {
|
||||||
|
.alloc = ws_protocol_decoder_infactory_alloc,
|
||||||
|
.free = ws_protocol_decoder_infactory_free,
|
||||||
|
|
||||||
|
.feed = ws_protocol_decoder_infactory_feed,
|
||||||
|
.reset = ws_protocol_decoder_infactory_reset,
|
||||||
|
|
||||||
|
.get_hash_data = ws_protocol_decoder_infactory_get_hash_data,
|
||||||
|
.serialize = ws_protocol_decoder_infactory_serialize,
|
||||||
|
.deserialize = ws_protocol_decoder_infactory_deserialize,
|
||||||
|
.get_string = ws_protocol_decoder_infactory_get_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolEncoder ws_protocol_infactory_encoder = {
|
||||||
|
.alloc = NULL,
|
||||||
|
.free = NULL,
|
||||||
|
|
||||||
|
.deserialize = NULL,
|
||||||
|
.stop = NULL,
|
||||||
|
.yield = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocol ws_protocol_infactory = {
|
||||||
|
.name = WS_PROTOCOL_INFACTORY_NAME,
|
||||||
|
.type = SubGhzProtocolWeatherStation,
|
||||||
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||||
|
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||||
|
|
||||||
|
.decoder = &ws_protocol_infactory_decoder,
|
||||||
|
.encoder = &ws_protocol_infactory_encoder,
|
||||||
|
};
|
||||||
|
|
||||||
|
void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment) {
|
||||||
|
UNUSED(environment);
|
||||||
|
WSProtocolDecoderInfactory* instance = malloc(sizeof(WSProtocolDecoderInfactory));
|
||||||
|
instance->base.protocol = &ws_protocol_infactory;
|
||||||
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_infactory_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderInfactory* instance = context;
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_infactory_reset(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderInfactory* instance = context;
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ws_protocol_infactory_check_crc(WSProtocolDecoderInfactory* instance) {
|
||||||
|
uint8_t msg[] = {
|
||||||
|
instance->decoder.decode_data >> 32,
|
||||||
|
(((instance->decoder.decode_data >> 24) & 0x0F) | (instance->decoder.decode_data & 0x0F)
|
||||||
|
<< 4),
|
||||||
|
instance->decoder.decode_data >> 16,
|
||||||
|
instance->decoder.decode_data >> 8,
|
||||||
|
instance->decoder.decode_data};
|
||||||
|
|
||||||
|
uint8_t crc =
|
||||||
|
subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704
|
||||||
|
crc ^= msg[4] >> 4; // last nibble is only XORed
|
||||||
|
return (crc == ((instance->decoder.decode_data >> 28) & 0x0F));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analysis of received data
|
||||||
|
* @param instance Pointer to a WSBlockGeneric* instance
|
||||||
|
*/
|
||||||
|
static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) {
|
||||||
|
instance->id = instance->data >> 32;
|
||||||
|
instance->battery_low = (instance->data >> 26) & 1;
|
||||||
|
instance->temp = ws_block_generic_fahrenheit_to_celsius(
|
||||||
|
((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
|
||||||
|
instance->humidity =
|
||||||
|
(((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH
|
||||||
|
instance->channel = instance->data & 0x03;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderInfactory* instance = context;
|
||||||
|
|
||||||
|
switch(instance->decoder.parser_step) {
|
||||||
|
case InfactoryDecoderStepReset:
|
||||||
|
if((level) && (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) <
|
||||||
|
ws_protocol_infactory_const.te_delta * 2)) {
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepCheckPreambule;
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
instance->header_count = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InfactoryDecoderStepCheckPreambule:
|
||||||
|
if(level) {
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
} else {
|
||||||
|
if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short * 2) <
|
||||||
|
ws_protocol_infactory_const.te_delta * 2) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) <
|
||||||
|
ws_protocol_infactory_const.te_delta * 2)) {
|
||||||
|
//Found preambule
|
||||||
|
instance->header_count++;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) <
|
||||||
|
ws_protocol_infactory_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 16) <
|
||||||
|
ws_protocol_infactory_const.te_delta * 8)) {
|
||||||
|
//Found syncPrefix
|
||||||
|
if(instance->header_count > 3) {
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepSaveDuration;
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepReset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InfactoryDecoderStepSaveDuration:
|
||||||
|
if(level) {
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepCheckDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InfactoryDecoderStepCheckDuration:
|
||||||
|
if(!level) {
|
||||||
|
if(duration >= ((uint32_t)ws_protocol_infactory_const.te_short * 30)) {
|
||||||
|
//Found syncPostfix
|
||||||
|
if((instance->decoder.decode_count_bit ==
|
||||||
|
ws_protocol_infactory_const.min_count_bit_for_found) &&
|
||||||
|
ws_protocol_infactory_check_crc(instance)) {
|
||||||
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||||
|
ws_protocol_infactory_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 = InfactoryDecoderStepReset;
|
||||||
|
break;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) <
|
||||||
|
ws_protocol_infactory_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_infactory_const.te_long) <
|
||||||
|
ws_protocol_infactory_const.te_delta * 2)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepSaveDuration;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) <
|
||||||
|
ws_protocol_infactory_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_infactory_const.te_long * 2) <
|
||||||
|
ws_protocol_infactory_const.te_delta * 4)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepSaveDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepReset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = InfactoryDecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderInfactory* instance = context;
|
||||||
|
return subghz_protocol_blocks_get_hash_data(
|
||||||
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_infactory_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderInfactory* instance = context;
|
||||||
|
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderInfactory* instance = context;
|
||||||
|
bool ret = false;
|
||||||
|
do {
|
||||||
|
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(instance->generic.data_count_bit !=
|
||||||
|
ws_protocol_infactory_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_infactory_get_string(void* context, FuriString* output) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderInfactory* 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
applications/plugins/weather_station/protocols/infactory.h
Normal file
79
applications/plugins/weather_station/protocols/infactory.h
Normal file
|
@ -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_INFACTORY_NAME "inFactory-TH"
|
||||||
|
|
||||||
|
typedef struct WSProtocolDecoderInfactory WSProtocolDecoderInfactory;
|
||||||
|
typedef struct WSProtocolEncoderInfactory WSProtocolEncoderInfactory;
|
||||||
|
|
||||||
|
extern const SubGhzProtocolDecoder ws_protocol_infactory_decoder;
|
||||||
|
extern const SubGhzProtocolEncoder ws_protocol_infactory_encoder;
|
||||||
|
extern const SubGhzProtocol ws_protocol_infactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate WSProtocolDecoderInfactory.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return WSProtocolDecoderInfactory* pointer to a WSProtocolDecoderInfactory instance
|
||||||
|
*/
|
||||||
|
void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free WSProtocolDecoderInfactory.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderInfactory instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_infactory_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset decoder WSProtocolDecoderInfactory.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderInfactory instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_infactory_reset(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a raw sequence of levels and durations received from the air.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderInfactory instance
|
||||||
|
* @param level Signal level true-high false-low
|
||||||
|
* @param duration Duration of this level in, us
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the hash sum of the last randomly received parcel.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderInfactory instance
|
||||||
|
* @return hash Hash sum
|
||||||
|
*/
|
||||||
|
uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data WSProtocolDecoderInfactory.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderInfactory 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_infactory_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data WSProtocolDecoderInfactory.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderInfactory instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting a textual representation of the received data.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderInfactory instance
|
||||||
|
* @param output Resulting text
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output);
|
261
applications/plugins/weather_station/protocols/nexus_th.c
Normal file
261
applications/plugins/weather_station/protocols/nexus_th.c
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
#include "nexus_th.h"
|
||||||
|
|
||||||
|
#define TAG "WSProtocolNexus_TH"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Help
|
||||||
|
* https://github.com/merbanan/rtl_433/blob/ef2d37cf51e3264d11cde9149ef87de2f0a4d37a/src/devices/nexus.c
|
||||||
|
*
|
||||||
|
* Nexus sensor protocol with ID, temperature and optional humidity
|
||||||
|
* also FreeTec (Pearl) NC-7345 sensors for FreeTec Weatherstation NC-7344,
|
||||||
|
* also infactory/FreeTec (Pearl) NX-3980 sensors for infactory/FreeTec NX-3974 station,
|
||||||
|
* also Solight TE82S sensors for Solight TE76/TE82/TE83/TE84 stations,
|
||||||
|
* also TFA 30.3209.02 temperature/humidity sensor.
|
||||||
|
* The sensor sends 36 bits 12 times,
|
||||||
|
* the packets are ppm modulated (distance coding) with a pulse of ~500 us
|
||||||
|
* followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a
|
||||||
|
* 1 bit, the sync gap is ~4000 us.
|
||||||
|
* The data is grouped in 9 nibbles:
|
||||||
|
* [id0] [id1] [flags] [temp0] [temp1] [temp2] [const] [humi0] [humi1]
|
||||||
|
* - The 8-bit id changes when the battery is changed in the sensor.
|
||||||
|
* - flags are 4 bits B 0 C C, where B is the battery status: 1=OK, 0=LOW
|
||||||
|
* - and CC is the channel: 0=CH1, 1=CH2, 2=CH3
|
||||||
|
* - temp is 12 bit signed scaled by 10
|
||||||
|
* - const is always 1111 (0x0F)
|
||||||
|
* - humidity is 8 bits
|
||||||
|
* The sensors can be bought at Clas Ohlsen (Nexus) and Pearl (infactory/FreeTec).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NEXUS_TH_CONST_DATA 0b1111
|
||||||
|
|
||||||
|
static const SubGhzBlockConst ws_protocol_nexus_th_const = {
|
||||||
|
.te_short = 500,
|
||||||
|
.te_long = 2000,
|
||||||
|
.te_delta = 150,
|
||||||
|
.min_count_bit_for_found = 36,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolDecoderNexus_TH {
|
||||||
|
SubGhzProtocolDecoderBase base;
|
||||||
|
|
||||||
|
SubGhzBlockDecoder decoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolEncoderNexus_TH {
|
||||||
|
SubGhzProtocolEncoderBase base;
|
||||||
|
|
||||||
|
SubGhzProtocolBlockEncoder encoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
Nexus_THDecoderStepReset = 0,
|
||||||
|
Nexus_THDecoderStepSaveDuration,
|
||||||
|
Nexus_THDecoderStepCheckDuration,
|
||||||
|
} Nexus_THDecoderStep;
|
||||||
|
|
||||||
|
const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder = {
|
||||||
|
.alloc = ws_protocol_decoder_nexus_th_alloc,
|
||||||
|
.free = ws_protocol_decoder_nexus_th_free,
|
||||||
|
|
||||||
|
.feed = ws_protocol_decoder_nexus_th_feed,
|
||||||
|
.reset = ws_protocol_decoder_nexus_th_reset,
|
||||||
|
|
||||||
|
.get_hash_data = ws_protocol_decoder_nexus_th_get_hash_data,
|
||||||
|
.serialize = ws_protocol_decoder_nexus_th_serialize,
|
||||||
|
.deserialize = ws_protocol_decoder_nexus_th_deserialize,
|
||||||
|
.get_string = ws_protocol_decoder_nexus_th_get_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder = {
|
||||||
|
.alloc = NULL,
|
||||||
|
.free = NULL,
|
||||||
|
|
||||||
|
.deserialize = NULL,
|
||||||
|
.stop = NULL,
|
||||||
|
.yield = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocol ws_protocol_nexus_th = {
|
||||||
|
.name = WS_PROTOCOL_NEXUS_TH_NAME,
|
||||||
|
.type = SubGhzProtocolWeatherStation,
|
||||||
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||||
|
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||||
|
|
||||||
|
.decoder = &ws_protocol_nexus_th_decoder,
|
||||||
|
.encoder = &ws_protocol_nexus_th_encoder,
|
||||||
|
};
|
||||||
|
|
||||||
|
void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment) {
|
||||||
|
UNUSED(environment);
|
||||||
|
WSProtocolDecoderNexus_TH* instance = malloc(sizeof(WSProtocolDecoderNexus_TH));
|
||||||
|
instance->base.protocol = &ws_protocol_nexus_th;
|
||||||
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_nexus_th_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderNexus_TH* instance = context;
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_nexus_th_reset(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderNexus_TH* instance = context;
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ws_protocol_nexus_th_check(WSProtocolDecoderNexus_TH* instance) {
|
||||||
|
uint8_t type = (instance->decoder.decode_data >> 8) & 0x0F;
|
||||||
|
|
||||||
|
if((type == NEXUS_TH_CONST_DATA) && ((instance->decoder.decode_data >> 4) != 0xffffffff)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analysis of received data
|
||||||
|
* @param instance Pointer to a WSBlockGeneric* instance
|
||||||
|
*/
|
||||||
|
static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) {
|
||||||
|
instance->id = (instance->data >> 28) & 0xFF;
|
||||||
|
instance->battery_low = !((instance->data >> 27) & 1);
|
||||||
|
instance->channel = ((instance->data >> 24) & 0x03) + 1;
|
||||||
|
|
||||||
|
if(!((instance->data >> 23) & 1)) {
|
||||||
|
instance->temp = (float)((instance->data >> 12) & 0x07FF) / 10.0f;
|
||||||
|
} else {
|
||||||
|
instance->temp = (float)((~(instance->data >> 12) & 0x07FF) + 1) / -10.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->humidity = instance->data & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderNexus_TH* instance = context;
|
||||||
|
|
||||||
|
switch(instance->decoder.parser_step) {
|
||||||
|
case Nexus_THDecoderStepReset:
|
||||||
|
if((!level) && (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) <
|
||||||
|
ws_protocol_nexus_th_const.te_delta * 4)) {
|
||||||
|
//Found sync
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration;
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nexus_THDecoderStepSaveDuration:
|
||||||
|
if(level) {
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nexus_THDecoderStepCheckDuration:
|
||||||
|
if(!level) {
|
||||||
|
if(DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) <
|
||||||
|
ws_protocol_nexus_th_const.te_delta * 4) {
|
||||||
|
//Found sync
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepReset;
|
||||||
|
if((instance->decoder.decode_count_bit ==
|
||||||
|
ws_protocol_nexus_th_const.min_count_bit_for_found) &&
|
||||||
|
ws_protocol_nexus_th_check(instance)) {
|
||||||
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||||
|
ws_protocol_nexus_th_remote_controller(&instance->generic);
|
||||||
|
if(instance->base.callback)
|
||||||
|
instance->base.callback(&instance->base, instance->base.context);
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration;
|
||||||
|
}
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) <
|
||||||
|
ws_protocol_nexus_th_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 2) <
|
||||||
|
ws_protocol_nexus_th_const.te_delta * 2)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) <
|
||||||
|
ws_protocol_nexus_th_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 4) <
|
||||||
|
ws_protocol_nexus_th_const.te_delta * 4)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepReset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = Nexus_THDecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderNexus_TH* instance = context;
|
||||||
|
return subghz_protocol_blocks_get_hash_data(
|
||||||
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_nexus_th_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderNexus_TH* instance = context;
|
||||||
|
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderNexus_TH* instance = context;
|
||||||
|
bool ret = false;
|
||||||
|
do {
|
||||||
|
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(instance->generic.data_count_bit !=
|
||||||
|
ws_protocol_nexus_th_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_nexus_th_get_string(void* context, FuriString* output) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderNexus_TH* 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
applications/plugins/weather_station/protocols/nexus_th.h
Normal file
79
applications/plugins/weather_station/protocols/nexus_th.h
Normal file
|
@ -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_NEXUS_TH_NAME "Nexus-TH"
|
||||||
|
|
||||||
|
typedef struct WSProtocolDecoderNexus_TH WSProtocolDecoderNexus_TH;
|
||||||
|
typedef struct WSProtocolEncoderNexus_TH WSProtocolEncoderNexus_TH;
|
||||||
|
|
||||||
|
extern const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder;
|
||||||
|
extern const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder;
|
||||||
|
extern const SubGhzProtocol ws_protocol_nexus_th;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate WSProtocolDecoderNexus_TH.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return WSProtocolDecoderNexus_TH* pointer to a WSProtocolDecoderNexus_TH instance
|
||||||
|
*/
|
||||||
|
void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free WSProtocolDecoderNexus_TH.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderNexus_TH instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_nexus_th_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset decoder WSProtocolDecoderNexus_TH.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderNexus_TH instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_nexus_th_reset(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a raw sequence of levels and durations received from the air.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderNexus_TH instance
|
||||||
|
* @param level Signal level true-high false-low
|
||||||
|
* @param duration Duration of this level in, us
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the hash sum of the last randomly received parcel.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderNexus_TH instance
|
||||||
|
* @return hash Hash sum
|
||||||
|
*/
|
||||||
|
uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data WSProtocolDecoderNexus_TH.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderNexus_TH 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_nexus_th_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data WSProtocolDecoderNexus_TH.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderNexus_TH instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting a textual representation of the received data.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderNexus_TH instance
|
||||||
|
* @param output Resulting text
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output);
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "protocol_items.h"
|
||||||
|
|
||||||
|
const SubGhzProtocol* weather_station_protocol_registry_items[] = {
|
||||||
|
&ws_protocol_infactory,
|
||||||
|
&ws_protocol_thermopro_tx4,
|
||||||
|
&ws_protocol_nexus_th,
|
||||||
|
&ws_protocol_gt_wt_03,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolRegistry weather_station_protocol_registry = {
|
||||||
|
.items = weather_station_protocol_registry_items,
|
||||||
|
.size = COUNT_OF(weather_station_protocol_registry_items)};
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
|
||||||
|
#include "infactory.h"
|
||||||
|
#include "thermopro_tx4.h"
|
||||||
|
#include "nexus_th.h"
|
||||||
|
#include "gt_wt_03.h"
|
||||||
|
|
||||||
|
extern const SubGhzProtocolRegistry weather_station_protocol_registry;
|
260
applications/plugins/weather_station/protocols/thermopro_tx4.c
Normal file
260
applications/plugins/weather_station/protocols/thermopro_tx4.c
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
#include "thermopro_tx4.h"
|
||||||
|
|
||||||
|
#define TAG "WSProtocolThermoPRO_TX4"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Help
|
||||||
|
* https://github.com/merbanan/rtl_433/blob/master/src/devices/thermopro_tx2.c
|
||||||
|
*
|
||||||
|
* The sensor sends 37 bits 6 times, before the first packet there is a sync pulse.
|
||||||
|
* The packets are ppm modulated (distance coding) with a pulse of ~500 us
|
||||||
|
* followed by a short gap of ~2000 us for a 0 bit or a long ~4000 us gap for a
|
||||||
|
* 1 bit, the sync gap is ~9000 us.
|
||||||
|
* The data is grouped in 9 nibbles
|
||||||
|
* [type] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1]
|
||||||
|
* - type: 4 bit fixed 1001 (9) or 0110 (5)
|
||||||
|
* - id: 8 bit a random id that is generated when the sensor starts, could include battery status
|
||||||
|
* the same batteries often generate the same id
|
||||||
|
* - flags(3): is 1 when the battery is low, otherwise 0 (ok)
|
||||||
|
* - flags(2): is 1 when the sensor sends a reading when pressing the button on the sensor
|
||||||
|
* - flags(1,0): the channel number that can be set by the sensor (1, 2, 3, X)
|
||||||
|
* - temp: 12 bit signed scaled by 10
|
||||||
|
* - humi: 8 bit always 11001100 (0xCC) if no humidity sensor is available
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define THERMO_PRO_TX4_TYPE_1 0b1001
|
||||||
|
#define THERMO_PRO_TX4_TYPE_2 0b0110
|
||||||
|
|
||||||
|
static const SubGhzBlockConst ws_protocol_thermopro_tx4_const = {
|
||||||
|
.te_short = 500,
|
||||||
|
.te_long = 2000,
|
||||||
|
.te_delta = 150,
|
||||||
|
.min_count_bit_for_found = 37,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolDecoderThermoPRO_TX4 {
|
||||||
|
SubGhzProtocolDecoderBase base;
|
||||||
|
|
||||||
|
SubGhzBlockDecoder decoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WSProtocolEncoderThermoPRO_TX4 {
|
||||||
|
SubGhzProtocolEncoderBase base;
|
||||||
|
|
||||||
|
SubGhzProtocolBlockEncoder encoder;
|
||||||
|
WSBlockGeneric generic;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ThermoPRO_TX4DecoderStepReset = 0,
|
||||||
|
ThermoPRO_TX4DecoderStepSaveDuration,
|
||||||
|
ThermoPRO_TX4DecoderStepCheckDuration,
|
||||||
|
} ThermoPRO_TX4DecoderStep;
|
||||||
|
|
||||||
|
const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder = {
|
||||||
|
.alloc = ws_protocol_decoder_thermopro_tx4_alloc,
|
||||||
|
.free = ws_protocol_decoder_thermopro_tx4_free,
|
||||||
|
|
||||||
|
.feed = ws_protocol_decoder_thermopro_tx4_feed,
|
||||||
|
.reset = ws_protocol_decoder_thermopro_tx4_reset,
|
||||||
|
|
||||||
|
.get_hash_data = ws_protocol_decoder_thermopro_tx4_get_hash_data,
|
||||||
|
.serialize = ws_protocol_decoder_thermopro_tx4_serialize,
|
||||||
|
.deserialize = ws_protocol_decoder_thermopro_tx4_deserialize,
|
||||||
|
.get_string = ws_protocol_decoder_thermopro_tx4_get_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder = {
|
||||||
|
.alloc = NULL,
|
||||||
|
.free = NULL,
|
||||||
|
|
||||||
|
.deserialize = NULL,
|
||||||
|
.stop = NULL,
|
||||||
|
.yield = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocol ws_protocol_thermopro_tx4 = {
|
||||||
|
.name = WS_PROTOCOL_THERMOPRO_TX4_NAME,
|
||||||
|
.type = SubGhzProtocolWeatherStation,
|
||||||
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||||
|
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||||
|
|
||||||
|
.decoder = &ws_protocol_thermopro_tx4_decoder,
|
||||||
|
.encoder = &ws_protocol_thermopro_tx4_encoder,
|
||||||
|
};
|
||||||
|
|
||||||
|
void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment) {
|
||||||
|
UNUSED(environment);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* instance = malloc(sizeof(WSProtocolDecoderThermoPRO_TX4));
|
||||||
|
instance->base.protocol = &ws_protocol_thermopro_tx4;
|
||||||
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_thermopro_tx4_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* instance = context;
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_thermopro_tx4_reset(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* instance = context;
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ws_protocol_thermopro_tx4_check(WSProtocolDecoderThermoPRO_TX4* instance) {
|
||||||
|
uint8_t type = instance->decoder.decode_data >> 33;
|
||||||
|
|
||||||
|
if((type == THERMO_PRO_TX4_TYPE_1) || (type == THERMO_PRO_TX4_TYPE_2)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analysis of received data
|
||||||
|
* @param instance Pointer to a WSBlockGeneric* instance
|
||||||
|
*/
|
||||||
|
static void ws_protocol_thermopro_tx4_remote_controller(WSBlockGeneric* instance) {
|
||||||
|
instance->id = (instance->data >> 25) & 0xFF;
|
||||||
|
instance->battery_low = (instance->data >> 24) & 1;
|
||||||
|
instance->btn = (instance->data >> 23) & 1;
|
||||||
|
instance->channel = ((instance->data >> 21) & 0x03) + 1;
|
||||||
|
|
||||||
|
if(!((instance->data >> 20) & 1)) {
|
||||||
|
instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f;
|
||||||
|
} else {
|
||||||
|
instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->humidity = (instance->data >> 1) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* instance = context;
|
||||||
|
|
||||||
|
switch(instance->decoder.parser_step) {
|
||||||
|
case ThermoPRO_TX4DecoderStepReset:
|
||||||
|
if((!level) && (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) <
|
||||||
|
ws_protocol_thermopro_tx4_const.te_delta * 10)) {
|
||||||
|
//Found sync
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration;
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ThermoPRO_TX4DecoderStepSaveDuration:
|
||||||
|
if(level) {
|
||||||
|
instance->decoder.te_last = duration;
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ThermoPRO_TX4DecoderStepCheckDuration:
|
||||||
|
if(!level) {
|
||||||
|
if(DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) <
|
||||||
|
ws_protocol_thermopro_tx4_const.te_delta * 10) {
|
||||||
|
//Found sync
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset;
|
||||||
|
if((instance->decoder.decode_count_bit ==
|
||||||
|
ws_protocol_thermopro_tx4_const.min_count_bit_for_found) &&
|
||||||
|
ws_protocol_thermopro_tx4_check(instance)) {
|
||||||
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||||
|
ws_protocol_thermopro_tx4_remote_controller(&instance->generic);
|
||||||
|
if(instance->base.callback)
|
||||||
|
instance->base.callback(&instance->base, instance->base.context);
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration;
|
||||||
|
}
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(
|
||||||
|
instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) <
|
||||||
|
ws_protocol_thermopro_tx4_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long) <
|
||||||
|
ws_protocol_thermopro_tx4_const.te_delta * 2)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration;
|
||||||
|
} else if(
|
||||||
|
(DURATION_DIFF(
|
||||||
|
instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) <
|
||||||
|
ws_protocol_thermopro_tx4_const.te_delta) &&
|
||||||
|
(DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long * 2) <
|
||||||
|
ws_protocol_thermopro_tx4_const.te_delta * 4)) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration;
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* instance = context;
|
||||||
|
return subghz_protocol_blocks_get_hash_data(
|
||||||
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_thermopro_tx4_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* instance = context;
|
||||||
|
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* instance = context;
|
||||||
|
bool ret = false;
|
||||||
|
do {
|
||||||
|
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(instance->generic.data_count_bit !=
|
||||||
|
ws_protocol_thermopro_tx4_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_thermopro_tx4_get_string(void* context, FuriString* output) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSProtocolDecoderThermoPRO_TX4* 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_THERMOPRO_TX4_NAME "ThermoPRO-TX4"
|
||||||
|
|
||||||
|
typedef struct WSProtocolDecoderThermoPRO_TX4 WSProtocolDecoderThermoPRO_TX4;
|
||||||
|
typedef struct WSProtocolEncoderThermoPRO_TX4 WSProtocolEncoderThermoPRO_TX4;
|
||||||
|
|
||||||
|
extern const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder;
|
||||||
|
extern const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder;
|
||||||
|
extern const SubGhzProtocol ws_protocol_thermopro_tx4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate WSProtocolDecoderThermoPRO_TX4.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return WSProtocolDecoderThermoPRO_TX4* pointer to a WSProtocolDecoderThermoPRO_TX4 instance
|
||||||
|
*/
|
||||||
|
void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free WSProtocolDecoderThermoPRO_TX4.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_thermopro_tx4_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset decoder WSProtocolDecoderThermoPRO_TX4.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_thermopro_tx4_reset(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a raw sequence of levels and durations received from the air.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance
|
||||||
|
* @param level Signal level true-high false-low
|
||||||
|
* @param duration Duration of this level in, us
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the hash sum of the last randomly received parcel.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance
|
||||||
|
* @return hash Hash sum
|
||||||
|
*/
|
||||||
|
uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data WSProtocolDecoderThermoPRO_TX4.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 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_thermopro_tx4_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data WSProtocolDecoderThermoPRO_TX4.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting a textual representation of the received data.
|
||||||
|
* @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance
|
||||||
|
* @param output Resulting text
|
||||||
|
*/
|
||||||
|
void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output);
|
198
applications/plugins/weather_station/protocols/ws_generic.c
Normal file
198
applications/plugins/weather_station/protocols/ws_generic.c
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
#include "ws_generic.h"
|
||||||
|
#include <lib/toolbox/stream/stream.h>
|
||||||
|
#include <lib/flipper_format/flipper_format_i.h>
|
||||||
|
#include "../helpers/weather_station_types.h"
|
||||||
|
|
||||||
|
#define TAG "WSBlockGeneric"
|
||||||
|
|
||||||
|
void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) {
|
||||||
|
const char* preset_name_temp;
|
||||||
|
if(!strcmp(preset_name, "AM270")) {
|
||||||
|
preset_name_temp = "FuriHalSubGhzPresetOok270Async";
|
||||||
|
} else if(!strcmp(preset_name, "AM650")) {
|
||||||
|
preset_name_temp = "FuriHalSubGhzPresetOok650Async";
|
||||||
|
} else if(!strcmp(preset_name, "FM238")) {
|
||||||
|
preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async";
|
||||||
|
} else if(!strcmp(preset_name, "FM476")) {
|
||||||
|
preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async";
|
||||||
|
} else {
|
||||||
|
preset_name_temp = "FuriHalSubGhzPresetCustom";
|
||||||
|
}
|
||||||
|
furi_string_set(preset_str, preset_name_temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_block_generic_serialize(
|
||||||
|
WSBlockGeneric* instance,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(instance);
|
||||||
|
bool res = false;
|
||||||
|
FuriString* temp_str;
|
||||||
|
temp_str = furi_string_alloc();
|
||||||
|
do {
|
||||||
|
stream_clean(flipper_format_get_raw_stream(flipper_format));
|
||||||
|
if(!flipper_format_write_header_cstr(
|
||||||
|
flipper_format, WS_KEY_FILE_TYPE, WS_KEY_FILE_VERSION)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add header");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Frequency");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);
|
||||||
|
if(!flipper_format_write_string_cstr(
|
||||||
|
flipper_format, "Preset", furi_string_get_cstr(temp_str))) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Preset");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
|
||||||
|
if(!flipper_format_write_string_cstr(
|
||||||
|
flipper_format, "Custom_preset_module", "CC1101")) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Custom_preset_module");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_write_hex(
|
||||||
|
flipper_format, "Custom_preset_data", preset->data, preset->data_size)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Custom_preset_data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Protocol");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t temp_data = instance->id;
|
||||||
|
if(!flipper_format_write_uint32(flipper_format, "Id", &temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Id");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_data = instance->data_count_bit;
|
||||||
|
if(!flipper_format_write_uint32(flipper_format, "Bit", &temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Bit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||||
|
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
||||||
|
key_data[sizeof(uint64_t) - i - 1] = (instance->data >> i * 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_data = instance->battery_low;
|
||||||
|
if(!flipper_format_write_uint32(flipper_format, "Batt", &temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Battery_low");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_data = instance->humidity;
|
||||||
|
if(!flipper_format_write_uint32(flipper_format, "Hum", &temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Humidity");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_data = instance->channel;
|
||||||
|
if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Channel");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// temp_data = instance->btn;
|
||||||
|
// if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) {
|
||||||
|
// FURI_LOG_E(TAG, "Unable to add Btn");
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
float temp = instance->temp;
|
||||||
|
if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to add Temperature");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = true;
|
||||||
|
} while(false);
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(instance);
|
||||||
|
bool res = false;
|
||||||
|
uint32_t temp_data = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(!flipper_format_rewind(flipper_format)) {
|
||||||
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(flipper_format, "Id", (uint32_t*)&temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Id");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->id = (uint32_t)temp_data;
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Bit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->data_count_bit = (uint8_t)temp_data;
|
||||||
|
|
||||||
|
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||||
|
if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||||
|
instance->data = instance->data << 8 | key_data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(flipper_format, "Batt", (uint32_t*)&temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Battery_low");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->battery_low = (uint8_t)temp_data;
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(flipper_format, "Hum", (uint32_t*)&temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Humidity");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->humidity = (uint8_t)temp_data;
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Channel");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->channel = (uint8_t)temp_data;
|
||||||
|
|
||||||
|
// if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) {
|
||||||
|
// FURI_LOG_E(TAG, "Missing Btn");
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// instance->btn = (uint8_t)temp_data;
|
||||||
|
|
||||||
|
float temp;
|
||||||
|
if(!flipper_format_read_float(flipper_format, "Temp", (float*)&temp, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Temperature");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->temp = temp;
|
||||||
|
|
||||||
|
res = true;
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ws_block_generic_fahrenheit_to_celsius(float fahrenheit) {
|
||||||
|
return (fahrenheit - 32.0f) / 1.8f;
|
||||||
|
}
|
61
applications/plugins/weather_station/protocols/ws_generic.h
Normal file
61
applications/plugins/weather_station/protocols/ws_generic.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <lib/flipper_format/flipper_format.h>
|
||||||
|
#include "furi.h"
|
||||||
|
#include "furi_hal.h"
|
||||||
|
#include <lib/subghz/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct WSBlockGeneric WSBlockGeneric;
|
||||||
|
|
||||||
|
struct WSBlockGeneric {
|
||||||
|
const char* protocol_name;
|
||||||
|
uint64_t data;
|
||||||
|
uint32_t id;
|
||||||
|
uint8_t data_count_bit;
|
||||||
|
uint8_t battery_low;
|
||||||
|
uint8_t humidity;
|
||||||
|
uint8_t channel;
|
||||||
|
uint8_t btn;
|
||||||
|
float temp;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name preset.
|
||||||
|
* @param preset_name name preset
|
||||||
|
* @param preset_str Output name preset
|
||||||
|
*/
|
||||||
|
void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data WSBlockGeneric.
|
||||||
|
* @param instance Pointer to a WSBlockGeneric 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_block_generic_serialize(
|
||||||
|
WSBlockGeneric* instance,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data WSBlockGeneric.
|
||||||
|
* @param instance Pointer to a WSBlockGeneric instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
float ws_block_generic_fahrenheit_to_celsius(float fahrenheit);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,207 @@
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
#include "../views/weather_station_receiver.h"
|
||||||
|
|
||||||
|
static const NotificationSequence subghs_sequence_rx = {
|
||||||
|
&message_green_255,
|
||||||
|
|
||||||
|
&message_vibro_on,
|
||||||
|
&message_note_c6,
|
||||||
|
&message_delay_50,
|
||||||
|
&message_sound_off,
|
||||||
|
&message_vibro_off,
|
||||||
|
|
||||||
|
&message_delay_50,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const NotificationSequence subghs_sequence_rx_locked = {
|
||||||
|
&message_green_255,
|
||||||
|
|
||||||
|
&message_display_backlight_on,
|
||||||
|
|
||||||
|
&message_vibro_on,
|
||||||
|
&message_note_c6,
|
||||||
|
&message_delay_50,
|
||||||
|
&message_sound_off,
|
||||||
|
&message_vibro_off,
|
||||||
|
|
||||||
|
&message_delay_500,
|
||||||
|
|
||||||
|
&message_display_backlight_off,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void weather_station_scene_receiver_update_statusbar(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
FuriString* history_stat_str;
|
||||||
|
history_stat_str = furi_string_alloc();
|
||||||
|
if(!ws_history_get_text_space_left(app->txrx->history, history_stat_str)) {
|
||||||
|
FuriString* frequency_str;
|
||||||
|
FuriString* modulation_str;
|
||||||
|
|
||||||
|
frequency_str = furi_string_alloc();
|
||||||
|
modulation_str = furi_string_alloc();
|
||||||
|
|
||||||
|
ws_get_frequency_modulation(app, frequency_str, modulation_str);
|
||||||
|
|
||||||
|
ws_view_receiver_add_data_statusbar(
|
||||||
|
app->ws_receiver,
|
||||||
|
furi_string_get_cstr(frequency_str),
|
||||||
|
furi_string_get_cstr(modulation_str),
|
||||||
|
furi_string_get_cstr(history_stat_str));
|
||||||
|
|
||||||
|
furi_string_free(frequency_str);
|
||||||
|
furi_string_free(modulation_str);
|
||||||
|
} else {
|
||||||
|
ws_view_receiver_add_data_statusbar(
|
||||||
|
app->ws_receiver, furi_string_get_cstr(history_stat_str), "", "");
|
||||||
|
}
|
||||||
|
furi_string_free(history_stat_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_callback(WSCustomEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void weather_station_scene_receiver_add_to_history_callback(
|
||||||
|
SubGhzReceiver* receiver,
|
||||||
|
SubGhzProtocolDecoderBase* decoder_base,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
FuriString* str_buff;
|
||||||
|
str_buff = furi_string_alloc();
|
||||||
|
|
||||||
|
if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) ==
|
||||||
|
WSHistoryStateAddKeyNewDada) {
|
||||||
|
furi_string_reset(str_buff);
|
||||||
|
|
||||||
|
ws_history_get_text_item_menu(
|
||||||
|
app->txrx->history, str_buff, ws_history_get_item(app->txrx->history) - 1);
|
||||||
|
ws_view_receiver_add_item_to_menu(
|
||||||
|
app->ws_receiver,
|
||||||
|
furi_string_get_cstr(str_buff),
|
||||||
|
ws_history_get_type_protocol(
|
||||||
|
app->txrx->history, ws_history_get_item(app->txrx->history) - 1));
|
||||||
|
|
||||||
|
weather_station_scene_receiver_update_statusbar(app);
|
||||||
|
notification_message(app->notifications, &sequence_blink_green_10);
|
||||||
|
if(app->lock != WSLockOn) {
|
||||||
|
notification_message(app->notifications, &subghs_sequence_rx);
|
||||||
|
} else {
|
||||||
|
notification_message(app->notifications, &subghs_sequence_rx_locked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subghz_receiver_reset(receiver);
|
||||||
|
furi_string_free(str_buff);
|
||||||
|
app->txrx->rx_key_state = WSRxKeyStateAddKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_on_enter(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
|
||||||
|
FuriString* str_buff;
|
||||||
|
str_buff = furi_string_alloc();
|
||||||
|
|
||||||
|
if(app->txrx->rx_key_state == WSRxKeyStateIDLE) {
|
||||||
|
ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0);
|
||||||
|
ws_history_reset(app->txrx->history);
|
||||||
|
app->txrx->rx_key_state = WSRxKeyStateStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_view_receiver_set_lock(app->ws_receiver, app->lock);
|
||||||
|
|
||||||
|
//Load history to receiver
|
||||||
|
ws_view_receiver_exit(app->ws_receiver);
|
||||||
|
for(uint8_t i = 0; i < ws_history_get_item(app->txrx->history); i++) {
|
||||||
|
furi_string_reset(str_buff);
|
||||||
|
ws_history_get_text_item_menu(app->txrx->history, str_buff, i);
|
||||||
|
ws_view_receiver_add_item_to_menu(
|
||||||
|
app->ws_receiver,
|
||||||
|
furi_string_get_cstr(str_buff),
|
||||||
|
ws_history_get_type_protocol(app->txrx->history, i));
|
||||||
|
app->txrx->rx_key_state = WSRxKeyStateAddKey;
|
||||||
|
}
|
||||||
|
furi_string_free(str_buff);
|
||||||
|
weather_station_scene_receiver_update_statusbar(app);
|
||||||
|
|
||||||
|
ws_view_receiver_set_callback(app->ws_receiver, weather_station_scene_receiver_callback, app);
|
||||||
|
subghz_receiver_set_rx_callback(
|
||||||
|
app->txrx->receiver, weather_station_scene_receiver_add_to_history_callback, app);
|
||||||
|
|
||||||
|
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||||
|
ws_rx_end(app);
|
||||||
|
};
|
||||||
|
if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) {
|
||||||
|
ws_begin(
|
||||||
|
app,
|
||||||
|
subghz_setting_get_preset_data_by_name(
|
||||||
|
app->setting, furi_string_get_cstr(app->txrx->preset->name)));
|
||||||
|
|
||||||
|
ws_rx(app, app->txrx->preset->frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_view_receiver_set_idx_menu(app->ws_receiver, app->txrx->idx_menu_chosen);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case WSCustomEventViewReceiverBack:
|
||||||
|
// Stop CC1101 Rx
|
||||||
|
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||||
|
ws_rx_end(app);
|
||||||
|
ws_sleep(app);
|
||||||
|
};
|
||||||
|
app->txrx->hopper_state = WSHopperStateOFF;
|
||||||
|
app->txrx->idx_menu_chosen = 0;
|
||||||
|
subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app);
|
||||||
|
|
||||||
|
app->txrx->rx_key_state = WSRxKeyStateIDLE;
|
||||||
|
ws_preset_init(
|
||||||
|
app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0);
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
app->scene_manager, WeatherStationSceneStart);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case WSCustomEventViewReceiverOK:
|
||||||
|
app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver);
|
||||||
|
scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverInfo);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case WSCustomEventViewReceiverConfig:
|
||||||
|
app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver);
|
||||||
|
scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverConfig);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case WSCustomEventViewReceiverOffDisplay:
|
||||||
|
notification_message(app->notifications, &sequence_display_backlight_off);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case WSCustomEventViewReceiverUnlock:
|
||||||
|
app->lock = WSLockOff;
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeTick) {
|
||||||
|
if(app->txrx->hopper_state != WSHopperStateOFF) {
|
||||||
|
ws_hopper_update(app);
|
||||||
|
weather_station_scene_receiver_update_statusbar(app);
|
||||||
|
}
|
||||||
|
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||||
|
notification_message(app->notifications, &sequence_blink_cyan_10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_on_exit(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
|
||||||
|
// Generate scene on_enter handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||||
|
void (*const weather_station_scene_on_enter_handlers[])(void*) = {
|
||||||
|
#include "weather_station_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_event handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||||
|
bool (*const weather_station_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||||
|
#include "weather_station_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_exit handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||||
|
void (*const weather_station_scene_on_exit_handlers[])(void* context) = {
|
||||||
|
#include "weather_station_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Initialize scene handlers configuration structure
|
||||||
|
const SceneManagerHandlers weather_station_scene_handlers = {
|
||||||
|
.on_enter_handlers = weather_station_scene_on_enter_handlers,
|
||||||
|
.on_event_handlers = weather_station_scene_on_event_handlers,
|
||||||
|
.on_exit_handlers = weather_station_scene_on_exit_handlers,
|
||||||
|
.scene_num = WeatherStationSceneNum,
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
|
||||||
|
// Generate scene id and total number
|
||||||
|
#define ADD_SCENE(prefix, name, id) WeatherStationScene##id,
|
||||||
|
typedef enum {
|
||||||
|
#include "weather_station_scene_config.h"
|
||||||
|
WeatherStationSceneNum,
|
||||||
|
} WeatherStationScene;
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
extern const SceneManagerHandlers weather_station_scene_handlers;
|
||||||
|
|
||||||
|
// Generate scene on_enter handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||||
|
#include "weather_station_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_event handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) \
|
||||||
|
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||||
|
#include "weather_station_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_exit handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||||
|
#include "weather_station_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
#include "../helpers/weather_station_types.h"
|
||||||
|
|
||||||
|
void weather_station_scene_about_widget_callback(
|
||||||
|
GuiButtonType result,
|
||||||
|
InputType type,
|
||||||
|
void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
if(type == InputTypeShort) {
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_about_on_enter(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
|
||||||
|
FuriString* temp_str;
|
||||||
|
temp_str = furi_string_alloc();
|
||||||
|
furi_string_printf(temp_str, "\e#%s\n", "Information");
|
||||||
|
|
||||||
|
furi_string_cat_printf(temp_str, "Version: %s\n", WS_VERSION_APP);
|
||||||
|
furi_string_cat_printf(temp_str, "Developed by: %s\n", WS_DEVELOPED);
|
||||||
|
furi_string_cat_printf(temp_str, "Github: %s\n\n", WS_GITHUB);
|
||||||
|
|
||||||
|
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str, "Reading messages from\nweather station that work\nwith SubGhz sensors\n\n");
|
||||||
|
|
||||||
|
furi_string_cat_printf(temp_str, "Supported protocols:\n");
|
||||||
|
size_t i = 0;
|
||||||
|
const char* protocol_name =
|
||||||
|
subghz_environment_get_protocol_name_registry(app->txrx->environment, i++);
|
||||||
|
do {
|
||||||
|
furi_string_cat_printf(temp_str, "%s\n", protocol_name);
|
||||||
|
protocol_name = subghz_environment_get_protocol_name_registry(app->txrx->environment, i++);
|
||||||
|
} while(protocol_name != NULL);
|
||||||
|
|
||||||
|
widget_add_text_box_element(
|
||||||
|
app->widget,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
14,
|
||||||
|
AlignCenter,
|
||||||
|
AlignBottom,
|
||||||
|
"\e#\e! \e!\n",
|
||||||
|
false);
|
||||||
|
widget_add_text_box_element(
|
||||||
|
app->widget,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
128,
|
||||||
|
14,
|
||||||
|
AlignCenter,
|
||||||
|
AlignBottom,
|
||||||
|
"\e#\e! Weather station \e!\n",
|
||||||
|
false);
|
||||||
|
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool weather_station_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
UNUSED(app);
|
||||||
|
UNUSED(event);
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_about_on_exit(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
|
||||||
|
// Clear views
|
||||||
|
widget_reset(app->widget);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
ADD_SCENE(weather_station, start, Start)
|
||||||
|
ADD_SCENE(weather_station, about, About)
|
||||||
|
ADD_SCENE(weather_station, receiver, Receiver)
|
||||||
|
ADD_SCENE(weather_station, receiver_config, ReceiverConfig)
|
||||||
|
ADD_SCENE(weather_station, receiver_info, ReceiverInfo)
|
|
@ -0,0 +1,223 @@
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
|
||||||
|
enum WSSettingIndex {
|
||||||
|
WSSettingIndexFrequency,
|
||||||
|
WSSettingIndexHopping,
|
||||||
|
WSSettingIndexModulation,
|
||||||
|
WSSettingIndexLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HOPPING_COUNT 2
|
||||||
|
const char* const hopping_text[HOPPING_COUNT] = {
|
||||||
|
"OFF",
|
||||||
|
"ON",
|
||||||
|
};
|
||||||
|
const uint32_t hopping_value[HOPPING_COUNT] = {
|
||||||
|
WSHopperStateOFF,
|
||||||
|
WSHopperStateRunnig,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t weather_station_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
uint8_t index = 0;
|
||||||
|
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) {
|
||||||
|
if(value == subghz_setting_get_frequency(app->setting, i)) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
index = subghz_setting_get_frequency_default_index(app->setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t weather_station_scene_receiver_config_next_preset(const char* preset_name, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
uint8_t index = 0;
|
||||||
|
for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) {
|
||||||
|
if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// index = subghz_setting_get_frequency_default_index(app ->setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t weather_station_scene_receiver_config_hopper_value_index(
|
||||||
|
const uint32_t value,
|
||||||
|
const uint32_t values[],
|
||||||
|
uint8_t values_count,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
UNUSED(values_count);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
|
||||||
|
if(value == values[0]) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
|
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||||
|
" -----");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void weather_station_scene_receiver_config_set_frequency(VariableItem* item) {
|
||||||
|
WeatherStationApp* app = variable_item_get_context(item);
|
||||||
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
if(app->txrx->hopper_state == WSHopperStateOFF) {
|
||||||
|
char text_buf[10] = {0};
|
||||||
|
snprintf(
|
||||||
|
text_buf,
|
||||||
|
sizeof(text_buf),
|
||||||
|
"%lu.%02lu",
|
||||||
|
subghz_setting_get_frequency(app->setting, index) / 1000000,
|
||||||
|
(subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000);
|
||||||
|
variable_item_set_current_value_text(item, text_buf);
|
||||||
|
app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index);
|
||||||
|
} else {
|
||||||
|
variable_item_set_current_value_index(
|
||||||
|
item, subghz_setting_get_frequency_default_index(app->setting));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void weather_station_scene_receiver_config_set_preset(VariableItem* item) {
|
||||||
|
WeatherStationApp* app = variable_item_get_context(item);
|
||||||
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, subghz_setting_get_preset_name(app->setting, index));
|
||||||
|
ws_preset_init(
|
||||||
|
app,
|
||||||
|
subghz_setting_get_preset_name(app->setting, index),
|
||||||
|
app->txrx->preset->frequency,
|
||||||
|
subghz_setting_get_preset_data(app->setting, index),
|
||||||
|
subghz_setting_get_preset_data_size(app->setting, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void weather_station_scene_receiver_config_set_hopping_running(VariableItem* item) {
|
||||||
|
WeatherStationApp* app = variable_item_get_context(item);
|
||||||
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
variable_item_set_current_value_text(item, hopping_text[index]);
|
||||||
|
if(hopping_value[index] == WSHopperStateOFF) {
|
||||||
|
char text_buf[10] = {0};
|
||||||
|
snprintf(
|
||||||
|
text_buf,
|
||||||
|
sizeof(text_buf),
|
||||||
|
"%lu.%02lu",
|
||||||
|
subghz_setting_get_default_frequency(app->setting) / 1000000,
|
||||||
|
(subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
|
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||||
|
text_buf);
|
||||||
|
app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting);
|
||||||
|
variable_item_set_current_value_index(
|
||||||
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
|
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||||
|
subghz_setting_get_frequency_default_index(app->setting));
|
||||||
|
} else {
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
|
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||||
|
" -----");
|
||||||
|
variable_item_set_current_value_index(
|
||||||
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
|
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||||
|
subghz_setting_get_frequency_default_index(app->setting));
|
||||||
|
}
|
||||||
|
|
||||||
|
app->txrx->hopper_state = hopping_value[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
weather_station_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
if(index == WSSettingIndexLock) {
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, WSCustomEventSceneSettingLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_config_on_enter(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
VariableItem* item;
|
||||||
|
uint8_t value_index;
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list,
|
||||||
|
"Frequency:",
|
||||||
|
subghz_setting_get_frequency_count(app->setting),
|
||||||
|
weather_station_scene_receiver_config_set_frequency,
|
||||||
|
app);
|
||||||
|
value_index =
|
||||||
|
weather_station_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, WeatherStationSceneReceiverConfig, (uint32_t)item);
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
char text_buf[10] = {0};
|
||||||
|
snprintf(
|
||||||
|
text_buf,
|
||||||
|
sizeof(text_buf),
|
||||||
|
"%lu.%02lu",
|
||||||
|
subghz_setting_get_frequency(app->setting, value_index) / 1000000,
|
||||||
|
(subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000);
|
||||||
|
variable_item_set_current_value_text(item, text_buf);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list,
|
||||||
|
"Hopping:",
|
||||||
|
HOPPING_COUNT,
|
||||||
|
weather_station_scene_receiver_config_set_hopping_running,
|
||||||
|
app);
|
||||||
|
value_index = weather_station_scene_receiver_config_hopper_value_index(
|
||||||
|
app->txrx->hopper_state, hopping_value, HOPPING_COUNT, app);
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
variable_item_set_current_value_text(item, hopping_text[value_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list,
|
||||||
|
"Modulation:",
|
||||||
|
subghz_setting_get_preset_count(app->setting),
|
||||||
|
weather_station_scene_receiver_config_set_preset,
|
||||||
|
app);
|
||||||
|
value_index = weather_station_scene_receiver_config_next_preset(
|
||||||
|
furi_string_get_cstr(app->txrx->preset->name), app);
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, subghz_setting_get_preset_name(app->setting, value_index));
|
||||||
|
|
||||||
|
variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
|
||||||
|
variable_item_list_set_enter_callback(
|
||||||
|
app->variable_item_list,
|
||||||
|
weather_station_scene_receiver_config_var_list_enter_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewVariableItemList);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool weather_station_scene_receiver_config_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == WSCustomEventSceneSettingLock) {
|
||||||
|
app->lock = WSLockOn;
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_config_on_exit(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
variable_item_list_set_selected_item(app->variable_item_list, 0);
|
||||||
|
variable_item_list_reset(app->variable_item_list);
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
#include "../views/weather_station_receiver.h"
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_info_callback(WSCustomEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void weather_station_scene_receiver_info_add_to_history_callback(
|
||||||
|
SubGhzReceiver* receiver,
|
||||||
|
SubGhzProtocolDecoderBase* decoder_base,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
|
||||||
|
if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) ==
|
||||||
|
WSHistoryStateAddKeyUpdateData) {
|
||||||
|
ws_view_receiver_info_update(
|
||||||
|
app->ws_receiver_info,
|
||||||
|
ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen));
|
||||||
|
subghz_receiver_reset(receiver);
|
||||||
|
|
||||||
|
notification_message(app->notifications, &sequence_blink_green_10);
|
||||||
|
app->txrx->rx_key_state = WSRxKeyStateAddKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_info_on_enter(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
|
||||||
|
subghz_receiver_set_rx_callback(
|
||||||
|
app->txrx->receiver, weather_station_scene_receiver_info_add_to_history_callback, app);
|
||||||
|
ws_view_receiver_info_update(
|
||||||
|
app->ws_receiver_info,
|
||||||
|
ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen));
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiverInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool weather_station_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
UNUSED(app);
|
||||||
|
UNUSED(event);
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_receiver_info_on_exit(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubmenuIndexWeatherStationReceiver,
|
||||||
|
SubmenuIndexWeatherStationAbout,
|
||||||
|
} SubmenuIndex;
|
||||||
|
|
||||||
|
void weather_station_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_start_on_enter(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Read Weather Station",
|
||||||
|
SubmenuIndexWeatherStationReceiver,
|
||||||
|
weather_station_scene_start_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"About",
|
||||||
|
SubmenuIndexWeatherStationAbout,
|
||||||
|
weather_station_scene_start_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_set_selected_item(
|
||||||
|
submenu, scene_manager_get_scene_state(app->scene_manager, WeatherStationSceneStart));
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool weather_station_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubmenuIndexWeatherStationAbout) {
|
||||||
|
scene_manager_next_scene(app->scene_manager, WeatherStationSceneAbout);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexWeatherStationReceiver) {
|
||||||
|
scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiver);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
scene_manager_set_scene_state(app->scene_manager, WeatherStationSceneStart, event.event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_scene_start_on_exit(void* context) {
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
|
@ -0,0 +1,437 @@
|
||||||
|
#include "weather_station_receiver.h"
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
#include "weather_station_icons.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <assets_icons.h>
|
||||||
|
#include <m-array.h>
|
||||||
|
|
||||||
|
#define FRAME_HEIGHT 12
|
||||||
|
#define MAX_LEN_PX 100
|
||||||
|
#define MENU_ITEMS 4u
|
||||||
|
#define UNLOCK_CNT 3
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriString* item_str;
|
||||||
|
uint8_t type;
|
||||||
|
} WSReceiverMenuItem;
|
||||||
|
|
||||||
|
ARRAY_DEF(WSReceiverMenuItemArray, WSReceiverMenuItem, M_POD_OPLIST)
|
||||||
|
|
||||||
|
#define M_OPL_WSReceiverMenuItemArray_t() ARRAY_OPLIST(WSReceiverMenuItemArray, M_POD_OPLIST)
|
||||||
|
|
||||||
|
struct WSReceiverHistory {
|
||||||
|
WSReceiverMenuItemArray_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct WSReceiverHistory WSReceiverHistory;
|
||||||
|
|
||||||
|
static const Icon* ReceiverItemIcons[] = {
|
||||||
|
[SubGhzProtocolTypeUnknown] = &I_Quest_7x8,
|
||||||
|
[SubGhzProtocolTypeStatic] = &I_Unlock_7x8,
|
||||||
|
[SubGhzProtocolTypeDynamic] = &I_Lock_7x8,
|
||||||
|
[SubGhzProtocolWeatherStation] = &I_station_icon,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WSReceiverBarShowDefault,
|
||||||
|
WSReceiverBarShowLock,
|
||||||
|
WSReceiverBarShowToUnlockPress,
|
||||||
|
WSReceiverBarShowUnlock,
|
||||||
|
} WSReceiverBarShow;
|
||||||
|
|
||||||
|
struct WSReceiver {
|
||||||
|
WSLock lock;
|
||||||
|
uint8_t lock_count;
|
||||||
|
FuriTimer* timer;
|
||||||
|
View* view;
|
||||||
|
WSReceiverCallback callback;
|
||||||
|
void* context;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriString* frequency_str;
|
||||||
|
FuriString* preset_str;
|
||||||
|
FuriString* history_stat_str;
|
||||||
|
WSReceiverHistory* history;
|
||||||
|
uint16_t idx;
|
||||||
|
uint16_t list_offset;
|
||||||
|
uint16_t history_item;
|
||||||
|
WSReceiverBarShow bar_show;
|
||||||
|
} WSReceiverModel;
|
||||||
|
|
||||||
|
void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
ws_receiver->lock_count = 0;
|
||||||
|
if(lock == WSLockOn) {
|
||||||
|
ws_receiver->lock = lock;
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{ model->bar_show = WSReceiverBarShowLock; },
|
||||||
|
true);
|
||||||
|
furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000));
|
||||||
|
} else {
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{ model->bar_show = WSReceiverBarShowDefault; },
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_set_callback(
|
||||||
|
WSReceiver* ws_receiver,
|
||||||
|
WSReceiverCallback callback,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
furi_assert(callback);
|
||||||
|
ws_receiver->callback = callback;
|
||||||
|
ws_receiver->context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ws_view_receiver_update_offset(WSReceiver* ws_receiver) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
size_t history_item = model->history_item;
|
||||||
|
uint16_t bounds = history_item > 3 ? 2 : history_item;
|
||||||
|
|
||||||
|
if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) {
|
||||||
|
model->list_offset = model->idx - 3;
|
||||||
|
} else if(model->list_offset < model->idx - bounds) {
|
||||||
|
model->list_offset =
|
||||||
|
CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0);
|
||||||
|
} else if(model->list_offset > model->idx - bounds) {
|
||||||
|
model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
WSReceiverMenuItem* item_menu = WSReceiverMenuItemArray_push_raw(model->history->data);
|
||||||
|
item_menu->item_str = furi_string_alloc_set(name);
|
||||||
|
item_menu->type = type;
|
||||||
|
if((model->idx == model->history_item - 1)) {
|
||||||
|
model->history_item++;
|
||||||
|
model->idx++;
|
||||||
|
} else {
|
||||||
|
model->history_item++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
ws_view_receiver_update_offset(ws_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_add_data_statusbar(
|
||||||
|
WSReceiver* ws_receiver,
|
||||||
|
const char* frequency_str,
|
||||||
|
const char* preset_str,
|
||||||
|
const char* history_stat_str) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
furi_string_set_str(model->frequency_str, frequency_str);
|
||||||
|
furi_string_set_str(model->preset_str, preset_str);
|
||||||
|
furi_string_set_str(model->history_stat_str, history_stat_str);
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||||
|
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT);
|
||||||
|
canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT);
|
||||||
|
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1);
|
||||||
|
|
||||||
|
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11);
|
||||||
|
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT);
|
||||||
|
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) {
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
elements_button_left(canvas, "Config");
|
||||||
|
canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||||
|
|
||||||
|
bool scrollbar = model->history_item > 4;
|
||||||
|
FuriString* str_buff;
|
||||||
|
str_buff = furi_string_alloc();
|
||||||
|
|
||||||
|
WSReceiverMenuItem* item_menu;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
|
||||||
|
size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);
|
||||||
|
item_menu = WSReceiverMenuItemArray_get(model->history->data, idx);
|
||||||
|
furi_string_set(str_buff, item_menu->item_str);
|
||||||
|
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||||
|
if(model->idx == idx) {
|
||||||
|
ws_view_receiver_draw_frame(canvas, i, scrollbar);
|
||||||
|
} else {
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
}
|
||||||
|
canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
||||||
|
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));
|
||||||
|
furi_string_reset(str_buff);
|
||||||
|
}
|
||||||
|
if(scrollbar) {
|
||||||
|
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
|
||||||
|
}
|
||||||
|
furi_string_free(str_buff);
|
||||||
|
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
if(model->history_item == 0) {
|
||||||
|
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 63, 46, "Scanning...");
|
||||||
|
canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(model->bar_show) {
|
||||||
|
case WSReceiverBarShowLock:
|
||||||
|
canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
|
||||||
|
canvas_draw_str(canvas, 74, 62, "Locked");
|
||||||
|
break;
|
||||||
|
case WSReceiverBarShowToUnlockPress:
|
||||||
|
canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str));
|
||||||
|
canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
|
||||||
|
canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str));
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
|
||||||
|
elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
|
||||||
|
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
|
||||||
|
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
|
||||||
|
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
|
||||||
|
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
|
||||||
|
canvas_draw_dot(canvas, 17, 61);
|
||||||
|
break;
|
||||||
|
case WSReceiverBarShowUnlock:
|
||||||
|
canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8);
|
||||||
|
canvas_draw_str(canvas, 74, 62, "Unlocked");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str));
|
||||||
|
canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
|
||||||
|
canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ws_view_receiver_timer_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSReceiver* ws_receiver = context;
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{ model->bar_show = WSReceiverBarShowDefault; },
|
||||||
|
true);
|
||||||
|
if(ws_receiver->lock_count < UNLOCK_CNT) {
|
||||||
|
ws_receiver->callback(WSCustomEventViewReceiverOffDisplay, ws_receiver->context);
|
||||||
|
} else {
|
||||||
|
ws_receiver->lock = WSLockOff;
|
||||||
|
ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context);
|
||||||
|
}
|
||||||
|
ws_receiver->lock_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_view_receiver_input(InputEvent* event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSReceiver* ws_receiver = context;
|
||||||
|
|
||||||
|
if(ws_receiver->lock == WSLockOn) {
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{ model->bar_show = WSReceiverBarShowToUnlockPress; },
|
||||||
|
true);
|
||||||
|
if(ws_receiver->lock_count == 0) {
|
||||||
|
furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||||
|
ws_receiver->lock_count++;
|
||||||
|
}
|
||||||
|
if(ws_receiver->lock_count >= UNLOCK_CNT) {
|
||||||
|
ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context);
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{ model->bar_show = WSReceiverBarShowUnlock; },
|
||||||
|
true);
|
||||||
|
ws_receiver->lock = WSLockOff;
|
||||||
|
furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(650));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||||
|
ws_receiver->callback(WSCustomEventViewReceiverBack, ws_receiver->context);
|
||||||
|
} else if(
|
||||||
|
event->key == InputKeyUp &&
|
||||||
|
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
if(model->idx != 0) model->idx--;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else if(
|
||||||
|
event->key == InputKeyDown &&
|
||||||
|
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
if(model->idx != model->history_item - 1) model->idx++;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||||
|
ws_receiver->callback(WSCustomEventViewReceiverConfig, ws_receiver->context);
|
||||||
|
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
if(model->history_item != 0) {
|
||||||
|
ws_receiver->callback(WSCustomEventViewReceiverOK, ws_receiver->context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_view_receiver_update_offset(ws_receiver);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSReceiver* ws_receiver = context;
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
furi_string_reset(model->frequency_str);
|
||||||
|
furi_string_reset(model->preset_str);
|
||||||
|
furi_string_reset(model->history_stat_str);
|
||||||
|
for
|
||||||
|
M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) {
|
||||||
|
furi_string_free(item_menu->item_str);
|
||||||
|
item_menu->type = 0;
|
||||||
|
}
|
||||||
|
WSReceiverMenuItemArray_reset(model->history->data);
|
||||||
|
model->idx = 0;
|
||||||
|
model->list_offset = 0;
|
||||||
|
model->history_item = 0;
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
furi_timer_stop(ws_receiver->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
WSReceiver* ws_view_receiver_alloc() {
|
||||||
|
WSReceiver* ws_receiver = malloc(sizeof(WSReceiver));
|
||||||
|
|
||||||
|
// View allocation and configuration
|
||||||
|
ws_receiver->view = view_alloc();
|
||||||
|
|
||||||
|
ws_receiver->lock = WSLockOff;
|
||||||
|
ws_receiver->lock_count = 0;
|
||||||
|
view_allocate_model(ws_receiver->view, ViewModelTypeLocking, sizeof(WSReceiverModel));
|
||||||
|
view_set_context(ws_receiver->view, ws_receiver);
|
||||||
|
view_set_draw_callback(ws_receiver->view, (ViewDrawCallback)ws_view_receiver_draw);
|
||||||
|
view_set_input_callback(ws_receiver->view, ws_view_receiver_input);
|
||||||
|
view_set_enter_callback(ws_receiver->view, ws_view_receiver_enter);
|
||||||
|
view_set_exit_callback(ws_receiver->view, ws_view_receiver_exit);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
model->frequency_str = furi_string_alloc();
|
||||||
|
model->preset_str = furi_string_alloc();
|
||||||
|
model->history_stat_str = furi_string_alloc();
|
||||||
|
model->bar_show = WSReceiverBarShowDefault;
|
||||||
|
model->history = malloc(sizeof(WSReceiverHistory));
|
||||||
|
WSReceiverMenuItemArray_init(model->history->data);
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
ws_receiver->timer =
|
||||||
|
furi_timer_alloc(ws_view_receiver_timer_callback, FuriTimerTypeOnce, ws_receiver);
|
||||||
|
return ws_receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_free(WSReceiver* ws_receiver) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
furi_string_free(model->frequency_str);
|
||||||
|
furi_string_free(model->preset_str);
|
||||||
|
furi_string_free(model->history_stat_str);
|
||||||
|
for
|
||||||
|
M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) {
|
||||||
|
furi_string_free(item_menu->item_str);
|
||||||
|
item_menu->type = 0;
|
||||||
|
}
|
||||||
|
WSReceiverMenuItemArray_clear(model->history->data);
|
||||||
|
free(model->history);
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
furi_timer_free(ws_receiver->timer);
|
||||||
|
view_free(ws_receiver->view);
|
||||||
|
free(ws_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
View* ws_view_receiver_get_view(WSReceiver* ws_receiver) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
return ws_receiver->view;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
uint32_t idx = 0;
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view, WSReceiverModel * model, { idx = model->idx; }, false);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx) {
|
||||||
|
furi_assert(ws_receiver);
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver->view,
|
||||||
|
WSReceiverModel * model,
|
||||||
|
{
|
||||||
|
model->idx = idx;
|
||||||
|
if(model->idx > 2) model->list_offset = idx - 2;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
ws_view_receiver_update_offset(ws_receiver);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include "../helpers/weather_station_types.h"
|
||||||
|
#include "../helpers/weather_station_event.h"
|
||||||
|
|
||||||
|
typedef struct WSReceiver WSReceiver;
|
||||||
|
|
||||||
|
typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context);
|
||||||
|
|
||||||
|
void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard);
|
||||||
|
|
||||||
|
void ws_view_receiver_set_callback(
|
||||||
|
WSReceiver* ws_receiver,
|
||||||
|
WSReceiverCallback callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
|
WSReceiver* ws_view_receiver_alloc();
|
||||||
|
|
||||||
|
void ws_view_receiver_free(WSReceiver* ws_receiver);
|
||||||
|
|
||||||
|
View* ws_view_receiver_get_view(WSReceiver* ws_receiver);
|
||||||
|
|
||||||
|
void ws_view_receiver_add_data_statusbar(
|
||||||
|
WSReceiver* ws_receiver,
|
||||||
|
const char* frequency_str,
|
||||||
|
const char* preset_str,
|
||||||
|
const char* history_stat_str);
|
||||||
|
|
||||||
|
void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type);
|
||||||
|
|
||||||
|
uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver);
|
||||||
|
|
||||||
|
void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx);
|
||||||
|
|
||||||
|
void ws_view_receiver_exit(void* context);
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include "weather_station_receiver.h"
|
||||||
|
#include "../weather_station_app_i.h"
|
||||||
|
#include "weather_station_icons.h"
|
||||||
|
#include "../protocols/ws_generic.h"
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
|
#define abs(x) ((x) > 0 ? (x) : -(x))
|
||||||
|
|
||||||
|
struct WSReceiverInfo {
|
||||||
|
View* view;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriString* protocol_name;
|
||||||
|
WSBlockGeneric* generic;
|
||||||
|
} WSReceiverInfoModel;
|
||||||
|
|
||||||
|
void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff) {
|
||||||
|
furi_assert(ws_receiver_info);
|
||||||
|
furi_assert(fff);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver_info->view,
|
||||||
|
WSReceiverInfoModel * model,
|
||||||
|
{
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
flipper_format_read_string(fff, "Protocol", model->protocol_name);
|
||||||
|
|
||||||
|
ws_block_generic_deserialize(model->generic, fff);
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) {
|
||||||
|
char buffer[64];
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
buffer,
|
||||||
|
sizeof(buffer),
|
||||||
|
"%s %db",
|
||||||
|
furi_string_get_cstr(model->protocol_name),
|
||||||
|
model->generic->data_count_bit);
|
||||||
|
canvas_draw_str(canvas, 5, 8, buffer);
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel);
|
||||||
|
canvas_draw_str(canvas, 105, 8, buffer);
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id);
|
||||||
|
canvas_draw_str(canvas, 5, 20, buffer);
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low"));
|
||||||
|
canvas_draw_str(canvas, 85, 20, buffer);
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data);
|
||||||
|
canvas_draw_str(canvas, 5, 32, buffer);
|
||||||
|
|
||||||
|
elements_bold_rounded_frame(canvas, 2, 37, 123, 25);
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
|
||||||
|
canvas_draw_icon(canvas, 13 + 5, 42, &I_Therm_7x16);
|
||||||
|
snprintf(
|
||||||
|
buffer,
|
||||||
|
sizeof(buffer),
|
||||||
|
"%3.2d.%d C",
|
||||||
|
(int16_t)model->generic->temp,
|
||||||
|
abs(((int16_t)(model->generic->temp * 10) - (((int16_t)model->generic->temp) * 10))));
|
||||||
|
canvas_draw_str_aligned(canvas, 58 + 5, 46, AlignRight, AlignTop, buffer);
|
||||||
|
canvas_draw_circle(canvas, 50 + 5, 45, 1);
|
||||||
|
|
||||||
|
canvas_draw_icon(canvas, 70 + 5, 42, &I_Humid_10x15);
|
||||||
|
snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity);
|
||||||
|
canvas_draw_str(canvas, 86 + 5, 54, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_view_receiver_info_input(InputEvent* event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
//WSReceiverInfo* ws_receiver_info = context;
|
||||||
|
|
||||||
|
if(event->key == InputKeyBack) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_info_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_info_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WSReceiverInfo* ws_receiver_info = context;
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver_info->view,
|
||||||
|
WSReceiverInfoModel * model,
|
||||||
|
{ furi_string_reset(model->protocol_name); },
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
WSReceiverInfo* ws_view_receiver_info_alloc() {
|
||||||
|
WSReceiverInfo* ws_receiver_info = malloc(sizeof(WSReceiverInfo));
|
||||||
|
|
||||||
|
// View allocation and configuration
|
||||||
|
ws_receiver_info->view = view_alloc();
|
||||||
|
|
||||||
|
view_allocate_model(ws_receiver_info->view, ViewModelTypeLocking, sizeof(WSReceiverInfoModel));
|
||||||
|
view_set_context(ws_receiver_info->view, ws_receiver_info);
|
||||||
|
view_set_draw_callback(ws_receiver_info->view, (ViewDrawCallback)ws_view_receiver_info_draw);
|
||||||
|
view_set_input_callback(ws_receiver_info->view, ws_view_receiver_info_input);
|
||||||
|
view_set_enter_callback(ws_receiver_info->view, ws_view_receiver_info_enter);
|
||||||
|
view_set_exit_callback(ws_receiver_info->view, ws_view_receiver_info_exit);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver_info->view,
|
||||||
|
WSReceiverInfoModel * model,
|
||||||
|
{
|
||||||
|
model->generic = malloc(sizeof(WSBlockGeneric));
|
||||||
|
model->protocol_name = furi_string_alloc();
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
|
||||||
|
return ws_receiver_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) {
|
||||||
|
furi_assert(ws_receiver_info);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
ws_receiver_info->view,
|
||||||
|
WSReceiverInfoModel * model,
|
||||||
|
{
|
||||||
|
furi_string_free(model->protocol_name);
|
||||||
|
free(model->generic);
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
|
view_free(ws_receiver_info->view);
|
||||||
|
free(ws_receiver_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info) {
|
||||||
|
furi_assert(ws_receiver_info);
|
||||||
|
return ws_receiver_info->view;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include "../helpers/weather_station_types.h"
|
||||||
|
#include "../helpers/weather_station_event.h"
|
||||||
|
#include <lib/flipper_format/flipper_format.h>
|
||||||
|
|
||||||
|
typedef struct WSReceiverInfo WSReceiverInfo;
|
||||||
|
|
||||||
|
void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff);
|
||||||
|
|
||||||
|
WSReceiverInfo* ws_view_receiver_info_alloc();
|
||||||
|
|
||||||
|
void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info);
|
||||||
|
|
||||||
|
View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info);
|
BIN
applications/plugins/weather_station/weather_station_10px.png
Normal file
BIN
applications/plugins/weather_station/weather_station_10px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 175 B |
178
applications/plugins/weather_station/weather_station_app.c
Normal file
178
applications/plugins/weather_station/weather_station_app.c
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
#include "weather_station_app_i.h"
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include "protocols/protocol_items.h"
|
||||||
|
|
||||||
|
static bool weather_station_app_custom_event_callback(void* context, uint32_t event) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool weather_station_app_back_event_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
return scene_manager_handle_back_event(app->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void weather_station_app_tick_event_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
scene_manager_handle_tick_event(app->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
WeatherStationApp* weather_station_app_alloc() {
|
||||||
|
WeatherStationApp* app = malloc(sizeof(WeatherStationApp));
|
||||||
|
|
||||||
|
// GUI
|
||||||
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
|
||||||
|
// View Dispatcher
|
||||||
|
app->view_dispatcher = view_dispatcher_alloc();
|
||||||
|
app->scene_manager = scene_manager_alloc(&weather_station_scene_handlers, app);
|
||||||
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||||
|
|
||||||
|
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||||
|
view_dispatcher_set_custom_event_callback(
|
||||||
|
app->view_dispatcher, weather_station_app_custom_event_callback);
|
||||||
|
view_dispatcher_set_navigation_event_callback(
|
||||||
|
app->view_dispatcher, weather_station_app_back_event_callback);
|
||||||
|
view_dispatcher_set_tick_event_callback(
|
||||||
|
app->view_dispatcher, weather_station_app_tick_event_callback, 100);
|
||||||
|
|
||||||
|
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||||
|
|
||||||
|
// Open Notification record
|
||||||
|
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||||
|
|
||||||
|
// Variable Item List
|
||||||
|
app->variable_item_list = variable_item_list_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher,
|
||||||
|
WeatherStationViewVariableItemList,
|
||||||
|
variable_item_list_get_view(app->variable_item_list));
|
||||||
|
|
||||||
|
// SubMenu
|
||||||
|
app->submenu = submenu_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, WeatherStationViewSubmenu, submenu_get_view(app->submenu));
|
||||||
|
|
||||||
|
// Widget
|
||||||
|
app->widget = widget_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, WeatherStationViewWidget, widget_get_view(app->widget));
|
||||||
|
|
||||||
|
// Receiver
|
||||||
|
app->ws_receiver = ws_view_receiver_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher,
|
||||||
|
WeatherStationViewReceiver,
|
||||||
|
ws_view_receiver_get_view(app->ws_receiver));
|
||||||
|
|
||||||
|
// Receiver Info
|
||||||
|
app->ws_receiver_info = ws_view_receiver_info_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher,
|
||||||
|
WeatherStationViewReceiverInfo,
|
||||||
|
ws_view_receiver_info_get_view(app->ws_receiver_info));
|
||||||
|
|
||||||
|
//init setting
|
||||||
|
app->setting = subghz_setting_alloc();
|
||||||
|
|
||||||
|
//ToDo FIX file name setting
|
||||||
|
subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
|
||||||
|
|
||||||
|
//init Worker & Protocol & History
|
||||||
|
app->lock = WSLockOff;
|
||||||
|
app->txrx = malloc(sizeof(WeatherStationTxRx));
|
||||||
|
app->txrx->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||||
|
app->txrx->preset->name = furi_string_alloc();
|
||||||
|
ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0);
|
||||||
|
|
||||||
|
app->txrx->hopper_state = WSHopperStateOFF;
|
||||||
|
app->txrx->history = ws_history_alloc();
|
||||||
|
app->txrx->worker = subghz_worker_alloc();
|
||||||
|
app->txrx->environment = subghz_environment_alloc();
|
||||||
|
subghz_environment_set_protocol_registry(
|
||||||
|
app->txrx->environment, (void*)&weather_station_protocol_registry);
|
||||||
|
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||||
|
|
||||||
|
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||||
|
subghz_worker_set_overrun_callback(
|
||||||
|
app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||||
|
subghz_worker_set_pair_callback(
|
||||||
|
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||||
|
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
|
||||||
|
|
||||||
|
furi_hal_power_suppress_charge_enter();
|
||||||
|
|
||||||
|
scene_manager_next_scene(app->scene_manager, WeatherStationSceneStart);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
void weather_station_app_free(WeatherStationApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
|
||||||
|
//CC1101 off
|
||||||
|
ws_sleep(app);
|
||||||
|
|
||||||
|
// Submenu
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu);
|
||||||
|
submenu_free(app->submenu);
|
||||||
|
|
||||||
|
// Variable Item List
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewVariableItemList);
|
||||||
|
variable_item_list_free(app->variable_item_list);
|
||||||
|
|
||||||
|
// Widget
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewWidget);
|
||||||
|
widget_free(app->widget);
|
||||||
|
|
||||||
|
// Receiver
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiver);
|
||||||
|
ws_view_receiver_free(app->ws_receiver);
|
||||||
|
|
||||||
|
// Receiver Info
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiverInfo);
|
||||||
|
ws_view_receiver_info_free(app->ws_receiver_info);
|
||||||
|
|
||||||
|
//setting
|
||||||
|
subghz_setting_free(app->setting);
|
||||||
|
|
||||||
|
//Worker & Protocol & History
|
||||||
|
subghz_receiver_free(app->txrx->receiver);
|
||||||
|
subghz_environment_free(app->txrx->environment);
|
||||||
|
ws_history_free(app->txrx->history);
|
||||||
|
subghz_worker_free(app->txrx->worker);
|
||||||
|
furi_string_free(app->txrx->preset->name);
|
||||||
|
free(app->txrx->preset);
|
||||||
|
free(app->txrx);
|
||||||
|
|
||||||
|
// View dispatcher
|
||||||
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
|
scene_manager_free(app->scene_manager);
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
furi_record_close(RECORD_NOTIFICATION);
|
||||||
|
app->notifications = NULL;
|
||||||
|
|
||||||
|
// Close records
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
|
||||||
|
furi_hal_power_suppress_charge_exit();
|
||||||
|
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t weather_station_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
WeatherStationApp* weather_station_app = weather_station_app_alloc();
|
||||||
|
|
||||||
|
view_dispatcher_run(weather_station_app->view_dispatcher);
|
||||||
|
|
||||||
|
weather_station_app_free(weather_station_app);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
159
applications/plugins/weather_station/weather_station_app_i.c
Normal file
159
applications/plugins/weather_station/weather_station_app_i.c
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
#include "weather_station_app_i.h"
|
||||||
|
|
||||||
|
#define TAG "WeatherStation"
|
||||||
|
#include <flipper_format/flipper_format_i.h>
|
||||||
|
|
||||||
|
void ws_preset_init(
|
||||||
|
void* context,
|
||||||
|
const char* preset_name,
|
||||||
|
uint32_t frequency,
|
||||||
|
uint8_t* preset_data,
|
||||||
|
size_t preset_data_size) {
|
||||||
|
furi_assert(context);
|
||||||
|
WeatherStationApp* app = context;
|
||||||
|
furi_string_set(app->txrx->preset->name, preset_name);
|
||||||
|
app->txrx->preset->frequency = frequency;
|
||||||
|
app->txrx->preset->data = preset_data;
|
||||||
|
app->txrx->preset->data_size = preset_data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_set_preset(WeatherStationApp* app, const char* preset) {
|
||||||
|
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
|
||||||
|
furi_string_set(app->txrx->preset->name, "AM270");
|
||||||
|
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
|
||||||
|
furi_string_set(app->txrx->preset->name, "AM650");
|
||||||
|
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
|
||||||
|
furi_string_set(app->txrx->preset->name, "FM238");
|
||||||
|
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
|
||||||
|
furi_string_set(app->txrx->preset->name, "FM476");
|
||||||
|
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
|
||||||
|
furi_string_set(app->txrx->preset->name, "CUSTOM");
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Unknown preset");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_get_frequency_modulation(
|
||||||
|
WeatherStationApp* app,
|
||||||
|
FuriString* frequency,
|
||||||
|
FuriString* modulation) {
|
||||||
|
furi_assert(app);
|
||||||
|
if(frequency != NULL) {
|
||||||
|
furi_string_printf(
|
||||||
|
frequency,
|
||||||
|
"%03ld.%02ld",
|
||||||
|
app->txrx->preset->frequency / 1000000 % 1000,
|
||||||
|
app->txrx->preset->frequency / 10000 % 100);
|
||||||
|
}
|
||||||
|
if(modulation != NULL) {
|
||||||
|
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(app->txrx->preset->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_begin(WeatherStationApp* app, uint8_t* preset_data) {
|
||||||
|
furi_assert(app);
|
||||||
|
UNUSED(preset_data);
|
||||||
|
furi_hal_subghz_reset();
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
furi_hal_subghz_load_custom_preset(preset_data);
|
||||||
|
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||||
|
app->txrx->txrx_state = WSTxRxStateIDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency) {
|
||||||
|
furi_assert(app);
|
||||||
|
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
|
||||||
|
furi_crash("WeatherStation: Incorrect RX frequency.");
|
||||||
|
}
|
||||||
|
furi_assert(
|
||||||
|
app->txrx->txrx_state != WSTxRxStateRx && app->txrx->txrx_state != WSTxRxStateSleep);
|
||||||
|
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
|
||||||
|
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||||
|
furi_hal_subghz_flush_rx();
|
||||||
|
furi_hal_subghz_rx();
|
||||||
|
|
||||||
|
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker);
|
||||||
|
subghz_worker_start(app->txrx->worker);
|
||||||
|
app->txrx->txrx_state = WSTxRxStateRx;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_idle(WeatherStationApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
furi_assert(app->txrx->txrx_state != WSTxRxStateSleep);
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
app->txrx->txrx_state = WSTxRxStateIDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_rx_end(WeatherStationApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
furi_assert(app->txrx->txrx_state == WSTxRxStateRx);
|
||||||
|
if(subghz_worker_is_running(app->txrx->worker)) {
|
||||||
|
subghz_worker_stop(app->txrx->worker);
|
||||||
|
furi_hal_subghz_stop_async_rx();
|
||||||
|
}
|
||||||
|
furi_hal_subghz_idle();
|
||||||
|
app->txrx->txrx_state = WSTxRxStateIDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_sleep(WeatherStationApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
furi_hal_subghz_sleep();
|
||||||
|
app->txrx->txrx_state = WSTxRxStateSleep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_hopper_update(WeatherStationApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
|
||||||
|
switch(app->txrx->hopper_state) {
|
||||||
|
case WSHopperStateOFF:
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case WSHopperStatePause:
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case WSHopperStateRSSITimeOut:
|
||||||
|
if(app->txrx->hopper_timeout != 0) {
|
||||||
|
app->txrx->hopper_timeout--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
float rssi = -127.0f;
|
||||||
|
if(app->txrx->hopper_state != WSHopperStateRSSITimeOut) {
|
||||||
|
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||||
|
rssi = furi_hal_subghz_get_rssi();
|
||||||
|
|
||||||
|
// Stay if RSSI is high enough
|
||||||
|
if(rssi > -90.0f) {
|
||||||
|
app->txrx->hopper_timeout = 10;
|
||||||
|
app->txrx->hopper_state = WSHopperStateRSSITimeOut;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app->txrx->hopper_state = WSHopperStateRunnig;
|
||||||
|
}
|
||||||
|
// Select next frequency
|
||||||
|
if(app->txrx->hopper_idx_frequency <
|
||||||
|
subghz_setting_get_hopper_frequency_count(app->setting) - 1) {
|
||||||
|
app->txrx->hopper_idx_frequency++;
|
||||||
|
} else {
|
||||||
|
app->txrx->hopper_idx_frequency = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||||
|
ws_rx_end(app);
|
||||||
|
};
|
||||||
|
if(app->txrx->txrx_state == WSTxRxStateIDLE) {
|
||||||
|
subghz_receiver_reset(app->txrx->receiver);
|
||||||
|
app->txrx->preset->frequency =
|
||||||
|
subghz_setting_get_hopper_frequency(app->setting, app->txrx->hopper_idx_frequency);
|
||||||
|
ws_rx(app, app->txrx->preset->frequency);
|
||||||
|
}
|
||||||
|
}
|
73
applications/plugins/weather_station/weather_station_app_i.h
Normal file
73
applications/plugins/weather_station/weather_station_app_i.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "helpers/weather_station_types.h"
|
||||||
|
|
||||||
|
#include "scenes/weather_station_scene.h"
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/variable_item_list.h>
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
#include "views/weather_station_receiver.h"
|
||||||
|
#include "views/weather_station_receiver_info.h"
|
||||||
|
#include "weather_station_history.h"
|
||||||
|
|
||||||
|
#include <lib/subghz/subghz_setting.h>
|
||||||
|
#include <lib/subghz/subghz_worker.h>
|
||||||
|
#include <lib/subghz/receiver.h>
|
||||||
|
#include <lib/subghz/transmitter.h>
|
||||||
|
#include <lib/subghz/registry.h>
|
||||||
|
|
||||||
|
typedef struct WeatherStationApp WeatherStationApp;
|
||||||
|
|
||||||
|
struct WeatherStationTxRx {
|
||||||
|
SubGhzWorker* worker;
|
||||||
|
|
||||||
|
SubGhzEnvironment* environment;
|
||||||
|
SubGhzReceiver* receiver;
|
||||||
|
SubGhzRadioPreset* preset;
|
||||||
|
WSHistory* history;
|
||||||
|
uint16_t idx_menu_chosen;
|
||||||
|
WSTxRxState txrx_state;
|
||||||
|
WSHopperState hopper_state;
|
||||||
|
uint8_t hopper_timeout;
|
||||||
|
uint8_t hopper_idx_frequency;
|
||||||
|
WSRxKeyState rx_key_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct WeatherStationTxRx WeatherStationTxRx;
|
||||||
|
|
||||||
|
struct WeatherStationApp {
|
||||||
|
Gui* gui;
|
||||||
|
ViewDispatcher* view_dispatcher;
|
||||||
|
WeatherStationTxRx* txrx;
|
||||||
|
SceneManager* scene_manager;
|
||||||
|
NotificationApp* notifications;
|
||||||
|
VariableItemList* variable_item_list;
|
||||||
|
Submenu* submenu;
|
||||||
|
Widget* widget;
|
||||||
|
WSReceiver* ws_receiver;
|
||||||
|
WSReceiverInfo* ws_receiver_info;
|
||||||
|
WSLock lock;
|
||||||
|
SubGhzSetting* setting;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ws_preset_init(
|
||||||
|
void* context,
|
||||||
|
const char* preset_name,
|
||||||
|
uint32_t frequency,
|
||||||
|
uint8_t* preset_data,
|
||||||
|
size_t preset_data_size);
|
||||||
|
bool ws_set_preset(WeatherStationApp* app, const char* preset);
|
||||||
|
void ws_get_frequency_modulation(
|
||||||
|
WeatherStationApp* app,
|
||||||
|
FuriString* frequency,
|
||||||
|
FuriString* modulation);
|
||||||
|
void ws_begin(WeatherStationApp* app, uint8_t* preset_data);
|
||||||
|
uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency);
|
||||||
|
void ws_idle(WeatherStationApp* app);
|
||||||
|
void ws_rx_end(WeatherStationApp* app);
|
||||||
|
void ws_sleep(WeatherStationApp* app);
|
||||||
|
void ws_hopper_update(WeatherStationApp* app);
|
246
applications/plugins/weather_station/weather_station_history.c
Normal file
246
applications/plugins/weather_station/weather_station_history.c
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
#include "weather_station_history.h"
|
||||||
|
#include <flipper_format/flipper_format_i.h>
|
||||||
|
#include <lib/toolbox/stream/stream.h>
|
||||||
|
#include <lib/subghz/receiver.h>
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
#define WS_HISTORY_MAX 50
|
||||||
|
#define TAG "WSHistory"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriString* item_str;
|
||||||
|
FlipperFormat* flipper_string;
|
||||||
|
uint8_t type;
|
||||||
|
uint32_t id;
|
||||||
|
SubGhzRadioPreset* preset;
|
||||||
|
} WSHistoryItem;
|
||||||
|
|
||||||
|
ARRAY_DEF(WSHistoryItemArray, WSHistoryItem, M_POD_OPLIST)
|
||||||
|
|
||||||
|
#define M_OPL_WSHistoryItemArray_t() ARRAY_OPLIST(WSHistoryItemArray, M_POD_OPLIST)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
WSHistoryItemArray_t data;
|
||||||
|
} WSHistoryStruct;
|
||||||
|
|
||||||
|
struct WSHistory {
|
||||||
|
uint32_t last_update_timestamp;
|
||||||
|
uint16_t last_index_write;
|
||||||
|
uint8_t code_last_hash_data;
|
||||||
|
FuriString* tmp_string;
|
||||||
|
WSHistoryStruct* history;
|
||||||
|
};
|
||||||
|
|
||||||
|
WSHistory* ws_history_alloc(void) {
|
||||||
|
WSHistory* instance = malloc(sizeof(WSHistory));
|
||||||
|
instance->tmp_string = furi_string_alloc();
|
||||||
|
instance->history = malloc(sizeof(WSHistoryStruct));
|
||||||
|
WSHistoryItemArray_init(instance->history->data);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_history_free(WSHistory* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_string_free(instance->tmp_string);
|
||||||
|
for
|
||||||
|
M_EACH(item, instance->history->data, WSHistoryItemArray_t) {
|
||||||
|
furi_string_free(item->item_str);
|
||||||
|
furi_string_free(item->preset->name);
|
||||||
|
free(item->preset);
|
||||||
|
flipper_format_free(item->flipper_string);
|
||||||
|
item->type = 0;
|
||||||
|
}
|
||||||
|
WSHistoryItemArray_clear(instance->history->data);
|
||||||
|
free(instance->history);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx) {
|
||||||
|
furi_assert(instance);
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||||
|
return item->preset->frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx) {
|
||||||
|
furi_assert(instance);
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||||
|
return item->preset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ws_history_get_preset(WSHistory* instance, uint16_t idx) {
|
||||||
|
furi_assert(instance);
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||||
|
return furi_string_get_cstr(item->preset->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_history_reset(WSHistory* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_string_reset(instance->tmp_string);
|
||||||
|
for
|
||||||
|
M_EACH(item, instance->history->data, WSHistoryItemArray_t) {
|
||||||
|
furi_string_free(item->item_str);
|
||||||
|
furi_string_free(item->preset->name);
|
||||||
|
free(item->preset);
|
||||||
|
flipper_format_free(item->flipper_string);
|
||||||
|
item->type = 0;
|
||||||
|
}
|
||||||
|
WSHistoryItemArray_reset(instance->history->data);
|
||||||
|
instance->last_index_write = 0;
|
||||||
|
instance->code_last_hash_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ws_history_get_item(WSHistory* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->last_index_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx) {
|
||||||
|
furi_assert(instance);
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||||
|
return item->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx) {
|
||||||
|
furi_assert(instance);
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||||
|
flipper_format_rewind(item->flipper_string);
|
||||||
|
if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Protocol");
|
||||||
|
furi_string_reset(instance->tmp_string);
|
||||||
|
}
|
||||||
|
return furi_string_get_cstr(instance->tmp_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx) {
|
||||||
|
furi_assert(instance);
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||||
|
if(item->flipper_string) {
|
||||||
|
return item->flipper_string;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->last_index_write == WS_HISTORY_MAX) {
|
||||||
|
if(output != NULL) furi_string_printf(output, "Memory is FULL");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(output != NULL)
|
||||||
|
furi_string_printf(output, "%02u/%02u", instance->last_index_write, WS_HISTORY_MAX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx) {
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||||
|
furi_string_set(output, item->item_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
WSHistoryStateAddKey
|
||||||
|
ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
if(instance->last_index_write >= WS_HISTORY_MAX) return WSHistoryStateAddKeyOverflow;
|
||||||
|
|
||||||
|
SubGhzProtocolDecoderBase* decoder_base = context;
|
||||||
|
if((instance->code_last_hash_data ==
|
||||||
|
subghz_protocol_decoder_base_get_hash_data(decoder_base)) &&
|
||||||
|
((furi_get_tick() - instance->last_update_timestamp) < 500)) {
|
||||||
|
instance->last_update_timestamp = furi_get_tick();
|
||||||
|
return WSHistoryStateAddKeyTimeOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base);
|
||||||
|
instance->last_update_timestamp = furi_get_tick();
|
||||||
|
|
||||||
|
FlipperFormat* fff = flipper_format_string_alloc();
|
||||||
|
uint32_t id = 0;
|
||||||
|
subghz_protocol_decoder_base_serialize(decoder_base, fff, preset);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(!flipper_format_rewind(fff)) {
|
||||||
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_read_uint32(fff, "Id", (uint32_t*)&id, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Id");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
flipper_format_free(fff);
|
||||||
|
|
||||||
|
//Update record if found
|
||||||
|
bool sensor_found = false;
|
||||||
|
for(size_t i = 0; i < WSHistoryItemArray_size(instance->history->data); i++) {
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, i);
|
||||||
|
if(item->id == id) {
|
||||||
|
sensor_found = true;
|
||||||
|
Stream* flipper_string_stream = flipper_format_get_raw_stream(item->flipper_string);
|
||||||
|
stream_clean(flipper_string_stream);
|
||||||
|
subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);
|
||||||
|
return WSHistoryStateAddKeyUpdateData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// or add new record
|
||||||
|
if(!sensor_found) {
|
||||||
|
WSHistoryItem* item = WSHistoryItemArray_push_raw(instance->history->data);
|
||||||
|
item->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||||
|
item->type = decoder_base->protocol->type;
|
||||||
|
item->preset->frequency = preset->frequency;
|
||||||
|
item->preset->name = furi_string_alloc();
|
||||||
|
furi_string_set(item->preset->name, preset->name);
|
||||||
|
item->preset->data = preset->data;
|
||||||
|
item->preset->data_size = preset->data_size;
|
||||||
|
item->id = id;
|
||||||
|
|
||||||
|
item->item_str = furi_string_alloc();
|
||||||
|
item->flipper_string = flipper_format_string_alloc();
|
||||||
|
subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(!flipper_format_rewind(item->flipper_string)) {
|
||||||
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
item->flipper_string, "Protocol", instance->tmp_string)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Protocol");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_rewind(item->flipper_string)) {
|
||||||
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||||
|
if(!flipper_format_read_hex(item->flipper_string, "Data", key_data, sizeof(uint64_t))) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint64_t data = 0;
|
||||||
|
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||||
|
data = (data << 8) | key_data[i];
|
||||||
|
}
|
||||||
|
if(!(uint32_t)(data >> 32)) {
|
||||||
|
furi_string_printf(
|
||||||
|
item->item_str,
|
||||||
|
"%s %lX",
|
||||||
|
furi_string_get_cstr(instance->tmp_string),
|
||||||
|
(uint32_t)(data & 0xFFFFFFFF));
|
||||||
|
} else {
|
||||||
|
furi_string_printf(
|
||||||
|
item->item_str,
|
||||||
|
"%s %lX%08lX",
|
||||||
|
furi_string_get_cstr(instance->tmp_string),
|
||||||
|
(uint32_t)(data >> 32),
|
||||||
|
(uint32_t)(data & 0xFFFFFFFF));
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
instance->last_index_write++;
|
||||||
|
return WSHistoryStateAddKeyNewDada;
|
||||||
|
}
|
||||||
|
return WSHistoryStateAddKeyUnknown;
|
||||||
|
}
|
112
applications/plugins/weather_station/weather_station_history.h
Normal file
112
applications/plugins/weather_station/weather_station_history.h
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <lib/flipper_format/flipper_format.h>
|
||||||
|
#include <lib/subghz/types.h>
|
||||||
|
|
||||||
|
typedef struct WSHistory WSHistory;
|
||||||
|
|
||||||
|
/** History state add key */
|
||||||
|
typedef enum {
|
||||||
|
WSHistoryStateAddKeyUnknown,
|
||||||
|
WSHistoryStateAddKeyTimeOut,
|
||||||
|
WSHistoryStateAddKeyNewDada,
|
||||||
|
WSHistoryStateAddKeyUpdateData,
|
||||||
|
WSHistoryStateAddKeyOverflow,
|
||||||
|
} WSHistoryStateAddKey;
|
||||||
|
|
||||||
|
/** Allocate WSHistory
|
||||||
|
*
|
||||||
|
* @return WSHistory*
|
||||||
|
*/
|
||||||
|
WSHistory* ws_history_alloc(void);
|
||||||
|
|
||||||
|
/** Free WSHistory
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
*/
|
||||||
|
void ws_history_free(WSHistory* instance);
|
||||||
|
|
||||||
|
/** Clear history
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
*/
|
||||||
|
void ws_history_reset(WSHistory* instance);
|
||||||
|
|
||||||
|
/** Get frequency to history[idx]
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param idx - record index
|
||||||
|
* @return frequency - frequency Hz
|
||||||
|
*/
|
||||||
|
uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx);
|
||||||
|
|
||||||
|
SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx);
|
||||||
|
|
||||||
|
/** Get preset to history[idx]
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param idx - record index
|
||||||
|
* @return preset - preset name
|
||||||
|
*/
|
||||||
|
const char* ws_history_get_preset(WSHistory* instance, uint16_t idx);
|
||||||
|
|
||||||
|
/** Get history index write
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @return idx - current record index
|
||||||
|
*/
|
||||||
|
uint16_t ws_history_get_item(WSHistory* instance);
|
||||||
|
|
||||||
|
/** Get type protocol to history[idx]
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param idx - record index
|
||||||
|
* @return type - type protocol
|
||||||
|
*/
|
||||||
|
uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx);
|
||||||
|
|
||||||
|
/** Get name protocol to history[idx]
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param idx - record index
|
||||||
|
* @return name - const char* name protocol
|
||||||
|
*/
|
||||||
|
const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx);
|
||||||
|
|
||||||
|
/** Get string item menu to history[idx]
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param output - FuriString* output
|
||||||
|
* @param idx - record index
|
||||||
|
*/
|
||||||
|
void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx);
|
||||||
|
|
||||||
|
/** Get string the remaining number of records to history
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param output - FuriString* output
|
||||||
|
* @return bool - is FUUL
|
||||||
|
*/
|
||||||
|
bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output);
|
||||||
|
|
||||||
|
/** Add protocol to history
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param context - SubGhzProtocolCommon context
|
||||||
|
* @param preset - SubGhzRadioPreset preset
|
||||||
|
* @return WSHistoryStateAddKey;
|
||||||
|
*/
|
||||||
|
WSHistoryStateAddKey
|
||||||
|
ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
|
||||||
|
*
|
||||||
|
* @param instance - WSHistory instance
|
||||||
|
* @param idx - record index
|
||||||
|
* @return SubGhzProtocolCommonLoad*
|
||||||
|
*/
|
||||||
|
FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx);
|
|
@ -1,3 +1,4 @@
|
||||||
|
# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user
|
||||||
# for adding manufacture keys
|
# for adding manufacture keys
|
||||||
# AABBCCDDEEFFAABB:X:NAME\r\n
|
# AABBCCDDEEFFAABB:X:NAME\r\n
|
||||||
# AABBCCDDEEFFAABB - man 64 bit
|
# AABBCCDDEEFFAABB - man 64 bit
|
|
@ -1,3 +1,4 @@
|
||||||
|
# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user
|
||||||
Filetype: Flipper SubGhz Setting File
|
Filetype: Flipper SubGhz Setting File
|
||||||
Version: 1
|
Version: 1
|
||||||
|
|
|
@ -2,6 +2,6 @@ Filetype: Flipper SubGhz Key File
|
||||||
Version: 1
|
Version: 1
|
||||||
Frequency: 433920000
|
Frequency: 433920000
|
||||||
Preset: FuriHalSubGhzPresetOok650Async
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
Protocol: Magellen
|
Protocol: Magellan
|
||||||
Bit: 32
|
Bit: 32
|
||||||
Key: 00 00 00 00 37 AE 48 28
|
Key: 00 00 00 00 37 AE 48 28
|
|
@ -1,5 +1,5 @@
|
||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,2.4,,
|
Version,+,3.3,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
|
@ -124,9 +124,15 @@ Header,+,lib/one_wire/one_wire_host.h,,
|
||||||
Header,+,lib/one_wire/one_wire_host_timing.h,,
|
Header,+,lib/one_wire/one_wire_host_timing.h,,
|
||||||
Header,+,lib/one_wire/one_wire_slave.h,,
|
Header,+,lib/one_wire/one_wire_slave.h,,
|
||||||
Header,+,lib/print/wrappers.h,,
|
Header,+,lib/print/wrappers.h,,
|
||||||
|
Header,+,lib/subghz/blocks/const.h,,
|
||||||
|
Header,+,lib/subghz/blocks/decoder.h,,
|
||||||
|
Header,+,lib/subghz/blocks/encoder.h,,
|
||||||
|
Header,+,lib/subghz/blocks/generic.h,,
|
||||||
|
Header,+,lib/subghz/blocks/math.h,,
|
||||||
Header,+,lib/subghz/environment.h,,
|
Header,+,lib/subghz/environment.h,,
|
||||||
Header,+,lib/subghz/protocols/raw.h,,
|
Header,+,lib/subghz/protocols/raw.h,,
|
||||||
Header,+,lib/subghz/receiver.h,,
|
Header,+,lib/subghz/receiver.h,,
|
||||||
|
Header,+,lib/subghz/subghz_setting.h,,
|
||||||
Header,+,lib/subghz/subghz_tx_rx_worker.h,,
|
Header,+,lib/subghz/subghz_tx_rx_worker.h,,
|
||||||
Header,+,lib/subghz/subghz_worker.h,,
|
Header,+,lib/subghz/subghz_worker.h,,
|
||||||
Header,+,lib/subghz/transmitter.h,,
|
Header,+,lib/subghz/transmitter.h,,
|
||||||
|
@ -2240,14 +2246,20 @@ Function,-,strupr,char*,char*
|
||||||
Function,-,strverscmp,int,"const char*, const char*"
|
Function,-,strverscmp,int,"const char*, const char*"
|
||||||
Function,-,strxfrm,size_t,"char*, const char*, size_t"
|
Function,-,strxfrm,size_t,"char*, const char*, size_t"
|
||||||
Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t"
|
Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t"
|
||||||
|
Function,+,subghz_block_generic_deserialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*"
|
||||||
|
Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*"
|
||||||
|
Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*"
|
||||||
Function,+,subghz_environment_alloc,SubGhzEnvironment*,
|
Function,+,subghz_environment_alloc,SubGhzEnvironment*,
|
||||||
Function,+,subghz_environment_free,void,SubGhzEnvironment*
|
Function,+,subghz_environment_free,void,SubGhzEnvironment*
|
||||||
Function,-,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment*
|
Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment*
|
||||||
Function,-,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment*
|
Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment*
|
||||||
Function,-,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment*
|
Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment*
|
||||||
|
Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t"
|
||||||
|
Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment*
|
||||||
Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*"
|
Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*"
|
||||||
Function,-,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*"
|
Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*"
|
||||||
Function,-,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*"
|
Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*"
|
||||||
|
Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*"
|
||||||
Function,-,subghz_keystore_alloc,SubGhzKeystore*,
|
Function,-,subghz_keystore_alloc,SubGhzKeystore*,
|
||||||
Function,-,subghz_keystore_free,void,SubGhzKeystore*
|
Function,-,subghz_keystore_free,void,SubGhzKeystore*
|
||||||
Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore*
|
Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore*
|
||||||
|
@ -2255,10 +2267,20 @@ Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*"
|
||||||
Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*"
|
Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*"
|
||||||
Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t"
|
Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t"
|
||||||
Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*"
|
Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*"
|
||||||
|
Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t"
|
||||||
|
Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t"
|
||||||
|
Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t"
|
||||||
|
Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t"
|
||||||
|
Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t"
|
||||||
|
Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t"
|
||||||
|
Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t"
|
||||||
|
Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t"
|
||||||
|
Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t"
|
||||||
|
Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t"
|
||||||
Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*"
|
Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*"
|
||||||
Function,-,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase*
|
Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase*
|
||||||
Function,-,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*"
|
Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*"
|
||||||
Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzPresetDefinition*"
|
Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*"
|
||||||
Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*"
|
Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*"
|
||||||
Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment*
|
Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment*
|
||||||
Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*"
|
Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*"
|
||||||
|
@ -2274,7 +2296,7 @@ Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void*
|
||||||
Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*"
|
Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*"
|
||||||
Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*"
|
Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*"
|
||||||
Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW*
|
Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW*
|
||||||
Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzPresetDefinition*"
|
Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*"
|
||||||
Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW*
|
Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW*
|
||||||
Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment*
|
Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment*
|
||||||
Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t"
|
Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t"
|
||||||
|
@ -2283,6 +2305,23 @@ Function,+,subghz_receiver_reset,void,SubGhzReceiver*
|
||||||
Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*"
|
Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*"
|
||||||
Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag"
|
Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag"
|
||||||
Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*"
|
Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*"
|
||||||
|
Function,+,subghz_setting_alloc,SubGhzSetting*,
|
||||||
|
Function,+,subghz_setting_delete_custom_preset,_Bool,"SubGhzSetting*, const char*"
|
||||||
|
Function,+,subghz_setting_free,void,SubGhzSetting*
|
||||||
|
Function,+,subghz_setting_get_default_frequency,uint32_t,SubGhzSetting*
|
||||||
|
Function,+,subghz_setting_get_frequency,uint32_t,"SubGhzSetting*, size_t"
|
||||||
|
Function,+,subghz_setting_get_frequency_count,size_t,SubGhzSetting*
|
||||||
|
Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting*
|
||||||
|
Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t"
|
||||||
|
Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting*
|
||||||
|
Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*"
|
||||||
|
Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting*
|
||||||
|
Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t"
|
||||||
|
Function,+,subghz_setting_get_preset_data_by_name,uint8_t*,"SubGhzSetting*, const char*"
|
||||||
|
Function,+,subghz_setting_get_preset_data_size,size_t,"SubGhzSetting*, size_t"
|
||||||
|
Function,+,subghz_setting_get_preset_name,const char*,"SubGhzSetting*, size_t"
|
||||||
|
Function,+,subghz_setting_load,void,"SubGhzSetting*, const char*"
|
||||||
|
Function,+,subghz_setting_load_custom_preset,_Bool,"SubGhzSetting*, const char*, FlipperFormat*"
|
||||||
Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*"
|
Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*"
|
||||||
Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*"
|
Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*"
|
||||||
Function,+,subghz_transmitter_free,void,SubGhzTransmitter*
|
Function,+,subghz_transmitter_free,void,SubGhzTransmitter*
|
||||||
|
|
|
|
@ -11,6 +11,12 @@ env.Append(
|
||||||
File("#/lib/subghz/subghz_tx_rx_worker.h"),
|
File("#/lib/subghz/subghz_tx_rx_worker.h"),
|
||||||
File("#/lib/subghz/transmitter.h"),
|
File("#/lib/subghz/transmitter.h"),
|
||||||
File("#/lib/subghz/protocols/raw.h"),
|
File("#/lib/subghz/protocols/raw.h"),
|
||||||
|
File("#/lib/subghz/blocks/const.h"),
|
||||||
|
File("#/lib/subghz/blocks/decoder.h"),
|
||||||
|
File("#/lib/subghz/blocks/encoder.h"),
|
||||||
|
File("#/lib/subghz/blocks/generic.h"),
|
||||||
|
File("#/lib/subghz/blocks/math.h"),
|
||||||
|
File("#/lib/subghz/subghz_setting.h"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,17 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const uint16_t te_long;
|
const uint16_t te_long;
|
||||||
const uint16_t te_short;
|
const uint16_t te_short;
|
||||||
const uint16_t te_delta;
|
const uint16_t te_delta;
|
||||||
const uint8_t min_count_bit_for_found;
|
const uint8_t min_count_bit_for_found;
|
||||||
} SubGhzBlockConst;
|
} SubGhzBlockConst;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -4,6 +4,10 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct SubGhzBlockDecoder SubGhzBlockDecoder;
|
typedef struct SubGhzBlockDecoder SubGhzBlockDecoder;
|
||||||
|
|
||||||
struct SubGhzBlockDecoder {
|
struct SubGhzBlockDecoder {
|
||||||
|
@ -26,3 +30,7 @@ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit);
|
||||||
* @return hash Hash sum
|
* @return hash Hash sum
|
||||||
*/
|
*/
|
||||||
uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len);
|
uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -6,6 +6,10 @@
|
||||||
|
|
||||||
#include <lib/toolbox/level_duration.h>
|
#include <lib/toolbox/level_duration.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool is_running;
|
bool is_running;
|
||||||
size_t repeat;
|
size_t repeat;
|
||||||
|
@ -50,3 +54,7 @@ size_t subghz_protocol_blocks_get_upload(
|
||||||
LevelDuration* upload,
|
LevelDuration* upload,
|
||||||
size_t max_size_upload,
|
size_t max_size_upload,
|
||||||
uint32_t duration_bit);
|
uint32_t duration_bit);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -23,7 +23,7 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p
|
||||||
bool subghz_block_generic_serialize(
|
bool subghz_block_generic_serialize(
|
||||||
SubGhzBlockGeneric* instance,
|
SubGhzBlockGeneric* instance,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
bool res = false;
|
bool res = false;
|
||||||
FuriString* temp_str;
|
FuriString* temp_str;
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
#include "furi_hal.h"
|
#include "furi_hal.h"
|
||||||
#include "../types.h"
|
#include "../types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct SubGhzBlockGeneric SubGhzBlockGeneric;
|
typedef struct SubGhzBlockGeneric SubGhzBlockGeneric;
|
||||||
|
|
||||||
struct SubGhzBlockGeneric {
|
struct SubGhzBlockGeneric {
|
||||||
|
@ -31,13 +35,13 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p
|
||||||
* Serialize data SubGhzBlockGeneric.
|
* Serialize data SubGhzBlockGeneric.
|
||||||
* @param instance Pointer to a SubGhzBlockGeneric instance
|
* @param instance Pointer to a SubGhzBlockGeneric instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_block_generic_serialize(
|
bool subghz_block_generic_serialize(
|
||||||
SubGhzBlockGeneric* instance,
|
SubGhzBlockGeneric* instance,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzBlockGeneric.
|
* Deserialize data SubGhzBlockGeneric.
|
||||||
|
@ -46,3 +50,7 @@ bool subghz_block_generic_serialize(
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format);
|
bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -14,4 +14,69 @@ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) {
|
||||||
parity += bit_read(key, i);
|
parity += bit_read(key, i);
|
||||||
}
|
}
|
||||||
return parity & 0x01;
|
return parity & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t subghz_protocol_blocks_crc4(
|
||||||
|
uint8_t const message[],
|
||||||
|
unsigned nBytes,
|
||||||
|
uint8_t polynomial,
|
||||||
|
uint8_t init) {
|
||||||
|
unsigned remainder = init << 4; // LSBs are unused
|
||||||
|
unsigned poly = polynomial << 4;
|
||||||
|
unsigned bit;
|
||||||
|
|
||||||
|
while(nBytes--) {
|
||||||
|
remainder ^= *message++;
|
||||||
|
for(bit = 0; bit < 8; bit++) {
|
||||||
|
if(remainder & 0x80) {
|
||||||
|
remainder = (remainder << 1) ^ poly;
|
||||||
|
} else {
|
||||||
|
remainder = (remainder << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remainder >> 4 & 0x0f; // discard the LSBs
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t subghz_protocol_blocks_crc7(
|
||||||
|
uint8_t const message[],
|
||||||
|
unsigned nBytes,
|
||||||
|
uint8_t polynomial,
|
||||||
|
uint8_t init) {
|
||||||
|
unsigned remainder = init << 1; // LSB is unused
|
||||||
|
unsigned poly = polynomial << 1;
|
||||||
|
unsigned byte, bit;
|
||||||
|
|
||||||
|
for(byte = 0; byte < nBytes; ++byte) {
|
||||||
|
remainder ^= message[byte];
|
||||||
|
for(bit = 0; bit < 8; ++bit) {
|
||||||
|
if(remainder & 0x80) {
|
||||||
|
remainder = (remainder << 1) ^ poly;
|
||||||
|
} else {
|
||||||
|
remainder = (remainder << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remainder >> 1 & 0x7f; // discard the LSB
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t subghz_protocol_blocks_crc8(
|
||||||
|
uint8_t const message[],
|
||||||
|
unsigned nBytes,
|
||||||
|
uint8_t polynomial,
|
||||||
|
uint8_t init) {
|
||||||
|
uint8_t remainder = init;
|
||||||
|
unsigned byte, bit;
|
||||||
|
|
||||||
|
for(byte = 0; byte < nBytes; ++byte) {
|
||||||
|
remainder ^= message[byte];
|
||||||
|
for(bit = 0; bit < 8; ++bit) {
|
||||||
|
if(remainder & 0x80) {
|
||||||
|
remainder = (remainder << 1) ^ polynomial;
|
||||||
|
} else {
|
||||||
|
remainder = (remainder << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remainder;
|
||||||
}
|
}
|
|
@ -9,7 +9,11 @@
|
||||||
#define bit_clear(value, bit) ((value) &= ~(1UL << (bit)))
|
#define bit_clear(value, bit) ((value) &= ~(1UL << (bit)))
|
||||||
#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))
|
#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))
|
||||||
#define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y))
|
#define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y))
|
||||||
|
#define abs(x) ((x) > 0 ? (x) : -(x))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
/**
|
/**
|
||||||
* Flip the data bitwise.
|
* Flip the data bitwise.
|
||||||
* @param key In data
|
* @param key In data
|
||||||
|
@ -25,3 +29,51 @@ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit);
|
||||||
* @return parity
|
* @return parity
|
||||||
*/
|
*/
|
||||||
uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit);
|
uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRC-4.
|
||||||
|
* @param message array of bytes to check
|
||||||
|
* @param nBytes number of bytes in message
|
||||||
|
* @param polynomial CRC polynomial
|
||||||
|
* @param init starting crc value
|
||||||
|
* @return CRC value
|
||||||
|
*/
|
||||||
|
uint8_t subghz_protocol_blocks_crc4(
|
||||||
|
uint8_t const message[],
|
||||||
|
unsigned nBytes,
|
||||||
|
uint8_t polynomial,
|
||||||
|
uint8_t init);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRC-7.
|
||||||
|
* @param message array of bytes to check
|
||||||
|
* @param nBytes number of bytes in message
|
||||||
|
* @param polynomial CRC polynomial
|
||||||
|
* @param init starting crc value
|
||||||
|
* @return CRC value
|
||||||
|
*/
|
||||||
|
uint8_t subghz_protocol_blocks_crc7(
|
||||||
|
uint8_t const message[],
|
||||||
|
unsigned nBytes,
|
||||||
|
uint8_t polynomial,
|
||||||
|
uint8_t init);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic Cyclic Redundancy Check CRC-8.
|
||||||
|
* Example polynomial: 0x31 = x8 + x5 + x4 + 1 (x8 is implicit)
|
||||||
|
* Example polynomial: 0x80 = x8 + x7 (a normal bit-by-bit parity XOR)
|
||||||
|
* @param message array of bytes to check
|
||||||
|
* @param nBytes number of bytes in message
|
||||||
|
* @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one)
|
||||||
|
* @param init starting crc value
|
||||||
|
* @return CRC value
|
||||||
|
*/
|
||||||
|
uint8_t subghz_protocol_blocks_crc8(
|
||||||
|
uint8_t const message[],
|
||||||
|
unsigned nBytes,
|
||||||
|
uint8_t polynomial,
|
||||||
|
uint8_t init);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
#include "registry.h"
|
||||||
|
|
||||||
struct SubGhzEnvironment {
|
struct SubGhzEnvironment {
|
||||||
SubGhzKeystore* keystore;
|
SubGhzKeystore* keystore;
|
||||||
|
const SubGhzProtocolRegistry* protocol_registry;
|
||||||
const char* came_atomo_rainbow_table_file_name;
|
const char* came_atomo_rainbow_table_file_name;
|
||||||
const char* nice_flor_s_rainbow_table_file_name;
|
const char* nice_flor_s_rainbow_table_file_name;
|
||||||
};
|
};
|
||||||
|
@ -10,6 +12,7 @@ SubGhzEnvironment* subghz_environment_alloc() {
|
||||||
SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment));
|
SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment));
|
||||||
|
|
||||||
instance->keystore = subghz_keystore_alloc();
|
instance->keystore = subghz_keystore_alloc();
|
||||||
|
instance->protocol_registry = NULL;
|
||||||
instance->came_atomo_rainbow_table_file_name = NULL;
|
instance->came_atomo_rainbow_table_file_name = NULL;
|
||||||
instance->nice_flor_s_rainbow_table_file_name = NULL;
|
instance->nice_flor_s_rainbow_table_file_name = NULL;
|
||||||
|
|
||||||
|
@ -19,6 +22,9 @@ SubGhzEnvironment* subghz_environment_alloc() {
|
||||||
void subghz_environment_free(SubGhzEnvironment* instance) {
|
void subghz_environment_free(SubGhzEnvironment* instance) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
|
||||||
|
instance->protocol_registry = NULL;
|
||||||
|
instance->came_atomo_rainbow_table_file_name = NULL;
|
||||||
|
instance->nice_flor_s_rainbow_table_file_name = NULL;
|
||||||
subghz_keystore_free(instance->keystore);
|
subghz_keystore_free(instance->keystore);
|
||||||
|
|
||||||
free(instance);
|
free(instance);
|
||||||
|
@ -65,3 +71,30 @@ const char*
|
||||||
|
|
||||||
return instance->nice_flor_s_rainbow_table_file_name;
|
return instance->nice_flor_s_rainbow_table_file_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void subghz_environment_set_protocol_registry(
|
||||||
|
SubGhzEnvironment* instance,
|
||||||
|
void* protocol_registry_items) {
|
||||||
|
furi_assert(instance);
|
||||||
|
const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items;
|
||||||
|
instance->protocol_registry = protocol_registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(instance->protocol_registry);
|
||||||
|
return (void*)instance->protocol_registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(instance->protocol_registry);
|
||||||
|
const SubGhzProtocol* protocol =
|
||||||
|
subghz_protocol_registry_get_by_index(instance->protocol_registry, idx);
|
||||||
|
if(protocol != NULL) {
|
||||||
|
return protocol->name;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,6 +69,30 @@ void subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
const char*
|
const char*
|
||||||
subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance);
|
subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set list of protocols to work.
|
||||||
|
* @param instance Pointer to a SubGhzEnvironment instance
|
||||||
|
* @param protocol_registry_items Pointer to a SubGhzProtocolRegistry
|
||||||
|
*/
|
||||||
|
void subghz_environment_set_protocol_registry(
|
||||||
|
SubGhzEnvironment* instance,
|
||||||
|
void* protocol_registry_items);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of protocols to work.
|
||||||
|
* @param instance Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return Pointer to a SubGhzProtocolRegistry
|
||||||
|
*/
|
||||||
|
void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of protocols names.
|
||||||
|
* @param instance Pointer to a SubGhzEnvironment instance
|
||||||
|
* @param idx index protocols
|
||||||
|
* @return Pointer to a SubGhzProtocolRegistry
|
||||||
|
*/
|
||||||
|
const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,7 +26,7 @@ bool subghz_protocol_decoder_base_get_string(
|
||||||
bool subghz_protocol_decoder_base_serialize(
|
bool subghz_protocol_decoder_base_serialize(
|
||||||
SubGhzProtocolDecoderBase* decoder_base,
|
SubGhzProtocolDecoderBase* decoder_base,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
bool status = false;
|
bool status = false;
|
||||||
|
|
||||||
if(decoder_base->protocol && decoder_base->protocol->decoder &&
|
if(decoder_base->protocol && decoder_base->protocol->decoder &&
|
||||||
|
|
|
@ -48,13 +48,13 @@ bool subghz_protocol_decoder_base_get_string(
|
||||||
* Serialize data SubGhzProtocolDecoderBase.
|
* Serialize data SubGhzProtocolDecoderBase.
|
||||||
* @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance
|
* @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_base_serialize(
|
bool subghz_protocol_decoder_base_serialize(
|
||||||
SubGhzProtocolDecoderBase* decoder_base,
|
SubGhzProtocolDecoderBase* decoder_base,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderBase.
|
* Deserialize data SubGhzProtocolDecoderBase.
|
||||||
|
|
|
@ -299,7 +299,7 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_bett_serialize(
|
bool subghz_protocol_decoder_bett_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderBETT* instance = context;
|
SubGhzProtocolDecoderBETT* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderBETT.
|
* Serialize data SubGhzProtocolDecoderBETT.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_bett_serialize(
|
bool subghz_protocol_decoder_bett_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderBETT.
|
* Deserialize data SubGhzProtocolDecoderBETT.
|
||||||
|
|
|
@ -295,7 +295,7 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_came_serialize(
|
bool subghz_protocol_decoder_came_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderCame* instance = context;
|
SubGhzProtocolDecoderCame* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderCame.
|
* Serialize data SubGhzProtocolDecoderCame.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_came_serialize(
|
bool subghz_protocol_decoder_came_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderCame.
|
* Deserialize data SubGhzProtocolDecoderCame.
|
||||||
|
|
|
@ -301,7 +301,7 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_came_atomo_serialize(
|
bool subghz_protocol_decoder_came_atomo_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -48,13 +48,13 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderCameAtomo.
|
* Serialize data SubGhzProtocolDecoderCameAtomo.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_came_atomo_serialize(
|
bool subghz_protocol_decoder_came_atomo_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderCameAtomo.
|
* Deserialize data SubGhzProtocolDecoderCameAtomo.
|
||||||
|
|
|
@ -422,7 +422,7 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_came_twee_serialize(
|
bool subghz_protocol_decoder_came_twee_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderCameTwee.
|
* Serialize data SubGhzProtocolDecoderCameTwee.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_came_twee_serialize(
|
bool subghz_protocol_decoder_came_twee_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderCameTwee.
|
* Deserialize data SubGhzProtocolDecoderCameTwee.
|
||||||
|
|
|
@ -427,7 +427,7 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_chamb_code_serialize(
|
bool subghz_protocol_decoder_chamb_code_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderChamb_Code.
|
* Serialize data SubGhzProtocolDecoderChamb_Code.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_chamb_code_serialize(
|
bool subghz_protocol_decoder_chamb_code_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderChamb_Code.
|
* Deserialize data SubGhzProtocolDecoderChamb_Code.
|
||||||
|
|
|
@ -319,7 +319,7 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_clemsa_serialize(
|
bool subghz_protocol_decoder_clemsa_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderClemsa* instance = context;
|
SubGhzProtocolDecoderClemsa* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderClemsa.
|
* Serialize data SubGhzProtocolDecoderClemsa.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_clemsa_serialize(
|
bool subghz_protocol_decoder_clemsa_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderClemsa.
|
* Deserialize data SubGhzProtocolDecoderClemsa.
|
||||||
|
|
|
@ -313,7 +313,7 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_doitrand_serialize(
|
bool subghz_protocol_decoder_doitrand_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderDoitrand.
|
* Serialize data SubGhzProtocolDecoderDoitrand.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_doitrand_serialize(
|
bool subghz_protocol_decoder_doitrand_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderDoitrand.
|
* Deserialize data SubGhzProtocolDecoderDoitrand.
|
||||||
|
|
|
@ -183,7 +183,7 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_faac_slh_serialize(
|
bool subghz_protocol_decoder_faac_slh_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderFaacSLH.
|
* Serialize data SubGhzProtocolDecoderFaacSLH.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_faac_slh_serialize(
|
bool subghz_protocol_decoder_faac_slh_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderFaacSLH.
|
* Deserialize data SubGhzProtocolDecoderFaacSLH.
|
||||||
|
|
|
@ -293,7 +293,7 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_gate_tx_serialize(
|
bool subghz_protocol_decoder_gate_tx_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderGateTx* instance = context;
|
SubGhzProtocolDecoderGateTx* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderGateTx.
|
* Serialize data SubGhzProtocolDecoderGateTx.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_gate_tx_serialize(
|
bool subghz_protocol_decoder_gate_tx_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderGateTx.
|
* Deserialize data SubGhzProtocolDecoderGateTx.
|
||||||
|
|
|
@ -326,7 +326,7 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_holtek_serialize(
|
bool subghz_protocol_decoder_holtek_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderHoltek* instance = context;
|
SubGhzProtocolDecoderHoltek* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderHoltek.
|
* Serialize data SubGhzProtocolDecoderHoltek.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_holtek_serialize(
|
bool subghz_protocol_decoder_holtek_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderHoltek.
|
* Deserialize data SubGhzProtocolDecoderHoltek.
|
||||||
|
|
|
@ -348,7 +348,7 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_honeywell_wdb_serialize(
|
bool subghz_protocol_decoder_honeywell_wdb_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderHoneywell_WDB.
|
* Serialize data SubGhzProtocolDecoderHoneywell_WDB.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_honeywell_wdb_serialize(
|
bool subghz_protocol_decoder_honeywell_wdb_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderHoneywell_WDB.
|
* Deserialize data SubGhzProtocolDecoderHoneywell_WDB.
|
||||||
|
|
|
@ -314,7 +314,7 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_hormann_serialize(
|
bool subghz_protocol_decoder_hormann_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderHormann* instance = context;
|
SubGhzProtocolDecoderHormann* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderHormann.
|
* Serialize data SubGhzProtocolDecoderHormann.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_hormann_serialize(
|
bool subghz_protocol_decoder_hormann_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderHormann.
|
* Deserialize data SubGhzProtocolDecoderHormann.
|
||||||
|
|
|
@ -182,7 +182,7 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_ido_serialize(
|
bool subghz_protocol_decoder_ido_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderIDo* instance = context;
|
SubGhzProtocolDecoderIDo* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderIDo.
|
* Serialize data SubGhzProtocolDecoderIDo.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_ido_serialize(
|
bool subghz_protocol_decoder_ido_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderIDo.
|
* Deserialize data SubGhzProtocolDecoderIDo.
|
||||||
|
|
|
@ -407,7 +407,7 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_intertechno_v3_serialize(
|
bool subghz_protocol_decoder_intertechno_v3_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderIntertechno_V3.
|
* Serialize data SubGhzProtocolDecoderIntertechno_V3.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_intertechno_v3_serialize(
|
bool subghz_protocol_decoder_intertechno_v3_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderIntertechno_V3.
|
* Deserialize data SubGhzProtocolDecoderIntertechno_V3.
|
||||||
|
|
|
@ -173,7 +173,7 @@ bool subghz_protocol_keeloq_create_data(
|
||||||
uint8_t btn,
|
uint8_t btn,
|
||||||
uint16_t cnt,
|
uint16_t cnt,
|
||||||
const char* manufacture_name,
|
const char* manufacture_name,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolEncoderKeeloq* instance = context;
|
SubGhzProtocolEncoderKeeloq* instance = context;
|
||||||
instance->generic.serial = serial;
|
instance->generic.serial = serial;
|
||||||
|
@ -646,7 +646,7 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_keeloq_serialize(
|
bool subghz_protocol_decoder_keeloq_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderKeeloq* instance = context;
|
SubGhzProtocolDecoderKeeloq* instance = context;
|
||||||
subghz_protocol_keeloq_check_remote_controller(
|
subghz_protocol_keeloq_check_remote_controller(
|
||||||
|
|
|
@ -32,7 +32,7 @@ void subghz_protocol_encoder_keeloq_free(void* context);
|
||||||
* @param btn Button number, 4 bit
|
* @param btn Button number, 4 bit
|
||||||
* @param cnt Container value, 16 bit
|
* @param cnt Container value, 16 bit
|
||||||
* @param manufacture_name Name of manufacturer's key
|
* @param manufacture_name Name of manufacturer's key
|
||||||
* @param preset Modulation, SubGhzPresetDefinition
|
* @param preset Modulation, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_keeloq_create_data(
|
bool subghz_protocol_keeloq_create_data(
|
||||||
|
@ -42,7 +42,7 @@ bool subghz_protocol_keeloq_create_data(
|
||||||
uint8_t btn,
|
uint8_t btn,
|
||||||
uint16_t cnt,
|
uint16_t cnt,
|
||||||
const char* manufacture_name,
|
const char* manufacture_name,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize and generating an upload to send.
|
* Deserialize and generating an upload to send.
|
||||||
|
@ -103,13 +103,13 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderKeeloq.
|
* Serialize data SubGhzProtocolDecoderKeeloq.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_keeloq_serialize(
|
bool subghz_protocol_decoder_keeloq_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderKeeloq.
|
* Deserialize data SubGhzProtocolDecoderKeeloq.
|
||||||
|
|
|
@ -233,7 +233,7 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_kia_serialize(
|
bool subghz_protocol_decoder_kia_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderKIA* instance = context;
|
SubGhzProtocolDecoderKIA* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderKIA.
|
* Serialize data SubGhzProtocolDecoderKIA.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_kia_serialize(
|
bool subghz_protocol_decoder_kia_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderKIA.
|
* Deserialize data SubGhzProtocolDecoderKIA.
|
||||||
|
|
|
@ -303,7 +303,7 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) {
|
||||||
bool subghz_protocol_decoder_linear_serialize(
|
bool subghz_protocol_decoder_linear_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderLinear* instance = context;
|
SubGhzProtocolDecoderLinear* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
|
|
@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context);
|
||||||
* Serialize data SubGhzProtocolDecoderLinear.
|
* Serialize data SubGhzProtocolDecoderLinear.
|
||||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||||
* @param flipper_format Pointer to a FlipperFormat instance
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
bool subghz_protocol_decoder_linear_serialize(
|
bool subghz_protocol_decoder_linear_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize data SubGhzProtocolDecoderLinear.
|
* Deserialize data SubGhzProtocolDecoderLinear.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "magellen.h"
|
#include "magellan.h"
|
||||||
|
|
||||||
#include "../blocks/const.h"
|
#include "../blocks/const.h"
|
||||||
#include "../blocks/decoder.h"
|
#include "../blocks/decoder.h"
|
||||||
|
@ -6,16 +6,16 @@
|
||||||
#include "../blocks/generic.h"
|
#include "../blocks/generic.h"
|
||||||
#include "../blocks/math.h"
|
#include "../blocks/math.h"
|
||||||
|
|
||||||
#define TAG "SubGhzProtocolMagellen"
|
#define TAG "SubGhzProtocolMagellan"
|
||||||
|
|
||||||
static const SubGhzBlockConst subghz_protocol_magellen_const = {
|
static const SubGhzBlockConst subghz_protocol_magellan_const = {
|
||||||
.te_short = 200,
|
.te_short = 200,
|
||||||
.te_long = 400,
|
.te_long = 400,
|
||||||
.te_delta = 100,
|
.te_delta = 100,
|
||||||
.min_count_bit_for_found = 32,
|
.min_count_bit_for_found = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SubGhzProtocolDecoderMagellen {
|
struct SubGhzProtocolDecoderMagellan {
|
||||||
SubGhzProtocolDecoderBase base;
|
SubGhzProtocolDecoderBase base;
|
||||||
|
|
||||||
SubGhzBlockDecoder decoder;
|
SubGhzBlockDecoder decoder;
|
||||||
|
@ -23,7 +23,7 @@ struct SubGhzProtocolDecoderMagellen {
|
||||||
uint16_t header_count;
|
uint16_t header_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SubGhzProtocolEncoderMagellen {
|
struct SubGhzProtocolEncoderMagellan {
|
||||||
SubGhzProtocolEncoderBase base;
|
SubGhzProtocolEncoderBase base;
|
||||||
|
|
||||||
SubGhzProtocolBlockEncoder encoder;
|
SubGhzProtocolBlockEncoder encoder;
|
||||||
|
@ -31,50 +31,50 @@ struct SubGhzProtocolEncoderMagellen {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MagellenDecoderStepReset = 0,
|
MagellanDecoderStepReset = 0,
|
||||||
MagellenDecoderStepCheckPreambula,
|
MagellanDecoderStepCheckPreambula,
|
||||||
MagellenDecoderStepFoundPreambula,
|
MagellanDecoderStepFoundPreambula,
|
||||||
MagellenDecoderStepSaveDuration,
|
MagellanDecoderStepSaveDuration,
|
||||||
MagellenDecoderStepCheckDuration,
|
MagellanDecoderStepCheckDuration,
|
||||||
} MagellenDecoderStep;
|
} MagellanDecoderStep;
|
||||||
|
|
||||||
const SubGhzProtocolDecoder subghz_protocol_magellen_decoder = {
|
const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = {
|
||||||
.alloc = subghz_protocol_decoder_magellen_alloc,
|
.alloc = subghz_protocol_decoder_magellan_alloc,
|
||||||
.free = subghz_protocol_decoder_magellen_free,
|
.free = subghz_protocol_decoder_magellan_free,
|
||||||
|
|
||||||
.feed = subghz_protocol_decoder_magellen_feed,
|
.feed = subghz_protocol_decoder_magellan_feed,
|
||||||
.reset = subghz_protocol_decoder_magellen_reset,
|
.reset = subghz_protocol_decoder_magellan_reset,
|
||||||
|
|
||||||
.get_hash_data = subghz_protocol_decoder_magellen_get_hash_data,
|
.get_hash_data = subghz_protocol_decoder_magellan_get_hash_data,
|
||||||
.serialize = subghz_protocol_decoder_magellen_serialize,
|
.serialize = subghz_protocol_decoder_magellan_serialize,
|
||||||
.deserialize = subghz_protocol_decoder_magellen_deserialize,
|
.deserialize = subghz_protocol_decoder_magellan_deserialize,
|
||||||
.get_string = subghz_protocol_decoder_magellen_get_string,
|
.get_string = subghz_protocol_decoder_magellan_get_string,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SubGhzProtocolEncoder subghz_protocol_magellen_encoder = {
|
const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = {
|
||||||
.alloc = subghz_protocol_encoder_magellen_alloc,
|
.alloc = subghz_protocol_encoder_magellan_alloc,
|
||||||
.free = subghz_protocol_encoder_magellen_free,
|
.free = subghz_protocol_encoder_magellan_free,
|
||||||
|
|
||||||
.deserialize = subghz_protocol_encoder_magellen_deserialize,
|
.deserialize = subghz_protocol_encoder_magellan_deserialize,
|
||||||
.stop = subghz_protocol_encoder_magellen_stop,
|
.stop = subghz_protocol_encoder_magellan_stop,
|
||||||
.yield = subghz_protocol_encoder_magellen_yield,
|
.yield = subghz_protocol_encoder_magellan_yield,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SubGhzProtocol subghz_protocol_magellen = {
|
const SubGhzProtocol subghz_protocol_magellan = {
|
||||||
.name = SUBGHZ_PROTOCOL_MAGELLEN_NAME,
|
.name = SUBGHZ_PROTOCOL_MAGELLAN_NAME,
|
||||||
.type = SubGhzProtocolTypeStatic,
|
.type = SubGhzProtocolTypeStatic,
|
||||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||||
|
|
||||||
.decoder = &subghz_protocol_magellen_decoder,
|
.decoder = &subghz_protocol_magellan_decoder,
|
||||||
.encoder = &subghz_protocol_magellen_encoder,
|
.encoder = &subghz_protocol_magellan_encoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) {
|
void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) {
|
||||||
UNUSED(environment);
|
UNUSED(environment);
|
||||||
SubGhzProtocolEncoderMagellen* instance = malloc(sizeof(SubGhzProtocolEncoderMagellen));
|
SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan));
|
||||||
|
|
||||||
instance->base.protocol = &subghz_protocol_magellen;
|
instance->base.protocol = &subghz_protocol_magellan;
|
||||||
instance->generic.protocol_name = instance->base.protocol->name;
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
|
||||||
instance->encoder.repeat = 10;
|
instance->encoder.repeat = 10;
|
||||||
|
@ -84,75 +84,75 @@ void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_protocol_encoder_magellen_free(void* context) {
|
void subghz_protocol_encoder_magellan_free(void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolEncoderMagellen* instance = context;
|
SubGhzProtocolEncoderMagellan* instance = context;
|
||||||
free(instance->encoder.upload);
|
free(instance->encoder.upload);
|
||||||
free(instance);
|
free(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generating an upload from data.
|
* Generating an upload from data.
|
||||||
* @param instance Pointer to a SubGhzProtocolEncoderMagellen instance
|
* @param instance Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||||
* @return true On success
|
* @return true On success
|
||||||
*/
|
*/
|
||||||
static bool subghz_protocol_encoder_magellen_get_upload(SubGhzProtocolEncoderMagellen* instance) {
|
static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
|
|
||||||
//Send header
|
//Send header
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short * 4);
|
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4);
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short);
|
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||||
for(uint8_t i = 0; i < 12; i++) {
|
for(uint8_t i = 0; i < 12; i++) {
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short);
|
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short);
|
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||||
}
|
}
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short);
|
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long);
|
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||||
|
|
||||||
//Send start bit
|
//Send start bit
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long * 3);
|
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3);
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long);
|
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||||
|
|
||||||
//Send key data
|
//Send key data
|
||||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||||
if(bit_read(instance->generic.data, i - 1)) {
|
if(bit_read(instance->generic.data, i - 1)) {
|
||||||
//send bit 1
|
//send bit 1
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short);
|
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long);
|
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||||
} else {
|
} else {
|
||||||
//send bit 0
|
//send bit 0
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long);
|
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short);
|
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Send stop bit
|
//Send stop bit
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short);
|
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||||
instance->encoder.upload[index++] =
|
instance->encoder.upload[index++] =
|
||||||
level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long * 100);
|
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100);
|
||||||
|
|
||||||
instance->encoder.size_upload = index;
|
instance->encoder.size_upload = index;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) {
|
bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolEncoderMagellen* instance = context;
|
SubGhzProtocolEncoderMagellan* instance = context;
|
||||||
bool res = false;
|
bool res = false;
|
||||||
do {
|
do {
|
||||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||||
|
@ -160,7 +160,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat*
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(instance->generic.data_count_bit !=
|
if(instance->generic.data_count_bit !=
|
||||||
subghz_protocol_magellen_const.min_count_bit_for_found) {
|
subghz_protocol_magellan_const.min_count_bit_for_found) {
|
||||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat*
|
||||||
flipper_format_read_uint32(
|
flipper_format_read_uint32(
|
||||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||||
|
|
||||||
if(!subghz_protocol_encoder_magellen_get_upload(instance)) break;
|
if(!subghz_protocol_encoder_magellan_get_upload(instance)) break;
|
||||||
instance->encoder.is_running = true;
|
instance->encoder.is_running = true;
|
||||||
|
|
||||||
res = true;
|
res = true;
|
||||||
|
@ -177,13 +177,13 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat*
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_protocol_encoder_magellen_stop(void* context) {
|
void subghz_protocol_encoder_magellan_stop(void* context) {
|
||||||
SubGhzProtocolEncoderMagellen* instance = context;
|
SubGhzProtocolEncoderMagellan* instance = context;
|
||||||
instance->encoder.is_running = false;
|
instance->encoder.is_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelDuration subghz_protocol_encoder_magellen_yield(void* context) {
|
LevelDuration subghz_protocol_encoder_magellan_yield(void* context) {
|
||||||
SubGhzProtocolEncoderMagellen* instance = context;
|
SubGhzProtocolEncoderMagellan* instance = context;
|
||||||
|
|
||||||
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
||||||
instance->encoder.is_running = false;
|
instance->encoder.is_running = false;
|
||||||
|
@ -200,27 +200,27 @@ LevelDuration subghz_protocol_encoder_magellen_yield(void* context) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment) {
|
void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) {
|
||||||
UNUSED(environment);
|
UNUSED(environment);
|
||||||
SubGhzProtocolDecoderMagellen* instance = malloc(sizeof(SubGhzProtocolDecoderMagellen));
|
SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan));
|
||||||
instance->base.protocol = &subghz_protocol_magellen;
|
instance->base.protocol = &subghz_protocol_magellan;
|
||||||
instance->generic.protocol_name = instance->base.protocol->name;
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_protocol_decoder_magellen_free(void* context) {
|
void subghz_protocol_decoder_magellan_free(void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderMagellen* instance = context;
|
SubGhzProtocolDecoderMagellan* instance = context;
|
||||||
free(instance);
|
free(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_protocol_decoder_magellen_reset(void* context) {
|
void subghz_protocol_decoder_magellan_reset(void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderMagellen* instance = context;
|
SubGhzProtocolDecoderMagellan* instance = context;
|
||||||
instance->decoder.parser_step = MagellenDecoderStepReset;
|
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) {
|
uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) {
|
||||||
uint8_t crc = 0x00;
|
uint8_t crc = 0x00;
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
for(i = 0; i < len; i++) {
|
for(i = 0; i < len; i++) {
|
||||||
|
@ -235,99 +235,99 @@ uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) {
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool subghz_protocol_magellen_check_crc(SubGhzProtocolDecoderMagellen* instance) {
|
static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) {
|
||||||
uint8_t data[3] = {
|
uint8_t data[3] = {
|
||||||
instance->decoder.decode_data >> 24,
|
instance->decoder.decode_data >> 24,
|
||||||
instance->decoder.decode_data >> 16,
|
instance->decoder.decode_data >> 16,
|
||||||
instance->decoder.decode_data >> 8};
|
instance->decoder.decode_data >> 8};
|
||||||
return (instance->decoder.decode_data & 0xFF) ==
|
return (instance->decoder.decode_data & 0xFF) ==
|
||||||
subghz_protocol_magellen_crc8(data, sizeof(data));
|
subghz_protocol_magellan_crc8(data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration) {
|
void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderMagellen* instance = context;
|
SubGhzProtocolDecoderMagellan* instance = context;
|
||||||
|
|
||||||
switch(instance->decoder.parser_step) {
|
switch(instance->decoder.parser_step) {
|
||||||
case MagellenDecoderStepReset:
|
case MagellanDecoderStepReset:
|
||||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) <
|
if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
|
||||||
subghz_protocol_magellen_const.te_delta)) {
|
subghz_protocol_magellan_const.te_delta)) {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepCheckPreambula;
|
instance->decoder.parser_step = MagellanDecoderStepCheckPreambula;
|
||||||
instance->decoder.te_last = duration;
|
instance->decoder.te_last = duration;
|
||||||
instance->header_count = 0;
|
instance->header_count = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MagellenDecoderStepCheckPreambula:
|
case MagellanDecoderStepCheckPreambula:
|
||||||
if(level) {
|
if(level) {
|
||||||
instance->decoder.te_last = duration;
|
instance->decoder.te_last = duration;
|
||||||
} else {
|
} else {
|
||||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) <
|
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
|
||||||
subghz_protocol_magellen_const.te_delta) &&
|
subghz_protocol_magellan_const.te_delta) &&
|
||||||
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) <
|
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
|
||||||
subghz_protocol_magellen_const.te_delta)) {
|
subghz_protocol_magellan_const.te_delta)) {
|
||||||
// Found header
|
// Found header
|
||||||
instance->header_count++;
|
instance->header_count++;
|
||||||
} else if(
|
} else if(
|
||||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) <
|
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
|
||||||
subghz_protocol_magellen_const.te_delta) &&
|
subghz_protocol_magellan_const.te_delta) &&
|
||||||
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) <
|
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
|
||||||
subghz_protocol_magellen_const.te_delta * 2) &&
|
subghz_protocol_magellan_const.te_delta * 2) &&
|
||||||
(instance->header_count > 10)) {
|
(instance->header_count > 10)) {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepFoundPreambula;
|
instance->decoder.parser_step = MagellanDecoderStepFoundPreambula;
|
||||||
} else {
|
} else {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepReset;
|
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MagellenDecoderStepFoundPreambula:
|
case MagellanDecoderStepFoundPreambula:
|
||||||
if(level) {
|
if(level) {
|
||||||
instance->decoder.te_last = duration;
|
instance->decoder.te_last = duration;
|
||||||
} else {
|
} else {
|
||||||
if((DURATION_DIFF(
|
if((DURATION_DIFF(
|
||||||
instance->decoder.te_last, subghz_protocol_magellen_const.te_short * 6) <
|
instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) <
|
||||||
subghz_protocol_magellen_const.te_delta * 3) &&
|
subghz_protocol_magellan_const.te_delta * 3) &&
|
||||||
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) <
|
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
|
||||||
subghz_protocol_magellen_const.te_delta * 2)) {
|
subghz_protocol_magellan_const.te_delta * 2)) {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepSaveDuration;
|
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
|
||||||
instance->decoder.decode_data = 0;
|
instance->decoder.decode_data = 0;
|
||||||
instance->decoder.decode_count_bit = 0;
|
instance->decoder.decode_count_bit = 0;
|
||||||
} else {
|
} else {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepReset;
|
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MagellenDecoderStepSaveDuration:
|
case MagellanDecoderStepSaveDuration:
|
||||||
if(level) {
|
if(level) {
|
||||||
instance->decoder.te_last = duration;
|
instance->decoder.te_last = duration;
|
||||||
instance->decoder.parser_step = MagellenDecoderStepCheckDuration;
|
instance->decoder.parser_step = MagellanDecoderStepCheckDuration;
|
||||||
} else {
|
} else {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepReset;
|
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MagellenDecoderStepCheckDuration:
|
case MagellanDecoderStepCheckDuration:
|
||||||
if(!level) {
|
if(!level) {
|
||||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) <
|
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
|
||||||
subghz_protocol_magellen_const.te_delta) &&
|
subghz_protocol_magellan_const.te_delta) &&
|
||||||
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) <
|
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
|
||||||
subghz_protocol_magellen_const.te_delta)) {
|
subghz_protocol_magellan_const.te_delta)) {
|
||||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||||
instance->decoder.parser_step = MagellenDecoderStepSaveDuration;
|
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
|
||||||
} else if(
|
} else if(
|
||||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_long) <
|
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) <
|
||||||
subghz_protocol_magellen_const.te_delta) &&
|
subghz_protocol_magellan_const.te_delta) &&
|
||||||
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) <
|
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
|
||||||
subghz_protocol_magellen_const.te_delta)) {
|
subghz_protocol_magellan_const.te_delta)) {
|
||||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||||
instance->decoder.parser_step = MagellenDecoderStepSaveDuration;
|
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
|
||||||
} else if(duration >= (subghz_protocol_magellen_const.te_long * 3)) {
|
} else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) {
|
||||||
//Found stop bit
|
//Found stop bit
|
||||||
if((instance->decoder.decode_count_bit ==
|
if((instance->decoder.decode_count_bit ==
|
||||||
subghz_protocol_magellen_const.min_count_bit_for_found) &&
|
subghz_protocol_magellan_const.min_count_bit_for_found) &&
|
||||||
subghz_protocol_magellen_check_crc(instance)) {
|
subghz_protocol_magellan_check_crc(instance)) {
|
||||||
instance->generic.data = instance->decoder.decode_data;
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||||
if(instance->base.callback)
|
if(instance->base.callback)
|
||||||
|
@ -335,12 +335,12 @@ void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t d
|
||||||
}
|
}
|
||||||
instance->decoder.decode_data = 0;
|
instance->decoder.decode_data = 0;
|
||||||
instance->decoder.decode_count_bit = 0;
|
instance->decoder.decode_count_bit = 0;
|
||||||
instance->decoder.parser_step = MagellenDecoderStepReset;
|
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||||
} else {
|
} else {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepReset;
|
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
instance->decoder.parser_step = MagellenDecoderStepReset;
|
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +350,7 @@ void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t d
|
||||||
* Analysis of received data
|
* Analysis of received data
|
||||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||||
*/
|
*/
|
||||||
static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric* instance) {
|
static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||||
/*
|
/*
|
||||||
* package 32b data 24b CRC8
|
* package 32b data 24b CRC8
|
||||||
* 0x037AE4828 => 001101111010111001001000 00101000
|
* 0x037AE4828 => 001101111010111001001000 00101000
|
||||||
|
@ -375,7 +375,7 @@ static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric*
|
||||||
instance->btn = (data_rev >> 16) & 0xFF;
|
instance->btn = (data_rev >> 16) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subghz_protocol_magellen_get_event_serialize(uint8_t event, FuriString* output) {
|
static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) {
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
output,
|
output,
|
||||||
"%s%s%s%s%s%s%s%s",
|
"%s%s%s%s%s%s%s%s",
|
||||||
|
@ -390,32 +390,32 @@ static void subghz_protocol_magellen_get_event_serialize(uint8_t event, FuriStri
|
||||||
((event >> 7) & 0x1 ? ", ?" : ""));
|
((event >> 7) & 0x1 ? ", ?" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context) {
|
uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderMagellen* instance = context;
|
SubGhzProtocolDecoderMagellan* instance = context;
|
||||||
return subghz_protocol_blocks_get_hash_data(
|
return subghz_protocol_blocks_get_hash_data(
|
||||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subghz_protocol_decoder_magellen_serialize(
|
bool subghz_protocol_decoder_magellan_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzPresetDefinition* preset) {
|
SubGhzRadioPreset* preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderMagellen* instance = context;
|
SubGhzProtocolDecoderMagellan* instance = context;
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) {
|
bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderMagellen* instance = context;
|
SubGhzProtocolDecoderMagellan* instance = context;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
do {
|
do {
|
||||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(instance->generic.data_count_bit !=
|
if(instance->generic.data_count_bit !=
|
||||||
subghz_protocol_magellen_const.min_count_bit_for_found) {
|
subghz_protocol_magellan_const.min_count_bit_for_found) {
|
||||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -424,10 +424,10 @@ bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat*
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* output) {
|
void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzProtocolDecoderMagellen* instance = context;
|
SubGhzProtocolDecoderMagellan* instance = context;
|
||||||
subghz_protocol_magellen_check_remote_controller(&instance->generic);
|
subghz_protocol_magellan_check_remote_controller(&instance->generic);
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
output,
|
output,
|
||||||
"%s %dbit\r\n"
|
"%s %dbit\r\n"
|
||||||
|
@ -441,5 +441,5 @@ void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* outp
|
||||||
instance->generic.serial & 0xFF,
|
instance->generic.serial & 0xFF,
|
||||||
instance->generic.btn);
|
instance->generic.btn);
|
||||||
|
|
||||||
subghz_protocol_magellen_get_event_serialize(instance->generic.btn, output);
|
subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output);
|
||||||
}
|
}
|
107
lib/subghz/protocols/magellan.h
Normal file
107
lib/subghz/protocols/magellan.h
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
|
||||||
|
#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan"
|
||||||
|
|
||||||
|
typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan;
|
||||||
|
typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan;
|
||||||
|
|
||||||
|
extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder;
|
||||||
|
extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder;
|
||||||
|
extern const SubGhzProtocol subghz_protocol_magellan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate SubGhzProtocolEncoderMagellan.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance
|
||||||
|
*/
|
||||||
|
void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free SubGhzProtocolEncoderMagellan.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||||
|
*/
|
||||||
|
void subghz_protocol_encoder_magellan_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize and generating an upload to send.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forced transmission stop.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||||
|
*/
|
||||||
|
void subghz_protocol_encoder_magellan_stop(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the level and duration of the upload to be loaded into DMA.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||||
|
* @return LevelDuration
|
||||||
|
*/
|
||||||
|
LevelDuration subghz_protocol_encoder_magellan_yield(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate SubGhzProtocolDecoderMagellan.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance
|
||||||
|
*/
|
||||||
|
void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free SubGhzProtocolDecoderMagellan.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||||
|
*/
|
||||||
|
void subghz_protocol_decoder_magellan_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset decoder SubGhzProtocolDecoderMagellan.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||||
|
*/
|
||||||
|
void subghz_protocol_decoder_magellan_reset(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a raw sequence of levels and durations received from the air.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||||
|
* @param level Signal level true-high false-low
|
||||||
|
* @param duration Duration of this level in, us
|
||||||
|
*/
|
||||||
|
void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the hash sum of the last randomly received parcel.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||||
|
* @return hash Hash sum
|
||||||
|
*/
|
||||||
|
uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data SubGhzProtocolDecoderMagellan.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderMagellan 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 subghz_protocol_decoder_magellan_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data SubGhzProtocolDecoderMagellan.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return true On success
|
||||||
|
*/
|
||||||
|
bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting a textual representation of the received data.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||||
|
* @param output Resulting text
|
||||||
|
*/
|
||||||
|
void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue