1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

Compare commits

...

19 Commits

Author SHA1 Message Date
MX
3303047a4a update changelog 2022-10-19 02:52:25 +03:00
MX
488563000a subghz bruteforce - add chamberlain 7b and 8b 2022-10-19 02:47:44 +03:00
MX
6c08564d37 rm 313 from hopper 2022-10-19 01:41:59 +03:00
MX
5b8311cdea rename setting user back (since we are using it for custom stuff)
update change log too
2022-10-19 01:03:46 +03:00
MX
68429e191d Merge pull request #119 from ankris812/dev
replacing dict with expanded one
2022-10-19 00:55:52 +03:00
MX
5e7bcea29d removed duplicates 2022-10-19 00:54:27 +03:00
SkorP
a139f015b9 SubGhz: fix unit_test 2022-10-18 20:38:57 +03:00
MX
6579576490 fix protocol items requirement 2022-10-18 20:38:37 +03:00
MX
57251eb028 Merge branch 'fz-dev' into dev 2022-10-18 19:55:26 +03:00
Aleksandr Kutuzov
f33ea3ebe0 SubGhz: partial unit tests fix 2022-10-18 19:54:40 +03:00
MX
84d12da45a add app WeatherStation
OFW PR 1833 by Skorpionm
2022-10-18 19:51:44 +03:00
Kevin Kwok
72713d6f4e Allow pins 0 and 1 as RTS/DTR for USB UART Bridge (#1864)
* Allow pins 0 and 1 as RTS/DTR for USB UART Bridge
* add logic to gpio_scene_usb_uart_config, fix flow_pins
* fixing count of pins
* disable PC0,PC1 RTS/DTR when using LPUART
* add logic to ensure flow pins dont overlap with uart lines

Co-authored-by: あく <alleteam@gmail.com>
2022-10-19 00:06:18 +09:00
ankris812
468bc1dace added dict with extra keys 2022-10-18 17:02:30 +02:00
Patrick Cunningham
56f760aa07 Picopass: Read Elite (#1888)
* working elite dict
* add csn to display

Co-authored-by: あく <alleteam@gmail.com>
2022-10-18 23:58:26 +09:00
ankris812
6db6d123d5 replacing dict with expanded one 2022-10-18 16:52:58 +02:00
Skorpionm
68009c6230 [FL-2919] SubGhz: CAME Wrong number of bits in key (add protocol Airforce) (#1890)
Co-authored-by: あく <alleteam@gmail.com>
2022-10-18 23:24:53 +09:00
Georgii Surkov
02c27becb0 [FL-2912] Forced RAW receive option for Infrared CLI #1891
Co-authored-by: あく <alleteam@gmail.com>
2022-10-18 23:10:21 +09:00
MX
576dab02a4 Merge branch 'fz-dev' into dev 2022-10-18 16:28:20 +03:00
hedger
4942bd2105 scripts: fixed c2 bundle format (#1889)
* scripts: fixed c2 bundle format
* scripts: copro.py: small refactoring

Co-authored-by: あく <alleteam@gmail.com>
2022-10-18 21:13:28 +09:00
94 changed files with 5140 additions and 312 deletions

View File

@@ -1,6 +1,14 @@
### New changes
* PR -> Power: remove % sign from desktop and center numbers (by @TQMatvey | PR #115)
* Plugins: Heap Defence Game (aka Stack Attack) [(original by wquinoa & Vedmein)](https://github.com/Vedmein/flipperzero-firmware/tree/hd/svisto-perdelki) -> Ported to latest firmware by @xMasterX
* Plugins: SubGHz Bruteforcer - added Chamberlain 7bit and 8bit
### Previous changes
* PR -> NFC: New mifare classic keys (by @ankris812 | PR #119)
* New API version 3.2 -> 4.1 (due to breaking changes in subghz part) (old apps needs to be recompiled, update for extra apps pack is coming soon)
* OFW PR: WeatherStation plugin and SubGHz changes (OFW PR 1833 by Skorpionm)
* OFW: Allow pins 0 and 1 as RTS/DTR for USB UART Bridge
* OFW: Picopass: Read Elite
* OFW: SubGhz: CAME Wrong number of bits in key (add protocol Airforce)
* OFW: Forced RAW receive option for Infrared CLI
* OFW: scripts: fixed c2 bundle format
#### [🎲 Download extra apps pack](https://download-directory.github.io/?url=https://github.com/UberGuidoZ/Flipper/tree/main/Applications/Unleashed)

View File

@@ -5,7 +5,7 @@
#include <lib/subghz/transmitter.h>
#include <lib/subghz/subghz_keystore.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>
#define TAG "SubGhz TEST"
@@ -43,6 +43,8 @@ static void subghz_test_init(void) {
environment_handler, CAME_ATOMO_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_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);
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");
}
MU_TEST(subghz_decoder_magellen_test) {
MU_TEST(subghz_decoder_magellan_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME),
"Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME),
"Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
}
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");
}
MU_TEST(subghz_encoder_magellen_test) {
MU_TEST(subghz_encoder_magellan_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")),
"Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")),
"Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
}
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_phoenix_v2_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_clemsa_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_phoenix_v2_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_clemsa_test);

View File

@@ -24,6 +24,7 @@ struct GpioApp {
Widget* widget;
VariableItemList* var_item_list;
VariableItem* var_item_flow;
GpioTest* gpio_test;
GpioUsbUart* gpio_usb_uart;
UsbUartBridge* usb_uart_bridge;

View File

@@ -13,7 +13,7 @@ static UsbUartConfig* cfg_set;
static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"13,14", "15,16"};
static const char* flow_pins[] = {"None", "2,3", "6,7"};
static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
static const char* baudrate_mode[] = {"Host"};
static const uint32_t baudrate_list[] = {
2400,
@@ -33,6 +33,24 @@ bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
return false;
}
void line_ensure_flow_invariant(GpioApp* app) {
// GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is
// selected. This function enforces that invariant by resetting flow_pins
// to None if it is configured to 16,15 when LPUART is selected.
uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
VariableItem* item = app->var_item_flow;
variable_item_set_values_count(item, available_flow_pins);
if(cfg_set->flow_pins >= available_flow_pins) {
cfg_set->flow_pins = 0;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
variable_item_set_current_value_index(item, cfg_set->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
}
}
static void line_vcp_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
@@ -54,6 +72,7 @@ static void line_port_cb(VariableItem* item) {
else if(index == 1)
cfg_set->uart_ch = FuriHalUartIdLPUART1;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
line_ensure_flow_invariant(app);
}
static void line_flow_cb(VariableItem* item) {
@@ -116,9 +135,12 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
variable_item_set_current_value_index(item, cfg_set->uart_ch);
variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]);
item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app);
item = variable_item_list_add(
var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
variable_item_set_current_value_index(item, cfg_set->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
app->var_item_flow = item;
line_ensure_flow_invariant(app);
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));

View File

@@ -14,6 +14,7 @@
static const GpioPin* flow_pins[][2] = {
{&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3
{&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7
{&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15
};
typedef enum {

View File

@@ -71,25 +71,9 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv
}
}
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
UNUSED(cli);
UNUSED(args);
InfraredWorker* worker = infrared_worker_alloc();
infrared_worker_rx_start(worker);
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n");
while(!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(50);
}
infrared_worker_rx_stop(worker);
infrared_worker_free(worker);
}
static void infrared_cli_print_usage(void) {
printf("Usage:\r\n");
printf("\tir rx\r\n");
printf("\tir rx [raw]\r\n");
printf("\tir tx <protocol> <address> <command>\r\n");
printf("\t<command> and <address> are hex-formatted\r\n");
printf("\tAvailable protocols:");
@@ -108,6 +92,35 @@ static void infrared_cli_print_usage(void) {
printf("\tir universal list <tv, ac>\r\n");
}
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
UNUSED(cli);
bool enable_decoding = true;
if(!furi_string_empty(args)) {
if(!furi_string_cmp_str(args, "raw")) {
enable_decoding = false;
} else {
printf("Wrong arguments.\r\n");
infrared_cli_print_usage();
return;
}
}
InfraredWorker* worker = infrared_worker_alloc();
infrared_worker_rx_enable_signal_decoding(worker, enable_decoding);
infrared_worker_rx_start(worker);
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW");
while(!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(50);
}
infrared_worker_rx_stop(worker);
infrared_worker_free(worker);
}
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
char protocol_name[32];
InfraredMessage message;

View File

@@ -72,18 +72,7 @@ typedef enum {
SubGhzViewIdTestPacket,
} SubGhzViewId;
struct SubGhzPresetDefinition {
FuriString* name;
uint32_t frequency;
uint8_t* data;
size_t data_size;
};
typedef struct SubGhzPresetDefinition SubGhzPresetDefinition;
typedef enum {
SubGhzViewReceiverModeLive,
SubGhzViewReceiverModeFile,
} SubGhzViewReceiverMode;
#define SUBGHZ_HISTORY_REMOVE_SAVED_ITEMS 1

View File

@@ -7,7 +7,7 @@
#include <dolphin/dolphin.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h>
#include <lib/subghz/protocols/registry.h>
#include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubGhzSetType"

View File

@@ -3,6 +3,7 @@
#include <subghz/types.h>
#include <lib/toolbox/path.h>
#include "subghz_i.h"
#include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubGhzApp"
@@ -243,7 +244,8 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
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);
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
subghz_last_settings_set_detect_raw_values(subghz);

View File

@@ -9,6 +9,7 @@
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include <lib/subghz/subghz_file_encoder_worker.h>
#include <lib/subghz/protocols/protocol_items.h>
#include "helpers/subghz_chat.h"
@@ -159,6 +160,7 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));
SubGhzEnvironment* environment = subghz_environment_alloc();
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton");
subghz_transmitter_deserialize(transmitter, flipper_format);
@@ -252,6 +254,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
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);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
@@ -371,6 +374,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
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);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);

View File

@@ -5,7 +5,7 @@
#include <furi.h>
#include <furi_hal.h>
#include <lib/flipper_format/flipper_format.h>
#include "helpers/subghz_types.h"
#include <lib/subghz/types.h>
typedef struct SubGhzHistory SubGhzHistory;

View File

@@ -1,6 +1,7 @@
#pragma once
#include "helpers/subghz_types.h"
#include <lib/subghz/types.h>
#include "subghz.h"
#include "views/receiver.h"
#include "views/transmitter.h"
@@ -12,8 +13,6 @@
#include "views/subghz_test_static.h"
#include "views/subghz_test_packet.h"
#endif
// #include <furi.h>
// #include <furi_hal.h>
#include <gui/gui.h>
#include <dialogs/dialogs.h>
#include <gui/scene_manager.h>
@@ -26,16 +25,13 @@
#include <gui/modules/widget.h>
#include <subghz/scenes/subghz_scene.h>
#include <lib/subghz/subghz_worker.h>
#include <lib/subghz/subghz_file_encoder_worker.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include "subghz_history.h"
#include "subghz_setting.h"
#include "subghz_last_settings.h"
#include <gui/modules/variable_item_list.h>

View File

@@ -217,7 +217,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
} else {
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));
furi_string_reset(str_buff);
}

View File

@@ -15,7 +15,7 @@
#include <applications/main/subghz/subghz_i.h>
#include <lib/subghz/protocols/raw.h>
#include <lib/subghz/protocols/registry.h>
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/types.h>
#include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/star_line.h>
@@ -602,10 +602,12 @@ void unirfremix_tx_stop(UniRFRemix* app) {
subghz_transmitter_stop(app->tx_transmitter);
FURI_LOG_D(TAG, "Checking if protocol is dynamic");
const SubGhzProtocol* registry =
subghz_protocol_registry_get_by_name(furi_string_get_cstr(app->txpreset->protocol));
FURI_LOG_D(TAG, "Protocol-TYPE %d", registry->type);
if(registry && registry->type == SubGhzProtocolTypeDynamic) {
const SubGhzProtocolRegistry* protocol_registry_items =
subghz_environment_get_protocol_registry(app->environment);
const SubGhzProtocol* proto = subghz_protocol_registry_get_by_name(
protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol));
FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type);
if(proto && proto->type == SubGhzProtocolTypeDynamic) {
FURI_LOG_D(TAG, "Protocol is dynamic. Saving key");
unirfremix_save_protocol_to_file(app->tx_fff_data, app->tx_file_path);
@@ -838,6 +840,7 @@ void unirfremix_subghz_alloc(UniRFRemix* app) {
app->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
app->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(app->environment, (void*)&subghz_protocol_registry);
app->subghz_receiver = subghz_receiver_alloc_init(app->environment);
}

View File

@@ -0,0 +1,151 @@
#include "iclass_elite_dict.h"
#include <lib/toolbox/args.h>
#include <lib/flipper_format/flipper_format.h>
#define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt")
#define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt")
#define TAG "IclassEliteDict"
#define ICLASS_ELITE_KEY_LINE_LEN (17)
#define ICLASS_ELITE_KEY_LEN (8)
struct IclassEliteDict {
Stream* stream;
uint32_t total_keys;
};
bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool dict_present = false;
if(dict_type == IclassEliteDictTypeFlipper) {
dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) ==
FSE_OK;
} else if(dict_type == IclassEliteDictTypeUser) {
dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK;
}
furi_record_close(RECORD_STORAGE);
return dict_present;
}
IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) {
IclassEliteDict* dict = malloc(sizeof(IclassEliteDict));
Storage* storage = furi_record_open(RECORD_STORAGE);
dict->stream = buffered_file_stream_alloc(storage);
furi_record_close(RECORD_STORAGE);
FuriString* next_line = furi_string_alloc();
bool dict_loaded = false;
do {
if(dict_type == IclassEliteDictTypeFlipper) {
if(!buffered_file_stream_open(
dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
buffered_file_stream_close(dict->stream);
break;
}
} else if(dict_type == IclassEliteDictTypeUser) {
if(!buffered_file_stream_open(
dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
buffered_file_stream_close(dict->stream);
break;
}
}
// Read total amount of keys
while(true) {
if(!stream_read_line(dict->stream, next_line)) break;
if(furi_string_get_char(next_line, 0) == '#') continue;
if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue;
dict->total_keys++;
}
furi_string_reset(next_line);
stream_rewind(dict->stream);
dict_loaded = true;
FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys);
} while(false);
if(!dict_loaded) {
buffered_file_stream_close(dict->stream);
free(dict);
dict = NULL;
}
furi_string_free(next_line);
return dict;
}
void iclass_elite_dict_free(IclassEliteDict* dict) {
furi_assert(dict);
furi_assert(dict->stream);
buffered_file_stream_close(dict->stream);
stream_free(dict->stream);
free(dict);
}
uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) {
furi_assert(dict);
return dict->total_keys;
}
bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
uint8_t key_byte_tmp = 0;
FuriString* next_line = furi_string_alloc();
bool key_read = false;
*key = 0ULL;
while(!key_read) {
if(!stream_read_line(dict->stream, next_line)) break;
if(furi_string_get_char(next_line, 0) == '#') continue;
if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue;
for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) {
args_char_to_hex(
furi_string_get_char(next_line, i),
furi_string_get_char(next_line, i + 1),
&key_byte_tmp);
key[i / 2] = key_byte_tmp;
}
key_read = true;
}
furi_string_free(next_line);
return key_read;
}
bool iclass_elite_dict_rewind(IclassEliteDict* dict) {
furi_assert(dict);
furi_assert(dict->stream);
return stream_rewind(dict->stream);
}
bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
FuriString* key_str = furi_string_alloc();
for(size_t i = 0; i < 6; i++) {
furi_string_cat_printf(key_str, "%02X", key[i]);
}
furi_string_cat_printf(key_str, "\n");
bool key_added = false;
do {
if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
if(!stream_insert_string(dict->stream, key_str)) break;
key_added = true;
} while(false);
furi_string_free(key_str);
return key_added;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <stdbool.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <lib/toolbox/stream/file_stream.h>
#include <lib/toolbox/stream/buffered_file_stream.h>
typedef enum {
IclassEliteDictTypeUser,
IclassEliteDictTypeFlipper,
} IclassEliteDictType;
typedef struct IclassEliteDict IclassEliteDict;
bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type);
IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type);
void iclass_elite_dict_free(IclassEliteDict* dict);
uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict);
bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key);
bool iclass_elite_dict_rewind(IclassEliteDict* dict);
bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key);

View File

@@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8
* @param loclass_hash1 loclass_hash1
* @param key_sel output key_sel=h[loclass_hash1[i]]
*/
void hash2(uint8_t* key64, uint8_t* outp_keytable) {
void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) {
/**
*Expected:
* High Security Key Table

View File

@@ -9,6 +9,7 @@
#include "rfal_picopass.h"
#include <optimized_ikeys.h>
#include <optimized_cipher.h>
#include "helpers/iclass_elite_dict.h"
#define PICOPASS_DEV_NAME_MAX_LEN 22
#define PICOPASS_READER_DATA_MAX_SIZE 64
@@ -49,6 +50,7 @@ typedef struct {
bool se_enabled;
bool sio;
bool biometrics;
uint8_t key[8];
uint8_t pin_length;
PicopassEncryption encryption;
uint8_t credential[8];

View File

@@ -1,5 +1,7 @@
#include "picopass_worker_i.h"
#include <flipper_format/flipper_format.h>
#define TAG "PicopassWorker"
const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
@@ -176,7 +178,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
return ERR_NONE;
}
ReturnCode picopass_read_card(PicopassBlock* AA1) {
ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes;
@@ -197,10 +199,68 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) {
loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
return err;
if(err == ERR_NONE) {
return ERR_NONE;
}
FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
FURI_LOG_E(TAG, "Starting dictionary attack");
size_t index = 0;
uint8_t key[PICOPASS_BLOCK_LEN] = {0};
if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) {
FURI_LOG_E(TAG, "Dictionary not found");
return ERR_PARAM;
}
IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper);
if(!dict) {
FURI_LOG_E(TAG, "Dictionary not allocated");
return ERR_PARAM;
}
FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict));
while(iclass_elite_dict_get_next_key(dict, key)) {
FURI_LOG_D(
TAG,
"Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x",
index++,
key[0],
key[1],
key[2],
key[3],
key[4],
key[5],
key[6],
key[7]);
err = rfalPicoPassPollerReadCheck(&rcRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
return err;
}
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes);
if(err == ERR_NONE) {
memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
break;
}
}
if(dict) {
iclass_elite_dict_free(dict);
}
return err;
}
ReturnCode picopass_read_card(PicopassBlock* AA1) {
ReturnCode err;
size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ?
AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] :
@@ -352,28 +412,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
if(pacs->se_enabled) {
FURI_LOG_D(TAG, "SE enabled");
nextState = PicopassWorkerEventFail;
}
err = picopass_read_card(AA1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
nextState = PicopassWorkerEventFail;
if(nextState == PicopassWorkerEventSuccess) {
err = picopass_auth(AA1, pacs);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_try_auth error %d", err);
nextState = PicopassWorkerEventFail;
}
}
if(nextState == PicopassWorkerEventSuccess) {
err = picopass_read_card(AA1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
nextState = PicopassWorkerEventFail;
}
}
if(nextState == PicopassWorkerEventSuccess) {
err = picopass_device_parse_credential(AA1, pacs);
}
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
nextState = PicopassWorkerEventFail;
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
nextState = PicopassWorkerEventFail;
}
}
if(nextState == PicopassWorkerEventSuccess) {
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
}
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
nextState = PicopassWorkerEventFail;
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
nextState = PicopassWorkerEventFail;
}
}
// Notify caller and exit

View File

@@ -18,6 +18,7 @@
struct PicopassWorker {
FuriThread* thread;
Storage* storage;
Stream* dict_stream;
PicopassDeviceData* dev_data;
PicopassWorkerCallback callback;

View File

@@ -15,12 +15,10 @@ void picopass_scene_read_card_success_widget_callback(
void picopass_scene_read_card_success_on_enter(void* context) {
Picopass* picopass = context;
FuriString* credential_str;
FuriString* wiegand_str;
FuriString* sio_str;
credential_str = furi_string_alloc();
wiegand_str = furi_string_alloc();
sio_str = furi_string_alloc();
FuriString* csn_str = furi_string_alloc_set("CSN:");
FuriString* credential_str = furi_string_alloc();
FuriString* wiegand_str = furi_string_alloc();
FuriString* sio_str = furi_string_alloc();
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
@@ -28,10 +26,18 @@ void picopass_scene_read_card_success_on_enter(void* context) {
notification_message(picopass->notifications, &sequence_success);
// Setup view
PicopassBlock* AA1 = picopass->dev->dev_data.AA1;
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget;
if(pacs->record.bitLength == 0) {
uint8_t csn[PICOPASS_BLOCK_LEN];
memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, " %02X", csn[i]);
}
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
furi_string_cat_printf(wiegand_str, "Read Failed");
if(pacs->se_enabled) {
@@ -79,18 +85,21 @@ void picopass_scene_read_card_success_on_enter(void* context) {
}
widget_add_string_element(
widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str));
widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str));
widget_add_string_element(
widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str));
widget_add_string_element(
widget,
64,
32,
36,
AlignCenter,
AlignCenter,
FontSecondary,
furi_string_get_cstr(credential_str));
widget_add_string_element(
widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str));
widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str));
furi_string_free(csn_str);
furi_string_free(credential_str);
furi_string_free(wiegand_str);
furi_string_free(sio_str);

View File

@@ -8,6 +8,7 @@
#include <lib/toolbox/path.h>
#include <assets_icons.h>
#include <lib/subghz/protocols/protocol_items.h>
#include <flipper_format/flipper_format_i.h>
#include <applications/main/subghz/subghz_i.h>
@@ -158,6 +159,7 @@ static int playlist_worker_process(
// (try to) send file
SubGhzEnvironment* environment = subghz_environment_alloc();
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzTransmitter* transmitter =
subghz_transmitter_alloc_init(environment, furi_string_get_cstr(protocol));

View File

@@ -3,6 +3,7 @@
#include <toolbox/stream/stream.h>
#include <flipper_format.h>
#include <flipper_format_i.h>
#include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubBruteWorker"
#define SUBBRUTE_TX_TIMEOUT 5
@@ -30,6 +31,8 @@ SubBruteWorker* subbrute_worker_alloc() {
instance->decoder_result = NULL;
instance->transmitter = NULL;
instance->environment = subghz_environment_alloc();
subghz_environment_set_protocol_registry(
instance->environment, (void*)&subghz_protocol_registry);
instance->transmit_mode = false;

View File

@@ -5,6 +5,7 @@
#include <lib/toolbox/stream/stream.h>
#include <lib/flipper_format/flipper_format.h>
#include <lib/flipper_format/flipper_format_i.h>
#include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubBruteDevice"
@@ -18,6 +19,8 @@ SubBruteDevice* subbrute_device_alloc() {
instance->decoder_result = NULL;
instance->receiver = NULL;
instance->environment = subghz_environment_alloc();
subghz_environment_set_protocol_registry(
instance->environment, (void*)&subghz_protocol_registry);
#ifdef FURI_DEBUG
subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit433);

View File

@@ -102,6 +102,72 @@ const SubBruteProtocol subbrute_protocol_chamberlain_9bit_390 = {
.preset = FuriHalSubGhzPresetOok650Async,
.file = ChamberlainFileProtocol};
/**
* Chamberlain 8bit 300MHz
*/
const SubBruteProtocol subbrute_protocol_chamberlain_8bit_300 = {
.frequency = 300000000,
.bits = 8,
.te = 0,
.repeat = 3,
.preset = FuriHalSubGhzPresetOok650Async,
.file = ChamberlainFileProtocol};
/**
* Chamberlain 8bit 315MHz
*/
const SubBruteProtocol subbrute_protocol_chamberlain_8bit_315 = {
.frequency = 315000000,
.bits = 8,
.te = 0,
.repeat = 3,
.preset = FuriHalSubGhzPresetOok650Async,
.file = ChamberlainFileProtocol};
/**
* Chamberlain 8bit 390MHz
*/
const SubBruteProtocol subbrute_protocol_chamberlain_8bit_390 = {
.frequency = 390000000,
.bits = 8,
.te = 0,
.repeat = 3,
.preset = FuriHalSubGhzPresetOok650Async,
.file = ChamberlainFileProtocol};
/**
* Chamberlain 7bit 300MHz
*/
const SubBruteProtocol subbrute_protocol_chamberlain_7bit_300 = {
.frequency = 300000000,
.bits = 7,
.te = 0,
.repeat = 3,
.preset = FuriHalSubGhzPresetOok650Async,
.file = ChamberlainFileProtocol};
/**
* Chamberlain 7bit 315MHz
*/
const SubBruteProtocol subbrute_protocol_chamberlain_7bit_315 = {
.frequency = 315000000,
.bits = 7,
.te = 0,
.repeat = 3,
.preset = FuriHalSubGhzPresetOok650Async,
.file = ChamberlainFileProtocol};
/**
* Chamberlain 7bit 390MHz
*/
const SubBruteProtocol subbrute_protocol_chamberlain_7bit_390 = {
.frequency = 390000000,
.bits = 7,
.te = 0,
.repeat = 3,
.preset = FuriHalSubGhzPresetOok650Async,
.file = ChamberlainFileProtocol};
/**
* Linear 10bit 300MHz
*/
@@ -140,6 +206,12 @@ static const char* subbrute_protocol_names[] = {
[SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300MHz",
[SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315MHz",
[SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390MHz",
[SubBruteAttackChamberlain8bit300] = "Chamberlain 8bit 300MHz",
[SubBruteAttackChamberlain8bit315] = "Chamberlain 8bit 315MHz",
[SubBruteAttackChamberlain8bit390] = "Chamberlain 8bit 390MHz",
[SubBruteAttackChamberlain7bit300] = "Chamberlain 7bit 300MHz",
[SubBruteAttackChamberlain7bit315] = "Chamberlain 7bit 315MHz",
[SubBruteAttackChamberlain7bit390] = "Chamberlain 7bit 390MHz",
[SubBruteAttackLinear10bit300] = "Linear 10bit 300MHz",
[SubBruteAttackLinear10bit310] = "Linear 10bit 310MHz",
[SubBruteAttackLoadFile] = "BF existing dump",
@@ -166,6 +238,12 @@ const SubBruteProtocol* subbrute_protocol_registry[] = {
[SubBruteAttackChamberlain9bit300] = &subbrute_protocol_chamberlain_9bit_300,
[SubBruteAttackChamberlain9bit315] = &subbrute_protocol_chamberlain_9bit_315,
[SubBruteAttackChamberlain9bit390] = &subbrute_protocol_chamberlain_9bit_390,
[SubBruteAttackChamberlain8bit300] = &subbrute_protocol_chamberlain_8bit_300,
[SubBruteAttackChamberlain8bit315] = &subbrute_protocol_chamberlain_8bit_315,
[SubBruteAttackChamberlain8bit390] = &subbrute_protocol_chamberlain_8bit_390,
[SubBruteAttackChamberlain7bit300] = &subbrute_protocol_chamberlain_7bit_300,
[SubBruteAttackChamberlain7bit315] = &subbrute_protocol_chamberlain_7bit_315,
[SubBruteAttackChamberlain7bit390] = &subbrute_protocol_chamberlain_7bit_390,
[SubBruteAttackLinear10bit300] = &subbrute_protocol_linear_10bit_300,
[SubBruteAttackLinear10bit310] = &subbrute_protocol_linear_10bit_310,
[SubBruteAttackLoadFile] = &subbrute_protocol_load_file};

View File

@@ -34,6 +34,12 @@ typedef enum {
SubBruteAttackChamberlain9bit300,
SubBruteAttackChamberlain9bit315,
SubBruteAttackChamberlain9bit390,
SubBruteAttackChamberlain8bit300,
SubBruteAttackChamberlain8bit315,
SubBruteAttackChamberlain8bit390,
SubBruteAttackChamberlain7bit300,
SubBruteAttackChamberlain7bit315,
SubBruteAttackChamberlain7bit390,
SubBruteAttackLinear10bit300,
SubBruteAttackLinear10bit310,
SubBruteAttackLoadFile,

View 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",
)

View File

@@ -0,0 +1,14 @@
#pragma once
typedef enum {
//WSCustomEvent
WSCustomEventStartId = 100,
WSCustomEventSceneSettingLock,
WSCustomEventViewReceiverOK,
WSCustomEventViewReceiverConfig,
WSCustomEventViewReceiverBack,
WSCustomEventViewReceiverOffDisplay,
WSCustomEventViewReceiverUnlock,
} WSCustomEvent;

View File

@@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View 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,
SubGhzPresetDefinition* 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);
}

View 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, SubGhzPresetDefinition
* @return true On success
*/
bool ws_protocol_decoder_gt_wt_03_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* 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);

View 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,
SubGhzPresetDefinition* 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);
}

View 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, SubGhzPresetDefinition
* @return true On success
*/
bool ws_protocol_decoder_infactory_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* 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);

View 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,
SubGhzPresetDefinition* 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);
}

View 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, SubGhzPresetDefinition
* @return true On success
*/
bool ws_protocol_decoder_nexus_th_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* 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);

View File

@@ -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)};

View File

@@ -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;

View 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,
SubGhzPresetDefinition* 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);
}

View 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_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, SubGhzPresetDefinition
* @return true On success
*/
bool ws_protocol_decoder_thermopro_tx4_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* 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);

View 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,
SubGhzPresetDefinition* 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;
}

View 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, SubGhzPresetDefinition
* @return true On success
*/
bool ws_block_generic_serialize(
WSBlockGeneric* instance,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* 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

View File

@@ -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);
}

View File

@@ -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,
};

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

View 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"), true);
//init Worker & Protocol & History
app->lock = WSLockOff;
app->txrx = malloc(sizeof(WeatherStationTxRx));
app->txrx->preset = malloc(sizeof(SubGhzPresetDefinition));
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;
}

View 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);
}
}

View 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;
SubGhzPresetDefinition* 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);

View File

@@ -0,0 +1,247 @@
#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>
#include <m-string.h>
#define WS_HISTORY_MAX 50
#define TAG "WSHistory"
typedef struct {
FuriString* item_str;
FlipperFormat* flipper_string;
uint8_t type;
uint32_t id;
SubGhzPresetDefinition* 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;
}
SubGhzPresetDefinition* ws_history_get_preset_def(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, SubGhzPresetDefinition* 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(SubGhzPresetDefinition));
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;
}

View 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);
SubGhzPresetDefinition* ws_history_get_preset_def(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 - SubGhzPresetDefinition preset
* @return WSHistoryStateAddKey;
*/
WSHistoryStateAddKey
ws_history_add_to_history(WSHistory* instance, void* context, SubGhzPresetDefinition* 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);

View File

@@ -396,6 +396,10 @@ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_v
item->current_value_index = current_value_index;
}
void variable_item_set_values_count(VariableItem* item, uint8_t values_count) {
item->values_count = values_count;
}
void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) {
furi_string_set(item->current_value_text, current_value_text);
}

View File

@@ -81,6 +81,13 @@ uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_it
*/
void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index);
/** Set number of values for item
*
* @param item VariableItem* instance
* @param values_count The new values count
*/
void variable_item_set_values_count(VariableItem* item, uint8_t values_count);
/** Set item current selected text
*
* @param item VariableItem* instance

View File

@@ -1,7 +1,7 @@
###########################
# Do not edit, this file will be overwritten after firmware update
# Use the user_dict file for user keys
# Last update 13th September, 2022
# Last update 19th October, 2022
# -------------------------
# MIFARE DEFAULT KEYS
# -- ICEMAN FORK VERSION --
@@ -12,7 +12,7 @@ FFFFFFFFFFFF
# BLANKKEY
000000000000
# NFC FORUM MADKEY
# MAD ACCESS KEY A (REVERSED)
# MAD ACCESS KEY A (REVERSED)
A5A4A3A2A1A0
# MAD ACCESS KEY B
89ECA97F8C2A
@@ -114,7 +114,7 @@ CCCCCCCCCCCC
DDDDDDDDDDDD
EEEEEEEEEEEE
# ELEVATOR
# DATA FROM FORUM
# DATA FROM FORUM
FFFFFF545846
F1A97341A9FC
# HOTEL SYSTEM
@@ -1314,7 +1314,7 @@ B5ADEFCA46C4
BF3FE47637EC
B290401B0CAD
AD11006B0601
# ARMENIAN METRO
# ARMENIAN METRO
E4410EF8ED2D
6A68A7D83E11
0D6057E8133B
@@ -1338,7 +1338,7 @@ FED791829013
29A791829013
668091829013
00008627C10A
# KEYS FROM NSP MANCHESTER UNIVERSITY UK ACCOMODATION STAFF AND STUDENTS
# KEYS FROM NSP MANCHESTER UNIVERSITY UK ACCOMODATION STAFF AND STUDENTS
199404281970
199404281998
# EASYCARD
@@ -3721,3 +3721,55 @@ BC305FE2DA65
CF0EC6ACF2F9
F7A545095C49
6862FD600F78
# RENFE MADRID (TRAIN) Extracted with detect reader
701AA491A4A5
12BA20088ED3
# MISC KEYS FROM MY OLD ACCESS CARDS
F18D91EE3033
0E726E11CFCC
1D14130D1A0B
4ADLE273EAFL
3DFL4C8000A1
E64Q986Q5D94
201106141030
D144BD193063
01E2C14F1B18
0380293A9E6D
08A55BC96DC1
08ED3F92AA53
0D61BA88789C
190E6242CE7B
1A8CFF2F1BC7
25FE2B4E4FA9
321803E38664
381F84DB8134
38540EEE8B1C
42F82DB5C4AF
507A6181E4BF
549BB4FD70C4
57059FFD3EE6
5E696FA0EAD1
6036F9D72D68
6611DFFAAE32
6AC79644E0CD
735DD20237A9
79E8B59A51E0
7B6C00CBAC92
7C3AF198425F
81C0BBCE32E9
8D2B780A148D
9001D0E23F8C
A8700E07A58F
AE683AC2A232
B6728D9B95BA
C290A397F84A
CA22AF33A19B
CBC83E1548B4
CC5F59A0CE0A
D410EFF9113E
DE5865F29C44
E108EA397A9A
E10F0E7A8DD5
F833E24C3F1C
FA8CA10C7D59
FE98F38F3EE2

View File

@@ -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
Version: 1
@@ -55,7 +56,6 @@ Frequency: 928000000
# Frequencies used for hopping mode (keep this list small or flipper will miss signal) - they added after default ones if enabled in Add_standard_frequencies
Hopper_frequency: 310000000
Hopper_frequency: 315000000
Hopper_frequency: 313000000
Hopper_frequency: 318000000
Hopper_frequency: 390000000
Hopper_frequency: 433920000

View File

@@ -2,6 +2,6 @@ Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: Magellen
Protocol: Magellan
Bit: 32
Key: 00 00 00 00 37 AE 48 28

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,3.2,,
Version,+,4.1,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
@@ -128,10 +128,17 @@ Header,+,lib/one_wire/one_wire_host.h,,
Header,+,lib/one_wire/one_wire_host_timing.h,,
Header,+,lib/one_wire/one_wire_slave.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/protocols/protocol_items.h,,
Header,+,lib/subghz/protocols/raw.h,,
Header,+,lib/subghz/protocols/registry.h,,
Header,+,lib/subghz/receiver.h,,
Header,+,lib/subghz/registry.h,,
Header,+,lib/subghz/subghz_setting.h,,
Header,+,lib/subghz/subghz_tx_rx_worker.h,,
Header,+,lib/subghz/subghz_worker.h,,
Header,+,lib/subghz/transmitter.h,,
@@ -949,6 +956,7 @@ Function,+,furi_hal_bt_nvm_sram_sem_release,void,
Function,+,furi_hal_bt_reinit,void,
Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void,
Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*"
Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus
Function,+,furi_hal_bt_serial_start,void,
Function,+,furi_hal_bt_serial_stop,void,
Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t"
@@ -2120,6 +2128,7 @@ Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*"
Function,-,serial_svc_is_started,_Bool,
Function,-,serial_svc_notify_buffer_is_empty,void,
Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*"
Function,+,serial_svc_set_rpc_status,void,SerialServiceRpcStatus
Function,-,serial_svc_start,void,
Function,-,serial_svc_stop,void,
Function,-,serial_svc_update_tx,_Bool,"uint8_t*, uint16_t"
@@ -2289,14 +2298,20 @@ Function,-,strupr,char*,char*
Function,-,strverscmp,int,"const char*, const char*"
Function,-,strxfrm,size_t,"char*, const char*, size_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*, SubGhzPresetDefinition*"
Function,+,subghz_environment_alloc,SubGhzEnvironment*,
Function,+,subghz_environment_free,void,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_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_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_protocol_registry,void,"SubGhzEnvironment*, void*"
Function,-,subghz_keystore_alloc,SubGhzKeystore*,
Function,-,subghz_keystore_free,void,SubGhzKeystore*
Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore*
@@ -2304,8 +2319,18 @@ 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_get_data,_Bool,"const char*, size_t, uint8_t*, size_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_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_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzPresetDefinition*"
Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*"
@@ -2445,14 +2470,14 @@ Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void*
Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*"
Function,-,subghz_protocol_decoder_linear_reset,void,void*
Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*"
Function,-,subghz_protocol_decoder_magellen_alloc,void*,SubGhzEnvironment*
Function,-,subghz_protocol_decoder_magellen_deserialize,_Bool,"void*, FlipperFormat*"
Function,-,subghz_protocol_decoder_magellen_feed,void,"void*, _Bool, uint32_t"
Function,-,subghz_protocol_decoder_magellen_free,void,void*
Function,-,subghz_protocol_decoder_magellen_get_hash_data,uint8_t,void*
Function,-,subghz_protocol_decoder_magellen_get_string,void,"void*, FuriString*"
Function,-,subghz_protocol_decoder_magellen_reset,void,void*
Function,-,subghz_protocol_decoder_magellen_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*"
Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment*
Function,-,subghz_protocol_decoder_magellan_deserialize,_Bool,"void*, FlipperFormat*"
Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t"
Function,-,subghz_protocol_decoder_magellan_free,void,void*
Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void*
Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*"
Function,-,subghz_protocol_decoder_magellan_reset,void,void*
Function,-,subghz_protocol_decoder_magellan_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*"
Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment*
Function,-,subghz_protocol_decoder_marantec_deserialize,_Bool,"void*, FlipperFormat*"
Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t"
@@ -2660,11 +2685,11 @@ Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperForma
Function,-,subghz_protocol_encoder_linear_free,void,void*
Function,-,subghz_protocol_encoder_linear_stop,void,void*
Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void*
Function,-,subghz_protocol_encoder_magellen_alloc,void*,SubGhzEnvironment*
Function,-,subghz_protocol_encoder_magellen_deserialize,_Bool,"void*, FlipperFormat*"
Function,-,subghz_protocol_encoder_magellen_free,void,void*
Function,-,subghz_protocol_encoder_magellen_stop,void,void*
Function,-,subghz_protocol_encoder_magellen_yield,LevelDuration,void*
Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment*
Function,-,subghz_protocol_encoder_magellan_deserialize,_Bool,"void*, FlipperFormat*"
Function,-,subghz_protocol_encoder_magellan_free,void,void*
Function,-,subghz_protocol_encoder_magellan_stop,void,void*
Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void*
Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment*
Function,-,subghz_protocol_encoder_marantec_deserialize,_Bool,"void*, FlipperFormat*"
Function,-,subghz_protocol_encoder_marantec_free,void,void*
@@ -2739,9 +2764,9 @@ 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_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzPresetDefinition*"
Function,-,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW*
Function,-,subghz_protocol_registry_count,size_t,
Function,-,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,size_t
Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,const char*
Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry*
Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t"
Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*"
Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t
Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzPresetDefinition*"
Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzPresetDefinition*"
@@ -2753,6 +2778,24 @@ Function,+,subghz_receiver_reset,void,SubGhzReceiver*
Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*"
Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag"
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*, _Bool"
Function,+,subghz_setting_load_custom_preset,_Bool,"SubGhzSetting*, const char*, FlipperFormat*"
Function,+,subghz_setting_set_default_frequency,void,"SubGhzSetting*, uint32_t"
Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*"
Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*"
Function,+,subghz_transmitter_free,void,SubGhzTransmitter*
@@ -3999,6 +4042,7 @@ Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, Variab
Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t"
Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t"
Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*"
Function,-,variable_item_set_values_count,void,"VariableItem*, uint8_t"
Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list"
Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list"
Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list"
@@ -4653,9 +4697,9 @@ Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder,
Variable,-,subghz_protocol_linear,const SubGhzProtocol,
Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder,
Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder,
Variable,-,subghz_protocol_magellen,const SubGhzProtocol,
Variable,-,subghz_protocol_magellen_decoder,const SubGhzProtocolDecoder,
Variable,-,subghz_protocol_magellen_encoder,const SubGhzProtocolEncoder,
Variable,-,subghz_protocol_magellan,const SubGhzProtocol,
Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder,
Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder,
Variable,-,subghz_protocol_marantec,const SubGhzProtocol,
Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder,
Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder,
@@ -4687,6 +4731,7 @@ Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder,
Variable,-,subghz_protocol_raw,const SubGhzProtocol,
Variable,-,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder,
Variable,-,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder,
Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry,
Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol,
Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder,
Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder,
1 entry status name type params
2 Version + 3.2 4.1
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
128 Header + lib/one_wire/one_wire_host_timing.h
129 Header + lib/one_wire/one_wire_slave.h
130 Header + lib/print/wrappers.h
131 Header + lib/subghz/blocks/const.h
132 Header + lib/subghz/blocks/decoder.h
133 Header + lib/subghz/blocks/encoder.h
134 Header + lib/subghz/blocks/generic.h
135 Header + lib/subghz/blocks/math.h
136 Header + lib/subghz/environment.h
137 Header + lib/subghz/protocols/protocol_items.h
138 Header + lib/subghz/protocols/raw.h
Header + lib/subghz/protocols/registry.h
139 Header + lib/subghz/receiver.h
140 Header + lib/subghz/registry.h
141 Header + lib/subghz/subghz_setting.h
142 Header + lib/subghz/subghz_tx_rx_worker.h
143 Header + lib/subghz/subghz_worker.h
144 Header + lib/subghz/transmitter.h
956 Function + furi_hal_bt_reinit void
957 Function + furi_hal_bt_serial_notify_buffer_is_empty void
958 Function + furi_hal_bt_serial_set_event_callback void uint16_t, FuriHalBtSerialCallback, void*
959 Function + furi_hal_bt_serial_set_rpc_status void FuriHalBtSerialRpcStatus
960 Function + furi_hal_bt_serial_start void
961 Function + furi_hal_bt_serial_stop void
962 Function + furi_hal_bt_serial_tx _Bool uint8_t*, uint16_t
2128 Function - serial_svc_is_started _Bool
2129 Function - serial_svc_notify_buffer_is_empty void
2130 Function - serial_svc_set_callbacks void uint16_t, SerialServiceEventCallback, void*
2131 Function + serial_svc_set_rpc_status void SerialServiceRpcStatus
2132 Function - serial_svc_start void
2133 Function - serial_svc_stop void
2134 Function - serial_svc_update_tx _Bool uint8_t*, uint16_t
2298 Function - strverscmp int const char*, const char*
2299 Function - strxfrm size_t char*, const char*, size_t
2300 Function - strxfrm_l size_t char*, const char*, size_t, locale_t
2301 Function + subghz_block_generic_deserialize _Bool SubGhzBlockGeneric*, FlipperFormat*
2302 Function + subghz_block_generic_get_preset_name void const char*, FuriString*
2303 Function + subghz_block_generic_serialize _Bool SubGhzBlockGeneric*, FlipperFormat*, SubGhzPresetDefinition*
2304 Function + subghz_environment_alloc SubGhzEnvironment*
2305 Function + subghz_environment_free void SubGhzEnvironment*
2306 Function - subghz_environment_get_came_atomo_rainbow_table_file_name const char* SubGhzEnvironment*
2307 Function - subghz_environment_get_keystore SubGhzKeystore* SubGhzEnvironment*
2308 Function - subghz_environment_get_nice_flor_s_rainbow_table_file_name const char* SubGhzEnvironment*
2309 Function + subghz_environment_get_protocol_name_registry const char* SubGhzEnvironment*, size_t
2310 Function + subghz_environment_get_protocol_registry void* SubGhzEnvironment*
2311 Function + subghz_environment_load_keystore _Bool SubGhzEnvironment*, const char*
2312 Function - subghz_environment_set_came_atomo_rainbow_table_file_name void SubGhzEnvironment*, const char*
2313 Function - subghz_environment_set_nice_flor_s_rainbow_table_file_name void SubGhzEnvironment*, const char*
2314 Function + subghz_environment_set_protocol_registry void SubGhzEnvironment*, void*
2315 Function - subghz_keystore_alloc SubGhzKeystore*
2316 Function - subghz_keystore_free void SubGhzKeystore*
2317 Function - subghz_keystore_get_data SubGhzKeyArray_t* SubGhzKeystore*
2319 Function - subghz_keystore_raw_encrypted_save _Bool const char*, const char*, uint8_t*
2320 Function - subghz_keystore_raw_get_data _Bool const char*, size_t, uint8_t*, size_t
2321 Function - subghz_keystore_save _Bool SubGhzKeystore*, const char*, uint8_t*
2322 Function + subghz_protocol_blocks_add_bit void SubGhzBlockDecoder*, uint8_t
2323 Function + subghz_protocol_blocks_crc4 uint8_t const uint8_t[], unsigned, uint8_t, uint8_t
2324 Function + subghz_protocol_blocks_crc7 uint8_t const uint8_t[], unsigned, uint8_t, uint8_t
2325 Function + subghz_protocol_blocks_crc8 uint8_t const uint8_t[], unsigned, uint8_t, uint8_t
2326 Function + subghz_protocol_blocks_get_bit_array _Bool uint8_t[], size_t
2327 Function + subghz_protocol_blocks_get_hash_data uint8_t SubGhzBlockDecoder*, size_t
2328 Function + subghz_protocol_blocks_get_parity uint8_t uint64_t, uint8_t
2329 Function + subghz_protocol_blocks_get_upload size_t uint8_t[], size_t, LevelDuration*, size_t, uint32_t
2330 Function + subghz_protocol_blocks_reverse_key uint64_t uint64_t, uint8_t
2331 Function + subghz_protocol_blocks_set_bit_array void _Bool, uint8_t[], size_t, size_t
2332 Function - subghz_protocol_decoder_base_deserialize _Bool SubGhzProtocolDecoderBase*, FlipperFormat*
2333 Function - + subghz_protocol_decoder_base_get_hash_data uint8_t SubGhzProtocolDecoderBase*
2334 Function - subghz_protocol_decoder_base_get_string _Bool SubGhzProtocolDecoderBase*, FuriString*
2335 Function + subghz_protocol_decoder_base_serialize _Bool SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzPresetDefinition*
2336 Function - subghz_protocol_decoder_base_set_decoder_callback void SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*
2470 Function - subghz_protocol_decoder_linear_get_string void void*, FuriString*
2471 Function - subghz_protocol_decoder_linear_reset void void*
2472 Function - subghz_protocol_decoder_linear_serialize _Bool void*, FlipperFormat*, SubGhzPresetDefinition*
2473 Function - subghz_protocol_decoder_magellen_alloc subghz_protocol_decoder_magellan_alloc void* SubGhzEnvironment*
2474 Function - subghz_protocol_decoder_magellen_deserialize subghz_protocol_decoder_magellan_deserialize _Bool void*, FlipperFormat*
2475 Function - subghz_protocol_decoder_magellen_feed subghz_protocol_decoder_magellan_feed void void*, _Bool, uint32_t
2476 Function - subghz_protocol_decoder_magellen_free subghz_protocol_decoder_magellan_free void void*
2477 Function - subghz_protocol_decoder_magellen_get_hash_data subghz_protocol_decoder_magellan_get_hash_data uint8_t void*
2478 Function - subghz_protocol_decoder_magellen_get_string subghz_protocol_decoder_magellan_get_string void void*, FuriString*
2479 Function - subghz_protocol_decoder_magellen_reset subghz_protocol_decoder_magellan_reset void void*
2480 Function - subghz_protocol_decoder_magellen_serialize subghz_protocol_decoder_magellan_serialize _Bool void*, FlipperFormat*, SubGhzPresetDefinition*
2481 Function - subghz_protocol_decoder_marantec_alloc void* SubGhzEnvironment*
2482 Function - subghz_protocol_decoder_marantec_deserialize _Bool void*, FlipperFormat*
2483 Function - subghz_protocol_decoder_marantec_feed void void*, _Bool, uint32_t
2685 Function - subghz_protocol_encoder_linear_free void void*
2686 Function - subghz_protocol_encoder_linear_stop void void*
2687 Function - subghz_protocol_encoder_linear_yield LevelDuration void*
2688 Function - subghz_protocol_encoder_magellen_alloc subghz_protocol_encoder_magellan_alloc void* SubGhzEnvironment*
2689 Function - subghz_protocol_encoder_magellen_deserialize subghz_protocol_encoder_magellan_deserialize _Bool void*, FlipperFormat*
2690 Function - subghz_protocol_encoder_magellen_free subghz_protocol_encoder_magellan_free void void*
2691 Function - subghz_protocol_encoder_magellen_stop subghz_protocol_encoder_magellan_stop void void*
2692 Function - subghz_protocol_encoder_magellen_yield subghz_protocol_encoder_magellan_yield LevelDuration void*
2693 Function - subghz_protocol_encoder_marantec_alloc void* SubGhzEnvironment*
2694 Function - subghz_protocol_encoder_marantec_deserialize _Bool void*, FlipperFormat*
2695 Function - subghz_protocol_encoder_marantec_free void void*
2764 Function - subghz_protocol_raw_get_sample_write size_t SubGhzProtocolDecoderRAW*
2765 Function - subghz_protocol_raw_save_to_file_init _Bool SubGhzProtocolDecoderRAW*, const char*, SubGhzPresetDefinition*
2766 Function - subghz_protocol_raw_save_to_file_stop void SubGhzProtocolDecoderRAW*
2767 Function - + subghz_protocol_registry_count size_t const SubGhzProtocolRegistry*
2768 Function - + subghz_protocol_registry_get_by_index const SubGhzProtocol* size_t const SubGhzProtocolRegistry*, size_t
2769 Function + subghz_protocol_registry_get_by_name const SubGhzProtocol* const char* const SubGhzProtocolRegistry*, const char*
2770 Function - subghz_protocol_secplus_v1_check_fixed _Bool uint32_t
2771 Function - subghz_protocol_secplus_v2_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzPresetDefinition*
2772 Function - subghz_protocol_star_line_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzPresetDefinition*
2778 Function + subghz_receiver_search_decoder_base_by_name SubGhzProtocolDecoderBase* SubGhzReceiver*, const char*
2779 Function + subghz_receiver_set_filter void SubGhzReceiver*, SubGhzProtocolFlag
2780 Function + subghz_receiver_set_rx_callback void SubGhzReceiver*, SubGhzReceiverCallback, void*
2781 Function + subghz_setting_alloc SubGhzSetting*
2782 Function + subghz_setting_delete_custom_preset _Bool SubGhzSetting*, const char*
2783 Function + subghz_setting_free void SubGhzSetting*
2784 Function + subghz_setting_get_default_frequency uint32_t SubGhzSetting*
2785 Function + subghz_setting_get_frequency uint32_t SubGhzSetting*, size_t
2786 Function + subghz_setting_get_frequency_count size_t SubGhzSetting*
2787 Function + subghz_setting_get_frequency_default_index uint32_t SubGhzSetting*
2788 Function + subghz_setting_get_hopper_frequency uint32_t SubGhzSetting*, size_t
2789 Function + subghz_setting_get_hopper_frequency_count size_t SubGhzSetting*
2790 Function + subghz_setting_get_inx_preset_by_name int SubGhzSetting*, const char*
2791 Function + subghz_setting_get_preset_count size_t SubGhzSetting*
2792 Function + subghz_setting_get_preset_data uint8_t* SubGhzSetting*, size_t
2793 Function + subghz_setting_get_preset_data_by_name uint8_t* SubGhzSetting*, const char*
2794 Function + subghz_setting_get_preset_data_size size_t SubGhzSetting*, size_t
2795 Function + subghz_setting_get_preset_name const char* SubGhzSetting*, size_t
2796 Function + subghz_setting_load void SubGhzSetting*, const char*, _Bool
2797 Function + subghz_setting_load_custom_preset _Bool SubGhzSetting*, const char*, FlipperFormat*
2798 Function + subghz_setting_set_default_frequency void SubGhzSetting*, uint32_t
2799 Function + subghz_transmitter_alloc_init SubGhzTransmitter* SubGhzEnvironment*, const char*
2800 Function + subghz_transmitter_deserialize _Bool SubGhzTransmitter*, FlipperFormat*
2801 Function + subghz_transmitter_free void SubGhzTransmitter*
4042 Function + variable_item_list_set_selected_item void VariableItemList*, uint8_t
4043 Function + variable_item_set_current_value_index void VariableItem*, uint8_t
4044 Function + variable_item_set_current_value_text void VariableItem*, const char*
4045 Function - variable_item_set_values_count void VariableItem*, uint8_t
4046 Function - vasiprintf int char**, const char*, __gnuc_va_list
4047 Function - vasniprintf char* char*, size_t*, const char*, __gnuc_va_list
4048 Function - vasnprintf char* char*, size_t*, const char*, __gnuc_va_list
4697 Variable - subghz_protocol_linear const SubGhzProtocol
4698 Variable - subghz_protocol_linear_decoder const SubGhzProtocolDecoder
4699 Variable - subghz_protocol_linear_encoder const SubGhzProtocolEncoder
4700 Variable - subghz_protocol_magellen subghz_protocol_magellan const SubGhzProtocol
4701 Variable - subghz_protocol_magellen_decoder subghz_protocol_magellan_decoder const SubGhzProtocolDecoder
4702 Variable - subghz_protocol_magellen_encoder subghz_protocol_magellan_encoder const SubGhzProtocolEncoder
4703 Variable - subghz_protocol_marantec const SubGhzProtocol
4704 Variable - subghz_protocol_marantec_decoder const SubGhzProtocolDecoder
4705 Variable - subghz_protocol_marantec_encoder const SubGhzProtocolEncoder
4731 Variable - subghz_protocol_raw const SubGhzProtocol
4732 Variable - subghz_protocol_raw_decoder const SubGhzProtocolDecoder
4733 Variable - subghz_protocol_raw_encoder const SubGhzProtocolEncoder
4734 Variable + subghz_protocol_registry const SubGhzProtocolRegistry
4735 Variable - subghz_protocol_scher_khan const SubGhzProtocol
4736 Variable - subghz_protocol_scher_khan_decoder const SubGhzProtocolDecoder
4737 Variable - subghz_protocol_scher_khan_encoder const SubGhzProtocolEncoder

View File

@@ -57,6 +57,7 @@ struct InfraredWorker {
InfraredDecoderHandler* infrared_decoder;
NotificationApp* notification;
bool blink_enable;
bool decode_enable;
union {
struct {
@@ -131,7 +132,8 @@ static void infrared_worker_process_timeout(InfraredWorker* instance) {
static void
infrared_worker_process_timings(InfraredWorker* instance, uint32_t duration, bool level) {
const InfraredMessage* message_decoded =
infrared_decode(instance->infrared_decoder, level, duration);
instance->decode_enable ? infrared_decode(instance->infrared_decoder, level, duration) :
NULL;
if(message_decoded) {
instance->signal.message = *message_decoded;
instance->signal.timings_cnt = 0;
@@ -233,6 +235,7 @@ InfraredWorker* infrared_worker_alloc() {
instance->infrared_decoder = infrared_alloc_decoder();
instance->infrared_encoder = infrared_alloc_encoder();
instance->blink_enable = false;
instance->decode_enable = true;
instance->notification = furi_record_open(RECORD_NOTIFICATION);
instance->state = InfraredWorkerStateIdle;
@@ -316,6 +319,11 @@ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool
instance->blink_enable = enable;
}
void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable) {
furi_assert(instance);
instance->decode_enable = enable;
}
void infrared_worker_tx_start(InfraredWorker* instance) {
furi_assert(instance);
furi_assert(instance->state == InfraredWorkerStateIdle);

View File

@@ -76,6 +76,14 @@ void infrared_worker_rx_set_received_signal_callback(
*/
void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable);
/** Enable decoding of received infrared signals.
*
* @param[in] instance - instance of InfraredWorker
* @param[in] enable - true if you want to enable decoding
* false otherwise
*/
void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable);
/** Clarify is received signal either decoded or raw
*
* @param[in] signal - received signal

View File

@@ -10,8 +10,15 @@ env.Append(
File("#/lib/subghz/subghz_worker.h"),
File("#/lib/subghz/subghz_tx_rx_worker.h"),
File("#/lib/subghz/transmitter.h"),
File("#/lib/subghz/protocols/registry.h"),
File("#/lib/subghz/registry.h"),
File("#/lib/subghz/protocols/protocol_items.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"),
],
)

View File

@@ -4,9 +4,17 @@
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
const uint16_t te_long;
const uint16_t te_short;
const uint16_t te_delta;
const uint8_t min_count_bit_for_found;
} SubGhzBlockConst;
#ifdef __cplusplus
}
#endif

View File

@@ -4,6 +4,10 @@
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzBlockDecoder SubGhzBlockDecoder;
struct SubGhzBlockDecoder {
@@ -26,3 +30,7 @@ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit);
* @return hash Hash sum
*/
uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len);
#ifdef __cplusplus
}
#endif

View File

@@ -6,6 +6,10 @@
#include <lib/toolbox/level_duration.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
bool is_running;
size_t repeat;
@@ -50,3 +54,7 @@ size_t subghz_protocol_blocks_get_upload(
LevelDuration* upload,
size_t max_size_upload,
uint32_t duration_bit);
#ifdef __cplusplus
}
#endif

View File

@@ -9,6 +9,10 @@
#include "furi_hal.h"
#include "../types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzBlockGeneric SubGhzBlockGeneric;
struct SubGhzBlockGeneric {
@@ -49,3 +53,7 @@ bool subghz_block_generic_serialize(
* @return true On success
*/
bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format);
#ifdef __cplusplus
}
#endif

View File

@@ -14,4 +14,69 @@ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) {
parity += bit_read(key, i);
}
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;
}

View File

@@ -9,7 +9,11 @@
#define bit_clear(value, bit) ((value) &= ~(1UL << (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 abs(x) ((x) > 0 ? (x) : -(x))
#ifdef __cplusplus
extern "C" {
#endif
/**
* Flip the data bitwise.
* @param key In data
@@ -25,3 +29,51 @@ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit);
* @return parity
*/
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

View File

@@ -1,7 +1,9 @@
#include "environment.h"
#include "registry.h"
struct SubGhzEnvironment {
SubGhzKeystore* keystore;
const SubGhzProtocolRegistry* protocol_registry;
const char* came_atomo_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));
instance->keystore = subghz_keystore_alloc();
instance->protocol_registry = NULL;
instance->came_atomo_rainbow_table_file_name = NULL;
instance->nice_flor_s_rainbow_table_file_name = NULL;
@@ -19,6 +22,7 @@ SubGhzEnvironment* subghz_environment_alloc() {
void subghz_environment_free(SubGhzEnvironment* 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);
@@ -67,3 +71,30 @@ const char*
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;
}
}

View File

@@ -69,6 +69,30 @@ void subghz_environment_set_nice_flor_s_rainbow_table_file_name(
const char*
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
}
#endif

View File

@@ -16,6 +16,8 @@
#define CAME_24_COUNT_BIT 24
#define PRASTEL_COUNT_BIT 25
#define PRASTEL_NAME "Prastel"
#define AIRFORCE_COUNT_BIT 18
#define AIRFORCE_NAME "Airforce"
static const SubGhzBlockConst subghz_protocol_came_const = {
.te_short = 320,
@@ -86,7 +88,7 @@ void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) {
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)
instance->encoder.size_upload = 128;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
@@ -151,10 +153,7 @@ bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flip
FURI_LOG_E(TAG, "Deserialize error");
break;
}
if((instance->generic.data_count_bit !=
subghz_protocol_came_const.min_count_bit_for_found) &&
(instance->generic.data_count_bit != CAME_24_COUNT_BIT) &&
(instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) {
if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
@@ -310,10 +309,7 @@ bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flip
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if((instance->generic.data_count_bit !=
subghz_protocol_came_const.min_count_bit_for_found) &&
(instance->generic.data_count_bit != CAME_24_COUNT_BIT) &&
(instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) {
if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
@@ -338,8 +334,11 @@ void subghz_protocol_decoder_came_get_string(void* context, FuriString* output)
"%s %dbit\r\n"
"Key:0x%08lX\r\n"
"Yek:0x%08lX\r\n",
(instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? PRASTEL_NAME :
instance->generic.protocol_name),
(instance->generic.data_count_bit == PRASTEL_COUNT_BIT ?
PRASTEL_NAME :
(instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ?
AIRFORCE_NAME :
instance->generic.protocol_name)),
instance->generic.data_count_bit,
code_found_lo,
code_found_reverse_lo);

View File

@@ -1,4 +1,4 @@
#include "magellen.h"
#include "magellan.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
@@ -6,16 +6,16 @@
#include "../blocks/generic.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_long = 400,
.te_delta = 100,
.min_count_bit_for_found = 32,
};
struct SubGhzProtocolDecoderMagellen {
struct SubGhzProtocolDecoderMagellan {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
@@ -23,7 +23,7 @@ struct SubGhzProtocolDecoderMagellen {
uint16_t header_count;
};
struct SubGhzProtocolEncoderMagellen {
struct SubGhzProtocolEncoderMagellan {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
@@ -31,50 +31,50 @@ struct SubGhzProtocolEncoderMagellen {
};
typedef enum {
MagellenDecoderStepReset = 0,
MagellenDecoderStepCheckPreambula,
MagellenDecoderStepFoundPreambula,
MagellenDecoderStepSaveDuration,
MagellenDecoderStepCheckDuration,
} MagellenDecoderStep;
MagellanDecoderStepReset = 0,
MagellanDecoderStepCheckPreambula,
MagellanDecoderStepFoundPreambula,
MagellanDecoderStepSaveDuration,
MagellanDecoderStepCheckDuration,
} MagellanDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_magellen_decoder = {
.alloc = subghz_protocol_decoder_magellen_alloc,
.free = subghz_protocol_decoder_magellen_free,
const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = {
.alloc = subghz_protocol_decoder_magellan_alloc,
.free = subghz_protocol_decoder_magellan_free,
.feed = subghz_protocol_decoder_magellen_feed,
.reset = subghz_protocol_decoder_magellen_reset,
.feed = subghz_protocol_decoder_magellan_feed,
.reset = subghz_protocol_decoder_magellan_reset,
.get_hash_data = subghz_protocol_decoder_magellen_get_hash_data,
.serialize = subghz_protocol_decoder_magellen_serialize,
.deserialize = subghz_protocol_decoder_magellen_deserialize,
.get_string = subghz_protocol_decoder_magellen_get_string,
.get_hash_data = subghz_protocol_decoder_magellan_get_hash_data,
.serialize = subghz_protocol_decoder_magellan_serialize,
.deserialize = subghz_protocol_decoder_magellan_deserialize,
.get_string = subghz_protocol_decoder_magellan_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_magellen_encoder = {
.alloc = subghz_protocol_encoder_magellen_alloc,
.free = subghz_protocol_encoder_magellen_free,
const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = {
.alloc = subghz_protocol_encoder_magellan_alloc,
.free = subghz_protocol_encoder_magellan_free,
.deserialize = subghz_protocol_encoder_magellen_deserialize,
.stop = subghz_protocol_encoder_magellen_stop,
.yield = subghz_protocol_encoder_magellen_yield,
.deserialize = subghz_protocol_encoder_magellan_deserialize,
.stop = subghz_protocol_encoder_magellan_stop,
.yield = subghz_protocol_encoder_magellan_yield,
};
const SubGhzProtocol subghz_protocol_magellen = {
.name = SUBGHZ_PROTOCOL_MAGELLEN_NAME,
const SubGhzProtocol subghz_protocol_magellan = {
.name = SUBGHZ_PROTOCOL_MAGELLAN_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_magellen_decoder,
.encoder = &subghz_protocol_magellen_encoder,
.decoder = &subghz_protocol_magellan_decoder,
.encoder = &subghz_protocol_magellan_encoder,
};
void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) {
void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* 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->encoder.repeat = 10;
@@ -84,75 +84,75 @@ void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) {
return instance;
}
void subghz_protocol_encoder_magellen_free(void* context) {
void subghz_protocol_encoder_magellan_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderMagellen* instance = context;
SubGhzProtocolEncoderMagellan* instance = context;
free(instance->encoder.upload);
free(instance);
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderMagellen instance
* @param instance Pointer to a SubGhzProtocolEncoderMagellan instance
* @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);
size_t index = 0;
//Send header
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++] =
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++) {
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++] =
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++] =
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++] =
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
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++] =
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
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
if(bit_read(instance->generic.data, i - 1)) {
//send bit 1
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++] =
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 {
//send bit 0
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++] =
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
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++] =
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;
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);
SubGhzProtocolEncoderMagellen* instance = context;
SubGhzProtocolEncoderMagellan* instance = context;
bool res = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
@@ -160,7 +160,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat*
break;
}
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");
break;
}
@@ -168,7 +168,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat*
flipper_format_read_uint32(
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;
res = true;
@@ -177,13 +177,13 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat*
return res;
}
void subghz_protocol_encoder_magellen_stop(void* context) {
SubGhzProtocolEncoderMagellen* instance = context;
void subghz_protocol_encoder_magellan_stop(void* context) {
SubGhzProtocolEncoderMagellan* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_magellen_yield(void* context) {
SubGhzProtocolEncoderMagellen* instance = context;
LevelDuration subghz_protocol_encoder_magellan_yield(void* context) {
SubGhzProtocolEncoderMagellan* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
@@ -200,27 +200,27 @@ LevelDuration subghz_protocol_encoder_magellen_yield(void* context) {
return ret;
}
void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment) {
void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderMagellen* instance = malloc(sizeof(SubGhzProtocolDecoderMagellen));
instance->base.protocol = &subghz_protocol_magellen;
SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan));
instance->base.protocol = &subghz_protocol_magellan;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_magellen_free(void* context) {
void subghz_protocol_decoder_magellan_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderMagellen* instance = context;
SubGhzProtocolDecoderMagellan* instance = context;
free(instance);
}
void subghz_protocol_decoder_magellen_reset(void* context) {
void subghz_protocol_decoder_magellan_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderMagellen* instance = context;
instance->decoder.parser_step = MagellenDecoderStepReset;
SubGhzProtocolDecoderMagellan* instance = context;
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;
size_t i, j;
for(i = 0; i < len; i++) {
@@ -235,99 +235,99 @@ uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) {
return crc;
}
static bool subghz_protocol_magellen_check_crc(SubGhzProtocolDecoderMagellen* instance) {
static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) {
uint8_t data[3] = {
instance->decoder.decode_data >> 24,
instance->decoder.decode_data >> 16,
instance->decoder.decode_data >> 8};
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);
SubGhzProtocolDecoderMagellen* instance = context;
SubGhzProtocolDecoderMagellan* instance = context;
switch(instance->decoder.parser_step) {
case MagellenDecoderStepReset:
if((level) && (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) <
subghz_protocol_magellen_const.te_delta)) {
instance->decoder.parser_step = MagellenDecoderStepCheckPreambula;
case MagellanDecoderStepReset:
if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
subghz_protocol_magellan_const.te_delta)) {
instance->decoder.parser_step = MagellanDecoderStepCheckPreambula;
instance->decoder.te_last = duration;
instance->header_count = 0;
}
break;
case MagellenDecoderStepCheckPreambula:
case MagellanDecoderStepCheckPreambula:
if(level) {
instance->decoder.te_last = duration;
} else {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) <
subghz_protocol_magellen_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) <
subghz_protocol_magellen_const.te_delta)) {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
subghz_protocol_magellan_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
subghz_protocol_magellan_const.te_delta)) {
// Found header
instance->header_count++;
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) <
subghz_protocol_magellen_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) <
subghz_protocol_magellen_const.te_delta * 2) &&
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
subghz_protocol_magellan_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
subghz_protocol_magellan_const.te_delta * 2) &&
(instance->header_count > 10)) {
instance->decoder.parser_step = MagellenDecoderStepFoundPreambula;
instance->decoder.parser_step = MagellanDecoderStepFoundPreambula;
} else {
instance->decoder.parser_step = MagellenDecoderStepReset;
instance->decoder.parser_step = MagellanDecoderStepReset;
}
}
break;
case MagellenDecoderStepFoundPreambula:
case MagellanDecoderStepFoundPreambula:
if(level) {
instance->decoder.te_last = duration;
} else {
if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_magellen_const.te_short * 6) <
subghz_protocol_magellen_const.te_delta * 3) &&
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) <
subghz_protocol_magellen_const.te_delta * 2)) {
instance->decoder.parser_step = MagellenDecoderStepSaveDuration;
instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) <
subghz_protocol_magellan_const.te_delta * 3) &&
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
subghz_protocol_magellan_const.te_delta * 2)) {
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
} else {
instance->decoder.parser_step = MagellenDecoderStepReset;
instance->decoder.parser_step = MagellanDecoderStepReset;
}
}
break;
case MagellenDecoderStepSaveDuration:
case MagellanDecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = MagellenDecoderStepCheckDuration;
instance->decoder.parser_step = MagellanDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = MagellenDecoderStepReset;
instance->decoder.parser_step = MagellanDecoderStepReset;
}
break;
case MagellenDecoderStepCheckDuration:
case MagellanDecoderStepCheckDuration:
if(!level) {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) <
subghz_protocol_magellen_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) <
subghz_protocol_magellen_const.te_delta)) {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
subghz_protocol_magellan_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
subghz_protocol_magellan_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = MagellenDecoderStepSaveDuration;
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_long) <
subghz_protocol_magellen_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) <
subghz_protocol_magellen_const.te_delta)) {
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) <
subghz_protocol_magellan_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
subghz_protocol_magellan_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = MagellenDecoderStepSaveDuration;
} else if(duration >= (subghz_protocol_magellen_const.te_long * 3)) {
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
} else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) {
//Found stop bit
if((instance->decoder.decode_count_bit ==
subghz_protocol_magellen_const.min_count_bit_for_found) &&
subghz_protocol_magellen_check_crc(instance)) {
subghz_protocol_magellan_const.min_count_bit_for_found) &&
subghz_protocol_magellan_check_crc(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
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_count_bit = 0;
instance->decoder.parser_step = MagellenDecoderStepReset;
instance->decoder.parser_step = MagellanDecoderStepReset;
} else {
instance->decoder.parser_step = MagellenDecoderStepReset;
instance->decoder.parser_step = MagellanDecoderStepReset;
}
} else {
instance->decoder.parser_step = MagellenDecoderStepReset;
instance->decoder.parser_step = MagellanDecoderStepReset;
}
break;
}
@@ -350,7 +350,7 @@ void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t d
* Analysis of received data
* @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
* 0x037AE4828 => 001101111010111001001000 00101000
@@ -375,7 +375,7 @@ static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric*
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(
output,
"%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 ? ", ?" : ""));
}
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);
SubGhzProtocolDecoderMagellen* instance = context;
SubGhzProtocolDecoderMagellan* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool subghz_protocol_decoder_magellen_serialize(
bool subghz_protocol_decoder_magellan_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* preset) {
furi_assert(context);
SubGhzProtocolDecoderMagellen* instance = context;
SubGhzProtocolDecoderMagellan* instance = context;
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);
SubGhzProtocolDecoderMagellen* instance = context;
SubGhzProtocolDecoderMagellan* instance = context;
bool ret = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
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");
break;
}
@@ -424,10 +424,10 @@ bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat*
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);
SubGhzProtocolDecoderMagellen* instance = context;
subghz_protocol_magellen_check_remote_controller(&instance->generic);
SubGhzProtocolDecoderMagellan* instance = context;
subghz_protocol_magellan_check_remote_controller(&instance->generic);
furi_string_cat_printf(
output,
"%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.btn);
subghz_protocol_magellen_get_event_serialize(instance->generic.btn, output);
subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output);
}

View 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, SubGhzPresetDefinition
* @return true On success
*/
bool subghz_protocol_decoder_magellan_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* 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);

View File

@@ -1,6 +1,6 @@
#include "registry.h"
#include "protocol_items.h"
const SubGhzProtocol* subghz_protocol_registry[] = {
const SubGhzProtocol* subghz_protocol_registry_items[] = {
&subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line,
&subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh,
&subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo,
@@ -11,26 +11,9 @@ const SubGhzProtocol* subghz_protocol_registry[] = {
&subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek,
&subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec,
&subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2,
&subghz_protocol_honeywell_wdb, &subghz_protocol_magellen, &subghz_protocol_intertechno_v3,
&subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3,
&subghz_protocol_clemsa, &subghz_protocol_oregon2};
const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name) {
for(size_t i = 0; i < subghz_protocol_registry_count(); i++) {
if(strcmp(name, subghz_protocol_registry[i]->name) == 0) {
return subghz_protocol_registry[i];
}
}
return NULL;
}
const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index) {
if(index < subghz_protocol_registry_count()) {
return subghz_protocol_registry[index];
} else {
return NULL;
}
}
size_t subghz_protocol_registry_count() {
return COUNT_OF(subghz_protocol_registry);
}
const SubGhzProtocolRegistry subghz_protocol_registry = {
.items = subghz_protocol_registry_items,
.size = COUNT_OF(subghz_protocol_registry_items)};

View File

@@ -1,6 +1,5 @@
#pragma once
#include "../types.h"
#include "../registry.h"
#include "princeton.h"
#include "keeloq.h"
@@ -33,7 +32,7 @@
#include "doitrand.h"
#include "phoenix_v2.h"
#include "honeywell_wdb.h"
#include "magellen.h"
#include "magellan.h"
#include "intertechno_v3.h"
#include "clemsa.h"
#include "oregon2.h"
@@ -42,26 +41,8 @@
extern "C" {
#endif
/**
* Registration by name SubGhzProtocol.
* @param name Protocol name
* @return SubGhzProtocol* pointer to a SubGhzProtocol instance
*/
const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name);
/**
* Registration protocol by index in array SubGhzProtocol.
* @param index Protocol by index in array
* @return SubGhzProtocol* pointer to a SubGhzProtocol instance
*/
const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index);
/**
* Getting the number of registered protocols.
* @return Number of protocols
*/
size_t subghz_protocol_registry_count();
extern const SubGhzProtocolRegistry subghz_protocol_registry;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,6 +1,7 @@
#include "receiver.h"
#include "protocols/registry.h"
#include "registry.h"
#include "protocols/protocol_items.h"
#include <m-array.h>
@@ -22,9 +23,12 @@ struct SubGhzReceiver {
SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) {
SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver));
SubGhzReceiverSlotArray_init(instance->slots);
const SubGhzProtocolRegistry* protocol_registry_items =
subghz_environment_get_protocol_registry(environment);
for(size_t i = 0; i < subghz_protocol_registry_count(); ++i) {
const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_index(i);
for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) {
const SubGhzProtocol* protocol =
subghz_protocol_registry_get_by_index(protocol_registry_items, i);
if(protocol->decoder && protocol->decoder->alloc) {
SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots);
@@ -34,7 +38,6 @@ SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) {
instance->callback = NULL;
instance->context = NULL;
return instance;
}

30
lib/subghz/registry.c Normal file
View File

@@ -0,0 +1,30 @@
#include "registry.h"
const SubGhzProtocol* subghz_protocol_registry_get_by_name(
const SubGhzProtocolRegistry* protocol_registry,
const char* name) {
furi_assert(protocol_registry);
for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) {
if(strcmp(name, protocol_registry->items[i]->name) == 0) {
return protocol_registry->items[i];
}
}
return NULL;
}
const SubGhzProtocol* subghz_protocol_registry_get_by_index(
const SubGhzProtocolRegistry* protocol_registry,
size_t index) {
furi_assert(protocol_registry);
if(index < subghz_protocol_registry_count(protocol_registry)) {
return protocol_registry->items[index];
} else {
return NULL;
}
}
size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) {
furi_assert(protocol_registry);
return protocol_registry->size;
}

47
lib/subghz/registry.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include "types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzEnvironment SubGhzEnvironment;
typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry;
struct SubGhzProtocolRegistry {
const SubGhzProtocol** items;
const size_t size;
};
/**
* Registration by name SubGhzProtocol.
* @param protocol_registry SubGhzProtocolRegistry
* @param name Protocol name
* @return SubGhzProtocol* pointer to a SubGhzProtocol instance
*/
const SubGhzProtocol* subghz_protocol_registry_get_by_name(
const SubGhzProtocolRegistry* protocol_registry,
const char* name);
/**
* Registration protocol by index in array SubGhzProtocol.
* @param protocol_registry SubGhzProtocolRegistry
* @param index Protocol by index in array
* @return SubGhzProtocol* pointer to a SubGhzProtocol instance
*/
const SubGhzProtocol* subghz_protocol_registry_get_by_index(
const SubGhzProtocolRegistry* protocol_registry,
size_t index);
/**
* Getting the number of registered protocols.
* @param protocol_registry SubGhzProtocolRegistry
* @return Number of protocols
*/
size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,6 @@
#include "subghz_setting.h"
#include "subghz_i.h"
#include "types.h"
//#include "subghz_i.h"
#include <furi.h>
#include <m-list.h>
@@ -197,7 +198,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path, bool no
if(file_path) {
do {
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
FURI_LOG_E(TAG, "Error open file %s", file_path);
FURI_LOG_I(TAG, "File is not used %s", file_path);
break;
}

View File

@@ -6,6 +6,10 @@
#include <furi_hal.h>
#include <lib/flipper_format/flipper_format.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4
typedef struct SubGhzSetting SubGhzSetting;
@@ -48,3 +52,7 @@ uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance);
uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance);
void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup);
#ifdef __cplusplus
}
#endif

View File

@@ -1,7 +1,8 @@
#include "transmitter.h"
#include "protocols/base.h"
#include "protocols/registry.h"
#include "registry.h"
#include "protocols/protocol_items.h"
struct SubGhzTransmitter {
const SubGhzProtocol* protocol;
@@ -11,14 +12,17 @@ struct SubGhzTransmitter {
SubGhzTransmitter*
subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) {
SubGhzTransmitter* instance = NULL;
const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_name(protocol_name);
const SubGhzProtocolRegistry* protocol_registry_items =
subghz_environment_get_protocol_registry(environment);
const SubGhzProtocol* protocol =
subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name);
if(protocol && protocol->encoder && protocol->encoder->alloc) {
instance = malloc(sizeof(SubGhzTransmitter));
instance->protocol = protocol;
instance->protocol_instance = instance->protocol->encoder->alloc(environment);
}
return instance;
}

View File

@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
//#include "m-string.h"
#include <lib/flipper_format/flipper_format.h>
#include <lib/toolbox/level_duration.h>
@@ -10,7 +11,6 @@
#include "environment.h"
#include <furi.h>
#include <furi_hal.h>
#include <subghz/helpers/subghz_types.h>
#define SUBGHZ_APP_FOLDER ANY_PATH("subghz")
#define SUBGHZ_RAW_FOLDER EXT_PATH("subghz")
@@ -26,6 +26,15 @@
// Abstract method types
//
struct SubGhzPresetDefinition {
FuriString* name;
uint32_t frequency;
uint8_t* data;
size_t data_size;
};
typedef struct SubGhzPresetDefinition SubGhzPresetDefinition;
// Allocator and Deallocator
typedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment);
typedef void (*SubGhzFree)(void* context);
@@ -74,6 +83,8 @@ typedef enum {
SubGhzProtocolTypeStatic,
SubGhzProtocolTypeDynamic,
SubGhzProtocolTypeRAW,
SubGhzProtocolWeatherStation,
SubGhzProtocolCustom,
} SubGhzProtocolType;
typedef enum {
@@ -96,4 +107,4 @@ typedef struct {
const SubGhzProtocolEncoder* encoder;
const SubGhzProtocolDecoder* decoder;
} SubGhzProtocol;
} SubGhzProtocol;

View File

@@ -3,6 +3,8 @@ import json
from io import BytesIO
import tarfile
import xml.etree.ElementTree as ET
import posixpath
import os
from flipper.utils import *
from flipper.assets.coprobin import CoproBinary, get_stack_type
@@ -23,6 +25,8 @@ MANIFEST_TEMPLATE = {
class Copro:
COPRO_TAR_DIR = "core2_firmware"
def __init__(self, mcu):
self.mcu = mcu
self.version = None
@@ -50,9 +54,8 @@ class Copro:
raise Exception(f"Unsupported cube version")
self.version = cube_version
@staticmethod
def _getFileName(name):
return os.path.join("core2_firmware", name)
def _getFileName(self, name):
return posixpath.join(self.COPRO_TAR_DIR, name)
def addFile(self, array, filename, **kwargs):
source_file = os.path.join(self.mcu_copro, filename)
@@ -61,6 +64,9 @@ class Copro:
def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None):
self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT)
fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR)
fw_directory.type = tarfile.DIRTYPE
self.output_tar.addfile(fw_directory)
stack_file = os.path.join(self.mcu_copro, stack_file_name)
# Form Manifest