diff --git a/src/core/elrs.c b/src/core/elrs.c index 07c5550e..6c79d15f 100644 --- a/src/core/elrs.c +++ b/src/core/elrs.c @@ -278,6 +278,7 @@ void msp_process_packet() { msp_send_packet(MSP_GET_REC_STATE, MSP_PACKET_RESPONSE, 1, &buf); } break; case MSP_SET_REC_STATE: { + g_setting.ht.alarm_on_arm = packet.payload[0]; if (g_app_state == APP_STATE_VIDEO) { record_state = packet.payload[0] == 0 ? 1 : 2; uint32_t delay = packet.payload[1] | (uint32_t)packet.payload[2] << 8; diff --git a/src/core/ht.c b/src/core/ht.c index 1d5689ed..c01ebb01 100644 --- a/src/core/ht.c +++ b/src/core/ht.c @@ -49,7 +49,10 @@ static const int ppmMaxPulse = 500; static const int ppmMinPulse = -500; static const int ppmCenter = 1500; +static pthread_t head_alarm_handle; + static void calculate_orientation(); +static void *head_alarm_thread(void *arg); /////////////////////////////////////////////////////////////////////////////// // no motion to disable OLED display @@ -239,6 +242,11 @@ void ht_set_maxangle(int angle) { ht_data.panFactor = 1000.0 / angle; } +void ht_set_alarm_angle() { + g_setting.ht.alarm_angle = ht_data.tiltAngle; + ini_putl("ht", "alarm_angle", g_setting.ht.alarm_angle, SETTING_INI); +} + static void calc_gyr(float *gyrAngle) // in degree { // convert gyro readings to degrees/sec (with calibration offsets) @@ -356,3 +364,36 @@ void ht_disable() { int16_t *ht_get_channels() { return ht_data.htChannels; } + +void head_alarm_init() { + pthread_create(&head_alarm_handle, NULL, head_alarm_thread, NULL); +} + +void *head_alarm_thread(void *arg) { + while (1) { + bool sounding_alarm = false; + if (ht_data.enable && (g_setting.ht.alarm_state != SETTING_HT_ALARM_STATE_OFF)) { // user settings + if ((g_setting.ht.alarm_on_arm && g_setting.ht.alarm_state == SETTING_HT_ALARM_STATE_ARM) || (g_setting.ht.alarm_on_video && g_setting.ht.alarm_state == SETTING_HT_ALARM_STATE_VIDEO)) { // system enabling alarm (when armed or has video signal) + + if (ht_data.tiltAngle < g_setting.ht.alarm_angle) { + beep(); + usleep(100000); + beep(); + for (int i = 0; i < 30; i++) { // delay of 3 seconds split into 30x100ms to allow for a quicker response to the alarm + if (ht_data.tiltAngle >= g_setting.ht.alarm_angle) { + break; + } + usleep(100000); + } + } + + usleep(150000); // prevent resource occupation (when armed or video) + } else { + usleep(250000); // prevent resource occupation (when !armed and/or !video) + } + } else { + sleep(3); // prevent resource occupation (when ht and/or alarm is disabled) + } + } + pthread_exit(NULL); +} \ No newline at end of file diff --git a/src/core/ht.h b/src/core/ht.h index a3d48742..d757a83b 100644 --- a/src/core/ht.h +++ b/src/core/ht.h @@ -55,8 +55,10 @@ void ht_disable(); void ht_detect_motion(); void ht_calibrate(); void ht_set_maxangle(int angle); +void ht_set_alarm_angle(); void ht_set_center_position(); int16_t *ht_get_channels(); +void head_alarm_init(); #ifdef __cplusplus } diff --git a/src/core/main.c b/src/core/main.c index 0fbe40ce..d46650a4 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -191,6 +191,9 @@ int main(int argc, char *argv[]) { // 8.1 set initial analog module power state Analog_Module_Power(0); + // Head alarm + head_alarm_init(); + // 10. Execute main loop g_init_done = 1; for (;;) { diff --git a/src/core/settings.c b/src/core/settings.c index 88ea3811..1af64c74 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -65,6 +65,12 @@ const setting_t g_setting_defaults = { .gyr_x = 0, .gyr_y = 0, .gyr_z = 0, + .alarm_state = SETTING_HT_ALARM_STATE_OFF, + .alarm_angle = 1300, + .alarm_delay = 5, + .alarm_pattern = SETTING_HT_ALARM_PATTERN_2SHORT, + .alarm_on_arm = false, + .alarm_on_video = false, }, .elrs = { .enable = false, @@ -403,6 +409,8 @@ void settings_load(void) { g_setting.ht.gyr_x = ini_getl("ht", "gyr_x", g_setting_defaults.ht.gyr_x, SETTING_INI); g_setting.ht.gyr_y = ini_getl("ht", "gyr_y", g_setting_defaults.ht.gyr_y, SETTING_INI); g_setting.ht.gyr_z = ini_getl("ht", "gyr_z", g_setting_defaults.ht.gyr_z, SETTING_INI); + g_setting.ht.alarm_state = ini_getl("ht", "alarm_state", g_setting_defaults.ht.alarm_state, SETTING_INI); + g_setting.ht.alarm_angle = ini_getl("ht", "alarm_angle", g_setting_defaults.ht.alarm_angle, SETTING_INI); // elrs g_setting.elrs.enable = settings_get_bool("elrs", "enable", g_setting_defaults.elrs.enable); diff --git a/src/core/settings.h b/src/core/settings.h index 388e7951..d978de1c 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -54,6 +54,18 @@ typedef enum { SETTING_POWER_WARNING_TYPE_BOTH = 2 } setting_power_warning_type_t; +typedef enum { + SETTING_HT_ALARM_STATE_OFF = 0, + SETTING_HT_ALARM_STATE_VIDEO = 1, + SETTING_HT_ALARM_STATE_ARM = 2, +} setting_ht_alarm_state_t; + +typedef enum { + SETTING_HT_ALARM_PATTERN_1SHORT = 0, + SETTING_HT_ALARM_PATTERN_2SHORT = 1, + SETTING_HT_ALARM_PATTERN_1LONG = 2 +} setting_ht_alarm_pattern_t; + typedef struct { int voltage; bool display_voltage; @@ -102,6 +114,12 @@ typedef struct { int32_t gyr_x; int32_t gyr_y; int32_t gyr_z; + setting_ht_alarm_state_t alarm_state; + int alarm_angle; + uint16_t alarm_delay; + setting_ht_alarm_pattern_t alarm_pattern; + bool alarm_on_arm; + bool alarm_on_video; } setting_head_tracker_t; typedef struct { diff --git a/src/core/thread.c b/src/core/thread.c index d8312467..6d1bc06b 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -78,6 +78,17 @@ static void check_hdzero_signal(int vtmg_change) { tune_channel_timer(); } + if (g_source_info.source == SOURCE_AV_IN) + is_valid = g_source_info.av_in_status; + else if (g_source_info.source == SOURCE_EXPANSION) + is_valid = g_source_info.av_bay_status; + else + is_valid = (rx_status[0].rx_valid || rx_status[1].rx_valid); + + if (g_setting.ht.alarm_state == SETTING_HT_ALARM_STATE_VIDEO) { + g_setting.ht.alarm_on_video = is_valid; + } + // exit if no SD card or not in video mode if (g_setting.record.mode_manual || (!g_sdcard_enable) || (g_app_state != APP_STATE_VIDEO)) return; @@ -103,19 +114,13 @@ static void check_hdzero_signal(int vtmg_change) { cnt = 0; } - if (g_source_info.source == SOURCE_AV_IN) - is_valid = g_source_info.av_in_status; - else if (g_source_info.source == SOURCE_EXPANSION) - is_valid = g_source_info.av_bay_status; - else - is_valid = (rx_status[0].rx_valid || rx_status[1].rx_valid); - if (dvr_is_recording) { // in-recording if (!is_valid) { cnt++; if (cnt >= SIGNAL_LOSS_DURATION_THR) { cnt = 0; LOGI("Signal lost"); + g_setting.ht.alarm_on_video = false; dvr_cmd(DVR_STOP); } } else diff --git a/src/ui/page_headtracker.c b/src/ui/page_headtracker.c index 1e7538a8..223c19c1 100644 --- a/src/ui/page_headtracker.c +++ b/src/ui/page_headtracker.c @@ -1,5 +1,6 @@ #include "page_headtracker.h" +#include #include #include "common.hh" @@ -9,39 +10,126 @@ #include "page_common.h" #include "ui/ui_style.h" +typedef enum { + PAGE1 = 1, + PAGE2 = 2, +} page_t; + +static btn_group_t page_select; +static page_t curr_page = 0; + static btn_group_t btn_group; static lv_coord_t col_dsc[] = {160, 160, 160, 160, 160, 160, LV_GRID_TEMPLATE_LAST}; -static lv_coord_t row_dsc[] = {60, 60, 60, 60, 60, 60, 40, 40, 40, 60, LV_GRID_TEMPLATE_LAST}; +static lv_coord_t row_dsc[] = {55, 55, 55, 55, 55, 55, 60, 30, 40, 40, 40, LV_GRID_TEMPLATE_LAST}; + static lv_obj_t *label_cali; static lv_obj_t *label_center; +static slider_group_t slider_group; static lv_timer_t *timer; static lv_obj_t *pan; static lv_obj_t *tilt; static lv_obj_t *roll; -static slider_group_t slider_group; + +static btn_group_t alarm_state; +static lv_obj_t *label_alarm_angle; +static uint8_t set_alarm_wait_for_timer = 0; +static lv_timer_t *set_alarm_angle_timer = NULL; + bool angle_slider_selected; -static void update_visibility() { - slider_enable(&slider_group, g_setting.ht.enable); +static void update_visibility(uint8_t page) { - if (g_setting.ht.enable) { + // enable/disable elements + if (g_setting.ht.enable && page == PAGE1) { lv_obj_clear_state(label_cali, STATE_DISABLED); lv_obj_clear_state(label_center, STATE_DISABLED); + slider_enable(&slider_group, true); lv_obj_add_flag(pp_headtracker.p_arr.panel[1], FLAG_SELECTABLE); lv_obj_add_flag(pp_headtracker.p_arr.panel[2], FLAG_SELECTABLE); lv_obj_add_flag(pp_headtracker.p_arr.panel[3], FLAG_SELECTABLE); - } else { + lv_obj_add_flag(pp_headtracker.p_arr.panel[4], FLAG_SELECTABLE); + + } else if (page == PAGE1) { lv_obj_add_state(label_cali, STATE_DISABLED); lv_obj_add_state(label_center, STATE_DISABLED); + slider_enable(&slider_group, false); + + lv_obj_add_flag(pp_headtracker.p_arr.panel[1], FLAG_SELECTABLE); + lv_obj_clear_flag(pp_headtracker.p_arr.panel[2], FLAG_SELECTABLE); + lv_obj_clear_flag(pp_headtracker.p_arr.panel[3], FLAG_SELECTABLE); + lv_obj_clear_flag(pp_headtracker.p_arr.panel[4], FLAG_SELECTABLE); + } + + if (g_setting.ht.enable && page == PAGE2) { + btn_group_enable(&alarm_state, true); + + lv_obj_add_flag(pp_headtracker.p_arr.panel[1], FLAG_SELECTABLE); + lv_obj_clear_flag(pp_headtracker.p_arr.panel[3], FLAG_SELECTABLE); + lv_obj_clear_flag(pp_headtracker.p_arr.panel[4], FLAG_SELECTABLE); + + if (g_setting.ht.alarm_state != SETTING_HT_ALARM_STATE_OFF) { + lv_obj_clear_state(label_alarm_angle, STATE_DISABLED); + lv_obj_add_flag(pp_headtracker.p_arr.panel[2], FLAG_SELECTABLE); + + } else { + lv_obj_add_state(label_alarm_angle, STATE_DISABLED); + lv_obj_clear_flag(pp_headtracker.p_arr.panel[2], FLAG_SELECTABLE); + } + } else if (page == PAGE2) { + lv_obj_add_state(label_alarm_angle, STATE_DISABLED); + btn_group_enable(&alarm_state, false); lv_obj_clear_flag(pp_headtracker.p_arr.panel[1], FLAG_SELECTABLE); lv_obj_clear_flag(pp_headtracker.p_arr.panel[2], FLAG_SELECTABLE); lv_obj_clear_flag(pp_headtracker.p_arr.panel[3], FLAG_SELECTABLE); + lv_obj_clear_flag(pp_headtracker.p_arr.panel[4], FLAG_SELECTABLE); + } + + // hiding and showing elements + switch (page) { + case PAGE1: + // show page 1 + btn_group_show(&btn_group, true); + slider_show(&slider_group, true); + lv_obj_clear_flag(label_cali, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(label_center, LV_OBJ_FLAG_HIDDEN); + + // hide page 2 + btn_group_show(&alarm_state, false); + lv_obj_add_flag(label_alarm_angle, LV_OBJ_FLAG_HIDDEN); + + break; + + case PAGE2: + // hide page 1 + btn_group_show(&btn_group, false); + slider_show(&slider_group, false); + lv_obj_add_flag(label_cali, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(label_center, LV_OBJ_FLAG_HIDDEN); + + // show page 2 + btn_group_show(&alarm_state, true); + lv_obj_clear_flag(label_alarm_angle, LV_OBJ_FLAG_HIDDEN); + + break; + } +} + +static void page_headtracker_set_alarm_reset() { + lv_label_set_text(label_alarm_angle, "Set Alarm Angle"); + if (set_alarm_angle_timer != NULL) { + lv_timer_del(set_alarm_angle_timer); + set_alarm_angle_timer = NULL; } } +static void page_headtracker_set_alarm_angle_timer_cb(struct _lv_timer_t *timer) { + page_headtracker_set_alarm_reset(); + // code to execute when the timer elapses and the angle has to be set? +} + static lv_obj_t *page_headtracker_create(lv_obj_t *parent, panel_arr_t *arr) { lv_obj_t *page = lv_menu_page_create(parent, NULL); lv_obj_clear_flag(page, LV_OBJ_FLAG_SCROLLABLE); @@ -67,20 +155,35 @@ static lv_obj_t *page_headtracker_create(lv_obj_t *parent, panel_arr_t *arr) { create_select_item(arr, cont); - create_btn_group_item(&btn_group, cont, 2, "Tracking", "On", "Off", "", "", 0); + create_btn_group_item(&page_select, cont, 2, "Page", "Tracking", "Tilt Alarm", "", "", 0); + + // page 2 items + create_btn_group_item(&alarm_state, cont, 3, "Alarm", "Off", "Video", "Arm", "", 1); - label_cali = create_label_item(cont, "Calibrate", 1, 1, 1); + label_alarm_angle = create_label_item(cont, "Set Alarm Angle", 1, 2, 1); + lv_obj_clear_flag(label_alarm_angle, LV_OBJ_FLAG_SCROLLABLE); - label_center = create_label_item(cont, "Set Center", 1, 2, 1); + // preload page 2 items + btn_group_enable(&alarm_state, false); + lv_obj_add_state(label_alarm_angle, STATE_DISABLED); - create_slider_item(&slider_group, cont, "Max Angle", 360, g_setting.ht.max_angle, 3); + // page 1 items + create_btn_group_item(&btn_group, cont, 2, "Tracking", "On", "Off", "", "", 1); + + label_cali = create_label_item(cont, "Calibrate", 1, 2, 1); + + label_center = create_label_item(cont, "Set Center", 1, 3, 1); + + create_slider_item(&slider_group, cont, "Max Angle", 360, g_setting.ht.max_angle, 4); lv_slider_set_range(slider_group.slider, 0, 360); - create_label_item(cont, "< Back", 1, 4, 1); + create_label_item(cont, "< Back", 1, 5, 1); btn_group_set_sel(&btn_group, !g_setting.ht.enable); + btn_group_set_sel(&alarm_state, g_setting.ht.alarm_state); + btn_group_set_sel(&page_select, 0); - create_label_item(cont, "Pan", 1, 6, 1); + create_label_item(cont, "Pan", 1, 7, 1); pan = lv_bar_create(cont); lv_bar_set_range(pan, 1000, 2000); lv_obj_set_size(pan, 500, 25); @@ -91,9 +194,9 @@ static lv_obj_t *page_headtracker_create(lv_obj_t *parent, panel_arr_t *arr) { lv_obj_set_style_bg_color(pan, lv_color_make(0, 0xff, 0), LV_PART_INDICATOR); lv_obj_set_style_radius(pan, 0, LV_PART_INDICATOR); lv_obj_set_grid_cell(pan, LV_GRID_ALIGN_START, 2, 1, - LV_GRID_ALIGN_CENTER, 6, 1); + LV_GRID_ALIGN_CENTER, 7, 1); - create_label_item(cont, "Tilt", 1, 7, 1); + create_label_item(cont, "Tilt", 1, 8, 1); tilt = lv_bar_create(cont); lv_bar_set_range(tilt, 1000, 2000); lv_obj_set_size(tilt, 500, 25); @@ -104,9 +207,9 @@ static lv_obj_t *page_headtracker_create(lv_obj_t *parent, panel_arr_t *arr) { lv_obj_set_style_bg_color(tilt, lv_color_make(0, 0xff, 0), LV_PART_INDICATOR); lv_obj_set_style_radius(tilt, 0, LV_PART_INDICATOR); lv_obj_set_grid_cell(tilt, LV_GRID_ALIGN_START, 2, 1, - LV_GRID_ALIGN_CENTER, 7, 1); + LV_GRID_ALIGN_CENTER, 8, 1); - create_label_item(cont, "Roll", 1, 8, 1); + create_label_item(cont, "Roll", 1, 9, 1); roll = lv_bar_create(cont); lv_bar_set_range(roll, 1000, 2000); lv_obj_set_size(roll, 500, 25); @@ -117,9 +220,10 @@ static lv_obj_t *page_headtracker_create(lv_obj_t *parent, panel_arr_t *arr) { lv_obj_set_style_bg_color(roll, lv_color_make(0, 0xff, 0), LV_PART_INDICATOR); lv_obj_set_style_radius(roll, 0, LV_PART_INDICATOR); lv_obj_set_grid_cell(roll, LV_GRID_ALIGN_START, 2, 1, - LV_GRID_ALIGN_CENTER, 8, 1); + LV_GRID_ALIGN_CENTER, 9, 1); - update_visibility(); + curr_page = PAGE1; + update_visibility(curr_page); return page; } @@ -167,6 +271,12 @@ static void page_headtracker_exit_slider() { } static void page_headtracker_on_roller(uint8_t key) { + + // Ignore commands until timer has expired before allowing user to proceed. + if (set_alarm_angle_timer != NULL) { + return; + } + if (angle_slider_selected == false) { return; } @@ -178,8 +288,8 @@ static void page_headtracker_on_roller(uint8_t key) { } } -static void page_headtracker_on_click(uint8_t key, int sel) { - if (sel == 0) { +static void page_headtracker_on_click_page1(uint8_t key, int sel) { + if (sel == 1) { btn_group_toggle_sel(&btn_group); g_setting.ht.enable = btn_group_get_sel(&btn_group) == 0; settings_put_bool("ht", "enable", g_setting.ht.enable); @@ -188,16 +298,16 @@ static void page_headtracker_on_click(uint8_t key, int sel) { else ht_disable(); - update_visibility(); - } else if (sel == 1) { + update_visibility(curr_page); + } else if (sel == 2) { lv_label_set_text(label_cali, "Calibrating..."); lv_timer_handler(); ht_calibrate(); lv_label_set_text(label_cali, "Re-calibrate"); lv_timer_handler(); - } else if (sel == 2) { - ht_set_center_position(); } else if (sel == 3) { + ht_set_center_position(); + } else if (sel == 4) { if (angle_slider_selected) { page_headtracker_exit_slider(); } else { @@ -208,6 +318,42 @@ static void page_headtracker_on_click(uint8_t key, int sel) { } } +static void page_headtracker_on_click_page2(uint8_t key, int sel) { + if (sel == 1) { + btn_group_toggle_sel(&alarm_state); + g_setting.ht.alarm_state = btn_group_get_sel(&alarm_state); + ini_putl("ht", "alarm_state", g_setting.ht.alarm_state, SETTING_INI); + } else if (sel == 2) { + lv_label_set_text(label_alarm_angle, "Updating Angle..."); + set_alarm_angle_timer = lv_timer_create(page_headtracker_set_alarm_angle_timer_cb, 1000, NULL); + lv_timer_set_repeat_count(set_alarm_angle_timer, 1); + ht_set_alarm_angle(); + } +} +static void page_headtracker_on_click(uint8_t key, int sel) { + if (sel == 0) { + btn_group_toggle_sel(&page_select); + int page_select_btn = btn_group_get_sel(&page_select); + LOGD("page_select: %d", btn_group_get_sel(&page_select)); + if (page_select_btn == 0) { + curr_page = PAGE1; + } else if (page_select_btn == 1) { + curr_page = PAGE2; + } + update_visibility(curr_page); + } + switch (curr_page) { + case PAGE1: + page_headtracker_on_click_page1(key, sel); + break; + case PAGE2: + page_headtracker_on_click_page2(key, sel); + break; + } + + update_visibility(curr_page); +} + static void page_headtracker_timer(struct _lv_timer_t *timer) { int16_t *channels = ht_get_channels(); lv_bar_set_value(pan, channels[0], LV_ANIM_OFF); @@ -223,16 +369,21 @@ static void page_headtracker_enter() { } static void page_headtracker_exit() { + LOGD("page_headtracker_exit"); if (angle_slider_selected) { page_headtracker_exit_slider(); } + LOGD("page_headtracker_exit 2"); lv_timer_del(timer); + LOGD("page_headtracker_exit 3"); + page_headtracker_set_alarm_reset(); + LOGD("page_headtracker_exit 4"); } page_pack_t pp_headtracker = { .p_arr = { .cur = 0, - .max = 5, + .max = 6, }, .name = "Head Tracker", .create = page_headtracker_create,