mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-11 19:33:30 -07:00
Formatting & New updated IR
This commit is contained in:
@@ -64,7 +64,13 @@ static void
|
||||
archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));
|
||||
} else {
|
||||
with_view_model(
|
||||
browser->view, ArchiveBrowserViewModel * model, { files_array_sort(model->files); model->list_loading = false; }, true);
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
files_array_sort(model->files);
|
||||
model->list_loading = false;
|
||||
},
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ void bad_usb_scene_error_on_enter(void* context) {
|
||||
app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app);
|
||||
} else if(app->error == BadUsbAppErrorCloseRpc) {
|
||||
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
|
||||
widget_add_string_multiline_element(
|
||||
@@ -45,8 +45,7 @@ void bad_usb_scene_error_on_enter(void* context) {
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!");
|
||||
widget_add_string_multiline_element(
|
||||
|
||||
@@ -50,10 +50,9 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
|
||||
if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
|
||||
(model->state.state == BadUsbStateNotConnected)) {
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
elements_button_center(canvas, "Start");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
elements_button_center(canvas, "Cum");
|
||||
}
|
||||
} else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
|
||||
@@ -70,21 +69,19 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
if(model->state.state == BadUsbStateNotConnected) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy");
|
||||
}
|
||||
} else if(model->state.state == BadUsbStateWillRun) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum");
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
|
||||
|
||||
@@ -12,10 +12,9 @@ void ibutton_scene_delete_success_on_enter(void* context) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
}
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -17,10 +17,9 @@ void ibutton_scene_read_on_enter(void* context) {
|
||||
|
||||
popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,9 @@ void ibutton_scene_save_success_on_enter(void* context) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
|
||||
@@ -13,10 +13,9 @@ void ibutton_scene_write_success_on_enter(void* context) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52);
|
||||
}
|
||||
popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -7,10 +7,9 @@ void infrared_scene_edit_rename_done_on_enter(void* context) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
|
||||
@@ -7,16 +7,14 @@ void infrared_scene_learn_done_on_enter(void* context) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
if (infrared->app_state.is_learning_new_remote) {
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,9 @@ void infrared_scene_learn_success_on_enter(void* context) {
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Retry");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Save");
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Send");
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
|
||||
}
|
||||
dialog_ex_set_result_callback(dialog_ex, infrared_scene_learn_success_dialog_result_callback);
|
||||
|
||||
@@ -71,7 +71,6 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(
|
||||
submenu_index == SubmenuIndexLearnNewRemote ||
|
||||
submenu_index == SubmenuIndexLearnNewRemoteRaw) {
|
||||
|
||||
// enable automatic signal decoding if "Learn New Remote"
|
||||
// disable automatic signal decoding if "Learn New Remote (RAW)"
|
||||
infrared_worker_rx_enable_signal_decoding(
|
||||
|
||||
@@ -45,10 +45,9 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) {
|
||||
writer_initialize(t55xxtiming);
|
||||
|
||||
popup_set_header(popup, "Removing\npassword", 90, 36, AlignCenter, AlignCenter);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
}
|
||||
popup_set_text(popup, curr_buf, 90, 56, AlignCenter, AlignCenter);
|
||||
@@ -81,10 +80,9 @@ void lfrfid_scene_clear_t5577_on_enter(void* context) {
|
||||
|
||||
notification_message(app->notifications, &sequence_success);
|
||||
popup_set_header(popup, "Done!", 94, 10, AlignCenter, AlignTop);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 7, &I_RFIDDolphinSuccess_108x57_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 7, &I_RFIDDolphinSuccess_108x57);
|
||||
}
|
||||
popup_set_context(popup, app);
|
||||
|
||||
@@ -7,10 +7,9 @@ void lfrfid_scene_delete_success_on_enter(void* context) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
}
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -19,10 +19,9 @@ void lfrfid_scene_emulate_on_enter(void* context) {
|
||||
AlignCenter,
|
||||
AlignTop);
|
||||
}
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,9 @@ void lfrfid_scene_raw_read_on_enter(void* context) {
|
||||
LfRfidReadRawState* state = malloc(sizeof(LfRfidReadRawState));
|
||||
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawRead, (uint32_t)state);
|
||||
state->string_file_name = furi_string_alloc();
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
|
||||
}
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
|
||||
|
||||
@@ -9,10 +9,9 @@ void lfrfid_scene_rpc_on_enter(void* context) {
|
||||
|
||||
popup_set_header(popup, "LF RFID", 89, 42, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,9 @@ void lfrfid_scene_save_success_on_enter(void* context) {
|
||||
|
||||
// Clear state of data enter scene
|
||||
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
|
||||
@@ -36,10 +36,9 @@ void lfrfid_scene_write_on_enter(void* context) {
|
||||
AlignCenter,
|
||||
AlignTop);
|
||||
}
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,9 @@ void lfrfid_scene_write_success_on_enter(void* context) {
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
|
||||
}
|
||||
popup_set_context(popup, app);
|
||||
|
||||
@@ -67,7 +67,7 @@ static void nfc_cli_emulate(Cli* cli, FuriString* args) {
|
||||
};
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
if (furi_hal_nfc_listen(¶ms, false, 100)) {
|
||||
if(furi_hal_nfc_listen(¶ms, false, 100)) {
|
||||
printf("Reader detected\r\n");
|
||||
furi_hal_nfc_sleep();
|
||||
}
|
||||
|
||||
@@ -13,10 +13,9 @@ void nfc_scene_delete_success_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
Popup* popup = nfc->popup;
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
}
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -40,10 +40,9 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
}
|
||||
widget_add_string_element(widget, 57, 13, AlignLeft, AlignTop, FontPrimary, "Emulating UID");
|
||||
|
||||
@@ -26,10 +26,9 @@ void nfc_scene_mf_classic_emulate_on_enter(void* context) {
|
||||
} else {
|
||||
nfc_text_store_set(nfc, "MIFARE\nClassic");
|
||||
}
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
}
|
||||
popup_set_text(popup, nfc->text_store, 90, 28, AlignCenter, AlignTop);
|
||||
|
||||
@@ -16,10 +16,9 @@ void nfc_scene_mf_classic_update_success_on_enter(void* context) {
|
||||
notification_message(nfc->notifications, &sequence_success);
|
||||
|
||||
Popup* popup = nfc->popup;
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -16,10 +16,9 @@ void nfc_scene_mf_classic_write_success_on_enter(void* context) {
|
||||
notification_message(nfc->notifications, &sequence_success);
|
||||
|
||||
Popup* popup = nfc->popup;
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -114,10 +114,9 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {
|
||||
} else {
|
||||
nfc_text_store_set(nfc, "MIFARE\nNTAG");
|
||||
}
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
}
|
||||
popup_set_text(popup, nfc->text_store, 90, 28, AlignCenter, AlignTop);
|
||||
|
||||
@@ -107,7 +107,7 @@ void nfc_scene_passport_auth_on_enter(void* context) {
|
||||
item = variable_item_list_add(variable_item_list, "Document Nr.", 1, NULL, NULL);
|
||||
|
||||
strncpy(temp_str, mrtd_data->auth.doc_number, temp_str_size);
|
||||
temp_str[temp_str_size -1] = '\x00';
|
||||
temp_str[temp_str_size - 1] = '\x00';
|
||||
if(strlen(temp_str) > 8) {
|
||||
temp_str[8] = '.';
|
||||
temp_str[9] = '.';
|
||||
|
||||
@@ -31,7 +31,6 @@ void nfc_scene_passport_date_on_enter(void* context) {
|
||||
date_value.month = 0;
|
||||
date_value.day = 0;
|
||||
|
||||
|
||||
switch(date_type) {
|
||||
case NFC_PASSPORT_DATE_BIRTH:
|
||||
text_input_set_header_text(text_input, "Birth Date");
|
||||
|
||||
@@ -13,10 +13,9 @@ void nfc_scene_restore_original_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
Popup* popup = nfc->popup;
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Original file\nrestored", 13, 22, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -10,10 +10,9 @@ void nfc_scene_rpc_on_enter(void* context) {
|
||||
popup_set_header(popup, "NFC", 89, 42, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop);
|
||||
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 12, &I_NFC_dolphin_emulation_47x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 12, &I_NFC_dolphin_emulation_47x61);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ void nfc_scene_save_success_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
Popup* popup = nfc->popup;
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -683,7 +683,8 @@ int32_t playlist_app(void* p) {
|
||||
dialog_file_browser_set_basic_options(&browser_options, PLAYLIST_EXT, &I_sub1_10px);
|
||||
browser_options.base_path = PLAYLIST_FOLDER;
|
||||
|
||||
bool res = dialog_file_browser_show(dialogs, app->file_path, app->file_path, &browser_options);
|
||||
bool res =
|
||||
dialog_file_browser_show(dialogs, app->file_path, app->file_path, &browser_options);
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
// check if a file was selected
|
||||
|
||||
@@ -15,10 +15,9 @@ void subghz_scene_delete_success_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
Popup* popup = subghz->popup;
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
}
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -16,10 +16,9 @@ void subghz_scene_rpc_on_enter(void* context) {
|
||||
|
||||
popup_set_header(popup, "Sub-GHz", 89, 42, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,9 @@ void subghz_scene_save_success_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
Popup* popup = subghz->popup;
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
}
|
||||
popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
|
||||
|
||||
@@ -659,18 +659,14 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) {
|
||||
notification_message(notification, &sequence_single_vibro);
|
||||
break;
|
||||
case SubGhzChatEventUserEntrance:
|
||||
furi_string_printf(
|
||||
sysmsg,
|
||||
"%s joined chat.\r\n",
|
||||
furi_hal_version_get_name_ptr());
|
||||
furi_string_printf(sysmsg, "%s joined chat.\r\n", furi_hal_version_get_name_ptr());
|
||||
subghz_chat_worker_write(
|
||||
subghz_chat,
|
||||
(uint8_t*)furi_string_get_cstr(sysmsg),
|
||||
strlen(furi_string_get_cstr(sysmsg)));
|
||||
break;
|
||||
case SubGhzChatEventUserExit:
|
||||
furi_string_printf(
|
||||
sysmsg, "%s left chat.\r\n", furi_hal_version_get_name_ptr());
|
||||
furi_string_printf(sysmsg, "%s left chat.\r\n", furi_hal_version_get_name_ptr());
|
||||
subghz_chat_worker_write(
|
||||
subghz_chat,
|
||||
(uint8_t*)furi_string_get_cstr(sysmsg),
|
||||
@@ -768,7 +764,7 @@ void subghz_on_system_start() {
|
||||
|
||||
cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
|
||||
// psst RM... i know you dont care much about errors, but if you ever see this... incompatible pointer type :3
|
||||
cli_add_command(cli, "chat", CliCommandFlagDefault, subghz_cli_command_chat, NULL);
|
||||
cli_add_command(cli, "chat", CliCommandFlagDefault, subghz_cli_command_chat, NULL);
|
||||
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
|
||||
@@ -234,10 +234,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
|
||||
|
||||
if(model->history_item == 0) {
|
||||
if(model->mode == SubGhzViewReceiverModeLive) {
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
|
||||
}
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
@@ -245,10 +244,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
|
||||
canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
} else {
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
|
||||
}
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
@@ -29,7 +29,7 @@ void u2f_scene_error_on_enter(void* context) {
|
||||
app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app);
|
||||
} else if(app->error == U2fAppErrorCloseRpc) {
|
||||
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
|
||||
widget_add_string_multiline_element(
|
||||
@@ -40,8 +40,7 @@ void u2f_scene_error_on_enter(void* context) {
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!");
|
||||
widget_add_string_multiline_element(
|
||||
|
||||
+19
-25
@@ -64,13 +64,13 @@ typedef struct {
|
||||
static const uint8_t ver_str[] = {"U2F_V2"};
|
||||
|
||||
// NFC applet selection fields
|
||||
static const uint8_t rid_ac_ax[] = { 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01 };
|
||||
static const uint8_t rid_ac_ax[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
|
||||
|
||||
static const uint8_t state_no_error[] = {0x90, 0x00};
|
||||
static const uint8_t state_not_supported[] = {0x6D, 0x00};
|
||||
static const uint8_t state_user_missing[] = {0x69, 0x85};
|
||||
static const uint8_t state_wrong_data[] = {0x6A, 0x80};
|
||||
static const uint8_t state_app_not_found[] = { 0x6A, 0x82 };
|
||||
static const uint8_t state_app_not_found[] = {0x6A, 0x82};
|
||||
|
||||
struct U2fData {
|
||||
uint8_t device_key[32];
|
||||
@@ -86,10 +86,9 @@ struct U2fData {
|
||||
static void* apdu_command_data(U2fApduCommand* cmd) {
|
||||
// Short encoding is a single byte.
|
||||
// Extended length encoding is 0 byte followed by MSB and LSB bytes.
|
||||
if (cmd->len[0] == 0) {
|
||||
if(cmd->len[0] == 0) {
|
||||
return cmd->len + 3;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return cmd->len + 1;
|
||||
}
|
||||
}
|
||||
@@ -278,7 +277,7 @@ static uint16_t u2f_authenticate(U2fData* U2F, const uint8_t* in_buf, uint8_t* o
|
||||
if(U2F->user_present == true) {
|
||||
flags |= 1;
|
||||
} else {
|
||||
if (cmd->p1 == U2fEnforce) {
|
||||
if(cmd->p1 == U2fEnforce) {
|
||||
memcpy(&out_buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
@@ -314,7 +313,7 @@ static uint16_t u2f_authenticate(U2fData* U2F, const uint8_t* in_buf, uint8_t* o
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (cmd->p1 == U2fCheckOnly) { // Check-only: don't need to send full response
|
||||
if(cmd->p1 == U2fCheckOnly) { // Check-only: don't need to send full response
|
||||
memcpy(&out_buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
@@ -352,7 +351,7 @@ uint16_t u2f_applet_selection(const uint8_t* in_buf, uint8_t* out_buf) {
|
||||
data[6],
|
||||
data[7]);
|
||||
|
||||
if (cmd->len[0] != 8 || memcmp(rid_ac_ax, data, 8) != 0) {
|
||||
if(cmd->len[0] != 8 || memcmp(rid_ac_ax, data, 8) != 0) {
|
||||
memcpy(&out_buf[0], state_app_not_found, 2);
|
||||
return 2;
|
||||
}
|
||||
@@ -364,24 +363,20 @@ uint16_t u2f_applet_selection(const uint8_t* in_buf, uint8_t* out_buf) {
|
||||
|
||||
uint16_t u2f_msg_parse(U2fData* U2F, const uint8_t* in_buf, uint16_t in_len, uint8_t* out_buf) {
|
||||
furi_assert(U2F);
|
||||
if (!U2F->ready) return 0;
|
||||
if ((in_buf[0] != 0x00) && (in_len < 5)) return 0;
|
||||
if(!U2F->ready) return 0;
|
||||
if((in_buf[0] != 0x00) && (in_len < 5)) return 0;
|
||||
FURI_LOG_D(TAG, "ins=0x%02x", in_buf[1]);
|
||||
if (in_buf[1] == U2F_CMD_REGISTER) { // Register request
|
||||
if(in_buf[1] == U2F_CMD_REGISTER) { // Register request
|
||||
return u2f_register(U2F, in_buf, out_buf);
|
||||
}
|
||||
else if (in_buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request
|
||||
} else if(in_buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request
|
||||
return u2f_authenticate(U2F, in_buf, out_buf);
|
||||
}
|
||||
else if (in_buf[1] == U2F_CMD_VERSION) { // Get U2F version string
|
||||
} else if(in_buf[1] == U2F_CMD_VERSION) { // Get U2F version string
|
||||
memcpy(&out_buf[0], ver_str, 6);
|
||||
memcpy(&out_buf[6], state_no_error, 2);
|
||||
return 8;
|
||||
}
|
||||
else if (in_buf[1] == U2F_CMD_APPLET_SELECTION) {
|
||||
} else if(in_buf[1] == U2F_CMD_APPLET_SELECTION) {
|
||||
return u2f_applet_selection(in_buf, out_buf);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
memcpy(&out_buf[0], state_not_supported, 2);
|
||||
return 2;
|
||||
}
|
||||
@@ -389,15 +384,14 @@ uint16_t u2f_msg_parse(U2fData* U2F, const uint8_t* in_buf, uint16_t in_len, uin
|
||||
}
|
||||
|
||||
void u2f_wink(U2fData* U2F) {
|
||||
if (U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context);
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context);
|
||||
}
|
||||
|
||||
void u2f_set_state(U2fData* U2F, uint8_t state) {
|
||||
if (state == 0) {
|
||||
if (U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context);
|
||||
}
|
||||
else {
|
||||
if (U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context);
|
||||
if(state == 0) {
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context);
|
||||
} else {
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context);
|
||||
}
|
||||
U2F->user_present = false;
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ struct U2fNfc {
|
||||
};
|
||||
|
||||
static uint16_t
|
||||
u2f_callback(U2fNfc* u2f_nfc, const uint8_t* buff_rx, uint16_t buff_rx_len, uint8_t* buff_tx) {
|
||||
u2f_callback(U2fNfc* u2f_nfc, const uint8_t* buff_rx, uint16_t buff_rx_len, uint8_t* buff_tx) {
|
||||
U2fApduCommand* cmd = (U2fApduCommand*)buff_rx;
|
||||
if (cmd->ins == 0xC0) {
|
||||
if (u2f_nfc->payload_len == 0) {
|
||||
if(cmd->ins == 0xC0) {
|
||||
if(u2f_nfc->payload_len == 0) {
|
||||
FURI_LOG_E(TAG, "requested block but not chaining");
|
||||
buff_tx[0] = 0x69;
|
||||
buff_tx[1] = 0x00;
|
||||
@@ -31,25 +31,23 @@ u2f_callback(U2fNfc* u2f_nfc, const uint8_t* buff_rx, uint16_t buff_rx_len, uint
|
||||
FURI_LOG_T(TAG, "continued chaining %d/%d", u2f_nfc->payload_cursor, u2f_nfc->payload_len);
|
||||
|
||||
uint16_t max_resp_len = cmd->len[0];
|
||||
if (max_resp_len == 0) {
|
||||
if(max_resp_len == 0) {
|
||||
max_resp_len = 256;
|
||||
}
|
||||
|
||||
uint16_t remaining_len = (u2f_nfc->payload_len - 2) - u2f_nfc->payload_cursor;
|
||||
if (remaining_len > max_resp_len) {
|
||||
if(remaining_len > max_resp_len) {
|
||||
memcpy(buff_tx, &u2f_nfc->payload[u2f_nfc->payload_cursor], max_resp_len);
|
||||
remaining_len -= max_resp_len;
|
||||
buff_tx[max_resp_len] = 0x61;
|
||||
if (remaining_len >= 256) {
|
||||
if(remaining_len >= 256) {
|
||||
buff_tx[max_resp_len + 1] = 0x00;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
buff_tx[max_resp_len + 1] = remaining_len;
|
||||
}
|
||||
u2f_nfc->payload_cursor += max_resp_len;
|
||||
return max_resp_len + 2;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
memcpy(
|
||||
buff_tx,
|
||||
&u2f_nfc->payload[u2f_nfc->payload_cursor],
|
||||
@@ -66,7 +64,7 @@ u2f_callback(U2fNfc* u2f_nfc, const uint8_t* buff_rx, uint16_t buff_rx_len, uint
|
||||
u2f_msg_parse(u2f_nfc->u2f_instance, buff_rx, buff_rx_len, u2f_nfc->payload);
|
||||
|
||||
// If this is extended format, send entire response at once
|
||||
if (cmd->len[0] == 0) {
|
||||
if(cmd->len[0] == 0) {
|
||||
FURI_LOG_T(TAG, "single extended response");
|
||||
memcpy(&buff_tx, u2f_nfc->payload, u2f_nfc->payload_len);
|
||||
uint16_t len = u2f_nfc->payload_len;
|
||||
@@ -78,21 +76,19 @@ u2f_callback(U2fNfc* u2f_nfc, const uint8_t* buff_rx, uint16_t buff_rx_len, uint
|
||||
uint16_t max_resp_len = 256;
|
||||
|
||||
// If this message happens to be less than the chaining size, send it all at once.
|
||||
if ((u2f_nfc->payload_len - 2) <= max_resp_len) {
|
||||
if((u2f_nfc->payload_len - 2) <= max_resp_len) {
|
||||
FURI_LOG_T(TAG, "single short response");
|
||||
memcpy(buff_tx, u2f_nfc->payload, u2f_nfc->payload_len);
|
||||
uint16_t len = u2f_nfc->payload_len;
|
||||
u2f_nfc->payload_len = 0;
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
memcpy(buff_tx, u2f_nfc->payload, max_resp_len);
|
||||
buff_tx[max_resp_len] = 0x61;
|
||||
uint16_t remaining_len = (u2f_nfc->payload_len - 2) - max_resp_len;
|
||||
if (remaining_len >= max_resp_len) {
|
||||
if(remaining_len >= max_resp_len) {
|
||||
buff_tx[max_resp_len + 1] = 0x00;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
buff_tx[max_resp_len + 1] = remaining_len;
|
||||
}
|
||||
u2f_nfc->payload_cursor = max_resp_len;
|
||||
@@ -105,7 +101,7 @@ u2f_callback(U2fNfc* u2f_nfc, const uint8_t* buff_rx, uint16_t buff_rx_len, uint
|
||||
static int32_t u2f_nfc_worker(void* context) {
|
||||
U2fNfc* u2f_nfc = context;
|
||||
FURI_LOG_D(TAG, "Init");
|
||||
while (furi_hal_nfc_is_busy()) {
|
||||
while(furi_hal_nfc_is_busy()) {
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
FuriHalNfcDevData params = {
|
||||
@@ -122,36 +118,35 @@ static int32_t u2f_nfc_worker(void* context) {
|
||||
|
||||
FURI_LOG_D(TAG, "Start");
|
||||
|
||||
while (1) {
|
||||
while(1) {
|
||||
uint32_t flags = furi_thread_flags_wait(WorkerEvtStop, FuriFlagWaitAny, 10);
|
||||
if (flags != FuriFlagErrorTimeout) {
|
||||
if(flags != FuriFlagErrorTimeout) {
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
if (flags & WorkerEvtStop) break;
|
||||
if(flags & WorkerEvtStop) break;
|
||||
}
|
||||
if (!furi_hal_nfc_listen(¶ms, false, 200)) {
|
||||
if(!furi_hal_nfc_listen(¶ms, false, 200)) {
|
||||
FURI_LOG_T(TAG, "wtf");
|
||||
continue;
|
||||
}
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
tx_rx.tx_bits = 0;
|
||||
tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
if (!furi_hal_nfc_tx_rx(&tx_rx, 300)) continue;
|
||||
if (tx_rx.rx_bits == 0) continue;
|
||||
if(!furi_hal_nfc_tx_rx(&tx_rx, 300)) continue;
|
||||
if(tx_rx.rx_bits == 0) continue;
|
||||
u2f_nfc->payload_len = 0;
|
||||
u2f_nfc->payload_cursor = 0;
|
||||
while (true) {
|
||||
while(true) {
|
||||
uint16_t payload_len =
|
||||
u2f_callback(u2f_nfc, tx_rx.rx_data, tx_rx.rx_bits / 8, tx_rx.tx_data);
|
||||
tx_rx.rx_bits = 0;
|
||||
FURI_LOG_T(TAG, "payload_len=%d", payload_len);
|
||||
if (payload_len == 0) {
|
||||
if(payload_len == 0) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tx_rx.tx_bits = payload_len * 8;
|
||||
tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
if (!furi_hal_nfc_tx_rx(&tx_rx, 300)) break;
|
||||
if (tx_rx.rx_bits == 0) break;
|
||||
if(!furi_hal_nfc_tx_rx(&tx_rx, 300)) break;
|
||||
if(tx_rx.rx_bits == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ extern "C" {
|
||||
|
||||
#include "u2f.h"
|
||||
|
||||
typedef struct U2fNfc U2fNfc;
|
||||
typedef struct U2fNfc U2fNfc;
|
||||
|
||||
U2fNfc* u2f_nfc_start(U2fData* u2f_inst);
|
||||
U2fNfc* u2f_nfc_start(U2fData* u2f_inst);
|
||||
|
||||
void u2f_nfc_stop(U2fNfc* u2f_hid);
|
||||
void u2f_nfc_stop(U2fNfc* u2f_hid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -18,70 +18,66 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
|
||||
|
||||
canvas_draw_icon(canvas, 8, 14, &I_Drive_112x35);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if (model->display_msg == U2fMsgNotConnected) {
|
||||
if (settings->sfw_mode) {
|
||||
if(model->display_msg == U2fMsgNotConnected) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Connect_me_62x31_sfw);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connect to a device");
|
||||
}
|
||||
else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connect to a device");
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Connect_me_62x31);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy");
|
||||
}
|
||||
}
|
||||
else if (model->display_msg == U2fMsgIdle) {
|
||||
if (settings->sfw_mode) {
|
||||
} else if(model->display_msg == U2fMsgIdle) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31_sfw);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!");
|
||||
}
|
||||
}
|
||||
else if (model->display_msg == U2fMsgRegister) {
|
||||
if (settings->sfw_mode) {
|
||||
} else if(model->display_msg == U2fMsgRegister) {
|
||||
if(settings->sfw_mode) {
|
||||
elements_button_center(canvas, "OK");
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31_sfw);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register");
|
||||
}
|
||||
else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register");
|
||||
} else {
|
||||
elements_button_center(canvas, "CUM");
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press CUM to register");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press CUM to register");
|
||||
}
|
||||
}
|
||||
else if (model->display_msg == U2fMsgAuth) {
|
||||
if (settings->sfw_mode) {
|
||||
} else if(model->display_msg == U2fMsgAuth) {
|
||||
if(settings->sfw_mode) {
|
||||
elements_button_center(canvas, "OK");
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31_sfw);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to authenticate");
|
||||
}
|
||||
else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to authenticate");
|
||||
} else {
|
||||
elements_button_center(canvas, "CUM");
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press CUM to authenticate");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press CUM to authenticate");
|
||||
}
|
||||
}
|
||||
else if (model->display_msg == U2fMsgSuccess) {
|
||||
if (settings->sfw_mode) {
|
||||
} else if(model->display_msg == U2fMsgSuccess) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31_sfw);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!");
|
||||
}
|
||||
else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!");
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~");
|
||||
}
|
||||
}
|
||||
else if (model->display_msg == U2fMsgError) {
|
||||
if (settings->sfw_mode) {
|
||||
} else if(model->display_msg == U2fMsgError) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Error_62x31_sfw);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error");
|
||||
}
|
||||
else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error");
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 22, 15, &I_Error_62x31);
|
||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum");
|
||||
}
|
||||
@@ -94,10 +90,10 @@ static bool u2f_view_input_callback(InputEvent* event, void* context) {
|
||||
U2fView* u2f = context;
|
||||
bool consumed = false;
|
||||
|
||||
if (event->type == InputTypeShort) {
|
||||
if (event->key == InputKeyOk) {
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyOk) {
|
||||
consumed = true;
|
||||
if (u2f->callback != NULL) u2f->callback(InputTypeShort, u2f->context);
|
||||
if(u2f->callback != NULL) u2f->callback(InputTypeShort, u2f->context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ void reset_level(Canvas* canvas, ArkanoidState* arkanoid_state) {
|
||||
arkanoid_state->brickCount = 0;
|
||||
arkanoid_state->ball_state.released = false;
|
||||
arkanoid_state->gameStarted = false;
|
||||
|
||||
|
||||
// Reset all brick hit states
|
||||
for(unsigned int row = 0; row < arkanoid_state->ROWS; row++) {
|
||||
for(unsigned int column = 0; column < arkanoid_state->COLUMNS; column++) {
|
||||
|
||||
@@ -32,42 +32,42 @@
|
||||
/* ============================ Data structures ============================= */
|
||||
|
||||
typedef struct Ship {
|
||||
float x, /* Ship x position. */
|
||||
y, /* Ship y position. */
|
||||
vx, /* x velocity. */
|
||||
vy, /* y velocity. */
|
||||
rot; /* Current rotation. 2*PI full rotation. */
|
||||
float x, /* Ship x position. */
|
||||
y, /* Ship y position. */
|
||||
vx, /* x velocity. */
|
||||
vy, /* y velocity. */
|
||||
rot; /* Current rotation. 2*PI full rotation. */
|
||||
} Ship;
|
||||
|
||||
typedef struct Bullet {
|
||||
float x, y, vx, vy; /* Fields like in ship. */
|
||||
uint32_t ttl; /* Time to live, in ticks. */
|
||||
float x, y, vx, vy; /* Fields like in ship. */
|
||||
uint32_t ttl; /* Time to live, in ticks. */
|
||||
} Bullet;
|
||||
|
||||
typedef struct Asteroid {
|
||||
float x, y, vx, vy, rot, /* Fields like ship. */
|
||||
rot_speed, /* Angular velocity (rot speed and sense). */
|
||||
size; /* Asteroid size. */
|
||||
uint8_t shape_seed; /* Seed to give random shape. */
|
||||
float x, y, vx, vy, rot, /* Fields like ship. */
|
||||
rot_speed, /* Angular velocity (rot speed and sense). */
|
||||
size; /* Asteroid size. */
|
||||
uint8_t shape_seed; /* Seed to give random shape. */
|
||||
} Asteroid;
|
||||
|
||||
#define MAXBUL 10 /* Max bullets on the screen. */
|
||||
#define MAXAST 32 /* Max asteroids on the screen. */
|
||||
#define MAXBUL 10 /* Max bullets on the screen. */
|
||||
#define MAXAST 32 /* Max asteroids on the screen. */
|
||||
#define SHIP_HIT_ANIMATION_LEN 15
|
||||
typedef struct AsteroidsApp {
|
||||
/* GUI */
|
||||
Gui *gui;
|
||||
ViewPort *view_port; /* We just use a raw viewport and we render
|
||||
Gui* gui;
|
||||
ViewPort* view_port; /* We just use a raw viewport and we render
|
||||
everything into the low level canvas. */
|
||||
FuriMessageQueue *event_queue; /* Key press events go here. */
|
||||
FuriMessageQueue* event_queue; /* Key press events go here. */
|
||||
|
||||
/* Game state. */
|
||||
int running; /* Once false exists the app. */
|
||||
bool gameover; /* Game over status. */
|
||||
uint32_t ticks; /* Game ticks. Increments at each refresh. */
|
||||
uint32_t score; /* Game score. */
|
||||
uint32_t lives; /* Number of lives in the current game. */
|
||||
uint32_t ship_hit; /* When non zero, the ship was hit by an asteroid
|
||||
int running; /* Once false exists the app. */
|
||||
bool gameover; /* Game over status. */
|
||||
uint32_t ticks; /* Game ticks. Increments at each refresh. */
|
||||
uint32_t score; /* Game score. */
|
||||
uint32_t lives; /* Number of lives in the current game. */
|
||||
uint32_t ship_hit; /* When non zero, the ship was hit by an asteroid
|
||||
and we need to show an animation as long as
|
||||
its value is non-zero (and decrease it's value
|
||||
at each tick of animation). */
|
||||
@@ -76,26 +76,26 @@ typedef struct AsteroidsApp {
|
||||
struct Ship ship;
|
||||
|
||||
/* Bullets state. */
|
||||
struct Bullet bullets[MAXBUL]; /* Each bullet state. */
|
||||
int bullets_num; /* Active bullets. */
|
||||
uint32_t last_bullet_tick; /* Tick the last bullet was fired. */
|
||||
struct Bullet bullets[MAXBUL]; /* Each bullet state. */
|
||||
int bullets_num; /* Active bullets. */
|
||||
uint32_t last_bullet_tick; /* Tick the last bullet was fired. */
|
||||
|
||||
/* Asteroids state. */
|
||||
Asteroid asteroids[MAXAST]; /* Each asteroid state. */
|
||||
int asteroids_num; /* Active asteroids. */
|
||||
Asteroid asteroids[MAXAST]; /* Each asteroid state. */
|
||||
int asteroids_num; /* Active asteroids. */
|
||||
|
||||
uint32_t pressed[InputKeyMAX]; /* pressed[id] is true if pressed.
|
||||
Each array item contains the time
|
||||
in milliseconds the key was pressed. */
|
||||
bool fire; /* Short press detected: fire a bullet. */
|
||||
bool fire; /* Short press detected: fire a bullet. */
|
||||
} AsteroidsApp;
|
||||
|
||||
/* ============================== Prototypes ================================ */
|
||||
|
||||
// Only functions called before their definition are here.
|
||||
|
||||
void restart_game_after_gameover(AsteroidsApp *app);
|
||||
uint32_t key_pressed_time(AsteroidsApp *app, InputKey key);
|
||||
void restart_game_after_gameover(AsteroidsApp* app);
|
||||
uint32_t key_pressed_time(AsteroidsApp* app, InputKey key);
|
||||
|
||||
/* ============================ 2D drawing ================================== */
|
||||
|
||||
@@ -110,29 +110,21 @@ typedef struct Poly {
|
||||
} Poly;
|
||||
|
||||
/* Define the polygons we use. */
|
||||
Poly ShipPoly = {
|
||||
{-3, 0, 3},
|
||||
{-3, 6, -3},
|
||||
3
|
||||
};
|
||||
Poly ShipPoly = {{-3, 0, 3}, {-3, 6, -3}, 3};
|
||||
|
||||
Poly ShipFirePoly = {
|
||||
{-1.5, 0, 1.5},
|
||||
{-3, -6, -3},
|
||||
3
|
||||
};
|
||||
Poly ShipFirePoly = {{-1.5, 0, 1.5}, {-3, -6, -3}, 3};
|
||||
|
||||
/* Rotate the point of the polygon 'poly' and store the new rotated
|
||||
* polygon in 'rot'. The polygon is rotated by an angle 'a', with
|
||||
* center at 0,0. */
|
||||
void rotate_poly(Poly *rot, Poly *poly, float a) {
|
||||
void rotate_poly(Poly* rot, Poly* poly, float a) {
|
||||
/* We want to compute sin(a) and cos(a) only one time
|
||||
* for every point to rotate. It's a slow operation. */
|
||||
float sin_a = (float)sin(a);
|
||||
float cos_a = (float)cos(a);
|
||||
for (uint32_t j = 0; j < poly->points; j++) {
|
||||
rot->x[j] = poly->x[j]*cos_a - poly->y[j]*sin_a;
|
||||
rot->y[j] = poly->y[j]*cos_a + poly->x[j]*sin_a;
|
||||
for(uint32_t j = 0; j < poly->points; j++) {
|
||||
rot->x[j] = poly->x[j] * cos_a - poly->y[j] * sin_a;
|
||||
rot->y[j] = poly->y[j] * cos_a + poly->x[j] * sin_a;
|
||||
}
|
||||
rot->points = poly->points;
|
||||
}
|
||||
@@ -140,36 +132,34 @@ void rotate_poly(Poly *rot, Poly *poly, float a) {
|
||||
/* This is an 8 bit LFSR we use to generate a predictable and fast
|
||||
* pseudorandom sequence of numbers, to give a different shape to
|
||||
* each asteroid. */
|
||||
void lfsr_next(unsigned char *prev) {
|
||||
void lfsr_next(unsigned char* prev) {
|
||||
unsigned char lsb = *prev & 1;
|
||||
*prev = *prev >> 1;
|
||||
if (lsb == 1) *prev ^= 0b11000111;
|
||||
*prev ^= *prev<<7; /* Mix things a bit more. */
|
||||
if(lsb == 1) *prev ^= 0b11000111;
|
||||
*prev ^= *prev << 7; /* Mix things a bit more. */
|
||||
}
|
||||
|
||||
/* Render the polygon 'poly' at x,y, rotated by the specified angle. */
|
||||
void draw_poly(Canvas *const canvas, Poly *poly, uint8_t x, uint8_t y, float a)
|
||||
{
|
||||
void draw_poly(Canvas* const canvas, Poly* poly, uint8_t x, uint8_t y, float a) {
|
||||
Poly rot;
|
||||
rotate_poly(&rot,poly,a);
|
||||
rotate_poly(&rot, poly, a);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
for (uint32_t j = 0; j < rot.points; j++) {
|
||||
for(uint32_t j = 0; j < rot.points; j++) {
|
||||
uint32_t a = j;
|
||||
uint32_t b = j+1;
|
||||
if (b == rot.points) b = 0;
|
||||
canvas_draw_line(canvas,x+rot.x[a],y+rot.y[a],
|
||||
x+rot.x[b],y+rot.y[b]);
|
||||
uint32_t b = j + 1;
|
||||
if(b == rot.points) b = 0;
|
||||
canvas_draw_line(canvas, x + rot.x[a], y + rot.y[a], x + rot.x[b], y + rot.y[b]);
|
||||
}
|
||||
}
|
||||
|
||||
/* A bullet is just a + pixels pattern. A single pixel is not
|
||||
* visible enough. */
|
||||
void draw_bullet(Canvas *const canvas, Bullet *b) {
|
||||
canvas_draw_dot(canvas,b->x-1,b->y);
|
||||
canvas_draw_dot(canvas,b->x+1,b->y);
|
||||
canvas_draw_dot(canvas,b->x,b->y);
|
||||
canvas_draw_dot(canvas,b->x,b->y-1);
|
||||
canvas_draw_dot(canvas,b->x,b->y+1);
|
||||
void draw_bullet(Canvas* const canvas, Bullet* b) {
|
||||
canvas_draw_dot(canvas, b->x - 1, b->y);
|
||||
canvas_draw_dot(canvas, b->x + 1, b->y);
|
||||
canvas_draw_dot(canvas, b->x, b->y);
|
||||
canvas_draw_dot(canvas, b->x, b->y - 1);
|
||||
canvas_draw_dot(canvas, b->x, b->y + 1);
|
||||
}
|
||||
|
||||
/* Draw an asteroid. The asteroid shapes is computed on the fly and
|
||||
@@ -177,15 +167,15 @@ void draw_bullet(Canvas *const canvas, Bullet *b) {
|
||||
* the shape, we use an initial fixed shape that we resize according
|
||||
* to the asteroid size, perturbed according to the asteroid shape
|
||||
* seed, and finally draw it rotated of the right amount. */
|
||||
void draw_asteroid(Canvas *const canvas, Asteroid *ast) {
|
||||
void draw_asteroid(Canvas* const canvas, Asteroid* ast) {
|
||||
Poly ap;
|
||||
|
||||
/* Start with what is kinda of a circle. Note that this could be
|
||||
* stored into a template and copied here, to avoid computing
|
||||
* sin() / cos(). But the Flipper can handle it without problems. */
|
||||
uint8_t r = ast->shape_seed;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
float a = (PI*2)/8*j;
|
||||
for(int j = 0; j < 8; j++) {
|
||||
float a = (PI * 2) / 8 * j;
|
||||
|
||||
/* Before generating the point, to make the shape unique generate
|
||||
* a random factor between .7 and 1.3 to scale the distance from
|
||||
@@ -193,75 +183,73 @@ void draw_asteroid(Canvas *const canvas, Asteroid *ast) {
|
||||
* that remains always the same, so we use a predictable PRNG
|
||||
* implemented by an 8 bit shift register. */
|
||||
lfsr_next(&r);
|
||||
float scaling = .7+((float)r/255*.6);
|
||||
float scaling = .7 + ((float)r / 255 * .6);
|
||||
|
||||
ap.x[j] = (float)sin(a) * ast->size * scaling;
|
||||
ap.y[j] = (float)cos(a) * ast->size * scaling;
|
||||
}
|
||||
ap.points = 8;
|
||||
draw_poly(canvas,&ap,ast->x,ast->y,ast->rot);
|
||||
draw_poly(canvas, &ap, ast->x, ast->y, ast->rot);
|
||||
}
|
||||
|
||||
/* Draw small ships in the top-right part of the screen, one for
|
||||
* each left live. */
|
||||
void draw_left_lives(Canvas *const canvas, AsteroidsApp *app) {
|
||||
void draw_left_lives(Canvas* const canvas, AsteroidsApp* app) {
|
||||
int lives = app->lives;
|
||||
int x = SCREEN_XRES-5;
|
||||
int x = SCREEN_XRES - 5;
|
||||
|
||||
Poly mini_ship = {
|
||||
{-2, 0, 2},
|
||||
{-2, 4, -2},
|
||||
3
|
||||
};
|
||||
Poly mini_ship = {{-2, 0, 2}, {-2, 4, -2}, 3};
|
||||
while(lives--) {
|
||||
draw_poly(canvas,&mini_ship,x,6,PI);
|
||||
draw_poly(canvas, &mini_ship, x, 6, PI);
|
||||
x -= 6;
|
||||
}
|
||||
}
|
||||
|
||||
/* Given the current position, update it according to the velocity and
|
||||
* wrap it back to the other side if the object went over the screen. */
|
||||
void update_pos_by_velocity(float *x, float *y, float vx, float vy) {
|
||||
void update_pos_by_velocity(float* x, float* y, float vx, float vy) {
|
||||
/* Return back from one side to the other of the screen. */
|
||||
*x += vx;
|
||||
*y += vy;
|
||||
if (*x >= SCREEN_XRES) *x = 0;
|
||||
else if (*x < 0) *x = SCREEN_XRES-1;
|
||||
if (*y >= SCREEN_YRES) *y = 0;
|
||||
else if (*y < 0) *y = SCREEN_YRES-1;
|
||||
if(*x >= SCREEN_XRES)
|
||||
*x = 0;
|
||||
else if(*x < 0)
|
||||
*x = SCREEN_XRES - 1;
|
||||
if(*y >= SCREEN_YRES)
|
||||
*y = 0;
|
||||
else if(*y < 0)
|
||||
*y = SCREEN_YRES - 1;
|
||||
}
|
||||
|
||||
/* Render the current game screen. */
|
||||
void render_callback(Canvas *const canvas, void *ctx) {
|
||||
AsteroidsApp *app = ctx;
|
||||
void render_callback(Canvas* const canvas, void* ctx) {
|
||||
AsteroidsApp* app = ctx;
|
||||
|
||||
/* Clear screen. */
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 0, 0, SCREEN_XRES-1, SCREEN_YRES-1);
|
||||
canvas_draw_box(canvas, 0, 0, SCREEN_XRES - 1, SCREEN_YRES - 1);
|
||||
|
||||
/* Draw score. */
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
char score[32];
|
||||
snprintf(score,sizeof(score),"%lu",app->score);
|
||||
snprintf(score, sizeof(score), "%lu", app->score);
|
||||
canvas_draw_str(canvas, 0, 8, score);
|
||||
|
||||
/* Draw left ships. */
|
||||
draw_left_lives(canvas,app);
|
||||
draw_left_lives(canvas, app);
|
||||
|
||||
/* Draw ship, asteroids, bullets. */
|
||||
draw_poly(canvas,&ShipPoly,app->ship.x,app->ship.y,app->ship.rot);
|
||||
if (key_pressed_time(app,InputKeyOk) > SHIP_ACCELERATION_KEYPRESS_TIME)
|
||||
draw_poly(canvas,&ShipFirePoly,app->ship.x,app->ship.y,app->ship.rot);
|
||||
draw_poly(canvas, &ShipPoly, app->ship.x, app->ship.y, app->ship.rot);
|
||||
if(key_pressed_time(app, InputKeyOk) > SHIP_ACCELERATION_KEYPRESS_TIME)
|
||||
draw_poly(canvas, &ShipFirePoly, app->ship.x, app->ship.y, app->ship.rot);
|
||||
|
||||
for (int j = 0; j < app->bullets_num; j++)
|
||||
draw_bullet(canvas,&app->bullets[j]);
|
||||
for(int j = 0; j < app->bullets_num; j++) draw_bullet(canvas, &app->bullets[j]);
|
||||
|
||||
for (int j = 0; j < app->asteroids_num; j++)
|
||||
draw_asteroid(canvas,&app->asteroids[j]);
|
||||
for(int j = 0; j < app->asteroids_num; j++) draw_asteroid(canvas, &app->asteroids[j]);
|
||||
|
||||
/* Game over text. */
|
||||
if (app->gameover) {
|
||||
if(app->gameover) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 28, 35, "GAME OVER");
|
||||
@@ -273,9 +261,9 @@ void render_callback(Canvas *const canvas, void *ctx) {
|
||||
/* ============================ Game logic ================================== */
|
||||
|
||||
float distance(float x1, float y1, float x2, float y2) {
|
||||
float dx = x1-x2;
|
||||
float dy = y1-y2;
|
||||
return sqrt(dx*dx+dy*dy);
|
||||
float dx = x1 - x2;
|
||||
float dy = y1 - y2;
|
||||
return sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/* Detect a collision between the object at x1,y1 of radius r1 and
|
||||
@@ -289,10 +277,7 @@ float distance(float x1, float y1, float x2, float y2) {
|
||||
* spheres (this is why this function only takes the radius). This
|
||||
* is, after all, kinda accurate for asteroids, for bullets, and
|
||||
* even for the ship "core" itself. */
|
||||
bool objects_are_colliding(float x1, float y1, float r1,
|
||||
float x2, float y2, float r2,
|
||||
float factor)
|
||||
{
|
||||
bool objects_are_colliding(float x1, float y1, float r1, float x2, float y2, float r2, float factor) {
|
||||
/* The objects are colliding if the distance between object 1 and 2
|
||||
* is smaller than the sum of the two radiuses r1 and r2.
|
||||
* So it would be like: sqrt((x1-x2)^2+(y1-y2)^2) < r1+r2.
|
||||
@@ -301,24 +286,24 @@ bool objects_are_colliding(float x1, float y1, float r1,
|
||||
* the comparison like this:
|
||||
*
|
||||
* (x1-x2)^2+(y1-y2)^2 < (r1+r2)^2. */
|
||||
float dx = (x1-x2)*factor;
|
||||
float dy = (y1-y2)*factor;
|
||||
float rsum = r1+r2;
|
||||
return dx*dx+dy*dy < rsum*rsum;
|
||||
float dx = (x1 - x2) * factor;
|
||||
float dy = (y1 - y2) * factor;
|
||||
float rsum = r1 + r2;
|
||||
return dx * dx + dy * dy < rsum * rsum;
|
||||
}
|
||||
|
||||
/* Create a new bullet headed in the same direction of the ship. */
|
||||
void ship_fire_bullet(AsteroidsApp *app) {
|
||||
if (app->bullets_num == MAXBUL) return;
|
||||
Bullet *b = &app->bullets[app->bullets_num];
|
||||
void ship_fire_bullet(AsteroidsApp* app) {
|
||||
if(app->bullets_num == MAXBUL) return;
|
||||
Bullet* b = &app->bullets[app->bullets_num];
|
||||
b->x = app->ship.x;
|
||||
b->y = app->ship.y;
|
||||
b->vx = -sin(app->ship.rot);
|
||||
b->vy = cos(app->ship.rot);
|
||||
|
||||
/* Ship should fire from its head, not in the middle. */
|
||||
b->x += b->vx*5;
|
||||
b->y += b->vy*5;
|
||||
b->x += b->vx * 5;
|
||||
b->y += b->vy * 5;
|
||||
|
||||
/* Give the bullet some velocity (for now the vector is just
|
||||
* normalized to 1). */
|
||||
@@ -336,68 +321,68 @@ void ship_fire_bullet(AsteroidsApp *app) {
|
||||
}
|
||||
|
||||
/* Remove the specified bullet by id (index in the array). */
|
||||
void remove_bullet(AsteroidsApp *app, int bid) {
|
||||
void remove_bullet(AsteroidsApp* app, int bid) {
|
||||
/* Replace the top bullet with the empty space left
|
||||
* by the removal of this bullet. This way we always take the
|
||||
* array dense, which is an advantage when looping. */
|
||||
int n = --app->bullets_num;
|
||||
if (n && bid != n) app->bullets[bid] = app->bullets[n];
|
||||
if(n && bid != n) app->bullets[bid] = app->bullets[n];
|
||||
}
|
||||
|
||||
/* Create a new asteroid, away from the ship. Return the
|
||||
* pointer to the asteroid object, so that the caller can change
|
||||
* certain things of the asteroid if needed. */
|
||||
Asteroid *add_asteroid(AsteroidsApp *app) {
|
||||
if (app->asteroids_num == MAXAST) return NULL;
|
||||
float size = 4+rand()%15;
|
||||
Asteroid* add_asteroid(AsteroidsApp* app) {
|
||||
if(app->asteroids_num == MAXAST) return NULL;
|
||||
float size = 4 + rand() % 15;
|
||||
float min_distance = 20;
|
||||
float x,y;
|
||||
float x, y;
|
||||
do {
|
||||
x = rand() % SCREEN_XRES;
|
||||
y = rand() % SCREEN_YRES;
|
||||
} while(distance(app->ship.x,app->ship.y,x,y) < min_distance+size);
|
||||
Asteroid *a = &app->asteroids[app->asteroids_num++];
|
||||
} while(distance(app->ship.x, app->ship.y, x, y) < min_distance + size);
|
||||
Asteroid* a = &app->asteroids[app->asteroids_num++];
|
||||
a->x = x;
|
||||
a->y = y;
|
||||
a->vx = 2*(-.5 + ((float)rand()/RAND_MAX));
|
||||
a->vy = 2*(-.5 + ((float)rand()/RAND_MAX));
|
||||
a->vx = 2 * (-.5 + ((float)rand() / RAND_MAX));
|
||||
a->vy = 2 * (-.5 + ((float)rand() / RAND_MAX));
|
||||
a->size = size;
|
||||
a->rot = 0;
|
||||
a->rot_speed = ((float)rand()/RAND_MAX)/10;
|
||||
if (app->ticks & 1) a->rot_speed = -(a->rot_speed);
|
||||
a->rot_speed = ((float)rand() / RAND_MAX) / 10;
|
||||
if(app->ticks & 1) a->rot_speed = -(a->rot_speed);
|
||||
a->shape_seed = rand() & 255;
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Remove the specified asteroid by id (index in the array). */
|
||||
void remove_asteroid(AsteroidsApp *app, int id) {
|
||||
void remove_asteroid(AsteroidsApp* app, int id) {
|
||||
/* Replace the top asteroid with the empty space left
|
||||
* by the removal of this one. This way we always take the
|
||||
* array dense, which is an advantage when looping. */
|
||||
int n = --app->asteroids_num;
|
||||
if (n && id != n) app->asteroids[id] = app->asteroids[n];
|
||||
if(n && id != n) app->asteroids[id] = app->asteroids[n];
|
||||
}
|
||||
|
||||
/* Called when an asteroid was reached by a bullet. The asteroid
|
||||
* hit is the one with the specified 'id'. */
|
||||
void asteroid_was_hit(AsteroidsApp *app, int id) {
|
||||
void asteroid_was_hit(AsteroidsApp* app, int id) {
|
||||
float sizelimit = 6; // Smaller than that, they disappear in one shot.
|
||||
Asteroid *a = &app->asteroids[id];
|
||||
Asteroid* a = &app->asteroids[id];
|
||||
|
||||
/* Asteroid is large enough to break into fragments. */
|
||||
float size = a->size;
|
||||
float x = a->x, y = a->y;
|
||||
remove_asteroid(app,id);
|
||||
if (size > sizelimit) {
|
||||
remove_asteroid(app, id);
|
||||
if(size > sizelimit) {
|
||||
int max_fragments = size / sizelimit;
|
||||
int fragments = 2+rand()%max_fragments;
|
||||
float newsize = size/fragments;
|
||||
if (newsize < 2) newsize = 2;
|
||||
for (int j = 0; j < fragments; j++) {
|
||||
int fragments = 2 + rand() % max_fragments;
|
||||
float newsize = size / fragments;
|
||||
if(newsize < 2) newsize = 2;
|
||||
for(int j = 0; j < fragments; j++) {
|
||||
a = add_asteroid(app);
|
||||
if (a == NULL) break; // Too many asteroids on screen.
|
||||
a->x = x + -(size/2) + rand() % (int)newsize;
|
||||
a->y = y + -(size/2) + rand() % (int)newsize;
|
||||
if(a == NULL) break; // Too many asteroids on screen.
|
||||
a->x = x + -(size / 2) + rand() % (int)newsize;
|
||||
a->y = y + -(size / 2) + rand() % (int)newsize;
|
||||
a->size = newsize;
|
||||
}
|
||||
} else {
|
||||
@@ -407,18 +392,19 @@ void asteroid_was_hit(AsteroidsApp *app, int id) {
|
||||
|
||||
/* Set game over state. When in game-over mode, the game displays a
|
||||
* game over text with a background of many asteroids floating around. */
|
||||
void game_over(AsteroidsApp *app) {
|
||||
void game_over(AsteroidsApp* app) {
|
||||
restart_game_after_gameover(app);
|
||||
app->gameover = true;
|
||||
int asteroids = 8;
|
||||
while(asteroids-- && add_asteroid(app) != NULL);
|
||||
while(asteroids-- && add_asteroid(app) != NULL)
|
||||
;
|
||||
}
|
||||
|
||||
/* Function called when a collision between the asteroid and the
|
||||
* ship is detected. */
|
||||
void ship_was_hit(AsteroidsApp *app) {
|
||||
void ship_was_hit(AsteroidsApp* app) {
|
||||
app->ship_hit = SHIP_HIT_ANIMATION_LEN;
|
||||
if (app->lives) {
|
||||
if(app->lives) {
|
||||
app->lives--;
|
||||
} else {
|
||||
game_over(app);
|
||||
@@ -427,10 +413,10 @@ void ship_was_hit(AsteroidsApp *app) {
|
||||
|
||||
/* Restart game after the ship is hit. Will reset the ship position, bullets
|
||||
* and asteroids to restart the game. */
|
||||
void restart_game(AsteroidsApp *app) {
|
||||
void restart_game(AsteroidsApp* app) {
|
||||
app->ship.x = SCREEN_XRES / 2;
|
||||
app->ship.y = SCREEN_YRES / 2;
|
||||
app->ship.rot = PI; /* Start headed towards top. */
|
||||
app->ship.rot = PI; /* Start headed towards top. */
|
||||
app->ship.vx = 0;
|
||||
app->ship.vy = 0;
|
||||
app->bullets_num = 0;
|
||||
@@ -440,22 +426,22 @@ void restart_game(AsteroidsApp *app) {
|
||||
|
||||
/* Called after game over to restart the game. This function
|
||||
* also calls restart_game(). */
|
||||
void restart_game_after_gameover(AsteroidsApp *app) {
|
||||
void restart_game_after_gameover(AsteroidsApp* app) {
|
||||
app->gameover = false;
|
||||
app->ticks = 0;
|
||||
app->score = 0;
|
||||
app->ship_hit = 0;
|
||||
app->lives = GAME_START_LIVES-1; /* -1 to account for current one. */
|
||||
app->lives = GAME_START_LIVES - 1; /* -1 to account for current one. */
|
||||
restart_game(app);
|
||||
}
|
||||
|
||||
/* Move bullets. */
|
||||
void update_bullets_position(AsteroidsApp *app) {
|
||||
for (int j = 0; j < app->bullets_num; j++) {
|
||||
update_pos_by_velocity(&app->bullets[j].x,&app->bullets[j].y,
|
||||
app->bullets[j].vx,app->bullets[j].vy);
|
||||
if (--app->bullets[j].ttl == 0) {
|
||||
remove_bullet(app,j);
|
||||
void update_bullets_position(AsteroidsApp* app) {
|
||||
for(int j = 0; j < app->bullets_num; j++) {
|
||||
update_pos_by_velocity(
|
||||
&app->bullets[j].x, &app->bullets[j].y, app->bullets[j].vx, app->bullets[j].vy);
|
||||
if(--app->bullets[j].ttl == 0) {
|
||||
remove_bullet(app, j);
|
||||
j--; /* Process this bullet index again: the removal will
|
||||
fill it with the top bullet to take the array dense. */
|
||||
}
|
||||
@@ -463,28 +449,28 @@ void update_bullets_position(AsteroidsApp *app) {
|
||||
}
|
||||
|
||||
/* Move asteroids. */
|
||||
void update_asteroids_position(AsteroidsApp *app) {
|
||||
for (int j = 0; j < app->asteroids_num; j++) {
|
||||
update_pos_by_velocity(&app->asteroids[j].x,&app->asteroids[j].y,
|
||||
app->asteroids[j].vx,app->asteroids[j].vy);
|
||||
void update_asteroids_position(AsteroidsApp* app) {
|
||||
for(int j = 0; j < app->asteroids_num; j++) {
|
||||
update_pos_by_velocity(
|
||||
&app->asteroids[j].x, &app->asteroids[j].y, app->asteroids[j].vx, app->asteroids[j].vy);
|
||||
app->asteroids[j].rot += app->asteroids[j].rot_speed;
|
||||
if (app->asteroids[j].rot < 0) app->asteroids[j].rot = 2*PI;
|
||||
else if (app->asteroids[j].rot > 2*PI) app->asteroids[j].rot = 0;
|
||||
if(app->asteroids[j].rot < 0)
|
||||
app->asteroids[j].rot = 2 * PI;
|
||||
else if(app->asteroids[j].rot > 2 * PI)
|
||||
app->asteroids[j].rot = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Collision detection and game state update based on collisions. */
|
||||
void detect_collisions(AsteroidsApp *app) {
|
||||
void detect_collisions(AsteroidsApp* app) {
|
||||
/* Detect collision between bullet and asteroid. */
|
||||
for (int j = 0; j < app->bullets_num; j++) {
|
||||
Bullet *b = &app->bullets[j];
|
||||
for (int i = 0; i < app->asteroids_num; i++) {
|
||||
Asteroid *a = &app->asteroids[i];
|
||||
if (objects_are_colliding(a->x, a->y, a->size,
|
||||
b->x, b->y, 1.5, 1))
|
||||
{
|
||||
asteroid_was_hit(app,i);
|
||||
remove_bullet(app,j);
|
||||
for(int j = 0; j < app->bullets_num; j++) {
|
||||
Bullet* b = &app->bullets[j];
|
||||
for(int i = 0; i < app->asteroids_num; i++) {
|
||||
Asteroid* a = &app->asteroids[i];
|
||||
if(objects_are_colliding(a->x, a->y, a->size, b->x, b->y, 1.5, 1)) {
|
||||
asteroid_was_hit(app, i);
|
||||
remove_bullet(app, j);
|
||||
/* The bullet no longer exist. Break the loop.
|
||||
* However we want to start processing from the
|
||||
* same bullet index, since now it is used by
|
||||
@@ -496,11 +482,9 @@ void detect_collisions(AsteroidsApp *app) {
|
||||
}
|
||||
|
||||
/* Detect collision between ship and asteroid. */
|
||||
for (int j = 0; j < app->asteroids_num; j++) {
|
||||
Asteroid *a = &app->asteroids[j];
|
||||
if (objects_are_colliding(a->x, a->y, a->size,
|
||||
app->ship.x, app->ship.y, 4, 1))
|
||||
{
|
||||
for(int j = 0; j < app->asteroids_num; j++) {
|
||||
Asteroid* a = &app->asteroids[j];
|
||||
if(objects_are_colliding(a->x, a->y, a->size, app->ship.x, app->ship.y, 4, 1)) {
|
||||
ship_was_hit(app);
|
||||
break;
|
||||
}
|
||||
@@ -513,26 +497,26 @@ void detect_collisions(AsteroidsApp *app) {
|
||||
* on velocity. Detect collisions. Update the score and so forth.
|
||||
*
|
||||
* Each time this function is called, app->tick is incremented. */
|
||||
void game_tick(void *ctx) {
|
||||
AsteroidsApp *app = ctx;
|
||||
void game_tick(void* ctx) {
|
||||
AsteroidsApp* app = ctx;
|
||||
|
||||
/* There are two special screens:
|
||||
*
|
||||
* 1. Ship was hit, we frozen the game as long as ship_hit isn't zero
|
||||
* again, and show an animation of a rotating ship. */
|
||||
if (app->ship_hit) {
|
||||
if(app->ship_hit) {
|
||||
app->ship.rot += 0.5;
|
||||
app->ship_hit--;
|
||||
view_port_update(app->view_port);
|
||||
if (app->ship_hit == 0) {
|
||||
if(app->ship_hit == 0) {
|
||||
restart_game(app);
|
||||
}
|
||||
return;
|
||||
} else if (app->gameover) {
|
||||
/* 2. Game over. We need to update only background asteroids. In this
|
||||
} else if(app->gameover) {
|
||||
/* 2. Game over. We need to update only background asteroids. In this
|
||||
* state the game just displays a GAME OVER text with the floating
|
||||
* asteroids in background. */
|
||||
if (key_pressed_time(app,InputKeyOk) > 100) {
|
||||
if(key_pressed_time(app, InputKeyOk) > 100) {
|
||||
restart_game_after_gameover(app);
|
||||
}
|
||||
update_asteroids_position(app);
|
||||
@@ -541,12 +525,12 @@ void game_tick(void *ctx) {
|
||||
}
|
||||
|
||||
/* Handle key presses. */
|
||||
if (app->pressed[InputKeyLeft]) app->ship.rot -= .35;
|
||||
if (app->pressed[InputKeyRight]) app->ship.rot += .35;
|
||||
if (key_pressed_time(app,InputKeyOk) > SHIP_ACCELERATION_KEYPRESS_TIME) {
|
||||
app->ship.vx -= 0.5*(float)sin(app->ship.rot);
|
||||
app->ship.vy += 0.5*(float)cos(app->ship.rot);
|
||||
} else if (app->pressed[InputKeyDown]) {
|
||||
if(app->pressed[InputKeyLeft]) app->ship.rot -= .35;
|
||||
if(app->pressed[InputKeyRight]) app->ship.rot += .35;
|
||||
if(key_pressed_time(app, InputKeyOk) > SHIP_ACCELERATION_KEYPRESS_TIME) {
|
||||
app->ship.vx -= 0.5 * (float)sin(app->ship.rot);
|
||||
app->ship.vy += 0.5 * (float)cos(app->ship.rot);
|
||||
} else if(app->pressed[InputKeyDown]) {
|
||||
app->ship.vx *= 0.75;
|
||||
app->ship.vy *= 0.75;
|
||||
}
|
||||
@@ -554,10 +538,10 @@ void game_tick(void *ctx) {
|
||||
/* Fire a bullet if needed. app->fire is set in
|
||||
* asteroids_update_keypress_state() since depends on exact
|
||||
* pressure timing. */
|
||||
if (app->fire) {
|
||||
if(app->fire) {
|
||||
uint32_t bullet_min_period = 200; // In milliseconds
|
||||
uint32_t now = furi_get_tick();
|
||||
if (now - app->last_bullet_tick >= bullet_min_period) {
|
||||
if(now - app->last_bullet_tick >= bullet_min_period) {
|
||||
ship_fire_bullet(app);
|
||||
app->last_bullet_tick = now;
|
||||
}
|
||||
@@ -565,7 +549,7 @@ void game_tick(void *ctx) {
|
||||
}
|
||||
|
||||
/* Update positions and detect collisions. */
|
||||
update_pos_by_velocity(&app->ship.x,&app->ship.y,app->ship.vx,app->ship.vy);
|
||||
update_pos_by_velocity(&app->ship.x, &app->ship.y, app->ship.vx, app->ship.vy);
|
||||
update_bullets_position(app);
|
||||
update_asteroids_position(app);
|
||||
detect_collisions(app);
|
||||
@@ -573,9 +557,7 @@ void game_tick(void *ctx) {
|
||||
/* From time to time, create a new asteroid. The more asteroids
|
||||
* already on the screen, the smaller probability of creating
|
||||
* a new one. */
|
||||
if (app->asteroids_num == 0 ||
|
||||
(random() % 5000) < (30/(1+app->asteroids_num)))
|
||||
{
|
||||
if(app->asteroids_num == 0 || (random() % 5000) < (30 / (1 + app->asteroids_num))) {
|
||||
add_asteroid(app);
|
||||
}
|
||||
|
||||
@@ -587,16 +569,15 @@ void game_tick(void *ctx) {
|
||||
|
||||
/* Here all we do is putting the events into the queue that will be handled
|
||||
* in the while() loop of the app entry point function. */
|
||||
void input_callback(InputEvent* input_event, void* ctx)
|
||||
{
|
||||
AsteroidsApp *app = ctx;
|
||||
furi_message_queue_put(app->event_queue,input_event,FuriWaitForever);
|
||||
void input_callback(InputEvent* input_event, void* ctx) {
|
||||
AsteroidsApp* app = ctx;
|
||||
furi_message_queue_put(app->event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
/* Allocate the application state and initialize a number of stuff.
|
||||
* This is called in the entry point to create the application state. */
|
||||
AsteroidsApp* asteroids_app_alloc() {
|
||||
AsteroidsApp *app = malloc(sizeof(AsteroidsApp));
|
||||
AsteroidsApp* app = malloc(sizeof(AsteroidsApp));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->view_port = view_port_alloc();
|
||||
@@ -605,16 +586,16 @@ AsteroidsApp* asteroids_app_alloc() {
|
||||
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
|
||||
app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
app->running = 1; /* Turns 0 when back is pressed. */
|
||||
app->running = 1; /* Turns 0 when back is pressed. */
|
||||
restart_game_after_gameover(app);
|
||||
memset(app->pressed,0,sizeof(app->pressed));
|
||||
memset(app->pressed, 0, sizeof(app->pressed));
|
||||
return app;
|
||||
}
|
||||
|
||||
/* Free what the application allocated. It is not clear to me if the
|
||||
* Flipper OS, once the application exits, will be able to reclaim space
|
||||
* even if we forget to free something here. */
|
||||
void asteroids_app_free(AsteroidsApp *app) {
|
||||
void asteroids_app_free(AsteroidsApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// View related.
|
||||
@@ -630,28 +611,27 @@ void asteroids_app_free(AsteroidsApp *app) {
|
||||
|
||||
/* Return the time in milliseconds the specified key is continuously
|
||||
* pressed. Or 0 if it is not pressed. */
|
||||
uint32_t key_pressed_time(AsteroidsApp *app, InputKey key) {
|
||||
return app->pressed[key] == 0 ? 0 :
|
||||
furi_get_tick() - app->pressed[key];
|
||||
uint32_t key_pressed_time(AsteroidsApp* app, InputKey key) {
|
||||
return app->pressed[key] == 0 ? 0 : furi_get_tick() - app->pressed[key];
|
||||
}
|
||||
|
||||
/* Handle keys interaction. */
|
||||
void asteroids_update_keypress_state(AsteroidsApp *app, InputEvent input) {
|
||||
if (input.type == InputTypePress) {
|
||||
void asteroids_update_keypress_state(AsteroidsApp* app, InputEvent input) {
|
||||
if(input.type == InputTypePress) {
|
||||
app->pressed[input.key] = furi_get_tick();
|
||||
} else if (input.type == InputTypeRelease) {
|
||||
uint32_t dur = key_pressed_time(app,input.key);
|
||||
} else if(input.type == InputTypeRelease) {
|
||||
uint32_t dur = key_pressed_time(app, input.key);
|
||||
app->pressed[input.key] = 0;
|
||||
if (dur < 200 && input.key == InputKeyOk) app->fire = true;
|
||||
if(dur < 200 && input.key == InputKeyOk) app->fire = true;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t asteroids_app_entry(void* p) {
|
||||
UNUSED(p);
|
||||
AsteroidsApp *app = asteroids_app_alloc();
|
||||
AsteroidsApp* app = asteroids_app_alloc();
|
||||
|
||||
/* Create a timer. We do data analysis in the callback. */
|
||||
FuriTimer *timer = furi_timer_alloc(game_tick, FuriTimerTypePeriodic, app);
|
||||
FuriTimer* timer = furi_timer_alloc(game_tick, FuriTimerTypePeriodic, app);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10);
|
||||
|
||||
/* This is the main event loop: here we get the events that are pushed
|
||||
@@ -660,25 +640,24 @@ int32_t asteroids_app_entry(void* p) {
|
||||
InputEvent input;
|
||||
while(app->running) {
|
||||
FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
|
||||
if (qstat == FuriStatusOk) {
|
||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u",
|
||||
input.type, input.key);
|
||||
if(qstat == FuriStatusOk) {
|
||||
if(DEBUG_MSG)
|
||||
FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
|
||||
|
||||
/* Handle navigation here. Then handle view-specific inputs
|
||||
* in the view specific handling function. */
|
||||
if (input.type == InputTypeShort &&
|
||||
input.key == InputKeyBack)
|
||||
{
|
||||
if(input.type == InputTypeShort && input.key == InputKeyBack) {
|
||||
app->running = 0;
|
||||
} else {
|
||||
asteroids_update_keypress_state(app,input);
|
||||
asteroids_update_keypress_state(app, input);
|
||||
}
|
||||
} else {
|
||||
/* Useful to understand if the app is still alive when it
|
||||
* does not respond because of bugs. */
|
||||
if (DEBUG_MSG) {
|
||||
static int c = 0; c++;
|
||||
if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
|
||||
if(DEBUG_MSG) {
|
||||
static int c = 0;
|
||||
c++;
|
||||
if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ static void cligui_tick_event_cb(void* context) {
|
||||
furi_string_push_back(app->text_box_store, c);
|
||||
}
|
||||
}
|
||||
if (available > 0) {
|
||||
if(available > 0) {
|
||||
text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
|
||||
}
|
||||
// Set input header stuff
|
||||
size_t len = furi_string_size(app->text_box_store);
|
||||
size_t idx = len - 2;
|
||||
while (idx > 0) {
|
||||
if (furi_string_get_char(app->text_box_store, idx) == '\n') {
|
||||
while(idx > 0) {
|
||||
if(furi_string_get_char(app->text_box_store, idx) == '\n') {
|
||||
idx++;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -9,5 +9,4 @@ void console_output_input_handler(CliguiApp* app, InputEvent* event) {
|
||||
char eot = 0x03;
|
||||
furi_stream_buffer_send(app->data->streams.app_tx, &eot, 1, FuriWaitForever);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,7 +88,6 @@ typedef struct {
|
||||
void* pubsub;
|
||||
} Loader_internal;
|
||||
|
||||
|
||||
typedef struct {
|
||||
CliCallback callback;
|
||||
void* context;
|
||||
|
||||
@@ -490,8 +490,9 @@ int32_t dap_link_app(void* p) {
|
||||
if(furi_hal_usb_is_locked()) {
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
if (settings->sfw_mode) {
|
||||
dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
|
||||
if(settings->sfw_mode) {
|
||||
dialog_message_set_header(
|
||||
message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(
|
||||
message,
|
||||
"Disconnect from\nPC or phone to\nuse this function.",
|
||||
@@ -499,8 +500,7 @@ int32_t dap_link_app(void* p) {
|
||||
30,
|
||||
AlignLeft,
|
||||
AlignTop);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
dialog_message_set_header(message, "I am not\na whore!", 3, 2, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(
|
||||
message,
|
||||
|
||||
@@ -209,10 +209,9 @@ void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(model->history_item == 0) {
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
|
||||
}
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
@@ -236,10 +235,9 @@ void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) {
|
||||
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
|
||||
}
|
||||
canvas_draw_dot(canvas, 17, 61);
|
||||
|
||||
@@ -28,34 +28,39 @@ typedef struct {
|
||||
InputEvent input;
|
||||
} EventApp;
|
||||
|
||||
typedef struct Players
|
||||
{
|
||||
uint8_t player1_X,player1_Y,player2_X,player2_Y;
|
||||
uint16_t player1_score,player2_score;
|
||||
uint8_t ball_X,ball_Y,ball_X_speed,ball_Y_speed,ball_X_direction,ball_Y_direction;
|
||||
typedef struct Players {
|
||||
uint8_t player1_X, player1_Y, player2_X, player2_Y;
|
||||
uint16_t player1_score, player2_score;
|
||||
uint8_t ball_X, ball_Y, ball_X_speed, ball_Y_speed, ball_X_direction, ball_Y_direction;
|
||||
} Players;
|
||||
|
||||
static void draw_callback(Canvas* canvas, void* ctx)
|
||||
{
|
||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
Players* playersMutex = (Players*)acquire_mutex_block((ValueMutex*)ctx);
|
||||
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
canvas_draw_box(canvas, playersMutex->player1_X, playersMutex->player1_Y, PAD_SIZE_X, PAD_SIZE_Y);
|
||||
canvas_draw_box(canvas, playersMutex->player2_X, playersMutex->player2_Y, PAD_SIZE_X, PAD_SIZE_Y);
|
||||
canvas_draw_box(
|
||||
canvas, playersMutex->player1_X, playersMutex->player1_Y, PAD_SIZE_X, PAD_SIZE_Y);
|
||||
canvas_draw_box(
|
||||
canvas, playersMutex->player2_X, playersMutex->player2_Y, PAD_SIZE_X, PAD_SIZE_Y);
|
||||
canvas_draw_box(canvas, playersMutex->ball_X, playersMutex->ball_Y, BALL_SIZE, BALL_SIZE);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_set_font_direction(canvas, CanvasDirectionBottomToTop);
|
||||
char buffer[16];
|
||||
snprintf(buffer, sizeof(buffer), "%u - %u", playersMutex->player1_score, playersMutex->player2_score);
|
||||
canvas_draw_str_aligned(canvas, SCREEN_SIZE_X/2+15, SCREEN_SIZE_Y/2+2, AlignCenter, AlignTop, buffer);
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%u - %u",
|
||||
playersMutex->player1_score,
|
||||
playersMutex->player2_score);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, SCREEN_SIZE_X / 2 + 15, SCREEN_SIZE_Y / 2 + 2, AlignCenter, AlignTop, buffer);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, playersMutex);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, void* ctx)
|
||||
{
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
EventApp event = {.type = EventTypeInput, .input = *input_event};
|
||||
@@ -69,48 +74,44 @@ static void clock_tick(void* ctx) {
|
||||
furi_message_queue_put(queue, &event, 0);
|
||||
}
|
||||
|
||||
bool insidePad(uint8_t x, uint8_t y, uint8_t playerX, uint8_t playerY)
|
||||
{
|
||||
if (x >= playerX && x <= playerX+PAD_SIZE_X && y >= playerY && y <= playerY+PAD_SIZE_Y) return true;
|
||||
bool insidePad(uint8_t x, uint8_t y, uint8_t playerX, uint8_t playerY) {
|
||||
if(x >= playerX && x <= playerX + PAD_SIZE_X && y >= playerY && y <= playerY + PAD_SIZE_Y)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t changeSpeed()
|
||||
{
|
||||
uint8_t changeSpeed() {
|
||||
uint8_t randomuint8[1];
|
||||
while(1)
|
||||
{
|
||||
furi_hal_random_fill_buf(randomuint8,1);
|
||||
randomuint8[0] &= 0b00000011;
|
||||
if (randomuint8[0] >= 1) break;
|
||||
while(1) {
|
||||
furi_hal_random_fill_buf(randomuint8, 1);
|
||||
randomuint8[0] &= 0b00000011;
|
||||
if(randomuint8[0] >= 1) break;
|
||||
}
|
||||
return randomuint8[0];
|
||||
}
|
||||
|
||||
uint8_t changeDirection()
|
||||
{
|
||||
uint8_t changeDirection() {
|
||||
uint8_t randomuint8[1];
|
||||
furi_hal_random_fill_buf(randomuint8,1);
|
||||
furi_hal_random_fill_buf(randomuint8, 1);
|
||||
randomuint8[0] &= 0b1;
|
||||
return randomuint8[0];
|
||||
return randomuint8[0];
|
||||
}
|
||||
|
||||
int32_t flipper_pong_app()
|
||||
{
|
||||
int32_t flipper_pong_app() {
|
||||
EventApp event;
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp));
|
||||
|
||||
Players players;
|
||||
players.player1_X = SCREEN_SIZE_X-PAD_SIZE_X-1;
|
||||
players.player1_Y = SCREEN_SIZE_Y/2 - PAD_SIZE_Y/2;
|
||||
players.player1_X = SCREEN_SIZE_X - PAD_SIZE_X - 1;
|
||||
players.player1_Y = SCREEN_SIZE_Y / 2 - PAD_SIZE_Y / 2;
|
||||
players.player1_score = 0;
|
||||
|
||||
players.player2_X = 1;
|
||||
players.player2_Y = SCREEN_SIZE_Y/2 - PAD_SIZE_Y/2;
|
||||
players.player2_Y = SCREEN_SIZE_Y / 2 - PAD_SIZE_Y / 2;
|
||||
players.player2_score = 0;
|
||||
|
||||
players.ball_X = SCREEN_SIZE_X/2 - BALL_SIZE/2;
|
||||
players.ball_Y = SCREEN_SIZE_Y/2 - BALL_SIZE/2;
|
||||
players.ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2;
|
||||
players.ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2;
|
||||
players.ball_X_speed = 1;
|
||||
players.ball_Y_speed = 1;
|
||||
players.ball_X_direction = changeDirection();
|
||||
@@ -127,112 +128,110 @@ int32_t flipper_pong_app()
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue);
|
||||
furi_timer_start(timer, 1000/FPS);
|
||||
furi_timer_start(timer, 1000 / FPS);
|
||||
|
||||
while(1)
|
||||
{
|
||||
while(1) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever);
|
||||
Players* playersMutex = (Players*)acquire_mutex_block(&state_mutex);
|
||||
|
||||
if (event_status == FuriStatusOk)
|
||||
{
|
||||
if(event.type == EventTypeInput)
|
||||
{
|
||||
if(event.input.key == InputKeyBack)
|
||||
{
|
||||
if(event_status == FuriStatusOk) {
|
||||
if(event.type == EventTypeInput) {
|
||||
if(event.input.key == InputKeyBack) {
|
||||
release_mutex(&state_mutex, playersMutex);
|
||||
break;
|
||||
} else if(event.input.key == InputKeyUp) {
|
||||
if(playersMutex->player1_Y >= 1 + PLAYER1_PAD_SPEED)
|
||||
playersMutex->player1_Y -= PLAYER1_PAD_SPEED;
|
||||
else
|
||||
playersMutex->player1_Y = 1;
|
||||
} else if(event.input.key == InputKeyDown) {
|
||||
if(playersMutex->player1_Y <=
|
||||
SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER1_PAD_SPEED - 1)
|
||||
playersMutex->player1_Y += PLAYER1_PAD_SPEED;
|
||||
else
|
||||
playersMutex->player1_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1;
|
||||
}
|
||||
else if(event.input.key == InputKeyUp)
|
||||
{
|
||||
if (playersMutex->player1_Y >= 1+PLAYER1_PAD_SPEED) playersMutex->player1_Y -= PLAYER1_PAD_SPEED;
|
||||
else playersMutex->player1_Y = 1;
|
||||
}
|
||||
else if(event.input.key == InputKeyDown)
|
||||
{
|
||||
if (playersMutex->player1_Y <= SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER1_PAD_SPEED -1) playersMutex->player1_Y += PLAYER1_PAD_SPEED;
|
||||
else playersMutex->player1_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1;
|
||||
}
|
||||
}
|
||||
else if (event.type == ClockEventTypeTick)
|
||||
{
|
||||
|
||||
if (playersMutex->ball_X + BALL_SIZE/2 <= SCREEN_SIZE_X*0.35 && playersMutex->ball_X_direction == 0)
|
||||
{
|
||||
if (playersMutex->ball_Y + BALL_SIZE/2 < playersMutex->player2_Y + PAD_SIZE_Y/2)
|
||||
{
|
||||
if (playersMutex->player2_Y >= 1+PLAYER2_PAD_SPEED) playersMutex->player2_Y -= PLAYER2_PAD_SPEED;
|
||||
else playersMutex->player2_Y= 1;
|
||||
}
|
||||
else if (playersMutex->ball_Y + BALL_SIZE/2 > playersMutex->player2_Y + PAD_SIZE_Y/2)
|
||||
{
|
||||
if (playersMutex->player2_Y <= SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER2_PAD_SPEED -1) playersMutex->player2_Y += PLAYER2_PAD_SPEED;
|
||||
else playersMutex->player2_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1;
|
||||
} else if(event.type == ClockEventTypeTick) {
|
||||
if(playersMutex->ball_X + BALL_SIZE / 2 <= SCREEN_SIZE_X * 0.35 &&
|
||||
playersMutex->ball_X_direction == 0) {
|
||||
if(playersMutex->ball_Y + BALL_SIZE / 2 <
|
||||
playersMutex->player2_Y + PAD_SIZE_Y / 2) {
|
||||
if(playersMutex->player2_Y >= 1 + PLAYER2_PAD_SPEED)
|
||||
playersMutex->player2_Y -= PLAYER2_PAD_SPEED;
|
||||
else
|
||||
playersMutex->player2_Y = 1;
|
||||
} else if(
|
||||
playersMutex->ball_Y + BALL_SIZE / 2 >
|
||||
playersMutex->player2_Y + PAD_SIZE_Y / 2) {
|
||||
if(playersMutex->player2_Y <=
|
||||
SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER2_PAD_SPEED - 1)
|
||||
playersMutex->player2_Y += PLAYER2_PAD_SPEED;
|
||||
else
|
||||
playersMutex->player2_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ball_corner_X[4] = {playersMutex->ball_X, playersMutex->ball_X + BALL_SIZE, playersMutex->ball_X + BALL_SIZE, playersMutex->ball_X};
|
||||
uint8_t ball_corner_Y[4] = {playersMutex->ball_Y, playersMutex->ball_Y, playersMutex->ball_Y + BALL_SIZE, playersMutex->ball_Y + BALL_SIZE};
|
||||
uint8_t ball_corner_X[4] = {
|
||||
playersMutex->ball_X,
|
||||
playersMutex->ball_X + BALL_SIZE,
|
||||
playersMutex->ball_X + BALL_SIZE,
|
||||
playersMutex->ball_X};
|
||||
uint8_t ball_corner_Y[4] = {
|
||||
playersMutex->ball_Y,
|
||||
playersMutex->ball_Y,
|
||||
playersMutex->ball_Y + BALL_SIZE,
|
||||
playersMutex->ball_Y + BALL_SIZE};
|
||||
bool insidePlayer1 = false, insidePlayer2 = false;
|
||||
|
||||
for (int i=0;i<4;i++)
|
||||
{
|
||||
if (insidePad(ball_corner_X[i], ball_corner_Y[i], playersMutex->player1_X, playersMutex->player1_Y) == true)
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(insidePad(
|
||||
ball_corner_X[i],
|
||||
ball_corner_Y[i],
|
||||
playersMutex->player1_X,
|
||||
playersMutex->player1_Y) == true) {
|
||||
insidePlayer1 = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (insidePad(ball_corner_X[i], ball_corner_Y[i], playersMutex->player2_X, playersMutex->player2_Y) == true)
|
||||
{
|
||||
if(insidePad(
|
||||
ball_corner_X[i],
|
||||
ball_corner_Y[i],
|
||||
playersMutex->player2_X,
|
||||
playersMutex->player2_Y) == true) {
|
||||
insidePlayer2 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (insidePlayer1 == true)
|
||||
{
|
||||
if(insidePlayer1 == true) {
|
||||
playersMutex->ball_X_direction = 0;
|
||||
playersMutex->ball_X -= playersMutex->ball_X_speed;
|
||||
playersMutex->ball_X_speed = changeSpeed();
|
||||
playersMutex->ball_Y_speed = changeSpeed();
|
||||
}
|
||||
else if (insidePlayer2 == true)
|
||||
{
|
||||
} else if(insidePlayer2 == true) {
|
||||
playersMutex->ball_X_direction = 1;
|
||||
playersMutex->ball_X += playersMutex->ball_X_speed;
|
||||
playersMutex->ball_X_speed = changeSpeed();
|
||||
playersMutex->ball_Y_speed = changeSpeed();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (playersMutex->ball_X_direction == 1)
|
||||
{
|
||||
|
||||
if (playersMutex->ball_X <= SCREEN_SIZE_X - BALL_SIZE - 1 - playersMutex->ball_X_speed)
|
||||
{
|
||||
} else {
|
||||
if(playersMutex->ball_X_direction == 1) {
|
||||
if(playersMutex->ball_X <=
|
||||
SCREEN_SIZE_X - BALL_SIZE - 1 - playersMutex->ball_X_speed) {
|
||||
playersMutex->ball_X += playersMutex->ball_X_speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
playersMutex->ball_X = SCREEN_SIZE_X/2 - BALL_SIZE/2;
|
||||
playersMutex->ball_Y = SCREEN_SIZE_Y/2 - BALL_SIZE/2;
|
||||
} else {
|
||||
playersMutex->ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2;
|
||||
playersMutex->ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2;
|
||||
playersMutex->ball_X_speed = 1;
|
||||
playersMutex->ball_Y_speed = 1;
|
||||
playersMutex->ball_X_direction = 0;
|
||||
playersMutex->player2_score++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (playersMutex->ball_X >= 1 + playersMutex->ball_X_speed)
|
||||
{
|
||||
} else {
|
||||
if(playersMutex->ball_X >= 1 + playersMutex->ball_X_speed) {
|
||||
playersMutex->ball_X -= playersMutex->ball_X_speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
playersMutex->ball_X = SCREEN_SIZE_X/2 - BALL_SIZE/2;
|
||||
playersMutex->ball_Y = SCREEN_SIZE_Y/2 - BALL_SIZE/2;
|
||||
} else {
|
||||
playersMutex->ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2;
|
||||
playersMutex->ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2;
|
||||
playersMutex->ball_X_speed = 1;
|
||||
playersMutex->ball_Y_speed = 1;
|
||||
playersMutex->ball_X_direction = 1;
|
||||
@@ -241,28 +240,20 @@ int32_t flipper_pong_app()
|
||||
}
|
||||
}
|
||||
|
||||
if (playersMutex->ball_Y_direction == 1)
|
||||
{
|
||||
if (playersMutex->ball_Y <= SCREEN_SIZE_Y - BALL_SIZE - 1 - playersMutex->ball_Y_speed)
|
||||
{
|
||||
if(playersMutex->ball_Y_direction == 1) {
|
||||
if(playersMutex->ball_Y <=
|
||||
SCREEN_SIZE_Y - BALL_SIZE - 1 - playersMutex->ball_Y_speed) {
|
||||
playersMutex->ball_Y += playersMutex->ball_Y_speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
playersMutex->ball_Y = SCREEN_SIZE_Y - BALL_SIZE - 1;
|
||||
playersMutex->ball_X_speed = changeSpeed();
|
||||
playersMutex->ball_Y_speed = changeSpeed();
|
||||
playersMutex->ball_Y_direction = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (playersMutex->ball_Y >= 1 + playersMutex->ball_Y_speed)
|
||||
{
|
||||
} else {
|
||||
if(playersMutex->ball_Y >= 1 + playersMutex->ball_Y_speed) {
|
||||
playersMutex->ball_Y -= playersMutex->ball_Y_speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
playersMutex->ball_Y = 1;
|
||||
playersMutex->ball_X_speed = changeSpeed();
|
||||
playersMutex->ball_Y_speed = changeSpeed();
|
||||
|
||||
@@ -40,8 +40,8 @@ extern const SubGhzProtocolRegistry protoview_protocol_registry;
|
||||
/* The callback actually just passes the control to the actual active
|
||||
* view callback, after setting up basic stuff like cleaning the screen
|
||||
* and setting color to black. */
|
||||
static void render_callback(Canvas *const canvas, void *ctx) {
|
||||
ProtoViewApp *app = ctx;
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
ProtoViewApp* app = ctx;
|
||||
|
||||
/* Clear screen. */
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
@@ -51,56 +51,63 @@ static void render_callback(Canvas *const canvas, void *ctx) {
|
||||
|
||||
/* Call who is in charge right now. */
|
||||
switch(app->current_view) {
|
||||
case ViewRawPulses: render_view_raw_pulses(canvas,app); break;
|
||||
case ViewInfo: render_view_info(canvas,app); break;
|
||||
case ViewRawPulses:
|
||||
render_view_raw_pulses(canvas, app);
|
||||
break;
|
||||
case ViewInfo:
|
||||
render_view_info(canvas, app);
|
||||
break;
|
||||
case ViewFrequencySettings:
|
||||
case ViewModulationSettings:
|
||||
render_view_settings(canvas,app); break;
|
||||
case ViewDirectSampling: render_view_direct_sampling(canvas,app); break;
|
||||
case ViewLast: furi_crash(TAG " ViewLast selected"); break;
|
||||
render_view_settings(canvas, app);
|
||||
break;
|
||||
case ViewDirectSampling:
|
||||
render_view_direct_sampling(canvas, app);
|
||||
break;
|
||||
case ViewLast:
|
||||
furi_crash(TAG " ViewLast selected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Here all we do is putting the events into the queue that will be handled
|
||||
* in the while() loop of the app entry point function. */
|
||||
static void input_callback(InputEvent* input_event, void* ctx)
|
||||
{
|
||||
ProtoViewApp *app = ctx;
|
||||
furi_message_queue_put(app->event_queue,input_event,FuriWaitForever);
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
ProtoViewApp* app = ctx;
|
||||
furi_message_queue_put(app->event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
|
||||
/* Called to switch view (when left/right is pressed). Handles
|
||||
* changing the current view ID and calling the enter/exit view
|
||||
* callbacks if needed. */
|
||||
static void app_switch_view(ProtoViewApp *app, SwitchViewDirection dir) {
|
||||
static void app_switch_view(ProtoViewApp* app, SwitchViewDirection dir) {
|
||||
ProtoViewCurrentView old = app->current_view;
|
||||
if (dir == AppNextView) {
|
||||
if(dir == AppNextView) {
|
||||
app->current_view++;
|
||||
if (app->current_view == ViewLast) app->current_view = 0;
|
||||
} else if (dir == AppPrevView) {
|
||||
if (app->current_view == 0)
|
||||
app->current_view = ViewLast-1;
|
||||
if(app->current_view == ViewLast) app->current_view = 0;
|
||||
} else if(dir == AppPrevView) {
|
||||
if(app->current_view == 0)
|
||||
app->current_view = ViewLast - 1;
|
||||
else
|
||||
app->current_view--;
|
||||
}
|
||||
ProtoViewCurrentView new = app->current_view;
|
||||
|
||||
/* Call the enter/exit view callbacks if needed. */
|
||||
if (old == ViewDirectSampling) view_exit_direct_sampling(app);
|
||||
if (new == ViewDirectSampling) view_enter_direct_sampling(app);
|
||||
if(old == ViewDirectSampling) view_exit_direct_sampling(app);
|
||||
if(new == ViewDirectSampling) view_enter_direct_sampling(app);
|
||||
/* The frequency/modulation settings are actually a single view:
|
||||
* as long as the user stays between the two modes of this view we
|
||||
* don't need to call the exit-view callback. */
|
||||
if ((old == ViewFrequencySettings && new != ViewModulationSettings) ||
|
||||
(old == ViewModulationSettings && new != ViewFrequencySettings))
|
||||
if((old == ViewFrequencySettings && new != ViewModulationSettings) ||
|
||||
(old == ViewModulationSettings && new != ViewFrequencySettings))
|
||||
view_exit_settings(app);
|
||||
}
|
||||
|
||||
/* Allocate the application state and initialize a number of stuff.
|
||||
* This is called in the entry point to create the application state. */
|
||||
ProtoViewApp* protoview_app_alloc() {
|
||||
ProtoViewApp *app = malloc(sizeof(ProtoViewApp));
|
||||
ProtoViewApp* app = malloc(sizeof(ProtoViewApp));
|
||||
|
||||
// Init shared data structures
|
||||
RawSamples = raw_samples_alloc();
|
||||
@@ -142,17 +149,14 @@ ProtoViewApp* protoview_app_alloc() {
|
||||
app->txrx->environment = subghz_environment_alloc();
|
||||
subghz_environment_set_protocol_registry(
|
||||
app->txrx->environment, (void*)&protoview_protocol_registry);
|
||||
app->txrx->receiver =
|
||||
subghz_receiver_alloc_init(app->txrx->environment);
|
||||
subghz_receiver_set_filter(app->txrx->receiver,
|
||||
SubGhzProtocolFlag_Decodable);
|
||||
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||
subghz_worker_set_overrun_callback(
|
||||
app->txrx->worker,
|
||||
(SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||
app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||
subghz_worker_set_pair_callback(
|
||||
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
|
||||
|
||||
|
||||
app->frequency = subghz_setting_get_default_frequency(app->setting);
|
||||
app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */
|
||||
|
||||
@@ -165,7 +169,7 @@ ProtoViewApp* protoview_app_alloc() {
|
||||
/* Free what the application allocated. It is not clear to me if the
|
||||
* Flipper OS, once the application exits, will be able to reclaim space
|
||||
* even if we forget to free something here. */
|
||||
void protoview_app_free(ProtoViewApp *app) {
|
||||
void protoview_app_free(ProtoViewApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Put CC1101 on sleep.
|
||||
@@ -183,7 +187,7 @@ void protoview_app_free(ProtoViewApp *app) {
|
||||
subghz_setting_free(app->setting);
|
||||
|
||||
// Worker stuff.
|
||||
if (!app->txrx->debug_timer_sampling) {
|
||||
if(!app->txrx->debug_timer_sampling) {
|
||||
subghz_receiver_free(app->txrx->receiver);
|
||||
subghz_environment_free(app->txrx->environment);
|
||||
subghz_worker_free(app->txrx->worker);
|
||||
@@ -201,8 +205,8 @@ void protoview_app_free(ProtoViewApp *app) {
|
||||
/* Called periodically. Do signal processing here. Data we process here
|
||||
* will be later displayed by the render callback. The side effect of this
|
||||
* function is to scan for signals and set DetectedSamples. */
|
||||
static void timer_callback(void *ctx) {
|
||||
ProtoViewApp *app = ctx;
|
||||
static void timer_callback(void* ctx) {
|
||||
ProtoViewApp* app = ctx;
|
||||
uint32_t delta, lastidx = app->signal_last_scan_idx;
|
||||
|
||||
/* scan_for_signal(), called by this function, deals with a
|
||||
@@ -210,22 +214,22 @@ static void timer_callback(void *ctx) {
|
||||
* cross-boundaries, it is enough if we scan each time the buffer fills
|
||||
* for 50% more compared to the last scan. Thanks to this check we
|
||||
* can avoid scanning too many times to just find the same data. */
|
||||
if (lastidx < RawSamples->idx) {
|
||||
if(lastidx < RawSamples->idx) {
|
||||
delta = RawSamples->idx - lastidx;
|
||||
} else {
|
||||
delta = RawSamples->total - lastidx + RawSamples->idx;
|
||||
}
|
||||
if (delta < RawSamples->total/2) return;
|
||||
if(delta < RawSamples->total / 2) return;
|
||||
app->signal_last_scan_idx = RawSamples->idx;
|
||||
scan_for_signal(app);
|
||||
}
|
||||
|
||||
int32_t protoview_app_entry(void* p) {
|
||||
UNUSED(p);
|
||||
ProtoViewApp *app = protoview_app_alloc();
|
||||
ProtoViewApp* app = protoview_app_alloc();
|
||||
|
||||
/* Create a timer. We do data analysis in the callback. */
|
||||
FuriTimer *timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app);
|
||||
FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
|
||||
|
||||
/* Start listening to signals immediately. */
|
||||
@@ -240,60 +244,57 @@ int32_t protoview_app_entry(void* p) {
|
||||
InputEvent input;
|
||||
while(app->running) {
|
||||
FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
|
||||
if (qstat == FuriStatusOk) {
|
||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u",
|
||||
input.type, input.key);
|
||||
if(qstat == FuriStatusOk) {
|
||||
if(DEBUG_MSG)
|
||||
FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
|
||||
|
||||
/* Handle navigation here. Then handle view-specific inputs
|
||||
* in the view specific handling function. */
|
||||
if (input.type == InputTypeShort &&
|
||||
input.key == InputKeyBack)
|
||||
{
|
||||
if(input.type == InputTypeShort && input.key == InputKeyBack) {
|
||||
/* Exit the app. */
|
||||
app->running = 0;
|
||||
} else if (input.type == InputTypeShort &&
|
||||
input.key == InputKeyRight)
|
||||
{
|
||||
} else if(input.type == InputTypeShort && input.key == InputKeyRight) {
|
||||
/* Go to the next view. */
|
||||
app_switch_view(app,AppNextView);
|
||||
} else if (input.type == InputTypeShort &&
|
||||
input.key == InputKeyLeft)
|
||||
{
|
||||
app_switch_view(app, AppNextView);
|
||||
} else if(input.type == InputTypeShort && input.key == InputKeyLeft) {
|
||||
/* Go to the previous view. */
|
||||
app_switch_view(app,AppPrevView);
|
||||
app_switch_view(app, AppPrevView);
|
||||
} else {
|
||||
/* This is where we pass the control to the currently
|
||||
* active view input processing. */
|
||||
switch(app->current_view) {
|
||||
case ViewRawPulses:
|
||||
process_input_raw_pulses(app,input);
|
||||
process_input_raw_pulses(app, input);
|
||||
break;
|
||||
case ViewInfo:
|
||||
process_input_info(app,input);
|
||||
process_input_info(app, input);
|
||||
break;
|
||||
case ViewFrequencySettings:
|
||||
case ViewModulationSettings:
|
||||
process_input_settings(app,input);
|
||||
process_input_settings(app, input);
|
||||
break;
|
||||
case ViewDirectSampling:
|
||||
process_input_direct_sampling(app,input);
|
||||
process_input_direct_sampling(app, input);
|
||||
break;
|
||||
case ViewLast:
|
||||
furi_crash(TAG " ViewLast selected");
|
||||
break;
|
||||
case ViewLast: furi_crash(TAG " ViewLast selected"); break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Useful to understand if the app is still alive when it
|
||||
* does not respond because of bugs. */
|
||||
if (DEBUG_MSG) {
|
||||
static int c = 0; c++;
|
||||
if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
|
||||
if(DEBUG_MSG) {
|
||||
static int c = 0;
|
||||
c++;
|
||||
if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
|
||||
}
|
||||
}
|
||||
view_port_update(app->view_port);
|
||||
}
|
||||
|
||||
/* App no longer running. Shut down and free. */
|
||||
if (app->txrx->txrx_state == TxRxStateRx) {
|
||||
if(app->txrx->txrx_state == TxRxStateRx) {
|
||||
FURI_LOG_E(TAG, "Putting CC1101 to sleep before exiting.");
|
||||
radio_rx_end(app);
|
||||
radio_sleep(app);
|
||||
@@ -303,4 +304,3 @@ int32_t protoview_app_entry(void* p) {
|
||||
protoview_app_free(app);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,15 +48,12 @@ typedef enum {
|
||||
} ProtoViewCurrentView;
|
||||
|
||||
/* Used by app_switch_view() */
|
||||
typedef enum {
|
||||
AppNextView,
|
||||
AppPrevView
|
||||
} SwitchViewDirection;
|
||||
typedef enum { AppNextView, AppPrevView } SwitchViewDirection;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char* name;
|
||||
FuriHalSubGhzPreset preset;
|
||||
uint8_t *custom;
|
||||
uint8_t* custom;
|
||||
} ProtoViewModulation;
|
||||
|
||||
extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */
|
||||
@@ -65,19 +62,19 @@ extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */
|
||||
* It receives data and we get our protocol "feed" callback called
|
||||
* with the level (1 or 0) and duration. */
|
||||
struct ProtoViewTxRx {
|
||||
bool freq_mod_changed; /* The user changed frequency and/or modulation
|
||||
bool freq_mod_changed; /* The user changed frequency and/or modulation
|
||||
from the interface. There is to restart the
|
||||
radio with the right parameters. */
|
||||
SubGhzWorker* worker; /* Our background worker. */
|
||||
SubGhzWorker* worker; /* Our background worker. */
|
||||
SubGhzEnvironment* environment;
|
||||
SubGhzReceiver* receiver;
|
||||
TxRxState txrx_state; /* Receiving, idle or sleeping? */
|
||||
|
||||
/* Timer sampling mode state. */
|
||||
bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only
|
||||
bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only
|
||||
for testing. */
|
||||
uint32_t last_g0_change_time; /* Last high->low (or reverse) switch. */
|
||||
bool last_g0_value; /* Current value (high or low): we are
|
||||
bool last_g0_value; /* Current value (high or low): we are
|
||||
checking the duration in the timer
|
||||
handler. */
|
||||
};
|
||||
@@ -97,43 +94,43 @@ typedef struct ProtoViewMsgInfo {
|
||||
char info2[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 2. */
|
||||
char info3[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 3. */
|
||||
char info4[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 4. */
|
||||
uint64_t len; /* Bits consumed from the stream. */
|
||||
uint64_t len; /* Bits consumed from the stream. */
|
||||
} ProtoViewMsgInfo;
|
||||
|
||||
struct ProtoViewApp {
|
||||
/* GUI */
|
||||
Gui *gui;
|
||||
ViewPort *view_port; /* We just use a raw viewport and we render
|
||||
Gui* gui;
|
||||
ViewPort* view_port; /* We just use a raw viewport and we render
|
||||
everything into the low level canvas. */
|
||||
ProtoViewCurrentView current_view; /* Active view ID. */
|
||||
FuriMessageQueue *event_queue; /* Keypress events go here. */
|
||||
ProtoViewCurrentView current_view; /* Active view ID. */
|
||||
FuriMessageQueue* event_queue; /* Keypress events go here. */
|
||||
|
||||
/* Radio related. */
|
||||
ProtoViewTxRx *txrx; /* Radio state. */
|
||||
SubGhzSetting *setting; /* A list of valid frequencies. */
|
||||
ProtoViewTxRx* txrx; /* Radio state. */
|
||||
SubGhzSetting* setting; /* A list of valid frequencies. */
|
||||
|
||||
/* Generic app state. */
|
||||
int running; /* Once false exists the app. */
|
||||
int running; /* Once false exists the app. */
|
||||
uint32_t signal_bestlen; /* Longest coherent signal observed so far. */
|
||||
uint32_t signal_last_scan_idx; /* Index of the buffer last time we
|
||||
performed the scan. */
|
||||
bool signal_decoded; /* Was the current signal decoded? */
|
||||
bool signal_decoded; /* Was the current signal decoded? */
|
||||
ProtoViewMsgInfo signal_info; /* Decoded message, if signal_decoded true. */
|
||||
bool direct_sampling_enabled; /* This special view needs an explicit
|
||||
acknowledge to work. */
|
||||
|
||||
/* Raw view apps state. */
|
||||
uint32_t us_scale; /* microseconds per pixel. */
|
||||
uint32_t signal_offset; /* Long press left/right panning in raw view. */
|
||||
uint32_t us_scale; /* microseconds per pixel. */
|
||||
uint32_t signal_offset; /* Long press left/right panning in raw view. */
|
||||
|
||||
/* Configuration view app state. */
|
||||
uint32_t frequency; /* Current frequency. */
|
||||
uint8_t modulation; /* Current modulation ID, array index in the
|
||||
uint32_t frequency; /* Current frequency. */
|
||||
uint8_t modulation; /* Current modulation ID, array index in the
|
||||
ProtoViewModulations table. */
|
||||
};
|
||||
|
||||
typedef struct ProtoViewDecoder {
|
||||
const char *name; /* Protocol name. */
|
||||
const char* name; /* Protocol name. */
|
||||
/* The decode function takes a buffer that is actually a bitmap, with
|
||||
* high and low levels represented as 0 and 1. The number of high/low
|
||||
* pulses represented by the bitmap is passed as the 'numbits' argument,
|
||||
@@ -141,7 +138,7 @@ typedef struct ProtoViewDecoder {
|
||||
* 'bits'. So 'numbytes' is mainly useful to pass as argument to other
|
||||
* functions that perform bit extraction with bound checking, such as
|
||||
* bitmap_get() and so forth. */
|
||||
bool (*decode)(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info);
|
||||
bool (*decode)(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info);
|
||||
} ProtoViewDecoder;
|
||||
|
||||
extern RawSamplesBuffer *RawSamples, *DetectedSamples;
|
||||
@@ -152,37 +149,61 @@ uint32_t radio_rx(ProtoViewApp* app);
|
||||
void radio_idle(ProtoViewApp* app);
|
||||
void radio_rx_end(ProtoViewApp* app);
|
||||
void radio_sleep(ProtoViewApp* app);
|
||||
void raw_sampling_worker_start(ProtoViewApp *app);
|
||||
void raw_sampling_worker_stop(ProtoViewApp *app);
|
||||
void raw_sampling_worker_start(ProtoViewApp* app);
|
||||
void raw_sampling_worker_stop(ProtoViewApp* app);
|
||||
|
||||
/* signal.c */
|
||||
uint32_t duration_delta(uint32_t a, uint32_t b);
|
||||
void reset_current_signal(ProtoViewApp *app);
|
||||
void scan_for_signal(ProtoViewApp *app);
|
||||
bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos);
|
||||
void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val);
|
||||
void bitmap_set_pattern(uint8_t *b, uint32_t blen, const char *pat);
|
||||
void bitmap_invert_bytes_bits(uint8_t *p, uint32_t len);
|
||||
bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits);
|
||||
uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits);
|
||||
uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern);
|
||||
uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous);
|
||||
void reset_current_signal(ProtoViewApp* app);
|
||||
void scan_for_signal(ProtoViewApp* app);
|
||||
bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos);
|
||||
void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val);
|
||||
void bitmap_set_pattern(uint8_t* b, uint32_t blen, const char* pat);
|
||||
void bitmap_invert_bytes_bits(uint8_t* p, uint32_t len);
|
||||
bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits);
|
||||
uint32_t bitmap_seek_bits(
|
||||
uint8_t* b,
|
||||
uint32_t blen,
|
||||
uint32_t startpos,
|
||||
uint32_t maxbits,
|
||||
const char* bits);
|
||||
uint32_t convert_from_line_code(
|
||||
uint8_t* buf,
|
||||
uint64_t buflen,
|
||||
uint8_t* bits,
|
||||
uint32_t len,
|
||||
uint32_t offset,
|
||||
const char* zero_pattern,
|
||||
const char* one_pattern);
|
||||
uint32_t convert_from_diff_manchester(
|
||||
uint8_t* buf,
|
||||
uint64_t buflen,
|
||||
uint8_t* bits,
|
||||
uint32_t len,
|
||||
uint32_t off,
|
||||
bool previous);
|
||||
|
||||
/* view_*.c */
|
||||
void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app);
|
||||
void process_input_raw_pulses(ProtoViewApp *app, InputEvent input);
|
||||
void render_view_settings(Canvas *const canvas, ProtoViewApp *app);
|
||||
void process_input_settings(ProtoViewApp *app, InputEvent input);
|
||||
void render_view_info(Canvas *const canvas, ProtoViewApp *app);
|
||||
void process_input_info(ProtoViewApp *app, InputEvent input);
|
||||
void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app);
|
||||
void process_input_direct_sampling(ProtoViewApp *app, InputEvent input);
|
||||
void view_enter_direct_sampling(ProtoViewApp *app);
|
||||
void view_exit_direct_sampling(ProtoViewApp *app);
|
||||
void view_exit_settings(ProtoViewApp *app);
|
||||
void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app);
|
||||
void process_input_raw_pulses(ProtoViewApp* app, InputEvent input);
|
||||
void render_view_settings(Canvas* const canvas, ProtoViewApp* app);
|
||||
void process_input_settings(ProtoViewApp* app, InputEvent input);
|
||||
void render_view_info(Canvas* const canvas, ProtoViewApp* app);
|
||||
void process_input_info(ProtoViewApp* app, InputEvent input);
|
||||
void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app);
|
||||
void process_input_direct_sampling(ProtoViewApp* app, InputEvent input);
|
||||
void view_enter_direct_sampling(ProtoViewApp* app);
|
||||
void view_exit_direct_sampling(ProtoViewApp* app);
|
||||
void view_exit_settings(ProtoViewApp* app);
|
||||
|
||||
/* ui.c */
|
||||
void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color);
|
||||
void canvas_draw_str_with_border(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
const char* str,
|
||||
Color text_color,
|
||||
Color border_color);
|
||||
|
||||
/* crc.c */
|
||||
uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly);
|
||||
uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly);
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
#include "app_buffer.h"
|
||||
|
||||
/* Allocate and initialize a samples buffer. */
|
||||
RawSamplesBuffer *raw_samples_alloc(void) {
|
||||
RawSamplesBuffer *buf = malloc(sizeof(*buf));
|
||||
RawSamplesBuffer* raw_samples_alloc(void) {
|
||||
RawSamplesBuffer* buf = malloc(sizeof(*buf));
|
||||
buf->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
raw_samples_reset(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Free a sample buffer. Should be called when the mutex is released. */
|
||||
void raw_samples_free(RawSamplesBuffer *s) {
|
||||
void raw_samples_free(RawSamplesBuffer* s) {
|
||||
furi_mutex_free(s->mutex);
|
||||
free(s);
|
||||
}
|
||||
@@ -24,36 +24,35 @@ void raw_samples_free(RawSamplesBuffer *s) {
|
||||
/* This just set all the samples to zero and also resets the internal
|
||||
* index. There is no need to call it after raw_samples_alloc(), but only
|
||||
* when one wants to reset the whole buffer of samples. */
|
||||
void raw_samples_reset(RawSamplesBuffer *s) {
|
||||
furi_mutex_acquire(s->mutex,FuriWaitForever);
|
||||
void raw_samples_reset(RawSamplesBuffer* s) {
|
||||
furi_mutex_acquire(s->mutex, FuriWaitForever);
|
||||
s->total = RAW_SAMPLES_NUM;
|
||||
s->idx = 0;
|
||||
s->short_pulse_dur = 0;
|
||||
memset(s->level,0,sizeof(s->level));
|
||||
memset(s->dur,0,sizeof(s->dur));
|
||||
memset(s->level, 0, sizeof(s->level));
|
||||
memset(s->dur, 0, sizeof(s->dur));
|
||||
furi_mutex_release(s->mutex);
|
||||
}
|
||||
|
||||
/* Set the raw sample internal index so that what is currently at
|
||||
* offset 'offset', will appear to be at 0 index. */
|
||||
void raw_samples_center(RawSamplesBuffer *s, uint32_t offset) {
|
||||
s->idx = (s->idx+offset) % RAW_SAMPLES_NUM;
|
||||
void raw_samples_center(RawSamplesBuffer* s, uint32_t offset) {
|
||||
s->idx = (s->idx + offset) % RAW_SAMPLES_NUM;
|
||||
}
|
||||
|
||||
/* Add the specified sample in the circular buffer. */
|
||||
void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) {
|
||||
furi_mutex_acquire(s->mutex,FuriWaitForever);
|
||||
void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur) {
|
||||
furi_mutex_acquire(s->mutex, FuriWaitForever);
|
||||
s->level[s->idx] = level;
|
||||
s->dur[s->idx] = dur;
|
||||
s->idx = (s->idx+1) % RAW_SAMPLES_NUM;
|
||||
s->idx = (s->idx + 1) % RAW_SAMPLES_NUM;
|
||||
furi_mutex_release(s->mutex);
|
||||
}
|
||||
|
||||
/* Get the sample from the buffer. It is possible to use out of range indexes
|
||||
* as 'idx' because the modulo operation will rewind back from the start. */
|
||||
void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur)
|
||||
{
|
||||
furi_mutex_acquire(s->mutex,FuriWaitForever);
|
||||
void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur) {
|
||||
furi_mutex_acquire(s->mutex, FuriWaitForever);
|
||||
idx = (s->idx + idx) % RAW_SAMPLES_NUM;
|
||||
*level = s->level[idx];
|
||||
*dur = s->dur[idx];
|
||||
@@ -61,13 +60,13 @@ void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *d
|
||||
}
|
||||
|
||||
/* Copy one buffer to the other, including current index. */
|
||||
void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src) {
|
||||
furi_mutex_acquire(src->mutex,FuriWaitForever);
|
||||
furi_mutex_acquire(dst->mutex,FuriWaitForever);
|
||||
void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src) {
|
||||
furi_mutex_acquire(src->mutex, FuriWaitForever);
|
||||
furi_mutex_acquire(dst->mutex, FuriWaitForever);
|
||||
dst->idx = src->idx;
|
||||
dst->short_pulse_dur = src->short_pulse_dur;
|
||||
memcpy(dst->level,src->level,sizeof(dst->level));
|
||||
memcpy(dst->dur,src->dur,sizeof(dst->dur));
|
||||
memcpy(dst->level, src->level, sizeof(dst->level));
|
||||
memcpy(dst->dur, src->dur, sizeof(dst->dur));
|
||||
furi_mutex_release(src->mutex);
|
||||
furi_mutex_release(dst->mutex);
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
/* Our circular buffer of raw samples, used in order to display
|
||||
* the signal. */
|
||||
|
||||
#define RAW_SAMPLES_NUM 2048 /* Use a power of two: we take the modulo
|
||||
#define RAW_SAMPLES_NUM \
|
||||
2048 /* Use a power of two: we take the modulo
|
||||
of the index quite often to normalize inside
|
||||
the range, and division is slow. */
|
||||
|
||||
typedef struct RawSamplesBuffer {
|
||||
FuriMutex *mutex;
|
||||
FuriMutex* mutex;
|
||||
uint8_t level[RAW_SAMPLES_NUM];
|
||||
uint32_t dur[RAW_SAMPLES_NUM];
|
||||
uint32_t idx; /* Current idx (next to write). */
|
||||
uint32_t idx; /* Current idx (next to write). */
|
||||
uint32_t total; /* Total samples: same as RAW_SAMPLES_NUM, we provide
|
||||
this field for a cleaner interface with the user, but
|
||||
we always use RAW_SAMPLES_NUM when taking the modulo so
|
||||
@@ -21,10 +22,10 @@ typedef struct RawSamplesBuffer {
|
||||
uint32_t short_pulse_dur; /* Duration of the shortest pulse. */
|
||||
} RawSamplesBuffer;
|
||||
|
||||
RawSamplesBuffer *raw_samples_alloc(void);
|
||||
void raw_samples_reset(RawSamplesBuffer *s);
|
||||
void raw_samples_center(RawSamplesBuffer *s, uint32_t offset);
|
||||
void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur);
|
||||
void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur);
|
||||
void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src);
|
||||
void raw_samples_free(RawSamplesBuffer *s);
|
||||
RawSamplesBuffer* raw_samples_alloc(void);
|
||||
void raw_samples_reset(RawSamplesBuffer* s);
|
||||
void raw_samples_center(RawSamplesBuffer* s, uint32_t offset);
|
||||
void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur);
|
||||
void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur);
|
||||
void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src);
|
||||
void raw_samples_free(RawSamplesBuffer* s);
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#include <furi_hal_spi.h>
|
||||
#include <furi_hal_interrupt.h>
|
||||
|
||||
void raw_sampling_worker_start(ProtoViewApp *app);
|
||||
void raw_sampling_worker_stop(ProtoViewApp *app);
|
||||
void raw_sampling_worker_start(ProtoViewApp* app);
|
||||
void raw_sampling_worker_stop(ProtoViewApp* app);
|
||||
|
||||
ProtoViewModulation ProtoViewModulations[] = {
|
||||
{"OOK 650Khz", FuriHalSubGhzPresetOok650Async, NULL},
|
||||
@@ -37,7 +37,7 @@ void radio_begin(ProtoViewApp* app) {
|
||||
/* The CC1101 preset can be either one of the standard presets, if
|
||||
* the modulation "custom" field is NULL, or a custom preset we
|
||||
* defined in custom_presets.h. */
|
||||
if (ProtoViewModulations[app->modulation].custom == NULL)
|
||||
if(ProtoViewModulations[app->modulation].custom == NULL)
|
||||
furi_hal_subghz_load_preset(ProtoViewModulations[app->modulation].preset);
|
||||
else
|
||||
furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom);
|
||||
@@ -49,10 +49,10 @@ void radio_begin(ProtoViewApp* app) {
|
||||
uint32_t radio_rx(ProtoViewApp* app) {
|
||||
furi_assert(app);
|
||||
if(!furi_hal_subghz_is_frequency_valid(app->frequency)) {
|
||||
furi_crash(TAG" Incorrect RX frequency.");
|
||||
furi_crash(TAG " Incorrect RX frequency.");
|
||||
}
|
||||
|
||||
if (app->txrx->txrx_state == TxRxStateRx) return app->frequency;
|
||||
if(app->txrx->txrx_state == TxRxStateRx) return app->frequency;
|
||||
|
||||
furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */
|
||||
uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency);
|
||||
@@ -60,10 +60,8 @@ uint32_t radio_rx(ProtoViewApp* app) {
|
||||
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_subghz_flush_rx();
|
||||
furi_hal_subghz_rx();
|
||||
if (!app->txrx->debug_timer_sampling) {
|
||||
|
||||
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback,
|
||||
app->txrx->worker);
|
||||
if(!app->txrx->debug_timer_sampling) {
|
||||
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker);
|
||||
subghz_worker_start(app->txrx->worker);
|
||||
} else {
|
||||
raw_sampling_worker_start(app);
|
||||
@@ -75,8 +73,8 @@ uint32_t radio_rx(ProtoViewApp* app) {
|
||||
/* Stop subghz worker (if active), put radio on idle state. */
|
||||
void radio_rx_end(ProtoViewApp* app) {
|
||||
furi_assert(app);
|
||||
if (app->txrx->txrx_state == TxRxStateRx) {
|
||||
if (!app->txrx->debug_timer_sampling) {
|
||||
if(app->txrx->txrx_state == TxRxStateRx) {
|
||||
if(!app->txrx->debug_timer_sampling) {
|
||||
if(subghz_worker_is_running(app->txrx->worker)) {
|
||||
subghz_worker_stop(app->txrx->worker);
|
||||
furi_hal_subghz_stop_async_rx();
|
||||
@@ -92,7 +90,7 @@ void radio_rx_end(ProtoViewApp* app) {
|
||||
/* Put radio on sleep. */
|
||||
void radio_sleep(ProtoViewApp* app) {
|
||||
furi_assert(app);
|
||||
if (app->txrx->txrx_state == TxRxStateRx) {
|
||||
if(app->txrx->txrx_state == TxRxStateRx) {
|
||||
/* We can't go from having an active RX worker to sleeping.
|
||||
* Stop the RX subsystems first. */
|
||||
radio_rx_end(app);
|
||||
@@ -108,15 +106,15 @@ void radio_sleep(ProtoViewApp* app) {
|
||||
* Flipper system.
|
||||
* ===========================================================================*/
|
||||
|
||||
void protoview_timer_isr(void *ctx) {
|
||||
ProtoViewApp *app = ctx;
|
||||
void protoview_timer_isr(void* ctx) {
|
||||
ProtoViewApp* app = ctx;
|
||||
|
||||
bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
|
||||
if (app->txrx->last_g0_value != level) {
|
||||
if(app->txrx->last_g0_value != level) {
|
||||
uint32_t now = DWT->CYCCNT;
|
||||
uint32_t dur = now - app->txrx->last_g0_change_time;
|
||||
dur /= furi_hal_cortex_instructions_per_microsecond();
|
||||
if (dur > 15000) dur = 15000;
|
||||
if(dur > 15000) dur = 15000;
|
||||
raw_samples_add(RawSamples, app->txrx->last_g0_value, dur);
|
||||
app->txrx->last_g0_value = level;
|
||||
app->txrx->last_g0_change_time = now;
|
||||
@@ -124,13 +122,13 @@ void protoview_timer_isr(void *ctx) {
|
||||
LL_TIM_ClearFlag_UPDATE(TIM2);
|
||||
}
|
||||
|
||||
void raw_sampling_worker_start(ProtoViewApp *app) {
|
||||
void raw_sampling_worker_start(ProtoViewApp* app) {
|
||||
UNUSED(app);
|
||||
|
||||
LL_TIM_InitTypeDef tim_init = {
|
||||
.Prescaler = 63, /* CPU frequency is ~64Mhz. */
|
||||
.Prescaler = 63, /* CPU frequency is ~64Mhz. */
|
||||
.CounterMode = LL_TIM_COUNTERMODE_UP,
|
||||
.Autoreload = 5, /* Sample every 5 us */
|
||||
.Autoreload = 5, /* Sample every 5 us */
|
||||
};
|
||||
|
||||
LL_TIM_Init(TIM2, &tim_init);
|
||||
@@ -143,7 +141,7 @@ void raw_sampling_worker_start(ProtoViewApp *app) {
|
||||
FURI_LOG_E(TAG, "Timer enabled");
|
||||
}
|
||||
|
||||
void raw_sampling_worker_stop(ProtoViewApp *app) {
|
||||
void raw_sampling_worker_stop(ProtoViewApp* app) {
|
||||
UNUSED(app);
|
||||
FURI_CRITICAL_ENTER();
|
||||
LL_TIM_DisableCounter(TIM2);
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
|
||||
/* CRC8 with the specified initialization value 'init' and
|
||||
* polynomial 'poly'. */
|
||||
uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly)
|
||||
{
|
||||
uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly) {
|
||||
uint8_t crc = init;
|
||||
size_t i, j;
|
||||
for (i = 0; i < len; i++) {
|
||||
for(i = 0; i < len; i++) {
|
||||
crc ^= data[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
if ((crc & 0x80) != 0)
|
||||
for(j = 0; j < 8; j++) {
|
||||
if((crc & 0x80) != 0)
|
||||
crc = (uint8_t)((crc << 1) ^ poly);
|
||||
else
|
||||
crc <<= 1;
|
||||
|
||||
@@ -72,7 +72,8 @@ static uint8_t protoview_subghz_tpms1_async_regs[][2] = {
|
||||
// // Modem Configuration
|
||||
{CC1101_MDMCFG0, 0x00},
|
||||
{CC1101_MDMCFG1, 0x02},
|
||||
{CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
|
||||
{CC1101_MDMCFG2,
|
||||
0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
|
||||
{CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud
|
||||
{CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz
|
||||
{CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz
|
||||
@@ -117,7 +118,8 @@ static uint8_t protoview_subghz_tpms2_async_regs[][2] = {
|
||||
// // Modem Configuration
|
||||
{CC1101_MDMCFG0, 0x00},
|
||||
{CC1101_MDMCFG1, 0x02},
|
||||
{CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
|
||||
{CC1101_MDMCFG2,
|
||||
0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
|
||||
{CC1101_MDMCFG3, 0x93}, // Data rate is 40kBaud
|
||||
{CC1101_MDMCFG4, 0x6A}, // 6 = BW filter 270kHz, A = Data rate exp
|
||||
{CC1101_DEVIATN, 0x41}, // Deviation 19.042 kHz
|
||||
@@ -189,4 +191,3 @@ static uint8_t protoview_subghz_tpms3_async_regs[][2] = {
|
||||
/* End */
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const SubGhzProtocol subghz_protocol_protoview;
|
||||
|
||||
/* The feed() method puts data in the RawSamples global (protected by
|
||||
* a mutex). */
|
||||
extern RawSamplesBuffer *RawSamples;
|
||||
extern RawSamplesBuffer* RawSamples;
|
||||
|
||||
/* This is totally dummy: we just define the decoder base for the async
|
||||
* system to work but we don't really use it if not to collect raw
|
||||
@@ -26,8 +26,7 @@ typedef struct SubGhzProtocolDecoderprotoview {
|
||||
void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolDecoderprotoview* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderprotoview));
|
||||
SubGhzProtocolDecoderprotoview* instance = malloc(sizeof(SubGhzProtocolDecoderprotoview));
|
||||
instance->base.protocol = &subghz_protocol_protoview;
|
||||
return instance;
|
||||
}
|
||||
@@ -66,8 +65,7 @@ uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) {
|
||||
bool subghz_protocol_decoder_protoview_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset)
|
||||
{
|
||||
SubGhzRadioPreset* preset) {
|
||||
UNUSED(context);
|
||||
UNUSED(flipper_format);
|
||||
UNUSED(preset);
|
||||
@@ -75,15 +73,13 @@ bool subghz_protocol_decoder_protoview_serialize(
|
||||
}
|
||||
|
||||
/* Not used. */
|
||||
bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format)
|
||||
{
|
||||
bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
UNUSED(context);
|
||||
UNUSED(flipper_format);
|
||||
return false;
|
||||
}
|
||||
|
||||
void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output)
|
||||
{
|
||||
void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
furi_string_cat_printf(output, "Protoview");
|
||||
}
|
||||
@@ -116,5 +112,4 @@ const SubGhzProtocol* protoview_protocol_registry_items[] = {
|
||||
|
||||
const SubGhzProtocolRegistry protoview_protocol_registry = {
|
||||
.items = protoview_protocol_registry_items,
|
||||
.size = COUNT_OF(protoview_protocol_registry_items)
|
||||
};
|
||||
.size = COUNT_OF(protoview_protocol_registry_items)};
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#include "../app.h"
|
||||
|
||||
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
|
||||
if (numbits < 30) return false;
|
||||
const char *sync_patterns[3] = {
|
||||
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
|
||||
if(numbits < 30) return false;
|
||||
const char* sync_patterns[3] = {
|
||||
"10000000000000000000000000000001", /* 30 zero bits. */
|
||||
"100000000000000000000000000000001", /* 31 zero bits. */
|
||||
"1000000000000000000000000000000001", /* 32 zero bits. */
|
||||
@@ -19,26 +19,23 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
|
||||
|
||||
uint32_t off;
|
||||
int j;
|
||||
for (j = 0; j < 3; j++) {
|
||||
off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_patterns[j]);
|
||||
if (off != BITMAP_SEEK_NOT_FOUND) break;
|
||||
for(j = 0; j < 3; j++) {
|
||||
off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_patterns[j]);
|
||||
if(off != BITMAP_SEEK_NOT_FOUND) break;
|
||||
}
|
||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu",off);
|
||||
off += strlen(sync_patterns[j])-1;
|
||||
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu", off);
|
||||
off += strlen(sync_patterns[j]) - 1;
|
||||
|
||||
uint8_t d[3]; /* 24 bits of data. */
|
||||
uint32_t decoded =
|
||||
convert_from_line_code(d,sizeof(d),bits,numbytes,off,"1000","1110");
|
||||
uint32_t decoded = convert_from_line_code(d, sizeof(d), bits, numbytes, off, "1000", "1110");
|
||||
|
||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu",decoded);
|
||||
if (decoded != 24) return false;
|
||||
snprintf(info->name,PROTOVIEW_MSG_STR_LEN,"PT/SC remote");
|
||||
snprintf(info->raw,PROTOVIEW_MSG_STR_LEN,"%02X%02X%02X",d[0],d[1],d[2]);
|
||||
info->len = off+(4*24);
|
||||
if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu", decoded);
|
||||
if(decoded != 24) return false;
|
||||
snprintf(info->name, PROTOVIEW_MSG_STR_LEN, "PT/SC remote");
|
||||
snprintf(info->raw, PROTOVIEW_MSG_STR_LEN, "%02X%02X%02X", d[0], d[1], d[2]);
|
||||
info->len = off + (4 * 24);
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoViewDecoder B4B1Decoder = {
|
||||
"B4B1", decode
|
||||
};
|
||||
ProtoViewDecoder B4B1Decoder = {"B4B1", decode};
|
||||
|
||||
@@ -7,54 +7,66 @@
|
||||
|
||||
#include "../app.h"
|
||||
|
||||
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
|
||||
|
||||
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
|
||||
/* We consider a preamble of 17 symbols. They are more, but the decoding
|
||||
* is more likely to happen if we don't pretend to receive from the
|
||||
* very start of the message. */
|
||||
uint32_t sync_len = 17;
|
||||
const char *sync_pattern = "10101010101010110";
|
||||
if (numbits-sync_len < 8*10) return false; /* Expect 10 bytes. */
|
||||
const char* sync_pattern = "10101010101010110";
|
||||
if(numbits - sync_len < 8 * 10) return false; /* Expect 10 bytes. */
|
||||
|
||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
FURI_LOG_E(TAG, "Renault TPMS preamble+sync found");
|
||||
|
||||
off += sync_len; /* Skip preamble + sync. */
|
||||
|
||||
uint8_t raw[10];
|
||||
uint32_t decoded =
|
||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
||||
"01","10"); /* Manchester. */
|
||||
uint32_t decoded = convert_from_line_code(
|
||||
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
|
||||
FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded);
|
||||
|
||||
if (decoded < 8*10) return false; /* Require the full 10 bytes. */
|
||||
if(decoded < 8 * 10) return false; /* Require the full 10 bytes. */
|
||||
|
||||
/* Check the CRC. It's a simple XOR of bytes 1-9, the first byte
|
||||
* is not included. The meaning of the first byte is unknown and
|
||||
* we don't display it. */
|
||||
uint8_t crc = 0;
|
||||
for (int j = 1; j < 10; j++) crc ^= raw[j];
|
||||
if (crc != 0) return false; /* Require sane checksum. */
|
||||
for(int j = 1; j < 10; j++) crc ^= raw[j];
|
||||
if(crc != 0) return false; /* Require sane checksum. */
|
||||
|
||||
int repeat = raw[5] & 0xf;
|
||||
float kpa = (float)raw[6]*1.364;
|
||||
int temp = raw[7]-50;
|
||||
float kpa = (float)raw[6] * 1.364;
|
||||
int temp = raw[7] - 50;
|
||||
int battery = raw[8]; /* This may be the battery. It's not clear. */
|
||||
|
||||
snprintf(info->name,sizeof(info->name),"%s","Citroen TPMS");
|
||||
snprintf(info->raw,sizeof(info->raw),
|
||||
snprintf(info->name, sizeof(info->name), "%s", "Citroen TPMS");
|
||||
snprintf(
|
||||
info->raw,
|
||||
sizeof(info->raw),
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
|
||||
raw[6],raw[7],raw[8],raw[9]);
|
||||
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X%02X",
|
||||
raw[1],raw[2],raw[3],raw[4]);
|
||||
snprintf(info->info2,sizeof(info->info2),"Pressure %.2f kpa", (double)kpa);
|
||||
snprintf(info->info3,sizeof(info->info3),"Temperature %d C", temp);
|
||||
snprintf(info->info4,sizeof(info->info4),"Repeat %d, Bat %d", repeat, battery);
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4],
|
||||
raw[5],
|
||||
raw[6],
|
||||
raw[7],
|
||||
raw[8],
|
||||
raw[9]);
|
||||
snprintf(
|
||||
info->info1,
|
||||
sizeof(info->info1),
|
||||
"Tire ID %02X%02X%02X%02X",
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4]);
|
||||
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f kpa", (double)kpa);
|
||||
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
|
||||
snprintf(info->info4, sizeof(info->info4), "Repeat %d, Bat %d", repeat, battery);
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoViewDecoder CitroenTPMSDecoder = {
|
||||
"Citroen TPMS", decode
|
||||
};
|
||||
ProtoViewDecoder CitroenTPMSDecoder = {"Citroen TPMS", decode};
|
||||
|
||||
@@ -10,55 +10,67 @@
|
||||
|
||||
#include "../app.h"
|
||||
|
||||
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
|
||||
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
|
||||
const char* sync_pattern = "010101010101"
|
||||
"0110";
|
||||
uint8_t sync_len = 12 + 4; /* We just use 12 preamble symbols + sync. */
|
||||
if(numbits - sync_len < 8 * 8) return false;
|
||||
|
||||
const char *sync_pattern = "010101010101" "0110";
|
||||
uint8_t sync_len = 12+4; /* We just use 12 preamble symbols + sync. */
|
||||
if (numbits-sync_len < 8*8) return false;
|
||||
|
||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
FURI_LOG_E(TAG, "Fort TPMS preamble+sync found");
|
||||
|
||||
off += sync_len; /* Skip preamble and sync. */
|
||||
|
||||
uint8_t raw[8];
|
||||
uint32_t decoded =
|
||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
||||
"01","10"); /* Manchester. */
|
||||
uint32_t decoded = convert_from_line_code(
|
||||
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
|
||||
FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded);
|
||||
|
||||
if (decoded < 8*8) return false; /* Require the full 8 bytes. */
|
||||
if(decoded < 8 * 8) return false; /* Require the full 8 bytes. */
|
||||
|
||||
/* CRC is just the sum of the first 7 bytes MOD 256. */
|
||||
uint8_t crc = 0;
|
||||
for (int j = 0; j < 7; j++) crc += raw[j];
|
||||
if (crc != raw[7]) return false; /* Require sane CRC. */
|
||||
for(int j = 0; j < 7; j++) crc += raw[j];
|
||||
if(crc != raw[7]) return false; /* Require sane CRC. */
|
||||
|
||||
float psi = 0.25 * (((raw[6]&0x20)<<3)|raw[4]);
|
||||
float psi = 0.25 * (((raw[6] & 0x20) << 3) | raw[4]);
|
||||
|
||||
/* Temperature apperas to be valid only if the most significant
|
||||
* bit of the value is not set. Otherwise its meaning is unknown.
|
||||
* Likely useful to alternatively send temperature or other info. */
|
||||
int temp = raw[5] & 0x80 ? 0 : raw[5]-56;
|
||||
int temp = raw[5] & 0x80 ? 0 : raw[5] - 56;
|
||||
int flags = raw[5] & 0x7f;
|
||||
int car_moving = (raw[6] & 0x44) == 0x44;
|
||||
|
||||
snprintf(info->name,sizeof(info->name),"%s","Ford TPMS");
|
||||
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
|
||||
raw[6],raw[7]);
|
||||
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3]);
|
||||
snprintf(info->info2,sizeof(info->info2),"Pressure %.2f psi", (double)psi);
|
||||
if (temp)
|
||||
snprintf(info->info3,sizeof(info->info3),"Temperature %d C", temp);
|
||||
snprintf(info->name, sizeof(info->name), "%s", "Ford TPMS");
|
||||
snprintf(
|
||||
info->raw,
|
||||
sizeof(info->raw),
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4],
|
||||
raw[5],
|
||||
raw[6],
|
||||
raw[7]);
|
||||
snprintf(
|
||||
info->info1,
|
||||
sizeof(info->info1),
|
||||
"Tire ID %02X%02X%02X%02X",
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3]);
|
||||
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f psi", (double)psi);
|
||||
if(temp)
|
||||
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
|
||||
else
|
||||
snprintf(info->info3,sizeof(info->info3),"Flags %d", flags);
|
||||
snprintf(info->info4,sizeof(info->info4),"Moving %s", car_moving ? "yes" : "no");
|
||||
snprintf(info->info3, sizeof(info->info3), "Flags %d", flags);
|
||||
snprintf(info->info4, sizeof(info->info4), "Moving %s", car_moving ? "yes" : "no");
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoViewDecoder FordTPMSDecoder = {
|
||||
"Ford TPMS", decode
|
||||
};
|
||||
ProtoViewDecoder FordTPMSDecoder = {"Ford TPMS", decode};
|
||||
|
||||
@@ -6,60 +6,84 @@
|
||||
|
||||
#include "../app.h"
|
||||
|
||||
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
|
||||
if (numbits < 32) return false;
|
||||
const char *sync_pattern = "01100110" "01100110" "10010110" "10010110";
|
||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
|
||||
if(numbits < 32) return false;
|
||||
const char* sync_pattern = "01100110"
|
||||
"01100110"
|
||||
"10010110"
|
||||
"10010110";
|
||||
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
FURI_LOG_E(TAG, "Oregon2 preamble+sync found");
|
||||
|
||||
off += 32; /* Skip preamble. */
|
||||
|
||||
uint8_t buffer[8], raw[8] = {0};
|
||||
uint32_t decoded =
|
||||
convert_from_line_code(buffer,sizeof(buffer),bits,numbytes,off,"1001","0110");
|
||||
convert_from_line_code(buffer, sizeof(buffer), bits, numbytes, off, "1001", "0110");
|
||||
FURI_LOG_E(TAG, "Oregon2 decoded bits: %lu", decoded);
|
||||
|
||||
if (decoded < 11*4) return false; /* Minimum len to extract some data. */
|
||||
if(decoded < 11 * 4) return false; /* Minimum len to extract some data. */
|
||||
|
||||
char temp[3] = {0}, deviceid[2] = {0}, hum[2] = {0};
|
||||
for (int j = 0; j < 64; j += 4) {
|
||||
for(int j = 0; j < 64; j += 4) {
|
||||
uint8_t nib[1];
|
||||
nib[0] = (bitmap_get(buffer,8,j+0) |
|
||||
bitmap_get(buffer,8,j+1) << 1 |
|
||||
bitmap_get(buffer,8,j+2) << 2 |
|
||||
bitmap_get(buffer,8,j+3) << 3);
|
||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j/4, (unsigned int)nib[0]);
|
||||
raw[j/8] |= nib[0] << (4-(j%4));
|
||||
switch(j/4) {
|
||||
case 1: deviceid[0] |= nib[0]; break;
|
||||
case 0: deviceid[0] |= nib[0] << 4; break;
|
||||
case 3: deviceid[1] |= nib[0]; break;
|
||||
case 2: deviceid[1] |= nib[0] << 4; break;
|
||||
case 10: temp[0] = nib[0]; break;
|
||||
nib[0] =
|
||||
(bitmap_get(buffer, 8, j + 0) | bitmap_get(buffer, 8, j + 1) << 1 |
|
||||
bitmap_get(buffer, 8, j + 2) << 2 | bitmap_get(buffer, 8, j + 3) << 3);
|
||||
if(DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j / 4, (unsigned int)nib[0]);
|
||||
raw[j / 8] |= nib[0] << (4 - (j % 4));
|
||||
switch(j / 4) {
|
||||
case 1:
|
||||
deviceid[0] |= nib[0];
|
||||
break;
|
||||
case 0:
|
||||
deviceid[0] |= nib[0] << 4;
|
||||
break;
|
||||
case 3:
|
||||
deviceid[1] |= nib[0];
|
||||
break;
|
||||
case 2:
|
||||
deviceid[1] |= nib[0] << 4;
|
||||
break;
|
||||
case 10:
|
||||
temp[0] = nib[0];
|
||||
break;
|
||||
/* Fixme: take the temperature sign from nibble 11. */
|
||||
case 9: temp[1] = nib[0]; break;
|
||||
case 8: temp[2] = nib[0]; break;
|
||||
case 13: hum[0] = nib[0]; break;
|
||||
case 12: hum[1] = nib[0]; break;
|
||||
case 9:
|
||||
temp[1] = nib[0];
|
||||
break;
|
||||
case 8:
|
||||
temp[2] = nib[0];
|
||||
break;
|
||||
case 13:
|
||||
hum[0] = nib[0];
|
||||
break;
|
||||
case 12:
|
||||
hum[1] = nib[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(info->name,sizeof(info->name),"%s","Oregon v2.1");
|
||||
snprintf(info->name, sizeof(info->name), "%s", "Oregon v2.1");
|
||||
/* The following line crashes the Flipper because of broken
|
||||
* snprintf() implementation. */
|
||||
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
|
||||
raw[6],raw[7]);
|
||||
snprintf(info->info1,sizeof(info->info1),"Sensor ID %02X%02X",
|
||||
deviceid[0], deviceid[1]);
|
||||
snprintf(info->info2,sizeof(info->info2),"Temperature %d%d.%d",
|
||||
temp[0],temp[1],temp[2]);
|
||||
snprintf(info->info3,sizeof(info->info3),"Humidity %d%d",
|
||||
hum[0],hum[1]);
|
||||
snprintf(
|
||||
info->raw,
|
||||
sizeof(info->raw),
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4],
|
||||
raw[5],
|
||||
raw[6],
|
||||
raw[7]);
|
||||
snprintf(info->info1, sizeof(info->info1), "Sensor ID %02X%02X", deviceid[0], deviceid[1]);
|
||||
snprintf(info->info2, sizeof(info->info2), "Temperature %d%d.%d", temp[0], temp[1], temp[2]);
|
||||
snprintf(info->info3, sizeof(info->info3), "Humidity %d%d", hum[0], hum[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoViewDecoder Oregon2Decoder = {
|
||||
"Oregon2", decode
|
||||
};
|
||||
ProtoViewDecoder Oregon2Decoder = {"Oregon2", decode};
|
||||
|
||||
@@ -6,61 +6,66 @@
|
||||
#include "../app.h"
|
||||
|
||||
#define USE_TEST_VECTOR 0
|
||||
static const char *test_vector =
|
||||
static const char* test_vector =
|
||||
"...01010101010101010110" // Preamble + sync
|
||||
|
||||
/* The following is Marshal encoded, so each two characters are
|
||||
* actaully one bit. 01 = 0, 10 = 1. */
|
||||
"010110010110" // Flags.
|
||||
"10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa.
|
||||
// 244 kpa here.
|
||||
"1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here.
|
||||
// 244 kpa here.
|
||||
"1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here.
|
||||
"1001010101101001"
|
||||
"0101100110010101"
|
||||
"1001010101100110" // Tire ID. 0x7AD779 here.
|
||||
"1001010101100110" // Tire ID. 0x7AD779 here.
|
||||
"0101010101010101"
|
||||
"0101010101010101" // Two FF bytes (usually). Unknown.
|
||||
"0101010101010101" // Two FF bytes (usually). Unknown.
|
||||
"0110010101010101"; // CRC8 with (poly 7, initialization 0).
|
||||
|
||||
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
|
||||
|
||||
if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
|
||||
bitmap_set_pattern(bits,numbytes,test_vector);
|
||||
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
|
||||
if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
|
||||
bitmap_set_pattern(bits, numbytes, test_vector);
|
||||
numbits = strlen(test_vector);
|
||||
}
|
||||
|
||||
if (numbits-12 < 9*8) return false;
|
||||
if(numbits - 12 < 9 * 8) return false;
|
||||
|
||||
const char *sync_pattern = "01010101010101010110";
|
||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
const char* sync_pattern = "01010101010101010110";
|
||||
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
FURI_LOG_E(TAG, "Renault TPMS preamble+sync found");
|
||||
|
||||
off += 20; /* Skip preamble. */
|
||||
|
||||
uint8_t raw[9];
|
||||
uint32_t decoded =
|
||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
||||
"01","10"); /* Manchester. */
|
||||
uint32_t decoded = convert_from_line_code(
|
||||
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
|
||||
FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded);
|
||||
|
||||
if (decoded < 8*9) return false; /* Require the full 9 bytes. */
|
||||
if (crc8(raw,8,0,7) != raw[8]) return false; /* Require sane CRC. */
|
||||
if(decoded < 8 * 9) return false; /* Require the full 9 bytes. */
|
||||
if(crc8(raw, 8, 0, 7) != raw[8]) return false; /* Require sane CRC. */
|
||||
|
||||
float kpa = 0.75 *((uint32_t)((raw[0]&3)<<8) | raw[1]);
|
||||
int temp = raw[2]-30;
|
||||
float kpa = 0.75 * ((uint32_t)((raw[0] & 3) << 8) | raw[1]);
|
||||
int temp = raw[2] - 30;
|
||||
|
||||
snprintf(info->name,sizeof(info->name),"%s","Renault TPMS");
|
||||
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
|
||||
raw[6],raw[7],raw[8]);
|
||||
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X",
|
||||
raw[3],raw[4],raw[5]);
|
||||
snprintf(info->info2,sizeof(info->info2),"Pressure %.2f kpa", (double)kpa);
|
||||
snprintf(info->info3,sizeof(info->info3),"Temperature %d C", temp);
|
||||
snprintf(info->name, sizeof(info->name), "%s", "Renault TPMS");
|
||||
snprintf(
|
||||
info->raw,
|
||||
sizeof(info->raw),
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4],
|
||||
raw[5],
|
||||
raw[6],
|
||||
raw[7],
|
||||
raw[8]);
|
||||
snprintf(info->info1, sizeof(info->info1), "Tire ID %02X%02X%02X", raw[3], raw[4], raw[5]);
|
||||
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f kpa", (double)kpa);
|
||||
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoViewDecoder RenaultTPMSDecoder = {
|
||||
"Renault TPMS", decode
|
||||
};
|
||||
ProtoViewDecoder RenaultTPMSDecoder = {"Renault TPMS", decode};
|
||||
|
||||
@@ -11,20 +11,21 @@
|
||||
#include "../app.h"
|
||||
|
||||
#define USE_TEST_VECTOR 0
|
||||
static const char *test_vector = "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010";
|
||||
static const char* test_vector =
|
||||
"000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010";
|
||||
|
||||
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
|
||||
|
||||
if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
|
||||
bitmap_set_pattern(bits,numbytes,test_vector);
|
||||
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
|
||||
if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
|
||||
bitmap_set_pattern(bits, numbytes, test_vector);
|
||||
numbits = strlen(test_vector);
|
||||
}
|
||||
|
||||
if (numbits < 64) return false; /* Preamble + data. */
|
||||
if(numbits < 64) return false; /* Preamble + data. */
|
||||
|
||||
const char *sync_pattern = "1111010101" "01011010";
|
||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
const char* sync_pattern = "1111010101"
|
||||
"01011010";
|
||||
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
FURI_LOG_E(TAG, "Schrader TPMS gap+preamble found");
|
||||
|
||||
off += 10; /* Skip just the long pulse and the first 3 bits of sync, so
|
||||
@@ -32,34 +33,46 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
|
||||
0011 = 0x3. */
|
||||
|
||||
uint8_t raw[8];
|
||||
uint32_t decoded =
|
||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
||||
"01","10"); /* Manchester code. */
|
||||
uint32_t decoded = convert_from_line_code(
|
||||
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */
|
||||
FURI_LOG_E(TAG, "Schrader TPMS decoded bits: %lu", decoded);
|
||||
|
||||
if (decoded < 64) return false; /* Require the full 8 bytes. */
|
||||
if(decoded < 64) return false; /* Require the full 8 bytes. */
|
||||
|
||||
raw[0] |= 0xf0; // Fix the preamble nibble for checksum computation.
|
||||
uint8_t cksum = crc8(raw,sizeof(raw)-1,0xf0,0x7);
|
||||
if (cksum != raw[7]) {
|
||||
uint8_t cksum = crc8(raw, sizeof(raw) - 1, 0xf0, 0x7);
|
||||
if(cksum != raw[7]) {
|
||||
FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
float kpa = (float)raw[5]*2.5;
|
||||
int temp = raw[6]-50;
|
||||
float kpa = (float)raw[5] * 2.5;
|
||||
int temp = raw[6] - 50;
|
||||
|
||||
snprintf(info->name,sizeof(info->name),"%s","Schrader TPMS");
|
||||
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
|
||||
raw[6],raw[7]);
|
||||
snprintf(info->info1,sizeof(info->info1),"Tire ID %01X%02X%02X%02X",
|
||||
raw[1]&7,raw[2],raw[3],raw[4]); /* Only 28 bits of ID, not 32. */
|
||||
snprintf(info->info2,sizeof(info->info2),"Pressure %.2f kpa", (double)kpa);
|
||||
snprintf(info->info3,sizeof(info->info3),"Temperature %d C", temp);
|
||||
snprintf(info->name, sizeof(info->name), "%s", "Schrader TPMS");
|
||||
snprintf(
|
||||
info->raw,
|
||||
sizeof(info->raw),
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4],
|
||||
raw[5],
|
||||
raw[6],
|
||||
raw[7]);
|
||||
snprintf(
|
||||
info->info1,
|
||||
sizeof(info->info1),
|
||||
"Tire ID %01X%02X%02X%02X",
|
||||
raw[1] & 7,
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4]); /* Only 28 bits of ID, not 32. */
|
||||
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f kpa", (double)kpa);
|
||||
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoViewDecoder SchraderTPMSDecoder = {
|
||||
"Schrader TPMS", decode
|
||||
};
|
||||
ProtoViewDecoder SchraderTPMSDecoder = {"Schrader TPMS", decode};
|
||||
|
||||
@@ -24,54 +24,61 @@
|
||||
|
||||
#include "../app.h"
|
||||
|
||||
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
|
||||
|
||||
if (numbits-6 < 64*2) return false; /* Ask for 64 bit of data (each bit
|
||||
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
|
||||
if(numbits - 6 < 64 * 2)
|
||||
return false; /* Ask for 64 bit of data (each bit
|
||||
is two symbols in the bitmap). */
|
||||
|
||||
char *sync[] = {
|
||||
"00111100",
|
||||
"001111100",
|
||||
"00111101",
|
||||
"001111101",
|
||||
NULL
|
||||
};
|
||||
char* sync[] = {"00111100", "001111100", "00111101", "001111101", NULL};
|
||||
|
||||
int j;
|
||||
uint32_t off = 0;
|
||||
for (j = 0; sync[j]; j++) {
|
||||
off = bitmap_seek_bits(bits,numbytes,0,numbits,sync[j]);
|
||||
if (off != BITMAP_SEEK_NOT_FOUND) {
|
||||
off += strlen(sync[j])-2;
|
||||
for(j = 0; sync[j]; j++) {
|
||||
off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync[j]);
|
||||
if(off != BITMAP_SEEK_NOT_FOUND) {
|
||||
off += strlen(sync[j]) - 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||
|
||||
FURI_LOG_E(TAG, "Toyota TPMS sync[%s] found", sync[j]);
|
||||
|
||||
uint8_t raw[9];
|
||||
uint32_t decoded =
|
||||
convert_from_diff_manchester(raw,sizeof(raw),bits,numbytes,off,true);
|
||||
uint32_t decoded = convert_from_diff_manchester(raw, sizeof(raw), bits, numbytes, off, true);
|
||||
FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded);
|
||||
|
||||
if (decoded < 8*9) return false; /* Require the full 8 bytes. */
|
||||
if (crc8(raw,8,0x80,7) != raw[8]) return false; /* Require sane CRC. */
|
||||
if(decoded < 8 * 9) return false; /* Require the full 8 bytes. */
|
||||
if(crc8(raw, 8, 0x80, 7) != raw[8]) return false; /* Require sane CRC. */
|
||||
|
||||
float kpa = (float)((raw[4]&0x7f)<<1 | raw[5]>>7) * 0.25 - 7;
|
||||
int temp = ((raw[5]&0x7f)<<1 | raw[6]>>7) - 40;
|
||||
float kpa = (float)((raw[4] & 0x7f) << 1 | raw[5] >> 7) * 0.25 - 7;
|
||||
int temp = ((raw[5] & 0x7f) << 1 | raw[6] >> 7) - 40;
|
||||
|
||||
snprintf(info->name,sizeof(info->name),"%s","Toyota TPMS");
|
||||
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
|
||||
raw[6],raw[7],raw[8]);
|
||||
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X%02X",
|
||||
raw[0],raw[1],raw[2],raw[3]);
|
||||
snprintf(info->info1,sizeof(info->info1),"Pressure %.2f psi", (double)kpa);
|
||||
snprintf(info->info2,sizeof(info->info2),"Temperature %d C", temp);
|
||||
snprintf(info->name, sizeof(info->name), "%s", "Toyota TPMS");
|
||||
snprintf(
|
||||
info->raw,
|
||||
sizeof(info->raw),
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3],
|
||||
raw[4],
|
||||
raw[5],
|
||||
raw[6],
|
||||
raw[7],
|
||||
raw[8]);
|
||||
snprintf(
|
||||
info->info1,
|
||||
sizeof(info->info1),
|
||||
"Tire ID %02X%02X%02X%02X",
|
||||
raw[0],
|
||||
raw[1],
|
||||
raw[2],
|
||||
raw[3]);
|
||||
snprintf(info->info1, sizeof(info->info1), "Pressure %.2f psi", (double)kpa);
|
||||
snprintf(info->info2, sizeof(info->info2), "Temperature %d C", temp);
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoViewDecoder ToyotaTPMSDecoder = {
|
||||
"Toyota TPMS", decode
|
||||
};
|
||||
ProtoViewDecoder ToyotaTPMSDecoder = {"Toyota TPMS", decode};
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include "app.h"
|
||||
|
||||
bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info);
|
||||
void initialize_msg_info(ProtoViewMsgInfo *i);
|
||||
bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info);
|
||||
void initialize_msg_info(ProtoViewMsgInfo* i);
|
||||
|
||||
/* =============================================================================
|
||||
* Raw signal detection
|
||||
@@ -17,7 +17,7 @@ uint32_t duration_delta(uint32_t a, uint32_t b) {
|
||||
}
|
||||
|
||||
/* Reset the current signal, so that a new one can be detected. */
|
||||
void reset_current_signal(ProtoViewApp *app) {
|
||||
void reset_current_signal(ProtoViewApp* app) {
|
||||
app->signal_bestlen = 0;
|
||||
app->signal_offset = 0;
|
||||
app->signal_decoded = false;
|
||||
@@ -38,47 +38,47 @@ void reset_current_signal(ProtoViewApp *app) {
|
||||
* For instance Oregon2 sensors, in the case of protocol 2.1 will send
|
||||
* pulses of ~400us (RF on) VS ~580us (RF off). */
|
||||
#define SEARCH_CLASSES 3
|
||||
uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
|
||||
uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) {
|
||||
struct {
|
||||
uint32_t dur[2]; /* dur[0] = low, dur[1] = high */
|
||||
uint32_t count[2]; /* Associated observed frequency. */
|
||||
uint32_t dur[2]; /* dur[0] = low, dur[1] = high */
|
||||
uint32_t count[2]; /* Associated observed frequency. */
|
||||
} classes[SEARCH_CLASSES];
|
||||
|
||||
memset(classes,0,sizeof(classes));
|
||||
memset(classes, 0, sizeof(classes));
|
||||
uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we
|
||||
allow for high and low. */
|
||||
uint32_t len = 0; /* Observed len of coherent samples. */
|
||||
s->short_pulse_dur = 0;
|
||||
for (uint32_t j = idx; j < idx+500; j++) {
|
||||
for(uint32_t j = idx; j < idx + 500; j++) {
|
||||
bool level;
|
||||
uint32_t dur;
|
||||
raw_samples_get(s, j, &level, &dur);
|
||||
|
||||
if (dur < minlen || dur > maxlen) break; /* return. */
|
||||
if(dur < minlen || dur > maxlen) break; /* return. */
|
||||
|
||||
/* Let's see if it matches a class we already have or if we
|
||||
* can populate a new (yet empty) class. */
|
||||
uint32_t k;
|
||||
for (k = 0; k < SEARCH_CLASSES; k++) {
|
||||
if (classes[k].count[level] == 0) {
|
||||
for(k = 0; k < SEARCH_CLASSES; k++) {
|
||||
if(classes[k].count[level] == 0) {
|
||||
classes[k].dur[level] = dur;
|
||||
classes[k].count[level] = 1;
|
||||
break; /* Sample accepted. */
|
||||
} else {
|
||||
uint32_t classavg = classes[k].dur[level];
|
||||
uint32_t count = classes[k].count[level];
|
||||
uint32_t delta = duration_delta(dur,classavg);
|
||||
uint32_t delta = duration_delta(dur, classavg);
|
||||
/* Is the difference in duration between this signal and
|
||||
* the class we are inspecting less than a given percentage?
|
||||
* If so, accept this signal. */
|
||||
if (delta < classavg/5) { /* 100%/5 = 20%. */
|
||||
if(delta < classavg / 5) { /* 100%/5 = 20%. */
|
||||
/* It is useful to compute the average of the class
|
||||
* we are observing. We know how many samples we got so
|
||||
* far, so we can recompute the average easily.
|
||||
* By always having a better estimate of the pulse len
|
||||
* we can avoid missing next samples in case the first
|
||||
* observed samples are too off. */
|
||||
classavg = ((classavg * count) + dur) / (count+1);
|
||||
classavg = ((classavg * count) + dur) / (count + 1);
|
||||
classes[k].dur[level] = classavg;
|
||||
classes[k].count[level]++;
|
||||
break; /* Sample accepted. */
|
||||
@@ -86,7 +86,7 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
|
||||
}
|
||||
}
|
||||
|
||||
if (k == SEARCH_CLASSES) break; /* No match, return. */
|
||||
if(k == SEARCH_CLASSES) break; /* No match, return. */
|
||||
|
||||
/* If we are here, we accepted this sample. Try with the next
|
||||
* one. */
|
||||
@@ -96,14 +96,12 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
|
||||
/* Update the buffer setting the shortest pulse we found
|
||||
* among the three classes. This will be used when scaling
|
||||
* for visualization. */
|
||||
uint32_t short_dur[2] = {0,0};
|
||||
for (int j = 0; j < SEARCH_CLASSES; j++) {
|
||||
for (int level = 0; level < 2; level++) {
|
||||
if (classes[j].dur[level] == 0) continue;
|
||||
if (classes[j].count[level] < 3) continue;
|
||||
if (short_dur[level] == 0 ||
|
||||
short_dur[level] > classes[j].dur[level])
|
||||
{
|
||||
uint32_t short_dur[2] = {0, 0};
|
||||
for(int j = 0; j < SEARCH_CLASSES; j++) {
|
||||
for(int level = 0; level < 2; level++) {
|
||||
if(classes[j].dur[level] == 0) continue;
|
||||
if(classes[j].count[level] < 3) continue;
|
||||
if(short_dur[level] == 0 || short_dur[level] > classes[j].dur[level]) {
|
||||
short_dur[level] = classes[j].dur[level];
|
||||
}
|
||||
}
|
||||
@@ -112,9 +110,9 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
|
||||
/* Use the average between high and low short pulses duration.
|
||||
* Often they are a bit different, and using the average is more robust
|
||||
* when we do decoding sampling at short_pulse_dur intervals. */
|
||||
if (short_dur[0] == 0) short_dur[0] = short_dur[1];
|
||||
if (short_dur[1] == 0) short_dur[1] = short_dur[0];
|
||||
s->short_pulse_dur = (short_dur[0]+short_dur[1])/2;
|
||||
if(short_dur[0] == 0) short_dur[0] = short_dur[1];
|
||||
if(short_dur[1] == 0) short_dur[1] = short_dur[0];
|
||||
s->short_pulse_dur = (short_dur[0] + short_dur[1]) / 2;
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -123,54 +121,56 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
|
||||
* in order to find a coherent signal. If a signal that does not appear to
|
||||
* be just noise is found, it is set in DetectedSamples global signal
|
||||
* buffer, that is what is rendered on the screen. */
|
||||
void scan_for_signal(ProtoViewApp *app) {
|
||||
void scan_for_signal(ProtoViewApp* app) {
|
||||
/* We need to work on a copy: the RawSamples buffer is populated
|
||||
* by the background thread receiving data. */
|
||||
RawSamplesBuffer *copy = raw_samples_alloc();
|
||||
raw_samples_copy(copy,RawSamples);
|
||||
RawSamplesBuffer* copy = raw_samples_alloc();
|
||||
raw_samples_copy(copy, RawSamples);
|
||||
|
||||
/* Try to seek on data that looks to have a regular high low high low
|
||||
* pattern. */
|
||||
uint32_t minlen = 18; /* Min run of coherent samples. With less
|
||||
uint32_t minlen = 18; /* Min run of coherent samples. With less
|
||||
than a few samples it's very easy to
|
||||
mistake noise for signal. */
|
||||
|
||||
ProtoViewMsgInfo *info = malloc(sizeof(ProtoViewMsgInfo));
|
||||
ProtoViewMsgInfo* info = malloc(sizeof(ProtoViewMsgInfo));
|
||||
uint32_t i = 0;
|
||||
|
||||
while (i < copy->total-1) {
|
||||
uint32_t thislen = search_coherent_signal(copy,i);
|
||||
while(i < copy->total - 1) {
|
||||
uint32_t thislen = search_coherent_signal(copy, i);
|
||||
|
||||
/* For messages that are long enough, attempt decoding. */
|
||||
if (thislen > minlen) {
|
||||
if(thislen > minlen) {
|
||||
initialize_msg_info(info);
|
||||
uint32_t saved_idx = copy->idx; /* Save index, see later. */
|
||||
/* decode_signal() expects the detected signal to start
|
||||
* from index .*/
|
||||
raw_samples_center(copy,i);
|
||||
bool decoded = decode_signal(copy,thislen,info);
|
||||
raw_samples_center(copy, i);
|
||||
bool decoded = decode_signal(copy, thislen, info);
|
||||
copy->idx = saved_idx; /* Restore the index as we are scanning
|
||||
the signal in the loop. */
|
||||
|
||||
/* Accept this signal as the new signal if either it's longer
|
||||
* than the previous undecoded one, or the previous one was
|
||||
* unknown and this is decoded. */
|
||||
if ((thislen > app->signal_bestlen && app->signal_decoded == false)
|
||||
|| (app->signal_decoded == false && decoded))
|
||||
{
|
||||
if((thislen > app->signal_bestlen && app->signal_decoded == false) ||
|
||||
(app->signal_decoded == false && decoded)) {
|
||||
app->signal_info = *info;
|
||||
app->signal_bestlen = thislen;
|
||||
app->signal_decoded = decoded;
|
||||
raw_samples_copy(DetectedSamples,copy);
|
||||
raw_samples_center(DetectedSamples,i);
|
||||
FURI_LOG_E(TAG, "===> Displayed sample updated (%d samples %lu us)",
|
||||
(int)thislen, DetectedSamples->short_pulse_dur);
|
||||
raw_samples_copy(DetectedSamples, copy);
|
||||
raw_samples_center(DetectedSamples, i);
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"===> Displayed sample updated (%d samples %lu us)",
|
||||
(int)thislen,
|
||||
DetectedSamples->short_pulse_dur);
|
||||
|
||||
/* Adjust raw view scale if the signal has an high
|
||||
* data rate. */
|
||||
if (DetectedSamples->short_pulse_dur < 75)
|
||||
if(DetectedSamples->short_pulse_dur < 75)
|
||||
app->us_scale = 10;
|
||||
else if (DetectedSamples->short_pulse_dur < 145)
|
||||
else if(DetectedSamples->short_pulse_dur < 145)
|
||||
app->us_scale = 30;
|
||||
}
|
||||
}
|
||||
@@ -196,43 +196,43 @@ void scan_for_signal(ProtoViewApp *app) {
|
||||
/* Set the 'bitpos' bit to value 'val', in the specified bitmap
|
||||
* 'b' of len 'blen'.
|
||||
* Out of range bits will silently be discarded. */
|
||||
void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) {
|
||||
uint32_t byte = bitpos/8;
|
||||
uint32_t bit = 7-(bitpos&7);
|
||||
if (byte >= blen) return;
|
||||
if (val)
|
||||
b[byte] |= 1<<bit;
|
||||
void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val) {
|
||||
uint32_t byte = bitpos / 8;
|
||||
uint32_t bit = 7 - (bitpos & 7);
|
||||
if(byte >= blen) return;
|
||||
if(val)
|
||||
b[byte] |= 1 << bit;
|
||||
else
|
||||
b[byte] &= ~(1<<bit);
|
||||
b[byte] &= ~(1 << bit);
|
||||
}
|
||||
|
||||
/* Get the bit 'bitpos' of the bitmap 'b' of 'blen' bytes.
|
||||
* Out of range bits return false (not bit set). */
|
||||
bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos) {
|
||||
uint32_t byte = bitpos/8;
|
||||
uint32_t bit = 7-(bitpos&7);
|
||||
if (byte >= blen) return 0;
|
||||
return (b[byte] & (1<<bit)) != 0;
|
||||
bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos) {
|
||||
uint32_t byte = bitpos / 8;
|
||||
uint32_t bit = 7 - (bitpos & 7);
|
||||
if(byte >= blen) return 0;
|
||||
return (b[byte] & (1 << bit)) != 0;
|
||||
}
|
||||
|
||||
/* We decode bits assuming the first bit we receive is the LSB
|
||||
* (see bitmap_set/get functions). Many devices send data
|
||||
* encoded in the reverse way. */
|
||||
void bitmap_invert_bytes_bits(uint8_t *p, uint32_t len) {
|
||||
for (uint32_t j = 0; j < len*8; j += 8) {
|
||||
void bitmap_invert_bytes_bits(uint8_t* p, uint32_t len) {
|
||||
for(uint32_t j = 0; j < len * 8; j += 8) {
|
||||
bool bits[8];
|
||||
for (int i = 0; i < 8; i++) bits[i] = bitmap_get(p,len,j+i);
|
||||
for (int i = 0; i < 8; i++) bitmap_set(p,len,j+i,bits[7-i]);
|
||||
for(int i = 0; i < 8; i++) bits[i] = bitmap_get(p, len, j + i);
|
||||
for(int i = 0; i < 8; i++) bitmap_set(p, len, j + i, bits[7 - i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if the specified sequence of bits, provided as a string in the
|
||||
* form "11010110..." is found in the 'b' bitmap of 'blen' bits at 'bitpos'
|
||||
* position. */
|
||||
bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits) {
|
||||
for (size_t j = 0; bits[j]; j++) {
|
||||
bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits) {
|
||||
for(size_t j = 0; bits[j]; j++) {
|
||||
bool expected = (bits[j] == '1') ? true : false;
|
||||
if (bitmap_get(b,blen,bitpos+j) != expected) return false;
|
||||
if(bitmap_get(b, blen, bitpos + j) != expected) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -245,12 +245,17 @@ bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *b
|
||||
* Note: there are better algorithms, such as Boyer-Moore. Here we hope that
|
||||
* for the kind of patterns we search we'll have a lot of early stops so
|
||||
* we use a vanilla approach. */
|
||||
uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits) {
|
||||
uint32_t endpos = startpos+blen*8;
|
||||
uint32_t end2 = startpos+maxbits;
|
||||
if (end2 < endpos) endpos = end2;
|
||||
for (uint32_t j = startpos; j < endpos; j++)
|
||||
if (bitmap_match_bits(b,blen,j,bits)) return j;
|
||||
uint32_t bitmap_seek_bits(
|
||||
uint8_t* b,
|
||||
uint32_t blen,
|
||||
uint32_t startpos,
|
||||
uint32_t maxbits,
|
||||
const char* bits) {
|
||||
uint32_t endpos = startpos + blen * 8;
|
||||
uint32_t end2 = startpos + maxbits;
|
||||
if(end2 < endpos) endpos = end2;
|
||||
for(uint32_t j = startpos; j < endpos; j++)
|
||||
if(bitmap_match_bits(b, blen, j, bits)) return j;
|
||||
return BITMAP_SEEK_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -259,10 +264,10 @@ uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t
|
||||
* This function is useful in order to set the test vectors in the protocol
|
||||
* decoders, to see if the decoding works regardless of the fact we are able
|
||||
* to actually receive a given signal. */
|
||||
void bitmap_set_pattern(uint8_t *b, uint32_t blen, const char *pat) {
|
||||
void bitmap_set_pattern(uint8_t* b, uint32_t blen, const char* pat) {
|
||||
uint32_t i = 0;
|
||||
while(pat[i]) {
|
||||
bitmap_set(b,blen,i,pat[i] == '1');
|
||||
bitmap_set(b, blen, i, pat[i] == '1');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -294,31 +299,36 @@ void bitmap_set_pattern(uint8_t *b, uint32_t blen, const char *pat) {
|
||||
* bits set into the buffer 'b'. The 'rate' argument, in microseconds, is
|
||||
* the detected short-pulse duration. We expect the line code to be
|
||||
* meaningful when interpreted at multiples of 'rate'. */
|
||||
uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, uint32_t idx, uint32_t count, uint32_t rate) {
|
||||
if (rate == 0) return 0; /* We can't perform the conversion. */
|
||||
uint32_t convert_signal_to_bits(
|
||||
uint8_t* b,
|
||||
uint32_t blen,
|
||||
RawSamplesBuffer* s,
|
||||
uint32_t idx,
|
||||
uint32_t count,
|
||||
uint32_t rate) {
|
||||
if(rate == 0) return 0; /* We can't perform the conversion. */
|
||||
uint32_t bitpos = 0;
|
||||
for (uint32_t j = 0; j < count; j++) {
|
||||
for(uint32_t j = 0; j < count; j++) {
|
||||
uint32_t dur;
|
||||
bool level;
|
||||
raw_samples_get(s, j+idx, &level, &dur);
|
||||
raw_samples_get(s, j + idx, &level, &dur);
|
||||
|
||||
uint32_t numbits = dur / rate; /* full bits that surely fit. */
|
||||
uint32_t rest = dur % rate; /* How much we are left with. */
|
||||
if (rest > rate/2) numbits++; /* There is another one. */
|
||||
uint32_t rest = dur % rate; /* How much we are left with. */
|
||||
if(rest > rate / 2) numbits++; /* There is another one. */
|
||||
|
||||
/* Limit how much a single sample can spawn. There are likely no
|
||||
* protocols doing such long pulses when the rate is low. */
|
||||
if (numbits > 1024) numbits = 1024;
|
||||
if(numbits > 1024) numbits = 1024;
|
||||
|
||||
if (0) /* Super verbose, so not under the DEBUG_MSG define. */
|
||||
FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits",
|
||||
dur,numbits,(int)level);
|
||||
if(0) /* Super verbose, so not under the DEBUG_MSG define. */
|
||||
FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", dur, numbits, (int)level);
|
||||
|
||||
/* If the signal is too short, let's claim it an interference
|
||||
* and ignore it completely. */
|
||||
if (numbits == 0) continue;
|
||||
if(numbits == 0) continue;
|
||||
|
||||
while(numbits--) bitmap_set(b,blen,bitpos++,level);
|
||||
while(numbits--) bitmap_set(b, blen, bitpos++, level);
|
||||
}
|
||||
return bitpos;
|
||||
}
|
||||
@@ -335,23 +345,29 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s,
|
||||
* specified in bytes by the caller, via the 'len' parameters).
|
||||
*
|
||||
* The decoding starts at the specified offset (in bits) 'off'. */
|
||||
uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, const char *zero_pattern, const char *one_pattern)
|
||||
{
|
||||
uint32_t convert_from_line_code(
|
||||
uint8_t* buf,
|
||||
uint64_t buflen,
|
||||
uint8_t* bits,
|
||||
uint32_t len,
|
||||
uint32_t off,
|
||||
const char* zero_pattern,
|
||||
const char* one_pattern) {
|
||||
uint32_t decoded = 0; /* Number of bits extracted. */
|
||||
len *= 8; /* Convert bytes to bits. */
|
||||
while(off < len) {
|
||||
bool bitval;
|
||||
if (bitmap_match_bits(bits,len,off,zero_pattern)) {
|
||||
if(bitmap_match_bits(bits, len, off, zero_pattern)) {
|
||||
bitval = false;
|
||||
off += strlen(zero_pattern);
|
||||
} else if (bitmap_match_bits(bits,len,off,one_pattern)) {
|
||||
} else if(bitmap_match_bits(bits, len, off, one_pattern)) {
|
||||
bitval = true;
|
||||
off += strlen(one_pattern);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
bitmap_set(buf,buflen,decoded++,bitval);
|
||||
if (decoded/8 == buflen) break; /* No space left on target buffer. */
|
||||
bitmap_set(buf, buflen, decoded++, bitval);
|
||||
if(decoded / 8 == buflen) break; /* No space left on target buffer. */
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
@@ -362,17 +378,22 @@ uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, ui
|
||||
* in differential codings the next bits depend on the previous one.
|
||||
*
|
||||
* Parameters and return values are like convert_from_line_code(). */
|
||||
uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous)
|
||||
{
|
||||
uint32_t convert_from_diff_manchester(
|
||||
uint8_t* buf,
|
||||
uint64_t buflen,
|
||||
uint8_t* bits,
|
||||
uint32_t len,
|
||||
uint32_t off,
|
||||
bool previous) {
|
||||
uint32_t decoded = 0;
|
||||
len *= 8; /* Conver to bits. */
|
||||
for (uint32_t j = off; j < len; j += 2) {
|
||||
bool b0 = bitmap_get(bits,len,j);
|
||||
bool b1 = bitmap_get(bits,len,j+1);
|
||||
if (b0 == previous) break; /* Each new bit must switch value. */
|
||||
bitmap_set(buf,buflen,decoded++,b0 == b1);
|
||||
for(uint32_t j = off; j < len; j += 2) {
|
||||
bool b0 = bitmap_get(bits, len, j);
|
||||
bool b1 = bitmap_get(bits, len, j + 1);
|
||||
if(b0 == previous) break; /* Each new bit must switch value. */
|
||||
bitmap_set(buf, buflen, decoded++, b0 == b1);
|
||||
previous = b1;
|
||||
if (decoded/8 == buflen) break; /* No space left on target buffer. */
|
||||
if(decoded / 8 == buflen) break; /* No space left on target buffer. */
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
@@ -388,44 +409,49 @@ extern ProtoViewDecoder SchraderTPMSDecoder;
|
||||
extern ProtoViewDecoder CitroenTPMSDecoder;
|
||||
extern ProtoViewDecoder FordTPMSDecoder;
|
||||
|
||||
ProtoViewDecoder *Decoders[] = {
|
||||
&Oregon2Decoder, /* Oregon sensors v2.1 protocol. */
|
||||
&B4B1Decoder, /* PT, SC, ... 24 bits remotes. */
|
||||
&RenaultTPMSDecoder, /* Renault TPMS. */
|
||||
&ToyotaTPMSDecoder, /* Toyota TPMS. */
|
||||
&SchraderTPMSDecoder, /* Schrader TPMS. */
|
||||
&CitroenTPMSDecoder, /* Citroen TPMS. */
|
||||
&FordTPMSDecoder, /* Ford TPMS. */
|
||||
NULL
|
||||
};
|
||||
ProtoViewDecoder* Decoders[] = {
|
||||
&Oregon2Decoder, /* Oregon sensors v2.1 protocol. */
|
||||
&B4B1Decoder, /* PT, SC, ... 24 bits remotes. */
|
||||
&RenaultTPMSDecoder, /* Renault TPMS. */
|
||||
&ToyotaTPMSDecoder, /* Toyota TPMS. */
|
||||
&SchraderTPMSDecoder, /* Schrader TPMS. */
|
||||
&CitroenTPMSDecoder, /* Citroen TPMS. */
|
||||
&FordTPMSDecoder, /* Ford TPMS. */
|
||||
NULL};
|
||||
|
||||
/* Reset the message info structure before passing it to the decoding
|
||||
* functions. */
|
||||
void initialize_msg_info(ProtoViewMsgInfo *i) {
|
||||
memset(i,0,sizeof(ProtoViewMsgInfo));
|
||||
void initialize_msg_info(ProtoViewMsgInfo* i) {
|
||||
memset(i, 0, sizeof(ProtoViewMsgInfo));
|
||||
}
|
||||
|
||||
/* This function is called when a new signal is detected. It converts it
|
||||
* to a bitstream, and the calls the protocol specific functions for
|
||||
* decoding. If the signal was decoded correctly by some protocol, true
|
||||
* is returned. Otherwise false is returned. */
|
||||
bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
|
||||
uint32_t bitmap_bits_size = 4096*8;
|
||||
uint32_t bitmap_size = bitmap_bits_size/8;
|
||||
bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info) {
|
||||
uint32_t bitmap_bits_size = 4096 * 8;
|
||||
uint32_t bitmap_size = bitmap_bits_size / 8;
|
||||
|
||||
/* We call the decoders with an offset a few samples before the actual
|
||||
* signal detected and for a len of a few bits after its end. */
|
||||
uint32_t before_samples = 20;
|
||||
uint32_t after_samples = 100;
|
||||
|
||||
uint8_t *bitmap = malloc(bitmap_size);
|
||||
uint32_t bits = convert_signal_to_bits(bitmap,bitmap_size,s,-before_samples,len+before_samples+after_samples,s->short_pulse_dur);
|
||||
uint8_t* bitmap = malloc(bitmap_size);
|
||||
uint32_t bits = convert_signal_to_bits(
|
||||
bitmap,
|
||||
bitmap_size,
|
||||
s,
|
||||
-before_samples,
|
||||
len + before_samples + after_samples,
|
||||
s->short_pulse_dur);
|
||||
|
||||
if (DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */
|
||||
char *str = malloc(1024);
|
||||
if(DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */
|
||||
char* str = malloc(1024);
|
||||
uint32_t j;
|
||||
for (j = 0; j < bits && j < 1023; j++) {
|
||||
str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0';
|
||||
for(j = 0; j < bits && j < 1023; j++) {
|
||||
str[j] = bitmap_get(bitmap, bitmap_size, j) ? '1' : '0';
|
||||
}
|
||||
str[j] = 0;
|
||||
FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str);
|
||||
@@ -438,18 +464,25 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
|
||||
bool decoded = false;
|
||||
while(Decoders[j]) {
|
||||
uint32_t start_time = furi_get_tick();
|
||||
decoded = Decoders[j]->decode(bitmap,bitmap_size,bits,info);
|
||||
decoded = Decoders[j]->decode(bitmap, bitmap_size, bits, info);
|
||||
uint32_t delta = furi_get_tick() - start_time;
|
||||
FURI_LOG_E(TAG, "Decoder %s took %lu ms",
|
||||
Decoders[j]->name, (unsigned long)delta);
|
||||
if (decoded) break;
|
||||
FURI_LOG_E(TAG, "Decoder %s took %lu ms", Decoders[j]->name, (unsigned long)delta);
|
||||
if(decoded) break;
|
||||
j++;
|
||||
}
|
||||
|
||||
if (!decoded) {
|
||||
if(!decoded) {
|
||||
FURI_LOG_E(TAG, "No decoding possible");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Decoded %s, raw=%s info=[%s,%s,%s,%s]", info->name, info->raw, info->info1, info->info2, info->info3, info->info4);
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Decoded %s, raw=%s info=[%s,%s,%s,%s]",
|
||||
info->name,
|
||||
info->raw,
|
||||
info->info1,
|
||||
info->info2,
|
||||
info->info3,
|
||||
info->info4);
|
||||
}
|
||||
free(bitmap);
|
||||
return decoded;
|
||||
|
||||
@@ -3,28 +3,24 @@
|
||||
|
||||
#include "app.h"
|
||||
|
||||
void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color)
|
||||
{
|
||||
void canvas_draw_str_with_border(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
const char* str,
|
||||
Color text_color,
|
||||
Color border_color) {
|
||||
struct {
|
||||
uint8_t x; uint8_t y;
|
||||
} dir[8] = {
|
||||
{-1,-1},
|
||||
{0,-1},
|
||||
{1,-1},
|
||||
{1,0},
|
||||
{1,1},
|
||||
{0,1},
|
||||
{-1,1},
|
||||
{-1,0}
|
||||
};
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
} dir[8] = {{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}};
|
||||
|
||||
/* Rotate in all the directions writing the same string to create a
|
||||
* border, then write the actual string in the other color in the
|
||||
* middle. */
|
||||
canvas_set_color(canvas, border_color);
|
||||
for (int j = 0; j < 8; j++)
|
||||
canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str);
|
||||
for(int j = 0; j < 8; j++) canvas_draw_str(canvas, x + dir[j].x, y + dir[j].y, str);
|
||||
canvas_set_color(canvas, text_color);
|
||||
canvas_draw_str(canvas,x,y,str);
|
||||
canvas_draw_str(canvas, x, y, str);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
@@ -7,47 +7,46 @@
|
||||
|
||||
/* Read directly from the G0 CC1101 pin, and draw a black or white
|
||||
* dot depending on the level. */
|
||||
void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) {
|
||||
if (!app->direct_sampling_enabled) {
|
||||
void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) {
|
||||
if(!app->direct_sampling_enabled) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas,2,9,"Direct sampling is a special");
|
||||
canvas_draw_str(canvas,2,18,"mode that displays the signal");
|
||||
canvas_draw_str(canvas,2,27,"captured in real time. Like in");
|
||||
canvas_draw_str(canvas,2,36,"a old CRT TV. It's very slow.");
|
||||
canvas_draw_str(canvas,2,45,"Can crash your Flipper.");
|
||||
canvas_draw_str(canvas, 2, 9, "Direct sampling is a special");
|
||||
canvas_draw_str(canvas, 2, 18, "mode that displays the signal");
|
||||
canvas_draw_str(canvas, 2, 27, "captured in real time. Like in");
|
||||
canvas_draw_str(canvas, 2, 36, "a old CRT TV. It's very slow.");
|
||||
canvas_draw_str(canvas, 2, 45, "Can crash your Flipper.");
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas,14,60,"To enable press OK");
|
||||
canvas_draw_str(canvas, 14, 60, "To enable press OK");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int y = 0; y < 64; y++) {
|
||||
for (int x = 0; x < 128; x++) {
|
||||
for(int y = 0; y < 64; y++) {
|
||||
for(int x = 0; x < 128; x++) {
|
||||
bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
|
||||
if (level) canvas_draw_dot(canvas,x,y);
|
||||
if(level) canvas_draw_dot(canvas, x, y);
|
||||
/* Busy loop: this is a terrible approach as it blocks
|
||||
* everything else, but for now it's the best we can do
|
||||
* to obtain direct data with some spacing. */
|
||||
uint32_t x = 250; while(x--);
|
||||
uint32_t x = 250;
|
||||
while(x--)
|
||||
;
|
||||
}
|
||||
}
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_with_border(canvas,36,60,"Direct sampling",
|
||||
ColorWhite,ColorBlack);
|
||||
canvas_draw_str_with_border(canvas, 36, 60, "Direct sampling", ColorWhite, ColorBlack);
|
||||
}
|
||||
|
||||
/* Handle input */
|
||||
void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) {
|
||||
if (input.type == InputTypePress && input.key == InputKeyOk) {
|
||||
void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) {
|
||||
if(input.type == InputTypePress && input.key == InputKeyOk) {
|
||||
app->direct_sampling_enabled = !app->direct_sampling_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enter view. Stop the subghz thread to prevent access as we read
|
||||
* the CC1101 data directly. */
|
||||
void view_enter_direct_sampling(ProtoViewApp *app) {
|
||||
if (app->txrx->txrx_state == TxRxStateRx &&
|
||||
!app->txrx->debug_timer_sampling)
|
||||
{
|
||||
void view_enter_direct_sampling(ProtoViewApp* app) {
|
||||
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
|
||||
subghz_worker_stop(app->txrx->worker);
|
||||
} else {
|
||||
raw_sampling_worker_stop(app);
|
||||
@@ -55,10 +54,8 @@ void view_enter_direct_sampling(ProtoViewApp *app) {
|
||||
}
|
||||
|
||||
/* Exit view. Restore the subghz thread. */
|
||||
void view_exit_direct_sampling(ProtoViewApp *app) {
|
||||
if (app->txrx->txrx_state == TxRxStateRx &&
|
||||
!app->txrx->debug_timer_sampling)
|
||||
{
|
||||
void view_exit_direct_sampling(ProtoViewApp* app) {
|
||||
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
|
||||
subghz_worker_start(app->txrx->worker);
|
||||
} else {
|
||||
raw_sampling_worker_start(app);
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
#include "app.h"
|
||||
|
||||
/* Renders the view with the detected message information. */
|
||||
void render_view_info(Canvas *const canvas, ProtoViewApp *app) {
|
||||
if (app->signal_decoded == false) {
|
||||
void render_view_info(Canvas* const canvas, ProtoViewApp* app) {
|
||||
if(app->signal_decoded == false) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 30,36,"No signal decoded");
|
||||
canvas_draw_str(canvas, 30, 36, "No signal decoded");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -20,21 +20,25 @@ void render_view_info(Canvas *const canvas, ProtoViewApp *app) {
|
||||
/* Info fields. */
|
||||
char buf[128];
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if (app->signal_info.raw[0]) {
|
||||
snprintf(buf,sizeof(buf),"Raw: %s", app->signal_info.raw);
|
||||
if(app->signal_info.raw[0]) {
|
||||
snprintf(buf, sizeof(buf), "Raw: %s", app->signal_info.raw);
|
||||
canvas_draw_str(canvas, 0, y, buf);
|
||||
y += lineheight;
|
||||
}
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info1); y += lineheight;
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info2); y += lineheight;
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info3); y += lineheight;
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info4); y += lineheight;
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info1);
|
||||
y += lineheight;
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info2);
|
||||
y += lineheight;
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info3);
|
||||
y += lineheight;
|
||||
canvas_draw_str(canvas, 0, y, app->signal_info.info4);
|
||||
y += lineheight;
|
||||
}
|
||||
|
||||
/* Handle input for the info view. */
|
||||
void process_input_info(ProtoViewApp *app, InputEvent input) {
|
||||
if (input.type == InputTypeShort) {
|
||||
if (input.key == InputKeyOk) {
|
||||
void process_input_info(ProtoViewApp* app, InputEvent input) {
|
||||
if(input.type == InputTypeShort) {
|
||||
if(input.key == InputKeyOk) {
|
||||
/* Reset the current sample to capture the next. */
|
||||
reset_current_signal(app);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*
|
||||
* The 'idx' argument is the first sample to render in the circular
|
||||
* buffer. */
|
||||
void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *buf, uint32_t idx) {
|
||||
void render_signal(ProtoViewApp* app, Canvas* const canvas, RawSamplesBuffer* buf, uint32_t idx) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
int rows = 8;
|
||||
@@ -20,31 +20,29 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu
|
||||
uint32_t start_idx = idx;
|
||||
bool level = 0;
|
||||
uint32_t dur = 0, sample_num = 0;
|
||||
for (int row = 0; row < rows ; row++) {
|
||||
for (int x = 0; x < 128; x++) {
|
||||
int y = 3 + row*8;
|
||||
if (dur < time_per_pixel/2) {
|
||||
for(int row = 0; row < rows; row++) {
|
||||
for(int x = 0; x < 128; x++) {
|
||||
int y = 3 + row * 8;
|
||||
if(dur < time_per_pixel / 2) {
|
||||
/* Get more data. */
|
||||
raw_samples_get(buf, idx++, &level, &dur);
|
||||
sample_num++;
|
||||
}
|
||||
|
||||
canvas_draw_line(canvas, x,y,x,y-(level*3));
|
||||
canvas_draw_line(canvas, x, y, x, y - (level * 3));
|
||||
|
||||
/* Write a small triangle under the last sample detected. */
|
||||
if (app->signal_bestlen != 0 &&
|
||||
sample_num+start_idx == app->signal_bestlen+1)
|
||||
{
|
||||
canvas_draw_dot(canvas,x,y+2);
|
||||
canvas_draw_dot(canvas,x-1,y+3);
|
||||
canvas_draw_dot(canvas,x,y+3);
|
||||
canvas_draw_dot(canvas,x+1,y+3);
|
||||
if(app->signal_bestlen != 0 && sample_num + start_idx == app->signal_bestlen + 1) {
|
||||
canvas_draw_dot(canvas, x, y + 2);
|
||||
canvas_draw_dot(canvas, x - 1, y + 3);
|
||||
canvas_draw_dot(canvas, x, y + 3);
|
||||
canvas_draw_dot(canvas, x + 1, y + 3);
|
||||
sample_num++; /* Make sure we don't mark the next, too. */
|
||||
}
|
||||
|
||||
/* Remove from the current level duration the time we
|
||||
* just plot. */
|
||||
if (dur > time_per_pixel)
|
||||
if(dur > time_per_pixel)
|
||||
dur -= time_per_pixel;
|
||||
else
|
||||
dur = 0;
|
||||
@@ -53,45 +51,46 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu
|
||||
}
|
||||
|
||||
/* Raw pulses rendering. This is our default view. */
|
||||
void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app) {
|
||||
void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app) {
|
||||
/* Show signal. */
|
||||
render_signal(app, canvas, DetectedSamples, app->signal_offset);
|
||||
|
||||
/* Show signal information. */
|
||||
char buf[64];
|
||||
snprintf(buf,sizeof(buf),"%luus",
|
||||
(unsigned long)DetectedSamples->short_pulse_dur);
|
||||
snprintf(buf, sizeof(buf), "%luus", (unsigned long)DetectedSamples->short_pulse_dur);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_with_border(canvas, 97, 63, buf, ColorWhite, ColorBlack);
|
||||
if (app->signal_decoded) {
|
||||
if(app->signal_decoded) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_with_border(canvas, 1, 61, app->signal_info.name, ColorWhite, ColorBlack);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle input for the raw pulses view. */
|
||||
void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) {
|
||||
if (input.type == InputTypeRepeat) {
|
||||
void process_input_raw_pulses(ProtoViewApp* app, InputEvent input) {
|
||||
if(input.type == InputTypeRepeat) {
|
||||
/* Handle panning of the signal window. Long pressing
|
||||
* right will show successive samples, long pressing left
|
||||
* previous samples. */
|
||||
if (input.key == InputKeyRight) app->signal_offset++;
|
||||
else if (input.key == InputKeyLeft) app->signal_offset--;
|
||||
else if (input.key == InputKeyOk) {
|
||||
if(input.key == InputKeyRight)
|
||||
app->signal_offset++;
|
||||
else if(input.key == InputKeyLeft)
|
||||
app->signal_offset--;
|
||||
else if(input.key == InputKeyOk) {
|
||||
app->signal_offset = 0;
|
||||
app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
|
||||
}
|
||||
} else if (input.type == InputTypeShort) {
|
||||
if (input.key == InputKeyOk) {
|
||||
} else if(input.type == InputTypeShort) {
|
||||
if(input.key == InputKeyOk) {
|
||||
/* Reset the current sample to capture the next. */
|
||||
reset_current_signal(app);
|
||||
} else if (input.key == InputKeyDown) {
|
||||
} else if(input.key == InputKeyDown) {
|
||||
/* Rescaling. The set becomes finer under 50us per pixel. */
|
||||
uint32_t scale_step = app->us_scale >= 50 ? 50 : 10;
|
||||
if (app->us_scale < 500) app->us_scale += scale_step;
|
||||
} else if (input.key == InputKeyUp) {
|
||||
if(app->us_scale < 500) app->us_scale += scale_step;
|
||||
} else if(input.key == InputKeyUp) {
|
||||
uint32_t scale_step = app->us_scale > 50 ? 50 : 10;
|
||||
if (app->us_scale > 10) app->us_scale -= scale_step;
|
||||
if(app->us_scale > 10) app->us_scale -= scale_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,30 +6,30 @@
|
||||
/* Renders a single view with frequency and modulation setting. However
|
||||
* this are logically two different views, and only one of the settings
|
||||
* will be highlighted. */
|
||||
void render_view_settings(Canvas *const canvas, ProtoViewApp *app) {
|
||||
void render_view_settings(Canvas* const canvas, ProtoViewApp* app) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
if (app->current_view == ViewFrequencySettings)
|
||||
canvas_draw_str_with_border(canvas,1,10,"Frequency",ColorWhite,ColorBlack);
|
||||
if(app->current_view == ViewFrequencySettings)
|
||||
canvas_draw_str_with_border(canvas, 1, 10, "Frequency", ColorWhite, ColorBlack);
|
||||
else
|
||||
canvas_draw_str(canvas,1,10,"Frequency");
|
||||
canvas_draw_str(canvas, 1, 10, "Frequency");
|
||||
|
||||
if (app->current_view == ViewModulationSettings)
|
||||
canvas_draw_str_with_border(canvas,70,10,"Modulation",ColorWhite,ColorBlack);
|
||||
if(app->current_view == ViewModulationSettings)
|
||||
canvas_draw_str_with_border(canvas, 70, 10, "Modulation", ColorWhite, ColorBlack);
|
||||
else
|
||||
canvas_draw_str(canvas,70,10,"Modulation");
|
||||
canvas_draw_str(canvas, 70, 10, "Modulation");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas,10,61,"Use up and down to modify");
|
||||
canvas_draw_str(canvas, 10, 61, "Use up and down to modify");
|
||||
|
||||
if (app->txrx->debug_timer_sampling)
|
||||
canvas_draw_str(canvas,3,52,"(DEBUG timer sampling is ON)");
|
||||
if(app->txrx->debug_timer_sampling)
|
||||
canvas_draw_str(canvas, 3, 52, "(DEBUG timer sampling is ON)");
|
||||
|
||||
/* Show frequency. We can use big numbers font since it's just a number. */
|
||||
if (app->current_view == ViewFrequencySettings) {
|
||||
if(app->current_view == ViewFrequencySettings) {
|
||||
char buf[16];
|
||||
snprintf(buf,sizeof(buf),"%.2f",(double)app->frequency/1000000);
|
||||
snprintf(buf, sizeof(buf), "%.2f", (double)app->frequency / 1000000);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str(canvas, 30, 40, buf);
|
||||
} else if (app->current_view == ViewModulationSettings) {
|
||||
} else if(app->current_view == ViewModulationSettings) {
|
||||
int current = app->modulation;
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 33, 39, ProtoViewModulations[current].name);
|
||||
@@ -37,13 +37,13 @@ void render_view_settings(Canvas *const canvas, ProtoViewApp *app) {
|
||||
}
|
||||
|
||||
/* Handle input for the settings view. */
|
||||
void process_input_settings(ProtoViewApp *app, InputEvent input) {
|
||||
if (input.type == InputTypeLong && input.key == InputKeyOk) {
|
||||
void process_input_settings(ProtoViewApp* app, InputEvent input) {
|
||||
if(input.type == InputTypeLong && input.key == InputKeyOk) {
|
||||
/* Long pressing to OK sets the default frequency and
|
||||
* modulation. */
|
||||
app->frequency = subghz_setting_get_default_frequency(app->setting);
|
||||
app->modulation = 0;
|
||||
} else if (0 && input.type == InputTypeLong && input.key == InputKeyDown) {
|
||||
} else if(0 && input.type == InputTypeLong && input.key == InputKeyDown) {
|
||||
/* Long pressing to down switches between normal and debug
|
||||
* timer sampling mode. NOTE: this feature is disabled for users,
|
||||
* only useful for devs (if useful at all). */
|
||||
@@ -55,42 +55,40 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) {
|
||||
app->txrx->debug_timer_sampling = !app->txrx->debug_timer_sampling;
|
||||
radio_begin(app);
|
||||
radio_rx(app);
|
||||
} else if (input.type == InputTypePress &&
|
||||
(input.key != InputKeyDown || input.key != InputKeyUp))
|
||||
{
|
||||
} else if(input.type == InputTypePress && (input.key != InputKeyDown || input.key != InputKeyUp)) {
|
||||
/* Handle up and down to change frequency or modulation. */
|
||||
if (app->current_view == ViewFrequencySettings) {
|
||||
if(app->current_view == ViewFrequencySettings) {
|
||||
size_t curidx = 0, i;
|
||||
size_t count = subghz_setting_get_frequency_count(app->setting);
|
||||
|
||||
/* Scan the list of frequencies to check for the index of the
|
||||
* currently set frequency. */
|
||||
for(i = 0; i < count; i++) {
|
||||
uint32_t freq = subghz_setting_get_frequency(app->setting,i);
|
||||
if (freq == app->frequency) {
|
||||
uint32_t freq = subghz_setting_get_frequency(app->setting, i);
|
||||
if(freq == app->frequency) {
|
||||
curidx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == count) return; /* Should never happen. */
|
||||
if(i == count) return; /* Should never happen. */
|
||||
|
||||
if (input.key == InputKeyUp) {
|
||||
curidx = curidx == 0 ? count-1 : curidx-1;
|
||||
} else if (input.key == InputKeyDown) {
|
||||
curidx = (curidx+1) % count;
|
||||
if(input.key == InputKeyUp) {
|
||||
curidx = curidx == 0 ? count - 1 : curidx - 1;
|
||||
} else if(input.key == InputKeyDown) {
|
||||
curidx = (curidx + 1) % count;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
app->frequency = subghz_setting_get_frequency(app->setting,curidx);
|
||||
} else if (app->current_view == ViewModulationSettings) {
|
||||
app->frequency = subghz_setting_get_frequency(app->setting, curidx);
|
||||
} else if(app->current_view == ViewModulationSettings) {
|
||||
uint32_t count = 0;
|
||||
uint32_t modid = app->modulation;
|
||||
|
||||
while(ProtoViewModulations[count].name != NULL) count++;
|
||||
if (input.key == InputKeyUp) {
|
||||
modid = modid == 0 ? count-1 : modid-1;
|
||||
} else if (input.key == InputKeyDown) {
|
||||
modid = (modid+1) % count;
|
||||
if(input.key == InputKeyUp) {
|
||||
modid = modid == 0 ? count - 1 : modid - 1;
|
||||
} else if(input.key == InputKeyDown) {
|
||||
modid = (modid + 1) % count;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -106,9 +104,13 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) {
|
||||
|
||||
/* When the user switches to some other view, if they changed the parameters
|
||||
* we need to restart the radio with the right frequency and modulation. */
|
||||
void view_exit_settings(ProtoViewApp *app) {
|
||||
if (app->txrx->freq_mod_changed) {
|
||||
FURI_LOG_E(TAG, "Setting view, setting frequency/modulation to %lu %s", app->frequency, ProtoViewModulations[app->modulation].name);
|
||||
void view_exit_settings(ProtoViewApp* app) {
|
||||
if(app->txrx->freq_mod_changed) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Setting view, setting frequency/modulation to %lu %s",
|
||||
app->frequency,
|
||||
ProtoViewModulations[app->modulation].name);
|
||||
radio_rx_end(app);
|
||||
radio_begin(app);
|
||||
radio_rx(app);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,6 @@
|
||||
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __QRCODE_H_
|
||||
#define __QRCODE_H_
|
||||
|
||||
@@ -46,55 +45,56 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// QR Code Format Encoding
|
||||
#define MODE_NUMERIC 0
|
||||
#define MODE_ALPHANUMERIC 1
|
||||
#define MODE_BYTE 2
|
||||
|
||||
#define MODE_NUMERIC 0
|
||||
#define MODE_ALPHANUMERIC 1
|
||||
#define MODE_BYTE 2
|
||||
|
||||
// Error Correction Code Levels
|
||||
#define ECC_LOW 0
|
||||
#define ECC_MEDIUM 1
|
||||
#define ECC_QUARTILE 2
|
||||
#define ECC_HIGH 3
|
||||
|
||||
#define ECC_LOW 0
|
||||
#define ECC_MEDIUM 1
|
||||
#define ECC_QUARTILE 2
|
||||
#define ECC_HIGH 3
|
||||
|
||||
// If set to non-zero, this library can ONLY produce QR codes at that version
|
||||
// This saves a lot of dynamic memory, as the codeword tables are skipped
|
||||
#ifndef LOCK_VERSION
|
||||
#define LOCK_VERSION 0
|
||||
#define LOCK_VERSION 0
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct QRCode {
|
||||
uint8_t version;
|
||||
uint8_t size;
|
||||
uint8_t ecc;
|
||||
uint8_t mode;
|
||||
uint8_t mask;
|
||||
uint8_t *modules;
|
||||
uint8_t* modules;
|
||||
} QRCode;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
uint16_t qrcode_getBufferSize(uint8_t version);
|
||||
|
||||
int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data);
|
||||
int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length);
|
||||
|
||||
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y);
|
||||
|
||||
int8_t qrcode_initText(
|
||||
QRCode* qrcode,
|
||||
uint8_t* modules,
|
||||
uint8_t version,
|
||||
uint8_t ecc,
|
||||
const char* data);
|
||||
int8_t qrcode_initBytes(
|
||||
QRCode* qrcode,
|
||||
uint8_t* modules,
|
||||
uint8_t version,
|
||||
uint8_t ecc,
|
||||
uint8_t* data,
|
||||
uint16_t length);
|
||||
|
||||
bool qrcode_getModule(QRCode* qrcode, uint8_t x, uint8_t y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* __QRCODE_H_ */
|
||||
#endif /* __QRCODE_H_ */
|
||||
|
||||
@@ -28,22 +28,22 @@ static const uint16_t MAX_LENGTH[3][4][MAX_QRCODE_VERSION] = {
|
||||
// Numeric
|
||||
{41, 77, 127, 187, 255, 322, 370, 461, 552, 652, 772}, // Low
|
||||
{34, 63, 101, 149, 202, 255, 293, 365, 432, 513, 604}, // Medium
|
||||
{27, 48, 77, 111, 144, 178, 207, 259, 312, 364, 427}, // Quartile
|
||||
{17, 34, 58, 82, 106, 139, 154, 202, 235, 288, 331}, // High
|
||||
{27, 48, 77, 111, 144, 178, 207, 259, 312, 364, 427}, // Quartile
|
||||
{17, 34, 58, 82, 106, 139, 154, 202, 235, 288, 331}, // High
|
||||
},
|
||||
{
|
||||
// Alphanumeric
|
||||
{25, 47, 77, 114, 154, 195, 224, 279, 335, 395, 468}, // Low
|
||||
{20, 38, 61, 90, 122, 154, 178, 221, 262, 311, 366}, // Medium
|
||||
{16, 29, 47, 67, 87, 108, 125, 157, 189, 221, 259}, // Quartile
|
||||
{10, 20, 35, 50, 64, 84, 93, 122, 143, 174, 200}, // High
|
||||
{20, 38, 61, 90, 122, 154, 178, 221, 262, 311, 366}, // Medium
|
||||
{16, 29, 47, 67, 87, 108, 125, 157, 189, 221, 259}, // Quartile
|
||||
{10, 20, 35, 50, 64, 84, 93, 122, 143, 174, 200}, // High
|
||||
},
|
||||
{
|
||||
// Binary
|
||||
{17, 32, 53, 78, 106, 134, 154, 192, 230, 271, 321}, // Low
|
||||
{14, 26, 42, 62, 84, 106, 122, 152, 180, 213, 251}, // Medium
|
||||
{11, 20, 32, 46, 60, 74, 86, 108, 130, 151, 177}, // Quartile
|
||||
{7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137}, // High
|
||||
{14, 26, 42, 62, 84, 106, 122, 152, 180, 213, 251}, // Medium
|
||||
{11, 20, 32, 46, 60, 74, 86, 108, 130, 151, 177}, // Quartile
|
||||
{7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137}, // High
|
||||
},
|
||||
};
|
||||
|
||||
@@ -72,12 +72,17 @@ typedef struct {
|
||||
* @returns a character corresponding to the ecc level
|
||||
*/
|
||||
static char get_ecc_char(uint8_t ecc) {
|
||||
switch (ecc) {
|
||||
case 0: return 'L';
|
||||
case 1: return 'M';
|
||||
case 2: return 'Q';
|
||||
case 3: return 'H';
|
||||
default: return '?';
|
||||
switch(ecc) {
|
||||
case 0:
|
||||
return 'L';
|
||||
case 1:
|
||||
return 'M';
|
||||
case 2:
|
||||
return 'Q';
|
||||
case 3:
|
||||
return 'H';
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +91,17 @@ static char get_ecc_char(uint8_t ecc) {
|
||||
* @returns a character corresponding to the mode
|
||||
*/
|
||||
static char get_mode_char(uint8_t mode) {
|
||||
switch (mode) {
|
||||
case 0: return 'N';
|
||||
case 1: return 'A';
|
||||
case 2: return 'B';
|
||||
case 3: return 'K';
|
||||
default: return '?';
|
||||
switch(mode) {
|
||||
case 0:
|
||||
return 'N';
|
||||
case 1:
|
||||
return 'A';
|
||||
case 2:
|
||||
return 'B';
|
||||
case 3:
|
||||
return 'K';
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,68 +124,108 @@ static void render_callback(Canvas* canvas, void* ctx) {
|
||||
uint8_t font_height = canvas_current_font_height(canvas);
|
||||
uint8_t width = canvas_width(canvas);
|
||||
uint8_t height = canvas_height(canvas);
|
||||
if (instance->loading) {
|
||||
canvas_draw_str_aligned(canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading...");
|
||||
} else if (instance->qrcode) {
|
||||
if(instance->loading) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading...");
|
||||
} else if(instance->qrcode) {
|
||||
uint8_t size = instance->qrcode->size;
|
||||
uint8_t pixel_size = height / size;
|
||||
uint8_t top = (height - pixel_size * size) / 2;
|
||||
uint8_t left = ((instance->show_stats ? 65 : width) - pixel_size * size) / 2;
|
||||
for (uint8_t y = 0; y < size; y++) {
|
||||
for (uint8_t x = 0; x < size; x++) {
|
||||
if (qrcode_getModule(instance->qrcode, x, y)) {
|
||||
if (pixel_size == 1) {
|
||||
for(uint8_t y = 0; y < size; y++) {
|
||||
for(uint8_t x = 0; x < size; x++) {
|
||||
if(qrcode_getModule(instance->qrcode, x, y)) {
|
||||
if(pixel_size == 1) {
|
||||
canvas_draw_dot(canvas, left + x * pixel_size, top + y * pixel_size);
|
||||
} else {
|
||||
canvas_draw_box(canvas, left + x * pixel_size, top + y * pixel_size, pixel_size, pixel_size);
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
left + x * pixel_size,
|
||||
top + y * pixel_size,
|
||||
pixel_size,
|
||||
pixel_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instance->show_stats) {
|
||||
if(instance->show_stats) {
|
||||
top = 10;
|
||||
left = 66;
|
||||
|
||||
FuriString* str = furi_string_alloc();
|
||||
|
||||
if (!instance->edit || instance->selected_idx == 0) {
|
||||
if(!instance->edit || instance->selected_idx == 0) {
|
||||
furi_string_printf(str, "Ver: %i", instance->set_version);
|
||||
canvas_draw_str(canvas, left + 5, top + font_height, furi_string_get_cstr(str));
|
||||
if (instance->selected_idx == 0) {
|
||||
canvas_draw_triangle(canvas, left, top + font_height / 2, font_height - 4, 4, CanvasDirectionLeftToRight);
|
||||
if(instance->selected_idx == 0) {
|
||||
canvas_draw_triangle(
|
||||
canvas,
|
||||
left,
|
||||
top + font_height / 2,
|
||||
font_height - 4,
|
||||
4,
|
||||
CanvasDirectionLeftToRight);
|
||||
}
|
||||
if (instance->edit) {
|
||||
if(instance->edit) {
|
||||
uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "Ver: 8") / 2;
|
||||
canvas_draw_triangle(canvas, arrow_left, top, font_height - 4, 4, CanvasDirectionBottomToTop);
|
||||
canvas_draw_triangle(canvas, arrow_left, top + font_height + 1, font_height - 4, 4, CanvasDirectionTopToBottom);
|
||||
canvas_draw_triangle(
|
||||
canvas, arrow_left, top, font_height - 4, 4, CanvasDirectionBottomToTop);
|
||||
canvas_draw_triangle(
|
||||
canvas,
|
||||
arrow_left,
|
||||
top + font_height + 1,
|
||||
font_height - 4,
|
||||
4,
|
||||
CanvasDirectionTopToBottom);
|
||||
}
|
||||
}
|
||||
|
||||
if (!instance->edit || instance->selected_idx == 1) {
|
||||
if(!instance->edit || instance->selected_idx == 1) {
|
||||
furi_string_printf(str, "ECC: %c", get_ecc_char(instance->set_ecc));
|
||||
canvas_draw_str(canvas, left + 5, 2 * font_height + top + 2, furi_string_get_cstr(str));
|
||||
if (instance->selected_idx == 1) {
|
||||
canvas_draw_triangle(canvas, left, 3 * font_height / 2 + top + 2, font_height - 4, 4, CanvasDirectionLeftToRight);
|
||||
canvas_draw_str(
|
||||
canvas, left + 5, 2 * font_height + top + 2, furi_string_get_cstr(str));
|
||||
if(instance->selected_idx == 1) {
|
||||
canvas_draw_triangle(
|
||||
canvas,
|
||||
left,
|
||||
3 * font_height / 2 + top + 2,
|
||||
font_height - 4,
|
||||
4,
|
||||
CanvasDirectionLeftToRight);
|
||||
}
|
||||
if (instance->edit) {
|
||||
if(instance->edit) {
|
||||
uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "ECC: H") / 2;
|
||||
canvas_draw_triangle(canvas, arrow_left, font_height + top + 2, font_height - 4, 4, CanvasDirectionBottomToTop);
|
||||
canvas_draw_triangle(canvas, arrow_left, 2 * font_height + top + 3, font_height - 4, 4, CanvasDirectionTopToBottom);
|
||||
canvas_draw_triangle(
|
||||
canvas,
|
||||
arrow_left,
|
||||
font_height + top + 2,
|
||||
font_height - 4,
|
||||
4,
|
||||
CanvasDirectionBottomToTop);
|
||||
canvas_draw_triangle(
|
||||
canvas,
|
||||
arrow_left,
|
||||
2 * font_height + top + 3,
|
||||
font_height - 4,
|
||||
4,
|
||||
CanvasDirectionTopToBottom);
|
||||
}
|
||||
}
|
||||
|
||||
if (!instance->edit) {
|
||||
if(!instance->edit) {
|
||||
furi_string_printf(str, "Mod: %c", get_mode_char(instance->qrcode->mode));
|
||||
canvas_draw_str(canvas, left + 5, 3 * font_height + top + 4, furi_string_get_cstr(str));
|
||||
canvas_draw_str(
|
||||
canvas, left + 5, 3 * font_height + top + 4, furi_string_get_cstr(str));
|
||||
}
|
||||
|
||||
furi_string_free(str);
|
||||
}
|
||||
} else {
|
||||
uint8_t margin = (height - font_height * 2) / 3;
|
||||
canvas_draw_str_aligned(canvas, width / 2, margin, AlignCenter, AlignTop, "Could not load qrcode.");
|
||||
if (instance->too_long) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, width / 2, margin, AlignCenter, AlignTop, "Could not load qrcode.");
|
||||
if(instance->too_long) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, width / 2, margin * 2 + font_height, "Message is too long.");
|
||||
}
|
||||
@@ -192,7 +242,7 @@ static void render_callback(Canvas* canvas, void* ctx) {
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(input_event);
|
||||
furi_assert(ctx);
|
||||
if (input_event->type == InputTypeShort) {
|
||||
if(input_event->type == InputTypeShort) {
|
||||
QRCodeApp* instance = ctx;
|
||||
furi_message_queue_put(instance->input_queue, input_event, 0);
|
||||
}
|
||||
@@ -205,9 +255,9 @@ static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
*/
|
||||
static bool is_numeric(const char* str, uint16_t len) {
|
||||
furi_assert(str);
|
||||
while (len > 0) {
|
||||
while(len > 0) {
|
||||
char c = str[--len];
|
||||
if (c < '0' || c > '9') return false;
|
||||
if(c < '0' || c > '9') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -219,19 +269,12 @@ static bool is_numeric(const char* str, uint16_t len) {
|
||||
*/
|
||||
static bool is_alphanumeric(const char* str, uint16_t len) {
|
||||
furi_assert(str);
|
||||
while (len > 0) {
|
||||
while(len > 0) {
|
||||
char c = str[--len];
|
||||
if (c >= '0' && c <= '9') continue;
|
||||
if (c >= 'A' && c <= 'Z') continue;
|
||||
if (c == ' '
|
||||
|| c == '$'
|
||||
|| c == '%'
|
||||
|| c == '*'
|
||||
|| c == '+'
|
||||
|| c == '-'
|
||||
|| c == '.'
|
||||
|| c == '/'
|
||||
|| c == ':')
|
||||
if(c >= '0' && c <= '9') continue;
|
||||
if(c >= 'A' && c <= 'Z') continue;
|
||||
if(c == ' ' || c == '$' || c == '%' || c == '*' || c == '+' || c == '-' || c == '.' ||
|
||||
c == '/' || c == ':')
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
@@ -277,8 +320,9 @@ static bool rebuild_qrcode(QRCodeApp* instance, uint8_t version, uint8_t ecc) {
|
||||
uint16_t len = strlen(cstr);
|
||||
instance->qrcode = qrcode_alloc(version);
|
||||
|
||||
int8_t res = qrcode_initBytes(instance->qrcode, instance->qrcode->modules, version, ecc, (uint8_t*)cstr, len);
|
||||
if (res != 0) {
|
||||
int8_t res = qrcode_initBytes(
|
||||
instance->qrcode, instance->qrcode->modules, version, ecc, (uint8_t*)cstr, len);
|
||||
if(res != 0) {
|
||||
FURI_LOG_E(TAG, "Could not create qrcode");
|
||||
|
||||
qrcode_free(instance->qrcode);
|
||||
@@ -300,11 +344,11 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
|
||||
furi_assert(str);
|
||||
|
||||
furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
if (instance->message) {
|
||||
if(instance->message) {
|
||||
furi_string_free(instance->message);
|
||||
instance->message = NULL;
|
||||
}
|
||||
if (instance->qrcode) {
|
||||
if(instance->qrcode) {
|
||||
qrcode_free(instance->qrcode);
|
||||
instance->qrcode = NULL;
|
||||
}
|
||||
@@ -319,15 +363,17 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
|
||||
uint16_t len = strlen(cstr);
|
||||
|
||||
instance->message = furi_string_alloc_set(str);
|
||||
if (!instance->message) {
|
||||
if(!instance->message) {
|
||||
FURI_LOG_E(TAG, "Could not allocate message");
|
||||
break;
|
||||
}
|
||||
|
||||
// figure out the qrcode "mode"
|
||||
uint8_t mode = MODE_BYTE;
|
||||
if (is_numeric(cstr, len)) mode = MODE_NUMERIC;
|
||||
else if (is_alphanumeric(cstr, len)) mode = MODE_ALPHANUMERIC;
|
||||
if(is_numeric(cstr, len))
|
||||
mode = MODE_NUMERIC;
|
||||
else if(is_alphanumeric(cstr, len))
|
||||
mode = MODE_ALPHANUMERIC;
|
||||
|
||||
// Figure out the smallest qrcode version that'll fit all of the data -
|
||||
// we prefer the smallest version to maximize the pixel size of each
|
||||
@@ -336,11 +382,11 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
|
||||
// number, so we'll add one later.
|
||||
uint8_t ecc = ECC_LOW;
|
||||
uint8_t version = 0;
|
||||
while (version < MAX_QRCODE_VERSION && MAX_LENGTH[mode][ecc][version] < len) {
|
||||
while(version < MAX_QRCODE_VERSION && MAX_LENGTH[mode][ecc][version] < len) {
|
||||
version++;
|
||||
}
|
||||
|
||||
if (version == MAX_QRCODE_VERSION) {
|
||||
if(version == MAX_QRCODE_VERSION) {
|
||||
instance->too_long = true;
|
||||
break;
|
||||
}
|
||||
@@ -350,13 +396,13 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
|
||||
// above that ECC_LOW (0) works... don't forget to add one to that
|
||||
// version number...
|
||||
ecc = ECC_HIGH;
|
||||
while (MAX_LENGTH[mode][ecc][version] < len) {
|
||||
while(MAX_LENGTH[mode][ecc][version] < len) {
|
||||
ecc--;
|
||||
}
|
||||
version++;
|
||||
|
||||
// Build the qrcode
|
||||
if (!rebuild_qrcode(instance, version, ecc)) {
|
||||
if(!rebuild_qrcode(instance, version, ecc)) {
|
||||
furi_string_free(instance->message);
|
||||
instance->message = NULL;
|
||||
break;
|
||||
@@ -365,7 +411,7 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
|
||||
instance->min_version = instance->set_version = version;
|
||||
instance->max_ecc_at_min_version = instance->set_ecc = ecc;
|
||||
result = true;
|
||||
} while (false);
|
||||
} while(false);
|
||||
|
||||
instance->loading = false;
|
||||
|
||||
@@ -391,27 +437,26 @@ static bool qrcode_load_file(QRCodeApp* instance, const char* file_path) {
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if (!flipper_format_file_open_existing(file, file_path)) break;
|
||||
if(!flipper_format_file_open_existing(file, file_path)) break;
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!flipper_format_read_header(file, temp_str, &version)) break;
|
||||
if (furi_string_cmp_str(temp_str, QRCODE_FILETYPE)
|
||||
|| version != QRCODE_FILE_VERSION) {
|
||||
if(!flipper_format_read_header(file, temp_str, &version)) break;
|
||||
if(furi_string_cmp_str(temp_str, QRCODE_FILETYPE) || version != QRCODE_FILE_VERSION) {
|
||||
FURI_LOG_E(TAG, "Incorrect file format or version");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!flipper_format_read_string(file, "Message", temp_str)) {
|
||||
if(!flipper_format_read_string(file, "Message", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Message is missing");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!qrcode_load_string(instance, temp_str)) {
|
||||
if(!qrcode_load_string(instance, temp_str)) {
|
||||
break;
|
||||
}
|
||||
|
||||
result = true;
|
||||
} while (false);
|
||||
} while(false);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
flipper_format_free(file);
|
||||
@@ -454,8 +499,8 @@ static QRCodeApp* qrcode_app_alloc() {
|
||||
* @param qrcode_app The app to free
|
||||
*/
|
||||
static void qrcode_app_free(QRCodeApp* instance) {
|
||||
if (instance->message) furi_string_free(instance->message);
|
||||
if (instance->qrcode) qrcode_free(instance->qrcode);
|
||||
if(instance->message) furi_string_free(instance->message);
|
||||
if(instance->qrcode) qrcode_free(instance->qrcode);
|
||||
|
||||
gui_remove_view_port(instance->gui, instance->view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
@@ -475,14 +520,14 @@ int32_t qrcode_app(void* p) {
|
||||
FuriString* file_path = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if (p && strlen(p)) {
|
||||
if(p && strlen(p)) {
|
||||
furi_string_set(file_path, (const char*)p);
|
||||
} else {
|
||||
furi_string_set(file_path, QRCODE_FOLDER);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, QRCODE_EXTENSION, &I_qrcode_10px);
|
||||
&browser_options, QRCODE_EXTENSION, &I_qrcode_10px);
|
||||
browser_options.hide_ext = true;
|
||||
browser_options.base_path = QRCODE_FOLDER;
|
||||
|
||||
@@ -490,26 +535,27 @@ int32_t qrcode_app(void* p) {
|
||||
bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
if (!res) {
|
||||
if(!res) {
|
||||
FURI_LOG_E(TAG, "No file selected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!qrcode_load_file(instance, furi_string_get_cstr(file_path))) {
|
||||
if(!qrcode_load_file(instance, furi_string_get_cstr(file_path))) {
|
||||
FURI_LOG_E(TAG, "Unable to load file");
|
||||
}
|
||||
|
||||
InputEvent input;
|
||||
while (furi_message_queue_get(instance->input_queue, &input, FuriWaitForever) == FuriStatusOk) {
|
||||
while(furi_message_queue_get(instance->input_queue, &input, FuriWaitForever) ==
|
||||
FuriStatusOk) {
|
||||
furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
if (input.key == InputKeyBack) {
|
||||
if (instance->message) {
|
||||
if(input.key == InputKeyBack) {
|
||||
if(instance->message) {
|
||||
furi_string_free(instance->message);
|
||||
instance->message = NULL;
|
||||
}
|
||||
if (instance->qrcode) {
|
||||
if(instance->qrcode) {
|
||||
qrcode_free(instance->qrcode);
|
||||
instance->qrcode = NULL;
|
||||
}
|
||||
@@ -517,43 +563,49 @@ int32_t qrcode_app(void* p) {
|
||||
instance->edit = false;
|
||||
furi_mutex_release(instance->mutex);
|
||||
break;
|
||||
} else if (input.key == InputKeyRight) {
|
||||
} else if(input.key == InputKeyRight) {
|
||||
instance->show_stats = true;
|
||||
} else if (input.key == InputKeyLeft) {
|
||||
} else if(input.key == InputKeyLeft) {
|
||||
instance->show_stats = false;
|
||||
} else if (instance->show_stats && !instance->loading && instance->qrcode) {
|
||||
if (input.key == InputKeyUp) {
|
||||
if (!instance->edit) {
|
||||
} else if(instance->show_stats && !instance->loading && instance->qrcode) {
|
||||
if(input.key == InputKeyUp) {
|
||||
if(!instance->edit) {
|
||||
instance->selected_idx = MAX(0, instance->selected_idx - 1);
|
||||
} else {
|
||||
if (instance->selected_idx == 0 && instance->set_version < MAX_QRCODE_VERSION) {
|
||||
if(instance->selected_idx == 0 &&
|
||||
instance->set_version < MAX_QRCODE_VERSION) {
|
||||
instance->set_version++;
|
||||
} else if (instance->selected_idx == 1) {
|
||||
uint8_t max_ecc = instance->set_version == instance->min_version ? instance->max_ecc_at_min_version : ECC_HIGH;
|
||||
if (instance->set_ecc < max_ecc) {
|
||||
} else if(instance->selected_idx == 1) {
|
||||
uint8_t max_ecc = instance->set_version == instance->min_version ?
|
||||
instance->max_ecc_at_min_version :
|
||||
ECC_HIGH;
|
||||
if(instance->set_ecc < max_ecc) {
|
||||
instance->set_ecc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (input.key == InputKeyDown) {
|
||||
if (!instance->edit) {
|
||||
} else if(input.key == InputKeyDown) {
|
||||
if(!instance->edit) {
|
||||
instance->selected_idx = MIN(1, instance->selected_idx + 1);
|
||||
} else {
|
||||
if (instance->selected_idx == 0 && instance->set_version > instance->min_version) {
|
||||
if(instance->selected_idx == 0 &&
|
||||
instance->set_version > instance->min_version) {
|
||||
instance->set_version--;
|
||||
if (instance->set_version == instance->min_version) {
|
||||
instance->set_ecc = MAX(instance->set_ecc, instance->max_ecc_at_min_version);
|
||||
if(instance->set_version == instance->min_version) {
|
||||
instance->set_ecc =
|
||||
MAX(instance->set_ecc, instance->max_ecc_at_min_version);
|
||||
}
|
||||
} else if (instance->selected_idx == 1 && instance->set_ecc > 0) {
|
||||
} else if(instance->selected_idx == 1 && instance->set_ecc > 0) {
|
||||
instance->set_ecc--;
|
||||
}
|
||||
}
|
||||
} else if (input.key == InputKeyOk) {
|
||||
if (instance->edit && (instance->set_version != instance->qrcode->version || instance->set_ecc != instance->qrcode->ecc)) {
|
||||
} else if(input.key == InputKeyOk) {
|
||||
if(instance->edit && (instance->set_version != instance->qrcode->version ||
|
||||
instance->set_ecc != instance->qrcode->ecc)) {
|
||||
QRCode* qrcode = instance->qrcode;
|
||||
instance->loading = true;
|
||||
|
||||
if (rebuild_qrcode(instance, instance->set_version, instance->set_ecc)) {
|
||||
if(rebuild_qrcode(instance, instance->set_version, instance->set_ecc)) {
|
||||
qrcode_free(qrcode);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Could not rebuild qrcode");
|
||||
@@ -572,12 +624,12 @@ int32_t qrcode_app(void* p) {
|
||||
view_port_update(instance->view_port);
|
||||
}
|
||||
|
||||
if (p && strlen(p)) {
|
||||
if(p && strlen(p)) {
|
||||
// if started with an arg, exit instead
|
||||
// of looping back to the browser
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
} while(true);
|
||||
|
||||
furi_string_free(file_path);
|
||||
qrcode_app_free(instance);
|
||||
|
||||
@@ -255,8 +255,7 @@ bool place_on_top(Card* where, Card what) {
|
||||
int8_t b_letter = (int8_t)what.character;
|
||||
if(a_letter == 12) a_letter = -1;
|
||||
if(b_letter == 12) b_letter = -1;
|
||||
if(where->disabled && b_letter!=-1)
|
||||
return false;
|
||||
if(where->disabled && b_letter != -1) return false;
|
||||
if((a_letter + 1) == b_letter) {
|
||||
where->disabled = what.disabled;
|
||||
where->pip = what.pip;
|
||||
|
||||
@@ -172,10 +172,9 @@ static void _draw_singleSensor(Canvas* canvas, Sensor* sensor, const uint8_t pos
|
||||
static void _draw_view_noSensors(Canvas* canvas) {
|
||||
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
|
||||
DESKTOP_SETTINGS_LOAD(settings);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 7, 17, &I_sherlok_53x45_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 7, 17, &I_sherlok_53x45);
|
||||
}
|
||||
//Рисование рамки
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
void strrev(char* arr, int start, int end) {
|
||||
char temp;
|
||||
|
||||
if (start >= end)
|
||||
return;
|
||||
if(start >= end) return;
|
||||
|
||||
temp = *(arr + start);
|
||||
*(arr + start) = *(arr + end);
|
||||
@@ -17,33 +16,28 @@ void strrev(char* arr, int start, int end) {
|
||||
strrev(arr, start, end);
|
||||
}
|
||||
|
||||
char *itoa(int number, char *arr, int base)
|
||||
{
|
||||
char* itoa(int number, char* arr, int base) {
|
||||
int i = 0, r, negative = 0;
|
||||
|
||||
if (number == 0)
|
||||
{
|
||||
if(number == 0) {
|
||||
arr[i] = '0';
|
||||
arr[i + 1] = '\0';
|
||||
return arr;
|
||||
}
|
||||
|
||||
if (number < 0 && base == 10)
|
||||
{
|
||||
if(number < 0 && base == 10) {
|
||||
number *= -1;
|
||||
negative = 1;
|
||||
}
|
||||
|
||||
while (number != 0)
|
||||
{
|
||||
while(number != 0) {
|
||||
r = number % base;
|
||||
arr[i] = (r > 9) ? (r - 10) + 'a' : r + '0';
|
||||
i++;
|
||||
number /= base;
|
||||
}
|
||||
|
||||
if (negative)
|
||||
{
|
||||
if(negative) {
|
||||
arr[i] = '-';
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef FLIPPERZERO_FIRMWARE_TOOLS_H
|
||||
#define FLIPPERZERO_FIRMWARE_TOOLS_H
|
||||
|
||||
void strrev(char *arr, int start, int end);
|
||||
char *itoa(int number, char *arr, int base);
|
||||
void strrev(char* arr, int start, int end);
|
||||
char* itoa(int number, char* arr, int base);
|
||||
|
||||
#endif //FLIPPERZERO_FIRMWARE_TOOLS_H
|
||||
|
||||
@@ -88,19 +88,19 @@ int32_t usb_hid_autofire_app(void* p) {
|
||||
}
|
||||
|
||||
switch(event.input.key) {
|
||||
case InputKeyOk:
|
||||
btn_left_autofire = !btn_left_autofire;
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(autofire_delay > 0) {
|
||||
autofire_delay -= 10;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
autofire_delay += 10;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case InputKeyOk:
|
||||
btn_left_autofire = !btn_left_autofire;
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(autofire_delay > 0) {
|
||||
autofire_delay -= 10;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
autofire_delay += 10;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,10 +205,9 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(model->history_item == 0) {
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
|
||||
}
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
@@ -232,10 +231,9 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) {
|
||||
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
|
||||
if (settings->sfw_mode) {
|
||||
if(settings->sfw_mode) {
|
||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42_sfw);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
|
||||
}
|
||||
canvas_draw_dot(canvas, 17, 61);
|
||||
|
||||
@@ -3,136 +3,146 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
const unsigned char* pp = NULL;
|
||||
uint32_t pix = 0;
|
||||
int bit = 0;
|
||||
int main(int argc, char* argv[]) {
|
||||
const unsigned char* pp = NULL;
|
||||
uint32_t pix = 0;
|
||||
int bit = 0;
|
||||
|
||||
uint8_t b = 0;
|
||||
uint8_t bcnt = 0;
|
||||
uint8_t b = 0;
|
||||
uint8_t bcnt = 0;
|
||||
|
||||
unsigned int lcnt = 0;
|
||||
static const int lmax = 16; // max hex values per line
|
||||
unsigned int lcnt = 0;
|
||||
static const int lmax = 16; // max hex values per line
|
||||
|
||||
uint8_t* buf = NULL;
|
||||
uint8_t* bp = NULL;
|
||||
unsigned int blen = 0;
|
||||
uint8_t* buf = NULL;
|
||||
uint8_t* bp = NULL;
|
||||
unsigned int blen = 0;
|
||||
|
||||
uint8_t* cmp = NULL;
|
||||
uint8_t* cp = NULL;
|
||||
unsigned int clen = 0;
|
||||
uint8_t ctag = 0xFF;
|
||||
uint32_t tag[256] = {0};
|
||||
uint32_t tmax = UINT32_MAX;
|
||||
uint8_t* cmp = NULL;
|
||||
uint8_t* cp = NULL;
|
||||
unsigned int clen = 0;
|
||||
uint8_t ctag = 0xFF;
|
||||
uint32_t tag[256] = {0};
|
||||
uint32_t tmax = UINT32_MAX;
|
||||
|
||||
unsigned int x, y, z;
|
||||
unsigned int x, y, z;
|
||||
|
||||
const char* name = argv[1];
|
||||
FILE* fh = fopen(argv[2], "wb");
|
||||
const char* name = argv[1];
|
||||
FILE* fh = fopen(argv[2], "wb");
|
||||
|
||||
uint32_t white = 0xFF;
|
||||
uint32_t white = 0xFF;
|
||||
|
||||
int rv = 0; // assume success
|
||||
int rv = 0; // assume success
|
||||
|
||||
// allocate buffers
|
||||
blen = ((img.w * img.h) +0x7) >>3;
|
||||
bp = (buf = calloc(blen +1, 1));
|
||||
cp = (cmp = calloc(blen +4, 1));
|
||||
// allocate buffers
|
||||
blen = ((img.w * img.h) + 0x7) >> 3;
|
||||
bp = (buf = calloc(blen + 1, 1));
|
||||
cp = (cmp = calloc(blen + 4, 1));
|
||||
|
||||
// sanity check
|
||||
if (!fh || !buf || !cmp) {
|
||||
printf("! fopen() or malloc() fail.\n");
|
||||
rv = 255;
|
||||
goto bail;
|
||||
}
|
||||
// sanity check
|
||||
if(!fh || !buf || !cmp) {
|
||||
printf("! fopen() or malloc() fail.\n");
|
||||
rv = 255;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Find white value
|
||||
for (x = 1; x < img.bpp; x++)
|
||||
white = (white << 8) | 0xFF ;
|
||||
// Find white value
|
||||
for(x = 1; x < img.bpp; x++) white = (white << 8) | 0xFF;
|
||||
|
||||
// build bit pattern
|
||||
// create the comment as we go
|
||||
for (pp = img.b, y = 0; y < img.h; y++) {
|
||||
fprintf(fh, "// ");
|
||||
for (x = 0; x < img.w; x++) {
|
||||
// read pixel
|
||||
for (pix = 0, z = 0; z < img.bpp; pix = (pix << 8) | *pp++, z++) ;
|
||||
// get bit and draw
|
||||
if (pix < white) {
|
||||
b = (b << 1) | 1;
|
||||
fprintf(fh, "##");
|
||||
} else {
|
||||
b <<= 1;
|
||||
fprintf(fh, "..");
|
||||
}
|
||||
// got byte
|
||||
if ((++bcnt) == 8) {
|
||||
*bp++ = b;
|
||||
tag[b]++;
|
||||
bcnt = (b = 0);
|
||||
}
|
||||
}
|
||||
fprintf(fh, "\n");
|
||||
}
|
||||
fprintf(fh, "\n");
|
||||
// padding
|
||||
if (bcnt) {
|
||||
b <<= (bcnt = 8 - bcnt);
|
||||
*bp++ = b;
|
||||
tag[b]++;
|
||||
}
|
||||
// Kill the compression
|
||||
*bp = ~bp[-1]; // https://youtube.com/clip/Ugkx-JZIr16hETy7hz_H6yIdKPtxVe8C5w_V
|
||||
// build bit pattern
|
||||
// create the comment as we go
|
||||
for(pp = img.b, y = 0; y < img.h; y++) {
|
||||
fprintf(fh, "// ");
|
||||
for(x = 0; x < img.w; x++) {
|
||||
// read pixel
|
||||
for(pix = 0, z = 0; z < img.bpp; pix = (pix << 8) | *pp++, z++)
|
||||
;
|
||||
// get bit and draw
|
||||
if(pix < white) {
|
||||
b = (b << 1) | 1;
|
||||
fprintf(fh, "##");
|
||||
} else {
|
||||
b <<= 1;
|
||||
fprintf(fh, "..");
|
||||
}
|
||||
// got byte
|
||||
if((++bcnt) == 8) {
|
||||
*bp++ = b;
|
||||
tag[b]++;
|
||||
bcnt = (b = 0);
|
||||
}
|
||||
}
|
||||
fprintf(fh, "\n");
|
||||
}
|
||||
fprintf(fh, "\n");
|
||||
// padding
|
||||
if(bcnt) {
|
||||
b <<= (bcnt = 8 - bcnt);
|
||||
*bp++ = b;
|
||||
tag[b]++;
|
||||
}
|
||||
// Kill the compression
|
||||
*bp = ~bp[-1]; // https://youtube.com/clip/Ugkx-JZIr16hETy7hz_H6yIdKPtxVe8C5w_V
|
||||
|
||||
// Byte run length compression
|
||||
// Find a good tag
|
||||
for (x = 0; tmax && (x < 256); x++) {
|
||||
if (tag[x] < tmax) {
|
||||
tmax = tag[x];
|
||||
ctag = x;
|
||||
}
|
||||
}
|
||||
// Byte run length compression
|
||||
// Find a good tag
|
||||
for(x = 0; tmax && (x < 256); x++) {
|
||||
if(tag[x] < tmax) {
|
||||
tmax = tag[x];
|
||||
ctag = x;
|
||||
}
|
||||
}
|
||||
|
||||
// compress the data
|
||||
for (bp = buf, x = 0; (clen < blen) && (x < blen); x++) {
|
||||
// need at least 4 the same to be worth it
|
||||
// must compress tag (if it occurs)
|
||||
if ((bp[x] == bp[x+1]) && (bp[x] == bp[x+2]) && (bp[x] == bp[x+3]) || (bp[x] == ctag)) {
|
||||
for (y = 1; (y < 255) && (bp[x] == bp[x+y]); y++) ;
|
||||
*cp++ = ctag; // tag
|
||||
*cp++ = y; // length
|
||||
*cp++ = bp[x]; // byte
|
||||
x += y -1;
|
||||
clen += 3;
|
||||
} else {
|
||||
*cp++ = bp[x];
|
||||
clen++;
|
||||
}
|
||||
}
|
||||
// compress the data
|
||||
for(bp = buf, x = 0; (clen < blen) && (x < blen); x++) {
|
||||
// need at least 4 the same to be worth it
|
||||
// must compress tag (if it occurs)
|
||||
if((bp[x] == bp[x + 1]) && (bp[x] == bp[x + 2]) && (bp[x] == bp[x + 3]) ||
|
||||
(bp[x] == ctag)) {
|
||||
for(y = 1; (y < 255) && (bp[x] == bp[x + y]); y++)
|
||||
;
|
||||
*cp++ = ctag; // tag
|
||||
*cp++ = y; // length
|
||||
*cp++ = bp[x]; // byte
|
||||
x += y - 1;
|
||||
clen += 3;
|
||||
} else {
|
||||
*cp++ = bp[x];
|
||||
clen++;
|
||||
}
|
||||
}
|
||||
|
||||
// create struct
|
||||
fprintf(fh, "#include \"images.h\"\n\n");
|
||||
fprintf(fh, "const image_t img_%s = { %d, %d, ", name, img.w, img.h);
|
||||
// create struct
|
||||
fprintf(fh, "#include \"images.h\"\n\n");
|
||||
fprintf(fh, "const image_t img_%s = { %d, %d, ", name, img.w, img.h);
|
||||
|
||||
if (clen < blen) { // dump compressed?
|
||||
fprintf(fh, "true, %d, 0x%02X, { // orig:%d, comp:%.2f%%\n\t",
|
||||
clen, ctag, blen, 100.0-((clen*100.0)/blen));
|
||||
for (x = 0; x < clen; x++)
|
||||
if (x == clen -1) fprintf(fh, "0x%02X\n}};\n", cmp[x]) ;
|
||||
else fprintf(fh, "0x%02X%s", cmp[x], (!((x+1)%16)) ? ",\n\t" : ", ") ;
|
||||
if(clen < blen) { // dump compressed?
|
||||
fprintf(
|
||||
fh,
|
||||
"true, %d, 0x%02X, { // orig:%d, comp:%.2f%%\n\t",
|
||||
clen,
|
||||
ctag,
|
||||
blen,
|
||||
100.0 - ((clen * 100.0) / blen));
|
||||
for(x = 0; x < clen; x++)
|
||||
if(x == clen - 1)
|
||||
fprintf(fh, "0x%02X\n}};\n", cmp[x]);
|
||||
else
|
||||
fprintf(fh, "0x%02X%s", cmp[x], (!((x + 1) % 16)) ? ",\n\t" : ", ");
|
||||
|
||||
} else { // dump UNcompressed
|
||||
fprintf(fh, "false, %d, 0, {\n\t", blen);
|
||||
for (x = 0; x < blen; x++)
|
||||
if (x == blen -1) fprintf(fh, "0x%02X\n}};\n", buf[x]) ;
|
||||
else fprintf(fh, "0x%02X%s", buf[x], (!((x+1)%16)) ? ",\n\t" : ", ") ;
|
||||
}
|
||||
} else { // dump UNcompressed
|
||||
fprintf(fh, "false, %d, 0, {\n\t", blen);
|
||||
for(x = 0; x < blen; x++)
|
||||
if(x == blen - 1)
|
||||
fprintf(fh, "0x%02X\n}};\n", buf[x]);
|
||||
else
|
||||
fprintf(fh, "0x%02X%s", buf[x], (!((x + 1) % 16)) ? ",\n\t" : ", ");
|
||||
}
|
||||
|
||||
bail:
|
||||
if (fh) fclose(fh) ;
|
||||
if (buf) free(buf) ;
|
||||
if (cmp) free(cmp) ;
|
||||
if(fh) fclose(fh);
|
||||
if(buf) free(buf);
|
||||
if(cmp) free(cmp);
|
||||
|
||||
return rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -1,55 +1,49 @@
|
||||
#include <gui/gui.h> // GUI (screen/keyboard) API
|
||||
#include <gui/gui.h> // GUI (screen/keyboard) API
|
||||
|
||||
#include "images.h"
|
||||
#include "images.h"
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
static Canvas* _canvas;
|
||||
static uint8_t _tlx;
|
||||
static uint8_t _tly;
|
||||
static Canvas* _canvas;
|
||||
static uint8_t _tlx;
|
||||
static uint8_t _tly;
|
||||
|
||||
static uint8_t _x;
|
||||
static uint8_t _y;
|
||||
static uint8_t _x;
|
||||
static uint8_t _y;
|
||||
|
||||
static const image_t* _img;
|
||||
static const image_t* _img;
|
||||
|
||||
static bool _blk;
|
||||
static Color _set;
|
||||
static Color _clr;
|
||||
static bool _blk;
|
||||
static Color _set;
|
||||
static Color _clr;
|
||||
|
||||
//+============================================================================
|
||||
static
|
||||
void _showByteSet (const uint8_t b)
|
||||
{
|
||||
for (uint8_t m = 0x80; m; m >>= 1) {
|
||||
if (b & m) // plot only SET bits
|
||||
canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
|
||||
if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
|
||||
}
|
||||
static void _showByteSet(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if(b & m) // plot only SET bits
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
static
|
||||
void _showByteClr (const uint8_t b)
|
||||
{
|
||||
for (uint8_t m = 0x80; m; m >>= 1) {
|
||||
if (!(b & m)) // plot only CLR bits
|
||||
canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
|
||||
if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
|
||||
}
|
||||
static void _showByteClr(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if(!(b & m)) // plot only CLR bits
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
static
|
||||
void _showByteAll (const uint8_t b)
|
||||
{
|
||||
for (uint8_t m = 0x80; m; m >>= 1) {
|
||||
if ((!!(b & m)) ^ _blk) { // Change colour only when required
|
||||
canvas_set_color(_canvas, ((b & m) ? _set : _clr));
|
||||
_blk = !_blk;
|
||||
}
|
||||
canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
|
||||
if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
|
||||
}
|
||||
static void _showByteAll(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if((!!(b & m)) ^ _blk) { // Change colour only when required
|
||||
canvas_set_color(_canvas, ((b & m) ? _set : _clr));
|
||||
_blk = !_blk;
|
||||
}
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
@@ -61,81 +55,83 @@ void _showByteAll (const uint8_t b)
|
||||
// SHOW_ALL - plot all images pixels as they are
|
||||
// SHOW_ALL_INV - plot all images pixels inverted
|
||||
//
|
||||
void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
|
||||
const image_t* img, const showMode_t mode)
|
||||
{
|
||||
void(*fnShow)(const uint8_t) = NULL;
|
||||
void show(
|
||||
Canvas* const canvas,
|
||||
const uint8_t tlx,
|
||||
const uint8_t tly,
|
||||
const image_t* img,
|
||||
const showMode_t mode) {
|
||||
void (*fnShow)(const uint8_t) = NULL;
|
||||
|
||||
const uint8_t* bp = img->data;
|
||||
const uint8_t* bp = img->data;
|
||||
|
||||
// code size optimisation
|
||||
switch (mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
_set = ColorBlack;
|
||||
_clr = ColorWhite;
|
||||
break;
|
||||
// code size optimisation
|
||||
switch(mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
_set = ColorBlack;
|
||||
_clr = ColorWhite;
|
||||
break;
|
||||
|
||||
case SHOW_INV_:
|
||||
_set = ColorWhite;
|
||||
_clr = ColorBlack;
|
||||
break;
|
||||
case SHOW_INV_:
|
||||
_set = ColorWhite;
|
||||
_clr = ColorBlack;
|
||||
break;
|
||||
|
||||
case SHOW_BLK_:
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
break;
|
||||
case SHOW_BLK_:
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
break;
|
||||
|
||||
case SHOW_WHT_:
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
break;
|
||||
case SHOW_WHT_:
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
break;
|
||||
}
|
||||
switch(mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
case SHOW_INV_:
|
||||
fnShow = _showByteAll;
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
_blk = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
switch (mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
case SHOW_INV_:
|
||||
fnShow = _showByteAll;
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
_blk = 0;
|
||||
break;
|
||||
case SHOW_BLK_:
|
||||
case SHOW_WHT_:
|
||||
switch(mode & SHOW_ALL_) {
|
||||
case SHOW_SET_:
|
||||
fnShow = _showByteSet;
|
||||
break;
|
||||
case SHOW_CLR_:
|
||||
fnShow = _showByteClr;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
furi_check(fnShow);
|
||||
|
||||
case SHOW_BLK_:
|
||||
case SHOW_WHT_:
|
||||
switch (mode & SHOW_ALL_) {
|
||||
case SHOW_SET_:
|
||||
fnShow = _showByteSet;
|
||||
break;
|
||||
case SHOW_CLR_:
|
||||
fnShow = _showByteClr;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
furi_check(fnShow);
|
||||
// I want nested functions!
|
||||
_canvas = canvas;
|
||||
_img = img;
|
||||
_tlx = tlx;
|
||||
_tly = tly;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
|
||||
// I want nested functions!
|
||||
_canvas = canvas;
|
||||
_img = img;
|
||||
_tlx = tlx;
|
||||
_tly = tly;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
// Compressed
|
||||
if(img->c) {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if(*bp == img->tag) {
|
||||
for(uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]);
|
||||
bp += 3 - 1;
|
||||
i += 3 - 1;
|
||||
|
||||
// Compressed
|
||||
if (img->c) {
|
||||
for (unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if (*bp == img->tag) {
|
||||
for (uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]) ;
|
||||
bp += 3 -1;
|
||||
i += 3 -1;
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
fnShow(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
fnShow(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Not compressed
|
||||
} else {
|
||||
for (unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp) ;
|
||||
}
|
||||
// Not compressed
|
||||
} else {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
#ifndef IMAGES_H_
|
||||
#define IMAGES_H_
|
||||
#ifndef IMAGES_H_
|
||||
#define IMAGES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
typedef
|
||||
enum showMode {
|
||||
// {INV:--:WHT:BLK::--:--:CLR:SET}
|
||||
SHOW_SET_ = 0x01,
|
||||
SHOW_CLR_ = 0x02,
|
||||
SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_,
|
||||
typedef enum showMode {
|
||||
// {INV:--:WHT:BLK::--:--:CLR:SET}
|
||||
SHOW_SET_ = 0x01,
|
||||
SHOW_CLR_ = 0x02,
|
||||
SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_,
|
||||
|
||||
SHOW_BLK_ = 0x10,
|
||||
SHOW_WHT_ = 0x20,
|
||||
SHOW_NRM_ = 0x00,
|
||||
SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_,
|
||||
SHOW_BLK_ = 0x10,
|
||||
SHOW_WHT_ = 0x20,
|
||||
SHOW_NRM_ = 0x00,
|
||||
SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_,
|
||||
|
||||
SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_,
|
||||
SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_,
|
||||
SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_,
|
||||
SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_,
|
||||
|
||||
SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_,
|
||||
SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_,
|
||||
SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_,
|
||||
SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_,
|
||||
|
||||
SHOW_ALL = SHOW_ALL_ | SHOW_NRM_,
|
||||
SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_,
|
||||
}
|
||||
showMode_t;
|
||||
SHOW_ALL = SHOW_ALL_ | SHOW_NRM_,
|
||||
SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_,
|
||||
} showMode_t;
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
typedef
|
||||
struct image {
|
||||
uint8_t w; // width
|
||||
uint8_t h; // height
|
||||
bool c; // compressed?
|
||||
uint16_t len; // image data length
|
||||
uint8_t tag; // rle tag
|
||||
uint8_t data[]; // image data
|
||||
}
|
||||
image_t;
|
||||
typedef struct image {
|
||||
uint8_t w; // width
|
||||
uint8_t h; // height
|
||||
bool c; // compressed?
|
||||
uint16_t len; // image data length
|
||||
uint8_t tag; // rle tag
|
||||
uint8_t data[]; // image data
|
||||
} image_t;
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
//[TAG]
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
#ifndef IMGTEST
|
||||
# include <gui/gui.h>
|
||||
void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
|
||||
const image_t* img, const showMode_t mode) ;
|
||||
#include <gui/gui.h>
|
||||
void show(
|
||||
Canvas* const canvas,
|
||||
const uint8_t tlx,
|
||||
const uint8_t tly,
|
||||
const image_t* img,
|
||||
const showMode_t mode);
|
||||
#endif
|
||||
|
||||
#endif //IMAGES_H_
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "images.h"
|
||||
#include "images.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This will be the plot function out of your graphics library
|
||||
//
|
||||
#define PLOT(x,y,c) do { \
|
||||
printf("%s", (c ? "#" : ".")); \
|
||||
if (x == img->w -1) printf("\n") ; \
|
||||
}while(0)
|
||||
#define PLOT(x, y, c) \
|
||||
do { \
|
||||
printf("%s", (c ? "#" : ".")); \
|
||||
if(x == img->w - 1) printf("\n"); \
|
||||
} while(0)
|
||||
|
||||
//+============================================================================
|
||||
// The pain we endure to avoid code duplication cleanly
|
||||
//
|
||||
#define PLOTBYTE(b) do { \
|
||||
for (uint8_t m = 0x80; m; m>>=1) { \
|
||||
PLOT(x,y, (b & m)); \
|
||||
if ( ((++x) == img->w) && !(x = 0) && ((++y) == img->h) ) break ; \
|
||||
} \
|
||||
}while(0)
|
||||
#define PLOTBYTE(b) \
|
||||
do { \
|
||||
for(uint8_t m = 0x80; m; m >>= 1) { \
|
||||
PLOT(x, y, (b & m)); \
|
||||
if(((++x) == img->w) && !(x = 0) && ((++y) == img->h)) break; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void show (const image_t* img)
|
||||
{
|
||||
// Some variables
|
||||
const uint8_t* bp = img->data;
|
||||
unsigned int x = 0;
|
||||
unsigned int y = 0;
|
||||
void show(const image_t* img) {
|
||||
// Some variables
|
||||
const uint8_t* bp = img->data;
|
||||
unsigned int x = 0;
|
||||
unsigned int y = 0;
|
||||
|
||||
// Compressed
|
||||
if (img->c) {
|
||||
for (unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if (*bp == img->tag) {
|
||||
for (uint16_t c = 0; c < bp[1]; c++) PLOTBYTE(bp[2]) ;
|
||||
bp += 3 -1;
|
||||
i += 3 -1;
|
||||
// Compressed
|
||||
if(img->c) {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if(*bp == img->tag) {
|
||||
for(uint16_t c = 0; c < bp[1]; c++) PLOTBYTE(bp[2]);
|
||||
bp += 3 - 1;
|
||||
i += 3 - 1;
|
||||
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
PLOTBYTE(*bp);
|
||||
}
|
||||
}
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
PLOTBYTE(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Not compressed
|
||||
} else {
|
||||
for (unsigned int i = 0; i < img->len; i++, bp++) PLOTBYTE(*bp) ;
|
||||
}
|
||||
// Not compressed
|
||||
} else {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) PLOTBYTE(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
#undef PLOTBYTE
|
||||
#undef PLOTBYTE
|
||||
|
||||
//+============================================================================
|
||||
int main (void)
|
||||
{
|
||||
show(&img_zzz);
|
||||
return 0;
|
||||
int main(void) {
|
||||
show(&img_zzz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#ifndef BC_LOGGING_H_
|
||||
#define BC_LOGGING_H_
|
||||
#ifndef BC_LOGGING_H_
|
||||
#define BC_LOGGING_H_
|
||||
|
||||
#include <furi.h>
|
||||
#include "err.h" // appName
|
||||
#include "err.h" // appName
|
||||
|
||||
//! WARNING: There is a bug in Furi such that if you crank LOG_LEVEL up to 6=TRACE
|
||||
//! AND you have menu->settings->system->logLevel = trace
|
||||
//! THEN this program will cause the FZ to crash when the plugin exits!
|
||||
#define LOG_LEVEL 4
|
||||
#define LOG_LEVEL 4
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time
|
||||
@@ -27,44 +27,44 @@
|
||||
// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time
|
||||
// This lets you limit it at COMPILE-time
|
||||
#ifndef LOG_LEVEL
|
||||
# define LOG_LEVEL 6 // default = full logging
|
||||
#define LOG_LEVEL 6 // default = full logging
|
||||
#endif
|
||||
|
||||
#if (LOG_LEVEL < 2)
|
||||
# undef FURI_LOG_E
|
||||
# define FURI_LOG_E(tag, fmt, ...)
|
||||
#if(LOG_LEVEL < 2)
|
||||
#undef FURI_LOG_E
|
||||
#define FURI_LOG_E(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if (LOG_LEVEL < 3)
|
||||
# undef FURI_LOG_W
|
||||
# define FURI_LOG_W(tag, fmt, ...)
|
||||
#if(LOG_LEVEL < 3)
|
||||
#undef FURI_LOG_W
|
||||
#define FURI_LOG_W(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if (LOG_LEVEL < 4)
|
||||
# undef FURI_LOG_I
|
||||
# define FURI_LOG_I(tag, fmt, ...)
|
||||
#if(LOG_LEVEL < 4)
|
||||
#undef FURI_LOG_I
|
||||
#define FURI_LOG_I(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if (LOG_LEVEL < 5)
|
||||
# undef FURI_LOG_D
|
||||
# define FURI_LOG_D(tag, fmt, ...)
|
||||
#if(LOG_LEVEL < 5)
|
||||
#undef FURI_LOG_D
|
||||
#define FURI_LOG_D(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if (LOG_LEVEL < 6)
|
||||
# undef FURI_LOG_T
|
||||
# define FURI_LOG_T(tag, fmt, ...)
|
||||
#if(LOG_LEVEL < 6)
|
||||
#undef FURI_LOG_T
|
||||
#define FURI_LOG_T(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Logging helper macros
|
||||
//
|
||||
#define ERROR(fmt, ...) FURI_LOG_E(appName, fmt __VA_OPT__(,) __VA_ARGS__)
|
||||
#define WARN(fmt, ...) FURI_LOG_W(appName, fmt __VA_OPT__(,) __VA_ARGS__)
|
||||
#define INFO(fmt, ...) FURI_LOG_I(appName, fmt __VA_OPT__(,) __VA_ARGS__)
|
||||
#define DEBUG(fmt, ...) FURI_LOG_D(appName, fmt __VA_OPT__(,) __VA_ARGS__)
|
||||
#define TRACE(fmt, ...) FURI_LOG_T(appName, fmt __VA_OPT__(,) __VA_ARGS__)
|
||||
#define ERROR(fmt, ...) FURI_LOG_E(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define WARN(fmt, ...) FURI_LOG_W(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define INFO(fmt, ...) FURI_LOG_I(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define DEBUG(fmt, ...) FURI_LOG_D(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define TRACE(fmt, ...) FURI_LOG_T(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
|
||||
#define ENTER TRACE("(+) %s", __func__)
|
||||
#define LEAVE TRACE("(-) %s", __func__)
|
||||
#define ENTER TRACE("(+) %s", __func__)
|
||||
#define LEAVE TRACE("(-) %s", __func__)
|
||||
|
||||
#endif //BC_LOGGING_H_
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Avoid circular/nested/mulitple inclusion
|
||||
#ifndef ERR_H_
|
||||
#define ERR_H_
|
||||
#ifndef ERR_H_
|
||||
#define ERR_H_
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
// Application name
|
||||
//
|
||||
static const char* const appName = "Wii_i2c"; //$ Name used in log files
|
||||
static const char* const appName = "Wii_i2c"; //$ Name used in log files
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
// Error codes and messages
|
||||
@@ -13,57 +13,60 @@ static const char* const appName = "Wii_i2c"; //$ Name used in log files
|
||||
|
||||
// You should only ever (need to) edit this list
|
||||
// ...Watch out for extraneous whitespace after the terminating backslashes
|
||||
#define FOREACH_ES(esPrial) \
|
||||
/* The first line MUST define 'ERR_OK = 0' */ \
|
||||
esPrial( 0, ERR_OK , "OK (no error)") \
|
||||
\
|
||||
esPrial( 1, ERR_MALLOC_QUEUE , "malloc() fail - queue") \
|
||||
esPrial( 2, ERR_MALLOC_STATE , "malloc() fail - state") \
|
||||
esPrial( 3, ERR_MALLOC_TEXT , "malloc() fail - text") \
|
||||
esPrial( 4, ERR_MALLOC_VIEW , "malloc() fail - viewport") \
|
||||
esPrial( 5, ERR_NO_MUTEX , "Cannot create mutex") \
|
||||
esPrial( 6, ERR_NO_GUI , "Cannot open GUI") \
|
||||
esPrial( 7, ERR_NO_TIMER , "Cannot create timer") \
|
||||
esPrial( 8, ERR_NO_NOTIFY , "Cannot acquire notifications handle") \
|
||||
\
|
||||
esPrial(10, ERR_MUTEX_BLOCK , "Mutex block failed") \
|
||||
esPrial(11, ERR_MUTEX_RELEASE , "Mutex release failed") \
|
||||
\
|
||||
esPrial(20, ERR_QUEUE_RTOS , "queue - Undefined RTOS error") \
|
||||
esPrial(21, DEBUG_QUEUE_TIMEOUT, "queue - Timeout") \
|
||||
esPrial(22, ERR_QUEUE_RESOURCE , "queue - Resource not available") \
|
||||
esPrial(23, ERR_QUEUE_BADPRM , "queue - Bad parameter") \
|
||||
esPrial(24, ERR_QUEUE_NOMEM , "queue - Out of memory") \
|
||||
esPrial(25, ERR_QUEUE_ISR , "queue - Banned in ISR") \
|
||||
esPrial(26, ERR_QUEUE_UNK , "queue - Unknown") \
|
||||
\
|
||||
esPrial(30, WARN_SCAN_START , "Scan - Already started") \
|
||||
esPrial(31, WARN_SCAN_STOP , "Scan - Already stopped") \
|
||||
esPrial(32, ERR_TIMER_START , "Scan - Cannot start timer") \
|
||||
esPrial(33, ERR_TIMER_STOP , "Scan - Cannot stop timer") \
|
||||
//[EOT]
|
||||
#define FOREACH_ES(esPrial) \
|
||||
/* The first line MUST define 'ERR_OK = 0' */ \
|
||||
esPrial(0, ERR_OK, "OK (no error)") \
|
||||
\
|
||||
esPrial(1, ERR_MALLOC_QUEUE, "malloc() fail - queue") esPrial( \
|
||||
2, \
|
||||
ERR_MALLOC_STATE, \
|
||||
"malloc() fail - state") esPrial(3, ERR_MALLOC_TEXT, "malloc() fail - text") \
|
||||
esPrial(4, ERR_MALLOC_VIEW, "malloc() fail - viewport") esPrial( \
|
||||
5, ERR_NO_MUTEX, "Cannot create mutex") esPrial(6, ERR_NO_GUI, "Cannot open GUI") \
|
||||
esPrial(7, ERR_NO_TIMER, "Cannot create timer") esPrial( \
|
||||
8, ERR_NO_NOTIFY, "Cannot acquire notifications handle") \
|
||||
\
|
||||
esPrial(10, ERR_MUTEX_BLOCK, "Mutex block failed") esPrial( \
|
||||
11, ERR_MUTEX_RELEASE, "Mutex release failed") \
|
||||
\
|
||||
esPrial(20, ERR_QUEUE_RTOS, "queue - Undefined RTOS error") \
|
||||
esPrial(21, DEBUG_QUEUE_TIMEOUT, "queue - Timeout") esPrial( \
|
||||
22, ERR_QUEUE_RESOURCE, "queue - Resource not available") \
|
||||
esPrial(23, ERR_QUEUE_BADPRM, "queue - Bad parameter") esPrial( \
|
||||
24, ERR_QUEUE_NOMEM, "queue - Out of memory") \
|
||||
esPrial(25, ERR_QUEUE_ISR, "queue - Banned in ISR") esPrial( \
|
||||
26, ERR_QUEUE_UNK, "queue - Unknown") \
|
||||
\
|
||||
esPrial(30, WARN_SCAN_START, "Scan - Already started") \
|
||||
esPrial(31, WARN_SCAN_STOP, "Scan - Already stopped") \
|
||||
esPrial( \
|
||||
32, \
|
||||
ERR_TIMER_START, \
|
||||
"Scan - Cannot start timer") \
|
||||
esPrial( \
|
||||
33, \
|
||||
ERR_TIMER_STOP, \
|
||||
"Scan - Cannot stop timer") //[EOT]
|
||||
|
||||
// Declare list extraction macros
|
||||
#define ES_ENUM(num, ename, string) ename = num,
|
||||
#define ES_STRING(num, ename, string) string"\r\n",
|
||||
#define ES_ENUM(num, ename, string) ename = num,
|
||||
#define ES_STRING(num, ename, string) string "\r\n",
|
||||
|
||||
// Build the enum
|
||||
typedef
|
||||
enum err { FOREACH_ES(ES_ENUM) }
|
||||
err_t ;
|
||||
typedef enum err { FOREACH_ES(ES_ENUM) } err_t;
|
||||
|
||||
// You need to '#define ERR_C_' in precisely ONE source file
|
||||
#ifdef ERR_C_
|
||||
// Build the string list
|
||||
const char* const wii_errs[] = { FOREACH_ES(ES_STRING) };
|
||||
// Build the string list
|
||||
const char* const wii_errs[] = {FOREACH_ES(ES_STRING)};
|
||||
#else
|
||||
// Give access to string list
|
||||
extern const char* const wii_errs[];
|
||||
// Give access to string list
|
||||
extern const char* const wii_errs[];
|
||||
#endif
|
||||
|
||||
// This is a header file, clean up
|
||||
#undef ES_ENUM
|
||||
#undef ES_STRING
|
||||
#undef FOREACH_ES
|
||||
#undef ES_ENUM
|
||||
#undef ES_STRING
|
||||
#undef FOREACH_ES
|
||||
|
||||
#endif // ERR_H_
|
||||
|
||||
@@ -1,55 +1,49 @@
|
||||
#include <gui/gui.h> // GUI (screen/keyboard) API
|
||||
#include <gui/gui.h> // GUI (screen/keyboard) API
|
||||
|
||||
#include "images.h"
|
||||
#include "images.h"
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
static Canvas* _canvas;
|
||||
static uint8_t _tlx;
|
||||
static uint8_t _tly;
|
||||
static Canvas* _canvas;
|
||||
static uint8_t _tlx;
|
||||
static uint8_t _tly;
|
||||
|
||||
static uint8_t _x;
|
||||
static uint8_t _y;
|
||||
static uint8_t _x;
|
||||
static uint8_t _y;
|
||||
|
||||
static const image_t* _img;
|
||||
static const image_t* _img;
|
||||
|
||||
static bool _blk;
|
||||
static Color _set;
|
||||
static Color _clr;
|
||||
static bool _blk;
|
||||
static Color _set;
|
||||
static Color _clr;
|
||||
|
||||
//+============================================================================
|
||||
static
|
||||
void _showByteSet (const uint8_t b)
|
||||
{
|
||||
for (uint8_t m = 0x80; m; m >>= 1) {
|
||||
if (b & m) // plot only SET bits
|
||||
canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
|
||||
if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
|
||||
}
|
||||
static void _showByteSet(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if(b & m) // plot only SET bits
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
static
|
||||
void _showByteClr (const uint8_t b)
|
||||
{
|
||||
for (uint8_t m = 0x80; m; m >>= 1) {
|
||||
if (!(b & m)) // plot only CLR bits
|
||||
canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
|
||||
if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
|
||||
}
|
||||
static void _showByteClr(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if(!(b & m)) // plot only CLR bits
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
static
|
||||
void _showByteAll (const uint8_t b)
|
||||
{
|
||||
for (uint8_t m = 0x80; m; m >>= 1) {
|
||||
if ((!!(b & m)) ^ _blk) { // Change colour only when required
|
||||
canvas_set_color(_canvas, ((b & m) ? _set : _clr));
|
||||
_blk = !_blk;
|
||||
}
|
||||
canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
|
||||
if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
|
||||
}
|
||||
static void _showByteAll(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if((!!(b & m)) ^ _blk) { // Change colour only when required
|
||||
canvas_set_color(_canvas, ((b & m) ? _set : _clr));
|
||||
_blk = !_blk;
|
||||
}
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
@@ -61,81 +55,83 @@ void _showByteAll (const uint8_t b)
|
||||
// SHOW_ALL - plot all images pixels as they are
|
||||
// SHOW_ALL_INV - plot all images pixels inverted
|
||||
//
|
||||
void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
|
||||
const image_t* img, const showMode_t mode)
|
||||
{
|
||||
void(*fnShow)(const uint8_t) = NULL;
|
||||
void show(
|
||||
Canvas* const canvas,
|
||||
const uint8_t tlx,
|
||||
const uint8_t tly,
|
||||
const image_t* img,
|
||||
const showMode_t mode) {
|
||||
void (*fnShow)(const uint8_t) = NULL;
|
||||
|
||||
const uint8_t* bp = img->data;
|
||||
const uint8_t* bp = img->data;
|
||||
|
||||
// code size optimisation
|
||||
switch (mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
_set = ColorBlack;
|
||||
_clr = ColorWhite;
|
||||
break;
|
||||
// code size optimisation
|
||||
switch(mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
_set = ColorBlack;
|
||||
_clr = ColorWhite;
|
||||
break;
|
||||
|
||||
case SHOW_INV_:
|
||||
_set = ColorWhite;
|
||||
_clr = ColorBlack;
|
||||
break;
|
||||
case SHOW_INV_:
|
||||
_set = ColorWhite;
|
||||
_clr = ColorBlack;
|
||||
break;
|
||||
|
||||
case SHOW_BLK_:
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
break;
|
||||
case SHOW_BLK_:
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
break;
|
||||
|
||||
case SHOW_WHT_:
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
break;
|
||||
case SHOW_WHT_:
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
break;
|
||||
}
|
||||
switch(mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
case SHOW_INV_:
|
||||
fnShow = _showByteAll;
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
_blk = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
switch (mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
case SHOW_INV_:
|
||||
fnShow = _showByteAll;
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
_blk = 0;
|
||||
break;
|
||||
case SHOW_BLK_:
|
||||
case SHOW_WHT_:
|
||||
switch(mode & SHOW_ALL_) {
|
||||
case SHOW_SET_:
|
||||
fnShow = _showByteSet;
|
||||
break;
|
||||
case SHOW_CLR_:
|
||||
fnShow = _showByteClr;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
furi_check(fnShow);
|
||||
|
||||
case SHOW_BLK_:
|
||||
case SHOW_WHT_:
|
||||
switch (mode & SHOW_ALL_) {
|
||||
case SHOW_SET_:
|
||||
fnShow = _showByteSet;
|
||||
break;
|
||||
case SHOW_CLR_:
|
||||
fnShow = _showByteClr;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
furi_check(fnShow);
|
||||
// I want nested functions!
|
||||
_canvas = canvas;
|
||||
_img = img;
|
||||
_tlx = tlx;
|
||||
_tly = tly;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
|
||||
// I want nested functions!
|
||||
_canvas = canvas;
|
||||
_img = img;
|
||||
_tlx = tlx;
|
||||
_tly = tly;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
// Compressed
|
||||
if(img->c) {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if(*bp == img->tag) {
|
||||
for(uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]);
|
||||
bp += 3 - 1;
|
||||
i += 3 - 1;
|
||||
|
||||
// Compressed
|
||||
if (img->c) {
|
||||
for (unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if (*bp == img->tag) {
|
||||
for (uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]) ;
|
||||
bp += 3 -1;
|
||||
i += 3 -1;
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
fnShow(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
fnShow(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Not compressed
|
||||
} else {
|
||||
for (unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp) ;
|
||||
}
|
||||
// Not compressed
|
||||
} else {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +1,134 @@
|
||||
#ifndef IMAGES_H_
|
||||
#define IMAGES_H_
|
||||
#ifndef IMAGES_H_
|
||||
#define IMAGES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
typedef
|
||||
enum showMode {
|
||||
// {INV:--:WHT:BLK::--:--:CLR:SET}
|
||||
SHOW_SET_ = 0x01,
|
||||
SHOW_CLR_ = 0x02,
|
||||
SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_,
|
||||
typedef enum showMode {
|
||||
// {INV:--:WHT:BLK::--:--:CLR:SET}
|
||||
SHOW_SET_ = 0x01,
|
||||
SHOW_CLR_ = 0x02,
|
||||
SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_,
|
||||
|
||||
SHOW_BLK_ = 0x10,
|
||||
SHOW_WHT_ = 0x20,
|
||||
SHOW_NRM_ = 0x00,
|
||||
SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_,
|
||||
SHOW_BLK_ = 0x10,
|
||||
SHOW_WHT_ = 0x20,
|
||||
SHOW_NRM_ = 0x00,
|
||||
SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_,
|
||||
|
||||
SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_,
|
||||
SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_,
|
||||
SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_,
|
||||
SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_,
|
||||
|
||||
SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_,
|
||||
SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_,
|
||||
SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_,
|
||||
SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_,
|
||||
|
||||
SHOW_ALL = SHOW_ALL_ | SHOW_NRM_,
|
||||
SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_,
|
||||
}
|
||||
showMode_t;
|
||||
SHOW_ALL = SHOW_ALL_ | SHOW_NRM_,
|
||||
SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_,
|
||||
} showMode_t;
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
typedef
|
||||
struct image {
|
||||
uint8_t w; // width
|
||||
uint8_t h; // height
|
||||
bool c; // compressed?
|
||||
uint16_t len; // image data length
|
||||
uint8_t tag; // rle tag
|
||||
uint8_t data[]; // image data
|
||||
}
|
||||
image_t;
|
||||
typedef struct image {
|
||||
uint8_t w; // width
|
||||
uint8_t h; // height
|
||||
bool c; // compressed?
|
||||
uint16_t len; // image data length
|
||||
uint8_t tag; // rle tag
|
||||
uint8_t data[]; // image data
|
||||
} image_t;
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
//[TAG]
|
||||
extern const image_t img_csLogo_Small;
|
||||
extern const image_t img_3x5_v;
|
||||
extern const image_t img_3x5_9;
|
||||
extern const image_t img_3x5_8;
|
||||
extern const image_t img_3x5_7;
|
||||
extern const image_t img_3x5_6;
|
||||
extern const image_t img_3x5_5;
|
||||
extern const image_t img_3x5_4;
|
||||
extern const image_t img_3x5_3;
|
||||
extern const image_t img_3x5_2;
|
||||
extern const image_t img_3x5_1;
|
||||
extern const image_t img_3x5_0;
|
||||
extern const image_t img_key_Ui;
|
||||
extern const image_t img_key_OKi;
|
||||
extern const image_t img_RIP;
|
||||
extern const image_t img_cc_trg_R4;
|
||||
extern const image_t img_cc_trg_R3;
|
||||
extern const image_t img_cc_trg_R2;
|
||||
extern const image_t img_cc_trg_R1;
|
||||
extern const image_t img_cc_trg_L4;
|
||||
extern const image_t img_cc_trg_L3;
|
||||
extern const image_t img_cc_trg_L2;
|
||||
extern const image_t img_cc_trg_L1;
|
||||
extern const image_t img_cc_Joy;
|
||||
extern const image_t img_cc_Main;
|
||||
extern const image_t img_cc_Cable;
|
||||
extern const image_t img_key_Back;
|
||||
extern const image_t img_key_OK;
|
||||
extern const image_t img_6x8_Z;
|
||||
extern const image_t img_6x8_Y;
|
||||
extern const image_t img_6x8_X;
|
||||
extern const image_t img_key_U;
|
||||
extern const image_t img_key_D;
|
||||
extern const image_t img_csLogo_FULL;
|
||||
extern const image_t img_6x8_7;
|
||||
extern const image_t img_key_R;
|
||||
extern const image_t img_key_L;
|
||||
extern const image_t img_5x7_7;
|
||||
extern const image_t img_5x7_F;
|
||||
extern const image_t img_5x7_E;
|
||||
extern const image_t img_5x7_D;
|
||||
extern const image_t img_5x7_C;
|
||||
extern const image_t img_5x7_B;
|
||||
extern const image_t img_5x7_A;
|
||||
extern const image_t img_5x7_9;
|
||||
extern const image_t img_5x7_8;
|
||||
extern const image_t img_5x7_6;
|
||||
extern const image_t img_5x7_5;
|
||||
extern const image_t img_5x7_4;
|
||||
extern const image_t img_5x7_3;
|
||||
extern const image_t img_5x7_2;
|
||||
extern const image_t img_5x7_1;
|
||||
extern const image_t img_5x7_0;
|
||||
extern const image_t img_6x8_v;
|
||||
extern const image_t img_6x8_n;
|
||||
extern const image_t img_6x8_G;
|
||||
extern const image_t img_6x8_F;
|
||||
extern const image_t img_6x8_E;
|
||||
extern const image_t img_6x8_d;
|
||||
extern const image_t img_6x8_C;
|
||||
extern const image_t img_6x8_B;
|
||||
extern const image_t img_6x8_A;
|
||||
extern const image_t img_6x8_9;
|
||||
extern const image_t img_6x8_8;
|
||||
extern const image_t img_6x8_6;
|
||||
extern const image_t img_6x8_5;
|
||||
extern const image_t img_6x8_4;
|
||||
extern const image_t img_6x8_3;
|
||||
extern const image_t img_6x8_2;
|
||||
extern const image_t img_6x8_1;
|
||||
extern const image_t img_6x8_0;
|
||||
extern const image_t img_ecp_SDA;
|
||||
extern const image_t img_ecp_SCL;
|
||||
extern const image_t img_ecp_port;
|
||||
extern const image_t img_cc_pad_UD1;
|
||||
extern const image_t img_cc_pad_LR1;
|
||||
extern const image_t img_cc_btn_Y1;
|
||||
extern const image_t img_cc_btn_X1;
|
||||
extern const image_t img_cc_btn_B1;
|
||||
extern const image_t img_cc_btn_A1;
|
||||
extern const image_t img_6x8_D;
|
||||
extern const image_t img_csLogo_Small;
|
||||
extern const image_t img_3x5_v;
|
||||
extern const image_t img_3x5_9;
|
||||
extern const image_t img_3x5_8;
|
||||
extern const image_t img_3x5_7;
|
||||
extern const image_t img_3x5_6;
|
||||
extern const image_t img_3x5_5;
|
||||
extern const image_t img_3x5_4;
|
||||
extern const image_t img_3x5_3;
|
||||
extern const image_t img_3x5_2;
|
||||
extern const image_t img_3x5_1;
|
||||
extern const image_t img_3x5_0;
|
||||
extern const image_t img_key_Ui;
|
||||
extern const image_t img_key_OKi;
|
||||
extern const image_t img_RIP;
|
||||
extern const image_t img_cc_trg_R4;
|
||||
extern const image_t img_cc_trg_R3;
|
||||
extern const image_t img_cc_trg_R2;
|
||||
extern const image_t img_cc_trg_R1;
|
||||
extern const image_t img_cc_trg_L4;
|
||||
extern const image_t img_cc_trg_L3;
|
||||
extern const image_t img_cc_trg_L2;
|
||||
extern const image_t img_cc_trg_L1;
|
||||
extern const image_t img_cc_Joy;
|
||||
extern const image_t img_cc_Main;
|
||||
extern const image_t img_cc_Cable;
|
||||
extern const image_t img_key_Back;
|
||||
extern const image_t img_key_OK;
|
||||
extern const image_t img_6x8_Z;
|
||||
extern const image_t img_6x8_Y;
|
||||
extern const image_t img_6x8_X;
|
||||
extern const image_t img_key_U;
|
||||
extern const image_t img_key_D;
|
||||
extern const image_t img_csLogo_FULL;
|
||||
extern const image_t img_6x8_7;
|
||||
extern const image_t img_key_R;
|
||||
extern const image_t img_key_L;
|
||||
extern const image_t img_5x7_7;
|
||||
extern const image_t img_5x7_F;
|
||||
extern const image_t img_5x7_E;
|
||||
extern const image_t img_5x7_D;
|
||||
extern const image_t img_5x7_C;
|
||||
extern const image_t img_5x7_B;
|
||||
extern const image_t img_5x7_A;
|
||||
extern const image_t img_5x7_9;
|
||||
extern const image_t img_5x7_8;
|
||||
extern const image_t img_5x7_6;
|
||||
extern const image_t img_5x7_5;
|
||||
extern const image_t img_5x7_4;
|
||||
extern const image_t img_5x7_3;
|
||||
extern const image_t img_5x7_2;
|
||||
extern const image_t img_5x7_1;
|
||||
extern const image_t img_5x7_0;
|
||||
extern const image_t img_6x8_v;
|
||||
extern const image_t img_6x8_n;
|
||||
extern const image_t img_6x8_G;
|
||||
extern const image_t img_6x8_F;
|
||||
extern const image_t img_6x8_E;
|
||||
extern const image_t img_6x8_d;
|
||||
extern const image_t img_6x8_C;
|
||||
extern const image_t img_6x8_B;
|
||||
extern const image_t img_6x8_A;
|
||||
extern const image_t img_6x8_9;
|
||||
extern const image_t img_6x8_8;
|
||||
extern const image_t img_6x8_6;
|
||||
extern const image_t img_6x8_5;
|
||||
extern const image_t img_6x8_4;
|
||||
extern const image_t img_6x8_3;
|
||||
extern const image_t img_6x8_2;
|
||||
extern const image_t img_6x8_1;
|
||||
extern const image_t img_6x8_0;
|
||||
extern const image_t img_ecp_SDA;
|
||||
extern const image_t img_ecp_SCL;
|
||||
extern const image_t img_ecp_port;
|
||||
extern const image_t img_cc_pad_UD1;
|
||||
extern const image_t img_cc_pad_LR1;
|
||||
extern const image_t img_cc_btn_Y1;
|
||||
extern const image_t img_cc_btn_X1;
|
||||
extern const image_t img_cc_btn_B1;
|
||||
extern const image_t img_cc_btn_A1;
|
||||
extern const image_t img_6x8_D;
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
#ifndef IMGTEST
|
||||
# include <gui/gui.h>
|
||||
void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
|
||||
const image_t* img, const showMode_t mode) ;
|
||||
#include <gui/gui.h>
|
||||
void show(
|
||||
Canvas* const canvas,
|
||||
const uint8_t tlx,
|
||||
const uint8_t tly,
|
||||
const image_t* img,
|
||||
const showMode_t mode);
|
||||
#endif
|
||||
|
||||
#endif //IMAGES_H_
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_0 = { 3, 5, false, 2, 0, {
|
||||
0xF6, 0xDE
|
||||
}};
|
||||
const image_t img_3x5_0 = {3, 5, false, 2, 0, {0xF6, 0xDE}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_1 = { 3, 5, false, 2, 0, {
|
||||
0xC9, 0x2E
|
||||
}};
|
||||
const image_t img_3x5_1 = {3, 5, false, 2, 0, {0xC9, 0x2E}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_2 = { 3, 5, false, 2, 0, {
|
||||
0xE7, 0xCE
|
||||
}};
|
||||
const image_t img_3x5_2 = {3, 5, false, 2, 0, {0xE7, 0xCE}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_3 = { 3, 5, false, 2, 0, {
|
||||
0xE5, 0x9E
|
||||
}};
|
||||
const image_t img_3x5_3 = {3, 5, false, 2, 0, {0xE5, 0x9E}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_4 = { 3, 5, false, 2, 0, {
|
||||
0x97, 0x92
|
||||
}};
|
||||
const image_t img_3x5_4 = {3, 5, false, 2, 0, {0x97, 0x92}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_5 = { 3, 5, false, 2, 0, {
|
||||
0xF3, 0x9E
|
||||
}};
|
||||
const image_t img_3x5_5 = {3, 5, false, 2, 0, {0xF3, 0x9E}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_6 = { 3, 5, false, 2, 0, {
|
||||
0xD3, 0xDE
|
||||
}};
|
||||
const image_t img_3x5_6 = {3, 5, false, 2, 0, {0xD3, 0xDE}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_7 = { 3, 5, false, 2, 0, {
|
||||
0xE5, 0x24
|
||||
}};
|
||||
const image_t img_3x5_7 = {3, 5, false, 2, 0, {0xE5, 0x24}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_8 = { 3, 5, false, 2, 0, {
|
||||
0xF7, 0xDE
|
||||
}};
|
||||
const image_t img_3x5_8 = {3, 5, false, 2, 0, {0xF7, 0xDE}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_9 = { 3, 5, false, 2, 0, {
|
||||
0xF7, 0x96
|
||||
}};
|
||||
const image_t img_3x5_9 = {3, 5, false, 2, 0, {0xF7, 0x96}};
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_3x5_v = { 3, 5, false, 2, 0, {
|
||||
0x02, 0xD4
|
||||
}};
|
||||
const image_t img_3x5_v = {3, 5, false, 2, 0, {0x02, 0xD4}};
|
||||
|
||||
@@ -8,6 +8,4 @@
|
||||
|
||||
#include "images.h"
|
||||
|
||||
const image_t img_5x7_0 = { 5, 7, false, 5, 0, {
|
||||
0x74, 0x67, 0x5C, 0xC5, 0xC0
|
||||
}};
|
||||
const image_t img_5x7_0 = {5, 7, false, 5, 0, {0x74, 0x67, 0x5C, 0xC5, 0xC0}};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user