mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-25 01:38:11 -07:00
Merge remote-tracking branch 'UFW/dev' into cc1101_ext
This commit is contained in:
@@ -12,5 +12,6 @@ App(
|
||||
"display_test",
|
||||
"text_box_test",
|
||||
"file_browser_test",
|
||||
"speaker_debug",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="crash_test",
|
||||
name="Crash Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="crash_test_app",
|
||||
cdefines=["APP_CRASH_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Debug",
|
||||
)
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <furi_hal.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
#define TAG "CrashTest"
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
} CrashTest;
|
||||
|
||||
typedef enum {
|
||||
CrashTestViewSubmenu,
|
||||
} CrashTestView;
|
||||
|
||||
typedef enum {
|
||||
CrashTestSubmenuCheck,
|
||||
CrashTestSubmenuCheckMessage,
|
||||
CrashTestSubmenuAssert,
|
||||
CrashTestSubmenuAssertMessage,
|
||||
CrashTestSubmenuCrash,
|
||||
CrashTestSubmenuHalt,
|
||||
} CrashTestSubmenu;
|
||||
|
||||
static void crash_test_submenu_callback(void* context, uint32_t index) {
|
||||
CrashTest* instance = (CrashTest*)context;
|
||||
UNUSED(instance);
|
||||
|
||||
switch(index) {
|
||||
case CrashTestSubmenuCheck:
|
||||
furi_check(false);
|
||||
break;
|
||||
case CrashTestSubmenuCheckMessage:
|
||||
furi_check(false, "Crash test: furi_check with message");
|
||||
break;
|
||||
case CrashTestSubmenuAssert:
|
||||
furi_assert(false);
|
||||
break;
|
||||
case CrashTestSubmenuAssertMessage:
|
||||
furi_assert(false, "Crash test: furi_assert with message");
|
||||
break;
|
||||
case CrashTestSubmenuCrash:
|
||||
furi_crash("Crash test: furi_crash");
|
||||
break;
|
||||
case CrashTestSubmenuHalt:
|
||||
furi_halt("Crash test: furi_halt");
|
||||
break;
|
||||
default:
|
||||
furi_crash("Programming error");
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t crash_test_exit_callback(void* context) {
|
||||
UNUSED(context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
CrashTest* crash_test_alloc() {
|
||||
CrashTest* instance = malloc(sizeof(CrashTest));
|
||||
|
||||
View* view = NULL;
|
||||
|
||||
instance->gui = furi_record_open(RECORD_GUI);
|
||||
instance->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(instance->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Menu
|
||||
instance->submenu = submenu_alloc();
|
||||
view = submenu_get_view(instance->submenu);
|
||||
view_set_previous_callback(view, crash_test_exit_callback);
|
||||
view_dispatcher_add_view(instance->view_dispatcher, CrashTestViewSubmenu, view);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Check", CrashTestSubmenuCheck, crash_test_submenu_callback, instance);
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Check with message",
|
||||
CrashTestSubmenuCheckMessage,
|
||||
crash_test_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Assert", CrashTestSubmenuAssert, crash_test_submenu_callback, instance);
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Assert with message",
|
||||
CrashTestSubmenuAssertMessage,
|
||||
crash_test_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Crash", CrashTestSubmenuCrash, crash_test_submenu_callback, instance);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Halt", CrashTestSubmenuHalt, crash_test_submenu_callback, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void crash_test_free(CrashTest* instance) {
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, CrashTestViewSubmenu);
|
||||
submenu_free(instance->submenu);
|
||||
|
||||
view_dispatcher_free(instance->view_dispatcher);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
int32_t crash_test_run(CrashTest* instance) {
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, CrashTestViewSubmenu);
|
||||
view_dispatcher_run(instance->view_dispatcher);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t crash_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
CrashTest* instance = crash_test_alloc();
|
||||
|
||||
int32_t ret = crash_test_run(instance);
|
||||
|
||||
crash_test_free(instance);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -6,6 +6,11 @@ static void comparator_trigger_callback(bool level, void* comp_ctx) {
|
||||
furi_hal_gpio_write(&gpio_ext_pa7, !level);
|
||||
}
|
||||
|
||||
void lfrfid_debug_view_tune_callback(void* context) {
|
||||
LfRfidDebug* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, 0xBA);
|
||||
}
|
||||
|
||||
void lfrfid_debug_scene_tune_on_enter(void* context) {
|
||||
LfRfidDebug* app = context;
|
||||
|
||||
@@ -16,6 +21,8 @@ void lfrfid_debug_scene_tune_on_enter(void* context) {
|
||||
|
||||
furi_hal_rfid_tim_read_start(125000, 0.5);
|
||||
|
||||
lfrfid_debug_view_tune_set_callback(app->tune_view, lfrfid_debug_view_tune_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ typedef struct {
|
||||
uint32_t ARR;
|
||||
uint32_t CCR;
|
||||
int pos;
|
||||
void (*update_callback)(void* context);
|
||||
void* update_context;
|
||||
} LfRfidTuneViewModel;
|
||||
|
||||
static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) {
|
||||
@@ -151,6 +153,18 @@ static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* conte
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(event->key == InputKeyLeft || event->key == InputKeyRight) {
|
||||
with_view_model(
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
if(model->update_callback) {
|
||||
model->update_callback(model->update_context);
|
||||
}
|
||||
},
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
@@ -161,19 +175,7 @@ LfRfidTuneView* lfrfid_debug_view_tune_alloc() {
|
||||
tune_view->view = view_alloc();
|
||||
view_set_context(tune_view->view, tune_view);
|
||||
view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel));
|
||||
|
||||
with_view_model(
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
model->dirty = true;
|
||||
model->fine = false;
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
},
|
||||
true);
|
||||
|
||||
lfrfid_debug_view_tune_clean(tune_view);
|
||||
view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback);
|
||||
view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback);
|
||||
|
||||
@@ -199,6 +201,8 @@ void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) {
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
model->update_callback = NULL;
|
||||
model->update_context = NULL;
|
||||
},
|
||||
true);
|
||||
}
|
||||
@@ -232,3 +236,17 @@ uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void lfrfid_debug_view_tune_set_callback(
|
||||
LfRfidTuneView* tune_view,
|
||||
void (*callback)(void* context),
|
||||
void* context) {
|
||||
with_view_model(
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
model->update_callback = callback;
|
||||
model->update_context = context;
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -16,3 +16,8 @@ bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view);
|
||||
uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view);
|
||||
|
||||
uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view);
|
||||
|
||||
void lfrfid_debug_view_tune_set_callback(
|
||||
LfRfidTuneView* tune_view,
|
||||
void (*callback)(void* context),
|
||||
void* context);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="speaker_debug",
|
||||
name="Speaker Debug",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="speaker_debug_app",
|
||||
requires=["gui", "notification"],
|
||||
stack_size=2 * 1024,
|
||||
order=10,
|
||||
fap_category="Debug",
|
||||
fap_libs=["music_worker"],
|
||||
)
|
||||
@@ -0,0 +1,120 @@
|
||||
#include <furi.h>
|
||||
#include <notification/notification.h>
|
||||
#include <music_worker/music_worker.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#define TAG "SpeakerDebug"
|
||||
#define CLI_COMMAND "speaker_debug"
|
||||
|
||||
typedef enum {
|
||||
SpeakerDebugAppMessageTypeStop,
|
||||
} SpeakerDebugAppMessageType;
|
||||
|
||||
typedef struct {
|
||||
SpeakerDebugAppMessageType type;
|
||||
} SpeakerDebugAppMessage;
|
||||
|
||||
typedef struct {
|
||||
MusicWorker* music_worker;
|
||||
FuriMessageQueue* message_queue;
|
||||
Cli* cli;
|
||||
} SpeakerDebugApp;
|
||||
|
||||
static SpeakerDebugApp* speaker_app_alloc() {
|
||||
SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp));
|
||||
app->music_worker = music_worker_alloc();
|
||||
app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage));
|
||||
app->cli = furi_record_open(RECORD_CLI);
|
||||
return app;
|
||||
}
|
||||
|
||||
static void speaker_app_free(SpeakerDebugApp* app) {
|
||||
music_worker_free(app->music_worker);
|
||||
furi_message_queue_free(app->message_queue);
|
||||
furi_record_close(RECORD_CLI);
|
||||
free(app);
|
||||
}
|
||||
|
||||
static void speaker_app_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
|
||||
SpeakerDebugApp* app = (SpeakerDebugApp*)context;
|
||||
SpeakerDebugAppMessage message;
|
||||
FuriString* cmd = furi_string_alloc();
|
||||
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
furi_string_free(cmd);
|
||||
printf("Usage:\r\n");
|
||||
printf("\t" CLI_COMMAND " stop\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(furi_string_cmp(cmd, "stop") == 0) {
|
||||
message.type = SpeakerDebugAppMessageTypeStop;
|
||||
FuriStatus status = furi_message_queue_put(app->message_queue, &message, 100);
|
||||
if(status != FuriStatusOk) {
|
||||
printf("Failed to send message\r\n");
|
||||
} else {
|
||||
printf("Stopping\r\n");
|
||||
}
|
||||
} else {
|
||||
printf("Usage:\r\n");
|
||||
printf("\t" CLI_COMMAND " stop\r\n");
|
||||
}
|
||||
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
static bool speaker_app_music_play(SpeakerDebugApp* app, const char* rtttl) {
|
||||
if(music_worker_is_playing(app->music_worker)) {
|
||||
music_worker_stop(app->music_worker);
|
||||
}
|
||||
|
||||
if(!music_worker_load_rtttl_from_string(app->music_worker, rtttl)) {
|
||||
FURI_LOG_E(TAG, "Failed to load RTTTL");
|
||||
return false;
|
||||
}
|
||||
|
||||
music_worker_set_volume(app->music_worker, 1.0f);
|
||||
music_worker_start(app->music_worker);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void speaker_app_music_stop(SpeakerDebugApp* app) {
|
||||
if(music_worker_is_playing(app->music_worker)) {
|
||||
music_worker_stop(app->music_worker);
|
||||
}
|
||||
}
|
||||
|
||||
static void speaker_app_run(SpeakerDebugApp* app, const char* arg) {
|
||||
if(!arg || !speaker_app_music_play(app, arg)) {
|
||||
FURI_LOG_E(TAG, "Provided RTTTL is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app);
|
||||
|
||||
SpeakerDebugAppMessage message;
|
||||
FuriStatus status;
|
||||
while(true) {
|
||||
status = furi_message_queue_get(app->message_queue, &message, FuriWaitForever);
|
||||
|
||||
if(status == FuriStatusOk) {
|
||||
if(message.type == SpeakerDebugAppMessageTypeStop) {
|
||||
speaker_app_music_stop(app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cli_delete_command(app->cli, CLI_COMMAND);
|
||||
}
|
||||
|
||||
int32_t speaker_debug_app(void* arg) {
|
||||
SpeakerDebugApp* app = speaker_app_alloc();
|
||||
speaker_app_run(app, arg);
|
||||
speaker_app_free(app);
|
||||
return 0;
|
||||
}
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#define LINES_ON_SCREEN 6
|
||||
#define COLUMNS_ON_SCREEN 21
|
||||
#define TAG "UartEcho"
|
||||
#define DEFAULT_BAUD_RATE 230400
|
||||
|
||||
typedef struct UartDumpModel UartDumpModel;
|
||||
|
||||
@@ -179,7 +181,7 @@ static int32_t uart_echo_worker(void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UartEchoApp* uart_echo_app_alloc() {
|
||||
static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
|
||||
UartEchoApp* app = malloc(sizeof(UartEchoApp));
|
||||
|
||||
app->rx_stream = furi_stream_buffer_alloc(2048, 1);
|
||||
@@ -220,7 +222,7 @@ static UartEchoApp* uart_echo_app_alloc() {
|
||||
|
||||
// Enable uart listener
|
||||
furi_hal_console_disable();
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate);
|
||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
||||
|
||||
return app;
|
||||
@@ -263,8 +265,18 @@ static void uart_echo_app_free(UartEchoApp* app) {
|
||||
}
|
||||
|
||||
int32_t uart_echo_app(void* p) {
|
||||
UNUSED(p);
|
||||
UartEchoApp* app = uart_echo_app_alloc();
|
||||
uint32_t baudrate = DEFAULT_BAUD_RATE;
|
||||
if(p) {
|
||||
const char* baudrate_str = p;
|
||||
if(sscanf(baudrate_str, "%lu", &baudrate) != 1) {
|
||||
FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str);
|
||||
baudrate = DEFAULT_BAUD_RATE;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate);
|
||||
|
||||
UartEchoApp* app = uart_echo_app_alloc(baudrate);
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
uart_echo_app_free(app);
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
|
||||
MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) {
|
||||
mu_assert(
|
||||
sizeof(DialogsFileBrowserOptions) == 28,
|
||||
"Changes to `DialogsFileBrowserOptions` should also be reflected in `dialog_file_browser_set_basic_options`");
|
||||
|
||||
DialogsFileBrowserOptions options;
|
||||
dialog_file_browser_set_basic_options(&options, ".fap", NULL);
|
||||
// note: this assertions can safely be changed, their primary purpose is to remind the maintainer
|
||||
// to update `dialog_file_browser_set_basic_options` by including all structure fields in it
|
||||
mu_assert_string_eq(".fap", options.extension);
|
||||
mu_assert_null(options.base_path);
|
||||
mu_assert(options.skip_assets, "`skip_assets` should default to `true");
|
||||
mu_assert(options.hide_dot_files, "`hide_dot_files` should default to `true");
|
||||
mu_assert_null(options.icon);
|
||||
mu_assert(options.hide_ext, "`hide_ext` should default to `true");
|
||||
mu_assert_null(options.item_loader_callback);
|
||||
mu_assert_null(options.item_loader_context);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(dialogs_file_browser_options) {
|
||||
MU_RUN_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields);
|
||||
}
|
||||
|
||||
int run_minunit_test_dialogs_file_browser_options() {
|
||||
MU_RUN_SUITE(dialogs_file_browser_options);
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
@@ -27,6 +27,7 @@ int run_minunit_test_nfc();
|
||||
int run_minunit_test_bit_lib();
|
||||
int run_minunit_test_float_tools();
|
||||
int run_minunit_test_bt();
|
||||
int run_minunit_test_dialogs_file_browser_options();
|
||||
|
||||
typedef int (*UnitTestEntry)();
|
||||
|
||||
@@ -55,6 +56,8 @@ const UnitTest unit_tests[] = {
|
||||
{.name = "bit_lib", .entry = run_minunit_test_bit_lib},
|
||||
{.name = "float_tools", .entry = run_minunit_test_float_tools},
|
||||
{.name = "bt", .entry = run_minunit_test_bt},
|
||||
{.name = "dialogs_file_browser_options",
|
||||
.entry = run_minunit_test_dialogs_file_browser_options},
|
||||
};
|
||||
|
||||
void minunit_print_progress() {
|
||||
|
||||
Reference in New Issue
Block a user