Merge branch 'dev' of https://github.com/ClaraCrazy/Flipper-Xtreme into fix-bad_kb_bt-flipper_app-conflict

This commit is contained in:
Willy-JL
2023-02-28 21:24:18 +00:00
226 changed files with 2963 additions and 4547 deletions

7
.gitignore vendored
View File

@@ -34,6 +34,10 @@ Brewfile.lock.json
# Visual Studio # Visual Studio
.vs/ .vs/
# Kate
.kateproject
.kateconfig
# legendary cmake's # legendary cmake's
build build
CMakeLists.txt CMakeLists.txt
@@ -68,9 +72,8 @@ PVS-Studio.log
# Automate files, etc # Automate files, etc
automate.py automate.py
deployments/ deployments/
fbt_options.py
commitnotes.md commitnotes.md
lib/STM32CubeWB fbt_options.py
# Asset packs # Asset packs
assets/dolphin/custom/* assets/dolphin/custom/*

View File

@@ -86,4 +86,4 @@ Small applications providing configuration for basic firmware and its services.
Utility apps not visible in other menus. Utility apps not visible in other menus.
- `storage_move_to_sd` - Data migration tool for internal storage - `storage_move_to_sd` - Data migration tool for internal storage
- `updater` - updater service & application - `updater` - Update service & application

View File

@@ -34,7 +34,7 @@ void AccessorApp::run(void) {
AccessorApp::AccessorApp() AccessorApp::AccessorApp()
: text_store{0} { : text_store{0} {
notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION)); notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
onewire_host = onewire_host_alloc(); onewire_host = onewire_host_alloc(&ibutton_gpio);
furi_hal_power_enable_otg(); furi_hal_power_enable_otg();
} }

View File

@@ -3,56 +3,63 @@
#include "../minunit.h" #include "../minunit.h"
static void power_test_deinit(void) { static void power_test_deinit(void) {
// Try to reset to default charging voltage // Try to reset to default charge voltage limit
furi_hal_power_set_battery_charging_voltage(4.208f); furi_hal_power_set_battery_charge_voltage_limit(4.208f);
} }
MU_TEST(test_power_charge_voltage_exact) { MU_TEST(test_power_charge_voltage_limit_exact) {
// Power of 16mV charge voltages get applied exactly // Power of 16mV charge voltage limits get applied exactly
// (bq25896 charge controller works in 16mV increments) // (bq25896 charge controller works in 16mV increments)
// //
// This test may need adapted if other charge controllers are used in the future. // This test may need adapted if other charge controllers are used in the future.
for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) { for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) {
float charge_volt = (float)charge_mv / 1000.0f; float charge_volt = (float)charge_mv / 1000.0f;
furi_hal_power_set_battery_charging_voltage(charge_volt); furi_hal_power_set_battery_charge_voltage_limit(charge_volt);
mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage()); mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit());
} }
} }
MU_TEST(test_power_charge_voltage_floating_imprecision) { MU_TEST(test_power_charge_voltage_limit_floating_imprecision) {
// 4.016f should act as 4.016 V, even with floating point imprecision // 4.016f should act as 4.016 V, even with floating point imprecision
furi_hal_power_set_battery_charging_voltage(4.016f); furi_hal_power_set_battery_charge_voltage_limit(4.016f);
mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage()); mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit());
} }
MU_TEST(test_power_charge_voltage_inexact) { MU_TEST(test_power_charge_voltage_limit_inexact) {
// Charge voltages that are not power of 16mV get truncated down // Charge voltage limits that are not power of 16mV get truncated down
furi_hal_power_set_battery_charging_voltage(3.841f); furi_hal_power_set_battery_charge_voltage_limit(3.841f);
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
furi_hal_power_set_battery_charging_voltage(3.900f); furi_hal_power_set_battery_charge_voltage_limit(3.900f);
mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage()); mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit());
furi_hal_power_set_battery_charging_voltage(4.200f); furi_hal_power_set_battery_charge_voltage_limit(4.200f);
mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage()); mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit());
} }
MU_TEST(test_power_charge_voltage_invalid_clamped) { MU_TEST(test_power_charge_voltage_limit_invalid_clamped) {
// Out-of-range charge voltages get clamped to 3.840 V and 4.208 V // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V
furi_hal_power_set_battery_charging_voltage(3.808f); furi_hal_power_set_battery_charge_voltage_limit(3.808f);
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
furi_hal_power_set_battery_charge_voltage_limit(1.0f);
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
// NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an // NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an
// unhappy battery if this fails. // unhappy battery if this fails.
furi_hal_power_set_battery_charging_voltage(4.240f); furi_hal_power_set_battery_charge_voltage_limit(4.240f);
mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage()); mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
// Likewise, picking a number that the uint8_t wraparound in the driver would result in a
// VREG value under 23 if this test fails.
// E.g. (uint8_t)((8105-3840)/16) -> 10
furi_hal_power_set_battery_charge_voltage_limit(8.105f);
mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
} }
MU_TEST_SUITE(test_power_suite) { MU_TEST_SUITE(test_power_suite) {
MU_RUN_TEST(test_power_charge_voltage_exact); MU_RUN_TEST(test_power_charge_voltage_limit_exact);
MU_RUN_TEST(test_power_charge_voltage_floating_imprecision); MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision);
MU_RUN_TEST(test_power_charge_voltage_inexact); MU_RUN_TEST(test_power_charge_voltage_limit_inexact);
MU_RUN_TEST(test_power_charge_voltage_invalid_clamped); MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped);
power_test_deinit(); power_test_deinit();
} }

View File

@@ -89,7 +89,7 @@ static void test_rpc_setup(void) {
} }
furi_check(rpc_session[0].session); furi_check(rpc_session[0].session);
rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1); rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);
rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();

View File

@@ -12,8 +12,9 @@
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") #define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
#define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
#define TEST_RANDOM_COUNT_PARSE 273 #define TEST_RANDOM_COUNT_PARSE 329
#define TEST_TIMEOUT 10000 #define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler; static SubGhzEnvironment* environment_handler;
@@ -43,6 +44,8 @@ static void subghz_test_init(void) {
environment_handler, CAME_ATOMO_DIR_NAME); environment_handler, CAME_ATOMO_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment_handler, NICE_FLOR_S_DIR_NAME); environment_handler, NICE_FLOR_S_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
environment_handler, ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_protocol_registry( subghz_environment_set_protocol_registry(
environment_handler, (void*)&subghz_protocol_registry); environment_handler, (void*)&subghz_protocol_registry);
@@ -489,6 +492,14 @@ MU_TEST(subghz_decoder_linear_test) {
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_linear_delta3_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"),
SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME),
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
}
MU_TEST(subghz_decoder_megacode_test) { MU_TEST(subghz_decoder_megacode_test) {
mu_assert( mu_assert(
subghz_decoder_test( subghz_decoder_test(
@@ -590,12 +601,6 @@ MU_TEST(subghz_decoder_ansonic_test) {
"Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_pocsag_test) {
mu_assert(
subghz_decoder_test(EXT_PATH("unit_tests/subghz/pocsag.sub"), SUBGHZ_PROTOCOL_POCSAG_NAME),
"Test decoder " SUBGHZ_PROTOCOL_POCSAG_NAME " error\r\n");
}
MU_TEST(subghz_decoder_smc5326_test) { MU_TEST(subghz_decoder_smc5326_test) {
mu_assert( mu_assert(
subghz_decoder_test( subghz_decoder_test(
@@ -610,6 +615,36 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) {
"Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_dooya_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME),
"Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
}
MU_TEST(subghz_decoder_alutech_at_4n_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"),
SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME),
"Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n");
}
MU_TEST(subghz_decoder_nice_one_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),
"Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n");
}
MU_TEST(subghz_decoder_kinggates_stylo4k_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"),
SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME),
"Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n");
}
//test encoders //test encoders
MU_TEST(subghz_encoder_princeton_test) { MU_TEST(subghz_encoder_princeton_test) {
mu_assert( mu_assert(
@@ -653,6 +688,12 @@ MU_TEST(subghz_encoder_linear_test) {
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_linear_delta3_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")),
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
}
MU_TEST(subghz_encoder_megacode_test) { MU_TEST(subghz_encoder_megacode_test) {
mu_assert( mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")), subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
@@ -749,6 +790,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) {
"Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_dooya_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")),
"Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
}
MU_TEST(subghz_random_test) { MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
} }
@@ -778,6 +825,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_somfy_telis_test); MU_RUN_TEST(subghz_decoder_somfy_telis_test);
MU_RUN_TEST(subghz_decoder_star_line_test); MU_RUN_TEST(subghz_decoder_star_line_test);
MU_RUN_TEST(subghz_decoder_linear_test); MU_RUN_TEST(subghz_decoder_linear_test);
MU_RUN_TEST(subghz_decoder_linear_delta3_test);
MU_RUN_TEST(subghz_decoder_megacode_test); MU_RUN_TEST(subghz_decoder_megacode_test);
MU_RUN_TEST(subghz_decoder_secplus_v1_test); MU_RUN_TEST(subghz_decoder_secplus_v1_test);
MU_RUN_TEST(subghz_decoder_secplus_v2_test); MU_RUN_TEST(subghz_decoder_secplus_v2_test);
@@ -792,9 +840,12 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_decoder_ansonic_test); MU_RUN_TEST(subghz_decoder_ansonic_test);
MU_RUN_TEST(subghz_decoder_pocsag_test);
MU_RUN_TEST(subghz_decoder_smc5326_test); MU_RUN_TEST(subghz_decoder_smc5326_test);
MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
MU_RUN_TEST(subghz_decoder_dooya_test);
MU_RUN_TEST(subghz_decoder_alutech_at_4n_test);
MU_RUN_TEST(subghz_decoder_nice_one_test);
MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test);
MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test); MU_RUN_TEST(subghz_encoder_came_test);
@@ -803,6 +854,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_nice_flo_test); MU_RUN_TEST(subghz_encoder_nice_flo_test);
MU_RUN_TEST(subghz_encoder_keelog_test); MU_RUN_TEST(subghz_encoder_keelog_test);
MU_RUN_TEST(subghz_encoder_linear_test); MU_RUN_TEST(subghz_encoder_linear_test);
MU_RUN_TEST(subghz_encoder_linear_delta3_test);
MU_RUN_TEST(subghz_encoder_megacode_test); MU_RUN_TEST(subghz_encoder_megacode_test);
MU_RUN_TEST(subghz_encoder_holtek_test); MU_RUN_TEST(subghz_encoder_holtek_test);
MU_RUN_TEST(subghz_encoder_secplus_v1_test); MU_RUN_TEST(subghz_encoder_secplus_v1_test);
@@ -819,6 +871,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_ansonic_test); MU_RUN_TEST(subghz_encoder_ansonic_test);
MU_RUN_TEST(subghz_encoder_smc5326_test); MU_RUN_TEST(subghz_encoder_smc5326_test);
MU_RUN_TEST(subghz_encoder_holtek_ht12x_test); MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
MU_RUN_TEST(subghz_encoder_dooya_test);
MU_RUN_TEST(subghz_random_test); MU_RUN_TEST(subghz_random_test);
subghz_test_deinit(); subghz_test_deinit();

View File

@@ -70,7 +70,7 @@ void minunit_print_progress() {
} }
void minunit_print_fail(const char* str) { void minunit_print_fail(const char* str) {
printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str); printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
} }
void unit_tests_cli(Cli* cli, FuriString* args, void* context) { void unit_tests_cli(Cli* cli, FuriString* args, void* context) {

View File

@@ -8,6 +8,7 @@
#include <gui/modules/file_browser_worker.h> #include <gui/modules/file_browser_worker.h>
#include <fap_loader/fap_loader_app.h> #include <fap_loader/fap_loader_app.h>
#include <math.h> #include <math.h>
#include <furi_hal.h>
static void static void
archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) {
@@ -464,14 +465,17 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
browser->last_tab_switch_dir = key; browser->last_tab_switch_dir = key;
for(int i = 0; i < 2; i++) {
if(key == InputKeyLeft) { if(key == InputKeyLeft) {
tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
} else { } else {
tab = (tab + 1) % ArchiveTabTotal; tab = (tab + 1) % ArchiveTabTotal;
} }
if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
break; if(key == InputKeyLeft) {
tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
} else {
tab = (tab + 1) % ArchiveTabTotal;
}
} }
browser->is_root = true; browser->is_root = true;

View File

@@ -107,6 +107,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == GpioStartEventUsbUart) { } else if(event.event == GpioStartEventUsbUart) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
if(!furi_hal_usb_is_locked()) { if(!furi_hal_usb_is_locked()) {
DOLPHIN_DEED(DolphinDeedGpioUartBridge);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
} else { } else {
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);

View File

@@ -271,7 +271,7 @@ void onewire_cli_print_usage() {
static void onewire_cli_search(Cli* cli) { static void onewire_cli_search(Cli* cli) {
UNUSED(cli); UNUSED(cli);
OneWireHost* onewire = onewire_host_alloc(); OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio);
uint8_t address[8]; uint8_t address[8];
bool done = false; bool done = false;

View File

@@ -39,7 +39,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
if(event.event == SubmenuIndexRead) { if(event.event == SubmenuIndexRead) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead);
//DOLPHIN_DEED(DolphinDeedIbuttonRead); DOLPHIN_DEED(DolphinDeedIbuttonRead);
} else if(event.event == SubmenuIndexSaved) { } else if(event.event == SubmenuIndexSaved) {
furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey);

View File

@@ -148,6 +148,12 @@ static Infrared* infrared_alloc() {
view_dispatcher_add_view( view_dispatcher_add_view(
view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input)); view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input));
infrared->variable_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
infrared->view_dispatcher,
InfraredViewVariableItemList,
variable_item_list_get_view(infrared->variable_item_list));
infrared->dialog_ex = dialog_ex_alloc(); infrared->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view( view_dispatcher_add_view(
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex)); view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
@@ -195,6 +201,9 @@ static void infrared_free(Infrared* infrared) {
view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput); view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);
text_input_free(infrared->text_input); text_input_free(infrared->text_input);
view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList);
variable_item_list_free(infrared->variable_item_list);
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx); view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
dialog_ex_free(infrared->dialog_ex); dialog_ex_free(infrared->dialog_ex);

View File

@@ -86,7 +86,7 @@ static void infrared_cli_print_usage(void) {
printf("\tir universal <remote_name> <signal_name>\r\n"); printf("\tir universal <remote_name> <signal_name>\r\n");
printf("\tir universal list <remote_name>\r\n"); printf("\tir universal list <remote_name>\r\n");
// TODO: Do not hardcode universal remote names // TODO: Do not hardcode universal remote names
printf("\tAvailable universal remotes: tv audio ac\r\n"); printf("\tAvailable universal remotes: tv audio ac projector\r\n");
} }
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {

View File

@@ -2,6 +2,7 @@
#include <gui/gui.h> #include <gui/gui.h>
#include <gui/view.h> #include <gui/view.h>
#include <assets_icons.h>
#include <gui/view_stack.h> #include <gui/view_stack.h>
#include <gui/view_dispatcher.h> #include <gui/view_dispatcher.h>
#include <gui/scene_manager.h> #include <gui/scene_manager.h>
@@ -9,6 +10,7 @@
#include <gui/modules/popup.h> #include <gui/modules/popup.h>
#include <gui/modules/loading.h> #include <gui/modules/loading.h>
#include <gui/modules/submenu.h> #include <gui/modules/submenu.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/dialog_ex.h> #include <gui/modules/dialog_ex.h>
#include <gui/modules/text_input.h> #include <gui/modules/text_input.h>
#include <gui/modules/button_menu.h> #include <gui/modules/button_menu.h>
@@ -32,8 +34,6 @@
#include "rpc/rpc_app.h" #include "rpc/rpc_app.h"
#include <assets_icons.h>
#define INFRARED_FILE_NAME_SIZE 100 #define INFRARED_FILE_NAME_SIZE 100
#define INFRARED_TEXT_STORE_NUM 2 #define INFRARED_TEXT_STORE_NUM 2
#define INFRARED_TEXT_STORE_SIZE 128 #define INFRARED_TEXT_STORE_SIZE 128
@@ -87,6 +87,7 @@ struct Infrared {
Submenu* submenu; Submenu* submenu;
TextInput* text_input; TextInput* text_input;
VariableItemList* variable_item_list;
DialogEx* dialog_ex; DialogEx* dialog_ex;
ButtonMenu* button_menu; ButtonMenu* button_menu;
Popup* popup; Popup* popup;
@@ -108,6 +109,7 @@ struct Infrared {
typedef enum { typedef enum {
InfraredViewSubmenu, InfraredViewSubmenu,
InfraredViewTextInput, InfraredViewTextInput,
InfraredViewVariableItemList,
InfraredViewDialogEx, InfraredViewDialogEx,
InfraredViewButtonMenu, InfraredViewButtonMenu,
InfraredViewPopup, InfraredViewPopup,

View File

@@ -21,4 +21,5 @@ ADD_SCENE(infrared, universal_audio, UniversalAudio)
ADD_SCENE(infrared, universal_projector, UniversalProjector) ADD_SCENE(infrared, universal_projector, UniversalProjector)
ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, debug, Debug)
ADD_SCENE(infrared, error_databases, ErrorDatabases) ADD_SCENE(infrared, error_databases, ErrorDatabases)
ADD_SCENE(infrared, debug_settings, DebugSettings)
ADD_SCENE(infrared, rpc, Rpc) ADD_SCENE(infrared, rpc, Rpc)

View File

@@ -0,0 +1,59 @@
#include "../infrared_i.h"
#include <furi_hal_infrared.h>
uint8_t value_index_ir;
#define DEB_PINS_COUNT (sizeof(infrared_debug_cfg_variables_text) / sizeof(char* const))
const char* const infrared_debug_cfg_variables_text[] = {
"Internal",
"2 (A7)",
};
static void infrared_scene_debug_settings_changed(VariableItem* item) {
Infrared* infrared = variable_item_get_context(item);
value_index_ir = variable_item_get_current_value_index(item);
UNUSED(infrared);
variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
furi_hal_infrared_set_debug_out(value_index_ir);
}
static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) {
Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
}
void infrared_scene_debug_settings_on_enter(void* context) {
Infrared* infrared = context;
VariableItemList* variable_item_list = infrared->variable_item_list;
value_index_ir = furi_hal_infrared_get_debug_out_status();
VariableItem* item = variable_item_list_add(
variable_item_list,
"Send signal to",
DEB_PINS_COUNT,
infrared_scene_debug_settings_changed,
infrared);
variable_item_list_set_enter_callback(
variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared);
variable_item_set_current_value_index(item, value_index_ir);
variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList);
}
bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) {
Infrared* infrared = context;
UNUSED(infrared);
UNUSED(event);
return false;
}
void infrared_scene_debug_settings_on_exit(void* context) {
Infrared* infrared = context;
variable_item_list_reset(infrared->variable_item_list);
}

View File

@@ -5,7 +5,8 @@ enum SubmenuIndex {
SubmenuIndexLearnNewRemote, SubmenuIndexLearnNewRemote,
SubmenuIndexLearnNewRemoteRaw, SubmenuIndexLearnNewRemoteRaw,
SubmenuIndexSavedRemotes, SubmenuIndexSavedRemotes,
SubmenuIndexDebug SubmenuIndexDebug,
SubmenuIndexDebugSettings
}; };
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) { static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -45,7 +46,17 @@ void infrared_scene_start_on_enter(void* context) {
infrared_scene_start_submenu_callback, infrared_scene_start_submenu_callback,
infrared); infrared);
submenu_add_item( submenu_add_item(
submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared); submenu,
"Debug RX",
SubmenuIndexDebug,
infrared_scene_start_submenu_callback,
infrared);
submenu_add_item(
submenu,
"Debug Settings",
SubmenuIndexDebugSettings,
infrared_scene_start_submenu_callback,
infrared);
} }
const uint32_t submenu_index = const uint32_t submenu_index =
@@ -85,6 +96,9 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(submenu_index == SubmenuIndexDebug) { } else if(submenu_index == SubmenuIndexDebug) {
scene_manager_next_scene(scene_manager, InfraredSceneDebug); scene_manager_next_scene(scene_manager, InfraredSceneDebug);
consumed = true; consumed = true;
} else if(submenu_index == SubmenuIndexDebugSettings) {
scene_manager_next_scene(scene_manager, InfraredSceneDebugSettings);
consumed = true;
} }
} }

View File

@@ -80,6 +80,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC); scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC);
consumed = true; consumed = true;
} }
scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event);
} }
return consumed; return consumed;

View File

@@ -5,8 +5,10 @@
#include <gui/gui.h> #include <gui/gui.h>
#include <gui/view.h> #include <gui/view.h>
#include <assets_icons.h>
#include <gui/view_dispatcher.h> #include <gui/view_dispatcher.h>
#include <gui/scene_manager.h> #include <gui/scene_manager.h>
#include <cli/cli.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include <gui/modules/submenu.h> #include <gui/modules/submenu.h>
@@ -32,8 +34,6 @@
#include <lfrfid/lfrfid_worker.h> #include <lfrfid/lfrfid_worker.h>
#include <lfrfid/scenes/lfrfid_scene.h> #include <lfrfid/scenes/lfrfid_scene.h>
#include <assets_icons.h>
// #include <lfrfid_icons.h>
#define LFRFID_KEY_NAME_SIZE 22 #define LFRFID_KEY_NAME_SIZE 22
#define LFRFID_TEXT_STORE_SIZE 40 #define LFRFID_TEXT_STORE_SIZE 40

View File

@@ -20,7 +20,7 @@ void lfrfid_scene_extra_actions_on_enter(void* context) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Read ASK (Animal, FDX)", "Read ASK (FDX,Regular)",
SubmenuIndexASK, SubmenuIndexASK,
lfrfid_scene_extra_actions_submenu_callback, lfrfid_scene_extra_actions_submenu_callback,
app); app);

View File

@@ -47,21 +47,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) { if(event.event == SubmenuIndexRead) {
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
DOLPHIN_DEED(DolphinDeedRfidRead); DOLPHIN_DEED(DolphinDeedRfidRead);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexSaved) { } else if(event.event == SubmenuIndexSaved) {
// Like in the other apps, explicitly save the scene state
// in each branch in case the user cancels loading a file.
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved);
furi_string_set(app->file_path, LFRFID_APP_FOLDER); furi_string_set(app->file_path, LFRFID_APP_FOLDER);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey); scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexAddManually) { } else if(event.event == SubmenuIndexAddManually) {
scene_manager_set_scene_state(
app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType); scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexExtraActions) { } else if(event.event == SubmenuIndexExtraActions) {
scene_manager_set_scene_state(
app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions);
scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions); scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions);
consumed = true; consumed = true;
} }
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event);
} }
return consumed; return consumed;

View File

@@ -8,11 +8,14 @@ typedef enum {
//SubmenuIndex //SubmenuIndex
SubmenuIndexFaacSLH_433, SubmenuIndexFaacSLH_433,
SubmenuIndexFaacSLH_868, SubmenuIndexFaacSLH_868,
SubmenuIndexBFT, SubmenuIndexBFTClone,
SubmenuIndexBFTMitto,
SubmenuIndexSomfyTelis,
SubmenuIndexPricenton, SubmenuIndexPricenton,
SubmenuIndexNiceFlo12bit, SubmenuIndexNiceFlo12bit,
SubmenuIndexNiceFlo24bit, SubmenuIndexNiceFlo24bit,
SubmenuIndexNiceFlorS_433_92, SubmenuIndexNiceFlorS_433_92,
SubmenuIndexNiceOne_433_92,
SubmenuIndexNiceSmilo_433_92, SubmenuIndexNiceSmilo_433_92,
SubmenuIndexCAME12bit, SubmenuIndexCAME12bit,
SubmenuIndexCAME24bit, SubmenuIndexCAME24bit,
@@ -64,6 +67,7 @@ typedef enum {
SubGhzCustomEventViewReceiverBack, SubGhzCustomEventViewReceiverBack,
SubGhzCustomEventViewReceiverOffDisplay, SubGhzCustomEventViewReceiverOffDisplay,
SubGhzCustomEventViewReceiverUnlock, SubGhzCustomEventViewReceiverUnlock,
SubGhzCustomEventViewReceiverDeleteItem,
SubGhzCustomEventViewReadRAWBack, SubGhzCustomEventViewReadRAWBack,
SubGhzCustomEventViewReadRAWIDLE, SubGhzCustomEventViewReadRAWIDLE,

View File

@@ -1,8 +1,9 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include "../helpers/subghz_custom_event.h" #include "../helpers/subghz_custom_event.h"
uint8_t value_index; uint8_t value_index_exm;
uint8_t value_index2; uint8_t value_index_dpin;
uint8_t value_index_cnt;
#define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const)) #define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const))
const char* const radio_modules_variables_text[] = { const char* const radio_modules_variables_text[] = {
@@ -16,12 +17,22 @@ const char* const debug_pin_text[DEBUG_P_COUNT] = {
"17(1W)", "17(1W)",
}; };
#define DEBUG_COUNTER_COUNT 6
const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
"+1",
"+2",
"+3",
"+4",
"+5",
"+10",
};
static void subghz_scene_ext_module_changed(VariableItem* item) { static void subghz_scene_ext_module_changed(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
value_index = variable_item_get_current_value_index(item); value_index_exm = variable_item_get_current_value_index(item);
UNUSED(subghz); UNUSED(subghz);
variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
} }
static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) { static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) {
SubGhz* subghz = context; SubGhz* subghz = context;
@@ -37,20 +48,49 @@ static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) {
subghz->txrx->debug_pin_state = index == 1; subghz->txrx->debug_pin_state = index == 1;
} }
static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, debug_counter_text[index]);
switch(index) {
case 0:
furi_hal_subghz_set_rolling_counter_mult(1);
break;
case 1:
furi_hal_subghz_set_rolling_counter_mult(2);
break;
case 2:
furi_hal_subghz_set_rolling_counter_mult(3);
break;
case 3:
furi_hal_subghz_set_rolling_counter_mult(4);
break;
case 4:
furi_hal_subghz_set_rolling_counter_mult(5);
break;
case 5:
furi_hal_subghz_set_rolling_counter_mult(10);
break;
default:
break;
}
}
void subghz_scene_ext_module_settings_on_enter(void* context) { void subghz_scene_ext_module_settings_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
VariableItemList* variable_item_list = subghz->variable_item_list; VariableItemList* variable_item_list = subghz->variable_item_list;
value_index = furi_hal_subghz.radio_type; value_index_exm = furi_hal_subghz.radio_type;
VariableItem* item = variable_item_list_add( VariableItem* item = variable_item_list_add(
variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz); variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz);
variable_item_list_set_enter_callback( variable_item_list_set_enter_callback(
variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz); variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index_exm);
variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
item = variable_item_list_add( item = variable_item_list_add(
@@ -59,9 +99,40 @@ void subghz_scene_ext_module_settings_on_enter(void* context) {
DEBUG_P_COUNT, DEBUG_P_COUNT,
subghz_scene_receiver_config_set_debug_pin, subghz_scene_receiver_config_set_debug_pin,
subghz); subghz);
value_index2 = subghz->txrx->debug_pin_state; value_index_dpin = subghz->txrx->debug_pin_state;
variable_item_set_current_value_index(item, value_index2); variable_item_set_current_value_index(item, value_index_dpin);
variable_item_set_current_value_text(item, debug_pin_text[value_index2]); variable_item_set_current_value_text(item, debug_pin_text[value_index_dpin]);
item = variable_item_list_add(
subghz->variable_item_list,
"Counter Mult:",
DEBUG_COUNTER_COUNT,
subghz_scene_receiver_config_set_debug_counter,
subghz);
switch(furi_hal_subghz_get_rolling_counter_mult()) {
case 1:
value_index_cnt = 0;
break;
case 2:
value_index_cnt = 1;
break;
case 3:
value_index_cnt = 2;
break;
case 4:
value_index_cnt = 3;
break;
case 5:
value_index_cnt = 4;
break;
case 10:
value_index_cnt = 5;
break;
default:
break;
}
variable_item_set_current_value_index(item, value_index_cnt);
variable_item_set_current_value_text(item, debug_counter_text[value_index_cnt]);
} }
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
@@ -73,12 +144,12 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent
UNUSED(event); UNUSED(event);
// Set selected radio module // Set selected radio module
furi_hal_subghz_set_radio_type(value_index); furi_hal_subghz_set_radio_type(value_index_exm);
// Check if module is present, if no -> show error // Check if module is present, if no -> show error
if(!furi_hal_subghz_check_radio()) { if(!furi_hal_subghz_check_radio()) {
value_index = 0; value_index_exm = 0;
furi_hal_subghz_set_radio_type(value_index); furi_hal_subghz_set_radio_type(value_index_exm);
furi_string_set(subghz->error_str, "Please connect\nexternal radio"); furi_string_set(subghz->error_str, "Please connect\nexternal radio");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
} }

View File

@@ -204,6 +204,16 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverDeleteItem:
subghz->txrx->idx_menu_chosen =
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
subghz_history_delete_item(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
subghz_view_receiver_delete_element_callback(subghz->subghz_receiver);
subghz_scene_receiver_update_statusbar(subghz);
consumed = true;
break;
case SubGhzCustomEventViewReceiverConfig: case SubGhzCustomEventViewReceiverConfig:
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->idx_menu_chosen = subghz->txrx->idx_menu_chosen =

View File

@@ -2,6 +2,9 @@
#include "../helpers/subghz_custom_event.h" #include "../helpers/subghz_custom_event.h"
#include <lib/subghz/protocols/keeloq.h> #include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/star_line.h> #include <lib/subghz/protocols/star_line.h>
#include <lib/subghz/protocols/alutech_at_4n.h>
#include <lib/subghz/protocols/nice_flor_s.h>
#include <lib/subghz/protocols/somfy_telis.h>
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context); furi_assert(context);
@@ -233,6 +236,10 @@ void subghz_scene_receiver_info_on_exit(void* context) {
widget_reset(subghz->widget); widget_reset(subghz->widget);
keeloq_reset_mfname(); keeloq_reset_mfname();
keeloq_reset_kl_type(); keeloq_reset_kl_type();
keeloq_reset_original_btn();
alutech_reset_original_btn();
nice_flors_reset_original_btn();
somfy_telis_reset_original_btn();
star_line_reset_mfname(); star_line_reset_mfname();
star_line_reset_kl_type(); star_line_reset_kl_type();
} }

View File

@@ -1,6 +1,10 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include <lib/subghz/protocols/keeloq.h> #include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/star_line.h> #include <lib/subghz/protocols/star_line.h>
#include <lib/subghz/protocols/alutech_at_4n.h>
#include <lib/subghz/protocols/nice_flor_s.h>
#include <lib/subghz/protocols/somfy_telis.h>
#include "xtreme/assets.h" #include "xtreme/assets.h"
typedef enum { typedef enum {
@@ -110,6 +114,10 @@ void subghz_scene_rpc_on_exit(void* context) {
keeloq_reset_mfname(); keeloq_reset_mfname();
keeloq_reset_kl_type(); keeloq_reset_kl_type();
keeloq_reset_original_btn();
alutech_reset_original_btn();
nice_flors_reset_original_btn();
somfy_telis_reset_original_btn();
star_line_reset_mfname(); star_line_reset_mfname();
star_line_reset_kl_type(); star_line_reset_kl_type();
} }

View File

@@ -79,10 +79,22 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexFaacSLH_433, SubmenuIndexFaacSLH_433,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item(
subghz->submenu,
"BFT [Manual] 433MHz",
SubmenuIndexBFTClone,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"BFT Mitto 433MHz", "BFT Mitto 433MHz",
SubmenuIndexBFT, SubmenuIndexBFTMitto,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"Somfy Telis 433MHz",
SubmenuIndexSomfyTelis,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item( submenu_add_item(
@@ -115,6 +127,12 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexNiceFlorS_433_92, SubmenuIndexNiceFlorS_433_92,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item(
subghz->submenu,
"Nice One 433MHz",
SubmenuIndexNiceOne_433_92,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"CAME 12bit 433MHz", "CAME 12bit 433MHz",
@@ -230,7 +248,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case SubmenuIndexFaacSLH_433: case SubmenuIndexFaacSLH_433:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac);
break; break;
case SubmenuIndexBFT: case SubmenuIndexBFTClone:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft);
break; break;
case SubmenuIndexPricenton: case SubmenuIndexPricenton:
@@ -306,6 +324,66 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
generated_protocol = true; generated_protocol = true;
} }
break; break;
case SubmenuIndexBFTMitto:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_bft_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key & 0x000FFFFF,
0x2,
0x0002,
key & 0x000FFFFF,
"BFT",
subghz->txrx->preset);
uint8_t seed_data[sizeof(uint32_t)] = {0};
for(size_t i = 0; i < sizeof(uint32_t); i++) {
seed_data[sizeof(uint32_t) - i - 1] = ((key & 0x000FFFFF) >> i * 8) & 0xFF;
}
flipper_format_write_hex(
subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t));
flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT");
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
if(!generated_protocol) {
furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
break;
case SubmenuIndexSomfyTelis:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME);
subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_somfy_telis_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key & 0x00FFFFFF,
0x2,
0x0003,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
if(!generated_protocol) {
furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
break;
case SubmenuIndexDoorHan_433_92: case SubmenuIndexDoorHan_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
@@ -367,7 +445,33 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
key & 0x0FFFFFFF, key & 0x0FFFFFFF,
0x1, 0x1,
0x0003, 0x0003,
subghz->txrx->preset); subghz->txrx->preset,
false);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
if(!generated_protocol) {
furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
break;
case SubmenuIndexNiceOne_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME);
subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_nice_flor_s_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key & 0x0FFFFFFF,
0x1,
0x0003,
subghz->txrx->preset,
true);
generated_protocol = true; generated_protocol = true;
} else { } else {
generated_protocol = false; generated_protocol = false;

View File

@@ -2,7 +2,10 @@
#include "../views/transmitter.h" #include "../views/transmitter.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <lib/subghz/protocols/keeloq.h> #include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/alutech_at_4n.h>
#include <lib/subghz/protocols/star_line.h> #include <lib/subghz/protocols/star_line.h>
#include <lib/subghz/protocols/nice_flor_s.h>
#include <lib/subghz/protocols/somfy_telis.h>
void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
furi_assert(context); furi_assert(context);
@@ -89,6 +92,27 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
subghz_tx_stop(subghz); subghz_tx_stop(subghz);
subghz_sleep(subghz); subghz_sleep(subghz);
} }
if(keeloq_get_custom_btn() != 0) {
keeloq_set_btn(0);
alutech_set_btn(0);
nice_flors_set_btn(0);
somfy_telis_set_btn(0);
uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
furi_hal_subghz_set_rolling_counter_mult(0);
// Calling restore!
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
}
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
}
}
subghz_tx_stop(subghz);
subghz_sleep(subghz);
furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
}
return true; return true;
} else if(event.event == SubGhzCustomEventViewTransmitterBack) { } else if(event.event == SubGhzCustomEventViewTransmitterBack) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
@@ -113,6 +137,10 @@ void subghz_scene_transmitter_on_exit(void* context) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
keeloq_reset_mfname(); keeloq_reset_mfname();
keeloq_reset_kl_type(); keeloq_reset_kl_type();
keeloq_reset_original_btn();
alutech_reset_original_btn();
nice_flors_reset_original_btn();
somfy_telis_reset_original_btn();
star_line_reset_mfname(); star_line_reset_mfname();
star_line_reset_kl_type(); star_line_reset_kl_type();
} }

View File

@@ -88,6 +88,28 @@ void subghz_history_reset(SubGhzHistory* instance) {
instance->code_last_hash_data = 0; instance->code_last_hash_data = 0;
} }
void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) {
furi_assert(instance);
SubGhzHistoryItemArray_it_t it;
//SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id);
SubGhzHistoryItemArray_it_last(it, instance->history->data);
while(!SubGhzHistoryItemArray_end_p(it)) {
SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it);
if(it->index == (size_t)(item_id)) {
furi_string_free(item->item_str);
furi_string_free(item->preset->name);
free(item->preset);
flipper_format_free(item->flipper_string);
item->type = 0;
SubGhzHistoryItemArray_remove(instance->history->data, it);
}
SubGhzHistoryItemArray_previous(it);
}
instance->last_index_write--;
}
uint16_t subghz_history_get_item(SubGhzHistory* instance) { uint16_t subghz_history_get_item(SubGhzHistory* instance) {
furi_assert(instance); furi_assert(instance);
return instance->last_index_write; return instance->last_index_write;

View File

@@ -27,6 +27,8 @@ void subghz_history_free(SubGhzHistory* instance);
*/ */
void subghz_history_reset(SubGhzHistory* instance); void subghz_history_reset(SubGhzHistory* instance);
void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id);
/** Get frequency to history[idx] /** Get frequency to history[idx]
* *
* @param instance - SubGhzHistory instance * @param instance - SubGhzHistory instance

View File

@@ -431,6 +431,34 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
true); true);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) { } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context); subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);
} else if(event->key == InputKeyRight && event->type == InputTypeLong) {
with_view_model(
subghz_receiver->view,
SubGhzViewReceiverModel * model,
{
if(model->history_item != 0) {
SubGhzReceiverMenuItemArray_it_t it;
// SubGhzReceiverMenuItem* target_item =
// SubGhzReceiverMenuItemArray_get(model->history->data, model->idx);
SubGhzReceiverMenuItemArray_it_last(it, model->history->data);
while(!SubGhzReceiverMenuItemArray_end_p(it)) {
SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it);
if(it->index == (size_t)(model->idx)) {
furi_string_free(item->item_str);
item->type = 0;
SubGhzReceiverMenuItemArray_remove(model->history->data, it);
}
SubGhzReceiverMenuItemArray_previous(it);
}
// Callback
subghz_receiver->callback(
SubGhzCustomEventViewReceiverDeleteItem, subghz_receiver->context);
}
},
true);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) { } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model( with_view_model(
subghz_receiver->view, subghz_receiver->view,
@@ -544,12 +572,34 @@ View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {
uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) { uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver); furi_assert(subghz_receiver);
uint32_t idx = 0; uint16_t idx = 0;
with_view_model( with_view_model(
subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false); subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false);
return idx; return idx;
} }
void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view,
SubGhzViewReceiverModel * model,
{
if(model->history_item == 5) {
if(model->idx >= 2) {
model->idx = model->history_item - 1;
}
}
model->history_item--;
if(model->idx != 0) {
model->idx--;
}
},
true);
furi_delay_ms(200);
}
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
furi_assert(subghz_receiver); furi_assert(subghz_receiver);
with_view_model( with_view_model(

View File

@@ -46,4 +46,6 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver);
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx); void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx);
void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver);
void subghz_view_receiver_exit(void* context); void subghz_view_receiver_exit(void* context);

View File

@@ -4,6 +4,11 @@
#include <input/input.h> #include <input/input.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/alutech_at_4n.h>
#include <lib/subghz/protocols/nice_flor_s.h>
#include <lib/subghz/protocols/somfy_telis.h>
struct SubGhzViewTransmitter { struct SubGhzViewTransmitter {
View* view; View* view;
SubGhzViewTransmitterCallback callback; SubGhzViewTransmitterCallback callback;
@@ -15,6 +20,8 @@ typedef struct {
FuriString* preset_str; FuriString* preset_str;
FuriString* key_str; FuriString* key_str;
uint8_t show_button; uint8_t show_button;
FuriString* temp_button_id;
bool draw_temp_button;
} SubGhzViewTransmitterModel; } SubGhzViewTransmitterModel;
void subghz_view_transmitter_set_callback( void subghz_view_transmitter_set_callback(
@@ -89,6 +96,12 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo
canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
if(model->draw_temp_button) {
canvas_set_font(canvas, FontBatteryPercent);
canvas_draw_str(canvas, 117, 40, furi_string_get_cstr(model->temp_button_id));
canvas_set_font(canvas, FontSecondary);
}
if(model->show_button) { if(model->show_button) {
canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int"); canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int");
subghz_view_transmitter_button_right(canvas, "Send"); subghz_view_transmitter_button_right(canvas, "Send");
@@ -108,7 +121,9 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
furi_string_reset(model->frequency_str); furi_string_reset(model->frequency_str);
furi_string_reset(model->preset_str); furi_string_reset(model->preset_str);
furi_string_reset(model->key_str); furi_string_reset(model->key_str);
furi_string_reset(model->temp_button_id);
model->show_button = 0; model->show_button = 0;
model->draw_temp_button = false;
}, },
false); false);
return false; return false;
@@ -125,6 +140,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
true); true);
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
with_view_model(
subghz_transmitter->view,
SubGhzViewTransmitterModel * model,
{
furi_string_reset(model->temp_button_id);
model->draw_temp_button = false;
},
true);
subghz_transmitter->callback( subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true; return true;
@@ -134,6 +157,141 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
return true; return true;
} }
// Temp Buttons (UP)
if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) {
keeloq_set_btn(1);
alutech_set_btn(1);
nice_flors_set_btn(1);
somfy_telis_set_btn(1);
with_view_model(
subghz_transmitter->view,
SubGhzViewTransmitterModel * model,
{
furi_string_reset(model->temp_button_id);
if(keeloq_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
model->draw_temp_button = true;
} else if(alutech_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
model->draw_temp_button = true;
} else if(nice_flors_get_original_btn() != 0) {
furi_string_printf(
model->temp_button_id, "%01X", nice_flors_get_original_btn());
model->draw_temp_button = true;
} else if(somfy_telis_get_original_btn() != 0) {
furi_string_printf(
model->temp_button_id, "%01X", somfy_telis_get_original_btn());
model->draw_temp_button = true;
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyUp && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
// Down
if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) {
keeloq_set_btn(2);
alutech_set_btn(2);
nice_flors_set_btn(2);
somfy_telis_set_btn(2);
with_view_model(
subghz_transmitter->view,
SubGhzViewTransmitterModel * model,
{
furi_string_reset(model->temp_button_id);
if(keeloq_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
model->draw_temp_button = true;
} else if(alutech_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
model->draw_temp_button = true;
} else if(nice_flors_get_original_btn() != 0) {
furi_string_printf(
model->temp_button_id, "%01X", nice_flors_get_original_btn());
model->draw_temp_button = true;
} else if(somfy_telis_get_original_btn() != 0) {
furi_string_printf(
model->temp_button_id, "%01X", somfy_telis_get_original_btn());
model->draw_temp_button = true;
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyDown && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
// Left
if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) {
keeloq_set_btn(3);
alutech_set_btn(3);
nice_flors_set_btn(3);
somfy_telis_set_btn(3);
with_view_model(
subghz_transmitter->view,
SubGhzViewTransmitterModel * model,
{
furi_string_reset(model->temp_button_id);
if(keeloq_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
model->draw_temp_button = true;
} else if(alutech_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
model->draw_temp_button = true;
} else if(nice_flors_get_original_btn() != 0) {
furi_string_printf(
model->temp_button_id, "%01X", nice_flors_get_original_btn());
model->draw_temp_button = true;
} else if(somfy_telis_get_original_btn() != 0) {
furi_string_printf(
model->temp_button_id, "%01X", somfy_telis_get_original_btn());
model->draw_temp_button = true;
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
// Right
if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) {
keeloq_set_btn(4);
alutech_set_btn(4);
with_view_model(
subghz_transmitter->view,
SubGhzViewTransmitterModel * model,
{
furi_string_reset(model->temp_button_id);
if(keeloq_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
model->draw_temp_button = true;
} else if(alutech_get_original_btn() != 0) {
furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
model->draw_temp_button = true;
}
},
true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
return true; return true;
} }
@@ -166,6 +324,7 @@ SubGhzViewTransmitter* subghz_view_transmitter_alloc() {
model->frequency_str = furi_string_alloc(); model->frequency_str = furi_string_alloc();
model->preset_str = furi_string_alloc(); model->preset_str = furi_string_alloc();
model->key_str = furi_string_alloc(); model->key_str = furi_string_alloc();
model->temp_button_id = furi_string_alloc();
}, },
true); true);
return subghz_transmitter; return subghz_transmitter;
@@ -181,6 +340,7 @@ void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) {
furi_string_free(model->frequency_str); furi_string_free(model->frequency_str);
furi_string_free(model->preset_str); furi_string_free(model->preset_str);
furi_string_free(model->key_str); furi_string_free(model->key_str);
furi_string_free(model->temp_button_id);
}, },
true); true);
view_free(subghz_transmitter->view); view_free(subghz_transmitter->view);

View File

@@ -19,6 +19,9 @@
#include <lib/subghz/types.h> #include <lib/subghz/types.h>
#include <lib/subghz/protocols/keeloq.h> #include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/star_line.h> #include <lib/subghz/protocols/star_line.h>
#include <lib/subghz/protocols/alutech_at_4n.h>
#include <lib/subghz/protocols/nice_flor_s.h>
#include <lib/subghz/protocols/somfy_telis.h>
#define UNIRFMAP_FOLDER "/ext/subghz/unirf" #define UNIRFMAP_FOLDER "/ext/subghz/unirf"
#define UNIRFMAP_EXTENSION ".txt" #define UNIRFMAP_EXTENSION ".txt"
@@ -481,6 +484,10 @@ void unirfremix_tx_stop(UniRFRemix* app) {
keeloq_reset_mfname(); keeloq_reset_mfname();
keeloq_reset_kl_type(); keeloq_reset_kl_type();
keeloq_reset_original_btn();
alutech_reset_original_btn();
nice_flors_reset_original_btn();
somfy_telis_reset_original_btn();
star_line_reset_mfname(); star_line_reset_mfname();
star_line_reset_kl_type(); star_line_reset_kl_type();
} }

View File

@@ -39,7 +39,7 @@ void xtreme_app_scene_misc_on_enter(void* context) {
variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF");
item = variable_item_list_add( item = variable_item_list_add(
var_item_list, "Dark Mode (Exp.)", 2, xtreme_app_scene_misc_dark_mode_changed, app); var_item_list, "Exp. Dark Mode", 2, xtreme_app_scene_misc_dark_mode_changed, app);
variable_item_set_current_value_index(item, xtreme_settings->dark_mode); variable_item_set_current_value_index(item, xtreme_settings->dark_mode);
variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF");

View File

@@ -177,7 +177,7 @@ XtremeApp* xtreme_app_alloc() {
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
app->version_tag = furi_string_alloc_printf( app->version_tag = furi_string_alloc_printf(
"%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); "%s %s", version_get_version(NULL), version_get_builddate(NULL));
return app; return app;
} }

View File

@@ -290,10 +290,10 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) {
state->diceQty, state->diceQty,
state->diceType[0], state->diceType[0],
state->rollTime[0]); state->rollTime[0]);
if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect) // if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect)
DOLPHIN_DEED(getRandomDeed()); // DOLPHIN_DEED(getRandomDeed());
if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect - 1) // if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect - 1)
DOLPHIN_DEED(getRandomDeed()); // DOLPHIN_DEED(getRandomDeed());
if(state->diceQty == 1) { if(state->diceQty == 1) {
snprintf(state->strings[1], sizeof(state->strings[1]), "%d", state->diceRoll); snprintf(state->strings[1], sizeof(state->strings[1]), "%d", state->diceRoll);
} else if(state->diceQty == 2) { } else if(state->diceQty == 2) {

View File

@@ -1 +0,0 @@
## (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo)

View File

@@ -3,6 +3,7 @@
// Ported and modified by @xMasterX // Ported and modified by @xMasterX
#include <furi.h> #include <furi.h>
#include <furi_hal.h>
#include <furi_hal_power.h> #include <furi_hal_power.h>
#include <furi_hal_console.h> #include <furi_hal_console.h>
#include <gui/gui.h> #include <gui/gui.h>
@@ -26,7 +27,7 @@ typedef struct {
NotificationApp* notification; NotificationApp* notification;
bool have_5v; bool have_5v;
bool measurement_made; bool measurement_made;
uint32_t echo; // ms uint32_t echo; // us
float distance; // meters float distance; // meters
} PluginState; } PluginState;
@@ -72,7 +73,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
FuriString* str_buf; FuriString* str_buf;
str_buf = furi_string_alloc(); str_buf = furi_string_alloc();
furi_string_printf(str_buf, "Echo: %ld ms", plugin_state->echo); furi_string_printf(str_buf, "Echo: %ld us", plugin_state->echo);
canvas_draw_str_aligned( canvas_draw_str_aligned(
canvas, 8, 38, AlignLeft, AlignTop, furi_string_get_cstr(str_buf)); canvas, 8, 38, AlignLeft, AlignTop, furi_string_get_cstr(str_buf));
@@ -110,9 +111,11 @@ static void hc_sr04_state_init(PluginState* const plugin_state) {
} }
} }
float hc_sr04_ms_to_m(uint32_t ms) { float hc_sr04_us_to_m(uint32_t us) {
const float speed_sound_m_per_s = 343.0f; //speed of sound for 20°C, 50% relative humidity
const float time_s = ms / 1e3f; //331.3 + 20 * 0.606 + 50 * 0.0124 = 0.034404
const float speed_sound_m_per_s = 344.04f;
const float time_s = us / 1e6f;
const float total_dist = time_s * speed_sound_m_per_s; const float total_dist = time_s * speed_sound_m_per_s;
return total_dist / 2.0f; return total_dist / 2.0f;
} }
@@ -147,10 +150,6 @@ static void hc_sr04_measure(PluginState* const plugin_state) {
furi_delay_ms(10); furi_delay_ms(10);
furi_hal_gpio_write(&gpio_usart_tx, false); furi_hal_gpio_write(&gpio_usart_tx, false);
// TODO change from furi_get_tick(), which returns ms,
// to DWT->CYCCNT, which is a more precise counter with
// us precision (see furi_hal_cortex_delay_us)
const uint32_t start = furi_get_tick(); const uint32_t start = furi_get_tick();
while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx)) while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx))
@@ -158,16 +157,17 @@ static void hc_sr04_measure(PluginState* const plugin_state) {
while(furi_get_tick() - start < timeout_ms && !furi_hal_gpio_read(&gpio_usart_rx)) while(furi_get_tick() - start < timeout_ms && !furi_hal_gpio_read(&gpio_usart_rx))
; ;
const uint32_t pulse_start = furi_get_tick(); const uint32_t pulse_start = DWT->CYCCNT;
while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx)) while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx))
; ;
const uint32_t pulse_end = DWT->CYCCNT;
const uint32_t pulse_end = furi_get_tick();
//FURI_CRITICAL_EXIT(); //FURI_CRITICAL_EXIT();
plugin_state->echo = pulse_end - pulse_start; plugin_state->echo =
plugin_state->distance = hc_sr04_ms_to_m(pulse_end - pulse_start); (pulse_end - pulse_start) / furi_hal_cortex_instructions_per_microsecond();
plugin_state->distance = hc_sr04_us_to_m(plugin_state->echo);
plugin_state->measurement_made = true; plugin_state->measurement_made = true;
//furi_hal_light_set(LightRed, 0x00); //furi_hal_light_set(LightRed, 0x00);

View File

@@ -1,191 +0,0 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignArrayOfStructures: None
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 99
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: true
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 10
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: Never
SortJavaStaticImport: Before
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: Never
SpaceBeforeParensOptions:
AfterControlStatements: false
AfterForeachMacros: false
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: false
AfterOverloadedOperator: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: c++03
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

View File

@@ -1,11 +1,10 @@
# flipperzero-lightmeter # flipperzero-lightmeter
[![Build FAP](https://github.com/oleksiikutuzov/flipperzero-lightmeter/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/oleksiikutuzov/flipperzero-lightmeter/actions/workflows/build.yml) [Original link](https://github.com/oleksiikutuzov/flipperzero-lightmeter)
<img src="images/framed_gui_main.png" width="500px"> <img src="images/framed_gui_main.png" width="500px">
<img src="images/framed_gui_lux_meter.png" width="500px">
## Wiring ## Wiring
``` ```
@@ -15,19 +14,6 @@ SCL -> C0
SDA -> C1 SDA -> C1
``` ```
## Sensor module
<img src="module/back.jpg" width="500px">
### If you want to build this module, you'll need (it's quite over-engineered, sorry :D)
1. [Module PCB](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_gerber.zip)
2. [Enclosure](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_enclosure.stl)
3. 4-pin female header
4. 10-pin male header
5. 2x M3 threaded inserts (max diameter 5.3 mm, max height 4 mm)
6. 2x M3x5 screws
## TODO ## TODO
- [ ] Save settings to sd card - [ ] Save settings to sd card

View File

@@ -9,7 +9,6 @@ App(
], ],
stack_size=1 * 1024, stack_size=1 * 1024,
order=90, order=90,
fap_version=(0, 7),
fap_icon="lightmeter.png", fap_icon="lightmeter.png",
fap_category="GPIO", fap_category="GPIO",
fap_private_libs=[ fap_private_libs=[
@@ -22,7 +21,4 @@ App(
), ),
], ],
fap_icon_assets="icons", fap_icon_assets="icons",
fap_description="Lightmeter app for photography based on BH1750 sensor",
fap_author="Oleksii Kutuzov",
fap_weburl="https://github.com/oleksiikutuzov/flipperzero-lightmeter",
) )

View File

@@ -62,11 +62,11 @@ const float aperture_numbers[] = {
const float speed_numbers[] = { const float speed_numbers[] = {
[SPEED_8000] = 1.0 / 8000, [SPEED_4000] = 1.0 / 4000, [SPEED_2000] = 1.0 / 2000, [SPEED_8000] = 1.0 / 8000, [SPEED_4000] = 1.0 / 4000, [SPEED_2000] = 1.0 / 2000,
[SPEED_1000] = 1.0 / 1000, [SPEED_500] = 1.0 / 500, [SPEED_250] = 1.0 / 250, [SPEED_1000] = 1.0 / 1000, [SPEED_500] = 1.0 / 500, [SPEED_250] = 1.0 / 250,
[SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_30] = 1.0 / 30, [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_48] = 1.0 / 48,
[SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8, [SPEED_4] = 1.0 / 4, [SPEED_30] = 1.0 / 30, [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8,
[SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0, [SPEED_2S] = 2.0, [SPEED_4] = 1.0 / 4, [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0,
[SPEED_4S] = 4.0, [SPEED_8S] = 8.0, [SPEED_15S] = 15.0, [SPEED_2S] = 2.0, [SPEED_4S] = 4.0, [SPEED_8S] = 8.0,
[SPEED_30S] = 30.0, [SPEED_15S] = 15.0, [SPEED_30S] = 30.0,
}; };
struct MainView { struct MainView {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -15,6 +15,7 @@
BH1750_mode bh1750_mode = BH1750_DEFAULT_MODE; // Current sensor mode BH1750_mode bh1750_mode = BH1750_DEFAULT_MODE; // Current sensor mode
uint8_t bh1750_mt_reg = BH1750_DEFAULT_MTREG; // Current MT register value uint8_t bh1750_mt_reg = BH1750_DEFAULT_MTREG; // Current MT register value
uint8_t bh1750_addr = BH1750_ADDRESS;
BH1750_STATUS bh1750_init() { BH1750_STATUS bh1750_init() {
if(BH1750_OK == bh1750_reset()) { if(BH1750_OK == bh1750_reset()) {
@@ -25,12 +26,17 @@ BH1750_STATUS bh1750_init() {
return BH1750_ERROR; return BH1750_ERROR;
} }
BH1750_STATUS bh1750_init_with_addr(uint8_t addr) {
bh1750_addr = (addr << 1);
return bh1750_init();
}
BH1750_STATUS bh1750_reset() { BH1750_STATUS bh1750_reset() {
uint8_t command = 0x07; uint8_t command = 0x07;
bool status; bool status;
furi_hal_i2c_acquire(I2C_BUS); furi_hal_i2c_acquire(I2C_BUS);
status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &command, 1, I2C_TIMEOUT); status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &command, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS); furi_hal_i2c_release(I2C_BUS);
if(status) { if(status) {
@@ -45,7 +51,7 @@ BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn) {
bool status; bool status;
furi_hal_i2c_acquire(I2C_BUS); furi_hal_i2c_acquire(I2C_BUS);
status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &PowerOn, 1, I2C_TIMEOUT); status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &PowerOn, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS); furi_hal_i2c_release(I2C_BUS);
if(status) { if(status) {
@@ -69,7 +75,7 @@ BH1750_STATUS bh1750_set_mode(BH1750_mode mode) {
bh1750_mode = mode; bh1750_mode = mode;
furi_hal_i2c_acquire(I2C_BUS); furi_hal_i2c_acquire(I2C_BUS);
status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &mode, 1, I2C_TIMEOUT); status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &mode, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS); furi_hal_i2c_release(I2C_BUS);
if(status) { if(status) {
@@ -93,14 +99,14 @@ BH1750_STATUS bh1750_set_mt_reg(uint8_t mt_reg) {
tmp[1] = (0x60 | (mt_reg & 0x1F)); tmp[1] = (0x60 | (mt_reg & 0x1F));
furi_hal_i2c_acquire(I2C_BUS); furi_hal_i2c_acquire(I2C_BUS);
status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[0], 1, I2C_TIMEOUT); status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[0], 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS); furi_hal_i2c_release(I2C_BUS);
if(!status) { if(!status) {
return BH1750_ERROR; return BH1750_ERROR;
} }
furi_hal_i2c_acquire(I2C_BUS); furi_hal_i2c_acquire(I2C_BUS);
status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[1], 1, I2C_TIMEOUT); status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[1], 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS); furi_hal_i2c_release(I2C_BUS);
if(status) { if(status) {
return BH1750_OK; return BH1750_OK;
@@ -122,7 +128,7 @@ BH1750_STATUS bh1750_read_light(float* result) {
bool status; bool status;
furi_hal_i2c_acquire(I2C_BUS); furi_hal_i2c_acquire(I2C_BUS);
status = furi_hal_i2c_rx(I2C_BUS, BH1750_ADDRESS, rcv, 2, I2C_TIMEOUT); status = furi_hal_i2c_rx(I2C_BUS, bh1750_addr, rcv, 2, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS); furi_hal_i2c_release(I2C_BUS);
if(status) { if(status) {

View File

@@ -49,6 +49,13 @@ typedef enum {
*/ */
BH1750_STATUS bh1750_init(); BH1750_STATUS bh1750_init();
/**
* @brief Change the I2C device address and then initialize the sensor.
*
* @return BH1750_STATUS
*/
BH1750_STATUS bh1750_init_with_addr(uint8_t addr);
/** /**
* @brief Reset all registers to the default value. * @brief Reset all registers to the default value.
* *

View File

@@ -79,6 +79,7 @@ typedef enum {
SPEED_250, SPEED_250,
SPEED_125, SPEED_125,
SPEED_60, SPEED_60,
SPEED_48,
SPEED_30, SPEED_30,
SPEED_15, SPEED_15,
SPEED_8, SPEED_8,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

View File

@@ -1,5 +1,7 @@
# Metronome # Metronome
[Original link](https://github.com/panki27/Metronome)
A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along perfectly with my [BPM tapper](https://github.com/panki27/bpm-tapper). A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along perfectly with my [BPM tapper](https://github.com/panki27/bpm-tapper).
![screenshot](img/screenshot.png) ![screenshot](img/screenshot.png)
@@ -17,5 +19,5 @@ A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along
## Compiling ## Compiling
``` ```
./fbt fap_metronome ./fbt firmware_metronome
``` ```

View File

@@ -8,8 +8,8 @@ App(
"gui", "gui",
], ],
fap_icon="metronome_icon.png", fap_icon="metronome_icon.png",
fap_icon_assets="icons",
fap_category="Music", fap_category="Music",
fap_icon_assets="images",
stack_size=2 * 1024, stack_size=2 * 1024,
order=20, order=20,
) )

View File

@@ -1,6 +1,6 @@
#include <gui/canvas.h> #include <gui/canvas.h>
#include <gui/icon_i.h> #include <gui/icon_i.h>
#include "Metronome_icons.h" #include <Metronome_icons.h>
//lib can only do bottom left/right //lib can only do bottom left/right
void elements_button_top_left(Canvas* canvas, const char* str) { void elements_button_top_left(Canvas* canvas, const char* str) {

View File

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 102 B

View File

@@ -287,7 +287,6 @@ int32_t metronome_app() {
metronome_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, &state_mutex); metronome_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, &state_mutex);
// Open GUI and register view_port // Open GUI and register view_port
//
Gui* gui = furi_record_open("gui"); Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen); gui_add_view_port(gui, view_port, GuiLayerFullscreen);
@@ -326,7 +325,7 @@ int32_t metronome_app() {
case InputKeyBack: case InputKeyBack:
processing = false; processing = false;
break; break;
case InputKeyMAX: default:
break; break;
} }
} else if(event.input.type == InputTypeLong) { } else if(event.input.type == InputTypeLong) {
@@ -348,7 +347,7 @@ int32_t metronome_app() {
case InputKeyBack: case InputKeyBack:
processing = false; processing = false;
break; break;
case InputKeyMAX: default:
break; break;
} }
} else if(event.input.type == InputTypeRepeat) { } else if(event.input.type == InputTypeRepeat) {
@@ -369,7 +368,7 @@ int32_t metronome_app() {
case InputKeyBack: case InputKeyBack:
processing = false; processing = false;
break; break;
case InputKeyMAX: default:
break; break;
} }
} }

View File

@@ -1,62 +0,0 @@
# flipperzero-nrf24
An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional.
## Warning
This repo contains two Flipper Zero apps that utilize the NRF24 driver to sniff for NRF24 addresses and perform mousejack attacks. These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment.
## Acknowledgments
The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html).
The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details.
Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html).
Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit).
## PinOut from from NoComp/Frog
<img src="https://media.discordapp.net/attachments/937479784726949900/994495234618687509/unknown.png?width=567&height=634">
# Mousejack / NRF24 pinout by UberGuidoZ
2/A7 on FZ goes to MOSI/6 on nrf24l01<br>
3/A6 on FZ goes to MISO/7 on nrf24l01<br>
4/A4 on FZ goes to CSN/4 on nrf24l01<br>
5/B3 on FZ goes to SCK/5 on nrf24l01<br>
6/B2 on FZ goes to CE/3 on nrf24l01<br>
8/GND on FZ goes to GND/1 on nrf24l01<br>
9/3V3 on FZ goes to VCC/2 on nrf24l01<br>
IRQ/8 is left disconnected on nrf24l01
![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png)
If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines! I've not tried the Plus model so it may have a bigger need for a cap. Otherwise, I haven't had any major issues. Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.) I learned if you wanna get fancy, include a 0.1 uF cap in parallel. The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯
![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg)
Selfmade NRF24 breakoutboard:
![NRF_soldered](https://user-images.githubusercontent.com/22019133/183419103-9c40b28b-8152-4212-98e0-9a3358f69518.jpeg)
![NRF_soldered2](https://user-images.githubusercontent.com/22019133/183419176-26c0359b-0ecb-4015-8f8b-3a75209502ba.jpeg)
## Practical hints from einstein2150
If you are not successfull with the NRF Sniff Plugin you can try to get the MAC of the device with the crazyradio pa USB dongle. Have a look here: https://github.com/SySS-Research/nrf24-playset
A sample output of the NRF-Research script could be like
```
[2022-08-05 13:28:56.366] Found nRF24 device with address 38:24:93:C0:07 on channel 75\
[2022-08-05 13:28:56.371] Checking communication\
[2022-08-05 13:28:59.088] Scan for nRF24 device\
[2022-08-05 13:28:59.097] Actively searching for address 07:C0:93:24:38\
```
Be carefull with the byte-order using in mousejacker! The correct byte order is the reverse-byteorder. In my example the reversed one is ```07:C0:93:24:38``` for my Logitech R400 presenter.
The next thing which could make trouble is the datarate. In my case the Logitech-device is only responding at 2Mbit. Commands at 1Mbit are not detected.
Now its time to write all the relevant information in the config file. In my case you are creating a file ```addresses.txt``` in the SD directory of ```/nrfsniff```. The file content corresponding to the results of the research is ```07C0932438,2``` representing the reversed byte order of the MAC and the comma-separated datarate.
Start the Mouse Jacker plugin and select the prepared ```addresses.txt```. If everything is alright you are starting with your MAC ready for attack:
![mousejacker start-screen](https://user-images.githubusercontent.com/22019133/183419533-ef5c8df1-e328-45e3-b85d-376513d69c82.jpeg)
If you have troube you can check if a datarate of 1 Mbit will help. Change ```07C0932438,2``` to ```07C0932438,1``` in this case. Another problem in the practical use is electromagnetical noise. In my case the external USB-3.1-Hub creating massive noise in the 2,4 Ghz-Frequency around a distance of 5 cm. Try using a long USB connection-cable for the receiver. In my case the response of the attack raises significant because the signals of the NRF-breakoutboard is no more covered with noise.

View File

@@ -9,7 +9,7 @@ App(
"dialogs", "dialogs",
], ],
stack_size=2 * 1024, stack_size=2 * 1024,
order=50, order=60,
fap_icon="mouse_10px.png", fap_icon="mouse_10px.png",
fap_category="GPIO", fap_category="GPIO",
fap_icon_assets="images", fap_icon_assets="images",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

View File

@@ -184,7 +184,7 @@ static bool process_ducky_file(
mj_process_ducky_script( mj_process_ducky_script(
nrf24_HANDLE, addr, addr_size, rate, (char*)file_buf, plugin_state); nrf24_HANDLE, addr, addr_size, rate, (char*)file_buf, plugin_state);
FURI_LOG_D(TAG, "finished execution"); FURI_LOG_D(TAG, "finished execution");
DOLPHIN_DEED(getRandomDeed()); // DOLPHIN_DEED(getRandomDeed());
loaded = true; loaded = true;
} else { } else {
FURI_LOG_D(TAG, "load failed. file size: %d", file_size); FURI_LOG_D(TAG, "load failed. file size: %d", file_size);

View File

@@ -3,6 +3,7 @@
static const char ducky_cmd_comment[] = {"REM"}; static const char ducky_cmd_comment[] = {"REM"};
static const char ducky_cmd_delay[] = {"DELAY "}; static const char ducky_cmd_delay[] = {"DELAY "};
static const char ducky_cmd_string[] = {"STRING "}; static const char ducky_cmd_string[] = {"STRING "};
static const char ducky_cmd_altstring[] = {"ALTSTRING "};
static const char ducky_cmd_repeat[] = {"REPEAT "}; static const char ducky_cmd_repeat[] = {"REPEAT "};
static uint8_t LOGITECH_HID_TEMPLATE[] = static uint8_t LOGITECH_HID_TEMPLATE[] =
@@ -11,6 +12,10 @@ static uint8_t LOGITECH_HELLO[] = {0x00, 0x4F, 0x00, 0x04, 0xB0, 0x10, 0x00, 0x0
static uint8_t LOGITECH_KEEPALIVE[] = {0x00, 0x40, 0x00, 0x55, 0x6B}; static uint8_t LOGITECH_KEEPALIVE[] = {0x00, 0x40, 0x00, 0x55, 0x6B};
uint8_t prev_hid = 0; uint8_t prev_hid = 0;
static bool holding_ctrl = false;
static bool holding_shift = false;
static bool holding_alt = false;
static bool holding_gui = false;
#define RT_THRESHOLD 50 #define RT_THRESHOLD 50
#define LOGITECH_MIN_CHANNEL 2 #define LOGITECH_MIN_CHANNEL 2
@@ -65,7 +70,10 @@ MJDuckyKey mj_ducky_keys[] = {{" ", 44, 0}, {"!", 30, 2}, {"\""
{"LEFTARROW", 80, 0}, {"RIGHTARROW", 79, 0}, {"PAGEDOWN", 78, 0}, {"LEFTARROW", 80, 0}, {"RIGHTARROW", 79, 0}, {"PAGEDOWN", 78, 0},
{"PAUSE", 72, 0}, {"SPACE", 44, 0}, {"UPARROW", 82, 0}, {"PAUSE", 72, 0}, {"SPACE", 44, 0}, {"UPARROW", 82, 0},
{"F11", 68, 0}, {"F7", 64, 0}, {"UP", 82, 0}, {"F11", 68, 0}, {"F7", 64, 0}, {"UP", 82, 0},
{"LEFT", 80, 0}}; {"LEFT", 80, 0}, {"NUM 1", 89, 0}, {"NUM 2", 90, 0},
{"NUM 3", 91, 0}, {"NUM 4", 92, 0}, {"NUM 5", 93, 0},
{"NUM 6", 94, 0}, {"NUM 7", 95, 0}, {"NUM 8", 96, 0},
{"NUM 9", 97, 0}, {"NUM 0", 98, 0}};
/* /*
static bool mj_ducky_get_number(const char* param, uint32_t* val) { static bool mj_ducky_get_number(const char* param, uint32_t* val) {
@@ -89,7 +97,8 @@ static uint32_t mj_ducky_get_command_len(const char* line) {
static bool mj_get_ducky_key(char* key, size_t keylen, MJDuckyKey* dk) { static bool mj_get_ducky_key(char* key, size_t keylen, MJDuckyKey* dk) {
//FURI_LOG_D(TAG, "looking up key %s with length %d", key, keylen); //FURI_LOG_D(TAG, "looking up key %s with length %d", key, keylen);
for(uint i = 0; i < sizeof(mj_ducky_keys) / sizeof(MJDuckyKey); i++) { for(uint i = 0; i < sizeof(mj_ducky_keys) / sizeof(MJDuckyKey); i++) {
if(!strncmp(mj_ducky_keys[i].name, key, keylen)) { if(strlen(mj_ducky_keys[i].name) == keylen &&
!strncmp(mj_ducky_keys[i].name, key, keylen)) {
memcpy(dk, &mj_ducky_keys[i], sizeof(MJDuckyKey)); memcpy(dk, &mj_ducky_keys[i], sizeof(MJDuckyKey));
return true; return true;
} }
@@ -152,6 +161,30 @@ static void build_hid_packet(uint8_t mod, uint8_t hid, uint8_t* payload) {
checksum(payload, LOGITECH_HID_TEMPLATE_SIZE); checksum(payload, LOGITECH_HID_TEMPLATE_SIZE);
} }
static void release_key(
FuriHalSpiBusHandle* handle,
uint8_t* addr,
uint8_t addr_size,
uint8_t rate,
PluginState* plugin_state) {
// This function release keys currently pressed, but keep pressing special keys
// if holding mod keys variable are set to true
uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
build_hid_packet(
0 | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3,
0,
hid_payload);
inject_packet(
handle,
addr,
addr_size,
rate,
hid_payload,
LOGITECH_HID_TEMPLATE_SIZE,
plugin_state); // empty hid packet
}
static void send_hid_packet( static void send_hid_packet(
FuriHalSpiBusHandle* handle, FuriHalSpiBusHandle* handle,
uint8_t* addr, uint8_t* addr,
@@ -161,24 +194,22 @@ static void send_hid_packet(
uint8_t hid, uint8_t hid,
PluginState* plugin_state) { PluginState* plugin_state) {
uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0}; uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
build_hid_packet(0, 0, hid_payload); if(hid == prev_hid) release_key(handle, addr, addr_size, rate, plugin_state);
if(hid == prev_hid)
inject_packet(
handle,
addr,
addr_size,
rate,
hid_payload,
LOGITECH_HID_TEMPLATE_SIZE,
plugin_state); // empty hid packet
prev_hid = hid; prev_hid = hid;
build_hid_packet(mod, hid, hid_payload); build_hid_packet(
mod | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3,
hid,
hid_payload);
inject_packet( inject_packet(
handle, addr, addr_size, rate, hid_payload, LOGITECH_HID_TEMPLATE_SIZE, plugin_state); handle, addr, addr_size, rate, hid_payload, LOGITECH_HID_TEMPLATE_SIZE, plugin_state);
furi_delay_ms(12); furi_delay_ms(12);
} }
static bool ducky_end_line(const char chr) {
return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
}
// returns false if there was an error processing script line // returns false if there was an error processing script line
static bool mj_process_ducky_line( static bool mj_process_ducky_line(
FuriHalSpiBusHandle* handle, FuriHalSpiBusHandle* handle,
@@ -251,6 +282,32 @@ static bool mj_process_ducky_line(
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
} }
return true;
} else if(strncmp(line_tmp, ducky_cmd_altstring, strlen(ducky_cmd_altstring)) == 0) {
// ALTSTRING
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
for(size_t i = 0; i < strlen(line_tmp); i++) {
if((line_tmp[i] < ' ') || (line_tmp[i] > '~')) {
continue; // Skip non-printable chars
}
char alt_code[4];
// Getting altcode of the char
snprintf(alt_code, 4, "%u", line_tmp[i]);
uint8_t j = 0;
while(!ducky_end_line(alt_code[j])) {
char pad_num[5] = {'N', 'U', 'M', ' ', alt_code[j]};
if(!mj_get_ducky_key(pad_num, 5, &dk)) return false;
holding_alt = true;
FURI_LOG_D(TAG, "Sending %s", pad_num);
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
j++;
}
holding_alt = false;
release_key(handle, addr, addr_size, rate, plugin_state);
}
return true; return true;
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
// REPEAT // REPEAT
@@ -269,7 +326,9 @@ static bool mj_process_ducky_line(
} else if(strncmp(line_tmp, "ALT", strlen("ALT")) == 0) { } else if(strncmp(line_tmp, "ALT", strlen("ALT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4, dk.hid, plugin_state); holding_alt = true;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
holding_alt = false;
return true; return true;
} else if( } else if(
strncmp(line_tmp, "GUI", strlen("GUI")) == 0 || strncmp(line_tmp, "GUI", strlen("GUI")) == 0 ||
@@ -277,33 +336,47 @@ static bool mj_process_ducky_line(
strncmp(line_tmp, "COMMAND", strlen("COMMAND")) == 0) { strncmp(line_tmp, "COMMAND", strlen("COMMAND")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 8, dk.hid, plugin_state); holding_gui = true;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
holding_gui = false;
return true; return true;
} else if( } else if(
strncmp(line_tmp, "CTRL-ALT", strlen("CTRL-ALT")) == 0 || strncmp(line_tmp, "CTRL-ALT", strlen("CTRL-ALT")) == 0 ||
strncmp(line_tmp, "CONTROL-ALT", strlen("CONTROL-ALT")) == 0) { strncmp(line_tmp, "CONTROL-ALT", strlen("CONTROL-ALT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 1, dk.hid, plugin_state); holding_ctrl = true;
holding_alt = true;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
holding_ctrl = false;
holding_alt = false;
return true; return true;
} else if( } else if(
strncmp(line_tmp, "CTRL-SHIFT", strlen("CTRL-SHIFT")) == 0 || strncmp(line_tmp, "CTRL-SHIFT", strlen("CTRL-SHIFT")) == 0 ||
strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) { strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state); holding_ctrl = true;
holding_shift = true;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
holding_ctrl = false;
holding_shift = false;
return true; return true;
} else if( } else if(
strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 || strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 ||
strncmp(line_tmp, "CONTROL", strlen("CONTROL")) == 0) { strncmp(line_tmp, "CONTROL", strlen("CONTROL")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1, dk.hid, plugin_state); holding_ctrl = true;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
holding_ctrl = false;
return true; return true;
} else if(strncmp(line_tmp, "SHIFT", strlen("SHIFT")) == 0) { } else if(strncmp(line_tmp, "SHIFT", strlen("SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 2, dk.hid, plugin_state); holding_shift = true;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
holding_shift = false;
return true; return true;
} else if( } else if(
strncmp(line_tmp, "ESC", strlen("ESC")) == 0 || strncmp(line_tmp, "ESC", strlen("ESC")) == 0 ||
@@ -344,6 +417,10 @@ static bool mj_process_ducky_line(
if(!mj_get_ducky_key("SPACE", 5, &dk)) return false; if(!mj_get_ducky_key("SPACE", 5, &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
return true; return true;
} else if(strncmp(line_tmp, "TAB", strlen("TAB")) == 0) {
if(!mj_get_ducky_key("TAB", 3, &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
return true;
} }
return false; return false;

View File

@@ -291,7 +291,7 @@ static void wrap_up(Storage* storage, NotificationApp* notification) {
hexlify(addr, 5, top_address); hexlify(addr, 5, top_address);
found_count++; found_count++;
save_addr_to_file(storage, addr, 5, notification); save_addr_to_file(storage, addr, 5, notification);
DOLPHIN_DEED(getRandomDeed()); // DOLPHIN_DEED(getRandomDeed());
if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5); if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5);
break; break;
} }

View File

@@ -1,6 +1,6 @@
App( App(
appid="Picopass", appid="Picopass",
name="PicoPass Reader", name="PicoPass",
apptype=FlipperAppType.EXTERNAL, apptype=FlipperAppType.EXTERNAL,
targets=["f7"], targets=["f7"],
entry_point="picopass_app", entry_point="picopass_app",
@@ -9,7 +9,7 @@ App(
"gui", "gui",
], ],
stack_size=4 * 1024, stack_size=4 * 1024,
order=175, order=30,
fap_icon="125_10px.png", fap_icon="125_10px.png",
fap_category="Tools", fap_category="Tools",
fap_libs=["mbedtls"], fap_libs=["mbedtls"],

View File

@@ -137,9 +137,9 @@ void picopass_text_store_clear(Picopass* picopass) {
memset(picopass->text_store, 0, sizeof(picopass->text_store)); memset(picopass->text_store, 0, sizeof(picopass->text_store));
} }
static const NotificationSequence picopass_sequence_blink_start_blue = { static const NotificationSequence picopass_sequence_blink_start_cyan = {
&message_blink_start_10, &message_blink_start_10,
&message_blink_set_color_blue, &message_blink_set_color_cyan,
&message_do_not_reset, &message_do_not_reset,
NULL, NULL,
}; };
@@ -150,7 +150,7 @@ static const NotificationSequence picopass_sequence_blink_stop = {
}; };
void picopass_blink_start(Picopass* picopass) { void picopass_blink_start(Picopass* picopass) {
notification_message(picopass->notifications, &picopass_sequence_blink_start_blue); notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan);
} }
void picopass_blink_stop(Picopass* picopass) { void picopass_blink_stop(Picopass* picopass) {
@@ -171,6 +171,16 @@ void picopass_show_loading_popup(void* context, bool show) {
} }
} }
bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size) {
bool result = size > 0;
while(size > 0) {
result &= (*data == pattern);
data++;
size--;
}
return result;
}
int32_t picopass_app(void* p) { int32_t picopass_app(void* p) {
UNUSED(p); UNUSED(p);
Picopass* picopass = picopass_alloc(); Picopass* picopass = picopass_alloc();

View File

@@ -368,7 +368,7 @@ ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* r
record->CardNumber = (bot >> 1) & 0xFFFF; record->CardNumber = (bot >> 1) & 0xFFFF;
record->FacilityCode = (bot >> 17) & 0xFF; record->FacilityCode = (bot >> 17) & 0xFF;
FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber);
record->valid = true; record->valid = true;
} else { } else {
record->CardNumber = 0; record->CardNumber = 0;

View File

@@ -22,6 +22,7 @@
#define PICOPASS_KD_BLOCK_INDEX 3 #define PICOPASS_KD_BLOCK_INDEX 3
#define PICOPASS_KC_BLOCK_INDEX 4 #define PICOPASS_KC_BLOCK_INDEX 4
#define PICOPASS_AIA_BLOCK_INDEX 5 #define PICOPASS_AIA_BLOCK_INDEX 5
#define PICOPASS_PACS_CFG_BLOCK_INDEX 6
#define PICOPASS_APP_FOLDER ANY_PATH("picopass") #define PICOPASS_APP_FOLDER ANY_PATH("picopass")
#define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_EXTENSION ".picopass"

View File

@@ -81,3 +81,15 @@ void picopass_blink_start(Picopass* picopass);
void picopass_blink_stop(Picopass* picopass); void picopass_blink_stop(Picopass* picopass);
void picopass_show_loading_popup(void* context, bool show); void picopass_show_loading_popup(void* context, bool show);
/** Check if memory is set to pattern
*
* @warning zero size will return false
*
* @param[in] data Pointer to the byte array
* @param[in] pattern The pattern
* @param[in] size The byte array size
*
* @return True if memory is set to pattern, false otherwise
*/
bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size);

View File

@@ -5,7 +5,8 @@
#define TAG "PicopassWorker" #define TAG "PicopassWorker"
const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87};
static void picopass_worker_enable_field() { static void picopass_worker_enable_field() {
furi_hal_nfc_ll_txrx_on(); furi_hal_nfc_ll_txrx_on();
@@ -197,6 +198,28 @@ static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) {
return rfalPicoPassPollerCheck(mac, &chkRes); return rfalPicoPassPollerCheck(mac, &chkRes);
} }
static ReturnCode picopass_auth_factory(uint8_t* csn, uint8_t* div_key) {
rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes;
ReturnCode err;
uint8_t mac[4] = {0};
uint8_t ccnr[12] = {0};
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_diversifyKey(csn, picopass_factory_debit_key, div_key);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
return rfalPicoPassPollerCheck(mac, &chkRes);
}
static ReturnCode picopass_auth_dict( static ReturnCode picopass_auth_dict(
uint8_t* csn, uint8_t* csn,
PicopassPacs* pacs, PicopassPacs* pacs,
@@ -264,14 +287,23 @@ static ReturnCode picopass_auth_dict(
ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
ReturnCode err; ReturnCode err;
FURI_LOG_E(TAG, "Trying standard legacy key"); FURI_LOG_I(TAG, "Trying standard legacy key");
err = picopass_auth_standard( err = picopass_auth_standard(
AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data); AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data);
if(err == ERR_NONE) { if(err == ERR_NONE) {
memcpy(pacs->key, picopass_iclass_key, PICOPASS_BLOCK_LEN);
return ERR_NONE; return ERR_NONE;
} }
FURI_LOG_E(TAG, "Starting user dictionary attack"); FURI_LOG_I(TAG, "Trying factory default key");
err = picopass_auth_factory(
AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data);
if(err == ERR_NONE) {
memcpy(pacs->key, picopass_factory_debit_key, PICOPASS_BLOCK_LEN);
return ERR_NONE;
}
FURI_LOG_I(TAG, "Starting user dictionary attack");
err = picopass_auth_dict( err = picopass_auth_dict(
AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_CSN_BLOCK_INDEX].data,
pacs, pacs,
@@ -281,7 +313,7 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
return ERR_NONE; return ERR_NONE;
} }
FURI_LOG_E(TAG, "Starting in-built dictionary attack"); FURI_LOG_I(TAG, "Starting system dictionary attack");
err = picopass_auth_dict( err = picopass_auth_dict(
AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_CSN_BLOCK_INDEX].data,
pacs, pacs,
@@ -406,6 +438,84 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) {
return ERR_NONE; return ERR_NONE;
} }
ReturnCode picopass_write_block(PicopassPacs* pacs, uint8_t blockNo, uint8_t* newBlock) {
rfalPicoPassIdentifyRes idRes;
rfalPicoPassSelectRes selRes;
rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes;
ReturnCode err;
uint8_t div_key[8] = {0};
uint8_t mac[4] = {0};
uint8_t ccnr[12] = {0};
err = rfalPicoPassPollerIdentify(&idRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
return err;
}
err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
return err;
}
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_diversifyKey(selRes.CSN, pacs->key, div_key);
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;
}
FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", blockNo);
uint8_t data[9] = {
blockNo,
newBlock[0],
newBlock[1],
newBlock[2],
newBlock[3],
newBlock[4],
newBlock[5],
newBlock[6],
newBlock[7]};
loclass_doMAC_N(data, sizeof(data), div_key, mac);
FURI_LOG_D(
TAG,
"loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
blockNo,
data[1],
data[2],
data[3],
data[4],
data[5],
data[6],
data[7],
data[8],
mac[0],
mac[1],
mac[2],
mac[3]);
err = rfalPicoPassPollerWriteBlock(data[0], data + 1, mac);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
return err;
}
return ERR_NONE;
}
int32_t picopass_worker_task(void* context) { int32_t picopass_worker_task(void* context) {
PicopassWorker* picopass_worker = context; PicopassWorker* picopass_worker = context;
@@ -414,6 +524,8 @@ int32_t picopass_worker_task(void* context) {
picopass_worker_detect(picopass_worker); picopass_worker_detect(picopass_worker);
} else if(picopass_worker->state == PicopassWorkerStateWrite) { } else if(picopass_worker->state == PicopassWorkerStateWrite) {
picopass_worker_write(picopass_worker); picopass_worker_write(picopass_worker);
} else if(picopass_worker->state == PicopassWorkerStateWriteStandardKey) {
picopass_worker_write_standard_key(picopass_worker);
} }
picopass_worker_disable_field(ERR_NONE); picopass_worker_disable_field(ERR_NONE);
@@ -448,7 +560,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
} }
// Thank you proxmark! // Thank you proxmark!
pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8);
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
if(pacs->se_enabled) { if(pacs->se_enabled) {
FURI_LOG_D(TAG, "SE enabled"); FURI_LOG_D(TAG, "SE enabled");
@@ -520,3 +632,46 @@ void picopass_worker_write(PicopassWorker* picopass_worker) {
furi_delay_ms(100); furi_delay_ms(100);
} }
} }
void picopass_worker_write_standard_key(PicopassWorker* picopass_worker) {
PicopassDeviceData* dev_data = picopass_worker->dev_data;
PicopassBlock* AA1 = dev_data->AA1;
PicopassPacs* pacs = &dev_data->pacs;
ReturnCode err;
PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data;
uint8_t fuses = configBlock[7];
uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data;
uint8_t newKey[PICOPASS_BLOCK_LEN] = {0};
loclass_diversifyKey(csn, picopass_iclass_key, newKey);
if((fuses & 0x80) == 0x80) {
FURI_LOG_D(TAG, "Plain write for personalized mode key change");
} else {
FURI_LOG_D(TAG, "XOR write for application mode key change");
// XOR when in application mode
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
newKey[i] ^= oldKey[i];
}
}
while(picopass_worker->state == PicopassWorkerStateWriteStandardKey) {
if(picopass_detect_card(1000) == ERR_NONE) {
err = picopass_write_block(pacs, PICOPASS_KD_BLOCK_INDEX, newKey);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_write_block error %d", err);
nextState = PicopassWorkerEventFail;
}
// Notify caller and exit
if(picopass_worker->callback) {
picopass_worker->callback(nextState, picopass_worker->context);
}
break;
}
furi_delay_ms(100);
}
}

View File

@@ -12,6 +12,7 @@ typedef enum {
// Main worker states // Main worker states
PicopassWorkerStateDetect, PicopassWorkerStateDetect,
PicopassWorkerStateWrite, PicopassWorkerStateWrite,
PicopassWorkerStateWriteStandardKey,
// Transition // Transition
PicopassWorkerStateStop, PicopassWorkerStateStop,
} PicopassWorkerState; } PicopassWorkerState;

View File

@@ -31,3 +31,4 @@ int32_t picopass_worker_task(void* context);
void picopass_worker_detect(PicopassWorker* picopass_worker); void picopass_worker_detect(PicopassWorker* picopass_worker);
void picopass_worker_write(PicopassWorker* picopass_worker); void picopass_worker_write(PicopassWorker* picopass_worker);
void picopass_worker_write_standard_key(PicopassWorker* picopass_worker);

View File

@@ -11,3 +11,5 @@ ADD_SCENE(picopass, delete, Delete)
ADD_SCENE(picopass, delete_success, DeleteSuccess) ADD_SCENE(picopass, delete_success, DeleteSuccess)
ADD_SCENE(picopass, write_card, WriteCard) ADD_SCENE(picopass, write_card, WriteCard)
ADD_SCENE(picopass, write_card_success, WriteCardSuccess) ADD_SCENE(picopass, write_card_success, WriteCardSuccess)
ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess)
ADD_SCENE(picopass, write_key, WriteKey)

View File

@@ -1,6 +1,8 @@
#include "../picopass_i.h" #include "../picopass_i.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
const uint8_t picopass_factory_key_check[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87};
void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) {
UNUSED(event); UNUSED(event);
Picopass* picopass = context; Picopass* picopass = context;
@@ -34,7 +36,14 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassCustomEventWorkerExit) { if(event.event == PicopassCustomEventWorkerExit) {
if(memcmp(
picopass->dev->dev_data.pacs.key,
picopass_factory_key_check,
PICOPASS_BLOCK_LEN) == 0) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess);
} else {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
}
consumed = true; consumed = true;
} }
} }

View File

@@ -15,6 +15,7 @@ void picopass_scene_read_card_success_widget_callback(
void picopass_scene_read_card_success_on_enter(void* context) { void picopass_scene_read_card_success_on_enter(void* context) {
Picopass* picopass = context; Picopass* picopass = context;
FuriString* csn_str = furi_string_alloc_set("CSN:"); FuriString* csn_str = furi_string_alloc_set("CSN:");
FuriString* credential_str = furi_string_alloc(); FuriString* credential_str = furi_string_alloc();
FuriString* wiegand_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc();
@@ -30,27 +31,31 @@ void picopass_scene_read_card_success_on_enter(void* context) {
PicopassPacs* pacs = &picopass->dev->dev_data.pacs; PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget; Widget* widget = picopass->widget;
uint8_t csn[PICOPASS_BLOCK_LEN]; uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, "%02X ", csn[i]); furi_string_cat_printf(csn_str, "%02X ", csn[i]);
} }
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN);
if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { bool empty =
picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN);
if(no_key) {
furi_string_cat_printf(wiegand_str, "Read Failed"); furi_string_cat_printf(wiegand_str, "Read Failed");
if(pacs->se_enabled) { if(pacs->se_enabled) {
furi_string_cat_printf(credential_str, "SE enabled"); furi_string_cat_printf(credential_str, "SE enabled");
} }
} else if(empty) {
furi_string_cat_printf(wiegand_str, "Empty");
} else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
furi_string_cat_printf(wiegand_str, "Invalid PACS");
widget_add_button_element( if(pacs->se_enabled) {
widget, furi_string_cat_printf(credential_str, "SE enabled");
GuiButtonTypeLeft, }
"Retry",
picopass_scene_read_card_success_widget_callback,
picopass);
} else { } else {
size_t bytesLength = 1 + pacs->record.bitLength / 8; size_t bytesLength = 1 + pacs->record.bitLength / 8;
furi_string_set(credential_str, ""); furi_string_set(credential_str, "");
@@ -69,12 +74,18 @@ void picopass_scene_read_card_success_on_enter(void* context) {
furi_string_cat_printf(sio_str, "+SIO"); furi_string_cat_printf(sio_str, "+SIO");
} }
widget_add_button_element( if(pacs->key) {
widget, if(pacs->sio) {
GuiButtonTypeLeft, furi_string_cat_printf(sio_str, " ");
"Retry", }
picopass_scene_read_card_success_widget_callback, furi_string_cat_printf(sio_str, "Key: ");
picopass);
uint8_t key[PICOPASS_BLOCK_LEN];
memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(sio_str, "%02X", key[i]);
}
}
widget_add_button_element( widget_add_button_element(
widget, widget,
@@ -84,6 +95,13 @@ void picopass_scene_read_card_success_on_enter(void* context) {
picopass); picopass);
} }
widget_add_button_element(
widget,
GuiButtonTypeLeft,
"Retry",
picopass_scene_read_card_success_widget_callback,
picopass);
widget_add_string_element( widget_add_string_element(
widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str));
widget_add_string_element( widget_add_string_element(

View File

@@ -0,0 +1,78 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
void picopass_scene_read_factory_success_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
furi_assert(context);
Picopass* picopass = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(picopass->view_dispatcher, result);
}
}
void picopass_scene_read_factory_success_on_enter(void* context) {
Picopass* picopass = context;
FuriString* title = furi_string_alloc_set("Factory Default");
FuriString* subtitle = furi_string_alloc_set("");
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Send notification
notification_message(picopass->notifications, &sequence_success);
// Setup view
Widget* widget = picopass->widget;
//PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
PicopassBlock* AA1 = picopass->dev->dev_data.AA1;
uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data;
uint8_t fuses = configBlock[7];
if((fuses & 0x80) == 0x80) {
furi_string_cat_printf(subtitle, "Personalization mode");
} else {
furi_string_cat_printf(subtitle, "Application mode");
}
widget_add_button_element(
widget,
GuiButtonTypeCenter,
"Write Standard iClass Key",
picopass_scene_read_factory_success_widget_callback,
picopass);
widget_add_string_element(
widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(title));
widget_add_string_element(
widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(subtitle));
furi_string_free(title);
furi_string_free(subtitle);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
}
bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(picopass->scene_manager);
} else if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
consumed = true;
}
}
return consumed;
}
void picopass_scene_read_factory_success_on_exit(void* context) {
Picopass* picopass = context;
// Clear view
widget_reset(picopass->widget);
}

View File

@@ -21,7 +21,7 @@ void picopass_scene_save_name_on_enter(void* context) {
} else { } else {
picopass_text_store_set(picopass, picopass->dev->dev_name); picopass_text_store_set(picopass, picopass->dev->dev_name);
} }
text_input_set_header_text(text_input, "Name The Card"); text_input_set_header_text(text_input, "Name the card");
text_input_set_result_callback( text_input_set_result_callback(
text_input, text_input,
picopass_scene_save_name_text_input_callback, picopass_scene_save_name_text_input_callback,

View File

@@ -32,13 +32,18 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) { if(event.event == SubmenuIndexRead) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneStart, SubmenuIndexRead);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexSaved) { } else if(event.event == SubmenuIndexSaved) {
// Explicitly save state so that the correct item is
// reselected if the user cancels loading a file.
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
consumed = true; consumed = true;
} }
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event);
} }
return consumed; return consumed;

View File

@@ -16,6 +16,7 @@ void picopass_scene_write_card_success_widget_callback(
void picopass_scene_write_card_success_on_enter(void* context) { void picopass_scene_write_card_success_on_enter(void* context) {
Picopass* picopass = context; Picopass* picopass = context;
Widget* widget = picopass->widget; Widget* widget = picopass->widget;
FuriString* str = furi_string_alloc_set("Write Success!");
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
@@ -29,6 +30,18 @@ void picopass_scene_write_card_success_on_enter(void* context) {
picopass_scene_write_card_success_widget_callback, picopass_scene_write_card_success_widget_callback,
picopass); picopass);
widget_add_button_element(
widget,
GuiButtonTypeRight,
"Menu",
picopass_scene_write_card_success_widget_callback,
picopass);
widget_add_string_element(
widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(str));
furi_string_free(str);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
} }

View File

@@ -0,0 +1,53 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context) {
UNUSED(event);
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit);
}
void picopass_scene_write_key_on_enter(void* context) {
Picopass* picopass = context;
DOLPHIN_DEED(DolphinDeedNfcSave);
// Setup view
Popup* popup = picopass->popup;
popup_set_header(popup, "Writing\niClass\nkey", 68, 30, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
// Start worker
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
picopass_worker_start(
picopass->worker,
PicopassWorkerStateWriteStandardKey,
&picopass->dev->dev_data,
picopass_write_key_worker_callback,
picopass);
picopass_blink_start(picopass);
}
bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassCustomEventWorkerExit) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess);
consumed = true;
}
}
return consumed;
}
void picopass_scene_write_key_on_exit(void* context) {
Picopass* picopass = context;
// Stop worker
picopass_worker_stop(picopass->worker);
// Clear view
popup_reset(picopass->popup);
picopass_blink_stop(picopass);
}

View File

@@ -14,7 +14,6 @@ typedef struct {
const size_t stack_size; const size_t stack_size;
const Icon* icon; const Icon* icon;
const FlipperApplicationFlag flags; const FlipperApplicationFlag flags;
const char* link;
} FlipperApplication; } FlipperApplication;
typedef void (*FlipperOnStartHook)(void); typedef void (*FlipperOnStartHook)(void);

View File

@@ -463,7 +463,7 @@ int32_t bt_srv(void* p) {
Bt* bt = bt_alloc(); Bt* bt = bt_alloc();
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
FURI_LOG_W(TAG, "Skipped BT init: device in special startup mode"); FURI_LOG_W(TAG, "Skipping start in special boot mode");
ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT);
furi_record_create(RECORD_BT, bt); furi_record_create(RECORD_BT, bt);
return 0; return 0;

View File

@@ -461,7 +461,7 @@ int32_t cli_srv(void* p) {
if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) {
cli_session_open(cli, &cli_vcp); cli_session_open(cli, &cli_vcp);
} else { } else {
FURI_LOG_W(TAG, "Skipped CLI session open: device in special startup mode"); FURI_LOG_W(TAG, "Skipping start in special boot mode");
} }
while(1) { while(1) {

View File

@@ -12,26 +12,48 @@
// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`
#define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d"
void cli_command_device_info_callback(const char* key, const char* value, bool last, void* context) { void cli_command_info_callback(const char* key, const char* value, bool last, void* context) {
UNUSED(context);
UNUSED(last); UNUSED(last);
UNUSED(context);
printf("%-30s: %s\r\n", key, value); printf("%-30s: %s\r\n", key, value);
} }
/* /** Info Command
* Device Info Command *
* This command is intended to be used by humans * This command is intended to be used by humans
*
* Arguments:
* - device - print device info
* - power - print power info
* - power_debug - print power debug info
*
* @param cli The cli instance
* @param args The arguments
* @param context The context
*/ */
void cli_command_device_info(Cli* cli, FuriString* args, void* context) { void cli_command_info(Cli* cli, FuriString* args, void* context) {
UNUSED(cli); UNUSED(cli);
UNUSED(args);
furi_hal_info_get(cli_command_device_info_callback, '_', context); if(context) {
furi_hal_info_get(cli_command_info_callback, '_', NULL);
return;
}
if(!furi_string_cmp(args, "device")) {
furi_hal_info_get(cli_command_info_callback, '.', NULL);
} else if(!furi_string_cmp(args, "power")) {
furi_hal_power_info_get(cli_command_info_callback, '.', NULL);
} else if(!furi_string_cmp(args, "power_debug")) {
furi_hal_power_debug_get(cli_command_info_callback, NULL);
} else {
cli_print_usage("info", "<device|power|power_debug>", furi_string_get_cstr(args));
}
} }
void cli_command_help(Cli* cli, FuriString* args, void* context) { void cli_command_help(Cli* cli, FuriString* args, void* context) {
UNUSED(args); UNUSED(args);
UNUSED(context); UNUSED(context);
printf("Commands we have:"); printf("Commands available:");
// Command count // Command count
const size_t commands_count = CliCommandTree_size(cli->commands); const size_t commands_count = CliCommandTree_size(cli->commands);
@@ -61,9 +83,9 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) {
if(furi_string_size(args) > 0) { if(furi_string_size(args) > 0) {
cli_nl(); cli_nl();
printf("Also I have no clue what '"); printf("`");
printf("%s", furi_string_get_cstr(args)); printf("%s", furi_string_get_cstr(args));
printf("' is."); printf("` command not found");
} }
} }
@@ -420,8 +442,9 @@ void cli_command_i2c(Cli* cli, FuriString* args, void* context) {
} }
void cli_commands_init(Cli* cli) { void cli_commands_init(Cli* cli) {
cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_device_info, NULL); cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_device_info, NULL); cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL);
cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
cli_add_command(cli, "src", CliCommandFlagParallelSafe, cli_command_src, NULL); cli_add_command(cli, "src", CliCommandFlagParallelSafe, cli_command_src, NULL);
cli_add_command(cli, "source", CliCommandFlagParallelSafe, cli_command_src, NULL); cli_add_command(cli, "source", CliCommandFlagParallelSafe, cli_command_src, NULL);

View File

@@ -415,7 +415,7 @@ static StorageAnimation*
} }
} }
uint32_t lucky_number = furi_hal_random_get() % (whole_weight != 0 ? whole_weight : 1); uint32_t lucky_number = furi_hal_random_get() % whole_weight;
uint32_t weight = 0; uint32_t weight = 0;
StorageAnimation* selected = NULL; StorageAnimation* selected = NULL;

View File

@@ -325,7 +325,7 @@ static bool animation_storage_load_frames(
FURI_CONST_ASSIGN(icon->width, width); FURI_CONST_ASSIGN(icon->width, width);
icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count);
bool frames_ok = true; bool frames_ok = false;
File* file = storage_file_alloc(storage); File* file = storage_file_alloc(storage);
FileInfo file_info; FileInfo file_info;
FuriString* filename; FuriString* filename;
@@ -333,13 +333,10 @@ static bool animation_storage_load_frames(
size_t max_filesize = ROUND_UP_TO(width, 8) * height + 2; size_t max_filesize = ROUND_UP_TO(width, 8) * height + 2;
for(int i = 0; i < icon->frame_count; ++i) { for(int i = 0; i < icon->frame_count; ++i) {
FURI_CONST_ASSIGN_PTR(icon->frames[i], 0);
if(frames_ok) {
frames_ok = false; frames_ok = false;
furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i); furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i);
do {
if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != FSE_OK)
FSE_OK)
break; break;
if(file_info.size > max_filesize) { if(file_info.size > max_filesize) {
FURI_LOG_E( FURI_LOG_E(
@@ -358,16 +355,12 @@ static bool animation_storage_load_frames(
} }
FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size)); FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size));
if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != file_info.size) {
file_info.size) {
FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename)); FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename));
break; break;
} else {
frames_ok = true;
} }
storage_file_close(file); storage_file_close(file);
} while(0); frames_ok = true;
}
} }
if(!frames_ok) { if(!frames_ok) {

View File

@@ -17,6 +17,8 @@
#include "helpers/pin_lock.h" #include "helpers/pin_lock.h"
#include "helpers/slideshow_filename.h" #include "helpers/slideshow_filename.h"
#define TAG "Desktop"
static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_arm(Desktop*);
static void desktop_auto_lock_inhibit(Desktop*); static void desktop_auto_lock_inhibit(Desktop*);
static void desktop_start_auto_lock_timer(Desktop*); static void desktop_start_auto_lock_timer(Desktop*);
@@ -304,8 +306,10 @@ int32_t desktop_srv(void* p) {
UNUSED(p); UNUSED(p);
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); FURI_LOG_W(TAG, "Skipping start in special boot mode");
} else { return 0;
}
Desktop* desktop = desktop_alloc(); Desktop* desktop = desktop_alloc();
bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings);
@@ -340,7 +344,6 @@ int32_t desktop_srv(void* p) {
view_dispatcher_run(desktop->view_dispatcher); view_dispatcher_run(desktop->view_dispatcher);
desktop_free(desktop); desktop_free(desktop);
}
return 0; return 0;
} }

View File

@@ -1,6 +1,6 @@
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
#include "applications/services/applications.h" #include <applications.h>
#include <assets_icons.h> #include <assets_icons.h>
#include <loader/loader.h> #include <loader/loader.h>

View File

@@ -75,7 +75,8 @@ void desktop_debug_render(Canvas* canvas, void* model) {
c2_ver ? c2_ver->StackTypeString : "<none>"); c2_ver ? c2_ver->StackTypeString : "<none>");
canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer);
snprintf(buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), "dev"); snprintf(
buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver));
canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer);
} else { } else {

View File

@@ -1,13 +1,9 @@
#include <furi.h> #include <furi.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <dolphin/dolphin.h>
#include <assets_icons.h> #include <assets_icons.h>
#include "../desktop_i.h" #include "../desktop_i.h"
#include "desktop_view_lock_menu.h" #include "desktop_view_lock_menu.h"
#include "xtreme/settings.h"
#define LOCK_MENU_ITEMS_NB 5
typedef enum { typedef enum {
DesktopLockMenuIndexLock, DesktopLockMenuIndexLock,

View File

@@ -85,7 +85,6 @@ DesktopMainView* desktop_main_alloc() {
DesktopMainView* main_view = malloc(sizeof(DesktopMainView)); DesktopMainView* main_view = malloc(sizeof(DesktopMainView));
main_view->view = view_alloc(); main_view->view = view_alloc();
view_allocate_model(main_view->view, ViewModelTypeLockFree, 1);
view_set_context(main_view->view, main_view); view_set_context(main_view->view, main_view);
view_set_input_callback(main_view->view, desktop_main_input_callback); view_set_input_callback(main_view->view, desktop_main_input_callback);

View File

@@ -61,9 +61,9 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) {
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT);
} else if(event->type == InputTypeRelease) { } else if(event->type == InputTypeRelease) {
furi_timer_stop(instance->timer); furi_timer_stop(instance->timer);
// if(!slideshow_is_one_page(model->slideshow)) { /*if(!slideshow_is_one_page(model->slideshow)) {
// furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
// } }*/
} }
} }
view_commit_model(instance->view, update_view); view_commit_model(instance->view, update_view);

View File

@@ -6,7 +6,6 @@
#include <furi_hal.h> #include <furi_hal.h>
#include <stdint.h> #include <stdint.h>
#include <furi.h> #include <furi.h>
#include "furi_hal_random.h"
#include <xtreme/settings.h> #include <xtreme/settings.h>
#define DOLPHIN_LOCK_EVENT_FLAG (0x1) #define DOLPHIN_LOCK_EVENT_FLAG (0x1)
@@ -23,18 +22,6 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) {
dolphin_event_send_async(dolphin, &event); dolphin_event_send_async(dolphin, &event);
} }
DolphinDeed getRandomDeed() {
DolphinDeed returnGrp[14] = {1, 5, 8, 10, 12, 15, 17, 20, 21, 25, 26, 28, 29, 32};
static bool rand_generator_inited = false;
if(!rand_generator_inited) {
srand(furi_get_tick());
rand_generator_inited = true;
}
uint8_t diceRoll = (rand() % COUNT_OF(returnGrp)); // JUST TO GET IT GOING? AND FIX BUG
diceRoll = (rand() % COUNT_OF(returnGrp));
return returnGrp[diceRoll];
}
DolphinStats dolphin_stats(Dolphin* dolphin) { DolphinStats dolphin_stats(Dolphin* dolphin) {
furi_assert(dolphin); furi_assert(dolphin);
@@ -173,6 +160,12 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) {
int32_t dolphin_srv(void* p) { int32_t dolphin_srv(void* p) {
UNUSED(p); UNUSED(p);
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
FURI_LOG_W(TAG, "Skipping start in special boot mode");
return 0;
}
Dolphin* dolphin = dolphin_alloc(); Dolphin* dolphin = dolphin_alloc();
furi_record_create(RECORD_DOLPHIN, dolphin); furi_record_create(RECORD_DOLPHIN, dolphin);

View File

@@ -44,10 +44,6 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed);
*/ */
DolphinStats dolphin_stats(Dolphin* dolphin); DolphinStats dolphin_stats(Dolphin* dolphin);
/** GET RANDOM 3PT DEED
*/
DolphinDeed getRandomDeed();
/** Flush dolphin queue and save state /** Flush dolphin queue and save state
* Thread safe, blocking * Thread safe, blocking
*/ */

Some files were not shown because too many files have changed in this diff Show More