mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-14 10:38:35 -07:00
drop useless file and some useless line
This commit is contained in:
@@ -95,14 +95,13 @@ BadBleApp* bad_ble_app_alloc(char* arg) {
|
|||||||
view_dispatcher_set_navigation_event_callback(
|
view_dispatcher_set_navigation_event_callback(
|
||||||
app->view_dispatcher, bad_ble_app_back_event_callback);
|
app->view_dispatcher, bad_ble_app_back_event_callback);
|
||||||
|
|
||||||
|
|
||||||
Bt* bt = furi_record_open(RECORD_BT);
|
Bt* bt = furi_record_open(RECORD_BT);
|
||||||
app->bt = bt;
|
app->bt = bt;
|
||||||
const char *adv_name = bt_get_profile_adv_name(bt);
|
const char* adv_name = bt_get_profile_adv_name(bt);
|
||||||
memcpy(app->name, adv_name, BAD_BLE_ADV_NAME_MAX_LEN);
|
memcpy(app->name, adv_name, BAD_BLE_ADV_NAME_MAX_LEN);
|
||||||
|
|
||||||
const uint8_t *mac_addr = bt_get_profile_mac_address(bt);
|
const uint8_t* mac_addr = bt_get_profile_mac_address(bt);
|
||||||
memcpy(app->mac, mac_addr, BAD_BLE_MAC_ADDRESS_LEN);
|
memcpy(app->mac, mac_addr, BAD_BLE_MAC_ADDRESS_LEN);
|
||||||
|
|
||||||
// Custom Widget
|
// Custom Widget
|
||||||
app->widget = widget_alloc();
|
app->widget = widget_alloc();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct BadBleApp BadBleApp;
|
typedef struct BadBleApp BadBleApp;
|
||||||
|
|
||||||
void bad_ble_set_name(BadBleApp *app, const char* fmt, ...);
|
void bad_ble_set_name(BadBleApp* app, const char* fmt, ...);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
#define BAD_BLE_APP_SCRIPT_EXTENSION ".txt"
|
#define BAD_BLE_APP_SCRIPT_EXTENSION ".txt"
|
||||||
#define BAD_BLE_APP_LAYOUT_EXTENSION ".kl"
|
#define BAD_BLE_APP_LAYOUT_EXTENSION ".kl"
|
||||||
|
|
||||||
#define BAD_BLE_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro
|
#define BAD_BLE_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro
|
||||||
#define BAD_BLE_ADV_NAME_MAX_LEN 18
|
#define BAD_BLE_ADV_NAME_MAX_LEN 18
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BadBleAppErrorNoFiles,
|
BadBleAppErrorNoFiles,
|
||||||
@@ -40,12 +40,11 @@ struct BadBleApp {
|
|||||||
Widget* widget;
|
Widget* widget;
|
||||||
Submenu* submenu;
|
Submenu* submenu;
|
||||||
|
|
||||||
TextInput *text_input;
|
TextInput* text_input;
|
||||||
ByteInput *byte_input;
|
ByteInput* byte_input;
|
||||||
uint8_t mac[BAD_BLE_MAC_ADDRESS_LEN];
|
uint8_t mac[BAD_BLE_MAC_ADDRESS_LEN];
|
||||||
char name[BAD_BLE_ADV_NAME_MAX_LEN + 1];
|
char name[BAD_BLE_ADV_NAME_MAX_LEN + 1];
|
||||||
|
|
||||||
|
|
||||||
BadBleAppError error;
|
BadBleAppError error;
|
||||||
FuriString* file_path;
|
FuriString* file_path;
|
||||||
FuriString* keyboard_layout;
|
FuriString* keyboard_layout;
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ typedef enum {
|
|||||||
WorkerEvtDisconnect = (1 << 3),
|
WorkerEvtDisconnect = (1 << 3),
|
||||||
} WorkerEvtFlags;
|
} WorkerEvtFlags;
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LevelRssi122_100,
|
LevelRssi122_100,
|
||||||
LevelRssi99_80,
|
LevelRssi99_80,
|
||||||
@@ -41,12 +40,15 @@ typedef enum {
|
|||||||
LevelRssiError = 0xFF,
|
LevelRssiError = 0xFF,
|
||||||
} LevelRssiRange;
|
} LevelRssiRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delays for waiting between HID key press and key release
|
||||||
|
*/
|
||||||
const uint8_t bt_hid_delays[LevelRssiNum] = {
|
const uint8_t bt_hid_delays[LevelRssiNum] = {
|
||||||
30, // LevelRssi122_100
|
30, // LevelRssi122_100
|
||||||
25, // LevelRssi99_80
|
25, // LevelRssi99_80
|
||||||
20, // LevelRssi79_60
|
20, // LevelRssi79_60
|
||||||
17, // LevelRssi59_40
|
17, // LevelRssi59_40
|
||||||
14, // LevelRssi39_0
|
14, // LevelRssi39_0
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BadBleScript {
|
struct BadBleScript {
|
||||||
@@ -161,31 +163,28 @@ static const uint8_t numpad_keys[10] = {
|
|||||||
|
|
||||||
uint8_t bt_timeout = 0;
|
uint8_t bt_timeout = 0;
|
||||||
|
|
||||||
static LevelRssiRange bt_remote_rssi_range(Bt *bt) {
|
static LevelRssiRange bt_remote_rssi_range(Bt* bt) {
|
||||||
|
BtRssi rssi_data = {0};
|
||||||
|
|
||||||
BtRssi rssi_data = { 0 };
|
if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError;
|
||||||
|
|
||||||
if (!bt_remote_rssi(bt, &rssi_data))
|
if(rssi_data.rssi <= 39)
|
||||||
return LevelRssiError;
|
|
||||||
|
|
||||||
if (rssi_data.rssi <= 39)
|
|
||||||
return LevelRssi39_0;
|
return LevelRssi39_0;
|
||||||
else if (rssi_data.rssi <= 59)
|
else if(rssi_data.rssi <= 59)
|
||||||
return LevelRssi59_40;
|
return LevelRssi59_40;
|
||||||
else if (rssi_data.rssi <= 79)
|
else if(rssi_data.rssi <= 79)
|
||||||
return LevelRssi79_60;
|
return LevelRssi79_60;
|
||||||
else if (rssi_data.rssi <= 99)
|
else if(rssi_data.rssi <= 99)
|
||||||
return LevelRssi99_80;
|
return LevelRssi99_80;
|
||||||
else if (rssi_data.rssi <= 122)
|
else if(rssi_data.rssi <= 122)
|
||||||
return LevelRssi122_100;
|
return LevelRssi122_100;
|
||||||
|
|
||||||
return LevelRssiError;
|
return LevelRssiError;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void update_bt_timeout(Bt *bt) {
|
static inline void update_bt_timeout(Bt* bt) {
|
||||||
|
|
||||||
LevelRssiRange r = bt_remote_rssi_range(bt);
|
LevelRssiRange r = bt_remote_rssi_range(bt);
|
||||||
if (r < LevelRssiNum) {
|
if(r < LevelRssiNum) {
|
||||||
bt_timeout = bt_hid_delays[r];
|
bt_timeout = bt_hid_delays[r];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,7 +296,7 @@ static bool ducky_string(BadBleScript* bad_ble, const char* param) {
|
|||||||
if(keycode != HID_KEYBOARD_NONE) {
|
if(keycode != HID_KEYBOARD_NONE) {
|
||||||
bt_hid_hold_while_keyboard_buffer_full(1, -1);
|
bt_hid_hold_while_keyboard_buffer_full(1, -1);
|
||||||
furi_hal_bt_hid_kb_press(keycode);
|
furi_hal_bt_hid_kb_press(keycode);
|
||||||
|
|
||||||
furi_delay_ms(bt_timeout);
|
furi_delay_ms(bt_timeout);
|
||||||
furi_hal_bt_hid_kb_release(keycode);
|
furi_hal_bt_hid_kb_release(keycode);
|
||||||
}
|
}
|
||||||
@@ -330,8 +329,6 @@ static int32_t
|
|||||||
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
|
|
||||||
|
|
||||||
// General commands
|
// General commands
|
||||||
if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
|
if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
|
||||||
// REM - comment line
|
// REM - comment line
|
||||||
@@ -426,7 +423,6 @@ static int32_t
|
|||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
key |= ducky_get_keycode(bad_ble, line_tmp, true);
|
key |= ducky_get_keycode(bad_ble, line_tmp, true);
|
||||||
}
|
}
|
||||||
FURI_LOG_I(WORKER_TAG, "Special key pressed %x\r\n", key);
|
|
||||||
furi_hal_bt_hid_kb_press(key);
|
furi_hal_bt_hid_kb_press(key);
|
||||||
|
|
||||||
furi_delay_ms(bt_timeout);
|
furi_delay_ms(bt_timeout);
|
||||||
@@ -545,7 +541,7 @@ static void bad_ble_hid_state_callback(BtStatus status, void* context) {
|
|||||||
|
|
||||||
if(state == true) {
|
if(state == true) {
|
||||||
LevelRssiRange r = bt_remote_rssi_range(bad_ble->bt);
|
LevelRssiRange r = bt_remote_rssi_range(bad_ble->bt);
|
||||||
if (r != LevelRssiError) {
|
if(r != LevelRssiError) {
|
||||||
bt_timeout = bt_hid_delays[r];
|
bt_timeout = bt_hid_delays[r];
|
||||||
}
|
}
|
||||||
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect);
|
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect);
|
||||||
@@ -559,9 +555,9 @@ static int32_t bad_ble_worker(void* context) {
|
|||||||
BadBleWorkerState worker_state = BadBleStateInit;
|
BadBleWorkerState worker_state = BadBleStateInit;
|
||||||
int32_t delay_val = 0;
|
int32_t delay_val = 0;
|
||||||
|
|
||||||
|
// BLE HID init
|
||||||
bt_timeout = bt_hid_delays[LevelRssi39_0];
|
bt_timeout = bt_hid_delays[LevelRssi39_0];
|
||||||
|
|
||||||
// init ble hid
|
|
||||||
bt_disconnect(bad_ble->bt);
|
bt_disconnect(bad_ble->bt);
|
||||||
|
|
||||||
// Wait 2nd core to update nvm storage
|
// Wait 2nd core to update nvm storage
|
||||||
@@ -569,10 +565,6 @@ static int32_t bad_ble_worker(void* context) {
|
|||||||
|
|
||||||
bt_keys_storage_set_storage_path(bad_ble->bt, HID_BT_KEYS_STORAGE_PATH);
|
bt_keys_storage_set_storage_path(bad_ble->bt, HID_BT_KEYS_STORAGE_PATH);
|
||||||
|
|
||||||
bt_set_profile_adv_name(bad_ble->bt, "Keyboard K99");
|
|
||||||
|
|
||||||
//furi_hal_bt_set_profile_adv_name("Keyboard K99", FuriHalBtProfileHidKeyboard);
|
|
||||||
|
|
||||||
if(!bt_set_profile(bad_ble->bt, BtProfileHidKeyboard)) {
|
if(!bt_set_profile(bad_ble->bt, BtProfileHidKeyboard)) {
|
||||||
FURI_LOG_E(TAG, "Failed to switch to HID profile");
|
FURI_LOG_E(TAG, "Failed to switch to HID profile");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -727,7 +719,7 @@ static int32_t bad_ble_worker(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_bt_timeout(bad_ble->bt);
|
update_bt_timeout(bad_ble->bt);
|
||||||
FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout);
|
FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// release all keys
|
// release all keys
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ typedef struct {
|
|||||||
char error[64];
|
char error[64];
|
||||||
} BadBleState;
|
} BadBleState;
|
||||||
|
|
||||||
BadBleScript* bad_ble_script_open(FuriString* file_path, Bt *bt);
|
BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt);
|
||||||
|
|
||||||
void bad_ble_script_close(BadBleScript* bad_ble);
|
void bad_ble_script_close(BadBleScript* bad_ble);
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ bool bad_ble_scene_config_on_event(void* context, SceneManagerEvent event) {
|
|||||||
consumed = true;
|
consumed = true;
|
||||||
if(event.event == SubmenuIndexKeyboardLayout) {
|
if(event.event == SubmenuIndexKeyboardLayout) {
|
||||||
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigLayout);
|
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigLayout);
|
||||||
} else if (event.event == SubmenuIndexAdvertisementName) {
|
} else if(event.event == SubmenuIndexAdvertisementName) {
|
||||||
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigName);
|
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigName);
|
||||||
} else if (event.event == SubmenuIndexMacAddress) {
|
} else if(event.event == SubmenuIndexMacAddress) {
|
||||||
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigMac);
|
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigMac);
|
||||||
} else {
|
} else {
|
||||||
furi_crash("Unknown key type");
|
furi_crash("Unknown key type");
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ bool bad_ble_scene_config_name_on_event(void* context, SceneManagerEvent event)
|
|||||||
if(event.event == BadBleAppCustomEventTextEditResult) {
|
if(event.event == BadBleAppCustomEventTextEditResult) {
|
||||||
bt_set_profile_adv_name(bad_ble->bt, bad_ble->name);
|
bt_set_profile_adv_name(bad_ble->bt, bad_ble->name);
|
||||||
}
|
}
|
||||||
scene_manager_previous_scene(
|
scene_manager_previous_scene(bad_ble->scene_manager);
|
||||||
bad_ble->scene_manager);
|
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void gpio_scene_start_var_list_change_callback(VariableItem* item) {
|
||||||
gpio_scene_start_var_list_change_callback(VariableItem* item) {
|
|
||||||
GpioApp* app = variable_item_get_context(item);
|
GpioApp* app = variable_item_get_context(item);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
|||||||
@@ -486,8 +486,7 @@ int32_t dap_link_app(void* p) {
|
|||||||
if(furi_hal_usb_is_locked()) {
|
if(furi_hal_usb_is_locked()) {
|
||||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
DialogMessage* message = dialog_message_alloc();
|
DialogMessage* message = dialog_message_alloc();
|
||||||
dialog_message_set_header(
|
dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
|
||||||
message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
|
|
||||||
dialog_message_set_text(
|
dialog_message_set_text(
|
||||||
message,
|
message,
|
||||||
"Disconnect from\nPC or phone to\nuse this function.",
|
"Disconnect from\nPC or phone to\nuse this function.",
|
||||||
|
|||||||
@@ -467,7 +467,6 @@ int32_t dice_app(void* p) {
|
|||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
ViewPort* view_port = view_port_alloc();
|
||||||
view_port_draw_callback_set(view_port, dice_render_callback, plugin_state);
|
view_port_draw_callback_set(view_port, dice_render_callback, plugin_state);
|
||||||
view_port_input_callback_set(view_port, dice_input_callback, plugin_state->event_queue);
|
view_port_input_callback_set(view_port, dice_input_callback, plugin_state->event_queue);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ int32_t orgasmotron_app(void* p) {
|
|||||||
|
|
||||||
PluginState* plugin_state = malloc(sizeof(PluginState));
|
PluginState* plugin_state = malloc(sizeof(PluginState));
|
||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if (!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
|
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
|
||||||
FURI_LOG_E("Orgasmatron", "cannot create mutex\r\n");
|
FURI_LOG_E("Orgasmatron", "cannot create mutex\r\n");
|
||||||
free(plugin_state);
|
free(plugin_state);
|
||||||
return 255;
|
return 255;
|
||||||
@@ -61,10 +61,10 @@ int32_t orgasmotron_app(void* p) {
|
|||||||
//int mode = 0;
|
//int mode = 0;
|
||||||
bool processing = true;
|
bool processing = true;
|
||||||
//while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
//while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||||
while (processing) {
|
while(processing) {
|
||||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||||
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
|
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
|
||||||
if (event_status == FuriStatusOk) {
|
if(event_status == FuriStatusOk) {
|
||||||
if(event.key == InputKeyBack && event.type == InputTypeShort) {
|
if(event.key == InputKeyBack && event.type == InputTypeShort) {
|
||||||
//Exit Application
|
//Exit Application
|
||||||
notification_message(notification, &sequence_reset_vibro);
|
notification_message(notification, &sequence_reset_vibro);
|
||||||
@@ -73,53 +73,58 @@ int32_t orgasmotron_app(void* p) {
|
|||||||
processing = false;
|
processing = false;
|
||||||
//break;
|
//break;
|
||||||
}
|
}
|
||||||
if(event.key == InputKeyOk && (event.type == InputTypePress || event.type == InputTypeRelease)) {
|
if(event.key == InputKeyOk &&
|
||||||
|
(event.type == InputTypePress || event.type == InputTypeRelease)) {
|
||||||
plugin_state->mode = 0;
|
plugin_state->mode = 0;
|
||||||
}
|
}
|
||||||
if(event.key == InputKeyLeft && (event.type == InputTypePress || event.type == InputTypeRelease)) {
|
if(event.key == InputKeyLeft &&
|
||||||
|
(event.type == InputTypePress || event.type == InputTypeRelease)) {
|
||||||
plugin_state->mode = 1;
|
plugin_state->mode = 1;
|
||||||
}
|
}
|
||||||
if(event.key == InputKeyRight && (event.type == InputTypePress || event.type == InputTypeRelease)) {
|
if(event.key == InputKeyRight &&
|
||||||
|
(event.type == InputTypePress || event.type == InputTypeRelease)) {
|
||||||
plugin_state->mode = 3;
|
plugin_state->mode = 3;
|
||||||
}
|
}
|
||||||
if(event.key == InputKeyUp && (event.type == InputTypePress || event.type == InputTypeRelease)) {
|
if(event.key == InputKeyUp &&
|
||||||
|
(event.type == InputTypePress || event.type == InputTypeRelease)) {
|
||||||
plugin_state->mode = 2;
|
plugin_state->mode = 2;
|
||||||
}
|
}
|
||||||
if(event.key == InputKeyDown && (event.type == InputTypePress || event.type == InputTypeRelease)) {
|
if(event.key == InputKeyDown &&
|
||||||
|
(event.type == InputTypePress || event.type == InputTypeRelease)) {
|
||||||
plugin_state->mode = 4;
|
plugin_state->mode = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin_state->mode == 0) {
|
if(plugin_state->mode == 0) {
|
||||||
//Stop Vibration
|
//Stop Vibration
|
||||||
notification_message(notification, &sequence_reset_vibro);
|
notification_message(notification, &sequence_reset_vibro);
|
||||||
notification_message(notification, &sequence_reset_green);
|
notification_message(notification, &sequence_reset_green);
|
||||||
} else if (plugin_state->mode == 1) {
|
} else if(plugin_state->mode == 1) {
|
||||||
//Full power
|
//Full power
|
||||||
notification_message(notification, &sequence_set_vibro_on);
|
notification_message(notification, &sequence_set_vibro_on);
|
||||||
notification_message(notification, &sequence_set_green_255);
|
notification_message(notification, &sequence_set_green_255);
|
||||||
} else if (plugin_state->mode == 2) {
|
} else if(plugin_state->mode == 2) {
|
||||||
//Pulsed Vibration
|
//Pulsed Vibration
|
||||||
notification_message(notification, &sequence_set_vibro_on);
|
notification_message(notification, &sequence_set_vibro_on);
|
||||||
notification_message(notification, &sequence_set_green_255);
|
notification_message(notification, &sequence_set_green_255);
|
||||||
delay(100);
|
delay(100);
|
||||||
notification_message(notification, &sequence_reset_vibro);
|
notification_message(notification, &sequence_reset_vibro);
|
||||||
} else if (plugin_state->mode == 3) {
|
} else if(plugin_state->mode == 3) {
|
||||||
//Soft power
|
//Soft power
|
||||||
notification_message(notification, &sequence_set_vibro_on);
|
notification_message(notification, &sequence_set_vibro_on);
|
||||||
notification_message(notification, &sequence_set_green_255);
|
notification_message(notification, &sequence_set_green_255);
|
||||||
delay(50);
|
delay(50);
|
||||||
notification_message(notification, &sequence_reset_vibro);
|
notification_message(notification, &sequence_reset_vibro);
|
||||||
} else if (plugin_state->mode == 4) {
|
} else if(plugin_state->mode == 4) {
|
||||||
//Special Sequence
|
//Special Sequence
|
||||||
for (int i = 0;i < 15;i++) {
|
for(int i = 0; i < 15; i++) {
|
||||||
notification_message(notification, &sequence_set_vibro_on);
|
notification_message(notification, &sequence_set_vibro_on);
|
||||||
notification_message(notification, &sequence_set_green_255);
|
notification_message(notification, &sequence_set_green_255);
|
||||||
delay(50);
|
delay(50);
|
||||||
notification_message(notification, &sequence_reset_vibro);
|
notification_message(notification, &sequence_reset_vibro);
|
||||||
delay(50);
|
delay(50);
|
||||||
}
|
}
|
||||||
for (int i = 0;i < 2;i++) {
|
for(int i = 0; i < 2; i++) {
|
||||||
notification_message(notification, &sequence_set_vibro_on);
|
notification_message(notification, &sequence_set_vibro_on);
|
||||||
notification_message(notification, &sequence_set_green_255);
|
notification_message(notification, &sequence_set_green_255);
|
||||||
delay(400);
|
delay(400);
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ extern const SubGhzProtocolRegistry protoview_protocol_registry;
|
|||||||
/* The callback actually just passes the control to the actual active
|
/* The callback actually just passes the control to the actual active
|
||||||
* view callback, after setting up basic stuff like cleaning the screen
|
* view callback, after setting up basic stuff like cleaning the screen
|
||||||
* and setting color to black. */
|
* and setting color to black. */
|
||||||
static void render_callback(Canvas *const canvas, void *ctx) {
|
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||||
ProtoViewApp *app = ctx;
|
ProtoViewApp* app = ctx;
|
||||||
|
|
||||||
/* Clear screen. */
|
/* Clear screen. */
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
@@ -51,14 +51,25 @@ static void render_callback(Canvas *const canvas, void *ctx) {
|
|||||||
|
|
||||||
/* Call who is in charge right now. */
|
/* Call who is in charge right now. */
|
||||||
switch(app->current_view) {
|
switch(app->current_view) {
|
||||||
case ViewRawPulses: render_view_raw_pulses(canvas,app); break;
|
case ViewRawPulses:
|
||||||
case ViewInfo: render_view_info(canvas,app); break;
|
render_view_raw_pulses(canvas, app);
|
||||||
|
break;
|
||||||
|
case ViewInfo:
|
||||||
|
render_view_info(canvas, app);
|
||||||
|
break;
|
||||||
case ViewFrequencySettings:
|
case ViewFrequencySettings:
|
||||||
case ViewModulationSettings:
|
case ViewModulationSettings:
|
||||||
render_view_settings(canvas,app); break;
|
render_view_settings(canvas, app);
|
||||||
case ViewDirectSampling: render_view_direct_sampling(canvas,app); break;
|
break;
|
||||||
case ViewBuildMessage: render_view_build_message(canvas,app); break;
|
case ViewDirectSampling:
|
||||||
default: furi_crash(TAG "Invalid view selected"); break;
|
render_view_direct_sampling(canvas, app);
|
||||||
|
break;
|
||||||
|
case ViewBuildMessage:
|
||||||
|
render_view_build_message(canvas, app);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
furi_crash(TAG "Invalid view selected");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw the alert box if set. */
|
/* Draw the alert box if set. */
|
||||||
@@ -67,10 +78,9 @@ static void render_callback(Canvas *const canvas, void *ctx) {
|
|||||||
|
|
||||||
/* Here all we do is putting the events into the queue that will be handled
|
/* 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. */
|
* in the while() loop of the app entry point function. */
|
||||||
static void input_callback(InputEvent* input_event, void* ctx)
|
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||||
{
|
ProtoViewApp* app = ctx;
|
||||||
ProtoViewApp *app = ctx;
|
furi_message_queue_put(app->event_queue, input_event, FuriWaitForever);
|
||||||
furi_message_queue_put(app->event_queue,input_event,FuriWaitForever);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called to switch view (when left/right is pressed). Handles
|
/* Called to switch view (when left/right is pressed). Handles
|
||||||
@@ -80,15 +90,15 @@ static void input_callback(InputEvent* input_event, void* ctx)
|
|||||||
* The 'switchto' parameter can be the identifier of a view, or the
|
* The 'switchto' parameter can be the identifier of a view, or the
|
||||||
* special views ViewGoNext and ViewGoPrev in order to move to
|
* special views ViewGoNext and ViewGoPrev in order to move to
|
||||||
* the logical next/prev view. */
|
* the logical next/prev view. */
|
||||||
static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
|
static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) {
|
||||||
/* Switch to the specified view. */
|
/* Switch to the specified view. */
|
||||||
ProtoViewCurrentView old = app->current_view;
|
ProtoViewCurrentView old = app->current_view;
|
||||||
if (switchto == ViewGoNext) {
|
if(switchto == ViewGoNext) {
|
||||||
app->current_view++;
|
app->current_view++;
|
||||||
if (app->current_view == ViewLast) app->current_view = 0;
|
if(app->current_view == ViewLast) app->current_view = 0;
|
||||||
} else if (switchto == ViewGoPrev) {
|
} else if(switchto == ViewGoPrev) {
|
||||||
if (app->current_view == 0)
|
if(app->current_view == 0)
|
||||||
app->current_view = ViewLast-1;
|
app->current_view = ViewLast - 1;
|
||||||
else
|
else
|
||||||
app->current_view--;
|
app->current_view--;
|
||||||
} else {
|
} else {
|
||||||
@@ -103,20 +113,20 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
|
|||||||
|
|
||||||
/* Reset the view private data each time, before calling the enter/exit
|
/* Reset the view private data each time, before calling the enter/exit
|
||||||
* callbacks that may want to setup some state. */
|
* callbacks that may want to setup some state. */
|
||||||
memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN);
|
memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN);
|
||||||
|
|
||||||
/* Call the enter/exit view callbacks if needed. */
|
/* Call the enter/exit view callbacks if needed. */
|
||||||
if (old == ViewDirectSampling) view_exit_direct_sampling(app);
|
if(old == ViewDirectSampling) view_exit_direct_sampling(app);
|
||||||
if (new == ViewDirectSampling) view_enter_direct_sampling(app);
|
if(new == ViewDirectSampling) view_enter_direct_sampling(app);
|
||||||
if (old == ViewBuildMessage) view_exit_build_message(app);
|
if(old == ViewBuildMessage) view_exit_build_message(app);
|
||||||
if (new == ViewBuildMessage) view_enter_build_message(app);
|
if(new == ViewBuildMessage) view_enter_build_message(app);
|
||||||
if (old == ViewInfo) view_exit_info(app);
|
if(old == ViewInfo) view_exit_info(app);
|
||||||
|
|
||||||
/* The frequency/modulation settings are actually a single view:
|
/* The frequency/modulation settings are actually a single view:
|
||||||
* as long as the user stays between the two modes of this view we
|
* as long as the user stays between the two modes of this view we
|
||||||
* don't need to call the exit-view callback. */
|
* don't need to call the exit-view callback. */
|
||||||
if ((old == ViewFrequencySettings && new != ViewModulationSettings) ||
|
if((old == ViewFrequencySettings && new != ViewModulationSettings) ||
|
||||||
(old == ViewModulationSettings && new != ViewFrequencySettings))
|
(old == ViewModulationSettings && new != ViewFrequencySettings))
|
||||||
view_exit_settings(app);
|
view_exit_settings(app);
|
||||||
|
|
||||||
ui_dismiss_alert(app);
|
ui_dismiss_alert(app);
|
||||||
@@ -125,7 +135,7 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
|
|||||||
/* Allocate the application state and initialize a number of stuff.
|
/* Allocate the application state and initialize a number of stuff.
|
||||||
* This is called in the entry point to create the application state. */
|
* This is called in the entry point to create the application state. */
|
||||||
ProtoViewApp* protoview_app_alloc() {
|
ProtoViewApp* protoview_app_alloc() {
|
||||||
ProtoViewApp *app = malloc(sizeof(ProtoViewApp));
|
ProtoViewApp* app = malloc(sizeof(ProtoViewApp));
|
||||||
|
|
||||||
// Init shared data structures
|
// Init shared data structures
|
||||||
RawSamples = raw_samples_alloc();
|
RawSamples = raw_samples_alloc();
|
||||||
@@ -148,10 +158,10 @@ ProtoViewApp* protoview_app_alloc() {
|
|||||||
app->show_text_input = false;
|
app->show_text_input = false;
|
||||||
app->alert_dismiss_time = 0;
|
app->alert_dismiss_time = 0;
|
||||||
app->current_view = ViewRawPulses;
|
app->current_view = ViewRawPulses;
|
||||||
for (int j = 0; j < ViewLast; j++) app->current_subview[j] = 0;
|
for(int j = 0; j < ViewLast; j++) app->current_subview[j] = 0;
|
||||||
app->direct_sampling_enabled = false;
|
app->direct_sampling_enabled = false;
|
||||||
app->view_privdata = malloc(PROTOVIEW_VIEW_PRIVDATA_LEN);
|
app->view_privdata = malloc(PROTOVIEW_VIEW_PRIVDATA_LEN);
|
||||||
memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN);
|
memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN);
|
||||||
|
|
||||||
// Signal found and visualization defaults
|
// Signal found and visualization defaults
|
||||||
app->signal_bestlen = 0;
|
app->signal_bestlen = 0;
|
||||||
@@ -176,17 +186,14 @@ ProtoViewApp* protoview_app_alloc() {
|
|||||||
app->txrx->environment = subghz_environment_alloc();
|
app->txrx->environment = subghz_environment_alloc();
|
||||||
subghz_environment_set_protocol_registry(
|
subghz_environment_set_protocol_registry(
|
||||||
app->txrx->environment, (void*)&protoview_protocol_registry);
|
app->txrx->environment, (void*)&protoview_protocol_registry);
|
||||||
app->txrx->receiver =
|
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||||
subghz_receiver_alloc_init(app->txrx->environment);
|
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||||
subghz_receiver_set_filter(app->txrx->receiver,
|
|
||||||
SubGhzProtocolFlag_Decodable);
|
|
||||||
subghz_worker_set_overrun_callback(
|
subghz_worker_set_overrun_callback(
|
||||||
app->txrx->worker,
|
app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||||
(SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
|
||||||
subghz_worker_set_pair_callback(
|
subghz_worker_set_pair_callback(
|
||||||
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||||
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
|
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
|
||||||
|
|
||||||
app->frequency = subghz_setting_get_default_frequency(app->setting);
|
app->frequency = subghz_setting_get_default_frequency(app->setting);
|
||||||
app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */
|
app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */
|
||||||
|
|
||||||
@@ -199,7 +206,7 @@ ProtoViewApp* protoview_app_alloc() {
|
|||||||
/* Free what the application allocated. It is not clear to me if the
|
/* 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
|
* Flipper OS, once the application exits, will be able to reclaim space
|
||||||
* even if we forget to free something here. */
|
* even if we forget to free something here. */
|
||||||
void protoview_app_free(ProtoViewApp *app) {
|
void protoview_app_free(ProtoViewApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
|
|
||||||
// Put CC1101 on sleep, this also restores charging.
|
// Put CC1101 on sleep, this also restores charging.
|
||||||
@@ -218,7 +225,7 @@ void protoview_app_free(ProtoViewApp *app) {
|
|||||||
subghz_setting_free(app->setting);
|
subghz_setting_free(app->setting);
|
||||||
|
|
||||||
// Worker stuff.
|
// Worker stuff.
|
||||||
if (!app->txrx->debug_timer_sampling) {
|
if(!app->txrx->debug_timer_sampling) {
|
||||||
subghz_receiver_free(app->txrx->receiver);
|
subghz_receiver_free(app->txrx->receiver);
|
||||||
subghz_environment_free(app->txrx->environment);
|
subghz_environment_free(app->txrx->environment);
|
||||||
subghz_worker_free(app->txrx->worker);
|
subghz_worker_free(app->txrx->worker);
|
||||||
@@ -236,8 +243,8 @@ void protoview_app_free(ProtoViewApp *app) {
|
|||||||
/* Called periodically. Do signal processing here. Data we process here
|
/* Called periodically. Do signal processing here. Data we process here
|
||||||
* will be later displayed by the render callback. The side effect of this
|
* will be later displayed by the render callback. The side effect of this
|
||||||
* function is to scan for signals and set DetectedSamples. */
|
* function is to scan for signals and set DetectedSamples. */
|
||||||
static void timer_callback(void *ctx) {
|
static void timer_callback(void* ctx) {
|
||||||
ProtoViewApp *app = ctx;
|
ProtoViewApp* app = ctx;
|
||||||
uint32_t delta, lastidx = app->signal_last_scan_idx;
|
uint32_t delta, lastidx = app->signal_last_scan_idx;
|
||||||
|
|
||||||
/* scan_for_signal(), called by this function, deals with a
|
/* scan_for_signal(), called by this function, deals with a
|
||||||
@@ -245,14 +252,14 @@ static void timer_callback(void *ctx) {
|
|||||||
* cross-boundaries, it is enough if we scan each time the buffer fills
|
* 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
|
* 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. */
|
* can avoid scanning too many times to just find the same data. */
|
||||||
if (lastidx < RawSamples->idx) {
|
if(lastidx < RawSamples->idx) {
|
||||||
delta = RawSamples->idx - lastidx;
|
delta = RawSamples->idx - lastidx;
|
||||||
} else {
|
} else {
|
||||||
delta = RawSamples->total - lastidx + RawSamples->idx;
|
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;
|
app->signal_last_scan_idx = RawSamples->idx;
|
||||||
scan_for_signal(app,RawSamples);
|
scan_for_signal(app, RawSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the navigation callback we use in the view dispatcher used
|
/* This is the navigation callback we use in the view dispatcher used
|
||||||
@@ -265,7 +272,7 @@ static void timer_callback(void *ctx) {
|
|||||||
* We just need a dummy callback returning false. We believe the
|
* We just need a dummy callback returning false. We believe the
|
||||||
* implementation should be changed and if no callback is set, it should be
|
* implementation should be changed and if no callback is set, it should be
|
||||||
* the same as returning false. */
|
* the same as returning false. */
|
||||||
static bool keyboard_view_dispatcher_navigation_callback(void *ctx) {
|
static bool keyboard_view_dispatcher_navigation_callback(void* ctx) {
|
||||||
UNUSED(ctx);
|
UNUSED(ctx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -273,10 +280,10 @@ static bool keyboard_view_dispatcher_navigation_callback(void *ctx) {
|
|||||||
/* App entry point, as specified in application.fam. */
|
/* App entry point, as specified in application.fam. */
|
||||||
int32_t protoview_app_entry(void* p) {
|
int32_t protoview_app_entry(void* p) {
|
||||||
UNUSED(p);
|
UNUSED(p);
|
||||||
ProtoViewApp *app = protoview_app_alloc();
|
ProtoViewApp* app = protoview_app_alloc();
|
||||||
|
|
||||||
/* Create a timer. We do data analysis in the callback. */
|
/* 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);
|
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
|
||||||
|
|
||||||
/* Start listening to signals immediately. */
|
/* Start listening to signals immediately. */
|
||||||
@@ -291,71 +298,68 @@ int32_t protoview_app_entry(void* p) {
|
|||||||
InputEvent input;
|
InputEvent input;
|
||||||
while(app->running) {
|
while(app->running) {
|
||||||
FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
|
FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
|
||||||
if (qstat == FuriStatusOk) {
|
if(qstat == FuriStatusOk) {
|
||||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u",
|
if(DEBUG_MSG)
|
||||||
input.type, input.key);
|
FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
|
||||||
|
|
||||||
/* Handle navigation here. Then handle view-specific inputs
|
/* Handle navigation here. Then handle view-specific inputs
|
||||||
* in the view specific handling function. */
|
* in the view specific handling function. */
|
||||||
if (input.type == InputTypeShort &&
|
if(input.type == InputTypeShort && input.key == InputKeyBack) {
|
||||||
input.key == InputKeyBack)
|
if(app->current_view != ViewRawPulses) {
|
||||||
{
|
|
||||||
if (app->current_view != ViewRawPulses) {
|
|
||||||
/* If this is not the main app view, go there. */
|
/* If this is not the main app view, go there. */
|
||||||
app_switch_view(app,ViewRawPulses);
|
app_switch_view(app, ViewRawPulses);
|
||||||
} else {
|
} else {
|
||||||
/* If we are in the main app view, warn the user
|
/* If we are in the main app view, warn the user
|
||||||
* they needs to long press to really quit. */
|
* they needs to long press to really quit. */
|
||||||
ui_show_alert(app,"Long press to exit",1000);
|
ui_show_alert(app, "Long press to exit", 1000);
|
||||||
}
|
}
|
||||||
} else if (input.type == InputTypeLong &&
|
} else if(input.type == InputTypeLong && input.key == InputKeyBack) {
|
||||||
input.key == InputKeyBack)
|
|
||||||
{
|
|
||||||
app->running = 0;
|
app->running = 0;
|
||||||
} else if (input.type == InputTypeShort &&
|
} else if(
|
||||||
input.key == InputKeyRight &&
|
input.type == InputTypeShort && input.key == InputKeyRight &&
|
||||||
ui_get_current_subview(app) == 0)
|
ui_get_current_subview(app) == 0) {
|
||||||
{
|
|
||||||
/* Go to the next view. */
|
/* Go to the next view. */
|
||||||
app_switch_view(app,ViewGoNext);
|
app_switch_view(app, ViewGoNext);
|
||||||
} else if (input.type == InputTypeShort &&
|
} else if(
|
||||||
input.key == InputKeyLeft &&
|
input.type == InputTypeShort && input.key == InputKeyLeft &&
|
||||||
ui_get_current_subview(app) == 0)
|
ui_get_current_subview(app) == 0) {
|
||||||
{
|
|
||||||
/* Go to the previous view. */
|
/* Go to the previous view. */
|
||||||
app_switch_view(app,ViewGoPrev);
|
app_switch_view(app, ViewGoPrev);
|
||||||
} else {
|
} else {
|
||||||
/* This is where we pass the control to the currently
|
/* This is where we pass the control to the currently
|
||||||
* active view input processing. */
|
* active view input processing. */
|
||||||
switch(app->current_view) {
|
switch(app->current_view) {
|
||||||
case ViewRawPulses:
|
case ViewRawPulses:
|
||||||
process_input_raw_pulses(app,input);
|
process_input_raw_pulses(app, input);
|
||||||
break;
|
break;
|
||||||
case ViewInfo:
|
case ViewInfo:
|
||||||
process_input_info(app,input);
|
process_input_info(app, input);
|
||||||
break;
|
break;
|
||||||
case ViewFrequencySettings:
|
case ViewFrequencySettings:
|
||||||
case ViewModulationSettings:
|
case ViewModulationSettings:
|
||||||
process_input_settings(app,input);
|
process_input_settings(app, input);
|
||||||
break;
|
break;
|
||||||
case ViewDirectSampling:
|
case ViewDirectSampling:
|
||||||
process_input_direct_sampling(app,input);
|
process_input_direct_sampling(app, input);
|
||||||
break;
|
break;
|
||||||
case ViewBuildMessage:
|
case ViewBuildMessage:
|
||||||
process_input_build_message(app,input);
|
process_input_build_message(app, input);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
furi_crash(TAG "Invalid view selected");
|
||||||
break;
|
break;
|
||||||
default: furi_crash(TAG "Invalid view selected"); break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Useful to understand if the app is still alive when it
|
/* Useful to understand if the app is still alive when it
|
||||||
* does not respond because of bugs. */
|
* does not respond because of bugs. */
|
||||||
if (DEBUG_MSG) {
|
if(DEBUG_MSG) {
|
||||||
static int c = 0; c++;
|
static int c = 0;
|
||||||
if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
|
c++;
|
||||||
|
if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (app->show_text_input) {
|
if(app->show_text_input) {
|
||||||
/* Remove our viewport: we need to use a view dispatcher
|
/* Remove our viewport: we need to use a view dispatcher
|
||||||
* in order to show the standard Flipper keyboard. */
|
* in order to show the standard Flipper keyboard. */
|
||||||
gui_remove_view_port(app->gui, app->view_port);
|
gui_remove_view_port(app->gui, app->view_port);
|
||||||
@@ -368,11 +372,11 @@ int32_t protoview_app_entry(void* p) {
|
|||||||
* otherwise when the user presses back on the keyboard to
|
* otherwise when the user presses back on the keyboard to
|
||||||
* abort, the dispatcher will not stop. */
|
* abort, the dispatcher will not stop. */
|
||||||
view_dispatcher_set_navigation_event_callback(
|
view_dispatcher_set_navigation_event_callback(
|
||||||
app->view_dispatcher,
|
app->view_dispatcher, keyboard_view_dispatcher_navigation_callback);
|
||||||
keyboard_view_dispatcher_navigation_callback);
|
|
||||||
app->text_input = text_input_alloc();
|
app->text_input = text_input_alloc();
|
||||||
view_dispatcher_set_event_callback_context(app->view_dispatcher,app);
|
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||||
view_dispatcher_add_view(app->view_dispatcher, 0, text_input_get_view(app->text_input));
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, 0, text_input_get_view(app->text_input));
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
||||||
|
|
||||||
/* Setup the text input view. The different parameters are set
|
/* Setup the text input view. The different parameters are set
|
||||||
@@ -388,7 +392,8 @@ int32_t protoview_app_entry(void* p) {
|
|||||||
false);
|
false);
|
||||||
|
|
||||||
/* Run the dispatcher with the keyboard. */
|
/* Run the dispatcher with the keyboard. */
|
||||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
view_dispatcher_attach_to_gui(
|
||||||
|
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||||
view_dispatcher_run(app->view_dispatcher);
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
/* Undo all it: remove the view from the dispatcher, free it
|
/* Undo all it: remove the view from the dispatcher, free it
|
||||||
@@ -406,7 +411,7 @@ int32_t protoview_app_entry(void* p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* App no longer running. Shut down and free. */
|
/* 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.");
|
FURI_LOG_E(TAG, "Putting CC1101 to sleep before exiting.");
|
||||||
radio_rx_end(app);
|
radio_rx_end(app);
|
||||||
radio_sleep(app);
|
radio_sleep(app);
|
||||||
@@ -416,4 +421,3 @@ int32_t protoview_app_entry(void* p) {
|
|||||||
protoview_app_free(app);
|
protoview_app_free(app);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,11 +66,11 @@ typedef enum {
|
|||||||
/* ================================== RX/TX ================================= */
|
/* ================================== RX/TX ================================= */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name; // Name to show to the user.
|
const char* name; // Name to show to the user.
|
||||||
const char *id; // Identifier in the Flipper API/file.
|
const char* id; // Identifier in the Flipper API/file.
|
||||||
FuriHalSubGhzPreset preset; // The preset ID.
|
FuriHalSubGhzPreset preset; // The preset ID.
|
||||||
uint8_t *custom; // If not null, a set of registers for
|
uint8_t* custom; // If not null, a set of registers for
|
||||||
// the CC1101, specifying a custom preset.
|
// the CC1101, specifying a custom preset.
|
||||||
} ProtoViewModulation;
|
} ProtoViewModulation;
|
||||||
|
|
||||||
extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */
|
extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */
|
||||||
@@ -79,19 +79,19 @@ extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */
|
|||||||
* It receives data and we get our protocol "feed" callback called
|
* It receives data and we get our protocol "feed" callback called
|
||||||
* with the level (1 or 0) and duration. */
|
* with the level (1 or 0) and duration. */
|
||||||
struct ProtoViewTxRx {
|
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
|
from the interface. There is to restart the
|
||||||
radio with the right parameters. */
|
radio with the right parameters. */
|
||||||
SubGhzWorker* worker; /* Our background worker. */
|
SubGhzWorker* worker; /* Our background worker. */
|
||||||
SubGhzEnvironment* environment;
|
SubGhzEnvironment* environment;
|
||||||
SubGhzReceiver* receiver;
|
SubGhzReceiver* receiver;
|
||||||
TxRxState txrx_state; /* Receiving, idle or sleeping? */
|
TxRxState txrx_state; /* Receiving, idle or sleeping? */
|
||||||
|
|
||||||
/* Timer sampling mode state. */
|
/* 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. */
|
for testing. */
|
||||||
uint32_t last_g0_change_time; /* Last high->low (or reverse) switch. */
|
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
|
checking the duration in the timer
|
||||||
handler. */
|
handler. */
|
||||||
};
|
};
|
||||||
@@ -103,44 +103,44 @@ typedef struct ProtoViewTxRx ProtoViewTxRx;
|
|||||||
#define ALERT_MAX_LEN 32
|
#define ALERT_MAX_LEN 32
|
||||||
struct ProtoViewApp {
|
struct ProtoViewApp {
|
||||||
/* GUI */
|
/* GUI */
|
||||||
Gui *gui;
|
Gui* gui;
|
||||||
NotificationApp *notification;
|
NotificationApp* notification;
|
||||||
ViewPort *view_port; /* We just use a raw viewport and we render
|
ViewPort* view_port; /* We just use a raw viewport and we render
|
||||||
everything into the low level canvas. */
|
everything into the low level canvas. */
|
||||||
ProtoViewCurrentView current_view; /* Active left-right view ID. */
|
ProtoViewCurrentView current_view; /* Active left-right view ID. */
|
||||||
int current_subview[ViewLast]; /* Active up-down subview ID. */
|
int current_subview[ViewLast]; /* Active up-down subview ID. */
|
||||||
FuriMessageQueue *event_queue; /* Keypress events go here. */
|
FuriMessageQueue* event_queue; /* Keypress events go here. */
|
||||||
|
|
||||||
/* Input text state. */
|
/* Input text state. */
|
||||||
ViewDispatcher *view_dispatcher; /* Used only when we want to show
|
ViewDispatcher* view_dispatcher; /* Used only when we want to show
|
||||||
the text_input view for a moment.
|
the text_input view for a moment.
|
||||||
Otherwise it is set to null. */
|
Otherwise it is set to null. */
|
||||||
TextInput *text_input;
|
TextInput* text_input;
|
||||||
bool show_text_input;
|
bool show_text_input;
|
||||||
char *text_input_buffer;
|
char* text_input_buffer;
|
||||||
uint32_t text_input_buffer_len;
|
uint32_t text_input_buffer_len;
|
||||||
void (*text_input_done_callback)(void*);
|
void (*text_input_done_callback)(void*);
|
||||||
|
|
||||||
/* Alert state. */
|
/* Alert state. */
|
||||||
uint32_t alert_dismiss_time; /* Millisecond when the alert will be
|
uint32_t alert_dismiss_time; /* Millisecond when the alert will be
|
||||||
no longer shown. Or zero if the alert
|
no longer shown. Or zero if the alert
|
||||||
is currently not set at all. */
|
is currently not set at all. */
|
||||||
char alert_text[ALERT_MAX_LEN]; /* Alert content. */
|
char alert_text[ALERT_MAX_LEN]; /* Alert content. */
|
||||||
|
|
||||||
/* Radio related. */
|
/* Radio related. */
|
||||||
ProtoViewTxRx *txrx; /* Radio state. */
|
ProtoViewTxRx* txrx; /* Radio state. */
|
||||||
SubGhzSetting *setting; /* A list of valid frequencies. */
|
SubGhzSetting* setting; /* A list of valid frequencies. */
|
||||||
|
|
||||||
/* Generic app state. */
|
/* 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_bestlen; /* Longest coherent signal observed so far. */
|
||||||
uint32_t signal_last_scan_idx; /* Index of the buffer last time we
|
uint32_t signal_last_scan_idx; /* Index of the buffer last time we
|
||||||
performed the scan. */
|
performed the scan. */
|
||||||
bool signal_decoded; /* Was the current signal decoded? */
|
bool signal_decoded; /* Was the current signal decoded? */
|
||||||
ProtoViewMsgInfo *msg_info; /* Decoded message info if not NULL. */
|
ProtoViewMsgInfo* msg_info; /* Decoded message info if not NULL. */
|
||||||
bool direct_sampling_enabled; /* This special view needs an explicit
|
bool direct_sampling_enabled; /* This special view needs an explicit
|
||||||
acknowledge to work. */
|
acknowledge to work. */
|
||||||
void *view_privdata; /* This is a piece of memory of total size
|
void* view_privdata; /* This is a piece of memory of total size
|
||||||
PROTOVIEW_VIEW_PRIVDATA_LEN that it is
|
PROTOVIEW_VIEW_PRIVDATA_LEN that it is
|
||||||
initialized to zero when we switch to
|
initialized to zero when we switch to
|
||||||
a a new view. While the view we are using
|
a a new view. While the view we are using
|
||||||
@@ -149,12 +149,12 @@ struct ProtoViewApp {
|
|||||||
the pointer to a few specific-data structure. */
|
the pointer to a few specific-data structure. */
|
||||||
|
|
||||||
/* Raw view apps state. */
|
/* Raw view apps state. */
|
||||||
uint32_t us_scale; /* microseconds per pixel. */
|
uint32_t us_scale; /* microseconds per pixel. */
|
||||||
uint32_t signal_offset; /* Long press left/right panning in raw view. */
|
uint32_t signal_offset; /* Long press left/right panning in raw view. */
|
||||||
|
|
||||||
/* Configuration view app state. */
|
/* Configuration view app state. */
|
||||||
uint32_t frequency; /* Current frequency. */
|
uint32_t frequency; /* Current frequency. */
|
||||||
uint8_t modulation; /* Current modulation ID, array index in the
|
uint8_t modulation; /* Current modulation ID, array index in the
|
||||||
ProtoViewModulations table. */
|
ProtoViewModulations table. */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -165,18 +165,18 @@ struct ProtoViewApp {
|
|||||||
* in the message info view. */
|
* in the message info view. */
|
||||||
#define PROTOVIEW_MSG_STR_LEN 32
|
#define PROTOVIEW_MSG_STR_LEN 32
|
||||||
typedef struct ProtoViewMsgInfo {
|
typedef struct ProtoViewMsgInfo {
|
||||||
ProtoViewDecoder *decoder; /* The decoder that decoded the message. */
|
ProtoViewDecoder* decoder; /* The decoder that decoded the message. */
|
||||||
ProtoViewFieldSet *fieldset; /* Decoded fields. */
|
ProtoViewFieldSet* fieldset; /* Decoded fields. */
|
||||||
/* Low level information of the detected signal: the following are filled
|
/* Low level information of the detected signal: the following are filled
|
||||||
* by the protocol decoding function: */
|
* by the protocol decoding function: */
|
||||||
uint32_t start_off; /* Pulses start offset in the bitmap. */
|
uint32_t start_off; /* Pulses start offset in the bitmap. */
|
||||||
uint32_t pulses_count; /* Number of pulses of the full message. */
|
uint32_t pulses_count; /* Number of pulses of the full message. */
|
||||||
/* The following are passed already filled to the decoder. */
|
/* The following are passed already filled to the decoder. */
|
||||||
uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */
|
uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */
|
||||||
/* The following are filled by ProtoView core after the decoder returned
|
/* The following are filled by ProtoView core after the decoder returned
|
||||||
* success. */
|
* success. */
|
||||||
uint8_t *bits; /* Bitmap with the signal. */
|
uint8_t* bits; /* Bitmap with the signal. */
|
||||||
uint32_t bits_bytes; /* Number of full bytes in the bitmap, that
|
uint32_t bits_bytes; /* Number of full bytes in the bitmap, that
|
||||||
is 'pulses_count/8' rounded to the next
|
is 'pulses_count/8' rounded to the next
|
||||||
integer. */
|
integer. */
|
||||||
} ProtoViewMsgInfo;
|
} ProtoViewMsgInfo;
|
||||||
@@ -196,28 +196,28 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ProtoViewFieldType type;
|
ProtoViewFieldType type;
|
||||||
uint32_t len; // Depends on type:
|
uint32_t len; // Depends on type:
|
||||||
// Bits for integers (signed,unsigned,binary,hex).
|
// Bits for integers (signed,unsigned,binary,hex).
|
||||||
// Number of characters for strings.
|
// Number of characters for strings.
|
||||||
// Number of nibbles for bytes (1 for each 4 bits).
|
// Number of nibbles for bytes (1 for each 4 bits).
|
||||||
// Number of digits after dot for floats.
|
// Number of digits after dot for floats.
|
||||||
char *name; // Field name.
|
char* name; // Field name.
|
||||||
union {
|
union {
|
||||||
char *str; // String type.
|
char* str; // String type.
|
||||||
int64_t value; // Signed integer type.
|
int64_t value; // Signed integer type.
|
||||||
uint64_t uvalue; // Unsigned integer type.
|
uint64_t uvalue; // Unsigned integer type.
|
||||||
uint8_t *bytes; // Raw bytes type.
|
uint8_t* bytes; // Raw bytes type.
|
||||||
float fvalue; // Float type.
|
float fvalue; // Float type.
|
||||||
};
|
};
|
||||||
} ProtoViewField;
|
} ProtoViewField;
|
||||||
|
|
||||||
typedef struct ProtoViewFieldSet {
|
typedef struct ProtoViewFieldSet {
|
||||||
ProtoViewField **fields;
|
ProtoViewField** fields;
|
||||||
uint32_t numfields;
|
uint32_t numfields;
|
||||||
} ProtoViewFieldSet;
|
} ProtoViewFieldSet;
|
||||||
|
|
||||||
typedef struct ProtoViewDecoder {
|
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
|
/* 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
|
* 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,
|
* pulses represented by the bitmap is passed as the 'numbits' argument,
|
||||||
@@ -225,15 +225,15 @@ typedef struct ProtoViewDecoder {
|
|||||||
* 'bits'. So 'numbytes' is mainly useful to pass as argument to other
|
* 'bits'. So 'numbytes' is mainly useful to pass as argument to other
|
||||||
* functions that perform bit extraction with bound checking, such as
|
* functions that perform bit extraction with bound checking, such as
|
||||||
* bitmap_get() and so forth. */
|
* 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);
|
||||||
/* This method is used by the decoder to return the fields it needs
|
/* This method is used by the decoder to return the fields it needs
|
||||||
* in order to build a new message. This way the message builder view
|
* in order to build a new message. This way the message builder view
|
||||||
* can ask the user to fill the right set of fields of the specified
|
* can ask the user to fill the right set of fields of the specified
|
||||||
* type. */
|
* type. */
|
||||||
void (*get_fields)(ProtoViewFieldSet *fields);
|
void (*get_fields)(ProtoViewFieldSet* fields);
|
||||||
/* This method takes the fields supported by the decoder, and
|
/* This method takes the fields supported by the decoder, and
|
||||||
* renders a message in 'samples'. */
|
* renders a message in 'samples'. */
|
||||||
void (*build_message)(RawSamplesBuffer *samples, ProtoViewFieldSet *fields);
|
void (*build_message)(RawSamplesBuffer* samples, ProtoViewFieldSet* fields);
|
||||||
} ProtoViewDecoder;
|
} ProtoViewDecoder;
|
||||||
|
|
||||||
extern RawSamplesBuffer *RawSamples, *DetectedSamples;
|
extern RawSamplesBuffer *RawSamples, *DetectedSamples;
|
||||||
@@ -244,76 +244,118 @@ uint32_t radio_rx(ProtoViewApp* app);
|
|||||||
void radio_idle(ProtoViewApp* app);
|
void radio_idle(ProtoViewApp* app);
|
||||||
void radio_rx_end(ProtoViewApp* app);
|
void radio_rx_end(ProtoViewApp* app);
|
||||||
void radio_sleep(ProtoViewApp* app);
|
void radio_sleep(ProtoViewApp* app);
|
||||||
void raw_sampling_worker_start(ProtoViewApp *app);
|
void raw_sampling_worker_start(ProtoViewApp* app);
|
||||||
void raw_sampling_worker_stop(ProtoViewApp *app);
|
void raw_sampling_worker_stop(ProtoViewApp* app);
|
||||||
void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx);
|
void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx);
|
||||||
|
|
||||||
/* signal.c */
|
/* signal.c */
|
||||||
uint32_t duration_delta(uint32_t a, uint32_t b);
|
uint32_t duration_delta(uint32_t a, uint32_t b);
|
||||||
void reset_current_signal(ProtoViewApp *app);
|
void reset_current_signal(ProtoViewApp* app);
|
||||||
void scan_for_signal(ProtoViewApp *app,RawSamplesBuffer *source);
|
void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source);
|
||||||
bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos);
|
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(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val);
|
||||||
void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, uint8_t *s, uint32_t slen, uint32_t soff, uint32_t count);
|
void bitmap_copy(
|
||||||
void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat);
|
uint8_t* d,
|
||||||
void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len);
|
uint32_t dlen,
|
||||||
bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits);
|
uint32_t doff,
|
||||||
uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits);
|
uint8_t* s,
|
||||||
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 slen,
|
||||||
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 soff,
|
||||||
void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app);
|
uint32_t count);
|
||||||
void free_msg_info(ProtoViewMsgInfo *i);
|
void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat);
|
||||||
|
void bitmap_reverse_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 init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app);
|
||||||
|
void free_msg_info(ProtoViewMsgInfo* i);
|
||||||
|
|
||||||
/* signal_file.c */
|
/* signal_file.c */
|
||||||
bool save_signal(ProtoViewApp *app, const char *filename);
|
bool save_signal(ProtoViewApp* app, const char* filename);
|
||||||
|
|
||||||
/* view_*.c */
|
/* view_*.c */
|
||||||
void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app);
|
void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app);
|
||||||
void process_input_raw_pulses(ProtoViewApp *app, InputEvent input);
|
void process_input_raw_pulses(ProtoViewApp* app, InputEvent input);
|
||||||
void render_view_settings(Canvas *const canvas, ProtoViewApp *app);
|
void render_view_settings(Canvas* const canvas, ProtoViewApp* app);
|
||||||
void process_input_settings(ProtoViewApp *app, InputEvent input);
|
void process_input_settings(ProtoViewApp* app, InputEvent input);
|
||||||
void render_view_info(Canvas *const canvas, ProtoViewApp *app);
|
void render_view_info(Canvas* const canvas, ProtoViewApp* app);
|
||||||
void process_input_info(ProtoViewApp *app, InputEvent input);
|
void process_input_info(ProtoViewApp* app, InputEvent input);
|
||||||
void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app);
|
void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app);
|
||||||
void process_input_direct_sampling(ProtoViewApp *app, InputEvent input);
|
void process_input_direct_sampling(ProtoViewApp* app, InputEvent input);
|
||||||
void render_view_build_message(Canvas *const canvas, ProtoViewApp *app);
|
void render_view_build_message(Canvas* const canvas, ProtoViewApp* app);
|
||||||
void process_input_build_message(ProtoViewApp *app, InputEvent input);
|
void process_input_build_message(ProtoViewApp* app, InputEvent input);
|
||||||
void view_enter_build_message(ProtoViewApp *app);
|
void view_enter_build_message(ProtoViewApp* app);
|
||||||
void view_exit_build_message(ProtoViewApp *app);
|
void view_exit_build_message(ProtoViewApp* app);
|
||||||
void view_enter_direct_sampling(ProtoViewApp *app);
|
void view_enter_direct_sampling(ProtoViewApp* app);
|
||||||
void view_exit_direct_sampling(ProtoViewApp *app);
|
void view_exit_direct_sampling(ProtoViewApp* app);
|
||||||
void view_exit_settings(ProtoViewApp *app);
|
void view_exit_settings(ProtoViewApp* app);
|
||||||
void view_exit_info(ProtoViewApp *app);
|
void view_exit_info(ProtoViewApp* app);
|
||||||
void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur);
|
void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur);
|
||||||
|
|
||||||
/* ui.c */
|
/* ui.c */
|
||||||
int ui_get_current_subview(ProtoViewApp *app);
|
int ui_get_current_subview(ProtoViewApp* app);
|
||||||
void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, int last_subview);
|
void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview);
|
||||||
bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview);
|
bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview);
|
||||||
void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
|
void ui_show_keyboard(
|
||||||
void (*done_callback)(void*));
|
ProtoViewApp* app,
|
||||||
void ui_dismiss_keyboard(ProtoViewApp *app);
|
char* buffer,
|
||||||
void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl);
|
uint32_t buflen,
|
||||||
void ui_dismiss_alert(ProtoViewApp *app);
|
void (*done_callback)(void*));
|
||||||
void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app);
|
void ui_dismiss_keyboard(ProtoViewApp* app);
|
||||||
void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color);
|
void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl);
|
||||||
|
void ui_dismiss_alert(ProtoViewApp* app);
|
||||||
|
void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app);
|
||||||
|
void canvas_draw_str_with_border(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
const char* str,
|
||||||
|
Color text_color,
|
||||||
|
Color border_color);
|
||||||
|
|
||||||
/* fields.c */
|
/* fields.c */
|
||||||
void fieldset_free(ProtoViewFieldSet *fs);
|
void fieldset_free(ProtoViewFieldSet* fs);
|
||||||
ProtoViewFieldSet *fieldset_new(void);
|
ProtoViewFieldSet* fieldset_new(void);
|
||||||
void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits);
|
void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits);
|
||||||
void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits);
|
void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits);
|
||||||
void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits);
|
void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits);
|
||||||
void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits);
|
void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits);
|
||||||
void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s);
|
void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s);
|
||||||
void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count);
|
void fieldset_add_bytes(
|
||||||
void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot);
|
ProtoViewFieldSet* fs,
|
||||||
const char *field_get_type_name(ProtoViewField *f);
|
const char* name,
|
||||||
int field_to_string(char *buf, size_t len, ProtoViewField *f);
|
const uint8_t* bytes,
|
||||||
bool field_set_from_string(ProtoViewField *f, char *buf, size_t len);
|
uint32_t count);
|
||||||
bool field_incr_value(ProtoViewField *f, int incr);
|
void fieldset_add_float(
|
||||||
void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, ProtoViewFieldSet *src);
|
ProtoViewFieldSet* fs,
|
||||||
void field_set_from_field(ProtoViewField *dst, ProtoViewField *src);
|
const char* name,
|
||||||
|
float val,
|
||||||
|
uint32_t digits_after_dot);
|
||||||
|
const char* field_get_type_name(ProtoViewField* f);
|
||||||
|
int field_to_string(char* buf, size_t len, ProtoViewField* f);
|
||||||
|
bool field_set_from_string(ProtoViewField* f, char* buf, size_t len);
|
||||||
|
bool field_incr_value(ProtoViewField* f, int incr);
|
||||||
|
void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src);
|
||||||
|
void field_set_from_field(ProtoViewField* dst, ProtoViewField* src);
|
||||||
|
|
||||||
/* crc.c */
|
/* 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);
|
||||||
|
|||||||
@@ -9,18 +9,20 @@
|
|||||||
#include <furi_hal_spi.h>
|
#include <furi_hal_spi.h>
|
||||||
#include <furi_hal_interrupt.h>
|
#include <furi_hal_interrupt.h>
|
||||||
|
|
||||||
void raw_sampling_worker_start(ProtoViewApp *app);
|
void raw_sampling_worker_start(ProtoViewApp* app);
|
||||||
void raw_sampling_worker_stop(ProtoViewApp *app);
|
void raw_sampling_worker_stop(ProtoViewApp* app);
|
||||||
|
|
||||||
ProtoViewModulation ProtoViewModulations[] = {
|
ProtoViewModulation ProtoViewModulations[] = {
|
||||||
{"OOK 650Khz", "FuriHalSubGhzPresetOok650Async",
|
{"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL},
|
||||||
FuriHalSubGhzPresetOok650Async, NULL},
|
{"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL},
|
||||||
{"OOK 270Khz", "FuriHalSubGhzPresetOok270Async",
|
{"2FSK 2.38Khz",
|
||||||
FuriHalSubGhzPresetOok270Async, NULL},
|
"FuriHalSubGhzPreset2FSKDev238Async",
|
||||||
{"2FSK 2.38Khz", "FuriHalSubGhzPreset2FSKDev238Async",
|
FuriHalSubGhzPreset2FSKDev238Async,
|
||||||
FuriHalSubGhzPreset2FSKDev238Async, NULL},
|
NULL},
|
||||||
{"2FSK 47.6Khz", "FuriHalSubGhzPreset2FSKDev476Async",
|
{"2FSK 47.6Khz",
|
||||||
FuriHalSubGhzPreset2FSKDev476Async, NULL},
|
"FuriHalSubGhzPreset2FSKDev476Async",
|
||||||
|
FuriHalSubGhzPreset2FSKDev476Async,
|
||||||
|
NULL},
|
||||||
{"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs},
|
{"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs},
|
||||||
{"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs},
|
{"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs},
|
||||||
{"TPMS 3 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs},
|
{"TPMS 3 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs},
|
||||||
@@ -44,12 +46,10 @@ void radio_begin(ProtoViewApp* app) {
|
|||||||
/* The CC1101 preset can be either one of the standard presets, if
|
/* The CC1101 preset can be either one of the standard presets, if
|
||||||
* the modulation "custom" field is NULL, or a custom preset we
|
* the modulation "custom" field is NULL, or a custom preset we
|
||||||
* defined in custom_presets.h. */
|
* defined in custom_presets.h. */
|
||||||
if (ProtoViewModulations[app->modulation].custom == NULL) {
|
if(ProtoViewModulations[app->modulation].custom == NULL) {
|
||||||
furi_hal_subghz_load_preset(
|
furi_hal_subghz_load_preset(ProtoViewModulations[app->modulation].preset);
|
||||||
ProtoViewModulations[app->modulation].preset);
|
|
||||||
} else {
|
} else {
|
||||||
furi_hal_subghz_load_custom_preset(
|
furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom);
|
||||||
ProtoViewModulations[app->modulation].custom);
|
|
||||||
}
|
}
|
||||||
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||||
app->txrx->txrx_state = TxRxStateIDLE;
|
app->txrx->txrx_state = TxRxStateIDLE;
|
||||||
@@ -61,10 +61,10 @@ void radio_begin(ProtoViewApp* app) {
|
|||||||
uint32_t radio_rx(ProtoViewApp* app) {
|
uint32_t radio_rx(ProtoViewApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
if(!furi_hal_subghz_is_frequency_valid(app->frequency)) {
|
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. */
|
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);
|
uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency);
|
||||||
@@ -72,10 +72,8 @@ uint32_t radio_rx(ProtoViewApp* app) {
|
|||||||
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||||
furi_hal_subghz_flush_rx();
|
furi_hal_subghz_flush_rx();
|
||||||
furi_hal_subghz_rx();
|
furi_hal_subghz_rx();
|
||||||
if (!app->txrx->debug_timer_sampling) {
|
if(!app->txrx->debug_timer_sampling) {
|
||||||
|
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker);
|
||||||
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback,
|
|
||||||
app->txrx->worker);
|
|
||||||
subghz_worker_start(app->txrx->worker);
|
subghz_worker_start(app->txrx->worker);
|
||||||
} else {
|
} else {
|
||||||
raw_sampling_worker_start(app);
|
raw_sampling_worker_start(app);
|
||||||
@@ -88,8 +86,8 @@ uint32_t radio_rx(ProtoViewApp* app) {
|
|||||||
void radio_rx_end(ProtoViewApp* app) {
|
void radio_rx_end(ProtoViewApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
|
|
||||||
if (app->txrx->txrx_state == TxRxStateRx) {
|
if(app->txrx->txrx_state == TxRxStateRx) {
|
||||||
if (!app->txrx->debug_timer_sampling) {
|
if(!app->txrx->debug_timer_sampling) {
|
||||||
if(subghz_worker_is_running(app->txrx->worker)) {
|
if(subghz_worker_is_running(app->txrx->worker)) {
|
||||||
subghz_worker_stop(app->txrx->worker);
|
subghz_worker_stop(app->txrx->worker);
|
||||||
furi_hal_subghz_stop_async_rx();
|
furi_hal_subghz_stop_async_rx();
|
||||||
@@ -105,7 +103,7 @@ void radio_rx_end(ProtoViewApp* app) {
|
|||||||
/* Put radio on sleep. */
|
/* Put radio on sleep. */
|
||||||
void radio_sleep(ProtoViewApp* app) {
|
void radio_sleep(ProtoViewApp* app) {
|
||||||
furi_assert(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.
|
/* We can't go from having an active RX worker to sleeping.
|
||||||
* Stop the RX subsystems first. */
|
* Stop the RX subsystems first. */
|
||||||
radio_rx_end(app);
|
radio_rx_end(app);
|
||||||
@@ -120,10 +118,10 @@ void radio_sleep(ProtoViewApp* app) {
|
|||||||
/* This function suspends the current RX state, switches to TX mode,
|
/* This function suspends the current RX state, switches to TX mode,
|
||||||
* transmits the signal provided by the callback data_feeder, and later
|
* transmits the signal provided by the callback data_feeder, and later
|
||||||
* restores the RX state if there was one. */
|
* restores the RX state if there was one. */
|
||||||
void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx) {
|
void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx) {
|
||||||
TxRxState oldstate = app->txrx->txrx_state;
|
TxRxState oldstate = app->txrx->txrx_state;
|
||||||
|
|
||||||
if (oldstate == TxRxStateRx) radio_rx_end(app);
|
if(oldstate == TxRxStateRx) radio_rx_end(app);
|
||||||
radio_begin(app);
|
radio_begin(app);
|
||||||
|
|
||||||
furi_hal_subghz_idle();
|
furi_hal_subghz_idle();
|
||||||
@@ -138,7 +136,7 @@ void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder
|
|||||||
furi_hal_subghz_idle();
|
furi_hal_subghz_idle();
|
||||||
|
|
||||||
radio_begin(app);
|
radio_begin(app);
|
||||||
if (oldstate == TxRxStateRx) radio_rx(app);
|
if(oldstate == TxRxStateRx) radio_rx(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================= Raw sampling mode =============================
|
/* ============================= Raw sampling mode =============================
|
||||||
@@ -148,15 +146,15 @@ void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder
|
|||||||
* Flipper system.
|
* Flipper system.
|
||||||
* ===========================================================================*/
|
* ===========================================================================*/
|
||||||
|
|
||||||
void protoview_timer_isr(void *ctx) {
|
void protoview_timer_isr(void* ctx) {
|
||||||
ProtoViewApp *app = ctx;
|
ProtoViewApp* app = ctx;
|
||||||
|
|
||||||
bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
|
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 now = DWT->CYCCNT;
|
||||||
uint32_t dur = now - app->txrx->last_g0_change_time;
|
uint32_t dur = now - app->txrx->last_g0_change_time;
|
||||||
dur /= furi_hal_cortex_instructions_per_microsecond();
|
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);
|
raw_samples_add(RawSamples, app->txrx->last_g0_value, dur);
|
||||||
app->txrx->last_g0_value = level;
|
app->txrx->last_g0_value = level;
|
||||||
app->txrx->last_g0_change_time = now;
|
app->txrx->last_g0_change_time = now;
|
||||||
@@ -164,13 +162,13 @@ void protoview_timer_isr(void *ctx) {
|
|||||||
LL_TIM_ClearFlag_UPDATE(TIM2);
|
LL_TIM_ClearFlag_UPDATE(TIM2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void raw_sampling_worker_start(ProtoViewApp *app) {
|
void raw_sampling_worker_start(ProtoViewApp* app) {
|
||||||
UNUSED(app);
|
UNUSED(app);
|
||||||
|
|
||||||
LL_TIM_InitTypeDef tim_init = {
|
LL_TIM_InitTypeDef tim_init = {
|
||||||
.Prescaler = 63, /* CPU frequency is ~64Mhz. */
|
.Prescaler = 63, /* CPU frequency is ~64Mhz. */
|
||||||
.CounterMode = LL_TIM_COUNTERMODE_UP,
|
.CounterMode = LL_TIM_COUNTERMODE_UP,
|
||||||
.Autoreload = 5, /* Sample every 5 us */
|
.Autoreload = 5, /* Sample every 5 us */
|
||||||
};
|
};
|
||||||
|
|
||||||
LL_TIM_Init(TIM2, &tim_init);
|
LL_TIM_Init(TIM2, &tim_init);
|
||||||
@@ -183,7 +181,7 @@ void raw_sampling_worker_start(ProtoViewApp *app) {
|
|||||||
FURI_LOG_E(TAG, "Timer enabled");
|
FURI_LOG_E(TAG, "Timer enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
void raw_sampling_worker_stop(ProtoViewApp *app) {
|
void raw_sampling_worker_stop(ProtoViewApp* app) {
|
||||||
UNUSED(app);
|
UNUSED(app);
|
||||||
FURI_CRITICAL_ENTER();
|
FURI_CRITICAL_ENTER();
|
||||||
LL_TIM_DisableCounter(TIM2);
|
LL_TIM_DisableCounter(TIM2);
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
|
|
||||||
/* CRC8 with the specified initialization value 'init' and
|
/* CRC8 with the specified initialization value 'init' and
|
||||||
* polynomial 'poly'. */
|
* 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;
|
uint8_t crc = init;
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
for (i = 0; i < len; i++) {
|
for(i = 0; i < len; i++) {
|
||||||
crc ^= data[i];
|
crc ^= data[i];
|
||||||
for (j = 0; j < 8; j++) {
|
for(j = 0; j < 8; j++) {
|
||||||
if ((crc & 0x80) != 0)
|
if((crc & 0x80) != 0)
|
||||||
crc = (uint8_t)((crc << 1) ^ poly);
|
crc = (uint8_t)((crc << 1) ^ poly);
|
||||||
else
|
else
|
||||||
crc <<= 1;
|
crc <<= 1;
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = {
|
|||||||
// // Modem Configuration
|
// // Modem Configuration
|
||||||
{CC1101_MDMCFG0, 0x00},
|
{CC1101_MDMCFG0, 0x00},
|
||||||
{CC1101_MDMCFG1, 0x02},
|
{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_MDMCFG3, 0x93}, // Data rate is 20kBaud
|
||||||
{CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz
|
{CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz
|
||||||
{CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz
|
{CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz
|
||||||
@@ -106,8 +107,10 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = {
|
|||||||
{0, 0},
|
{0, 0},
|
||||||
|
|
||||||
/* CC1101 2FSK PATABLE. */
|
/* CC1101 2FSK PATABLE. */
|
||||||
{0xC0, 0}, {0,0}, {0,0}, {0,0}
|
{0xC0, 0},
|
||||||
};
|
{0, 0},
|
||||||
|
{0, 0},
|
||||||
|
{0, 0}};
|
||||||
|
|
||||||
/* This is like the default Flipper OOK 640Khz bandwidth preset, but
|
/* This is like the default Flipper OOK 640Khz bandwidth preset, but
|
||||||
* the bandwidth is changed to 10kBaud to accomodate TPMS frequency. */
|
* the bandwidth is changed to 10kBaud to accomodate TPMS frequency. */
|
||||||
@@ -156,8 +159,10 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = {
|
|||||||
{0, 0},
|
{0, 0},
|
||||||
|
|
||||||
/* CC1101 OOK PATABLE. */
|
/* CC1101 OOK PATABLE. */
|
||||||
{0, 0xC0}, {0,0}, {0,0}, {0,0}
|
{0, 0xC0},
|
||||||
};
|
{0, 0},
|
||||||
|
{0, 0},
|
||||||
|
{0, 0}};
|
||||||
|
|
||||||
/* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */
|
/* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */
|
||||||
static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = {
|
static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = {
|
||||||
@@ -174,7 +179,8 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = {
|
|||||||
// // Modem Configuration
|
// // Modem Configuration
|
||||||
{CC1101_MDMCFG0, 0x00},
|
{CC1101_MDMCFG0, 0x00},
|
||||||
{CC1101_MDMCFG1, 0x02},
|
{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_MDMCFG3, 0x93}, // Data rate is 40kBaud
|
||||||
{CC1101_MDMCFG4, 0x6A}, // 6 = BW filter 270kHz, A = Data rate exp
|
{CC1101_MDMCFG4, 0x6A}, // 6 = BW filter 270kHz, A = Data rate exp
|
||||||
{CC1101_DEVIATN, 0x41}, // Deviation 28kHz
|
{CC1101_DEVIATN, 0x41}, // Deviation 28kHz
|
||||||
@@ -204,8 +210,10 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = {
|
|||||||
{0, 0},
|
{0, 0},
|
||||||
|
|
||||||
/* CC1101 2FSK PATABLE. */
|
/* CC1101 2FSK PATABLE. */
|
||||||
{0xC0, 0}, {0,0}, {0,0}, {0,0}
|
{0xC0, 0},
|
||||||
};
|
{0, 0},
|
||||||
|
{0, 0},
|
||||||
|
{0, 0}};
|
||||||
|
|
||||||
/* FSK 19k dev, 325 Khz filter, 20kBaud. Works well with Toyota. */
|
/* FSK 19k dev, 325 Khz filter, 20kBaud. Works well with Toyota. */
|
||||||
static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = {
|
static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = {
|
||||||
@@ -250,6 +258,7 @@ static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = {
|
|||||||
{0, 0},
|
{0, 0},
|
||||||
|
|
||||||
/* CC1101 2FSK PATABLE. */
|
/* CC1101 2FSK PATABLE. */
|
||||||
{0xC0, 0}, {0,0}, {0,0}, {0,0}
|
{0xC0, 0},
|
||||||
};
|
{0, 0},
|
||||||
|
{0, 0},
|
||||||
|
{0, 0}};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const SubGhzProtocol subghz_protocol_protoview;
|
|||||||
|
|
||||||
/* The feed() method puts data in the RawSamples global (protected by
|
/* The feed() method puts data in the RawSamples global (protected by
|
||||||
* a mutex). */
|
* a mutex). */
|
||||||
extern RawSamplesBuffer *RawSamples;
|
extern RawSamplesBuffer* RawSamples;
|
||||||
|
|
||||||
/* This is totally dummy: we just define the decoder base for the async
|
/* 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
|
* 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) {
|
void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) {
|
||||||
UNUSED(environment);
|
UNUSED(environment);
|
||||||
|
|
||||||
SubGhzProtocolDecoderprotoview* instance =
|
SubGhzProtocolDecoderprotoview* instance = malloc(sizeof(SubGhzProtocolDecoderprotoview));
|
||||||
malloc(sizeof(SubGhzProtocolDecoderprotoview));
|
|
||||||
instance->base.protocol = &subghz_protocol_protoview;
|
instance->base.protocol = &subghz_protocol_protoview;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@@ -66,8 +65,7 @@ uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) {
|
|||||||
bool subghz_protocol_decoder_protoview_serialize(
|
bool subghz_protocol_decoder_protoview_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzRadioPreset* preset)
|
SubGhzRadioPreset* preset) {
|
||||||
{
|
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
UNUSED(flipper_format);
|
UNUSED(flipper_format);
|
||||||
UNUSED(preset);
|
UNUSED(preset);
|
||||||
@@ -75,15 +73,13 @@ bool subghz_protocol_decoder_protoview_serialize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Not used. */
|
/* 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(context);
|
||||||
UNUSED(flipper_format);
|
UNUSED(flipper_format);
|
||||||
return false;
|
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_assert(context);
|
||||||
furi_string_cat_printf(output, "Protoview");
|
furi_string_cat_printf(output, "Protoview");
|
||||||
}
|
}
|
||||||
@@ -116,5 +112,4 @@ const SubGhzProtocol* protoview_protocol_registry_items[] = {
|
|||||||
|
|
||||||
const SubGhzProtocolRegistry protoview_protocol_registry = {
|
const SubGhzProtocolRegistry protoview_protocol_registry = {
|
||||||
.items = protoview_protocol_registry_items,
|
.items = protoview_protocol_registry_items,
|
||||||
.size = COUNT_OF(protoview_protocol_registry_items)
|
.size = COUNT_OF(protoview_protocol_registry_items)};
|
||||||
};
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
/* Create a new field of the specified type. Without populating its
|
/* Create a new field of the specified type. Without populating its
|
||||||
* type-specific value. */
|
* type-specific value. */
|
||||||
static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) {
|
static ProtoViewField* field_new(ProtoViewFieldType type, const char* name) {
|
||||||
ProtoViewField *f = malloc(sizeof(*f));
|
ProtoViewField* f = malloc(sizeof(*f));
|
||||||
f->type = type;
|
f->type = type;
|
||||||
f->name = strdup(name);
|
f->name = strdup(name);
|
||||||
return f;
|
return f;
|
||||||
@@ -16,72 +16,80 @@ static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) {
|
|||||||
|
|
||||||
/* Free only the auxiliary data of a field, used to represent the
|
/* Free only the auxiliary data of a field, used to represent the
|
||||||
* current type. Name and type are not touched. */
|
* current type. Name and type are not touched. */
|
||||||
static void field_free_aux_data(ProtoViewField *f) {
|
static void field_free_aux_data(ProtoViewField* f) {
|
||||||
switch(f->type) {
|
switch(f->type) {
|
||||||
case FieldTypeStr: free(f->str); break;
|
case FieldTypeStr:
|
||||||
case FieldTypeBytes: free(f->bytes); break;
|
free(f->str);
|
||||||
default: break; // Nothing to free for other types.
|
break;
|
||||||
|
case FieldTypeBytes:
|
||||||
|
free(f->bytes);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; // Nothing to free for other types.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free a field an associated data. */
|
/* Free a field an associated data. */
|
||||||
static void field_free(ProtoViewField *f) {
|
static void field_free(ProtoViewField* f) {
|
||||||
field_free_aux_data(f);
|
field_free_aux_data(f);
|
||||||
free(f->name);
|
free(f->name);
|
||||||
free(f);
|
free(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the type of the field as string. */
|
/* Return the type of the field as string. */
|
||||||
const char *field_get_type_name(ProtoViewField *f) {
|
const char* field_get_type_name(ProtoViewField* f) {
|
||||||
switch(f->type) {
|
switch(f->type) {
|
||||||
case FieldTypeStr: return "str";
|
case FieldTypeStr:
|
||||||
case FieldTypeSignedInt: return "int";
|
return "str";
|
||||||
case FieldTypeUnsignedInt: return "uint";
|
case FieldTypeSignedInt:
|
||||||
case FieldTypeBinary: return "bin";
|
return "int";
|
||||||
case FieldTypeHex: return "hex";
|
case FieldTypeUnsignedInt:
|
||||||
case FieldTypeBytes: return "bytes";
|
return "uint";
|
||||||
case FieldTypeFloat: return "float";
|
case FieldTypeBinary:
|
||||||
|
return "bin";
|
||||||
|
case FieldTypeHex:
|
||||||
|
return "hex";
|
||||||
|
case FieldTypeBytes:
|
||||||
|
return "bytes";
|
||||||
|
case FieldTypeFloat:
|
||||||
|
return "float";
|
||||||
}
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set a string representation of the specified field in buf. */
|
/* Set a string representation of the specified field in buf. */
|
||||||
int field_to_string(char *buf, size_t len, ProtoViewField *f) {
|
int field_to_string(char* buf, size_t len, ProtoViewField* f) {
|
||||||
switch(f->type) {
|
switch(f->type) {
|
||||||
case FieldTypeStr:
|
case FieldTypeStr:
|
||||||
return snprintf(buf,len,"%s", f->str);
|
return snprintf(buf, len, "%s", f->str);
|
||||||
case FieldTypeSignedInt:
|
case FieldTypeSignedInt:
|
||||||
return snprintf(buf,len,"%lld", (long long) f->value);
|
return snprintf(buf, len, "%lld", (long long)f->value);
|
||||||
case FieldTypeUnsignedInt:
|
case FieldTypeUnsignedInt:
|
||||||
return snprintf(buf,len,"%llu", (unsigned long long) f->uvalue);
|
return snprintf(buf, len, "%llu", (unsigned long long)f->uvalue);
|
||||||
case FieldTypeBinary:
|
case FieldTypeBinary: {
|
||||||
{
|
uint64_t test_bit = (1 << (f->len - 1));
|
||||||
uint64_t test_bit = (1 << (f->len-1));
|
uint64_t idx = 0;
|
||||||
uint64_t idx = 0;
|
while(idx < len - 1 && test_bit) {
|
||||||
while(idx < len-1 && test_bit) {
|
buf[idx++] = (f->uvalue & test_bit) ? '1' : '0';
|
||||||
buf[idx++] = (f->uvalue & test_bit) ? '1' : '0';
|
test_bit >>= 1;
|
||||||
test_bit >>= 1;
|
|
||||||
}
|
|
||||||
buf[idx] = 0;
|
|
||||||
return idx;
|
|
||||||
}
|
}
|
||||||
|
buf[idx] = 0;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
case FieldTypeHex:
|
case FieldTypeHex:
|
||||||
return snprintf(buf, len, "%*llX", (int)(f->len+7)/8, f->uvalue);
|
return snprintf(buf, len, "%*llX", (int)(f->len + 7) / 8, f->uvalue);
|
||||||
case FieldTypeFloat:
|
case FieldTypeFloat:
|
||||||
return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue);
|
return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue);
|
||||||
case FieldTypeBytes:
|
case FieldTypeBytes: {
|
||||||
{
|
uint64_t idx = 0;
|
||||||
uint64_t idx = 0;
|
while(idx < len - 1 && idx < f->len) {
|
||||||
while(idx < len-1 && idx < f->len) {
|
const char* charset = "0123456789ABCDEF";
|
||||||
const char *charset = "0123456789ABCDEF";
|
uint32_t nibble = idx & 1 ? (f->bytes[idx / 2] & 0xf) : (f->bytes[idx / 2] >> 4);
|
||||||
uint32_t nibble = idx & 1 ?
|
buf[idx++] = charset[nibble];
|
||||||
(f->bytes[idx/2] & 0xf) :
|
|
||||||
(f->bytes[idx/2] >> 4);
|
|
||||||
buf[idx++] = charset[nibble];
|
|
||||||
}
|
|
||||||
buf[idx] = 0;
|
|
||||||
return idx;
|
|
||||||
}
|
}
|
||||||
|
buf[idx] = 0;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -96,7 +104,7 @@ int field_to_string(char *buf, size_t len, ProtoViewField *f) {
|
|||||||
* The function returns true if the filed was successfully set to the
|
* The function returns true if the filed was successfully set to the
|
||||||
* new value, otherwise if the specified value is invalid for the
|
* new value, otherwise if the specified value is invalid for the
|
||||||
* field type, false is returned. */
|
* field type, false is returned. */
|
||||||
bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) {
|
bool field_set_from_string(ProtoViewField* f, char* buf, size_t len) {
|
||||||
// Initialize values to zero since the Flipper sscanf() implementation
|
// Initialize values to zero since the Flipper sscanf() implementation
|
||||||
// is fuzzy... may populate only part of the value.
|
// is fuzzy... may populate only part of the value.
|
||||||
long long val = 0;
|
long long val = 0;
|
||||||
@@ -107,80 +115,78 @@ bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) {
|
|||||||
case FieldTypeStr:
|
case FieldTypeStr:
|
||||||
free(f->str);
|
free(f->str);
|
||||||
f->len = len;
|
f->len = len;
|
||||||
f->str = malloc(len+1);
|
f->str = malloc(len + 1);
|
||||||
memcpy(f->str,buf,len+1);
|
memcpy(f->str, buf, len + 1);
|
||||||
break;
|
break;
|
||||||
case FieldTypeSignedInt:
|
case FieldTypeSignedInt:
|
||||||
if (!sscanf(buf,"%lld",&val)) return false;
|
if(!sscanf(buf, "%lld", &val)) return false;
|
||||||
f->value = val;
|
f->value = val;
|
||||||
break;
|
break;
|
||||||
case FieldTypeUnsignedInt:
|
case FieldTypeUnsignedInt:
|
||||||
if (!sscanf(buf,"%llu",&uval)) return false;
|
if(!sscanf(buf, "%llu", &uval)) return false;
|
||||||
f->uvalue = uval;
|
f->uvalue = uval;
|
||||||
break;
|
break;
|
||||||
case FieldTypeBinary:
|
case FieldTypeBinary: {
|
||||||
{
|
uint64_t bit_to_set = (1 << (len - 1));
|
||||||
uint64_t bit_to_set = (1 << (len-1));
|
uint64_t idx = 0;
|
||||||
uint64_t idx = 0;
|
uval = 0;
|
||||||
uval = 0;
|
while(buf[idx]) {
|
||||||
while(buf[idx]) {
|
if(buf[idx] == '1')
|
||||||
if (buf[idx] == '1') uval |= bit_to_set;
|
uval |= bit_to_set;
|
||||||
else if (buf[idx] != '0') return false;
|
else if(buf[idx] != '0')
|
||||||
bit_to_set >>= 1;
|
return false;
|
||||||
idx++;
|
bit_to_set >>= 1;
|
||||||
}
|
idx++;
|
||||||
f->uvalue = uval;
|
|
||||||
}
|
}
|
||||||
break;
|
f->uvalue = uval;
|
||||||
|
} break;
|
||||||
case FieldTypeHex:
|
case FieldTypeHex:
|
||||||
if (!sscanf(buf,"%llx",&uval) &&
|
if(!sscanf(buf, "%llx", &uval) && !sscanf(buf, "%llX", &uval)) return false;
|
||||||
!sscanf(buf,"%llX",&uval)) return false;
|
|
||||||
f->uvalue = uval;
|
f->uvalue = uval;
|
||||||
break;
|
break;
|
||||||
case FieldTypeFloat:
|
case FieldTypeFloat:
|
||||||
if (!sscanf(buf,"%f",&fval)) return false;
|
if(!sscanf(buf, "%f", &fval)) return false;
|
||||||
f->fvalue = fval;
|
f->fvalue = fval;
|
||||||
break;
|
break;
|
||||||
case FieldTypeBytes:
|
case FieldTypeBytes: {
|
||||||
{
|
if(len > f->len) return false;
|
||||||
if (len > f->len) return false;
|
uint64_t idx = 0;
|
||||||
uint64_t idx = 0;
|
while(buf[idx]) {
|
||||||
while(buf[idx]) {
|
uint8_t nibble = 0;
|
||||||
uint8_t nibble = 0;
|
char c = toupper(buf[idx]);
|
||||||
char c = toupper(buf[idx]);
|
if(c >= '0' && c <= '9')
|
||||||
if (c >= '0' && c <= '9') nibble = c-'0';
|
nibble = c - '0';
|
||||||
else if (c >= 'A' && c <= 'F') nibble = 10+(c-'A');
|
else if(c >= 'A' && c <= 'F')
|
||||||
else return false;
|
nibble = 10 + (c - 'A');
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
if (idx & 1) {
|
if(idx & 1) {
|
||||||
f->bytes[idx/2] =
|
f->bytes[idx / 2] = (f->bytes[idx / 2] & 0xF0) | nibble;
|
||||||
(f->bytes[idx/2] & 0xF0) | nibble;
|
} else {
|
||||||
} else {
|
f->bytes[idx / 2] = (f->bytes[idx / 2] & 0x0F) | (nibble << 4);
|
||||||
f->bytes[idx/2] =
|
|
||||||
(f->bytes[idx/2] & 0x0F) | (nibble<<4);
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
}
|
||||||
buf[idx] = 0;
|
idx++;
|
||||||
}
|
}
|
||||||
break;
|
buf[idx] = 0;
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the 'dst' field to contain a copy of the value of the 'src'
|
/* Set the 'dst' field to contain a copy of the value of the 'src'
|
||||||
* field. The field name is not modified. */
|
* field. The field name is not modified. */
|
||||||
void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) {
|
void field_set_from_field(ProtoViewField* dst, ProtoViewField* src) {
|
||||||
field_free_aux_data(dst);
|
field_free_aux_data(dst);
|
||||||
dst->type = src->type;
|
dst->type = src->type;
|
||||||
dst->len = src->len;
|
dst->len = src->len;
|
||||||
switch(src->type) {
|
switch(src->type) {
|
||||||
case FieldTypeStr:
|
case FieldTypeStr:
|
||||||
dst->str = strdup(src->str);
|
dst->str = strdup(src->str);
|
||||||
break;
|
break;
|
||||||
case FieldTypeBytes:
|
case FieldTypeBytes:
|
||||||
dst->bytes = malloc(src->len);
|
dst->bytes = malloc(src->len);
|
||||||
memcpy(dst->bytes,src->bytes,dst->len);
|
memcpy(dst->bytes, src->bytes, dst->len);
|
||||||
break;
|
break;
|
||||||
case FieldTypeSignedInt:
|
case FieldTypeSignedInt:
|
||||||
dst->value = src->value;
|
dst->value = src->value;
|
||||||
@@ -199,159 +205,159 @@ void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) {
|
|||||||
/* Increment the specified field value of 'incr'. If the field type
|
/* Increment the specified field value of 'incr'. If the field type
|
||||||
* does not support increments false is returned, otherwise the
|
* does not support increments false is returned, otherwise the
|
||||||
* action is performed. */
|
* action is performed. */
|
||||||
bool field_incr_value(ProtoViewField *f, int incr) {
|
bool field_incr_value(ProtoViewField* f, int incr) {
|
||||||
switch(f->type) {
|
switch(f->type) {
|
||||||
case FieldTypeStr: return false;
|
case FieldTypeStr:
|
||||||
case FieldTypeSignedInt: {
|
return false;
|
||||||
/* Wrap around depending on the number of bits (f->len)
|
case FieldTypeSignedInt: {
|
||||||
|
/* Wrap around depending on the number of bits (f->len)
|
||||||
* the integer was declared to have. */
|
* the integer was declared to have. */
|
||||||
int64_t max = (1ULL << (f->len-1))-1;
|
int64_t max = (1ULL << (f->len - 1)) - 1;
|
||||||
int64_t min = -max-1;
|
int64_t min = -max - 1;
|
||||||
int64_t v = (int64_t)f->value + incr;
|
int64_t v = (int64_t)f->value + incr;
|
||||||
if (v > max) v = min+(v-max-1);
|
if(v > max) v = min + (v - max - 1);
|
||||||
if (v < min) v = max+(v-min+1);
|
if(v < min) v = max + (v - min + 1);
|
||||||
f->value = v;
|
f->value = v;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FieldTypeBinary:
|
case FieldTypeBinary:
|
||||||
case FieldTypeHex:
|
case FieldTypeHex:
|
||||||
case FieldTypeUnsignedInt: {
|
case FieldTypeUnsignedInt: {
|
||||||
/* Wrap around like for the unsigned case, but here
|
/* Wrap around like for the unsigned case, but here
|
||||||
* is simpler. */
|
* is simpler. */
|
||||||
uint64_t max = (1ULL << f->len)-1; // Broken for 64 bits.
|
uint64_t max = (1ULL << f->len) - 1; // Broken for 64 bits.
|
||||||
uint64_t uv = (uint64_t)f->value + incr;
|
uint64_t uv = (uint64_t)f->value + incr;
|
||||||
if (uv > max) uv = uv & max;
|
if(uv > max) uv = uv & max;
|
||||||
f->uvalue = uv;
|
f->uvalue = uv;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FieldTypeFloat:
|
case FieldTypeFloat:
|
||||||
f->fvalue += incr;
|
f->fvalue += incr;
|
||||||
break;
|
break;
|
||||||
case FieldTypeBytes: {
|
case FieldTypeBytes: {
|
||||||
// For bytes we only support single unit increments.
|
// For bytes we only support single unit increments.
|
||||||
if (incr != -1 && incr != 1) return false;
|
if(incr != -1 && incr != 1) return false;
|
||||||
for (int j = f->len-1; j >= 0; j--) {
|
for(int j = f->len - 1; j >= 0; j--) {
|
||||||
uint8_t nibble = (j&1) ? (f->bytes[j/2] & 0x0F) :
|
uint8_t nibble = (j & 1) ? (f->bytes[j / 2] & 0x0F) : ((f->bytes[j / 2] & 0xF0) >> 4);
|
||||||
((f->bytes[j/2] & 0xF0) >> 4);
|
|
||||||
|
|
||||||
nibble += incr;
|
nibble += incr;
|
||||||
nibble &= 0x0F;
|
nibble &= 0x0F;
|
||||||
|
|
||||||
f->bytes[j/2] = (j&1) ? ((f->bytes[j/2] & 0xF0) | nibble) :
|
f->bytes[j / 2] = (j & 1) ? ((f->bytes[j / 2] & 0xF0) | nibble) :
|
||||||
((f->bytes[j/2] & 0x0F) | (nibble<<4));
|
((f->bytes[j / 2] & 0x0F) | (nibble << 4));
|
||||||
|
|
||||||
/* Propagate the operation on overflow of this nibble. */
|
/* Propagate the operation on overflow of this nibble. */
|
||||||
if ((incr == 1 && nibble == 0) ||
|
if((incr == 1 && nibble == 0) || (incr == -1 && nibble == 0xf)) {
|
||||||
(incr == -1 && nibble == 0xf))
|
continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break; // Otherwise stop the loop here.
|
|
||||||
}
|
}
|
||||||
break;
|
break; // Otherwise stop the loop here.
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Free a field set and its contained fields. */
|
/* Free a field set and its contained fields. */
|
||||||
void fieldset_free(ProtoViewFieldSet *fs) {
|
void fieldset_free(ProtoViewFieldSet* fs) {
|
||||||
for (uint32_t j = 0; j < fs->numfields; j++)
|
for(uint32_t j = 0; j < fs->numfields; j++) field_free(fs->fields[j]);
|
||||||
field_free(fs->fields[j]);
|
|
||||||
free(fs->fields);
|
free(fs->fields);
|
||||||
free(fs);
|
free(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and init an empty field set. */
|
/* Allocate and init an empty field set. */
|
||||||
ProtoViewFieldSet *fieldset_new(void) {
|
ProtoViewFieldSet* fieldset_new(void) {
|
||||||
ProtoViewFieldSet *fs = malloc(sizeof(*fs));
|
ProtoViewFieldSet* fs = malloc(sizeof(*fs));
|
||||||
fs->numfields = 0;
|
fs->numfields = 0;
|
||||||
fs->fields = NULL;
|
fs->fields = NULL;
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append an already allocated field at the end of the specified field set. */
|
/* Append an already allocated field at the end of the specified field set. */
|
||||||
static void fieldset_add_field(ProtoViewFieldSet *fs, ProtoViewField *field) {
|
static void fieldset_add_field(ProtoViewFieldSet* fs, ProtoViewField* field) {
|
||||||
fs->numfields++;
|
fs->numfields++;
|
||||||
fs->fields = realloc(fs->fields,sizeof(ProtoViewField*)*fs->numfields);
|
fs->fields = realloc(fs->fields, sizeof(ProtoViewField*) * fs->numfields);
|
||||||
fs->fields[fs->numfields-1] = field;
|
fs->fields[fs->numfields - 1] = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and append an integer field. */
|
/* Allocate and append an integer field. */
|
||||||
void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits) {
|
void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits) {
|
||||||
ProtoViewField *f = field_new(FieldTypeSignedInt,name);
|
ProtoViewField* f = field_new(FieldTypeSignedInt, name);
|
||||||
f->value = val;
|
f->value = val;
|
||||||
f->len = bits;
|
f->len = bits;
|
||||||
fieldset_add_field(fs,f);
|
fieldset_add_field(fs, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and append an unsigned field. */
|
/* Allocate and append an unsigned field. */
|
||||||
void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) {
|
void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) {
|
||||||
ProtoViewField *f = field_new(FieldTypeUnsignedInt,name);
|
ProtoViewField* f = field_new(FieldTypeUnsignedInt, name);
|
||||||
f->uvalue = uval;
|
f->uvalue = uval;
|
||||||
f->len = bits;
|
f->len = bits;
|
||||||
fieldset_add_field(fs,f);
|
fieldset_add_field(fs, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and append a hex field. This is an unsigned number but
|
/* Allocate and append a hex field. This is an unsigned number but
|
||||||
* with an hex representation. */
|
* with an hex representation. */
|
||||||
void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) {
|
void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) {
|
||||||
ProtoViewField *f = field_new(FieldTypeHex,name);
|
ProtoViewField* f = field_new(FieldTypeHex, name);
|
||||||
f->uvalue = uval;
|
f->uvalue = uval;
|
||||||
f->len = bits;
|
f->len = bits;
|
||||||
fieldset_add_field(fs,f);
|
fieldset_add_field(fs, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and append a bin field. This is an unsigned number but
|
/* Allocate and append a bin field. This is an unsigned number but
|
||||||
* with a binary representation. */
|
* with a binary representation. */
|
||||||
void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) {
|
void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) {
|
||||||
ProtoViewField *f = field_new(FieldTypeBinary,name);
|
ProtoViewField* f = field_new(FieldTypeBinary, name);
|
||||||
f->uvalue = uval;
|
f->uvalue = uval;
|
||||||
f->len = bits;
|
f->len = bits;
|
||||||
fieldset_add_field(fs,f);
|
fieldset_add_field(fs, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and append a string field. */
|
/* Allocate and append a string field. */
|
||||||
void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s) {
|
void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s) {
|
||||||
ProtoViewField *f = field_new(FieldTypeStr,name);
|
ProtoViewField* f = field_new(FieldTypeStr, name);
|
||||||
f->str = strdup(s);
|
f->str = strdup(s);
|
||||||
f->len = strlen(s);
|
f->len = strlen(s);
|
||||||
fieldset_add_field(fs,f);
|
fieldset_add_field(fs, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and append a bytes field. Note that 'count' is specified in
|
/* Allocate and append a bytes field. Note that 'count' is specified in
|
||||||
* nibbles (bytes*2). */
|
* nibbles (bytes*2). */
|
||||||
void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count_nibbles) {
|
void fieldset_add_bytes(
|
||||||
uint32_t numbytes = (count_nibbles+count_nibbles%2)/2;
|
ProtoViewFieldSet* fs,
|
||||||
ProtoViewField *f = field_new(FieldTypeBytes,name);
|
const char* name,
|
||||||
|
const uint8_t* bytes,
|
||||||
|
uint32_t count_nibbles) {
|
||||||
|
uint32_t numbytes = (count_nibbles + count_nibbles % 2) / 2;
|
||||||
|
ProtoViewField* f = field_new(FieldTypeBytes, name);
|
||||||
f->bytes = malloc(numbytes);
|
f->bytes = malloc(numbytes);
|
||||||
memcpy(f->bytes,bytes,numbytes);
|
memcpy(f->bytes, bytes, numbytes);
|
||||||
f->len = count_nibbles;
|
f->len = count_nibbles;
|
||||||
fieldset_add_field(fs,f);
|
fieldset_add_field(fs, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and append a float field. */
|
/* Allocate and append a float field. */
|
||||||
void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot) {
|
void fieldset_add_float(
|
||||||
ProtoViewField *f = field_new(FieldTypeFloat,name);
|
ProtoViewFieldSet* fs,
|
||||||
|
const char* name,
|
||||||
|
float val,
|
||||||
|
uint32_t digits_after_dot) {
|
||||||
|
ProtoViewField* f = field_new(FieldTypeFloat, name);
|
||||||
f->fvalue = val;
|
f->fvalue = val;
|
||||||
f->len = digits_after_dot;
|
f->len = digits_after_dot;
|
||||||
fieldset_add_field(fs,f);
|
fieldset_add_field(fs, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For each field of the destination filedset 'dst', look for a matching
|
/* For each field of the destination filedset 'dst', look for a matching
|
||||||
* field name/type in the source fieldset 'src', and if one is found copy
|
* field name/type in the source fieldset 'src', and if one is found copy
|
||||||
* its value into the 'dst' field. */
|
* its value into the 'dst' field. */
|
||||||
void fieldset_copy_matching_fields(ProtoViewFieldSet *dst,
|
void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src) {
|
||||||
ProtoViewFieldSet *src)
|
for(uint32_t j = 0; j < dst->numfields; j++) {
|
||||||
{
|
for(uint32_t i = 0; i < src->numfields; i++) {
|
||||||
for (uint32_t j = 0; j < dst->numfields; j++) {
|
if(dst->fields[j]->type == src->fields[i]->type &&
|
||||||
for (uint32_t i = 0; i < src->numfields; i++) {
|
!strcmp(dst->fields[j]->name, src->fields[i]->name)) {
|
||||||
if (dst->fields[j]->type == src->fields[i]->type &&
|
field_set_from_field(dst->fields[j], src->fields[i]);
|
||||||
!strcmp(dst->fields[j]->name,src->fields[i]->name))
|
|
||||||
{
|
|
||||||
field_set_from_field(dst->fields[j],
|
|
||||||
src->fields[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
#include "../app.h"
|
#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) {
|
||||||
if (numbits < 30) return false;
|
if(numbits < 30) return false;
|
||||||
const char *sync_patterns[3] = {
|
const char* sync_patterns[3] = {
|
||||||
"10000000000000000000000000000001", /* 30 zero bits. */
|
"10000000000000000000000000000001", /* 30 zero bits. */
|
||||||
"100000000000000000000000000000001", /* 31 zero bits. */
|
"100000000000000000000000000000001", /* 31 zero bits. */
|
||||||
"1000000000000000000000000000000001", /* 32 zero bits. */
|
"1000000000000000000000000000000001", /* 32 zero bits. */
|
||||||
@@ -19,70 +19,67 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
|
|||||||
|
|
||||||
uint32_t off;
|
uint32_t off;
|
||||||
int j;
|
int j;
|
||||||
for (j = 0; j < 3; j++) {
|
for(j = 0; j < 3; j++) {
|
||||||
off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_patterns[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) break;
|
||||||
}
|
}
|
||||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu",off);
|
if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu", off);
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
|
|
||||||
// Seek data setction. Why -1? Last bit is data.
|
// Seek data setction. Why -1? Last bit is data.
|
||||||
off += strlen(sync_patterns[j])-1;
|
off += strlen(sync_patterns[j]) - 1;
|
||||||
|
|
||||||
uint8_t d[3]; /* 24 bits of data. */
|
uint8_t d[3]; /* 24 bits of data. */
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_line_code(d, sizeof(d), bits, numbytes, off, "1000", "1110");
|
||||||
convert_from_line_code(d,sizeof(d),bits,numbytes,off,"1000","1110");
|
|
||||||
|
|
||||||
if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu",decoded);
|
if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu", decoded);
|
||||||
if (decoded < 24) return false;
|
if(decoded < 24) return false;
|
||||||
|
|
||||||
off += 24*4; // seek to end symbol offset to calculate the length.
|
off += 24 * 4; // seek to end symbol offset to calculate the length.
|
||||||
off++; // In this protocol there is a final pulse as terminator.
|
off++; // In this protocol there is a final pulse as terminator.
|
||||||
info->pulses_count = off - info->start_off;
|
info->pulses_count = off - info->start_off;
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"id",d,5);
|
fieldset_add_bytes(info->fieldset, "id", d, 5);
|
||||||
fieldset_add_uint(info->fieldset,"button",d[2]&0xf,4);
|
fieldset_add_uint(info->fieldset, "button", d[2] & 0xf, 4);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Give fields and defaults for the signal creator. */
|
/* Give fields and defaults for the signal creator. */
|
||||||
static void get_fields(ProtoViewFieldSet *fieldset) {
|
static void get_fields(ProtoViewFieldSet* fieldset) {
|
||||||
uint8_t default_id[3]= {0xAB, 0xCD, 0xE0};
|
uint8_t default_id[3] = {0xAB, 0xCD, 0xE0};
|
||||||
fieldset_add_bytes(fieldset,"id",default_id,5);
|
fieldset_add_bytes(fieldset, "id", default_id, 5);
|
||||||
fieldset_add_uint(fieldset,"button",1,4);
|
fieldset_add_uint(fieldset, "button", 1, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a signal. */
|
/* Create a signal. */
|
||||||
static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fs)
|
static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fs) {
|
||||||
{
|
|
||||||
uint32_t te = 334; // Short pulse duration in microseconds.
|
uint32_t te = 334; // Short pulse duration in microseconds.
|
||||||
|
|
||||||
// Sync: 1 te pulse, 31 te gap.
|
// Sync: 1 te pulse, 31 te gap.
|
||||||
raw_samples_add(samples,true,te);
|
raw_samples_add(samples, true, te);
|
||||||
raw_samples_add(samples,false,te*31);
|
raw_samples_add(samples, false, te * 31);
|
||||||
|
|
||||||
// ID + button state
|
// ID + button state
|
||||||
uint8_t data[3];
|
uint8_t data[3];
|
||||||
memcpy(data,fs->fields[0]->bytes,3);
|
memcpy(data, fs->fields[0]->bytes, 3);
|
||||||
data[2] = (data[2]&0xF0) | (fs->fields[1]->uvalue & 0xF);
|
data[2] = (data[2] & 0xF0) | (fs->fields[1]->uvalue & 0xF);
|
||||||
for (uint32_t j = 0; j < 24; j++) {
|
for(uint32_t j = 0; j < 24; j++) {
|
||||||
if (bitmap_get(data,sizeof(data),j)) {
|
if(bitmap_get(data, sizeof(data), j)) {
|
||||||
raw_samples_add(samples,true,te*3);
|
raw_samples_add(samples, true, te * 3);
|
||||||
raw_samples_add(samples,false,te);
|
raw_samples_add(samples, false, te);
|
||||||
} else {
|
} else {
|
||||||
raw_samples_add(samples,true,te);
|
raw_samples_add(samples, true, te);
|
||||||
raw_samples_add(samples,false,te*3);
|
raw_samples_add(samples, false, te * 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal terminator. Just a single short pulse.
|
// Signal terminator. Just a single short pulse.
|
||||||
raw_samples_add(samples,true,te);
|
raw_samples_add(samples, true, te);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder B4B1Decoder = {
|
ProtoViewDecoder B4B1Decoder = {
|
||||||
.name = "PT/SC remote",
|
.name = "PT/SC remote",
|
||||||
.decode = decode,
|
.decode = decode,
|
||||||
.get_fields = get_fields,
|
.get_fields = get_fields,
|
||||||
.build_message = build_message
|
.build_message = build_message};
|
||||||
};
|
|
||||||
|
|||||||
@@ -24,16 +24,16 @@
|
|||||||
|
|
||||||
#include "../app.h"
|
#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) {
|
||||||
|
|
||||||
/* In the sync pattern, we require the 12 high/low pulses and at least
|
/* In the sync pattern, we require the 12 high/low pulses and at least
|
||||||
* half the gap we expect (5 pulses times, one is the final zero in the
|
* half the gap we expect (5 pulses times, one is the final zero in the
|
||||||
* 24 symbols high/low sequence, then other 4). */
|
* 24 symbols high/low sequence, then other 4). */
|
||||||
const char *sync_pattern = "101010101010101010101010" "0000";
|
const char* sync_pattern = "101010101010101010101010"
|
||||||
uint8_t sync_len = 24+4;
|
"0000";
|
||||||
if (numbits-sync_len+sync_len < 3*66) return false;
|
uint8_t sync_len = 24 + 4;
|
||||||
uint32_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
if(numbits - sync_len + sync_len < 3 * 66) return false;
|
||||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
uint32_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||||
|
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||||
|
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
off += sync_len; // Seek start of message.
|
off += sync_len; // Seek start of message.
|
||||||
@@ -42,84 +42,77 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
|
|||||||
* symbols of gap, to avoid missing the signal for a matter of wrong
|
* symbols of gap, to avoid missing the signal for a matter of wrong
|
||||||
* timing. */
|
* timing. */
|
||||||
uint8_t gap_len = 0;
|
uint8_t gap_len = 0;
|
||||||
while(gap_len <= 7 && bitmap_get(bits,numbytes,off+gap_len) == 0)
|
while(gap_len <= 7 && bitmap_get(bits, numbytes, off + gap_len) == 0) gap_len++;
|
||||||
gap_len++;
|
if(gap_len < 3 || gap_len > 7) return false;
|
||||||
if (gap_len < 3 || gap_len > 7) return false;
|
|
||||||
|
|
||||||
off += gap_len;
|
off += gap_len;
|
||||||
FURI_LOG_E(TAG, "Keeloq preamble+sync found");
|
FURI_LOG_E(TAG, "Keeloq preamble+sync found");
|
||||||
|
|
||||||
uint8_t raw[9] = {0};
|
uint8_t raw[9] = {0};
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_line_code(
|
||||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
raw, sizeof(raw), bits, numbytes, off, "110", "100"); /* Pulse width modulation. */
|
||||||
"110","100"); /* Pulse width modulation. */
|
|
||||||
FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded);
|
FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded);
|
||||||
if (decoded < 66) return false; /* Require the full 66 bits. */
|
if(decoded < 66) return false; /* Require the full 66 bits. */
|
||||||
|
|
||||||
info->pulses_count = (off+66*3) - info->start_off;
|
info->pulses_count = (off + 66 * 3) - info->start_off;
|
||||||
|
|
||||||
bitmap_reverse_bytes_bits(raw,sizeof(raw)); /* Keeloq is LSB first. */
|
bitmap_reverse_bytes_bits(raw, sizeof(raw)); /* Keeloq is LSB first. */
|
||||||
|
|
||||||
int buttons = raw[7]>>4;
|
int buttons = raw[7] >> 4;
|
||||||
int lowbat = (raw[8]&0x1) == 0; // Actual bit meaning: good battery level
|
int lowbat = (raw[8] & 0x1) == 0; // Actual bit meaning: good battery level
|
||||||
int alwaysone = (raw[8]&0x2) != 0;
|
int alwaysone = (raw[8] & 0x2) != 0;
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"encr",raw,8);
|
fieldset_add_bytes(info->fieldset, "encr", raw, 8);
|
||||||
raw[7] = raw[7]<<4; // Make ID bits contiguous
|
raw[7] = raw[7] << 4; // Make ID bits contiguous
|
||||||
fieldset_add_bytes(info->fieldset,"id",raw+4,7); // 28 bits, 7 nibbles
|
fieldset_add_bytes(info->fieldset, "id", raw + 4, 7); // 28 bits, 7 nibbles
|
||||||
fieldset_add_bin(info->fieldset,"s[2,1,0,3]",buttons,4);
|
fieldset_add_bin(info->fieldset, "s[2,1,0,3]", buttons, 4);
|
||||||
fieldset_add_bin(info->fieldset,"low battery",lowbat,1);
|
fieldset_add_bin(info->fieldset, "low battery", lowbat, 1);
|
||||||
fieldset_add_bin(info->fieldset,"always one",alwaysone,1);
|
fieldset_add_bin(info->fieldset, "always one", alwaysone, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_fields(ProtoViewFieldSet *fieldset) {
|
static void get_fields(ProtoViewFieldSet* fieldset) {
|
||||||
uint8_t remote_id[4] = {0xab, 0xcd, 0xef, 0xa0};
|
uint8_t remote_id[4] = {0xab, 0xcd, 0xef, 0xa0};
|
||||||
uint8_t encr[4] = {0xab, 0xab, 0xab, 0xab};
|
uint8_t encr[4] = {0xab, 0xab, 0xab, 0xab};
|
||||||
fieldset_add_bytes(fieldset,"encr",encr,8);
|
fieldset_add_bytes(fieldset, "encr", encr, 8);
|
||||||
fieldset_add_bytes(fieldset,"id",remote_id,7);
|
fieldset_add_bytes(fieldset, "id", remote_id, 7);
|
||||||
fieldset_add_bin(fieldset,"s[2,1,0,3]",2,4);
|
fieldset_add_bin(fieldset, "s[2,1,0,3]", 2, 4);
|
||||||
fieldset_add_bin(fieldset,"low battery",0,1);
|
fieldset_add_bin(fieldset, "low battery", 0, 1);
|
||||||
fieldset_add_bin(fieldset,"always one",1,1);
|
fieldset_add_bin(fieldset, "always one", 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset)
|
static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) {
|
||||||
{
|
|
||||||
uint32_t te = 380; // Short pulse duration in microseconds.
|
uint32_t te = 380; // Short pulse duration in microseconds.
|
||||||
|
|
||||||
// Sync: 12 pairs of pulse/gap + 9 times gap
|
// Sync: 12 pairs of pulse/gap + 9 times gap
|
||||||
for (int j = 0; j < 12; j++) {
|
for(int j = 0; j < 12; j++) {
|
||||||
raw_samples_add(samples,true,te);
|
raw_samples_add(samples, true, te);
|
||||||
raw_samples_add(samples,false,te);
|
raw_samples_add(samples, false, te);
|
||||||
}
|
}
|
||||||
raw_samples_add(samples,false,te*9);
|
raw_samples_add(samples, false, te * 9);
|
||||||
|
|
||||||
// Data, 66 bits.
|
// Data, 66 bits.
|
||||||
uint8_t data[9] = {0};
|
uint8_t data[9] = {0};
|
||||||
memcpy(data,fieldset->fields[0]->bytes,4); // Encrypted part.
|
memcpy(data, fieldset->fields[0]->bytes, 4); // Encrypted part.
|
||||||
memcpy(data+4,fieldset->fields[1]->bytes,4); // ID.
|
memcpy(data + 4, fieldset->fields[1]->bytes, 4); // ID.
|
||||||
data[7] = data[7]>>4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3]
|
data[7] = data[7] >> 4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3]
|
||||||
int low_battery = fieldset->fields[3] != 0;
|
int low_battery = fieldset->fields[3] != 0;
|
||||||
int always_one = fieldset->fields[4] != 0;
|
int always_one = fieldset->fields[4] != 0;
|
||||||
low_battery = !low_battery; // Bit real meaning is good battery level.
|
low_battery = !low_battery; // Bit real meaning is good battery level.
|
||||||
data[8] |= low_battery;
|
data[8] |= low_battery;
|
||||||
data[8] |= (always_one << 1);
|
data[8] |= (always_one << 1);
|
||||||
bitmap_reverse_bytes_bits(data,sizeof(data)); /* Keeloq is LSB first. */
|
bitmap_reverse_bytes_bits(data, sizeof(data)); /* Keeloq is LSB first. */
|
||||||
|
|
||||||
for (int j = 0; j < 66; j++) {
|
for(int j = 0; j < 66; j++) {
|
||||||
if (bitmap_get(data,9,j)) {
|
if(bitmap_get(data, 9, j)) {
|
||||||
raw_samples_add(samples,true,te);
|
raw_samples_add(samples, true, te);
|
||||||
raw_samples_add(samples,false,te*2);
|
raw_samples_add(samples, false, te * 2);
|
||||||
} else {
|
} else {
|
||||||
raw_samples_add(samples,true,te*2);
|
raw_samples_add(samples, true, te * 2);
|
||||||
raw_samples_add(samples,false,te);
|
raw_samples_add(samples, false, te);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder KeeloqDecoder = {
|
ProtoViewDecoder KeeloqDecoder =
|
||||||
.name = "Keeloq",
|
{.name = "Keeloq", .decode = decode, .get_fields = get_fields, .build_message = build_message};
|
||||||
.decode = decode,
|
|
||||||
.get_fields = get_fields,
|
|
||||||
.build_message = build_message
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -6,11 +6,14 @@
|
|||||||
|
|
||||||
#include "../app.h"
|
#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) {
|
||||||
if (numbits < 32) return false;
|
if(numbits < 32) return false;
|
||||||
const char *sync_pattern = "01100110" "01100110" "10010110" "10010110";
|
const char* sync_pattern = "01100110"
|
||||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
"01100110"
|
||||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
"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");
|
FURI_LOG_E(TAG, "Oregon2 preamble+sync found");
|
||||||
|
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
@@ -18,50 +21,61 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
|
|||||||
|
|
||||||
uint8_t buffer[8], raw[8] = {0};
|
uint8_t buffer[8], raw[8] = {0};
|
||||||
uint32_t decoded =
|
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);
|
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. */
|
||||||
info->pulses_count = (off+11*4*4) - info->start_off;
|
info->pulses_count = (off + 11 * 4 * 4) - info->start_off;
|
||||||
|
|
||||||
char temp[3] = {0}, hum[2] = {0};
|
char temp[3] = {0}, hum[2] = {0};
|
||||||
uint8_t deviceid[2];
|
uint8_t deviceid[2];
|
||||||
for (int j = 0; j < 64; j += 4) {
|
for(int j = 0; j < 64; j += 4) {
|
||||||
uint8_t nib[1];
|
uint8_t nib[1];
|
||||||
nib[0] = (bitmap_get(buffer,8,j+0) |
|
nib[0] =
|
||||||
bitmap_get(buffer,8,j+1) << 1 |
|
(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 + 2) << 2 | bitmap_get(buffer, 8, j + 3) << 3);
|
||||||
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]);
|
||||||
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));
|
||||||
raw[j/8] |= nib[0] << (4-(j%4));
|
switch(j / 4) {
|
||||||
switch(j/4) {
|
case 1:
|
||||||
case 1: deviceid[0] |= nib[0]; break;
|
deviceid[0] |= nib[0];
|
||||||
case 0: deviceid[0] |= nib[0] << 4; break;
|
break;
|
||||||
case 3: deviceid[1] |= nib[0]; break;
|
case 0:
|
||||||
case 2: deviceid[1] |= nib[0] << 4; break;
|
deviceid[0] |= nib[0] << 4;
|
||||||
case 10: temp[0] = nib[0]; break;
|
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. */
|
/* Fixme: take the temperature sign from nibble 11. */
|
||||||
case 9: temp[1] = nib[0]; break;
|
case 9:
|
||||||
case 8: temp[2] = nib[0]; break;
|
temp[1] = nib[0];
|
||||||
case 13: hum[0] = nib[0]; break;
|
break;
|
||||||
case 12: hum[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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float tempval = ((temp[0]-'0')*10) +
|
float tempval = ((temp[0] - '0') * 10) + (temp[1] - '0') + ((float)(temp[2] - '0') * 0.1);
|
||||||
(temp[1]-'0') +
|
int humval = (hum[0] - '0') * 10 + (hum[1] - '0');
|
||||||
((float)(temp[2]-'0')*0.1);
|
|
||||||
int humval = (hum[0]-'0')*10 + (hum[1]-'0');
|
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"Sensor ID",deviceid,4);
|
fieldset_add_bytes(info->fieldset, "Sensor ID", deviceid, 4);
|
||||||
fieldset_add_float(info->fieldset,"Temperature",tempval,1);
|
fieldset_add_float(info->fieldset, "Temperature", tempval, 1);
|
||||||
fieldset_add_uint(info->fieldset,"Humidity",humval,7);
|
fieldset_add_uint(info->fieldset, "Humidity", humval, 7);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder Oregon2Decoder = {
|
ProtoViewDecoder Oregon2Decoder =
|
||||||
.name = "Oregon2",
|
{.name = "Oregon2", .decode = decode, .get_fields = NULL, .build_message = NULL};
|
||||||
.decode = decode,
|
|
||||||
.get_fields = NULL,
|
|
||||||
.build_message = NULL
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -7,55 +7,49 @@
|
|||||||
|
|
||||||
#include "../../app.h"
|
#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
|
/* 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
|
* is more likely to happen if we don't pretend to receive from the
|
||||||
* very start of the message. */
|
* very start of the message. */
|
||||||
uint32_t sync_len = 17;
|
uint32_t sync_len = 17;
|
||||||
const char *sync_pattern = "10101010101010110";
|
const char* sync_pattern = "10101010101010110";
|
||||||
if (numbits-sync_len < 8*10) return false; /* Expect 10 bytes. */
|
if(numbits - sync_len < 8 * 10) return false; /* Expect 10 bytes. */
|
||||||
|
|
||||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||||
FURI_LOG_E(TAG, "Renault TPMS preamble+sync found");
|
FURI_LOG_E(TAG, "Renault TPMS preamble+sync found");
|
||||||
|
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
off += sync_len; /* Skip preamble + sync. */
|
off += sync_len; /* Skip preamble + sync. */
|
||||||
|
|
||||||
uint8_t raw[10];
|
uint8_t raw[10];
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_line_code(
|
||||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
|
||||||
"01","10"); /* Manchester. */
|
|
||||||
FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded);
|
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
|
/* 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
|
* is not included. The meaning of the first byte is unknown and
|
||||||
* we don't display it. */
|
* we don't display it. */
|
||||||
uint8_t crc = 0;
|
uint8_t crc = 0;
|
||||||
for (int j = 1; j < 10; j++) crc ^= raw[j];
|
for(int j = 1; j < 10; j++) crc ^= raw[j];
|
||||||
if (crc != 0) return false; /* Require sane checksum. */
|
if(crc != 0) return false; /* Require sane checksum. */
|
||||||
|
|
||||||
info->pulses_count = (off+8*10*2) - info->start_off;
|
info->pulses_count = (off + 8 * 10 * 2) - info->start_off;
|
||||||
|
|
||||||
int repeat = raw[5] & 0xf;
|
int repeat = raw[5] & 0xf;
|
||||||
float kpa = (float)raw[6]*1.364;
|
float kpa = (float)raw[6] * 1.364;
|
||||||
int temp = raw[7]-50;
|
int temp = raw[7] - 50;
|
||||||
int battery = raw[8]; /* This may be the battery. It's not clear. */
|
int battery = raw[8]; /* This may be the battery. It's not clear. */
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"Tire ID",raw+1,4*2);
|
fieldset_add_bytes(info->fieldset, "Tire ID", raw + 1, 4 * 2);
|
||||||
fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2);
|
fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2);
|
||||||
fieldset_add_int(info->fieldset,"Temperature C",temp,8);
|
fieldset_add_int(info->fieldset, "Temperature C", temp, 8);
|
||||||
fieldset_add_uint(info->fieldset,"Repeat",repeat,4);
|
fieldset_add_uint(info->fieldset, "Repeat", repeat, 4);
|
||||||
fieldset_add_uint(info->fieldset,"Battery",battery,8);
|
fieldset_add_uint(info->fieldset, "Battery", battery, 8);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder CitroenTPMSDecoder = {
|
ProtoViewDecoder CitroenTPMSDecoder =
|
||||||
.name = "Citroen TPMS",
|
{.name = "Citroen TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL};
|
||||||
.decode = decode,
|
|
||||||
.get_fields = NULL,
|
|
||||||
.build_message = NULL
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -10,54 +10,49 @@
|
|||||||
|
|
||||||
#include "../../app.h"
|
#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";
|
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||||
uint8_t sync_len = 12+4; /* We just use 12 preamble symbols + sync. */
|
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||||
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;
|
|
||||||
FURI_LOG_E(TAG, "Fort TPMS preamble+sync found");
|
FURI_LOG_E(TAG, "Fort TPMS preamble+sync found");
|
||||||
|
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
off += sync_len; /* Skip preamble and sync. */
|
off += sync_len; /* Skip preamble and sync. */
|
||||||
|
|
||||||
uint8_t raw[8];
|
uint8_t raw[8];
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_line_code(
|
||||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
|
||||||
"01","10"); /* Manchester. */
|
|
||||||
FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded);
|
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. */
|
/* CRC is just the sum of the first 7 bytes MOD 256. */
|
||||||
uint8_t crc = 0;
|
uint8_t crc = 0;
|
||||||
for (int j = 0; j < 7; j++) crc += raw[j];
|
for(int j = 0; j < 7; j++) crc += raw[j];
|
||||||
if (crc != raw[7]) return false; /* Require sane CRC. */
|
if(crc != raw[7]) return false; /* Require sane CRC. */
|
||||||
|
|
||||||
info->pulses_count = (off+8*8*2) - info->start_off;
|
info->pulses_count = (off + 8 * 8 * 2) - info->start_off;
|
||||||
|
|
||||||
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
|
/* Temperature apperas to be valid only if the most significant
|
||||||
* bit of the value is not set. Otherwise its meaning is unknown.
|
* bit of the value is not set. Otherwise its meaning is unknown.
|
||||||
* Likely useful to alternatively send temperature or other info. */
|
* 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 flags = raw[5] & 0x7f;
|
||||||
int car_moving = (raw[6] & 0x44) == 0x44;
|
int car_moving = (raw[6] & 0x44) == 0x44;
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2);
|
fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2);
|
||||||
fieldset_add_float(info->fieldset,"Pressure psi",psi,2);
|
fieldset_add_float(info->fieldset, "Pressure psi", psi, 2);
|
||||||
fieldset_add_int(info->fieldset,"Temperature C",temp,8);
|
fieldset_add_int(info->fieldset, "Temperature C", temp, 8);
|
||||||
fieldset_add_hex(info->fieldset,"Flags",flags,7);
|
fieldset_add_hex(info->fieldset, "Flags", flags, 7);
|
||||||
fieldset_add_uint(info->fieldset,"Moving",car_moving,1);
|
fieldset_add_uint(info->fieldset, "Moving", car_moving, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder FordTPMSDecoder = {
|
ProtoViewDecoder FordTPMSDecoder =
|
||||||
.name = "Ford TPMS",
|
{.name = "Ford TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL};
|
||||||
.decode = decode,
|
|
||||||
.get_fields = NULL,
|
|
||||||
.build_message = NULL
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -6,85 +6,82 @@
|
|||||||
#include "../../app.h"
|
#include "../../app.h"
|
||||||
|
|
||||||
#define USE_TEST_VECTOR 0
|
#define USE_TEST_VECTOR 0
|
||||||
static const char *test_vector =
|
static const char* test_vector =
|
||||||
"...01010101010101010110" // Preamble + sync
|
"...01010101010101010110" // Preamble + sync
|
||||||
|
|
||||||
/* The following is Marshal encoded, so each two characters are
|
/* The following is Marshal encoded, so each two characters are
|
||||||
* actaully one bit. 01 = 0, 10 = 1. */
|
* actaully one bit. 01 = 0, 10 = 1. */
|
||||||
"010110010110" // Flags.
|
"010110010110" // Flags.
|
||||||
"10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa.
|
"10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa.
|
||||||
// 244 kpa here.
|
// 244 kpa here.
|
||||||
"1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here.
|
"1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here.
|
||||||
"1001010101101001"
|
"1001010101101001"
|
||||||
"0101100110010101"
|
"0101100110010101"
|
||||||
"1001010101100110" // Tire ID. 0x7AD779 here.
|
"1001010101100110" // Tire ID. 0x7AD779 here.
|
||||||
"0101010101010101"
|
"0101010101010101"
|
||||||
"0101010101010101" // Two FF bytes (usually). Unknown.
|
"0101010101010101" // Two FF bytes (usually). Unknown.
|
||||||
"0110010101010101"; // CRC8 with (poly 7, initialization 0).
|
"0110010101010101"; // CRC8 with (poly 7, initialization 0).
|
||||||
|
|
||||||
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) {
|
||||||
|
if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
|
||||||
if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
|
bitmap_set_pattern(bits, numbytes, 0, test_vector);
|
||||||
bitmap_set_pattern(bits,numbytes,0,test_vector);
|
|
||||||
numbits = strlen(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";
|
const char* sync_pattern = "01010101010101010110";
|
||||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||||
if (off == BITMAP_SEEK_NOT_FOUND) return false;
|
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||||
FURI_LOG_E(TAG, "Renault TPMS preamble+sync found");
|
FURI_LOG_E(TAG, "Renault TPMS preamble+sync found");
|
||||||
|
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
off += 20; /* Skip preamble. */
|
off += 20; /* Skip preamble. */
|
||||||
|
|
||||||
uint8_t raw[9];
|
uint8_t raw[9];
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_line_code(
|
||||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
|
||||||
"01","10"); /* Manchester. */
|
|
||||||
FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded);
|
FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded);
|
||||||
|
|
||||||
if (decoded < 8*9) return false; /* Require the full 9 bytes. */
|
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(crc8(raw, 8, 0, 7) != raw[8]) return false; /* Require sane CRC. */
|
||||||
|
|
||||||
info->pulses_count = (off+8*9*2) - info->start_off;
|
info->pulses_count = (off + 8 * 9 * 2) - info->start_off;
|
||||||
|
|
||||||
uint8_t flags = raw[0]>>2;
|
uint8_t flags = raw[0] >> 2;
|
||||||
float kpa = 0.75 * ((uint32_t)((raw[0]&3)<<8) | raw[1]);
|
float kpa = 0.75 * ((uint32_t)((raw[0] & 3) << 8) | raw[1]);
|
||||||
int temp = raw[2]-30;
|
int temp = raw[2] - 30;
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"Tire ID",raw+3,3*2);
|
fieldset_add_bytes(info->fieldset, "Tire ID", raw + 3, 3 * 2);
|
||||||
fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2);
|
fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2);
|
||||||
fieldset_add_int(info->fieldset,"Temperature C",temp,8);
|
fieldset_add_int(info->fieldset, "Temperature C", temp, 8);
|
||||||
fieldset_add_hex(info->fieldset,"Flags",flags,6);
|
fieldset_add_hex(info->fieldset, "Flags", flags, 6);
|
||||||
fieldset_add_bytes(info->fieldset,"Unknown1",raw+6,2);
|
fieldset_add_bytes(info->fieldset, "Unknown1", raw + 6, 2);
|
||||||
fieldset_add_bytes(info->fieldset,"Unknown2",raw+7,2);
|
fieldset_add_bytes(info->fieldset, "Unknown2", raw + 7, 2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Give fields and defaults for the signal creator. */
|
/* Give fields and defaults for the signal creator. */
|
||||||
static void get_fields(ProtoViewFieldSet *fieldset) {
|
static void get_fields(ProtoViewFieldSet* fieldset) {
|
||||||
uint8_t default_id[3]= {0xAB, 0xCD, 0xEF};
|
uint8_t default_id[3] = {0xAB, 0xCD, 0xEF};
|
||||||
fieldset_add_bytes(fieldset,"Tire ID",default_id,3*2);
|
fieldset_add_bytes(fieldset, "Tire ID", default_id, 3 * 2);
|
||||||
fieldset_add_float(fieldset,"Pressure kpa",123,2);
|
fieldset_add_float(fieldset, "Pressure kpa", 123, 2);
|
||||||
fieldset_add_int(fieldset,"Temperature C",20,8);
|
fieldset_add_int(fieldset, "Temperature C", 20, 8);
|
||||||
// We don't know what flags are, but 1B is a common value.
|
// We don't know what flags are, but 1B is a common value.
|
||||||
fieldset_add_hex(fieldset,"Flags",0x1b,6);
|
fieldset_add_hex(fieldset, "Flags", 0x1b, 6);
|
||||||
fieldset_add_bytes(fieldset,"Unknown1",(uint8_t*)"\xff",2);
|
fieldset_add_bytes(fieldset, "Unknown1", (uint8_t*)"\xff", 2);
|
||||||
fieldset_add_bytes(fieldset,"Unknown2",(uint8_t*)"\xff",2);
|
fieldset_add_bytes(fieldset, "Unknown2", (uint8_t*)"\xff", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a Renault TPMS signal, according to the fields provided. */
|
/* Create a Renault TPMS signal, according to the fields provided. */
|
||||||
static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset)
|
static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) {
|
||||||
{
|
|
||||||
uint32_t te = 50; // Short pulse duration in microseconds.
|
uint32_t te = 50; // Short pulse duration in microseconds.
|
||||||
|
|
||||||
// Preamble + sync
|
// Preamble + sync
|
||||||
const char *psync = "01010101010101010101010101010110";
|
const char* psync = "01010101010101010101010101010110";
|
||||||
const char *p = psync;
|
const char* p = psync;
|
||||||
while(*p) {
|
while(*p) {
|
||||||
raw_samples_add_or_update(samples,*p == '1',te);
|
raw_samples_add_or_update(samples, *p == '1', te);
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,21 +90,21 @@ static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset
|
|||||||
unsigned int raw_pressure = fieldset->fields[1]->fvalue * 4 / 3;
|
unsigned int raw_pressure = fieldset->fields[1]->fvalue * 4 / 3;
|
||||||
data[0] = fieldset->fields[3]->uvalue << 2; // Flags
|
data[0] = fieldset->fields[3]->uvalue << 2; // Flags
|
||||||
data[0] |= (raw_pressure >> 8) & 3; // Pressure kpa high 2 bits
|
data[0] |= (raw_pressure >> 8) & 3; // Pressure kpa high 2 bits
|
||||||
data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits
|
data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits
|
||||||
data[2] = fieldset->fields[2]->value + 30; // Temperature C
|
data[2] = fieldset->fields[2]->value + 30; // Temperature C
|
||||||
memcpy(data+3,fieldset->fields[0]->bytes,6); // ID, 24 bits.
|
memcpy(data + 3, fieldset->fields[0]->bytes, 6); // ID, 24 bits.
|
||||||
data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1
|
data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1
|
||||||
data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2
|
data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2
|
||||||
data[8] = crc8(data,8,0,7);
|
data[8] = crc8(data, 8, 0, 7);
|
||||||
|
|
||||||
// Generate Manchester code for each bit
|
// Generate Manchester code for each bit
|
||||||
for (uint32_t j = 0; j < 9*8; j++) {
|
for(uint32_t j = 0; j < 9 * 8; j++) {
|
||||||
if (bitmap_get(data,sizeof(data),j)) {
|
if(bitmap_get(data, sizeof(data), j)) {
|
||||||
raw_samples_add_or_update(samples,true,te);
|
raw_samples_add_or_update(samples, true, te);
|
||||||
raw_samples_add_or_update(samples,false,te);
|
raw_samples_add_or_update(samples, false, te);
|
||||||
} else {
|
} else {
|
||||||
raw_samples_add_or_update(samples,false,te);
|
raw_samples_add_or_update(samples, false, te);
|
||||||
raw_samples_add_or_update(samples,true,te);
|
raw_samples_add_or_update(samples, true, te);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,5 +113,4 @@ ProtoViewDecoder RenaultTPMSDecoder = {
|
|||||||
.name = "Renault TPMS",
|
.name = "Renault TPMS",
|
||||||
.decode = decode,
|
.decode = decode,
|
||||||
.get_fields = get_fields,
|
.get_fields = get_fields,
|
||||||
.build_message = build_message
|
.build_message = build_message};
|
||||||
};
|
|
||||||
|
|||||||
@@ -11,20 +11,21 @@
|
|||||||
#include "../../app.h"
|
#include "../../app.h"
|
||||||
|
|
||||||
#define USE_TEST_VECTOR 0
|
#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) {
|
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. */
|
||||||
if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
|
bitmap_set_pattern(bits, numbytes, 0, test_vector);
|
||||||
bitmap_set_pattern(bits,numbytes,0,test_vector);
|
|
||||||
numbits = strlen(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";
|
const char* sync_pattern = "1111010101"
|
||||||
uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
|
"01011010";
|
||||||
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, "Schrader TPMS gap+preamble found");
|
FURI_LOG_E(TAG, "Schrader TPMS gap+preamble found");
|
||||||
|
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
@@ -34,38 +35,33 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
|
|||||||
|
|
||||||
uint8_t raw[8];
|
uint8_t raw[8];
|
||||||
uint8_t id[4];
|
uint8_t id[4];
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_line_code(
|
||||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */
|
||||||
"01","10"); /* Manchester code. */
|
|
||||||
FURI_LOG_E(TAG, "Schrader TPMS decoded bits: %lu", decoded);
|
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.
|
raw[0] |= 0xf0; // Fix the preamble nibble for checksum computation.
|
||||||
uint8_t cksum = crc8(raw,sizeof(raw)-1,0xf0,0x7);
|
uint8_t cksum = crc8(raw, sizeof(raw) - 1, 0xf0, 0x7);
|
||||||
if (cksum != raw[7]) {
|
if(cksum != raw[7]) {
|
||||||
FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch");
|
FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->pulses_count = (off+8*8*2) - info->start_off;
|
info->pulses_count = (off + 8 * 8 * 2) - info->start_off;
|
||||||
|
|
||||||
float kpa = (float)raw[5]*2.5;
|
float kpa = (float)raw[5] * 2.5;
|
||||||
int temp = raw[6]-50;
|
int temp = raw[6] - 50;
|
||||||
id[0] = raw[1]&7;
|
id[0] = raw[1] & 7;
|
||||||
id[1] = raw[2];
|
id[1] = raw[2];
|
||||||
id[2] = raw[3];
|
id[2] = raw[3];
|
||||||
id[3] = raw[4];
|
id[3] = raw[4];
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"Tire ID",id,4*2);
|
fieldset_add_bytes(info->fieldset, "Tire ID", id, 4 * 2);
|
||||||
fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2);
|
fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2);
|
||||||
fieldset_add_int(info->fieldset,"Temperature C",temp,8);
|
fieldset_add_int(info->fieldset, "Temperature C", temp, 8);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder SchraderTPMSDecoder = {
|
ProtoViewDecoder SchraderTPMSDecoder =
|
||||||
.name = "Schrader TPMS",
|
{.name = "Schrader TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL};
|
||||||
.decode = decode,
|
|
||||||
.get_fields = NULL,
|
|
||||||
.build_message = NULL
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -15,50 +15,45 @@
|
|||||||
|
|
||||||
#include "../../app.h"
|
#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"
|
||||||
|
"01100101";
|
||||||
|
uint8_t sync_len = 12 + 8; /* We just use 12 preamble symbols + sync. */
|
||||||
|
if(numbits - sync_len + 8 < 8 * 10) return false;
|
||||||
|
|
||||||
const char *sync_pattern = "010101010101" "01100101";
|
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
|
||||||
uint8_t sync_len = 12+8; /* We just use 12 preamble symbols + sync. */
|
if(off == BITMAP_SEEK_NOT_FOUND) return false;
|
||||||
if (numbits-sync_len+8 < 8*10) 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, "Schrader EG53MA4 TPMS preamble+sync found");
|
FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS preamble+sync found");
|
||||||
|
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
off += sync_len-8; /* Skip preamble, not sync that is part of the data. */
|
off += sync_len - 8; /* Skip preamble, not sync that is part of the data. */
|
||||||
|
|
||||||
uint8_t raw[10];
|
uint8_t raw[10];
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_line_code(
|
||||||
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
|
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */
|
||||||
"01","10"); /* Manchester code. */
|
|
||||||
FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS decoded bits: %lu", decoded);
|
FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS decoded bits: %lu", decoded);
|
||||||
|
|
||||||
if (decoded < 10*8) return false; /* Require the full 10 bytes. */
|
if(decoded < 10 * 8) return false; /* Require the full 10 bytes. */
|
||||||
|
|
||||||
/* CRC is just all bytes added mod 256. */
|
/* CRC is just all bytes added mod 256. */
|
||||||
uint8_t crc = 0;
|
uint8_t crc = 0;
|
||||||
for (int j = 0; j < 9; j++) crc += raw[j];
|
for(int j = 0; j < 9; j++) crc += raw[j];
|
||||||
if (crc != raw[9]) return false; /* Require sane CRC. */
|
if(crc != raw[9]) return false; /* Require sane CRC. */
|
||||||
|
|
||||||
info->pulses_count = (off+10*8*2) - info->start_off;
|
info->pulses_count = (off + 10 * 8 * 2) - info->start_off;
|
||||||
|
|
||||||
/* To convert the raw pressure to kPa, RTL433 uses 2.5, but is likely
|
/* To convert the raw pressure to kPa, RTL433 uses 2.5, but is likely
|
||||||
* wrong. Searching on Google for users experimenting with the value
|
* wrong. Searching on Google for users experimenting with the value
|
||||||
* reported, the value appears to be 2.75. */
|
* reported, the value appears to be 2.75. */
|
||||||
float kpa = (float)raw[7]*2.75;
|
float kpa = (float)raw[7] * 2.75;
|
||||||
int temp_f = raw[8];
|
int temp_f = raw[8];
|
||||||
int temp_c = (temp_f-32)*5/9; /* Convert Fahrenheit to Celsius. */
|
int temp_c = (temp_f - 32) * 5 / 9; /* Convert Fahrenheit to Celsius. */
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"Tire ID",raw+4,3*2);
|
fieldset_add_bytes(info->fieldset, "Tire ID", raw + 4, 3 * 2);
|
||||||
fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2);
|
fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2);
|
||||||
fieldset_add_int(info->fieldset,"Temperature C",temp_c,8);
|
fieldset_add_int(info->fieldset, "Temperature C", temp_c, 8);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder SchraderEG53MA4TPMSDecoder = {
|
ProtoViewDecoder SchraderEG53MA4TPMSDecoder =
|
||||||
.name = "Schrader EG53MA4 TPMS",
|
{.name = "Schrader EG53MA4 TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL};
|
||||||
.decode = decode,
|
|
||||||
.get_fields = NULL,
|
|
||||||
.build_message = NULL
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -24,40 +24,33 @@
|
|||||||
|
|
||||||
#include "../../app.h"
|
#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) {
|
||||||
|
if(numbits - 6 < 64 * 2)
|
||||||
if (numbits-6 < 64*2) return false; /* Ask for 64 bit of data (each bit
|
return false; /* Ask for 64 bit of data (each bit
|
||||||
is two symbols in the bitmap). */
|
is two symbols in the bitmap). */
|
||||||
|
|
||||||
char *sync[] = {
|
char* sync[] = {"00111100", "001111100", "00111101", "001111101", NULL};
|
||||||
"00111100",
|
|
||||||
"001111100",
|
|
||||||
"00111101",
|
|
||||||
"001111101",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
int j;
|
int j;
|
||||||
uint32_t off = 0;
|
uint32_t off = 0;
|
||||||
for (j = 0; sync[j]; j++) {
|
for(j = 0; sync[j]; j++) {
|
||||||
off = bitmap_seek_bits(bits,numbytes,0,numbits,sync[j]);
|
off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync[j]);
|
||||||
if (off != BITMAP_SEEK_NOT_FOUND) {
|
if(off != BITMAP_SEEK_NOT_FOUND) {
|
||||||
info->start_off = off;
|
info->start_off = off;
|
||||||
off += strlen(sync[j])-2;
|
off += strlen(sync[j]) - 2;
|
||||||
break;
|
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]);
|
FURI_LOG_E(TAG, "Toyota TPMS sync[%s] found", sync[j]);
|
||||||
|
|
||||||
uint8_t raw[9];
|
uint8_t raw[9];
|
||||||
uint32_t decoded =
|
uint32_t decoded = convert_from_diff_manchester(raw, sizeof(raw), bits, numbytes, off, true);
|
||||||
convert_from_diff_manchester(raw,sizeof(raw),bits,numbytes,off,true);
|
|
||||||
FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded);
|
FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded);
|
||||||
|
|
||||||
if (decoded < 8*9) return false; /* Require the full 8 bytes. */
|
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(crc8(raw, 8, 0x80, 7) != raw[8]) return false; /* Require sane CRC. */
|
||||||
|
|
||||||
/* We detected a valid signal. However now info->start_off is actually
|
/* We detected a valid signal. However now info->start_off is actually
|
||||||
* pointing to the sync part, not the preamble of alternating 0 and 1.
|
* pointing to the sync part, not the preamble of alternating 0 and 1.
|
||||||
@@ -65,25 +58,21 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
|
|||||||
* for the decoder itself to fix the signal if neeeded, so that its
|
* for the decoder itself to fix the signal if neeeded, so that its
|
||||||
* logical representation will be more accurate and better to save
|
* logical representation will be more accurate and better to save
|
||||||
* and retransmit. */
|
* and retransmit. */
|
||||||
if (info->start_off >= 12) {
|
if(info->start_off >= 12) {
|
||||||
info->start_off -= 12;
|
info->start_off -= 12;
|
||||||
bitmap_set_pattern(bits,numbytes,info->start_off,"010101010101");
|
bitmap_set_pattern(bits, numbytes, info->start_off, "010101010101");
|
||||||
}
|
}
|
||||||
|
|
||||||
info->pulses_count = (off+8*9*2) - info->start_off;
|
info->pulses_count = (off + 8 * 9 * 2) - info->start_off;
|
||||||
|
|
||||||
float psi = (float)((raw[4]&0x7f)<<1 | raw[5]>>7) * 0.25 - 7;
|
float psi = (float)((raw[4] & 0x7f) << 1 | raw[5] >> 7) * 0.25 - 7;
|
||||||
int temp = ((raw[5]&0x7f)<<1 | raw[6]>>7) - 40;
|
int temp = ((raw[5] & 0x7f) << 1 | raw[6] >> 7) - 40;
|
||||||
|
|
||||||
fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2);
|
fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2);
|
||||||
fieldset_add_float(info->fieldset,"Pressure psi",psi,2);
|
fieldset_add_float(info->fieldset, "Pressure psi", psi, 2);
|
||||||
fieldset_add_int(info->fieldset,"Temperature C",temp,8);
|
fieldset_add_int(info->fieldset, "Temperature C", temp, 8);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoViewDecoder ToyotaTPMSDecoder = {
|
ProtoViewDecoder ToyotaTPMSDecoder =
|
||||||
.name = "Toyota TPMS",
|
{.name = "Toyota TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL};
|
||||||
.decode = decode,
|
|
||||||
.get_fields = NULL,
|
|
||||||
.build_message = NULL
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@
|
|||||||
#include "raw_samples.h"
|
#include "raw_samples.h"
|
||||||
|
|
||||||
/* Allocate and initialize a samples buffer. */
|
/* Allocate and initialize a samples buffer. */
|
||||||
RawSamplesBuffer *raw_samples_alloc(void) {
|
RawSamplesBuffer* raw_samples_alloc(void) {
|
||||||
RawSamplesBuffer *buf = malloc(sizeof(*buf));
|
RawSamplesBuffer* buf = malloc(sizeof(*buf));
|
||||||
buf->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
buf->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
raw_samples_reset(buf);
|
raw_samples_reset(buf);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free a sample buffer. Should be called when the mutex is released. */
|
/* 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);
|
furi_mutex_free(s->mutex);
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
@@ -24,27 +24,27 @@ void raw_samples_free(RawSamplesBuffer *s) {
|
|||||||
/* This just set all the samples to zero and also resets the internal
|
/* 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
|
* index. There is no need to call it after raw_samples_alloc(), but only
|
||||||
* when one wants to reset the whole buffer of samples. */
|
* when one wants to reset the whole buffer of samples. */
|
||||||
void raw_samples_reset(RawSamplesBuffer *s) {
|
void raw_samples_reset(RawSamplesBuffer* s) {
|
||||||
furi_mutex_acquire(s->mutex,FuriWaitForever);
|
furi_mutex_acquire(s->mutex, FuriWaitForever);
|
||||||
s->total = RAW_SAMPLES_NUM;
|
s->total = RAW_SAMPLES_NUM;
|
||||||
s->idx = 0;
|
s->idx = 0;
|
||||||
s->short_pulse_dur = 0;
|
s->short_pulse_dur = 0;
|
||||||
memset(s->samples,0,sizeof(s->samples));
|
memset(s->samples, 0, sizeof(s->samples));
|
||||||
furi_mutex_release(s->mutex);
|
furi_mutex_release(s->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the raw sample internal index so that what is currently at
|
/* Set the raw sample internal index so that what is currently at
|
||||||
* offset 'offset', will appear to be at 0 index. */
|
* offset 'offset', will appear to be at 0 index. */
|
||||||
void raw_samples_center(RawSamplesBuffer *s, uint32_t offset) {
|
void raw_samples_center(RawSamplesBuffer* s, uint32_t offset) {
|
||||||
s->idx = (s->idx+offset) % RAW_SAMPLES_NUM;
|
s->idx = (s->idx + offset) % RAW_SAMPLES_NUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add the specified sample in the circular buffer. */
|
/* Add the specified sample in the circular buffer. */
|
||||||
void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) {
|
void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur) {
|
||||||
furi_mutex_acquire(s->mutex,FuriWaitForever);
|
furi_mutex_acquire(s->mutex, FuriWaitForever);
|
||||||
s->samples[s->idx].level = level;
|
s->samples[s->idx].level = level;
|
||||||
s->samples[s->idx].dur = dur;
|
s->samples[s->idx].dur = dur;
|
||||||
s->idx = (s->idx+1) % RAW_SAMPLES_NUM;
|
s->idx = (s->idx + 1) % RAW_SAMPLES_NUM;
|
||||||
furi_mutex_release(s->mutex);
|
furi_mutex_release(s->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,28 +56,25 @@ void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) {
|
|||||||
*
|
*
|
||||||
* This function is a bit slower so the internal data sampling should
|
* This function is a bit slower so the internal data sampling should
|
||||||
* be performed with raw_samples_add(). */
|
* be performed with raw_samples_add(). */
|
||||||
void raw_samples_add_or_update(RawSamplesBuffer *s, bool level, uint32_t dur) {
|
void raw_samples_add_or_update(RawSamplesBuffer* s, bool level, uint32_t dur) {
|
||||||
furi_mutex_acquire(s->mutex,FuriWaitForever);
|
furi_mutex_acquire(s->mutex, FuriWaitForever);
|
||||||
uint32_t previdx = (s->idx-1) % RAW_SAMPLES_NUM;
|
uint32_t previdx = (s->idx - 1) % RAW_SAMPLES_NUM;
|
||||||
if (s->samples[previdx].level == level &&
|
if(s->samples[previdx].level == level && s->samples[previdx].dur != 0) {
|
||||||
s->samples[previdx].dur != 0)
|
|
||||||
{
|
|
||||||
/* Update the last sample: it has the same level. */
|
/* Update the last sample: it has the same level. */
|
||||||
s->samples[previdx].dur += dur;
|
s->samples[previdx].dur += dur;
|
||||||
} else {
|
} else {
|
||||||
/* Add a new sample. */
|
/* Add a new sample. */
|
||||||
s->samples[s->idx].level = level;
|
s->samples[s->idx].level = level;
|
||||||
s->samples[s->idx].dur = dur;
|
s->samples[s->idx].dur = dur;
|
||||||
s->idx = (s->idx+1) % RAW_SAMPLES_NUM;
|
s->idx = (s->idx + 1) % RAW_SAMPLES_NUM;
|
||||||
}
|
}
|
||||||
furi_mutex_release(s->mutex);
|
furi_mutex_release(s->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the sample from the buffer. It is possible to use out of range indexes
|
/* 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. */
|
* 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)
|
void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur) {
|
||||||
{
|
furi_mutex_acquire(s->mutex, FuriWaitForever);
|
||||||
furi_mutex_acquire(s->mutex,FuriWaitForever);
|
|
||||||
idx = (s->idx + idx) % RAW_SAMPLES_NUM;
|
idx = (s->idx + idx) % RAW_SAMPLES_NUM;
|
||||||
*level = s->samples[idx].level;
|
*level = s->samples[idx].level;
|
||||||
*dur = s->samples[idx].dur;
|
*dur = s->samples[idx].dur;
|
||||||
@@ -85,12 +82,12 @@ void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *d
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Copy one buffer to the other, including current index. */
|
/* Copy one buffer to the other, including current index. */
|
||||||
void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src) {
|
void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src) {
|
||||||
furi_mutex_acquire(src->mutex,FuriWaitForever);
|
furi_mutex_acquire(src->mutex, FuriWaitForever);
|
||||||
furi_mutex_acquire(dst->mutex,FuriWaitForever);
|
furi_mutex_acquire(dst->mutex, FuriWaitForever);
|
||||||
dst->idx = src->idx;
|
dst->idx = src->idx;
|
||||||
dst->short_pulse_dur = src->short_pulse_dur;
|
dst->short_pulse_dur = src->short_pulse_dur;
|
||||||
memcpy(dst->samples,src->samples,sizeof(dst->samples));
|
memcpy(dst->samples, src->samples, sizeof(dst->samples));
|
||||||
furi_mutex_release(src->mutex);
|
furi_mutex_release(src->mutex);
|
||||||
furi_mutex_release(dst->mutex);
|
furi_mutex_release(dst->mutex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,17 @@
|
|||||||
/* Our circular buffer of raw samples, used in order to display
|
/* Our circular buffer of raw samples, used in order to display
|
||||||
* the signal. */
|
* 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
|
of the index quite often to normalize inside
|
||||||
the range, and division is slow. */
|
the range, and division is slow. */
|
||||||
typedef struct RawSamplesBuffer {
|
typedef struct RawSamplesBuffer {
|
||||||
FuriMutex *mutex;
|
FuriMutex* mutex;
|
||||||
struct {
|
struct {
|
||||||
uint16_t level:1;
|
uint16_t level : 1;
|
||||||
uint16_t dur:15;
|
uint16_t dur : 15;
|
||||||
} samples[RAW_SAMPLES_NUM];
|
} samples[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
|
uint32_t total; /* Total samples: same as RAW_SAMPLES_NUM, we provide
|
||||||
this field for a cleaner interface with the user, but
|
this field for a cleaner interface with the user, but
|
||||||
we always use RAW_SAMPLES_NUM when taking the modulo so
|
we always use RAW_SAMPLES_NUM when taking the modulo so
|
||||||
@@ -22,11 +23,11 @@ typedef struct RawSamplesBuffer {
|
|||||||
uint32_t short_pulse_dur; /* Duration of the shortest pulse. */
|
uint32_t short_pulse_dur; /* Duration of the shortest pulse. */
|
||||||
} RawSamplesBuffer;
|
} RawSamplesBuffer;
|
||||||
|
|
||||||
RawSamplesBuffer *raw_samples_alloc(void);
|
RawSamplesBuffer* raw_samples_alloc(void);
|
||||||
void raw_samples_reset(RawSamplesBuffer *s);
|
void raw_samples_reset(RawSamplesBuffer* s);
|
||||||
void raw_samples_center(RawSamplesBuffer *s, uint32_t offset);
|
void raw_samples_center(RawSamplesBuffer* s, uint32_t offset);
|
||||||
void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur);
|
void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur);
|
||||||
void raw_samples_add_or_update(RawSamplesBuffer *s, bool level, uint32_t dur);
|
void raw_samples_add_or_update(RawSamplesBuffer* s, bool level, uint32_t dur);
|
||||||
void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, 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_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src);
|
||||||
void raw_samples_free(RawSamplesBuffer *s);
|
void raw_samples_free(RawSamplesBuffer* s);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info);
|
bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info);
|
||||||
|
|
||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
* Raw signal detection
|
* Raw signal detection
|
||||||
@@ -16,7 +16,7 @@ uint32_t duration_delta(uint32_t a, uint32_t b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Reset the current signal, so that a new one can be detected. */
|
/* 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_bestlen = 0;
|
||||||
app->signal_offset = 0;
|
app->signal_offset = 0;
|
||||||
app->signal_decoded = false;
|
app->signal_decoded = false;
|
||||||
@@ -39,47 +39,47 @@ void reset_current_signal(ProtoViewApp *app) {
|
|||||||
* For instance Oregon2 sensors, in the case of protocol 2.1 will send
|
* For instance Oregon2 sensors, in the case of protocol 2.1 will send
|
||||||
* pulses of ~400us (RF on) VS ~580us (RF off). */
|
* pulses of ~400us (RF on) VS ~580us (RF off). */
|
||||||
#define SEARCH_CLASSES 3
|
#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 {
|
struct {
|
||||||
uint32_t dur[2]; /* dur[0] = low, dur[1] = high */
|
uint32_t dur[2]; /* dur[0] = low, dur[1] = high */
|
||||||
uint32_t count[2]; /* Associated observed frequency. */
|
uint32_t count[2]; /* Associated observed frequency. */
|
||||||
} classes[SEARCH_CLASSES];
|
} 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
|
uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we
|
||||||
allow for high and low. */
|
allow for high and low. */
|
||||||
uint32_t len = 0; /* Observed len of coherent samples. */
|
uint32_t len = 0; /* Observed len of coherent samples. */
|
||||||
s->short_pulse_dur = 0;
|
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;
|
bool level;
|
||||||
uint32_t dur;
|
uint32_t dur;
|
||||||
raw_samples_get(s, j, &level, &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
|
/* Let's see if it matches a class we already have or if we
|
||||||
* can populate a new (yet empty) class. */
|
* can populate a new (yet empty) class. */
|
||||||
uint32_t k;
|
uint32_t k;
|
||||||
for (k = 0; k < SEARCH_CLASSES; k++) {
|
for(k = 0; k < SEARCH_CLASSES; k++) {
|
||||||
if (classes[k].count[level] == 0) {
|
if(classes[k].count[level] == 0) {
|
||||||
classes[k].dur[level] = dur;
|
classes[k].dur[level] = dur;
|
||||||
classes[k].count[level] = 1;
|
classes[k].count[level] = 1;
|
||||||
break; /* Sample accepted. */
|
break; /* Sample accepted. */
|
||||||
} else {
|
} else {
|
||||||
uint32_t classavg = classes[k].dur[level];
|
uint32_t classavg = classes[k].dur[level];
|
||||||
uint32_t count = classes[k].count[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
|
/* Is the difference in duration between this signal and
|
||||||
* the class we are inspecting less than a given percentage?
|
* the class we are inspecting less than a given percentage?
|
||||||
* If so, accept this signal. */
|
* 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
|
/* It is useful to compute the average of the class
|
||||||
* we are observing. We know how many samples we got so
|
* we are observing. We know how many samples we got so
|
||||||
* far, so we can recompute the average easily.
|
* far, so we can recompute the average easily.
|
||||||
* By always having a better estimate of the pulse len
|
* By always having a better estimate of the pulse len
|
||||||
* we can avoid missing next samples in case the first
|
* we can avoid missing next samples in case the first
|
||||||
* observed samples are too off. */
|
* observed samples are too off. */
|
||||||
classavg = ((classavg * count) + dur) / (count+1);
|
classavg = ((classavg * count) + dur) / (count + 1);
|
||||||
classes[k].dur[level] = classavg;
|
classes[k].dur[level] = classavg;
|
||||||
classes[k].count[level]++;
|
classes[k].count[level]++;
|
||||||
break; /* Sample accepted. */
|
break; /* Sample accepted. */
|
||||||
@@ -87,7 +87,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
|
/* If we are here, we accepted this sample. Try with the next
|
||||||
* one. */
|
* one. */
|
||||||
@@ -97,14 +97,12 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
|
|||||||
/* Update the buffer setting the shortest pulse we found
|
/* Update the buffer setting the shortest pulse we found
|
||||||
* among the three classes. This will be used when scaling
|
* among the three classes. This will be used when scaling
|
||||||
* for visualization. */
|
* for visualization. */
|
||||||
uint32_t short_dur[2] = {0,0};
|
uint32_t short_dur[2] = {0, 0};
|
||||||
for (int j = 0; j < SEARCH_CLASSES; j++) {
|
for(int j = 0; j < SEARCH_CLASSES; j++) {
|
||||||
for (int level = 0; level < 2; level++) {
|
for(int level = 0; level < 2; level++) {
|
||||||
if (classes[j].dur[level] == 0) continue;
|
if(classes[j].dur[level] == 0) continue;
|
||||||
if (classes[j].count[level] < 3) continue;
|
if(classes[j].count[level] < 3) continue;
|
||||||
if (short_dur[level] == 0 ||
|
if(short_dur[level] == 0 || short_dur[level] > classes[j].dur[level]) {
|
||||||
short_dur[level] > classes[j].dur[level])
|
|
||||||
{
|
|
||||||
short_dur[level] = classes[j].dur[level];
|
short_dur[level] = classes[j].dur[level];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,33 +111,28 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
|
|||||||
/* Use the average between high and low short pulses duration.
|
/* Use the average between high and low short pulses duration.
|
||||||
* Often they are a bit different, and using the average is more robust
|
* Often they are a bit different, and using the average is more robust
|
||||||
* when we do decoding sampling at short_pulse_dur intervals. */
|
* when we do decoding sampling at short_pulse_dur intervals. */
|
||||||
if (short_dur[0] == 0) short_dur[0] = short_dur[1];
|
if(short_dur[0] == 0) short_dur[0] = short_dur[1];
|
||||||
if (short_dur[1] == 0) short_dur[1] = short_dur[0];
|
if(short_dur[1] == 0) short_dur[1] = short_dur[0];
|
||||||
s->short_pulse_dur = (short_dur[0]+short_dur[1])/2;
|
s->short_pulse_dur = (short_dur[0] + short_dur[1]) / 2;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when we detect a message. Just blinks when the message was
|
/* Called when we detect a message. Just blinks when the message was
|
||||||
* not decoded. Vibrates, too, when the message was correctly decoded. */
|
* not decoded. Vibrates, too, when the message was correctly decoded. */
|
||||||
void notify_signal_detected(ProtoViewApp *app, bool decoded) {
|
void notify_signal_detected(ProtoViewApp* app, bool decoded) {
|
||||||
static const NotificationSequence decoded_seq = {
|
static const NotificationSequence decoded_seq = {
|
||||||
&message_vibro_on,
|
&message_vibro_on,
|
||||||
&message_green_255,
|
&message_green_255,
|
||||||
&message_delay_50,
|
&message_delay_50,
|
||||||
&message_green_0,
|
&message_green_0,
|
||||||
&message_vibro_off,
|
&message_vibro_off,
|
||||||
NULL
|
NULL};
|
||||||
};
|
|
||||||
|
|
||||||
static const NotificationSequence unknown_seq = {
|
static const NotificationSequence unknown_seq = {
|
||||||
&message_red_255,
|
&message_red_255, &message_delay_50, &message_red_0, NULL};
|
||||||
&message_delay_50,
|
|
||||||
&message_red_0,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
if (decoded)
|
if(decoded)
|
||||||
notification_message(app->notification, &decoded_seq);
|
notification_message(app->notification, &decoded_seq);
|
||||||
else
|
else
|
||||||
notification_message(app->notification, &unknown_seq);
|
notification_message(app->notification, &unknown_seq);
|
||||||
@@ -149,57 +142,59 @@ void notify_signal_detected(ProtoViewApp *app, bool decoded) {
|
|||||||
* in order to find a coherent signal. If a signal that does not appear to
|
* 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
|
* be just noise is found, it is set in DetectedSamples global signal
|
||||||
* buffer, that is what is rendered on the screen. */
|
* buffer, that is what is rendered on the screen. */
|
||||||
void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source) {
|
void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) {
|
||||||
/* We need to work on a copy: the source buffer may be populated
|
/* We need to work on a copy: the source buffer may be populated
|
||||||
* by the background thread receiving data. */
|
* by the background thread receiving data. */
|
||||||
RawSamplesBuffer *copy = raw_samples_alloc();
|
RawSamplesBuffer* copy = raw_samples_alloc();
|
||||||
raw_samples_copy(copy,source);
|
raw_samples_copy(copy, source);
|
||||||
|
|
||||||
/* Try to seek on data that looks to have a regular high low high low
|
/* Try to seek on data that looks to have a regular high low high low
|
||||||
* pattern. */
|
* 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
|
than a few samples it's very easy to
|
||||||
mistake noise for signal. */
|
mistake noise for signal. */
|
||||||
|
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
|
|
||||||
while (i < copy->total-1) {
|
while(i < copy->total - 1) {
|
||||||
uint32_t thislen = search_coherent_signal(copy,i);
|
uint32_t thislen = search_coherent_signal(copy, i);
|
||||||
|
|
||||||
/* For messages that are long enough, attempt decoding. */
|
/* For messages that are long enough, attempt decoding. */
|
||||||
if (thislen > minlen) {
|
if(thislen > minlen) {
|
||||||
/* Allocate the message information that some decoder may
|
/* Allocate the message information that some decoder may
|
||||||
* fill, in case it is able to decode a message. */
|
* fill, in case it is able to decode a message. */
|
||||||
ProtoViewMsgInfo *info = malloc(sizeof(ProtoViewMsgInfo));
|
ProtoViewMsgInfo* info = malloc(sizeof(ProtoViewMsgInfo));
|
||||||
init_msg_info(info,app);
|
init_msg_info(info, app);
|
||||||
info->short_pulse_dur = copy->short_pulse_dur;
|
info->short_pulse_dur = copy->short_pulse_dur;
|
||||||
|
|
||||||
uint32_t saved_idx = copy->idx; /* Save index, see later. */
|
uint32_t saved_idx = copy->idx; /* Save index, see later. */
|
||||||
|
|
||||||
/* decode_signal() expects the detected signal to start
|
/* decode_signal() expects the detected signal to start
|
||||||
* from index zero .*/
|
* from index zero .*/
|
||||||
raw_samples_center(copy,i);
|
raw_samples_center(copy, i);
|
||||||
bool decoded = decode_signal(copy,thislen,info);
|
bool decoded = decode_signal(copy, thislen, info);
|
||||||
copy->idx = saved_idx; /* Restore the index as we are scanning
|
copy->idx = saved_idx; /* Restore the index as we are scanning
|
||||||
the signal in the loop. */
|
the signal in the loop. */
|
||||||
|
|
||||||
/* Accept this signal as the new signal if either it's longer
|
/* Accept this signal as the new signal if either it's longer
|
||||||
* than the previous undecoded one, or the previous one was
|
* than the previous undecoded one, or the previous one was
|
||||||
* unknown and this is decoded. */
|
* unknown and this is decoded. */
|
||||||
if ((thislen > app->signal_bestlen && app->signal_decoded == false)
|
if((thislen > app->signal_bestlen && app->signal_decoded == false) ||
|
||||||
|| (app->signal_decoded == false && decoded))
|
(app->signal_decoded == false && decoded)) {
|
||||||
{
|
|
||||||
free_msg_info(app->msg_info);
|
free_msg_info(app->msg_info);
|
||||||
app->msg_info = info;
|
app->msg_info = info;
|
||||||
app->signal_bestlen = thislen;
|
app->signal_bestlen = thislen;
|
||||||
app->signal_decoded = decoded;
|
app->signal_decoded = decoded;
|
||||||
raw_samples_copy(DetectedSamples,copy);
|
raw_samples_copy(DetectedSamples, copy);
|
||||||
raw_samples_center(DetectedSamples,i);
|
raw_samples_center(DetectedSamples, i);
|
||||||
FURI_LOG_E(TAG, "===> Displayed sample updated (%d samples %lu us)",
|
FURI_LOG_E(
|
||||||
(int)thislen, DetectedSamples->short_pulse_dur);
|
TAG,
|
||||||
|
"===> Displayed sample updated (%d samples %lu us)",
|
||||||
|
(int)thislen,
|
||||||
|
DetectedSamples->short_pulse_dur);
|
||||||
|
|
||||||
adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur);
|
adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur);
|
||||||
notify_signal_detected(app,decoded);
|
notify_signal_detected(app, decoded);
|
||||||
} else {
|
} else {
|
||||||
/* If the structure was not filled, discard it. Otherwise
|
/* If the structure was not filled, discard it. Otherwise
|
||||||
* now the owner is app->msg_info. */
|
* now the owner is app->msg_info. */
|
||||||
@@ -227,38 +222,42 @@ void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source) {
|
|||||||
/* Set the 'bitpos' bit to value 'val', in the specified bitmap
|
/* Set the 'bitpos' bit to value 'val', in the specified bitmap
|
||||||
* 'b' of len 'blen'.
|
* 'b' of len 'blen'.
|
||||||
* Out of range bits will silently be discarded. */
|
* Out of range bits will silently be discarded. */
|
||||||
void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) {
|
void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val) {
|
||||||
uint32_t byte = bitpos/8;
|
uint32_t byte = bitpos / 8;
|
||||||
uint32_t bit = 7-(bitpos&7);
|
uint32_t bit = 7 - (bitpos & 7);
|
||||||
if (byte >= blen) return;
|
if(byte >= blen) return;
|
||||||
if (val)
|
if(val)
|
||||||
b[byte] |= 1<<bit;
|
b[byte] |= 1 << bit;
|
||||||
else
|
else
|
||||||
b[byte] &= ~(1<<bit);
|
b[byte] &= ~(1 << bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the bit 'bitpos' of the bitmap 'b' of 'blen' bytes.
|
/* Get the bit 'bitpos' of the bitmap 'b' of 'blen' bytes.
|
||||||
* Out of range bits return false (not bit set). */
|
* Out of range bits return false (not bit set). */
|
||||||
bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos) {
|
bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos) {
|
||||||
uint32_t byte = bitpos/8;
|
uint32_t byte = bitpos / 8;
|
||||||
uint32_t bit = 7-(bitpos&7);
|
uint32_t bit = 7 - (bitpos & 7);
|
||||||
if (byte >= blen) return 0;
|
if(byte >= blen) return 0;
|
||||||
return (b[byte] & (1<<bit)) != 0;
|
return (b[byte] & (1 << bit)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy 'count' bits from the bitmap 's' of 'slen' total bytes, to the
|
/* Copy 'count' bits from the bitmap 's' of 'slen' total bytes, to the
|
||||||
* bitmap 'd' of 'dlen' total bytes. The bits are copied starting from
|
* bitmap 'd' of 'dlen' total bytes. The bits are copied starting from
|
||||||
* offset 'soff' of the source bitmap to the offset 'doff' of the
|
* offset 'soff' of the source bitmap to the offset 'doff' of the
|
||||||
* destination bitmap. */
|
* destination bitmap. */
|
||||||
void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff,
|
void bitmap_copy(
|
||||||
uint8_t *s, uint32_t slen, uint32_t soff,
|
uint8_t* d,
|
||||||
uint32_t count)
|
uint32_t dlen,
|
||||||
{
|
uint32_t doff,
|
||||||
|
uint8_t* s,
|
||||||
|
uint32_t slen,
|
||||||
|
uint32_t soff,
|
||||||
|
uint32_t count) {
|
||||||
/* If we are byte-aligned in both source and destination, use a fast
|
/* If we are byte-aligned in both source and destination, use a fast
|
||||||
* path for the number of bytes we can consume this way. */
|
* path for the number of bytes we can consume this way. */
|
||||||
if ((doff & 7) == 0 && (soff & 7) == 0) {
|
if((doff & 7) == 0 && (soff & 7) == 0) {
|
||||||
uint32_t didx = doff/8;
|
uint32_t didx = doff / 8;
|
||||||
uint32_t sidx = soff/8;
|
uint32_t sidx = soff / 8;
|
||||||
while(count > 8 && didx < dlen && sidx < slen) {
|
while(count > 8 && didx < dlen && sidx < slen) {
|
||||||
d[didx++] = s[sidx++];
|
d[didx++] = s[sidx++];
|
||||||
count -= 8;
|
count -= 8;
|
||||||
@@ -271,9 +270,9 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff,
|
|||||||
|
|
||||||
/* Copy the bits needed to reach an offset where we can copy
|
/* Copy the bits needed to reach an offset where we can copy
|
||||||
* two half bytes of src to a full byte of destination. */
|
* two half bytes of src to a full byte of destination. */
|
||||||
while(count > 8 && (doff&7) != 0) {
|
while(count > 8 && (doff & 7) != 0) {
|
||||||
bool bit = bitmap_get(s,slen,soff++);
|
bool bit = bitmap_get(s, slen, soff++);
|
||||||
bitmap_set(d,dlen,doff++,bit);
|
bitmap_set(d, dlen, doff++, bit);
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,13 +315,12 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff,
|
|||||||
* src[2] << 5, that is "WORLDS!!" >> 5 = ".....WOR"
|
* src[2] << 5, that is "WORLDS!!" >> 5 = ".....WOR"
|
||||||
* That is "HELLOWOR"
|
* That is "HELLOWOR"
|
||||||
*/
|
*/
|
||||||
if (count > 8) {
|
if(count > 8) {
|
||||||
uint8_t skew = soff % 8; /* Don't worry, compiler will optimize. */
|
uint8_t skew = soff % 8; /* Don't worry, compiler will optimize. */
|
||||||
uint32_t didx = doff/8;
|
uint32_t didx = doff / 8;
|
||||||
uint32_t sidx = soff/8;
|
uint32_t sidx = soff / 8;
|
||||||
while(count > 8 && didx < dlen && sidx < slen) {
|
while(count > 8 && didx < dlen && sidx < slen) {
|
||||||
d[didx] = ((s[sidx] << skew) |
|
d[didx] = ((s[sidx] << skew) | (s[sidx + 1] >> (8 - skew)));
|
||||||
(s[sidx+1] >> (8-skew)));
|
|
||||||
sidx++;
|
sidx++;
|
||||||
didx++;
|
didx++;
|
||||||
soff += 8;
|
soff += 8;
|
||||||
@@ -334,8 +332,8 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff,
|
|||||||
/* Here count is guaranteed to be < 8.
|
/* Here count is guaranteed to be < 8.
|
||||||
* Copy the final bits bit by bit. */
|
* Copy the final bits bit by bit. */
|
||||||
while(count) {
|
while(count) {
|
||||||
bool bit = bitmap_get(s,slen,soff++);
|
bool bit = bitmap_get(s, slen, soff++);
|
||||||
bitmap_set(d,dlen,doff++,bit);
|
bitmap_set(d, dlen, doff++, bit);
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,15 +341,15 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff,
|
|||||||
/* We decode bits assuming the first bit we receive is the MSB
|
/* We decode bits assuming the first bit we receive is the MSB
|
||||||
* (see bitmap_set/get functions). Certain devices send data
|
* (see bitmap_set/get functions). Certain devices send data
|
||||||
* encoded in the reverse way. */
|
* encoded in the reverse way. */
|
||||||
void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) {
|
void bitmap_reverse_bytes_bits(uint8_t* p, uint32_t len) {
|
||||||
for (uint32_t j = 0; j < len; j++) {
|
for(uint32_t j = 0; j < len; j++) {
|
||||||
uint32_t b = p[j];
|
uint32_t b = p[j];
|
||||||
/* Step 1: swap the two nibbles: 12345678 -> 56781234 */
|
/* Step 1: swap the two nibbles: 12345678 -> 56781234 */
|
||||||
b = (b&0xf0)>>4 | (b&0x0f)<<4;
|
b = (b & 0xf0) >> 4 | (b & 0x0f) << 4;
|
||||||
/* Step 2: swap adjacent pairs : 56781234 -> 78563412 */
|
/* Step 2: swap adjacent pairs : 56781234 -> 78563412 */
|
||||||
b = (b&0xcc)>>2 | (b&0x33)<<2;
|
b = (b & 0xcc) >> 2 | (b & 0x33) << 2;
|
||||||
/* Step 3: swap adjacent bits : 78563412 -> 87654321 */
|
/* Step 3: swap adjacent bits : 78563412 -> 87654321 */
|
||||||
b = (b&0xaa)>>1 | (b&0x55)<<1;
|
b = (b & 0xaa) >> 1 | (b & 0x55) << 1;
|
||||||
p[j] = b;
|
p[j] = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,10 +357,10 @@ void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) {
|
|||||||
/* Return true if the specified sequence of bits, provided as a string in the
|
/* 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'
|
* form "11010110..." is found in the 'b' bitmap of 'blen' bits at 'bitpos'
|
||||||
* position. */
|
* position. */
|
||||||
bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits) {
|
bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits) {
|
||||||
for (size_t j = 0; bits[j]; j++) {
|
for(size_t j = 0; bits[j]; j++) {
|
||||||
bool expected = (bits[j] == '1') ? true : false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -375,12 +373,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
|
* 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
|
* for the kind of patterns we search we'll have a lot of early stops so
|
||||||
* we use a vanilla approach. */
|
* 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 bitmap_seek_bits(
|
||||||
uint32_t endpos = startpos+blen*8;
|
uint8_t* b,
|
||||||
uint32_t end2 = startpos+maxbits;
|
uint32_t blen,
|
||||||
if (end2 < endpos) endpos = end2;
|
uint32_t startpos,
|
||||||
for (uint32_t j = startpos; j < endpos; j++)
|
uint32_t maxbits,
|
||||||
if (bitmap_match_bits(b,blen,j,bits)) return j;
|
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;
|
return BITMAP_SEEK_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,10 +394,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
|
* 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
|
* decoders, to see if the decoding works regardless of the fact we are able
|
||||||
* to actually receive a given signal. */
|
* to actually receive a given signal. */
|
||||||
void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat) {
|
void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat) {
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
while(pat[i]) {
|
while(pat[i]) {
|
||||||
bitmap_set(b,blen,i+off,pat[i] == '1');
|
bitmap_set(b, blen, i + off, pat[i] == '1');
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,31 +429,36 @@ void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat
|
|||||||
* bits set into the buffer 'b'. The 'rate' argument, in microseconds, is
|
* bits set into the buffer 'b'. The 'rate' argument, in microseconds, is
|
||||||
* the detected short-pulse duration. We expect the line code to be
|
* the detected short-pulse duration. We expect the line code to be
|
||||||
* meaningful when interpreted at multiples of 'rate'. */
|
* 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) {
|
uint32_t convert_signal_to_bits(
|
||||||
if (rate == 0) return 0; /* We can't perform the conversion. */
|
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;
|
uint32_t bitpos = 0;
|
||||||
for (uint32_t j = 0; j < count; j++) {
|
for(uint32_t j = 0; j < count; j++) {
|
||||||
uint32_t dur;
|
uint32_t dur;
|
||||||
bool level;
|
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 numbits = dur / rate; /* full bits that surely fit. */
|
||||||
uint32_t rest = dur % rate; /* How much we are left with. */
|
uint32_t rest = dur % rate; /* How much we are left with. */
|
||||||
if (rest > rate/2) numbits++; /* There is another one. */
|
if(rest > rate / 2) numbits++; /* There is another one. */
|
||||||
|
|
||||||
/* Limit how much a single sample can spawn. There are likely no
|
/* Limit how much a single sample can spawn. There are likely no
|
||||||
* protocols doing such long pulses when the rate is low. */
|
* 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. */
|
if(0) /* Super verbose, so not under the DEBUG_MSG define. */
|
||||||
FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits",
|
FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", dur, numbits, (int)level);
|
||||||
dur,numbits,(int)level);
|
|
||||||
|
|
||||||
/* If the signal is too short, let's claim it an interference
|
/* If the signal is too short, let's claim it an interference
|
||||||
* and ignore it completely. */
|
* 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;
|
return bitpos;
|
||||||
}
|
}
|
||||||
@@ -467,23 +475,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).
|
* specified in bytes by the caller, via the 'len' parameters).
|
||||||
*
|
*
|
||||||
* The decoding starts at the specified offset (in bits) 'off'. */
|
* 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. */
|
uint32_t decoded = 0; /* Number of bits extracted. */
|
||||||
len *= 8; /* Convert bytes to bits. */
|
len *= 8; /* Convert bytes to bits. */
|
||||||
while(off < len) {
|
while(off < len) {
|
||||||
bool bitval;
|
bool bitval;
|
||||||
if (bitmap_match_bits(bits,len,off,zero_pattern)) {
|
if(bitmap_match_bits(bits, len, off, zero_pattern)) {
|
||||||
bitval = false;
|
bitval = false;
|
||||||
off += strlen(zero_pattern);
|
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;
|
bitval = true;
|
||||||
off += strlen(one_pattern);
|
off += strlen(one_pattern);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bitmap_set(buf,buflen,decoded++,bitval);
|
bitmap_set(buf, buflen, decoded++, bitval);
|
||||||
if (decoded/8 == buflen) break; /* No space left on target buffer. */
|
if(decoded / 8 == buflen) break; /* No space left on target buffer. */
|
||||||
}
|
}
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
@@ -494,17 +508,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.
|
* in differential codings the next bits depend on the previous one.
|
||||||
*
|
*
|
||||||
* Parameters and return values are like convert_from_line_code(). */
|
* 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;
|
uint32_t decoded = 0;
|
||||||
len *= 8; /* Conver to bits. */
|
len *= 8; /* Conver to bits. */
|
||||||
for (uint32_t j = off; j < len; j += 2) {
|
for(uint32_t j = off; j < len; j += 2) {
|
||||||
bool b0 = bitmap_get(bits,len,j);
|
bool b0 = bitmap_get(bits, len, j);
|
||||||
bool b1 = bitmap_get(bits,len,j+1);
|
bool b1 = bitmap_get(bits, len, j + 1);
|
||||||
if (b0 == previous) break; /* Each new bit must switch value. */
|
if(b0 == previous) break; /* Each new bit must switch value. */
|
||||||
bitmap_set(buf,buflen,decoded++,b0 == b1);
|
bitmap_set(buf, buflen, decoded++, b0 == b1);
|
||||||
previous = 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;
|
return decoded;
|
||||||
}
|
}
|
||||||
@@ -522,22 +541,21 @@ extern ProtoViewDecoder CitroenTPMSDecoder;
|
|||||||
extern ProtoViewDecoder FordTPMSDecoder;
|
extern ProtoViewDecoder FordTPMSDecoder;
|
||||||
extern ProtoViewDecoder KeeloqDecoder;
|
extern ProtoViewDecoder KeeloqDecoder;
|
||||||
|
|
||||||
ProtoViewDecoder *Decoders[] = {
|
ProtoViewDecoder* Decoders[] = {
|
||||||
&Oregon2Decoder, /* Oregon sensors v2.1 protocol. */
|
&Oregon2Decoder, /* Oregon sensors v2.1 protocol. */
|
||||||
&B4B1Decoder, /* PT, SC, ... 24 bits remotes. */
|
&B4B1Decoder, /* PT, SC, ... 24 bits remotes. */
|
||||||
&RenaultTPMSDecoder, /* Renault TPMS. */
|
&RenaultTPMSDecoder, /* Renault TPMS. */
|
||||||
&ToyotaTPMSDecoder, /* Toyota TPMS. */
|
&ToyotaTPMSDecoder, /* Toyota TPMS. */
|
||||||
&SchraderTPMSDecoder, /* Schrader TPMS. */
|
&SchraderTPMSDecoder, /* Schrader TPMS. */
|
||||||
&SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */
|
&SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */
|
||||||
&CitroenTPMSDecoder, /* Citroen TPMS. */
|
&CitroenTPMSDecoder, /* Citroen TPMS. */
|
||||||
&FordTPMSDecoder, /* Ford TPMS. */
|
&FordTPMSDecoder, /* Ford TPMS. */
|
||||||
&KeeloqDecoder, /* Keeloq remote. */
|
&KeeloqDecoder, /* Keeloq remote. */
|
||||||
NULL
|
NULL};
|
||||||
};
|
|
||||||
|
|
||||||
/* Free the message info and allocated data. */
|
/* Free the message info and allocated data. */
|
||||||
void free_msg_info(ProtoViewMsgInfo *i) {
|
void free_msg_info(ProtoViewMsgInfo* i) {
|
||||||
if (i == NULL) return;
|
if(i == NULL) return;
|
||||||
fieldset_free(i->fieldset);
|
fieldset_free(i->fieldset);
|
||||||
free(i->bits);
|
free(i->bits);
|
||||||
free(i);
|
free(i);
|
||||||
@@ -545,9 +563,9 @@ void free_msg_info(ProtoViewMsgInfo *i) {
|
|||||||
|
|
||||||
/* Reset the message info structure before passing it to the decoding
|
/* Reset the message info structure before passing it to the decoding
|
||||||
* functions. */
|
* functions. */
|
||||||
void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) {
|
void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app) {
|
||||||
UNUSED(app);
|
UNUSED(app);
|
||||||
memset(i,0,sizeof(ProtoViewMsgInfo));
|
memset(i, 0, sizeof(ProtoViewMsgInfo));
|
||||||
i->bits = NULL;
|
i->bits = NULL;
|
||||||
i->fieldset = fieldset_new();
|
i->fieldset = fieldset_new();
|
||||||
}
|
}
|
||||||
@@ -556,23 +574,29 @@ void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) {
|
|||||||
* to a bitstream, and the calls the protocol specific functions for
|
* to a bitstream, and the calls the protocol specific functions for
|
||||||
* decoding. If the signal was decoded correctly by some protocol, true
|
* decoding. If the signal was decoded correctly by some protocol, true
|
||||||
* is returned. Otherwise false is returned. */
|
* is returned. Otherwise false is returned. */
|
||||||
bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
|
bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info) {
|
||||||
uint32_t bitmap_bits_size = 4096*8;
|
uint32_t bitmap_bits_size = 4096 * 8;
|
||||||
uint32_t bitmap_size = bitmap_bits_size/8;
|
uint32_t bitmap_size = bitmap_bits_size / 8;
|
||||||
|
|
||||||
/* We call the decoders with an offset a few samples before the actual
|
/* 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. */
|
* signal detected and for a len of a few bits after its end. */
|
||||||
uint32_t before_samples = 32;
|
uint32_t before_samples = 32;
|
||||||
uint32_t after_samples = 100;
|
uint32_t after_samples = 100;
|
||||||
|
|
||||||
uint8_t *bitmap = malloc(bitmap_size);
|
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);
|
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. */
|
if(DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */
|
||||||
char *str = malloc(1024);
|
char* str = malloc(1024);
|
||||||
uint32_t j;
|
uint32_t j;
|
||||||
for (j = 0; j < bits && j < 1023; j++) {
|
for(j = 0; j < bits && j < 1023; j++) {
|
||||||
str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0';
|
str[j] = bitmap_get(bitmap, bitmap_size, j) ? '1' : '0';
|
||||||
}
|
}
|
||||||
str[j] = 0;
|
str[j] = 0;
|
||||||
FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str);
|
FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str);
|
||||||
@@ -585,18 +609,17 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
|
|||||||
bool decoded = false;
|
bool decoded = false;
|
||||||
while(Decoders[j]) {
|
while(Decoders[j]) {
|
||||||
uint32_t start_time = furi_get_tick();
|
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;
|
uint32_t delta = furi_get_tick() - start_time;
|
||||||
FURI_LOG_E(TAG, "Decoder %s took %lu ms",
|
FURI_LOG_E(TAG, "Decoder %s took %lu ms", Decoders[j]->name, (unsigned long)delta);
|
||||||
Decoders[j]->name, (unsigned long)delta);
|
if(decoded) {
|
||||||
if (decoded) {
|
|
||||||
info->decoder = Decoders[j];
|
info->decoder = Decoders[j];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decoded) {
|
if(!decoded) {
|
||||||
FURI_LOG_E(TAG, "No decoding possible");
|
FURI_LOG_E(TAG, "No decoding possible");
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(TAG, "+++ Decoded %s", info->decoder->name);
|
FURI_LOG_E(TAG, "+++ Decoded %s", info->decoder->name);
|
||||||
@@ -604,12 +627,17 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
|
|||||||
* with the decoded signal. The decoder may not implement offset/len
|
* with the decoded signal. The decoder may not implement offset/len
|
||||||
* filling of the structure. In such case we have no info and
|
* filling of the structure. In such case we have no info and
|
||||||
* pulses_count will be set to zero. */
|
* pulses_count will be set to zero. */
|
||||||
if (info->pulses_count) {
|
if(info->pulses_count) {
|
||||||
info->bits_bytes = (info->pulses_count+7)/8; // Round to full byte.
|
info->bits_bytes = (info->pulses_count + 7) / 8; // Round to full byte.
|
||||||
info->bits = malloc(info->bits_bytes);
|
info->bits = malloc(info->bits_bytes);
|
||||||
bitmap_copy(info->bits,info->bits_bytes,0,
|
bitmap_copy(
|
||||||
bitmap,bitmap_size,info->start_off,
|
info->bits,
|
||||||
info->pulses_count);
|
info->bits_bytes,
|
||||||
|
0,
|
||||||
|
bitmap,
|
||||||
|
bitmap_size,
|
||||||
|
info->start_off,
|
||||||
|
info->pulses_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(bitmap);
|
free(bitmap);
|
||||||
|
|||||||
@@ -13,57 +13,56 @@
|
|||||||
* but it's logical representation stored in the app->msg_info bitmap, where
|
* but it's logical representation stored in the app->msg_info bitmap, where
|
||||||
* each 1 or 0 means a puls or gap for the specified short pulse duration time
|
* each 1 or 0 means a puls or gap for the specified short pulse duration time
|
||||||
* (te). */
|
* (te). */
|
||||||
bool save_signal(ProtoViewApp *app, const char *filename) {
|
bool save_signal(ProtoViewApp* app, const char* filename) {
|
||||||
/* We have a message at all? */
|
/* We have a message at all? */
|
||||||
if (app->msg_info == NULL || app->msg_info->pulses_count == 0) return false;
|
if(app->msg_info == NULL || app->msg_info->pulses_count == 0) return false;
|
||||||
|
|
||||||
Storage *storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
FlipperFormat *file = flipper_format_file_alloc(storage);
|
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||||
Stream *stream = flipper_format_get_raw_stream(file);
|
Stream* stream = flipper_format_get_raw_stream(file);
|
||||||
FuriString *file_content = NULL;
|
FuriString* file_content = NULL;
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
if (flipper_format_file_open_always(file, filename)) {
|
if(flipper_format_file_open_always(file, filename)) {
|
||||||
/* Write the file header. */
|
/* Write the file header. */
|
||||||
FuriString *file_content = furi_string_alloc();
|
FuriString* file_content = furi_string_alloc();
|
||||||
const char *preset_id = ProtoViewModulations[app->modulation].id;
|
const char* preset_id = ProtoViewModulations[app->modulation].id;
|
||||||
|
|
||||||
furi_string_printf(file_content,
|
furi_string_printf(
|
||||||
"Filetype: Flipper SubGhz RAW File\n"
|
file_content,
|
||||||
"Version: 1\n"
|
"Filetype: Flipper SubGhz RAW File\n"
|
||||||
"Frequency: %ld\n"
|
"Version: 1\n"
|
||||||
"Preset: %s\n",
|
"Frequency: %ld\n"
|
||||||
app->frequency,
|
"Preset: %s\n",
|
||||||
preset_id ? preset_id : "FuriHalSubGhzPresetCustom");
|
app->frequency,
|
||||||
|
preset_id ? preset_id : "FuriHalSubGhzPresetCustom");
|
||||||
|
|
||||||
/* For custom modulations, we need to emit a set of registers. */
|
/* For custom modulations, we need to emit a set of registers. */
|
||||||
if (preset_id == NULL) {
|
if(preset_id == NULL) {
|
||||||
FuriString *custom = furi_string_alloc();
|
FuriString* custom = furi_string_alloc();
|
||||||
uint8_t *regs = ProtoViewModulations[app->modulation].custom;
|
uint8_t* regs = ProtoViewModulations[app->modulation].custom;
|
||||||
furi_string_printf(custom,
|
furi_string_printf(
|
||||||
|
custom,
|
||||||
"Custom_preset_module: CC1101\n"
|
"Custom_preset_module: CC1101\n"
|
||||||
"Custom_preset_data: ");
|
"Custom_preset_data: ");
|
||||||
for (int j = 0; regs[j]; j += 2) {
|
for(int j = 0; regs[j]; j += 2) {
|
||||||
furi_string_cat_printf(custom, "%02X %02X ",
|
furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]);
|
||||||
(int)regs[j], (int)regs[j+1]);
|
|
||||||
}
|
}
|
||||||
size_t len = furi_string_size(file_content);
|
size_t len = furi_string_size(file_content);
|
||||||
furi_string_set_char(custom,len-1,'\n');
|
furi_string_set_char(custom, len - 1, '\n');
|
||||||
furi_string_cat(file_content,custom);
|
furi_string_cat(file_content, custom);
|
||||||
furi_string_free(custom);
|
furi_string_free(custom);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We always save raw files. */
|
/* We always save raw files. */
|
||||||
furi_string_cat_printf(file_content,
|
furi_string_cat_printf(
|
||||||
"Protocol: RAW\n"
|
file_content,
|
||||||
"RAW_Data: -10000\n"); // Start with 10 ms of gap
|
"Protocol: RAW\n"
|
||||||
|
"RAW_Data: -10000\n"); // Start with 10 ms of gap
|
||||||
|
|
||||||
/* Write header. */
|
/* Write header. */
|
||||||
size_t len = furi_string_size(file_content);
|
size_t len = furi_string_size(file_content);
|
||||||
if (stream_write(stream,
|
if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != len) {
|
||||||
(uint8_t*) furi_string_get_cstr(file_content), len)
|
|
||||||
!= len)
|
|
||||||
{
|
|
||||||
FURI_LOG_W(TAG, "Short write to file");
|
FURI_LOG_W(TAG, "Short write to file");
|
||||||
success = false;
|
success = false;
|
||||||
goto write_err;
|
goto write_err;
|
||||||
@@ -76,15 +75,13 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
|
|||||||
uint32_t this_line_samples = 0;
|
uint32_t this_line_samples = 0;
|
||||||
uint32_t max_line_samples = 100;
|
uint32_t max_line_samples = 100;
|
||||||
uint32_t idx = 0; // Iindex in the signal bitmap.
|
uint32_t idx = 0; // Iindex in the signal bitmap.
|
||||||
ProtoViewMsgInfo *i = app->msg_info;
|
ProtoViewMsgInfo* i = app->msg_info;
|
||||||
while(idx < i->pulses_count) {
|
while(idx < i->pulses_count) {
|
||||||
bool level = bitmap_get(i->bits,i->bits_bytes,idx);
|
bool level = bitmap_get(i->bits, i->bits_bytes, idx);
|
||||||
uint32_t te_times = 1;
|
uint32_t te_times = 1;
|
||||||
idx++;
|
idx++;
|
||||||
/* Count the duration of the current pulse/gap. */
|
/* Count the duration of the current pulse/gap. */
|
||||||
while(idx < i->pulses_count &&
|
while(idx < i->pulses_count && bitmap_get(i->bits, i->bits_bytes, idx) == level) {
|
||||||
bitmap_get(i->bits,i->bits_bytes,idx) == level)
|
|
||||||
{
|
|
||||||
te_times++;
|
te_times++;
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
@@ -92,32 +89,29 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
|
|||||||
// next gap or pulse.
|
// next gap or pulse.
|
||||||
|
|
||||||
int32_t dur = (int32_t)i->short_pulse_dur * te_times;
|
int32_t dur = (int32_t)i->short_pulse_dur * te_times;
|
||||||
if (level == 0) dur = -dur; /* Negative is gap in raw files. */
|
if(level == 0) dur = -dur; /* Negative is gap in raw files. */
|
||||||
|
|
||||||
/* Emit the sample. If this is the first sample of the line,
|
/* Emit the sample. If this is the first sample of the line,
|
||||||
* also emit the RAW_Data: field. */
|
* also emit the RAW_Data: field. */
|
||||||
if (this_line_samples == 0)
|
if(this_line_samples == 0) furi_string_cat_printf(file_content, "RAW_Data: ");
|
||||||
furi_string_cat_printf(file_content,"RAW_Data: ");
|
furi_string_cat_printf(file_content, "%d ", (int)dur);
|
||||||
furi_string_cat_printf(file_content,"%d ",(int)dur);
|
|
||||||
this_line_samples++;
|
this_line_samples++;
|
||||||
|
|
||||||
/* Store the current set of samples on disk, when we reach a
|
/* Store the current set of samples on disk, when we reach a
|
||||||
* given number or the end of the signal. */
|
* given number or the end of the signal. */
|
||||||
bool end_reached = (idx == i->pulses_count);
|
bool end_reached = (idx == i->pulses_count);
|
||||||
if (this_line_samples == max_line_samples || end_reached) {
|
if(this_line_samples == max_line_samples || end_reached) {
|
||||||
/* If that's the end, terminate the signal with a long
|
/* If that's the end, terminate the signal with a long
|
||||||
* gap. */
|
* gap. */
|
||||||
if (end_reached) furi_string_cat_printf(file_content,"-10000 ");
|
if(end_reached) furi_string_cat_printf(file_content, "-10000 ");
|
||||||
|
|
||||||
/* We always have a trailing space in the last sample. Make it
|
/* We always have a trailing space in the last sample. Make it
|
||||||
* a newline. */
|
* a newline. */
|
||||||
size_t len = furi_string_size(file_content);
|
size_t len = furi_string_size(file_content);
|
||||||
furi_string_set_char(file_content,len-1,'\n');
|
furi_string_set_char(file_content, len - 1, '\n');
|
||||||
|
|
||||||
if (stream_write(stream,
|
if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) !=
|
||||||
(uint8_t*) furi_string_get_cstr(file_content),
|
len) {
|
||||||
len) != len)
|
|
||||||
{
|
|
||||||
FURI_LOG_W(TAG, "Short write to file");
|
FURI_LOG_W(TAG, "Short write to file");
|
||||||
success = false;
|
success = false;
|
||||||
goto write_err;
|
goto write_err;
|
||||||
@@ -136,6 +130,6 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
|
|||||||
write_err:
|
write_err:
|
||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
flipper_format_free(file);
|
flipper_format_free(file);
|
||||||
if (file_content != NULL) furi_string_free(file_content);
|
if(file_content != NULL) furi_string_free(file_content);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,36 +10,31 @@
|
|||||||
|
|
||||||
/* Return the ID of the currently selected subview, of the current
|
/* Return the ID of the currently selected subview, of the current
|
||||||
* view. */
|
* view. */
|
||||||
int ui_get_current_subview(ProtoViewApp *app) {
|
int ui_get_current_subview(ProtoViewApp* app) {
|
||||||
return app->current_subview[app->current_view];
|
return app->current_subview[app->current_view];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called by view rendering callback that has subviews, to show small triangles
|
/* Called by view rendering callback that has subviews, to show small triangles
|
||||||
* facing down/up if there are other subviews the user can access with up
|
* facing down/up if there are other subviews the user can access with up
|
||||||
* and down. */
|
* and down. */
|
||||||
void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app,
|
void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview) {
|
||||||
int last_subview)
|
|
||||||
{
|
|
||||||
int subview = ui_get_current_subview(app);
|
int subview = ui_get_current_subview(app);
|
||||||
if (subview != 0)
|
if(subview != 0) canvas_draw_triangle(canvas, 120, 5, 8, 5, CanvasDirectionBottomToTop);
|
||||||
canvas_draw_triangle(canvas,120,5,8,5,CanvasDirectionBottomToTop);
|
if(subview != last_subview - 1)
|
||||||
if (subview != last_subview-1)
|
canvas_draw_triangle(canvas, 120, 59, 8, 5, CanvasDirectionTopToBottom);
|
||||||
canvas_draw_triangle(canvas,120,59,8,5,CanvasDirectionTopToBottom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle up/down keys when we are in a subview. If the function catched
|
/* Handle up/down keys when we are in a subview. If the function catched
|
||||||
* such keypress, it returns true, so that the actual view input callback
|
* such keypress, it returns true, so that the actual view input callback
|
||||||
* knows it can just return ASAP without doing anything. */
|
* knows it can just return ASAP without doing anything. */
|
||||||
bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview) {
|
bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview) {
|
||||||
int subview = ui_get_current_subview(app);
|
int subview = ui_get_current_subview(app);
|
||||||
if (input.type == InputTypePress) {
|
if(input.type == InputTypePress) {
|
||||||
if (input.key == InputKeyUp) {
|
if(input.key == InputKeyUp) {
|
||||||
if (subview != 0)
|
if(subview != 0) app->current_subview[app->current_view]--;
|
||||||
app->current_subview[app->current_view]--;
|
|
||||||
return true;
|
return true;
|
||||||
} else if (input.key == InputKeyDown) {
|
} else if(input.key == InputKeyDown) {
|
||||||
if (subview != last_subview-1)
|
if(subview != last_subview - 1) app->current_subview[app->current_view]++;
|
||||||
app->current_subview[app->current_view]++;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,16 +57,18 @@ bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_sub
|
|||||||
*
|
*
|
||||||
* Note: if the buffer is not a null-termined zero string, what it contains will
|
* Note: if the buffer is not a null-termined zero string, what it contains will
|
||||||
* be used as initial input for the user. */
|
* be used as initial input for the user. */
|
||||||
void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
|
void ui_show_keyboard(
|
||||||
void (*done_callback)(void*))
|
ProtoViewApp* app,
|
||||||
{
|
char* buffer,
|
||||||
|
uint32_t buflen,
|
||||||
|
void (*done_callback)(void*)) {
|
||||||
app->show_text_input = true;
|
app->show_text_input = true;
|
||||||
app->text_input_buffer = buffer;
|
app->text_input_buffer = buffer;
|
||||||
app->text_input_buffer_len = buflen;
|
app->text_input_buffer_len = buflen;
|
||||||
app->text_input_done_callback = done_callback;
|
app->text_input_done_callback = done_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_dismiss_keyboard(ProtoViewApp *app) {
|
void ui_dismiss_keyboard(ProtoViewApp* app) {
|
||||||
view_dispatcher_stop(app->view_dispatcher);
|
view_dispatcher_stop(app->view_dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,24 +76,24 @@ void ui_dismiss_keyboard(ProtoViewApp *app) {
|
|||||||
|
|
||||||
/* Set an alert message to be shown over any currently active view, for
|
/* Set an alert message to be shown over any currently active view, for
|
||||||
* the specified amount of time of 'ttl' milliseconds. */
|
* the specified amount of time of 'ttl' milliseconds. */
|
||||||
void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl) {
|
void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl) {
|
||||||
app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl);
|
app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl);
|
||||||
snprintf(app->alert_text,ALERT_MAX_LEN,"%s",text);
|
snprintf(app->alert_text, ALERT_MAX_LEN, "%s", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cancel the alert before its time has elapsed. */
|
/* Cancel the alert before its time has elapsed. */
|
||||||
void ui_dismiss_alert(ProtoViewApp *app) {
|
void ui_dismiss_alert(ProtoViewApp* app) {
|
||||||
app->alert_dismiss_time = 0;
|
app->alert_dismiss_time = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show the alert if an alert is set. This is called after the currently
|
/* Show the alert if an alert is set. This is called after the currently
|
||||||
* active view displayed its stuff, so we overwrite the screen with the
|
* active view displayed its stuff, so we overwrite the screen with the
|
||||||
* alert message. */
|
* alert message. */
|
||||||
void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) {
|
void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app) {
|
||||||
if (app->alert_dismiss_time == 0) {
|
if(app->alert_dismiss_time == 0) {
|
||||||
/* No active alert. */
|
/* No active alert. */
|
||||||
return;
|
return;
|
||||||
} else if (app->alert_dismiss_time < furi_get_tick()) {
|
} else if(app->alert_dismiss_time < furi_get_tick()) {
|
||||||
/* Alert just expired. */
|
/* Alert just expired. */
|
||||||
ui_dismiss_alert(app);
|
ui_dismiss_alert(app);
|
||||||
return;
|
return;
|
||||||
@@ -106,41 +103,43 @@ void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) {
|
|||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
uint8_t w = canvas_string_width(canvas, app->alert_text);
|
uint8_t w = canvas_string_width(canvas, app->alert_text);
|
||||||
uint8_t h = 8; // Font height.
|
uint8_t h = 8; // Font height.
|
||||||
uint8_t text_x = 64-(w/2);
|
uint8_t text_x = 64 - (w / 2);
|
||||||
uint8_t text_y = 32+4;
|
uint8_t text_y = 32 + 4;
|
||||||
uint8_t padding = 3;
|
uint8_t padding = 3;
|
||||||
canvas_set_color(canvas,ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
canvas_draw_box(canvas,text_x-padding,text_y-padding-h,w+padding*2,h+padding*2);
|
canvas_draw_box(
|
||||||
canvas_set_color(canvas,ColorWhite);
|
canvas, text_x - padding, text_y - padding - h, w + padding * 2, h + padding * 2);
|
||||||
canvas_draw_box(canvas,text_x-padding+1,text_y-padding-h+1,w+padding*2-2,h+padding*2-2);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
canvas_set_color(canvas,ColorBlack);
|
canvas_draw_box(
|
||||||
canvas_draw_str(canvas,text_x,text_y,app->alert_text);
|
canvas,
|
||||||
|
text_x - padding + 1,
|
||||||
|
text_y - padding - h + 1,
|
||||||
|
w + padding * 2 - 2,
|
||||||
|
h + padding * 2 - 2);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_draw_str(canvas, text_x, text_y, app->alert_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================== Canvas extensions ============================ */
|
/* =========================== Canvas extensions ============================ */
|
||||||
|
|
||||||
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 {
|
struct {
|
||||||
uint8_t x; uint8_t y;
|
uint8_t x;
|
||||||
} dir[8] = {
|
uint8_t y;
|
||||||
{-1,-1},
|
} dir[8] = {{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}};
|
||||||
{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
|
/* Rotate in all the directions writing the same string to create a
|
||||||
* border, then write the actual string in the other color in the
|
* border, then write the actual string in the other color in the
|
||||||
* middle. */
|
* middle. */
|
||||||
canvas_set_color(canvas, border_color);
|
canvas_set_color(canvas, border_color);
|
||||||
for (int j = 0; j < 8; j++)
|
for(int j = 0; j < 8; j++) canvas_draw_str(canvas, x + dir[j].x, y + dir[j].y, str);
|
||||||
canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str);
|
|
||||||
canvas_set_color(canvas, text_color);
|
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);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,39 +3,38 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
extern ProtoViewDecoder *Decoders[]; // Defined in signal.c.
|
extern ProtoViewDecoder* Decoders[]; // Defined in signal.c.
|
||||||
|
|
||||||
/* Our view private data. */
|
/* Our view private data. */
|
||||||
#define USER_VALUE_LEN 64
|
#define USER_VALUE_LEN 64
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ProtoViewDecoder *decoder; /* Decoder we are using to create a
|
ProtoViewDecoder* decoder; /* Decoder we are using to create a
|
||||||
message. */
|
message. */
|
||||||
uint32_t cur_decoder; /* Decoder index when we are yet selecting
|
uint32_t cur_decoder; /* Decoder index when we are yet selecting
|
||||||
a decoder. Used when decoder is NULL. */
|
a decoder. Used when decoder is NULL. */
|
||||||
ProtoViewFieldSet *fieldset; /* The fields to populate. */
|
ProtoViewFieldSet* fieldset; /* The fields to populate. */
|
||||||
uint32_t cur_field; /* Field we are editing right now. This
|
uint32_t cur_field; /* Field we are editing right now. This
|
||||||
is the index inside the 'fieldset'
|
is the index inside the 'fieldset'
|
||||||
fields. */
|
fields. */
|
||||||
char *user_value; /* Keyboard input to replace the current
|
char* user_value; /* Keyboard input to replace the current
|
||||||
field value goes here. */
|
field value goes here. */
|
||||||
} BuildViewPrivData;
|
} BuildViewPrivData;
|
||||||
|
|
||||||
/* Not all the decoders support message bulding, so we can't just
|
/* Not all the decoders support message bulding, so we can't just
|
||||||
* increment / decrement the cur_decoder index here. */
|
* increment / decrement the cur_decoder index here. */
|
||||||
static void select_next_decoder(ProtoViewApp *app) {
|
static void select_next_decoder(ProtoViewApp* app) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
do {
|
do {
|
||||||
privdata->cur_decoder++;
|
privdata->cur_decoder++;
|
||||||
if (Decoders[privdata->cur_decoder] == NULL)
|
if(Decoders[privdata->cur_decoder] == NULL) privdata->cur_decoder = 0;
|
||||||
privdata->cur_decoder = 0;
|
|
||||||
} while(Decoders[privdata->cur_decoder]->get_fields == NULL);
|
} while(Decoders[privdata->cur_decoder]->get_fields == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like select_next_decoder() but goes backward. */
|
/* Like select_next_decoder() but goes backward. */
|
||||||
static void select_prev_decoder(ProtoViewApp *app) {
|
static void select_prev_decoder(ProtoViewApp* app) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
do {
|
do {
|
||||||
if (privdata->cur_decoder == 0) {
|
if(privdata->cur_decoder == 0) {
|
||||||
/* Go one after the last one to wrap around. */
|
/* Go one after the last one to wrap around. */
|
||||||
while(Decoders[privdata->cur_decoder]) privdata->cur_decoder++;
|
while(Decoders[privdata->cur_decoder]) privdata->cur_decoder++;
|
||||||
}
|
}
|
||||||
@@ -45,69 +44,73 @@ static void select_prev_decoder(ProtoViewApp *app) {
|
|||||||
|
|
||||||
/* Render the view to select the decoder, among the ones that
|
/* Render the view to select the decoder, among the ones that
|
||||||
* support message building. */
|
* support message building. */
|
||||||
static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app) {
|
static void render_view_select_decoder(Canvas* const canvas, ProtoViewApp* app) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
canvas_draw_str(canvas, 0, 9, "Signal creator");
|
canvas_draw_str(canvas, 0, 9, "Signal creator");
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose");
|
canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose");
|
||||||
|
|
||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
canvas_draw_str_aligned(canvas,64,38,AlignCenter,AlignCenter,
|
canvas_draw_str_aligned(
|
||||||
Decoders[privdata->cur_decoder]->name);
|
canvas, 64, 38, AlignCenter, AlignCenter, Decoders[privdata->cur_decoder]->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Render the view that allows the user to populate the fields needed
|
/* Render the view that allows the user to populate the fields needed
|
||||||
* for the selected decoder to build a message. */
|
* for the selected decoder to build a message. */
|
||||||
static void render_view_set_fields(Canvas *const canvas, ProtoViewApp *app) {
|
static void render_view_set_fields(Canvas* const canvas, ProtoViewApp* app) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
snprintf(buf,sizeof(buf), "%s field %d/%d",
|
snprintf(
|
||||||
privdata->decoder->name, (int)privdata->cur_field+1,
|
buf,
|
||||||
|
sizeof(buf),
|
||||||
|
"%s field %d/%d",
|
||||||
|
privdata->decoder->name,
|
||||||
|
(int)privdata->cur_field + 1,
|
||||||
(int)privdata->fieldset->numfields);
|
(int)privdata->fieldset->numfields);
|
||||||
canvas_set_color(canvas,ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
canvas_draw_box(canvas,0,0,128,21);
|
canvas_draw_box(canvas, 0, 0, 128, 21);
|
||||||
canvas_set_color(canvas,ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
canvas_draw_str(canvas, 1, 9, buf);
|
canvas_draw_str(canvas, 1, 9, buf);
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas, 1, 19, "up/down: next field, ok: edit");
|
canvas_draw_str(canvas, 1, 19, "up/down: next field, ok: edit");
|
||||||
|
|
||||||
/* Write the field name, type, current content. */
|
/* Write the field name, type, current content. */
|
||||||
canvas_set_color(canvas,ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
ProtoViewField *field = privdata->fieldset->fields[privdata->cur_field];
|
ProtoViewField* field = privdata->fieldset->fields[privdata->cur_field];
|
||||||
snprintf(buf,sizeof(buf), "%s %s:%d", field->name,
|
snprintf(
|
||||||
field_get_type_name(field), (int)field->len);
|
buf, sizeof(buf), "%s %s:%d", field->name, field_get_type_name(field), (int)field->len);
|
||||||
buf[0] = toupper(buf[0]);
|
buf[0] = toupper(buf[0]);
|
||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
canvas_draw_str_aligned(canvas,64,30,AlignCenter,AlignCenter,buf);
|
canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignCenter, buf);
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
/* Render the current value between "" */
|
/* Render the current value between "" */
|
||||||
unsigned int written = (unsigned int) field_to_string(buf+1,sizeof(buf)-1,field);
|
unsigned int written = (unsigned int)field_to_string(buf + 1, sizeof(buf) - 1, field);
|
||||||
buf[0] = '"';
|
buf[0] = '"';
|
||||||
if (written+3 < sizeof(buf)) memcpy(buf+written+1,"\"\x00",2);
|
if(written + 3 < sizeof(buf)) memcpy(buf + written + 1, "\"\x00", 2);
|
||||||
canvas_draw_str_aligned(canvas,63,45,AlignCenter,AlignCenter,buf);
|
canvas_draw_str_aligned(canvas, 63, 45, AlignCenter, AlignCenter, buf);
|
||||||
|
|
||||||
/* Footer instructions. */
|
/* Footer instructions. */
|
||||||
canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr");
|
canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Render the build message view. */
|
/* Render the build message view. */
|
||||||
void render_view_build_message(Canvas *const canvas, ProtoViewApp *app) {
|
void render_view_build_message(Canvas* const canvas, ProtoViewApp* app) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
|
|
||||||
if (privdata->decoder)
|
if(privdata->decoder)
|
||||||
render_view_set_fields(canvas,app);
|
render_view_set_fields(canvas, app);
|
||||||
else
|
else
|
||||||
render_view_select_decoder(canvas,app);
|
render_view_select_decoder(canvas, app);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle input for the decoder selection. */
|
/* Handle input for the decoder selection. */
|
||||||
static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) {
|
static void process_input_select_decoder(ProtoViewApp* app, InputEvent input) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
if (input.type == InputTypeShort) {
|
if(input.type == InputTypeShort) {
|
||||||
if (input.key == InputKeyOk) {
|
if(input.key == InputKeyOk) {
|
||||||
privdata->decoder = Decoders[privdata->cur_decoder];
|
privdata->decoder = Decoders[privdata->cur_decoder];
|
||||||
privdata->fieldset = fieldset_new();
|
privdata->fieldset = fieldset_new();
|
||||||
privdata->decoder->get_fields(privdata->fieldset);
|
privdata->decoder->get_fields(privdata->fieldset);
|
||||||
@@ -116,11 +119,8 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) {
|
|||||||
* same decoder the user selected, let's populate the
|
* same decoder the user selected, let's populate the
|
||||||
* defaults with the current values. So the user will
|
* defaults with the current values. So the user will
|
||||||
* actaully edit the current message. */
|
* actaully edit the current message. */
|
||||||
if (app->signal_decoded &&
|
if(app->signal_decoded && app->msg_info->decoder == privdata->decoder) {
|
||||||
app->msg_info->decoder == privdata->decoder)
|
fieldset_copy_matching_fields(privdata->fieldset, app->msg_info->fieldset);
|
||||||
{
|
|
||||||
fieldset_copy_matching_fields(privdata->fieldset,
|
|
||||||
app->msg_info->fieldset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now we use the subview system in order to protect the
|
/* Now we use the subview system in order to protect the
|
||||||
@@ -128,10 +128,10 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) {
|
|||||||
Since we are technically into a subview now, we'll have
|
Since we are technically into a subview now, we'll have
|
||||||
control of < and >. */
|
control of < and >. */
|
||||||
InputEvent ii = {.type = InputTypePress, .key = InputKeyDown};
|
InputEvent ii = {.type = InputTypePress, .key = InputKeyDown};
|
||||||
ui_process_subview_updown(app,ii,2);
|
ui_process_subview_updown(app, ii, 2);
|
||||||
} else if (input.key == InputKeyDown) {
|
} else if(input.key == InputKeyDown) {
|
||||||
select_next_decoder(app);
|
select_next_decoder(app);
|
||||||
} else if (input.key == InputKeyUp) {
|
} else if(input.key == InputKeyUp) {
|
||||||
select_prev_decoder(app);
|
select_prev_decoder(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,12 +140,13 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) {
|
|||||||
/* Called after the user typed the new field value in the keyboard.
|
/* Called after the user typed the new field value in the keyboard.
|
||||||
* Let's save it and remove the keyboard view. */
|
* Let's save it and remove the keyboard view. */
|
||||||
static void text_input_done_callback(void* context) {
|
static void text_input_done_callback(void* context) {
|
||||||
ProtoViewApp *app = context;
|
ProtoViewApp* app = context;
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
|
|
||||||
if (field_set_from_string(privdata->fieldset->fields[privdata->cur_field],
|
if(field_set_from_string(
|
||||||
privdata->user_value, strlen(privdata->user_value)) == false)
|
privdata->fieldset->fields[privdata->cur_field],
|
||||||
{
|
privdata->user_value,
|
||||||
|
strlen(privdata->user_value)) == false) {
|
||||||
ui_show_alert(app, "Invalid value", 1500);
|
ui_show_alert(app, "Invalid value", 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,94 +161,88 @@ static void text_input_done_callback(void* context) {
|
|||||||
* decrement the current field in a much simpler way.
|
* decrement the current field in a much simpler way.
|
||||||
*
|
*
|
||||||
* The current filed is changed by 'incr' amount. */
|
* The current filed is changed by 'incr' amount. */
|
||||||
static bool increment_current_field(ProtoViewApp *app, int incr) {
|
static bool increment_current_field(ProtoViewApp* app, int incr) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
ProtoViewFieldSet *fs = privdata->fieldset;
|
ProtoViewFieldSet* fs = privdata->fieldset;
|
||||||
ProtoViewField *f = fs->fields[privdata->cur_field];
|
ProtoViewField* f = fs->fields[privdata->cur_field];
|
||||||
return field_incr_value(f,incr);
|
return field_incr_value(f, incr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle input for fields editing mode. */
|
/* Handle input for fields editing mode. */
|
||||||
static void process_input_set_fields(ProtoViewApp *app, InputEvent input) {
|
static void process_input_set_fields(ProtoViewApp* app, InputEvent input) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
ProtoViewFieldSet *fs = privdata->fieldset;
|
ProtoViewFieldSet* fs = privdata->fieldset;
|
||||||
|
|
||||||
if (input.type == InputTypeShort && input.key == InputKeyOk) {
|
if(input.type == InputTypeShort && input.key == InputKeyOk) {
|
||||||
/* Show the keyboard to let the user type the new
|
/* Show the keyboard to let the user type the new
|
||||||
* value. */
|
* value. */
|
||||||
if (privdata->user_value == NULL)
|
if(privdata->user_value == NULL) privdata->user_value = malloc(USER_VALUE_LEN);
|
||||||
privdata->user_value = malloc(USER_VALUE_LEN);
|
field_to_string(privdata->user_value, USER_VALUE_LEN, fs->fields[privdata->cur_field]);
|
||||||
field_to_string(privdata->user_value, USER_VALUE_LEN,
|
ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN, text_input_done_callback);
|
||||||
fs->fields[privdata->cur_field]);
|
} else if(input.type == InputTypeShort && input.key == InputKeyDown) {
|
||||||
ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN,
|
privdata->cur_field = (privdata->cur_field + 1) % fs->numfields;
|
||||||
text_input_done_callback);
|
} else if(input.type == InputTypeShort && input.key == InputKeyUp) {
|
||||||
} else if (input.type == InputTypeShort && input.key == InputKeyDown) {
|
if(privdata->cur_field == 0)
|
||||||
privdata->cur_field = (privdata->cur_field+1) % fs->numfields;
|
privdata->cur_field = fs->numfields - 1;
|
||||||
} else if (input.type == InputTypeShort && input.key == InputKeyUp) {
|
|
||||||
if (privdata->cur_field == 0)
|
|
||||||
privdata->cur_field = fs->numfields-1;
|
|
||||||
else
|
else
|
||||||
privdata->cur_field--;
|
privdata->cur_field--;
|
||||||
} else if (input.type == InputTypeShort && input.key == InputKeyRight) {
|
} else if(input.type == InputTypeShort && input.key == InputKeyRight) {
|
||||||
increment_current_field(app,1);
|
increment_current_field(app, 1);
|
||||||
} else if (input.type == InputTypeShort && input.key == InputKeyLeft) {
|
} else if(input.type == InputTypeShort && input.key == InputKeyLeft) {
|
||||||
increment_current_field(app,-1);
|
increment_current_field(app, -1);
|
||||||
} else if (input.type == InputTypeRepeat && input.key == InputKeyRight) {
|
} else if(input.type == InputTypeRepeat && input.key == InputKeyRight) {
|
||||||
// The reason why we don't use a large increment directly
|
// The reason why we don't use a large increment directly
|
||||||
// is that certain field types only support +1 -1 increments.
|
// is that certain field types only support +1 -1 increments.
|
||||||
int times = 10;
|
int times = 10;
|
||||||
while(times--) increment_current_field(app,1);
|
while(times--) increment_current_field(app, 1);
|
||||||
} else if (input.type == InputTypeRepeat && input.key == InputKeyLeft) {
|
} else if(input.type == InputTypeRepeat && input.key == InputKeyLeft) {
|
||||||
int times = 10;
|
int times = 10;
|
||||||
while(times--) increment_current_field(app,-1);
|
while(times--) increment_current_field(app, -1);
|
||||||
} else if (input.type == InputTypeLong && input.key == InputKeyOk) {
|
} else if(input.type == InputTypeLong && input.key == InputKeyOk) {
|
||||||
// Build the message in a fresh raw buffer.
|
// Build the message in a fresh raw buffer.
|
||||||
if (privdata->decoder->build_message) {
|
if(privdata->decoder->build_message) {
|
||||||
RawSamplesBuffer *rs = raw_samples_alloc();
|
RawSamplesBuffer* rs = raw_samples_alloc();
|
||||||
privdata->decoder->build_message(rs,privdata->fieldset);
|
privdata->decoder->build_message(rs, privdata->fieldset);
|
||||||
app->signal_decoded = false; // So that the new signal will be
|
app->signal_decoded = false; // So that the new signal will be
|
||||||
// accepted as the current signal.
|
// accepted as the current signal.
|
||||||
scan_for_signal(app,rs);
|
scan_for_signal(app, rs);
|
||||||
raw_samples_free(rs);
|
raw_samples_free(rs);
|
||||||
ui_show_alert(app,"Done: press back key",3000);
|
ui_show_alert(app, "Done: press back key", 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle input for the build message view. */
|
/* Handle input for the build message view. */
|
||||||
void process_input_build_message(ProtoViewApp *app, InputEvent input) {
|
void process_input_build_message(ProtoViewApp* app, InputEvent input) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
if (privdata->decoder)
|
if(privdata->decoder)
|
||||||
process_input_set_fields(app,input);
|
process_input_set_fields(app, input);
|
||||||
else
|
else
|
||||||
process_input_select_decoder(app,input);
|
process_input_select_decoder(app, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enter view callback. */
|
/* Enter view callback. */
|
||||||
void view_enter_build_message(ProtoViewApp *app) {
|
void view_enter_build_message(ProtoViewApp* app) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
|
|
||||||
// When we enter the view, the current decoder is just set to zero.
|
// When we enter the view, the current decoder is just set to zero.
|
||||||
// Seek the next valid if needed.
|
// Seek the next valid if needed.
|
||||||
if (Decoders[privdata->cur_decoder]->get_fields == NULL) {
|
if(Decoders[privdata->cur_decoder]->get_fields == NULL) {
|
||||||
select_next_decoder(app);
|
select_next_decoder(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
// However if there is currently a decoded message, and the
|
// However if there is currently a decoded message, and the
|
||||||
// decoder of such message supports message building, let's
|
// decoder of such message supports message building, let's
|
||||||
// select it.
|
// select it.
|
||||||
if (app->signal_decoded &&
|
if(app->signal_decoded && app->msg_info->decoder->get_fields &&
|
||||||
app->msg_info->decoder->get_fields &&
|
app->msg_info->decoder->build_message) {
|
||||||
app->msg_info->decoder->build_message)
|
while(Decoders[privdata->cur_decoder] != app->msg_info->decoder) select_next_decoder(app);
|
||||||
{
|
|
||||||
while(Decoders[privdata->cur_decoder] != app->msg_info->decoder)
|
|
||||||
select_next_decoder(app);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called on exit for cleanup. */
|
/* Called on exit for cleanup. */
|
||||||
void view_exit_build_message(ProtoViewApp *app) {
|
void view_exit_build_message(ProtoViewApp* app) {
|
||||||
BuildViewPrivData *privdata = app->view_privdata;
|
BuildViewPrivData* privdata = app->view_privdata;
|
||||||
if (privdata->fieldset) fieldset_free(privdata->fieldset);
|
if(privdata->fieldset) fieldset_free(privdata->fieldset);
|
||||||
if (privdata->user_value) free(privdata->user_value);
|
if(privdata->user_value) free(privdata->user_value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,47 +7,46 @@
|
|||||||
|
|
||||||
/* Read directly from the G0 CC1101 pin, and draw a black or white
|
/* Read directly from the G0 CC1101 pin, and draw a black or white
|
||||||
* dot depending on the level. */
|
* dot depending on the level. */
|
||||||
void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) {
|
void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) {
|
||||||
if (!app->direct_sampling_enabled) {
|
if(!app->direct_sampling_enabled) {
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas,2,9,"Direct sampling is a special");
|
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, 18, "mode that displays the signal");
|
||||||
canvas_draw_str(canvas,2,27,"captured in real time. Like in");
|
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, 36, "a old CRT TV. It's very slow.");
|
||||||
canvas_draw_str(canvas,2,45,"Can crash your Flipper.");
|
canvas_draw_str(canvas, 2, 45, "Can crash your Flipper.");
|
||||||
canvas_set_font(canvas, FontPrimary);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < 64; y++) {
|
for(int y = 0; y < 64; y++) {
|
||||||
for (int x = 0; x < 128; x++) {
|
for(int x = 0; x < 128; x++) {
|
||||||
bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
|
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
|
/* Busy loop: this is a terrible approach as it blocks
|
||||||
* everything else, but for now it's the best we can do
|
* everything else, but for now it's the best we can do
|
||||||
* to obtain direct data with some spacing. */
|
* 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_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str_with_border(canvas,36,60,"Direct sampling",
|
canvas_draw_str_with_border(canvas, 36, 60, "Direct sampling", ColorWhite, ColorBlack);
|
||||||
ColorWhite,ColorBlack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle input */
|
/* Handle input */
|
||||||
void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) {
|
void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) {
|
||||||
if (input.type == InputTypePress && input.key == InputKeyOk) {
|
if(input.type == InputTypePress && input.key == InputKeyOk) {
|
||||||
app->direct_sampling_enabled = !app->direct_sampling_enabled;
|
app->direct_sampling_enabled = !app->direct_sampling_enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enter view. Stop the subghz thread to prevent access as we read
|
/* Enter view. Stop the subghz thread to prevent access as we read
|
||||||
* the CC1101 data directly. */
|
* the CC1101 data directly. */
|
||||||
void view_enter_direct_sampling(ProtoViewApp *app) {
|
void view_enter_direct_sampling(ProtoViewApp* app) {
|
||||||
if (app->txrx->txrx_state == TxRxStateRx &&
|
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
|
||||||
!app->txrx->debug_timer_sampling)
|
|
||||||
{
|
|
||||||
subghz_worker_stop(app->txrx->worker);
|
subghz_worker_stop(app->txrx->worker);
|
||||||
} else {
|
} else {
|
||||||
raw_sampling_worker_stop(app);
|
raw_sampling_worker_stop(app);
|
||||||
@@ -55,10 +54,8 @@ void view_enter_direct_sampling(ProtoViewApp *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Exit view. Restore the subghz thread. */
|
/* Exit view. Restore the subghz thread. */
|
||||||
void view_exit_direct_sampling(ProtoViewApp *app) {
|
void view_exit_direct_sampling(ProtoViewApp* app) {
|
||||||
if (app->txrx->txrx_state == TxRxStateRx &&
|
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
|
||||||
!app->txrx->debug_timer_sampling)
|
|
||||||
{
|
|
||||||
subghz_worker_start(app->txrx->worker);
|
subghz_worker_start(app->txrx->worker);
|
||||||
} else {
|
} else {
|
||||||
raw_sampling_worker_start(app);
|
raw_sampling_worker_start(app);
|
||||||
|
|||||||
@@ -20,31 +20,29 @@ typedef struct {
|
|||||||
* so that the user can see what they are saving. With left/right
|
* so that the user can see what they are saving. With left/right
|
||||||
* you can move to next rows. Here we store where we are. */
|
* you can move to next rows. Here we store where we are. */
|
||||||
uint32_t signal_display_start_row;
|
uint32_t signal_display_start_row;
|
||||||
char *filename;
|
char* filename;
|
||||||
uint8_t cur_info_page; // Info page to display. Useful when there are
|
uint8_t cur_info_page; // Info page to display. Useful when there are
|
||||||
// too many fields populated by the decoder that
|
// too many fields populated by the decoder that
|
||||||
// a single page is not enough.
|
// a single page is not enough.
|
||||||
} InfoViewPrivData;
|
} InfoViewPrivData;
|
||||||
|
|
||||||
/* Draw the text label and value of the specified info field at x,y. */
|
/* Draw the text label and value of the specified info field at x,y. */
|
||||||
static void render_info_field(Canvas *const canvas,
|
static void render_info_field(Canvas* const canvas, ProtoViewField* f, uint8_t x, uint8_t y) {
|
||||||
ProtoViewField *f, uint8_t x, uint8_t y)
|
|
||||||
{
|
|
||||||
char buf[64];
|
char buf[64];
|
||||||
char strval[32];
|
char strval[32];
|
||||||
|
|
||||||
field_to_string(strval,sizeof(strval),f);
|
field_to_string(strval, sizeof(strval), f);
|
||||||
snprintf(buf,sizeof(buf),"%s: %s", f->name, strval);
|
snprintf(buf, sizeof(buf), "%s: %s", f->name, strval);
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas, x, y, buf);
|
canvas_draw_str(canvas, x, y, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Render the view with the detected message information. */
|
/* Render the view with the detected message information. */
|
||||||
#define INFO_LINES_PER_PAGE 5
|
#define INFO_LINES_PER_PAGE 5
|
||||||
static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) {
|
static void render_subview_main(Canvas* const canvas, ProtoViewApp* app) {
|
||||||
InfoViewPrivData *privdata = app->view_privdata;
|
InfoViewPrivData* privdata = app->view_privdata;
|
||||||
uint8_t pages = (app->msg_info->fieldset->numfields
|
uint8_t pages =
|
||||||
+(INFO_LINES_PER_PAGE-1)) / INFO_LINES_PER_PAGE;
|
(app->msg_info->fieldset->numfields + (INFO_LINES_PER_PAGE - 1)) / INFO_LINES_PER_PAGE;
|
||||||
privdata->cur_info_page %= pages;
|
privdata->cur_info_page %= pages;
|
||||||
uint8_t current_page = privdata->cur_info_page;
|
uint8_t current_page = privdata->cur_info_page;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
@@ -53,9 +51,9 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) {
|
|||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
uint8_t y = 8, lineheight = 10;
|
uint8_t y = 8, lineheight = 10;
|
||||||
|
|
||||||
if (pages > 1) {
|
if(pages > 1) {
|
||||||
snprintf(buf,sizeof(buf),"%s %u/%u", app->msg_info->decoder->name,
|
snprintf(
|
||||||
current_page+1, pages);
|
buf, sizeof(buf), "%s %u/%u", app->msg_info->decoder->name, current_page + 1, pages);
|
||||||
canvas_draw_str(canvas, 0, y, buf);
|
canvas_draw_str(canvas, 0, y, buf);
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_str(canvas, 0, y, app->msg_info->decoder->name);
|
canvas_draw_str(canvas, 0, y, app->msg_info->decoder->name);
|
||||||
@@ -64,26 +62,30 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) {
|
|||||||
|
|
||||||
/* Draw the info fields. */
|
/* Draw the info fields. */
|
||||||
uint8_t max_lines = INFO_LINES_PER_PAGE;
|
uint8_t max_lines = INFO_LINES_PER_PAGE;
|
||||||
uint32_t j = current_page*max_lines;
|
uint32_t j = current_page * max_lines;
|
||||||
while (j < app->msg_info->fieldset->numfields) {
|
while(j < app->msg_info->fieldset->numfields) {
|
||||||
render_info_field(canvas,app->msg_info->fieldset->fields[j++],0,y);
|
render_info_field(canvas, app->msg_info->fieldset->fields[j++], 0, y);
|
||||||
y += lineheight;
|
y += lineheight;
|
||||||
if (--max_lines == 0) break;
|
if(--max_lines == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw a vertical "save" label. Temporary solution, to switch to
|
/* Draw a vertical "save" label. Temporary solution, to switch to
|
||||||
* something better ASAP. */
|
* something better ASAP. */
|
||||||
y = 37;
|
y = 37;
|
||||||
lineheight = 7;
|
lineheight = 7;
|
||||||
canvas_draw_str(canvas, 119, y, "s"); y += lineheight;
|
canvas_draw_str(canvas, 119, y, "s");
|
||||||
canvas_draw_str(canvas, 119, y, "a"); y += lineheight;
|
y += lineheight;
|
||||||
canvas_draw_str(canvas, 119, y, "v"); y += lineheight;
|
canvas_draw_str(canvas, 119, y, "a");
|
||||||
canvas_draw_str(canvas, 119, y, "e"); y += lineheight;
|
y += lineheight;
|
||||||
|
canvas_draw_str(canvas, 119, y, "v");
|
||||||
|
y += lineheight;
|
||||||
|
canvas_draw_str(canvas, 119, y, "e");
|
||||||
|
y += lineheight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Render view with save option. */
|
/* Render view with save option. */
|
||||||
static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) {
|
static void render_subview_save(Canvas* const canvas, ProtoViewApp* app) {
|
||||||
InfoViewPrivData *privdata = app->view_privdata;
|
InfoViewPrivData* privdata = app->view_privdata;
|
||||||
|
|
||||||
/* Display our signal in digital form: here we don't show the
|
/* Display our signal in digital form: here we don't show the
|
||||||
* signal with the exact timing of the received samples, but as it
|
* signal with the exact timing of the received samples, but as it
|
||||||
@@ -92,21 +94,20 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) {
|
|||||||
uint8_t rowheight = 11;
|
uint8_t rowheight = 11;
|
||||||
uint8_t bitwidth = 4;
|
uint8_t bitwidth = 4;
|
||||||
uint8_t bitheight = 5;
|
uint8_t bitheight = 5;
|
||||||
uint32_t idx = privdata->signal_display_start_row * (128/4);
|
uint32_t idx = privdata->signal_display_start_row * (128 / 4);
|
||||||
bool prevbit = false;
|
bool prevbit = false;
|
||||||
for (uint8_t y = bitheight+12; y <= rows*rowheight; y += rowheight) {
|
for(uint8_t y = bitheight + 12; y <= rows * rowheight; y += rowheight) {
|
||||||
for (uint8_t x = 0; x < 128; x += 4) {
|
for(uint8_t x = 0; x < 128; x += 4) {
|
||||||
bool bit = bitmap_get(app->msg_info->bits,
|
bool bit = bitmap_get(app->msg_info->bits, app->msg_info->bits_bytes, idx);
|
||||||
app->msg_info->bits_bytes,idx);
|
uint8_t prevy = y + prevbit * (bitheight * -1) - 1;
|
||||||
uint8_t prevy = y + prevbit*(bitheight*-1) - 1;
|
uint8_t thisy = y + bit * (bitheight * -1) - 1;
|
||||||
uint8_t thisy = y + bit*(bitheight*-1) - 1;
|
canvas_draw_line(canvas, x, prevy, x, thisy);
|
||||||
canvas_draw_line(canvas,x,prevy,x,thisy);
|
canvas_draw_line(canvas, x, thisy, x + bitwidth - 1, thisy);
|
||||||
canvas_draw_line(canvas,x,thisy,x+bitwidth-1,thisy);
|
|
||||||
prevbit = bit;
|
prevbit = bit;
|
||||||
if (idx >= app->msg_info->pulses_count) {
|
if(idx >= app->msg_info->pulses_count) {
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
canvas_draw_dot(canvas, x+1,thisy);
|
canvas_draw_dot(canvas, x + 1, thisy);
|
||||||
canvas_draw_dot(canvas, x+3,thisy);
|
canvas_draw_dot(canvas, x + 3, thisy);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
}
|
}
|
||||||
idx++; // Draw next bit
|
idx++; // Draw next bit
|
||||||
@@ -118,28 +119,32 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Render the selected subview of this view. */
|
/* Render the selected subview of this view. */
|
||||||
void render_view_info(Canvas *const canvas, ProtoViewApp *app) {
|
void render_view_info(Canvas* const canvas, ProtoViewApp* app) {
|
||||||
if (app->signal_decoded == false) {
|
if(app->signal_decoded == false) {
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas, 30,36,"No signal decoded");
|
canvas_draw_str(canvas, 30, 36, "No signal decoded");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui_show_available_subviews(canvas,app,SubViewInfoLast);
|
ui_show_available_subviews(canvas, app, SubViewInfoLast);
|
||||||
switch(app->current_subview[app->current_view]) {
|
switch(app->current_subview[app->current_view]) {
|
||||||
case SubViewInfoMain: render_subview_main(canvas,app); break;
|
case SubViewInfoMain:
|
||||||
case SubViewInfoSave: render_subview_save(canvas,app); break;
|
render_subview_main(canvas, app);
|
||||||
|
break;
|
||||||
|
case SubViewInfoSave:
|
||||||
|
render_subview_save(canvas, app);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The user typed the file name. Let's save it and remove the keyboard
|
/* The user typed the file name. Let's save it and remove the keyboard
|
||||||
* view. */
|
* view. */
|
||||||
static void text_input_done_callback(void* context) {
|
static void text_input_done_callback(void* context) {
|
||||||
ProtoViewApp *app = context;
|
ProtoViewApp* app = context;
|
||||||
InfoViewPrivData *privdata = app->view_privdata;
|
InfoViewPrivData* privdata = app->view_privdata;
|
||||||
|
|
||||||
FuriString *save_path = furi_string_alloc_printf(
|
FuriString* save_path =
|
||||||
"%s/%s.sub", EXT_PATH("subghz"), privdata->filename);
|
furi_string_alloc_printf("%s/%s.sub", EXT_PATH("subghz"), privdata->filename);
|
||||||
save_signal(app, furi_string_get_cstr(save_path));
|
save_signal(app, furi_string_get_cstr(save_path));
|
||||||
furi_string_free(save_path);
|
furi_string_free(save_path);
|
||||||
|
|
||||||
@@ -151,22 +156,22 @@ static void text_input_done_callback(void* context) {
|
|||||||
|
|
||||||
/* Replace all the occurrences of character c1 with c2 in the specified
|
/* Replace all the occurrences of character c1 with c2 in the specified
|
||||||
* string. */
|
* string. */
|
||||||
void str_replace(char *buf, char c1, char c2) {
|
void str_replace(char* buf, char c1, char c2) {
|
||||||
char *p = buf;
|
char* p = buf;
|
||||||
while(*p) {
|
while(*p) {
|
||||||
if (*p == c1) *p = c2;
|
if(*p == c1) *p = c2;
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set a random filename the user can edit. */
|
/* Set a random filename the user can edit. */
|
||||||
void set_signal_random_filename(ProtoViewApp *app, char *buf, size_t buflen) {
|
void set_signal_random_filename(ProtoViewApp* app, char* buf, size_t buflen) {
|
||||||
char suffix[6];
|
char suffix[6];
|
||||||
set_random_name(suffix,sizeof(suffix));
|
set_random_name(suffix, sizeof(suffix));
|
||||||
snprintf(buf,buflen,"%.10s-%s-%d",app->msg_info->decoder->name,suffix,rand()%1000);
|
snprintf(buf, buflen, "%.10s-%s-%d", app->msg_info->decoder->name, suffix, rand() % 1000);
|
||||||
str_replace(buf,' ','_');
|
str_replace(buf, ' ', '_');
|
||||||
str_replace(buf,'-','_');
|
str_replace(buf, '-', '_');
|
||||||
str_replace(buf,'/','_');
|
str_replace(buf, '/', '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== Signal transmission =========================== */
|
/* ========================== Signal transmission =========================== */
|
||||||
@@ -180,20 +185,20 @@ typedef enum {
|
|||||||
SendSignalEndTransmission
|
SendSignalEndTransmission
|
||||||
} SendSignalState;
|
} SendSignalState;
|
||||||
|
|
||||||
#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */
|
#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */
|
||||||
#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */
|
#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SendSignalState state; // Current state.
|
SendSignalState state; // Current state.
|
||||||
uint32_t curpos; // Current bit position of data to send.
|
uint32_t curpos; // Current bit position of data to send.
|
||||||
ProtoViewApp *app; // App reference.
|
ProtoViewApp* app; // App reference.
|
||||||
uint32_t start_gap_dur; // Gap to send at the start.
|
uint32_t start_gap_dur; // Gap to send at the start.
|
||||||
uint32_t end_gap_dur; // Gap to send at the end.
|
uint32_t end_gap_dur; // Gap to send at the end.
|
||||||
} SendSignalCtx;
|
} SendSignalCtx;
|
||||||
|
|
||||||
/* Setup the state context for the callback responsible to feed data
|
/* Setup the state context for the callback responsible to feed data
|
||||||
* to the subghz async tx system. */
|
* to the subghz async tx system. */
|
||||||
static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) {
|
static void send_signal_init(SendSignalCtx* ss, ProtoViewApp* app) {
|
||||||
ss->state = SendSignalSendStartGap;
|
ss->state = SendSignalSendStartGap;
|
||||||
ss->curpos = 0;
|
ss->curpos = 0;
|
||||||
ss->app = app;
|
ss->app = app;
|
||||||
@@ -214,27 +219,26 @@ static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) {
|
|||||||
* message we are, in ss->curoff. We also send a start and end gap in order
|
* message we are, in ss->curoff. We also send a start and end gap in order
|
||||||
* to make sure the transmission is clear.
|
* to make sure the transmission is clear.
|
||||||
*/
|
*/
|
||||||
LevelDuration radio_tx_feed_data(void *ctx) {
|
LevelDuration radio_tx_feed_data(void* ctx) {
|
||||||
SendSignalCtx *ss = ctx;
|
SendSignalCtx* ss = ctx;
|
||||||
|
|
||||||
/* Send start gap. */
|
/* Send start gap. */
|
||||||
if (ss->state == SendSignalSendStartGap) {
|
if(ss->state == SendSignalSendStartGap) {
|
||||||
ss->state = SendSignalSendBits;
|
ss->state = SendSignalSendBits;
|
||||||
return level_duration_make(0,ss->start_gap_dur);
|
return level_duration_make(0, ss->start_gap_dur);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send data. */
|
/* Send data. */
|
||||||
if (ss->state == SendSignalSendBits) {
|
if(ss->state == SendSignalSendBits) {
|
||||||
uint32_t dur = 0, j;
|
uint32_t dur = 0, j;
|
||||||
uint32_t level = 0;
|
uint32_t level = 0;
|
||||||
|
|
||||||
/* Let's see how many consecutive bits we have with the same
|
/* Let's see how many consecutive bits we have with the same
|
||||||
* level. */
|
* level. */
|
||||||
for (j = 0; ss->curpos+j < ss->app->msg_info->pulses_count; j++) {
|
for(j = 0; ss->curpos + j < ss->app->msg_info->pulses_count; j++) {
|
||||||
uint32_t l = bitmap_get(ss->app->msg_info->bits,
|
uint32_t l =
|
||||||
ss->app->msg_info->bits_bytes,
|
bitmap_get(ss->app->msg_info->bits, ss->app->msg_info->bits_bytes, ss->curpos + j);
|
||||||
ss->curpos+j);
|
if(j == 0) {
|
||||||
if (j == 0) {
|
|
||||||
/* At the first bit of this sequence, we store the
|
/* At the first bit of this sequence, we store the
|
||||||
* level of the sequence. */
|
* level of the sequence. */
|
||||||
level = l;
|
level = l;
|
||||||
@@ -244,22 +248,21 @@ LevelDuration radio_tx_feed_data(void *ctx) {
|
|||||||
|
|
||||||
/* As long as the level is the same, we update the duration.
|
/* As long as the level is the same, we update the duration.
|
||||||
* Otherwise stop the loop and return this sample. */
|
* Otherwise stop the loop and return this sample. */
|
||||||
if (l != level) break;
|
if(l != level) break;
|
||||||
dur += ss->app->msg_info->short_pulse_dur;
|
dur += ss->app->msg_info->short_pulse_dur;
|
||||||
}
|
}
|
||||||
ss->curpos += j;
|
ss->curpos += j;
|
||||||
|
|
||||||
/* If this was the last set of bits, change the state to
|
/* If this was the last set of bits, change the state to
|
||||||
* send the final gap. */
|
* send the final gap. */
|
||||||
if (ss->curpos >= ss->app->msg_info->pulses_count)
|
if(ss->curpos >= ss->app->msg_info->pulses_count) ss->state = SendSignalSendEndGap;
|
||||||
ss->state = SendSignalSendEndGap;
|
|
||||||
return level_duration_make(level, dur);
|
return level_duration_make(level, dur);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send end gap. */
|
/* Send end gap. */
|
||||||
if (ss->state == SendSignalSendEndGap) {
|
if(ss->state == SendSignalSendEndGap) {
|
||||||
ss->state = SendSignalEndTransmission;
|
ss->state = SendSignalEndTransmission;
|
||||||
return level_duration_make(0,ss->end_gap_dur);
|
return level_duration_make(0, ss->end_gap_dur);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End transmission. Here state is guaranteed
|
/* End transmission. Here state is guaranteed
|
||||||
@@ -268,7 +271,7 @@ LevelDuration radio_tx_feed_data(void *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Vibrate and produce a click sound when a signal is sent. */
|
/* Vibrate and produce a click sound when a signal is sent. */
|
||||||
void notify_signal_sent(ProtoViewApp *app) {
|
void notify_signal_sent(ProtoViewApp* app) {
|
||||||
static const NotificationSequence sent_seq = {
|
static const NotificationSequence sent_seq = {
|
||||||
&message_blue_255,
|
&message_blue_255,
|
||||||
&message_vibro_on,
|
&message_vibro_on,
|
||||||
@@ -277,59 +280,53 @@ void notify_signal_sent(ProtoViewApp *app) {
|
|||||||
&message_sound_off,
|
&message_sound_off,
|
||||||
&message_vibro_off,
|
&message_vibro_off,
|
||||||
&message_blue_0,
|
&message_blue_0,
|
||||||
NULL
|
NULL};
|
||||||
};
|
|
||||||
notification_message(app->notification, &sent_seq);
|
notification_message(app->notification, &sent_seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle input for the info view. */
|
/* Handle input for the info view. */
|
||||||
void process_input_info(ProtoViewApp *app, InputEvent input) {
|
void process_input_info(ProtoViewApp* app, InputEvent input) {
|
||||||
/* If we don't have a decoded signal, we don't allow to go up/down
|
/* If we don't have a decoded signal, we don't allow to go up/down
|
||||||
* in the subviews: they are only useful when a loaded signal. */
|
* in the subviews: they are only useful when a loaded signal. */
|
||||||
if (app->signal_decoded &&
|
if(app->signal_decoded && ui_process_subview_updown(app, input, SubViewInfoLast)) return;
|
||||||
ui_process_subview_updown(app,input,SubViewInfoLast)) return;
|
|
||||||
|
|
||||||
InfoViewPrivData *privdata = app->view_privdata;
|
InfoViewPrivData* privdata = app->view_privdata;
|
||||||
int subview = ui_get_current_subview(app);
|
int subview = ui_get_current_subview(app);
|
||||||
|
|
||||||
/* Main subview. */
|
/* Main subview. */
|
||||||
if (subview == SubViewInfoMain) {
|
if(subview == SubViewInfoMain) {
|
||||||
if (input.type == InputTypeLong && input.key == InputKeyOk) {
|
if(input.type == InputTypeLong && input.key == InputKeyOk) {
|
||||||
/* Reset the current sample to capture the next. */
|
/* Reset the current sample to capture the next. */
|
||||||
reset_current_signal(app);
|
reset_current_signal(app);
|
||||||
} else if (input.type == InputTypeShort && input.key == InputKeyOk) {
|
} else if(input.type == InputTypeShort && input.key == InputKeyOk) {
|
||||||
/* Show next info page. */
|
/* Show next info page. */
|
||||||
privdata->cur_info_page++;
|
privdata->cur_info_page++;
|
||||||
}
|
}
|
||||||
} else if (subview == SubViewInfoSave) {
|
} else if(subview == SubViewInfoSave) {
|
||||||
/* Save subview. */
|
/* Save subview. */
|
||||||
if (input.type == InputTypePress && input.key == InputKeyRight) {
|
if(input.type == InputTypePress && input.key == InputKeyRight) {
|
||||||
privdata->signal_display_start_row++;
|
privdata->signal_display_start_row++;
|
||||||
} else if (input.type == InputTypePress && input.key == InputKeyLeft) {
|
} else if(input.type == InputTypePress && input.key == InputKeyLeft) {
|
||||||
if (privdata->signal_display_start_row != 0)
|
if(privdata->signal_display_start_row != 0) privdata->signal_display_start_row--;
|
||||||
privdata->signal_display_start_row--;
|
} else if(input.type == InputTypeLong && input.key == InputKeyOk) {
|
||||||
} else if (input.type == InputTypeLong && input.key == InputKeyOk)
|
|
||||||
{
|
|
||||||
// We have have the buffer already allocated, in case the
|
// We have have the buffer already allocated, in case the
|
||||||
// user aborted with BACK a previous saving.
|
// user aborted with BACK a previous saving.
|
||||||
if (privdata->filename == NULL)
|
if(privdata->filename == NULL) privdata->filename = malloc(SAVE_FILENAME_LEN);
|
||||||
privdata->filename = malloc(SAVE_FILENAME_LEN);
|
set_signal_random_filename(app, privdata->filename, SAVE_FILENAME_LEN);
|
||||||
set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN);
|
ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, text_input_done_callback);
|
||||||
ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
|
} else if(input.type == InputTypeShort && input.key == InputKeyOk) {
|
||||||
text_input_done_callback);
|
|
||||||
} else if (input.type == InputTypeShort && input.key == InputKeyOk) {
|
|
||||||
SendSignalCtx send_state;
|
SendSignalCtx send_state;
|
||||||
send_signal_init(&send_state,app);
|
send_signal_init(&send_state, app);
|
||||||
radio_tx_signal(app,radio_tx_feed_data,&send_state);
|
radio_tx_signal(app, radio_tx_feed_data, &send_state);
|
||||||
notify_signal_sent(app);
|
notify_signal_sent(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called on view exit. */
|
/* Called on view exit. */
|
||||||
void view_exit_info(ProtoViewApp *app) {
|
void view_exit_info(ProtoViewApp* app) {
|
||||||
InfoViewPrivData *privdata = app->view_privdata;
|
InfoViewPrivData* privdata = app->view_privdata;
|
||||||
// When the user aborts the keyboard input, we are left with the
|
// When the user aborts the keyboard input, we are left with the
|
||||||
// filename buffer allocated.
|
// filename buffer allocated.
|
||||||
if (privdata->filename) free(privdata->filename);
|
if(privdata->filename) free(privdata->filename);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
*
|
*
|
||||||
* The 'idx' argument is the first sample to render in the circular
|
* The 'idx' argument is the first sample to render in the circular
|
||||||
* buffer. */
|
* 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);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
int rows = 8;
|
int rows = 8;
|
||||||
@@ -20,31 +20,29 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu
|
|||||||
uint32_t start_idx = idx;
|
uint32_t start_idx = idx;
|
||||||
bool level = 0;
|
bool level = 0;
|
||||||
uint32_t dur = 0, sample_num = 0;
|
uint32_t dur = 0, sample_num = 0;
|
||||||
for (int row = 0; row < rows ; row++) {
|
for(int row = 0; row < rows; row++) {
|
||||||
for (int x = 0; x < 128; x++) {
|
for(int x = 0; x < 128; x++) {
|
||||||
int y = 3 + row*8;
|
int y = 3 + row * 8;
|
||||||
if (dur < time_per_pixel/2) {
|
if(dur < time_per_pixel / 2) {
|
||||||
/* Get more data. */
|
/* Get more data. */
|
||||||
raw_samples_get(buf, idx++, &level, &dur);
|
raw_samples_get(buf, idx++, &level, &dur);
|
||||||
sample_num++;
|
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. */
|
/* Write a small triangle under the last sample detected. */
|
||||||
if (app->signal_bestlen != 0 &&
|
if(app->signal_bestlen != 0 && sample_num + start_idx == app->signal_bestlen + 1) {
|
||||||
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+2);
|
canvas_draw_dot(canvas, x, y + 3);
|
||||||
canvas_draw_dot(canvas,x-1,y+3);
|
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. */
|
sample_num++; /* Make sure we don't mark the next, too. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove from the current level duration the time we
|
/* Remove from the current level duration the time we
|
||||||
* just plot. */
|
* just plot. */
|
||||||
if (dur > time_per_pixel)
|
if(dur > time_per_pixel)
|
||||||
dur -= time_per_pixel;
|
dur -= time_per_pixel;
|
||||||
else
|
else
|
||||||
dur = 0;
|
dur = 0;
|
||||||
@@ -53,61 +51,63 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Raw pulses rendering. This is our default view. */
|
/* 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. */
|
/* Show signal. */
|
||||||
render_signal(app, canvas, DetectedSamples, app->signal_offset);
|
render_signal(app, canvas, DetectedSamples, app->signal_offset);
|
||||||
|
|
||||||
/* Show signal information. */
|
/* Show signal information. */
|
||||||
char buf[64];
|
char buf[64];
|
||||||
snprintf(buf,sizeof(buf),"%luus",
|
snprintf(buf, sizeof(buf), "%luus", (unsigned long)DetectedSamples->short_pulse_dur);
|
||||||
(unsigned long)DetectedSamples->short_pulse_dur);
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str_with_border(canvas, 97, 63, buf, ColorWhite, ColorBlack);
|
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_set_font(canvas, FontPrimary);
|
||||||
canvas_draw_str_with_border(canvas, 1, 61, app->msg_info->decoder->name, ColorWhite, ColorBlack);
|
canvas_draw_str_with_border(
|
||||||
|
canvas, 1, 61, app->msg_info->decoder->name, ColorWhite, ColorBlack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle input for the raw pulses view. */
|
/* Handle input for the raw pulses view. */
|
||||||
void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) {
|
void process_input_raw_pulses(ProtoViewApp* app, InputEvent input) {
|
||||||
if (input.type == InputTypeRepeat) {
|
if(input.type == InputTypeRepeat) {
|
||||||
/* Handle panning of the signal window. Long pressing
|
/* Handle panning of the signal window. Long pressing
|
||||||
* right will show successive samples, long pressing left
|
* right will show successive samples, long pressing left
|
||||||
* previous samples. */
|
* previous samples. */
|
||||||
if (input.key == InputKeyRight) app->signal_offset++;
|
if(input.key == InputKeyRight)
|
||||||
else if (input.key == InputKeyLeft) app->signal_offset--;
|
app->signal_offset++;
|
||||||
} else if (input.type == InputTypeLong) {
|
else if(input.key == InputKeyLeft)
|
||||||
if (input.key == InputKeyOk) {
|
app->signal_offset--;
|
||||||
|
} else if(input.type == InputTypeLong) {
|
||||||
|
if(input.key == InputKeyOk) {
|
||||||
/* Reset the current sample to capture the next. */
|
/* Reset the current sample to capture the next. */
|
||||||
reset_current_signal(app);
|
reset_current_signal(app);
|
||||||
}
|
}
|
||||||
} else if (input.type == InputTypeShort) {
|
} else if(input.type == InputTypeShort) {
|
||||||
if (input.key == InputKeyOk) {
|
if(input.key == InputKeyOk) {
|
||||||
app->signal_offset = 0;
|
app->signal_offset = 0;
|
||||||
adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur);
|
adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur);
|
||||||
} else if (input.key == InputKeyDown) {
|
} else if(input.key == InputKeyDown) {
|
||||||
/* Rescaling. The set becomes finer under 50us per pixel. */
|
/* Rescaling. The set becomes finer under 50us per pixel. */
|
||||||
uint32_t scale_step = app->us_scale >= 50 ? 50 : 10;
|
uint32_t scale_step = app->us_scale >= 50 ? 50 : 10;
|
||||||
if (app->us_scale < 500) app->us_scale += scale_step;
|
if(app->us_scale < 500) app->us_scale += scale_step;
|
||||||
} else if (input.key == InputKeyUp) {
|
} else if(input.key == InputKeyUp) {
|
||||||
uint32_t scale_step = app->us_scale > 50 ? 50 : 10;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust raw view scale depending on short pulse duration. */
|
/* Adjust raw view scale depending on short pulse duration. */
|
||||||
void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur) {
|
void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur) {
|
||||||
if (short_pulse_dur == 0)
|
if(short_pulse_dur == 0)
|
||||||
app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
|
app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
|
||||||
else if (short_pulse_dur < 75)
|
else if(short_pulse_dur < 75)
|
||||||
app->us_scale = 10;
|
app->us_scale = 10;
|
||||||
else if (short_pulse_dur < 145)
|
else if(short_pulse_dur < 145)
|
||||||
app->us_scale = 30;
|
app->us_scale = 30;
|
||||||
else if (short_pulse_dur < 400)
|
else if(short_pulse_dur < 400)
|
||||||
app->us_scale = 100;
|
app->us_scale = 100;
|
||||||
else if (short_pulse_dur < 1000)
|
else if(short_pulse_dur < 1000)
|
||||||
app->us_scale = 200;
|
app->us_scale = 200;
|
||||||
else
|
else
|
||||||
app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
|
app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
|
||||||
|
|||||||
@@ -6,30 +6,30 @@
|
|||||||
/* Renders a single view with frequency and modulation setting. However
|
/* Renders a single view with frequency and modulation setting. However
|
||||||
* this are logically two different views, and only one of the settings
|
* this are logically two different views, and only one of the settings
|
||||||
* will be highlighted. */
|
* 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);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
if (app->current_view == ViewFrequencySettings)
|
if(app->current_view == ViewFrequencySettings)
|
||||||
canvas_draw_str_with_border(canvas,1,10,"Frequency",ColorWhite,ColorBlack);
|
canvas_draw_str_with_border(canvas, 1, 10, "Frequency", ColorWhite, ColorBlack);
|
||||||
else
|
else
|
||||||
canvas_draw_str(canvas,1,10,"Frequency");
|
canvas_draw_str(canvas, 1, 10, "Frequency");
|
||||||
|
|
||||||
if (app->current_view == ViewModulationSettings)
|
if(app->current_view == ViewModulationSettings)
|
||||||
canvas_draw_str_with_border(canvas,70,10,"Modulation",ColorWhite,ColorBlack);
|
canvas_draw_str_with_border(canvas, 70, 10, "Modulation", ColorWhite, ColorBlack);
|
||||||
else
|
else
|
||||||
canvas_draw_str(canvas,70,10,"Modulation");
|
canvas_draw_str(canvas, 70, 10, "Modulation");
|
||||||
canvas_set_font(canvas, FontSecondary);
|
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)
|
if(app->txrx->debug_timer_sampling)
|
||||||
canvas_draw_str(canvas,3,52,"(DEBUG timer sampling is ON)");
|
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. */
|
/* 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];
|
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_set_font(canvas, FontBigNumbers);
|
||||||
canvas_draw_str(canvas, 30, 40, buf);
|
canvas_draw_str(canvas, 30, 40, buf);
|
||||||
} else if (app->current_view == ViewModulationSettings) {
|
} else if(app->current_view == ViewModulationSettings) {
|
||||||
int current = app->modulation;
|
int current = app->modulation;
|
||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
canvas_draw_str(canvas, 33, 39, ProtoViewModulations[current].name);
|
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. */
|
/* Handle input for the settings view. */
|
||||||
void process_input_settings(ProtoViewApp *app, InputEvent input) {
|
void process_input_settings(ProtoViewApp* app, InputEvent input) {
|
||||||
if (input.type == InputTypeLong && input.key == InputKeyOk) {
|
if(input.type == InputTypeLong && input.key == InputKeyOk) {
|
||||||
/* Long pressing to OK sets the default frequency and
|
/* Long pressing to OK sets the default frequency and
|
||||||
* modulation. */
|
* modulation. */
|
||||||
app->frequency = subghz_setting_get_default_frequency(app->setting);
|
app->frequency = subghz_setting_get_default_frequency(app->setting);
|
||||||
app->modulation = 0;
|
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
|
/* Long pressing to down switches between normal and debug
|
||||||
* timer sampling mode. NOTE: this feature is disabled for users,
|
* timer sampling mode. NOTE: this feature is disabled for users,
|
||||||
* only useful for devs (if useful at all). */
|
* 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;
|
app->txrx->debug_timer_sampling = !app->txrx->debug_timer_sampling;
|
||||||
radio_begin(app);
|
radio_begin(app);
|
||||||
radio_rx(app);
|
radio_rx(app);
|
||||||
} else if (input.type == InputTypePress &&
|
} else if(input.type == InputTypePress && (input.key != InputKeyDown || input.key != InputKeyUp)) {
|
||||||
(input.key != InputKeyDown || input.key != InputKeyUp))
|
|
||||||
{
|
|
||||||
/* Handle up and down to change frequency or modulation. */
|
/* 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 curidx = 0, i;
|
||||||
size_t count = subghz_setting_get_frequency_count(app->setting);
|
size_t count = subghz_setting_get_frequency_count(app->setting);
|
||||||
|
|
||||||
/* Scan the list of frequencies to check for the index of the
|
/* Scan the list of frequencies to check for the index of the
|
||||||
* currently set frequency. */
|
* currently set frequency. */
|
||||||
for(i = 0; i < count; i++) {
|
for(i = 0; i < count; i++) {
|
||||||
uint32_t freq = subghz_setting_get_frequency(app->setting,i);
|
uint32_t freq = subghz_setting_get_frequency(app->setting, i);
|
||||||
if (freq == app->frequency) {
|
if(freq == app->frequency) {
|
||||||
curidx = i;
|
curidx = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == count) return; /* Should never happen. */
|
if(i == count) return; /* Should never happen. */
|
||||||
|
|
||||||
if (input.key == InputKeyUp) {
|
if(input.key == InputKeyUp) {
|
||||||
curidx = curidx == 0 ? count-1 : curidx-1;
|
curidx = curidx == 0 ? count - 1 : curidx - 1;
|
||||||
} else if (input.key == InputKeyDown) {
|
} else if(input.key == InputKeyDown) {
|
||||||
curidx = (curidx+1) % count;
|
curidx = (curidx + 1) % count;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app->frequency = subghz_setting_get_frequency(app->setting,curidx);
|
app->frequency = subghz_setting_get_frequency(app->setting, curidx);
|
||||||
} else if (app->current_view == ViewModulationSettings) {
|
} else if(app->current_view == ViewModulationSettings) {
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
uint32_t modid = app->modulation;
|
uint32_t modid = app->modulation;
|
||||||
|
|
||||||
while(ProtoViewModulations[count].name != NULL) count++;
|
while(ProtoViewModulations[count].name != NULL) count++;
|
||||||
if (input.key == InputKeyUp) {
|
if(input.key == InputKeyUp) {
|
||||||
modid = modid == 0 ? count-1 : modid-1;
|
modid = modid == 0 ? count - 1 : modid - 1;
|
||||||
} else if (input.key == InputKeyDown) {
|
} else if(input.key == InputKeyDown) {
|
||||||
modid = (modid+1) % count;
|
modid = (modid + 1) % count;
|
||||||
} else {
|
} else {
|
||||||
return;
|
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
|
/* 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. */
|
* we need to restart the radio with the right frequency and modulation. */
|
||||||
void view_exit_settings(ProtoViewApp *app) {
|
void view_exit_settings(ProtoViewApp* app) {
|
||||||
if (app->txrx->freq_mod_changed) {
|
if(app->txrx->freq_mod_changed) {
|
||||||
FURI_LOG_E(TAG, "Setting view, setting frequency/modulation to %lu %s", app->frequency, ProtoViewModulations[app->modulation].name);
|
FURI_LOG_E(
|
||||||
|
TAG,
|
||||||
|
"Setting view, setting frequency/modulation to %lu %s",
|
||||||
|
app->frequency,
|
||||||
|
ProtoViewModulations[app->modulation].name);
|
||||||
radio_rx_end(app);
|
radio_rx_end(app);
|
||||||
radio_begin(app);
|
radio_begin(app);
|
||||||
radio_rx(app);
|
radio_rx(app);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ static void tama_p1_hal_log(log_level_t level, char* buff, ...) {
|
|||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, buff);
|
va_start(args, buff);
|
||||||
furi_string_cat_vprintf(string, buff, args);
|
furi_string_cat_vprintf(string, buff, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
switch(level) {
|
switch(level) {
|
||||||
case LOG_ERROR:
|
case LOG_ERROR:
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#define STATE_FILE_VERSION 2
|
#define STATE_FILE_VERSION 2
|
||||||
#define TAMA_SAVE_PATH EXT_PATH("tama_p1/save.bin")
|
#define TAMA_SAVE_PATH EXT_PATH("tama_p1/save.bin")
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FuriThread* thread;
|
FuriThread* thread;
|
||||||
hal_t hal;
|
hal_t hal;
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) {
|
|||||||
uint16_t lcd_icon_lower_left = lcd_matrix_left;
|
uint16_t lcd_icon_lower_left = lcd_matrix_left;
|
||||||
uint16_t lcd_icon_spacing_horiz =
|
uint16_t lcd_icon_spacing_horiz =
|
||||||
(lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE;
|
(lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE;
|
||||||
|
|
||||||
|
|
||||||
uint16_t y = lcd_matrix_top;
|
uint16_t y = lcd_matrix_top;
|
||||||
for(uint8_t row = 0; row < 16; ++row) {
|
for(uint8_t row = 0; row < 16; ++row) {
|
||||||
@@ -71,7 +70,7 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) {
|
|||||||
|
|
||||||
// Start drawing icons
|
// Start drawing icons
|
||||||
uint8_t lcd_icons = g_ctx->icons;
|
uint8_t lcd_icons = g_ctx->icons;
|
||||||
|
|
||||||
// Draw top icons
|
// Draw top icons
|
||||||
y = lcd_icon_upper_top;
|
y = lcd_icon_upper_top;
|
||||||
// y = 64 - TAMA_LCD_ICON_SIZE;
|
// y = 64 - TAMA_LCD_ICON_SIZE;
|
||||||
@@ -114,135 +113,134 @@ static void tama_p1_update_timer_callback(FuriMessageQueue* event_queue) {
|
|||||||
TamaEvent event = {.type = EventTypeTick};
|
TamaEvent event = {.type = EventTypeTick};
|
||||||
furi_message_queue_put(event_queue, &event, 0);
|
furi_message_queue_put(event_queue, &event, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tama_p1_load_state() {
|
static void tama_p1_load_state() {
|
||||||
state_t *state;
|
state_t* state;
|
||||||
uint8_t buf[4];
|
uint8_t buf[4];
|
||||||
bool error = false;
|
bool error = false;
|
||||||
state = tamalib_get_state();
|
state = tamalib_get_state();
|
||||||
|
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
|
storage_file_read(file, &buf, 4);
|
||||||
storage_file_read(file, &buf, 4);
|
if(buf[0] != (uint8_t)STATE_FILE_MAGIC[0] || buf[1] != (uint8_t)STATE_FILE_MAGIC[1] ||
|
||||||
if (buf[0] != (uint8_t) STATE_FILE_MAGIC[0] || buf[1] != (uint8_t) STATE_FILE_MAGIC[1] ||
|
buf[2] != (uint8_t)STATE_FILE_MAGIC[2] || buf[3] != (uint8_t)STATE_FILE_MAGIC[3]) {
|
||||||
buf[2] != (uint8_t) STATE_FILE_MAGIC[2] || buf[3] != (uint8_t) STATE_FILE_MAGIC[3]) {
|
|
||||||
FURI_LOG_E(TAG, "FATAL: Wrong state file magic in \"%s\" !\n", TAMA_SAVE_PATH);
|
FURI_LOG_E(TAG, "FATAL: Wrong state file magic in \"%s\" !\n", TAMA_SAVE_PATH);
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
if (buf[0] != STATE_FILE_VERSION) {
|
if(buf[0] != STATE_FILE_VERSION) {
|
||||||
FURI_LOG_E(TAG, "FATAL: Unsupported version");
|
FURI_LOG_E(TAG, "FATAL: Unsupported version");
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
if (!error) {
|
if(!error) {
|
||||||
FURI_LOG_D(TAG, "Reading save.bin");
|
FURI_LOG_D(TAG, "Reading save.bin");
|
||||||
|
|
||||||
storage_file_read(file, &buf, 2);
|
storage_file_read(file, &buf, 2);
|
||||||
*(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8);
|
*(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8);
|
||||||
|
|
||||||
storage_file_read(file, &buf, 2);
|
|
||||||
*(state->x) = buf[0] | ((buf[1] & 0xF) << 8);
|
|
||||||
|
|
||||||
storage_file_read(file, &buf, 2);
|
storage_file_read(file, &buf, 2);
|
||||||
*(state->y) = buf[0] | ((buf[1] & 0xF) << 8);
|
*(state->x) = buf[0] | ((buf[1] & 0xF) << 8);
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 2);
|
||||||
*(state->a) = buf[0] & 0xF;
|
*(state->y) = buf[0] | ((buf[1] & 0xF) << 8);
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
*(state->b) = buf[0] & 0xF;
|
*(state->a) = buf[0] & 0xF;
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
|
*(state->b) = buf[0] & 0xF;
|
||||||
|
|
||||||
|
storage_file_read(file, &buf, 1);
|
||||||
*(state->np) = buf[0] & 0x1F;
|
*(state->np) = buf[0] & 0x1F;
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
*(state->sp) = buf[0];
|
*(state->sp) = buf[0];
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
*(state->flags) = buf[0] & 0xF;
|
*(state->flags) = buf[0] & 0xF;
|
||||||
|
|
||||||
storage_file_read(file, &buf, 4);
|
storage_file_read(file, &buf, 4);
|
||||||
*(state->tick_counter) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
*(state->tick_counter) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||||
|
|
||||||
storage_file_read(file, &buf, 4);
|
storage_file_read(file, &buf, 4);
|
||||||
*(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
*(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) |
|
||||||
|
(buf[3] << 24);
|
||||||
|
|
||||||
storage_file_read(file, &buf, 4);
|
storage_file_read(file, &buf, 4);
|
||||||
*(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
*(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) |
|
||||||
|
(buf[3] << 24);
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
*(state->prog_timer_enabled) = buf[0] & 0x1;
|
*(state->prog_timer_enabled) = buf[0] & 0x1;
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
*(state->prog_timer_data) = buf[0];
|
*(state->prog_timer_data) = buf[0];
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
*(state->prog_timer_rld) = buf[0];
|
*(state->prog_timer_rld) = buf[0];
|
||||||
|
|
||||||
storage_file_read(file, &buf, 4);
|
storage_file_read(file, &buf, 4);
|
||||||
*(state->call_depth) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
*(state->call_depth) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||||
|
|
||||||
FURI_LOG_D(TAG, "Restoring Interupts");
|
FURI_LOG_D(TAG, "Restoring Interupts");
|
||||||
for (uint32_t i = 0; i < INT_SLOT_NUM; i++) {
|
for(uint32_t i = 0; i < INT_SLOT_NUM; i++) {
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
state->interrupts[i].factor_flag_reg = buf[0] & 0xF;
|
state->interrupts[i].factor_flag_reg = buf[0] & 0xF;
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
state->interrupts[i].mask_reg = buf[0] & 0xF;
|
state->interrupts[i].mask_reg = buf[0] & 0xF;
|
||||||
|
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
state->interrupts[i].triggered = buf[0] & 0x1;
|
state->interrupts[i].triggered = buf[0] & 0x1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First 640 half bytes correspond to the RAM */
|
/* First 640 half bytes correspond to the RAM */
|
||||||
FURI_LOG_D(TAG, "Restoring RAM");
|
FURI_LOG_D(TAG, "Restoring RAM");
|
||||||
for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) {
|
for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) {
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
SET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR, buf[0] & 0xF);
|
SET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR, buf[0] & 0xF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I/Os are from 0xF00 to 0xF7F */
|
/* I/Os are from 0xF00 to 0xF7F */
|
||||||
FURI_LOG_D(TAG, "Restoring I/O");
|
FURI_LOG_D(TAG, "Restoring I/O");
|
||||||
for (uint32_t i = 0; i < MEM_IO_SIZE; i++) {
|
for(uint32_t i = 0; i < MEM_IO_SIZE; i++) {
|
||||||
storage_file_read(file, &buf, 1);
|
storage_file_read(file, &buf, 1);
|
||||||
SET_IO_MEMORY(state->memory, i + MEM_IO_ADDR, buf[0] & 0xF);
|
SET_IO_MEMORY(state->memory, i + MEM_IO_ADDR, buf[0] & 0xF);
|
||||||
}
|
}
|
||||||
FURI_LOG_D(TAG, "Refreshing Hardware");
|
FURI_LOG_D(TAG, "Refreshing Hardware");
|
||||||
tamalib_refresh_hw();
|
tamalib_refresh_hw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
storage_file_close(file);
|
storage_file_close(file);
|
||||||
storage_file_free(file);
|
storage_file_free(file);
|
||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void tama_p1_save_state() {
|
static void tama_p1_save_state() {
|
||||||
|
|
||||||
// Saving state
|
// Saving state
|
||||||
FURI_LOG_D(TAG, "Saving Gamestate");
|
FURI_LOG_D(TAG, "Saving Gamestate");
|
||||||
uint8_t buf[4];
|
uint8_t buf[4];
|
||||||
state_t *state;
|
state_t* state;
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
state = tamalib_get_state();
|
state = tamalib_get_state();
|
||||||
|
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
|
|
||||||
if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||||
buf[0] = (uint8_t) STATE_FILE_MAGIC[0];
|
buf[0] = (uint8_t)STATE_FILE_MAGIC[0];
|
||||||
buf[1] = (uint8_t) STATE_FILE_MAGIC[1];
|
buf[1] = (uint8_t)STATE_FILE_MAGIC[1];
|
||||||
buf[2] = (uint8_t) STATE_FILE_MAGIC[2];
|
buf[2] = (uint8_t)STATE_FILE_MAGIC[2];
|
||||||
buf[3] = (uint8_t) STATE_FILE_MAGIC[3];
|
buf[3] = (uint8_t)STATE_FILE_MAGIC[3];
|
||||||
offset += storage_file_write(file, &buf, sizeof(buf));
|
offset += storage_file_write(file, &buf, sizeof(buf));
|
||||||
|
|
||||||
buf[0] = STATE_FILE_VERSION & 0xFF;
|
buf[0] = STATE_FILE_VERSION & 0xFF;
|
||||||
offset += storage_file_write(file, &buf, 1);
|
offset += storage_file_write(file, &buf, 1);
|
||||||
|
|
||||||
buf[0] = *(state->pc) & 0xFF;
|
buf[0] = *(state->pc) & 0xFF;
|
||||||
buf[1] = (*(state->pc) >> 8) & 0x1F;
|
buf[1] = (*(state->pc) >> 8) & 0x1F;
|
||||||
offset += storage_file_write(file, &buf, 2);
|
offset += storage_file_write(file, &buf, 2);
|
||||||
@@ -303,7 +301,7 @@ static void tama_p1_save_state() {
|
|||||||
buf[3] = (*(state->call_depth) >> 24) & 0xFF;
|
buf[3] = (*(state->call_depth) >> 24) & 0xFF;
|
||||||
offset += storage_file_write(file, &buf, sizeof(buf));
|
offset += storage_file_write(file, &buf, sizeof(buf));
|
||||||
|
|
||||||
for (uint32_t i = 0; i < INT_SLOT_NUM; i++) {
|
for(uint32_t i = 0; i < INT_SLOT_NUM; i++) {
|
||||||
buf[0] = state->interrupts[i].factor_flag_reg & 0xF;
|
buf[0] = state->interrupts[i].factor_flag_reg & 0xF;
|
||||||
offset += storage_file_write(file, &buf, 1);
|
offset += storage_file_write(file, &buf, 1);
|
||||||
|
|
||||||
@@ -315,17 +313,17 @@ static void tama_p1_save_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* First 640 half bytes correspond to the RAM */
|
/* First 640 half bytes correspond to the RAM */
|
||||||
for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) {
|
for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) {
|
||||||
buf[0] = GET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR) & 0xF;
|
buf[0] = GET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR) & 0xF;
|
||||||
offset += storage_file_write(file, &buf, 1);
|
offset += storage_file_write(file, &buf, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I/Os are from 0xF00 to 0xF7F */
|
/* I/Os are from 0xF00 to 0xF7F */
|
||||||
for (uint32_t i = 0; i < MEM_IO_SIZE; i++) {
|
for(uint32_t i = 0; i < MEM_IO_SIZE; i++) {
|
||||||
buf[0] = GET_IO_MEMORY(state->memory, i + MEM_IO_ADDR) & 0xF;
|
buf[0] = GET_IO_MEMORY(state->memory, i + MEM_IO_ADDR) & 0xF;
|
||||||
offset += storage_file_write(file, &buf, 1);
|
offset += storage_file_write(file, &buf, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storage_file_close(file);
|
storage_file_close(file);
|
||||||
storage_file_free(file);
|
storage_file_free(file);
|
||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
@@ -333,7 +331,6 @@ static void tama_p1_save_state() {
|
|||||||
FURI_LOG_D(TAG, "Finished Writing %lu", offset);
|
FURI_LOG_D(TAG, "Finished Writing %lu", offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int32_t tama_p1_worker(void* context) {
|
static int32_t tama_p1_worker(void* context) {
|
||||||
bool running = true;
|
bool running = true;
|
||||||
FuriMutex* mutex = context;
|
FuriMutex* mutex = context;
|
||||||
@@ -357,8 +354,6 @@ static int32_t tama_p1_worker(void* context) {
|
|||||||
furi_mutex_release(mutex);
|
furi_mutex_release(mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void tama_p1_init(TamaApp* const ctx) {
|
static void tama_p1_init(TamaApp* const ctx) {
|
||||||
g_ctx = ctx;
|
g_ctx = ctx;
|
||||||
@@ -485,9 +480,9 @@ int32_t tama_p1_app(void* p) {
|
|||||||
tamalib_set_button(BTN_MIDDLE, tama_btn_state);
|
tamalib_set_button(BTN_MIDDLE, tama_btn_state);
|
||||||
} else if(event.input.key == InputKeyRight) {
|
} else if(event.input.key == InputKeyRight) {
|
||||||
tamalib_set_button(BTN_RIGHT, tama_btn_state);
|
tamalib_set_button(BTN_RIGHT, tama_btn_state);
|
||||||
} else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) {
|
} else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) {
|
||||||
// TODO: pause or fast-forward tamagotchi
|
// TODO: pause or fast-forward tamagotchi
|
||||||
tama_p1_save_state();
|
tama_p1_save_state();
|
||||||
} else if(event.input.key == InputKeyUp) { // mute tamagotchi
|
} else if(event.input.key == InputKeyUp) { // mute tamagotchi
|
||||||
tamalib_set_button(BTN_LEFT, tama_btn_state);
|
tamalib_set_button(BTN_LEFT, tama_btn_state);
|
||||||
tamalib_set_button(BTN_RIGHT, tama_btn_state);
|
tamalib_set_button(BTN_RIGHT, tama_btn_state);
|
||||||
@@ -500,7 +495,7 @@ int32_t tama_p1_app(void* p) {
|
|||||||
furi_timer_stop(timer);
|
furi_timer_stop(timer);
|
||||||
running = false;
|
running = false;
|
||||||
|
|
||||||
tama_p1_save_state();
|
tama_p1_save_state();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -370,8 +370,8 @@ static void bt_close_connection(Bt* bt) {
|
|||||||
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
|
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_restart(Bt *bt) {
|
static void bt_restart(Bt* bt) {
|
||||||
if (bt->profile == BtProfileHidKeyboard) {
|
if(bt->profile == BtProfileHidKeyboard) {
|
||||||
furi_hal_bt_change_app(FuriHalBtProfileHidKeyboard, bt_on_gap_event_callback, bt);
|
furi_hal_bt_change_app(FuriHalBtProfileHidKeyboard, bt_on_gap_event_callback, bt);
|
||||||
} else {
|
} else {
|
||||||
furi_hal_bt_change_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt);
|
furi_hal_bt_change_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt);
|
||||||
@@ -379,7 +379,7 @@ static void bt_restart(Bt *bt) {
|
|||||||
furi_hal_bt_start_advertising();
|
furi_hal_bt_start_advertising();
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...) {
|
void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...) {
|
||||||
furi_assert(bt);
|
furi_assert(bt);
|
||||||
furi_assert(fmt);
|
furi_assert(fmt);
|
||||||
|
|
||||||
@@ -388,7 +388,7 @@ void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...) {
|
|||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vsnprintf(name, sizeof(name), fmt, args);
|
vsnprintf(name, sizeof(name), fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
if (bt->profile == BtProfileHidKeyboard) {
|
if(bt->profile == BtProfileHidKeyboard) {
|
||||||
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, name);
|
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, name);
|
||||||
} else {
|
} else {
|
||||||
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileSerial, name);
|
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileSerial, name);
|
||||||
@@ -397,46 +397,45 @@ void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...) {
|
|||||||
bt_restart(bt);
|
bt_restart(bt);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *bt_get_profile_adv_name(Bt *bt) {
|
const char* bt_get_profile_adv_name(Bt* bt) {
|
||||||
furi_assert(bt);
|
furi_assert(bt);
|
||||||
if (bt->profile == BtProfileHidKeyboard) {
|
if(bt->profile == BtProfileHidKeyboard) {
|
||||||
return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard);
|
return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard);
|
||||||
} else {
|
} else {
|
||||||
return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileSerial);
|
return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileSerial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_set_profile_mac_address(Bt *bt, const uint8_t mac[6]) {
|
void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]) {
|
||||||
furi_assert(bt);
|
furi_assert(bt);
|
||||||
furi_assert(mac);
|
furi_assert(mac);
|
||||||
|
|
||||||
if (bt->profile == BtProfileHidKeyboard) {
|
if(bt->profile == BtProfileHidKeyboard) {
|
||||||
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac);
|
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac);
|
||||||
} else {
|
} else {
|
||||||
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileSerial, mac);
|
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileSerial, mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_restart(bt);
|
bt_restart(bt);
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t *bt_get_profile_mac_address(Bt *bt) {
|
const uint8_t* bt_get_profile_mac_address(Bt* bt) {
|
||||||
furi_assert(bt);
|
furi_assert(bt);
|
||||||
if (bt->profile == BtProfileHidKeyboard) {
|
if(bt->profile == BtProfileHidKeyboard) {
|
||||||
return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard);
|
return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard);
|
||||||
} else {
|
} else {
|
||||||
return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileSerial);
|
return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileSerial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bt_remote_rssi(Bt *bt, BtRssi *rssi) {
|
bool bt_remote_rssi(Bt* bt, BtRssi* rssi) {
|
||||||
furi_assert(bt);
|
furi_assert(bt);
|
||||||
UNUSED(rssi);
|
UNUSED(rssi);
|
||||||
|
|
||||||
uint8_t rssi_val;
|
uint8_t rssi_val;
|
||||||
uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val);
|
uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val);
|
||||||
|
|
||||||
if (since == 0)
|
if(since == 0) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
rssi->rssi = rssi_val;
|
rssi->rssi = rssi_val;
|
||||||
rssi->since = since;
|
rssi->since = since;
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ typedef struct {
|
|||||||
uint32_t since;
|
uint32_t since;
|
||||||
} BtRssi;
|
} BtRssi;
|
||||||
|
|
||||||
|
|
||||||
typedef void (*BtStatusChangedCallback)(BtStatus status, void* context);
|
typedef void (*BtStatusChangedCallback)(BtStatus status, void* context);
|
||||||
|
|
||||||
/** Change BLE Profile
|
/** Change BLE Profile
|
||||||
@@ -41,15 +40,13 @@ typedef void (*BtStatusChangedCallback)(BtStatus status, void* context);
|
|||||||
*/
|
*/
|
||||||
bool bt_set_profile(Bt* bt, BtProfile profile);
|
bool bt_set_profile(Bt* bt, BtProfile profile);
|
||||||
|
|
||||||
|
void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...);
|
||||||
|
const char* bt_get_profile_adv_name(Bt* bt);
|
||||||
|
|
||||||
void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...);
|
void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]);
|
||||||
const char *bt_get_profile_adv_name(Bt *bt);
|
const uint8_t* bt_get_profile_mac_address(Bt* bt);
|
||||||
|
|
||||||
void bt_set_profile_mac_address(Bt *bt, const uint8_t mac[6]);
|
|
||||||
const uint8_t *bt_get_profile_mac_address(Bt *bt);
|
|
||||||
|
|
||||||
bool bt_remote_rssi(Bt *bt, BtRssi *rssi);
|
|
||||||
|
|
||||||
|
bool bt_remote_rssi(Bt* bt, BtRssi* rssi);
|
||||||
|
|
||||||
/** Disconnect from Central
|
/** Disconnect from Central
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ void animation_manager_check_blocking_process(AnimationManager* animation_manage
|
|||||||
|
|
||||||
const StorageAnimationManifestInfo* manifest_info =
|
const StorageAnimationManifestInfo* manifest_info =
|
||||||
animation_storage_get_meta(animation_manager->current_animation);
|
animation_storage_get_meta(animation_manager->current_animation);
|
||||||
bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, XTREME_SETTINGS()->unlock_anims);
|
bool valid = animation_manager_is_valid_idle_animation(
|
||||||
|
manifest_info, &stats, XTREME_SETTINGS()->unlock_anims);
|
||||||
|
|
||||||
if(!valid) {
|
if(!valid) {
|
||||||
animation_manager_start_new_idle(animation_manager);
|
animation_manager_start_new_idle(animation_manager);
|
||||||
@@ -201,8 +202,10 @@ static void animation_manager_start_new_idle(AnimationManager* animation_manager
|
|||||||
animation_storage_get_bubble_animation(animation_manager->current_animation);
|
animation_storage_get_bubble_animation(animation_manager->current_animation);
|
||||||
animation_manager->state = AnimationManagerStateIdle;
|
animation_manager->state = AnimationManagerStateIdle;
|
||||||
XtremeSettings* xtreme_settings = XTREME_SETTINGS();
|
XtremeSettings* xtreme_settings = XTREME_SETTINGS();
|
||||||
int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : (xtreme_settings->cycle_anims);
|
int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) :
|
||||||
furi_timer_start(animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0);
|
(xtreme_settings->cycle_anims);
|
||||||
|
furi_timer_start(
|
||||||
|
animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool animation_manager_check_blocking(AnimationManager* animation_manager) {
|
static bool animation_manager_check_blocking(AnimationManager* animation_manager) {
|
||||||
@@ -355,7 +358,7 @@ static bool animation_manager_is_valid_idle_animation(
|
|||||||
|
|
||||||
result = (sd_status == FSE_NOT_READY);
|
result = (sd_status == FSE_NOT_READY);
|
||||||
}
|
}
|
||||||
if (!unlock) {
|
if(!unlock) {
|
||||||
if((stats->butthurt < info->min_butthurt) || (stats->butthurt > info->max_butthurt)) {
|
if((stats->butthurt < info->min_butthurt) || (stats->butthurt > info->max_butthurt)) {
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
@@ -370,8 +373,9 @@ static bool animation_manager_is_valid_idle_animation(
|
|||||||
static StorageAnimation*
|
static StorageAnimation*
|
||||||
animation_manager_select_idle_animation(AnimationManager* animation_manager) {
|
animation_manager_select_idle_animation(AnimationManager* animation_manager) {
|
||||||
const char* old_animation_name = NULL;
|
const char* old_animation_name = NULL;
|
||||||
if (animation_manager->current_animation) {
|
if(animation_manager->current_animation) {
|
||||||
old_animation_name = animation_storage_get_meta(animation_manager->current_animation)->name;
|
old_animation_name =
|
||||||
|
animation_storage_get_meta(animation_manager->current_animation)->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageAnimationList_t animation_list;
|
StorageAnimationList_t animation_list;
|
||||||
@@ -391,8 +395,8 @@ static StorageAnimation*
|
|||||||
animation_storage_get_meta(storage_animation);
|
animation_storage_get_meta(storage_animation);
|
||||||
bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, unlock);
|
bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, unlock);
|
||||||
|
|
||||||
if (old_animation_name != NULL) {
|
if(old_animation_name != NULL) {
|
||||||
if (strcmp(manifest_info->name, old_animation_name) == 0) {
|
if(strcmp(manifest_info->name, old_animation_name) == 0) {
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -512,7 +516,8 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m
|
|||||||
furi_record_close(RECORD_DOLPHIN);
|
furi_record_close(RECORD_DOLPHIN);
|
||||||
const StorageAnimationManifestInfo* manifest_info =
|
const StorageAnimationManifestInfo* manifest_info =
|
||||||
animation_storage_get_meta(restore_animation);
|
animation_storage_get_meta(restore_animation);
|
||||||
bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, XTREME_SETTINGS()->unlock_anims);
|
bool valid = animation_manager_is_valid_idle_animation(
|
||||||
|
manifest_info, &stats, XTREME_SETTINGS()->unlock_anims);
|
||||||
if(valid) {
|
if(valid) {
|
||||||
animation_manager_replace_current_animation(
|
animation_manager_replace_current_animation(
|
||||||
animation_manager, restore_animation);
|
animation_manager, restore_animation);
|
||||||
@@ -523,12 +528,16 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m
|
|||||||
animation_manager->idle_animation_timer,
|
animation_manager->idle_animation_timer,
|
||||||
animation_manager->freezed_animation_time_left);
|
animation_manager->freezed_animation_time_left);
|
||||||
} else {
|
} else {
|
||||||
const BubbleAnimation* bubble_animation = animation_storage_get_bubble_animation(
|
const BubbleAnimation* bubble_animation =
|
||||||
animation_manager->current_animation);
|
animation_storage_get_bubble_animation(
|
||||||
|
animation_manager->current_animation);
|
||||||
XtremeSettings* xtreme_settings = XTREME_SETTINGS();
|
XtremeSettings* xtreme_settings = XTREME_SETTINGS();
|
||||||
int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : (xtreme_settings->cycle_anims);
|
int32_t duration = (xtreme_settings->cycle_anims == 0) ?
|
||||||
|
(bubble_animation->duration) :
|
||||||
|
(xtreme_settings->cycle_anims);
|
||||||
furi_timer_start(
|
furi_timer_start(
|
||||||
animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0);
|
animation_manager->idle_animation_timer,
|
||||||
|
(duration > 0) ? (duration * 1000) : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -35,18 +35,18 @@ void animation_handler_select_manifest() {
|
|||||||
FuriString* anim_dir = furi_string_alloc();
|
FuriString* anim_dir = furi_string_alloc();
|
||||||
FuriString* manifest = furi_string_alloc();
|
FuriString* manifest = furi_string_alloc();
|
||||||
bool use_asset_pack = xtreme_settings->asset_pack[0] != '\0';
|
bool use_asset_pack = xtreme_settings->asset_pack[0] != '\0';
|
||||||
if (use_asset_pack) {
|
if(use_asset_pack) {
|
||||||
furi_string_printf(anim_dir, "%s/%s/Anims", PACKS_DIR, xtreme_settings->asset_pack);
|
furi_string_printf(anim_dir, "%s/%s/Anims", PACKS_DIR, xtreme_settings->asset_pack);
|
||||||
furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir));
|
furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir));
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
if (storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) {
|
if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) {
|
||||||
FURI_LOG_I(TAG, "Custom Manifest selected");
|
FURI_LOG_I(TAG, "Custom Manifest selected");
|
||||||
} else {
|
} else {
|
||||||
use_asset_pack = false;
|
use_asset_pack = false;
|
||||||
}
|
}
|
||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
}
|
}
|
||||||
if (!use_asset_pack) {
|
if(!use_asset_pack) {
|
||||||
furi_string_set(anim_dir, BASE_ANIMATION_DIR);
|
furi_string_set(anim_dir, BASE_ANIMATION_DIR);
|
||||||
if(xtreme_settings->nsfw_mode) {
|
if(xtreme_settings->nsfw_mode) {
|
||||||
furi_string_cat_str(anim_dir, "/nsfw");
|
furi_string_cat_str(anim_dir, "/nsfw");
|
||||||
@@ -58,7 +58,8 @@ void animation_handler_select_manifest() {
|
|||||||
furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir));
|
furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir));
|
||||||
}
|
}
|
||||||
strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR));
|
strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR));
|
||||||
strlcpy(ANIMATION_MANIFEST_FILE, furi_string_get_cstr(manifest), sizeof(ANIMATION_MANIFEST_FILE));
|
strlcpy(
|
||||||
|
ANIMATION_MANIFEST_FILE, furi_string_get_cstr(manifest), sizeof(ANIMATION_MANIFEST_FILE));
|
||||||
furi_string_free(manifest);
|
furi_string_free(manifest);
|
||||||
furi_string_free(anim_dir);
|
furi_string_free(anim_dir);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) {
|
|||||||
str = "Set PIN + Off";
|
str = "Set PIN + Off";
|
||||||
}
|
}
|
||||||
} else if(i == DesktopLockMenuIndexXtremeSettings) {
|
} else if(i == DesktopLockMenuIndexXtremeSettings) {
|
||||||
str = "Xtreme Settings";
|
str = "Xtreme Settings";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(str) //-V547
|
if(str) //-V547
|
||||||
|
|||||||
@@ -15,9 +15,10 @@
|
|||||||
#define DOLPHIN_STATE_HEADER_MAGIC 0xD0
|
#define DOLPHIN_STATE_HEADER_MAGIC 0xD0
|
||||||
#define DOLPHIN_STATE_HEADER_VERSION 0x01
|
#define DOLPHIN_STATE_HEADER_VERSION 0x01
|
||||||
|
|
||||||
const int DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT] = {100, 200, 300, 450, 600, 750, 950, 1150, 1350, 1600,
|
const int DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT] = {100, 200, 300, 450, 600, 750, 950, 1150,
|
||||||
1850, 2100, 2400, 2700, 3000, 3350, 3700, 4050, 4450, 4850,
|
1350, 1600, 1850, 2100, 2400, 2700, 3000, 3350,
|
||||||
5250, 5700, 6150, 6600, 7100, 7600, 8100, 8650, 9200};
|
3700, 4050, 4450, 4850, 5250, 5700, 6150, 6600,
|
||||||
|
7100, 7600, 8100, 8650, 9200};
|
||||||
|
|
||||||
#define BUTTHURT_MAX 14
|
#define BUTTHURT_MAX 14
|
||||||
#define BUTTHURT_MIN 0
|
#define BUTTHURT_MIN 0
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
|
|||||||
snprintf(batteryPercentile, sizeof(batteryPercentile), "%d", power->info.charge);
|
snprintf(batteryPercentile, sizeof(batteryPercentile), "%d", power->info.charge);
|
||||||
|
|
||||||
if((battery_style == BatteryStylePercent) &&
|
if((battery_style == BatteryStylePercent) &&
|
||||||
(power->state !=
|
(power->state !=
|
||||||
PowerStateCharging)) { //if display battery percentage, black background white text
|
PowerStateCharging)) { //if display battery percentage, black background white text
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
canvas_set_font(canvas, FontBatteryPercent);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
@@ -36,7 +36,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
|
|||||||
} else if(
|
} else if(
|
||||||
(battery_style == BatteryStyleInvertedPercent) &&
|
(battery_style == BatteryStyleInvertedPercent) &&
|
||||||
(power->state !=
|
(power->state !=
|
||||||
PowerStateCharging)) { //if display inverted percentage, white background black text
|
PowerStateCharging)) { //if display inverted percentage, white background black text
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
canvas_set_font(canvas, FontBatteryPercent);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
canvas_draw_str_aligned(canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile);
|
canvas_draw_str_aligned(canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile);
|
||||||
@@ -74,7 +74,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
|
|||||||
(battery_style == BatteryStyleBarPercent) &&
|
(battery_style == BatteryStyleBarPercent) &&
|
||||||
(power->state != PowerStateCharging) && // Default bar display with percentage
|
(power->state != PowerStateCharging) && // Default bar display with percentage
|
||||||
(power->info.voltage_battery_charging >=
|
(power->info.voltage_battery_charging >=
|
||||||
4.2)) { // not looking nice with low voltage indicator
|
4.2)) { // not looking nice with low voltage indicator
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
canvas_set_font(canvas, FontBatteryPercent);
|
||||||
|
|
||||||
// align charge dispaly value with digits to draw
|
// align charge dispaly value with digits to draw
|
||||||
@@ -145,8 +145,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
|
|||||||
if(power->state == PowerStateCharging) {
|
if(power->state == PowerStateCharging) {
|
||||||
canvas_set_bitmap_mode(canvas, 1);
|
canvas_set_bitmap_mode(canvas, 1);
|
||||||
// TODO: replace -1 magic for uint8_t with re-framing
|
// TODO: replace -1 magic for uint8_t with re-framing
|
||||||
if(battery_style == BatteryStylePercent ||
|
if(battery_style == BatteryStylePercent || battery_style == BatteryStyleBarPercent) {
|
||||||
battery_style == BatteryStyleBarPercent) {
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
canvas_draw_box(canvas, 1, 1, 22, 6);
|
canvas_draw_box(canvas, 1, 1, 22, 6);
|
||||||
canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10);
|
canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10);
|
||||||
|
|||||||
@@ -222,9 +222,9 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) {
|
|||||||
snprintf(header, sizeof(header), "Charged!");
|
snprintf(header, sizeof(header), "Charged!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(value, "")) {
|
if(!strcmp(value, "")) {
|
||||||
canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, header);
|
canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, header);
|
||||||
} else if (!strcmp(header, "")) {
|
} else if(!strcmp(header, "")) {
|
||||||
canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, value);
|
canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, value);
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header);
|
canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header);
|
||||||
@@ -298,7 +298,6 @@ const AboutDialogScreen about_screens[] = {
|
|||||||
|
|
||||||
const int about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
|
const int about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
|
||||||
|
|
||||||
|
|
||||||
int32_t about_settings_app(void* p) {
|
int32_t about_settings_app(void* p) {
|
||||||
bool battery_info = false;
|
bool battery_info = false;
|
||||||
if(p && strlen(p) && !strcmp(p, "batt")) {
|
if(p && strlen(p) && !strcmp(p, "batt")) {
|
||||||
@@ -324,24 +323,19 @@ int32_t about_settings_app(void* p) {
|
|||||||
DialogMessageButton screen_result;
|
DialogMessageButton screen_result;
|
||||||
|
|
||||||
// draw empty screen to prevent menu flickering
|
// draw empty screen to prevent menu flickering
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(view_dispatcher, battery_info_index, battery_view);
|
||||||
view_dispatcher, battery_info_index, battery_view);
|
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
view_dispatcher, empty_screen_index, empty_screen_get_view(empty_screen));
|
view_dispatcher, empty_screen_index, empty_screen_get_view(empty_screen));
|
||||||
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||||
|
|
||||||
screen_index = -1 + !battery_info;
|
screen_index = -1 + !battery_info;
|
||||||
while(screen_index > -2) {
|
while(screen_index > -2) {
|
||||||
|
if(screen_index == -1) {
|
||||||
if (screen_index == -1) {
|
if(!battery_info) {
|
||||||
if (!battery_info) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
with_view_model(
|
with_view_model(
|
||||||
battery_view,
|
battery_view, PowerInfo * model, { power_get_info(power, model); }, true);
|
||||||
PowerInfo * model,
|
|
||||||
{ power_get_info(power, model); },
|
|
||||||
true);
|
|
||||||
view_dispatcher_switch_to_view(view_dispatcher, battery_info_index);
|
view_dispatcher_switch_to_view(view_dispatcher, battery_info_index);
|
||||||
furi_semaphore_acquire(semaphore, 2000);
|
furi_semaphore_acquire(semaphore, 2000);
|
||||||
} else {
|
} else {
|
||||||
@@ -360,7 +354,6 @@ int32_t about_settings_app(void* p) {
|
|||||||
screen_index = -2;
|
screen_index = -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog_message_free(message);
|
dialog_message_free(message);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ static void render_callback(Canvas* canvas, void* _ctx) {
|
|||||||
uint32_t xp_need = dolphin_state_xp_to_levelup(stats->icounter);
|
uint32_t xp_need = dolphin_state_xp_to_levelup(stats->icounter);
|
||||||
uint32_t xp_above_last_levelup = dolphin_state_xp_above_last_levelup(stats->icounter);
|
uint32_t xp_above_last_levelup = dolphin_state_xp_above_last_levelup(stats->icounter);
|
||||||
uint32_t xp_levelup = 0;
|
uint32_t xp_levelup = 0;
|
||||||
if (ctx->progress_total) {
|
if(ctx->progress_total) {
|
||||||
xp_levelup = xp_need + stats->icounter;
|
xp_levelup = xp_need + stats->icounter;
|
||||||
} else {
|
} else {
|
||||||
xp_levelup = xp_need + xp_above_last_levelup;
|
xp_levelup = xp_need + xp_above_last_levelup;
|
||||||
|
|||||||
@@ -15,8 +15,12 @@ static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item
|
|||||||
static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) {
|
static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) {
|
||||||
XtremeSettingsApp* app = variable_item_get_context(item);
|
XtremeSettingsApp* app = variable_item_get_context(item);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
variable_item_set_current_value_text(item, index == 0 ? "OFF" : *asset_packs_get(app->asset_packs, index - 1));
|
variable_item_set_current_value_text(
|
||||||
strlcpy(XTREME_SETTINGS()->asset_pack, index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), MAX_PACK_NAME_LEN);
|
item, index == 0 ? "OFF" : *asset_packs_get(app->asset_packs, index - 1));
|
||||||
|
strlcpy(
|
||||||
|
XTREME_SETTINGS()->asset_pack,
|
||||||
|
index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1),
|
||||||
|
MAX_PACK_NAME_LEN);
|
||||||
app->settings_changed = true;
|
app->settings_changed = true;
|
||||||
app->assets_changed = true;
|
app->assets_changed = true;
|
||||||
}
|
}
|
||||||
@@ -33,8 +37,20 @@ static void xtreme_settings_scene_start_anim_speed_changed(VariableItem* item) {
|
|||||||
app->settings_changed = true;
|
app->settings_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* const cycle_anims_names[] =
|
const char* const cycle_anims_names[] = {
|
||||||
{"OFF", "Meta.txt", "30 S", "1 M", "5 M", "10 M", "15 M", "30 M", "1 H", "2 H", "6 H", "12 H", "24 H"};
|
"OFF",
|
||||||
|
"Meta.txt",
|
||||||
|
"30 S",
|
||||||
|
"1 M",
|
||||||
|
"5 M",
|
||||||
|
"10 M",
|
||||||
|
"15 M",
|
||||||
|
"30 M",
|
||||||
|
"1 H",
|
||||||
|
"2 H",
|
||||||
|
"6 H",
|
||||||
|
"12 H",
|
||||||
|
"24 H"};
|
||||||
const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] =
|
const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] =
|
||||||
{-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400};
|
{-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400};
|
||||||
static void xtreme_settings_scene_start_cycle_anims_changed(VariableItem* item) {
|
static void xtreme_settings_scene_start_cycle_anims_changed(VariableItem* item) {
|
||||||
@@ -62,8 +78,7 @@ const int32_t battery_style_values[COUNT_OF(battery_style_names)] = {
|
|||||||
BatteryStyleInvertedPercent,
|
BatteryStyleInvertedPercent,
|
||||||
BatteryStyleRetro3,
|
BatteryStyleRetro3,
|
||||||
BatteryStyleRetro5,
|
BatteryStyleRetro5,
|
||||||
BatteryStyleBarPercent
|
BatteryStyleBarPercent};
|
||||||
};
|
|
||||||
static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item) {
|
static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item) {
|
||||||
XtremeSettingsApp* app = variable_item_get_context(item);
|
XtremeSettingsApp* app = variable_item_get_context(item);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
@@ -112,7 +127,8 @@ void xtreme_settings_scene_start_on_enter(void* context) {
|
|||||||
app->subghz_extend = false;
|
app->subghz_extend = false;
|
||||||
app->subghz_bypass = false;
|
app->subghz_bypass = false;
|
||||||
if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) {
|
if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) {
|
||||||
flipper_format_read_bool(subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1);
|
flipper_format_read_bool(
|
||||||
|
subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1);
|
||||||
flipper_format_read_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1);
|
flipper_format_read_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1);
|
||||||
}
|
}
|
||||||
flipper_format_free(subghz_range);
|
flipper_format_free(subghz_range);
|
||||||
@@ -123,14 +139,15 @@ void xtreme_settings_scene_start_on_enter(void* context) {
|
|||||||
FileInfo info;
|
FileInfo info;
|
||||||
char* name = malloc(MAX_PACK_NAME_LEN);
|
char* name = malloc(MAX_PACK_NAME_LEN);
|
||||||
do {
|
do {
|
||||||
if (!storage_dir_open(folder, PACKS_DIR)) break;
|
if(!storage_dir_open(folder, PACKS_DIR)) break;
|
||||||
while(true) {
|
while(true) {
|
||||||
if (!storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) break;
|
if(!storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) break;
|
||||||
if(info.flags & FSF_DIRECTORY) {
|
if(info.flags & FSF_DIRECTORY) {
|
||||||
char* copy = malloc(MAX_PACK_NAME_LEN);
|
char* copy = malloc(MAX_PACK_NAME_LEN);
|
||||||
strlcpy(copy, name, MAX_PACK_NAME_LEN);
|
strlcpy(copy, name, MAX_PACK_NAME_LEN);
|
||||||
asset_packs_push_back(app->asset_packs, copy);
|
asset_packs_push_back(app->asset_packs, copy);
|
||||||
if (strcmp(name, xtreme_settings->asset_pack) == 0) current_pack = asset_packs_size(app->asset_packs);
|
if(strcmp(name, xtreme_settings->asset_pack) == 0)
|
||||||
|
current_pack = asset_packs_size(app->asset_packs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while(false);
|
} while(false);
|
||||||
@@ -139,11 +156,7 @@ void xtreme_settings_scene_start_on_enter(void* context) {
|
|||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
var_item_list,
|
var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app);
|
||||||
"Base Graphics",
|
|
||||||
2,
|
|
||||||
xtreme_settings_scene_start_base_graphics_changed,
|
|
||||||
app);
|
|
||||||
variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode);
|
variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode);
|
||||||
variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW");
|
variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW");
|
||||||
|
|
||||||
@@ -154,7 +167,8 @@ void xtreme_settings_scene_start_on_enter(void* context) {
|
|||||||
xtreme_settings_scene_start_asset_pack_changed,
|
xtreme_settings_scene_start_asset_pack_changed,
|
||||||
app);
|
app);
|
||||||
variable_item_set_current_value_index(item, current_pack);
|
variable_item_set_current_value_index(item, current_pack);
|
||||||
variable_item_set_current_value_text(item, current_pack == 0 ? "OFF" : *asset_packs_get(app->asset_packs, current_pack - 1));
|
variable_item_set_current_value_text(
|
||||||
|
item, current_pack == 0 ? "OFF" : *asset_packs_get(app->asset_packs, current_pack - 1));
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
var_item_list,
|
var_item_list,
|
||||||
@@ -179,11 +193,7 @@ void xtreme_settings_scene_start_on_enter(void* context) {
|
|||||||
variable_item_set_current_value_text(item, cycle_anims_names[value_index]);
|
variable_item_set_current_value_text(item, cycle_anims_names[value_index]);
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
var_item_list,
|
var_item_list, "Unlock Anims", 2, xtreme_settings_scene_start_unlock_anims_changed, app);
|
||||||
"Unlock Anims",
|
|
||||||
2,
|
|
||||||
xtreme_settings_scene_start_unlock_anims_changed,
|
|
||||||
app);
|
|
||||||
variable_item_set_current_value_index(item, xtreme_settings->unlock_anims);
|
variable_item_set_current_value_index(item, xtreme_settings->unlock_anims);
|
||||||
variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF");
|
variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF");
|
||||||
|
|
||||||
@@ -210,30 +220,18 @@ void xtreme_settings_scene_start_on_enter(void* context) {
|
|||||||
variable_item_set_current_value_text(item, level_str);
|
variable_item_set_current_value_text(item, level_str);
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
var_item_list,
|
var_item_list, "SubGHz Extend", 2, xtreme_settings_scene_start_subghz_extend_changed, app);
|
||||||
"SubGHz Extend",
|
|
||||||
2,
|
|
||||||
xtreme_settings_scene_start_subghz_extend_changed,
|
|
||||||
app);
|
|
||||||
variable_item_set_current_value_index(item, app->subghz_extend);
|
variable_item_set_current_value_index(item, app->subghz_extend);
|
||||||
variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF");
|
variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF");
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
var_item_list,
|
var_item_list, "SubGHz Bypass", 2, xtreme_settings_scene_start_subghz_bypass_changed, app);
|
||||||
"SubGHz Bypass",
|
|
||||||
2,
|
|
||||||
xtreme_settings_scene_start_subghz_bypass_changed,
|
|
||||||
app);
|
|
||||||
variable_item_set_current_value_index(item, app->subghz_bypass);
|
variable_item_set_current_value_index(item, app->subghz_bypass);
|
||||||
variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF");
|
variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF");
|
||||||
|
|
||||||
FuriString* version_tag = furi_string_alloc_printf("%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL));
|
FuriString* version_tag = furi_string_alloc_printf(
|
||||||
item = variable_item_list_add(
|
"%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL));
|
||||||
var_item_list,
|
item = variable_item_list_add(var_item_list, furi_string_get_cstr(version_tag), 0, NULL, app);
|
||||||
furi_string_get_cstr(version_tag),
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
app);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, XtremeSettingsAppViewVarItemList);
|
view_dispatcher_switch_to_view(app->view_dispatcher, XtremeSettingsAppViewVarItemList);
|
||||||
}
|
}
|
||||||
@@ -248,7 +246,7 @@ bool xtreme_settings_scene_start_on_event(void* context, SceneManagerEvent event
|
|||||||
void xtreme_settings_scene_start_on_exit(void* context) {
|
void xtreme_settings_scene_start_on_exit(void* context) {
|
||||||
XtremeSettingsApp* app = context;
|
XtremeSettingsApp* app = context;
|
||||||
asset_packs_it_t it;
|
asset_packs_it_t it;
|
||||||
for (asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) {
|
for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) {
|
||||||
free(*asset_packs_cref(it));
|
free(*asset_packs_cref(it));
|
||||||
}
|
}
|
||||||
asset_packs_clear(app->asset_packs);
|
asset_packs_clear(app->asset_packs);
|
||||||
|
|||||||
@@ -5,94 +5,158 @@
|
|||||||
XtremeAssets* xtreme_assets = NULL;
|
XtremeAssets* xtreme_assets = NULL;
|
||||||
|
|
||||||
XtremeAssets* XTREME_ASSETS() {
|
XtremeAssets* XTREME_ASSETS() {
|
||||||
if (xtreme_assets == NULL) {
|
if(xtreme_assets == NULL) {
|
||||||
XTREME_ASSETS_LOAD();
|
XTREME_ASSETS_LOAD();
|
||||||
}
|
}
|
||||||
return xtreme_assets;
|
return xtreme_assets;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XTREME_ASSETS_LOAD() {
|
void XTREME_ASSETS_LOAD() {
|
||||||
if (xtreme_assets != NULL) return;
|
if(xtreme_assets != NULL) return;
|
||||||
|
|
||||||
xtreme_assets = malloc(sizeof(XtremeAssets));
|
xtreme_assets = malloc(sizeof(XtremeAssets));
|
||||||
XtremeSettings* xtreme_settings = XTREME_SETTINGS();
|
XtremeSettings* xtreme_settings = XTREME_SETTINGS();
|
||||||
|
|
||||||
if (xtreme_settings->nsfw_mode) {
|
if(xtreme_settings->nsfw_mode) {
|
||||||
xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64;
|
xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64;
|
||||||
xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48;
|
xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48;
|
||||||
xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62;
|
xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62;
|
||||||
xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59;
|
xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59;
|
||||||
xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59;
|
xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59;
|
||||||
xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52;
|
xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52;
|
||||||
xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63;
|
xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63;
|
||||||
xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61;
|
xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61;
|
||||||
xtreme_assets->I_passport_bad_46x49 = &I_flipper;
|
xtreme_assets->I_passport_bad_46x49 = &I_flipper;
|
||||||
xtreme_assets->I_passport_DB = &I_passport_DB;
|
xtreme_assets->I_passport_DB = &I_passport_DB;
|
||||||
xtreme_assets->I_passport_happy_46x49 = &I_flipper;
|
xtreme_assets->I_passport_happy_46x49 = &I_flipper;
|
||||||
xtreme_assets->I_passport_okay_46x49 = &I_flipper;
|
xtreme_assets->I_passport_okay_46x49 = &I_flipper;
|
||||||
xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61;
|
xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61;
|
||||||
xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61;
|
xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61;
|
||||||
xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57;
|
xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57;
|
||||||
xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52;
|
xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52;
|
||||||
xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52;
|
xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52;
|
||||||
xtreme_assets->I_Auth_62x31 = &I_Auth_62x31;
|
xtreme_assets->I_Auth_62x31 = &I_Auth_62x31;
|
||||||
xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31;
|
xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31;
|
||||||
xtreme_assets->I_Connected_62x31 = &I_Connected_62x31;
|
xtreme_assets->I_Connected_62x31 = &I_Connected_62x31;
|
||||||
xtreme_assets->I_Error_62x31 = &I_Error_62x31;
|
xtreme_assets->I_Error_62x31 = &I_Error_62x31;
|
||||||
} else {
|
} else {
|
||||||
xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw;
|
xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw;
|
||||||
xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw;
|
xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw;
|
||||||
xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw;
|
xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw;
|
||||||
xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw;
|
xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw;
|
||||||
xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw;
|
xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw;
|
||||||
xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52_sfw;
|
xtreme_assets->I_iButtonDolphinVerySuccess_108x52 =
|
||||||
xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw;
|
&I_iButtonDolphinVerySuccess_108x52_sfw;
|
||||||
xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw;
|
xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw;
|
||||||
xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw;
|
xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw;
|
||||||
xtreme_assets->I_passport_DB = &I_passport_DB_sfw;
|
xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw;
|
||||||
xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw;
|
xtreme_assets->I_passport_DB = &I_passport_DB_sfw;
|
||||||
xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw;
|
xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw;
|
||||||
xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw;
|
xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw;
|
||||||
xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw;
|
xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw;
|
||||||
xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw;
|
xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw;
|
||||||
xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw;
|
xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw;
|
||||||
xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw;
|
xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw;
|
||||||
xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw;
|
xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw;
|
||||||
xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw;
|
xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw;
|
||||||
xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw;
|
xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw;
|
||||||
xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw;
|
xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw;
|
||||||
|
xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xtreme_settings->asset_pack[0] == '\0') return;
|
if(xtreme_settings->asset_pack[0] == '\0') return;
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
FuriString* path = furi_string_alloc();
|
FuriString* path = furi_string_alloc();
|
||||||
const char* pack = xtreme_settings->asset_pack;
|
const char* pack = xtreme_settings->asset_pack;
|
||||||
furi_string_printf(path, PACKS_DIR "/%s", pack);
|
furi_string_printf(path, PACKS_DIR "/%s", pack);
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
if (storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) {
|
if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK &&
|
||||||
|
info.flags & FSF_DIRECTORY) {
|
||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
|
|
||||||
swap_bmx_icon(&xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file);
|
swap_bmx_icon(
|
||||||
swap_bmx_icon(&xtreme_assets->I_DolphinCommon_56x48, pack, "Dolphin/DolphinCommon_56x48.bmx", path, file);
|
&xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file);
|
||||||
swap_bmx_icon(&xtreme_assets->I_DolphinMafia_115x62, pack, "iButton/DolphinMafia_115x62.bmx", path, file);
|
swap_bmx_icon(
|
||||||
swap_bmx_icon(&xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file);
|
&xtreme_assets->I_DolphinCommon_56x48,
|
||||||
swap_bmx_icon(&xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file);
|
pack,
|
||||||
swap_bmx_icon(&xtreme_assets->I_iButtonDolphinVerySuccess_108x52, pack, "iButton/iButtonDolphinVerySuccess_108x52.bmx", path, file);
|
"Dolphin/DolphinCommon_56x48.bmx",
|
||||||
swap_bmx_icon(&xtreme_assets->I_DolphinReadingSuccess_59x63, pack, "Infrared/DolphinReadingSuccess_59x63.bmx", path, file);
|
path,
|
||||||
swap_bmx_icon(&xtreme_assets->I_NFC_dolphin_emulation_47x61, pack, "NFC/NFC_dolphin_emulation_47x61.bmx", path, file);
|
file);
|
||||||
swap_bmx_icon(&xtreme_assets->I_passport_bad_46x49, pack, "Passport/passport_bad_46x49.bmx", path, file);
|
swap_bmx_icon(
|
||||||
swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file);
|
&xtreme_assets->I_DolphinMafia_115x62,
|
||||||
swap_bmx_icon(&xtreme_assets->I_passport_happy_46x49, pack, "Passport/passport_happy_46x49.bmx", path, file);
|
pack,
|
||||||
swap_bmx_icon(&xtreme_assets->I_passport_okay_46x49, pack, "Passport/passport_okay_46x49.bmx", path, file);
|
"iButton/DolphinMafia_115x62.bmx",
|
||||||
swap_bmx_icon(&xtreme_assets->I_RFIDDolphinReceive_97x61, pack, "RFID/RFIDDolphinReceive_97x61.bmx", path, file);
|
path,
|
||||||
swap_bmx_icon(&xtreme_assets->I_RFIDDolphinSend_97x61, pack, "RFID/RFIDDolphinSend_97x61.bmx", path, file);
|
file);
|
||||||
swap_bmx_icon(&xtreme_assets->I_RFIDDolphinSuccess_108x57, pack, "RFID/RFIDDolphinSuccess_108x57.bmx", path, file);
|
swap_bmx_icon(
|
||||||
swap_bmx_icon(&xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file);
|
&xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file);
|
||||||
swap_bmx_icon(&xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file);
|
swap_bmx_icon(
|
||||||
swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file);
|
&xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file);
|
||||||
swap_bmx_icon(&xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file);
|
swap_bmx_icon(
|
||||||
swap_bmx_icon(&xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file);
|
&xtreme_assets->I_iButtonDolphinVerySuccess_108x52,
|
||||||
swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file);
|
pack,
|
||||||
|
"iButton/iButtonDolphinVerySuccess_108x52.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_DolphinReadingSuccess_59x63,
|
||||||
|
pack,
|
||||||
|
"Infrared/DolphinReadingSuccess_59x63.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_NFC_dolphin_emulation_47x61,
|
||||||
|
pack,
|
||||||
|
"NFC/NFC_dolphin_emulation_47x61.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_passport_bad_46x49,
|
||||||
|
pack,
|
||||||
|
"Passport/passport_bad_46x49.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_passport_happy_46x49,
|
||||||
|
pack,
|
||||||
|
"Passport/passport_happy_46x49.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_passport_okay_46x49,
|
||||||
|
pack,
|
||||||
|
"Passport/passport_okay_46x49.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_RFIDDolphinReceive_97x61,
|
||||||
|
pack,
|
||||||
|
"RFID/RFIDDolphinReceive_97x61.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_RFIDDolphinSend_97x61,
|
||||||
|
pack,
|
||||||
|
"RFID/RFIDDolphinSend_97x61.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_RFIDDolphinSuccess_108x57,
|
||||||
|
pack,
|
||||||
|
"RFID/RFIDDolphinSuccess_108x57.bmx",
|
||||||
|
path,
|
||||||
|
file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file);
|
||||||
|
swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file);
|
||||||
|
swap_bmx_icon(
|
||||||
|
&xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file);
|
||||||
|
swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file);
|
||||||
|
|
||||||
storage_file_free(file);
|
storage_file_free(file);
|
||||||
}
|
}
|
||||||
@@ -100,9 +164,14 @@ void XTREME_ASSETS_LOAD() {
|
|||||||
furi_string_free(path);
|
furi_string_free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap_bmx_icon(const Icon** replace, const char* pack, const char* name, FuriString* path, File* file) {
|
void swap_bmx_icon(
|
||||||
|
const Icon** replace,
|
||||||
|
const char* pack,
|
||||||
|
const char* name,
|
||||||
|
FuriString* path,
|
||||||
|
File* file) {
|
||||||
furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name);
|
furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name);
|
||||||
if (storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
uint64_t size = storage_file_size(file) - 8;
|
uint64_t size = storage_file_size(file) - 8;
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
storage_file_read(file, &width, 4);
|
storage_file_read(file, &width, 4);
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ XtremeAssets* XTREME_ASSETS();
|
|||||||
|
|
||||||
void XTREME_ASSETS_LOAD();
|
void XTREME_ASSETS_LOAD();
|
||||||
|
|
||||||
void swap_bmx_icon(const Icon** replace, const char* base, const char* name, FuriString* path, File* file);
|
void swap_bmx_icon(
|
||||||
|
const Icon** replace,
|
||||||
|
const char* base,
|
||||||
|
const char* name,
|
||||||
|
FuriString* path,
|
||||||
|
File* file);
|
||||||
|
|
||||||
void free_bmx_icon(Icon* icon);
|
void free_bmx_icon(Icon* icon);
|
||||||
|
|||||||
@@ -3,17 +3,21 @@
|
|||||||
XtremeSettings* xtreme_settings = NULL;
|
XtremeSettings* xtreme_settings = NULL;
|
||||||
|
|
||||||
XtremeSettings* XTREME_SETTINGS() {
|
XtremeSettings* XTREME_SETTINGS() {
|
||||||
if (xtreme_settings == NULL) {
|
if(xtreme_settings == NULL) {
|
||||||
XTREME_SETTINGS_LOAD();
|
XTREME_SETTINGS_LOAD();
|
||||||
}
|
}
|
||||||
return xtreme_settings;
|
return xtreme_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XTREME_SETTINGS_LOAD() {
|
bool XTREME_SETTINGS_LOAD() {
|
||||||
if (xtreme_settings == NULL) {
|
if(xtreme_settings == NULL) {
|
||||||
xtreme_settings = malloc(sizeof(XtremeSettings));
|
xtreme_settings = malloc(sizeof(XtremeSettings));
|
||||||
bool loaded = saved_struct_load(
|
bool loaded = saved_struct_load(
|
||||||
XTREME_SETTINGS_PATH, xtreme_settings, sizeof(XtremeSettings), XTREME_SETTINGS_MAGIC, XTREME_SETTINGS_VERSION);
|
XTREME_SETTINGS_PATH,
|
||||||
|
xtreme_settings,
|
||||||
|
sizeof(XtremeSettings),
|
||||||
|
XTREME_SETTINGS_MAGIC,
|
||||||
|
XTREME_SETTINGS_VERSION);
|
||||||
if(!loaded) {
|
if(!loaded) {
|
||||||
memset(xtreme_settings, 0, sizeof(XtremeSettings));
|
memset(xtreme_settings, 0, sizeof(XtremeSettings));
|
||||||
loaded = XTREME_SETTINGS_SAVE();
|
loaded = XTREME_SETTINGS_SAVE();
|
||||||
@@ -24,9 +28,13 @@ bool XTREME_SETTINGS_LOAD() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool XTREME_SETTINGS_SAVE() {
|
bool XTREME_SETTINGS_SAVE() {
|
||||||
if (xtreme_settings == NULL) {
|
if(xtreme_settings == NULL) {
|
||||||
XTREME_SETTINGS_LOAD();
|
XTREME_SETTINGS_LOAD();
|
||||||
}
|
}
|
||||||
return saved_struct_save(
|
return saved_struct_save(
|
||||||
XTREME_SETTINGS_PATH, xtreme_settings, sizeof(XtremeSettings), XTREME_SETTINGS_MAGIC, XTREME_SETTINGS_VERSION);
|
XTREME_SETTINGS_PATH,
|
||||||
|
xtreme_settings,
|
||||||
|
sizeof(XtremeSettings),
|
||||||
|
XTREME_SETTINGS_MAGIC,
|
||||||
|
XTREME_SETTINGS_VERSION);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ static bool xtreme_settings_back_event_callback(void* context) {
|
|||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
XtremeSettingsApp* app = context;
|
XtremeSettingsApp* app = context;
|
||||||
|
|
||||||
if (app->level_changed) {
|
if(app->level_changed) {
|
||||||
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
|
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
|
||||||
DolphinStats stats = dolphin_stats(dolphin);
|
DolphinStats stats = dolphin_stats(dolphin);
|
||||||
if (app->dolphin_level != stats.level) {
|
if(app->dolphin_level != stats.level) {
|
||||||
int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0;
|
int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0;
|
||||||
dolphin->state->data.icounter = xp + 1;
|
dolphin->state->data.icounter = xp + 1;
|
||||||
dolphin->state->dirty = true;
|
dolphin->state->dirty = true;
|
||||||
@@ -27,20 +27,22 @@ static bool xtreme_settings_back_event_callback(void* context) {
|
|||||||
furi_record_close(RECORD_DOLPHIN);
|
furi_record_close(RECORD_DOLPHIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app->subghz_changed) {
|
if(app->subghz_changed) {
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
FlipperFormat* subghz_range = flipper_format_file_alloc(storage);
|
FlipperFormat* subghz_range = flipper_format_file_alloc(storage);
|
||||||
if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) {
|
if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) {
|
||||||
flipper_format_insert_or_update_bool(subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1);
|
flipper_format_insert_or_update_bool(
|
||||||
flipper_format_insert_or_update_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1);
|
subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1);
|
||||||
|
flipper_format_insert_or_update_bool(
|
||||||
|
subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1);
|
||||||
}
|
}
|
||||||
flipper_format_free(subghz_range);
|
flipper_format_free(subghz_range);
|
||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app->settings_changed) {
|
if(app->settings_changed) {
|
||||||
XTREME_SETTINGS_SAVE();
|
XTREME_SETTINGS_SAVE();
|
||||||
if (app->assets_changed) {
|
if(app->assets_changed) {
|
||||||
popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter);
|
popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter);
|
||||||
popup_set_text(app->popup, "Swapping assets...", 64, 40, AlignCenter, AlignCenter);
|
popup_set_text(app->popup, "Swapping assets...", 64, 40, AlignCenter, AlignCenter);
|
||||||
popup_set_callback(app->popup, xtreme_settings_reboot);
|
popup_set_callback(app->popup, xtreme_settings_reboot);
|
||||||
@@ -81,9 +83,7 @@ XtremeSettingsApp* xtreme_settings_app_alloc() {
|
|||||||
|
|
||||||
app->popup = popup_alloc();
|
app->popup = popup_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher,
|
app->view_dispatcher, XtremeSettingsAppViewPopup, popup_get_view(app->popup));
|
||||||
XtremeSettingsAppViewPopup,
|
|
||||||
popup_get_view(app->popup));
|
|
||||||
|
|
||||||
// Set first scene
|
// Set first scene
|
||||||
scene_manager_next_scene(app->scene_manager, XtremeSettingsAppSceneStart);
|
scene_manager_next_scene(app->scene_manager, XtremeSettingsAppSceneStart);
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ static Gap* gap = NULL;
|
|||||||
|
|
||||||
static inline void fetch_rssi() {
|
static inline void fetch_rssi() {
|
||||||
uint8_t ret_rssi = 127;
|
uint8_t ret_rssi = 127;
|
||||||
if (hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) {
|
if(hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) {
|
||||||
gap->conn_rssi = (int8_t) ret_rssi;
|
gap->conn_rssi = (int8_t)ret_rssi;
|
||||||
gap->time_rssi_sample = furi_get_tick();
|
gap->time_rssi_sample = furi_get_tick();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
|||||||
gap->connection_params.slave_latency = event->Conn_Latency;
|
gap->connection_params.slave_latency = event->Conn_Latency;
|
||||||
gap->connection_params.supervisor_timeout = event->Supervision_Timeout;
|
gap->connection_params.supervisor_timeout = event->Supervision_Timeout;
|
||||||
|
|
||||||
|
|
||||||
// Stop advertising as connection completed
|
// Stop advertising as connection completed
|
||||||
furi_timer_stop(gap->advertise_timer);
|
furi_timer_stop(gap->advertise_timer);
|
||||||
|
|
||||||
@@ -383,7 +382,7 @@ static void gap_init_svc(Gap* gap) {
|
|||||||
keypress_supported,
|
keypress_supported,
|
||||||
CFG_ENCRYPTION_KEY_SIZE_MIN,
|
CFG_ENCRYPTION_KEY_SIZE_MIN,
|
||||||
CFG_ENCRYPTION_KEY_SIZE_MAX,
|
CFG_ENCRYPTION_KEY_SIZE_MAX,
|
||||||
CFG_USED_FIXED_PIN, // 0x0 for no pin
|
CFG_USED_FIXED_PIN, // 0x0 for no pin
|
||||||
0,
|
0,
|
||||||
PUBLIC_ADDR);
|
PUBLIC_ADDR);
|
||||||
// Configure whitelist
|
// Configure whitelist
|
||||||
@@ -504,9 +503,15 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
|||||||
// Initialization of GATT & GAP layer
|
// Initialization of GATT & GAP layer
|
||||||
gap->service.adv_name = config->adv_name;
|
gap->service.adv_name = config->adv_name;
|
||||||
FURI_LOG_I(TAG, "Advertising name: %s", &(gap->service.adv_name[1]));
|
FURI_LOG_I(TAG, "Advertising name: %s", &(gap->service.adv_name[1]));
|
||||||
FURI_LOG_I(TAG, "MAC @ : %02X:%02X:%02X:%02X:%02X:%02X",
|
FURI_LOG_I(
|
||||||
config->mac_address[0], config->mac_address[1], config->mac_address[2],
|
TAG,
|
||||||
config->mac_address[3], config->mac_address[4], config->mac_address[5]);
|
"MAC @ : %02X:%02X:%02X:%02X:%02X:%02X",
|
||||||
|
config->mac_address[0],
|
||||||
|
config->mac_address[1],
|
||||||
|
config->mac_address[2],
|
||||||
|
config->mac_address[3],
|
||||||
|
config->mac_address[4],
|
||||||
|
config->mac_address[5]);
|
||||||
gap_init_svc(gap);
|
gap_init_svc(gap);
|
||||||
// Initialization of the BLE Services
|
// Initialization of the BLE Services
|
||||||
SVCCTL_Init();
|
SVCCTL_Init();
|
||||||
@@ -538,15 +543,12 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t gap_get_remote_conn_rssi(int8_t *rssi) {
|
uint32_t gap_get_remote_conn_rssi(int8_t* rssi) {
|
||||||
if (gap && gap->state == GapStateConnected) {
|
if(gap && gap->state == GapStateConnected) {
|
||||||
fetch_rssi();
|
fetch_rssi();
|
||||||
*rssi = gap->conn_rssi;
|
*rssi = gap->conn_rssi;
|
||||||
|
|
||||||
FURI_LOG_D(TAG, "RSSI: %d", *rssi);
|
if(gap->time_rssi_sample) return furi_get_tick() - gap->time_rssi_sample;
|
||||||
|
|
||||||
if (gap->time_rssi_sample)
|
|
||||||
return furi_get_tick() - gap->time_rssi_sample;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ GapState gap_get_state();
|
|||||||
|
|
||||||
void gap_thread_stop();
|
void gap_thread_stop();
|
||||||
|
|
||||||
uint32_t gap_get_remote_conn_rssi(int8_t *rssi);
|
uint32_t gap_get_remote_conn_rssi(int8_t* rssi);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -429,15 +429,13 @@ float furi_hal_bt_get_rssi() {
|
|||||||
* the beginning of the connection
|
* the beginning of the connection
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
uint32_t furi_hal_bt_get_conn_rssi(uint8_t *rssi) {
|
uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi) {
|
||||||
|
|
||||||
int8_t ret_rssi = 0;
|
int8_t ret_rssi = 0;
|
||||||
uint32_t since = gap_get_remote_conn_rssi(&ret_rssi);
|
uint32_t since = gap_get_remote_conn_rssi(&ret_rssi);
|
||||||
|
|
||||||
if (ret_rssi == 127 || since == 0)
|
if(ret_rssi == 127 || since == 0) return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
*rssi = (uint8_t) abs(ret_rssi);
|
*rssi = (uint8_t)abs(ret_rssi);
|
||||||
|
|
||||||
return since;
|
return since;
|
||||||
}
|
}
|
||||||
@@ -469,12 +467,14 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) {
|
|||||||
|
|
||||||
void furi_hal_bt_set_profile_adv_name(
|
void furi_hal_bt_set_profile_adv_name(
|
||||||
FuriHalBtProfile profile,
|
FuriHalBtProfile profile,
|
||||||
const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1]) {
|
const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1]) {
|
||||||
furi_assert(profile < FuriHalBtProfileNumber);
|
furi_assert(profile < FuriHalBtProfileNumber);
|
||||||
furi_assert(name);
|
furi_assert(name);
|
||||||
|
|
||||||
memcpy(
|
memcpy(
|
||||||
&(profile_config[profile].config.adv_name[1]), name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1);
|
&(profile_config[profile].config.adv_name[1]),
|
||||||
|
name,
|
||||||
|
FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) {
|
const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) {
|
||||||
|
|||||||
@@ -199,8 +199,7 @@ bool furi_hal_bt_hid_kb_press(uint16_t button) {
|
|||||||
bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots) {
|
bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots) {
|
||||||
furi_assert(kb_report);
|
furi_assert(kb_report);
|
||||||
for(uint8_t i = 0; n_empty_slots > 0 && i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {
|
for(uint8_t i = 0; n_empty_slots > 0 && i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {
|
||||||
if(kb_report->key[i] == 0)
|
if(kb_report->key[i] == 0) n_empty_slots--;
|
||||||
n_empty_slots--;
|
|
||||||
}
|
}
|
||||||
return (n_empty_slots == 0);
|
return (n_empty_slots == 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,19 +228,23 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode);
|
|||||||
* @param[in] profile profile type
|
* @param[in] profile profile type
|
||||||
* @param[in] name new adv name
|
* @param[in] name new adv name
|
||||||
*/
|
*/
|
||||||
void furi_hal_bt_set_profile_adv_name(FuriHalBtProfile profile, const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1]);
|
void furi_hal_bt_set_profile_adv_name(
|
||||||
|
FuriHalBtProfile profile,
|
||||||
|
const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1]);
|
||||||
|
|
||||||
const char *furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile);
|
const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile);
|
||||||
|
|
||||||
/** Modify profile mac address and restart bluetooth
|
/** Modify profile mac address and restart bluetooth
|
||||||
* @param[in] profile profile type
|
* @param[in] profile profile type
|
||||||
* @param[in] mac new mac address
|
* @param[in] mac new mac address
|
||||||
*/
|
*/
|
||||||
void furi_hal_bt_set_profile_mac_addr(FuriHalBtProfile profile, const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]);
|
void furi_hal_bt_set_profile_mac_addr(
|
||||||
|
FuriHalBtProfile profile,
|
||||||
|
const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]);
|
||||||
|
|
||||||
const uint8_t *furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile);
|
const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile);
|
||||||
|
|
||||||
uint32_t furi_hal_bt_get_conn_rssi(uint8_t *rssi);
|
uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
3146
flipper.log
3146
flipper.log
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user