/******************************************************************************* * @file power_ctrl.c * @brief Power Management and Control Functions * @author Charles KWON * @date 2025-01-30 * @copyright (c) 2025 Medithings Inc. All rights reserved. * * @details Power on/off control, sleep mode, button state machine. ******************************************************************************/ #include "sdk_config.h" #include "power_ctrl.h" #include "ble_core.h" #include "nrf_gpio.h" #include "nrf_delay.h" #include "app_timer.h" #include "app_error.h" #include "bsp.h" #include "bsp_btn_ble.h" #include "ble_advertising.h" #include "debug_print.h" #include "main.h" #include "main_timer.h" #include "battery_saadc.h" #include "power_control.h" #include "measurements.h" #include "cat_interface.h" #include #include "app_raw_main.h" #include "ada2200_spi.h" #include "mcp4725_i2c.h" #include "storage/dr_mem.h" #include "fstorage.h" /*============================================================================== * EXTERNAL DECLARATIONS *============================================================================*/ extern uint16_t m_conn_handle; extern bool bond_data_delete; extern bool erase_bonds; extern volatile bool ble_connection_st; /* Functions from ble_core module - declared in ble_core.h */ /*============================================================================== * GLOBAL VARIABLES *============================================================================*/ volatile bool processing = false; volatile bool power_state = false; bool power_off_duble_prohibit = false; bool device_status = false; bool device_reset = true; uint16_t cnt_s = 0; uint8_t m_reset_status = 0; bool go_device_power_off = false; bool go_sleep_mode_enter = false; bool go_NVIC_SystemReset = false; /*============================================================================== * TIMER INSTANCES *============================================================================*/ APP_TIMER_DEF(m_power_on_delay_timer_id); APP_TIMER_DEF(m_power_off_delay_timer_id); APP_TIMER_DEF(m_PM_timer_id); /*============================================================================== * TIMER INITIALIZATION *============================================================================*/ void power_ctrl_timers_init(void) { APP_ERROR_CHECK(app_timer_create(&m_power_on_delay_timer_id, APP_TIMER_MODE_SINGLE_SHOT, power_button_handler)); APP_ERROR_CHECK(app_timer_create(&m_power_off_delay_timer_id, APP_TIMER_MODE_SINGLE_SHOT, power_off_timeout_handler)); APP_ERROR_CHECK(app_timer_create(&m_PM_timer_id, APP_TIMER_MODE_SINGLE_SHOT, pm_timeout_handler)); } void power_ctrl_timers_start(void) { ret_code_t err_code; err_code = app_timer_start(m_power_on_delay_timer_id, APP_TIMER_TICKS(POWER_ON_DELAY), NULL); APP_ERROR_CHECK(err_code); } /*============================================================================== * POWER CONTROL FUNCTIONS *============================================================================*/ void power_control_set(on_off_cont_t device_power_st) { if (device_power_st == OFF) { nrf_gpio_pin_clear(POWER_HOLD); DBG_PRINTF("Main Power OFF\r\n"); } else if (device_power_st == ON) { nrf_gpio_pin_set(POWER_HOLD); DBG_PRINTF("Main Power ON\r\n"); } } void device_power_off(void) { uint32_t err_code = bsp_indication_set(BSP_INDICATE_USER_STATE_ON); APP_ERROR_CHECK(err_code); APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL)); } void sleep_mode_enter(void) { uint32_t err_code = bsp_indication_set(BSP_INDICATE_USER_STATE_ON); APP_ERROR_CHECK(err_code); DBG_PRINTF("go Sleep!\r\n"); APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL)); } /*============================================================================== * TIMER CALLBACKS *============================================================================*/ void power_off_timeout_handler(void * p_context) { UNUSED_PARAMETER(p_context); APP_ERROR_CHECK(app_timer_stop(m_power_off_delay_timer_id)); DBG_PRINTF("Off stop\r\n"); uint32_t err_code = bsp_indication_set(BSP_INDICATE_USER_STATE_OFF); APP_ERROR_CHECK(err_code); power_control_set(OFF); } void pm_timeout_handler(void * p_context) { UNUSED_PARAMETER(p_context); APP_ERROR_CHECK(app_timer_stop(m_PM_timer_id)); if (m_reset_status == 5) { DBG_PRINTF("kill\r\n"); disconnect(m_conn_handle, NULL); } } /*============================================================================== * POWER BUTTON STATE MACHINE *============================================================================*/ void power_button_handler(void * p_context) { UNUSED_PARAMETER(p_context); APP_ERROR_CHECK(app_timer_stop(m_power_on_delay_timer_id)); if (nrf_gpio_pin_read(POWER_BUTTON)) { /* Button released - determine action based on press duration */ if ((cnt_s < 150) && (m_reset_status != 2)) { /* Short press - power off */ uint32_t err_code = bsp_indication_set(BSP_INDICATE_USER_STATE_OFF); (void)err_code; power_control_set(OFF); cnt_s = 0; } else if (cnt_s > 1000) { /* Very long press - factory reset */ power_control_set(ON); nrf_delay_ms(100); bond_data_delete = true; /* Factory reset: save defaults to FDS */ { uint8_t bd = (uint8_t)bond_data_delete; dr_memWrite("bond_delete", &bd, 1); const char pass_init[6] = "123456"; dr_memWrite("passkey", (uint8_t *)pass_init, 6); } nrf_delay_ms(1000); go_device_power_off = true; main_timer_start(); } else if (cnt_s > 150 || (m_reset_status == 2)) { /* Long press - normal startup */ device_reset = false; DBG_PRINTF("\r\n\r\n/////////////////////////////////\r\n"); DBG_PRINTF("MEDiThings VivaMayo Started 1.12C\r\n"); DBG_PRINTF("/////////////////////////////////\r\n"); power_control_set(ON); battery_timer_start(); icm42670_init(); nrf_delay_ms(2); DBG_PRINTF("ADV_START\r\n"); #if FEATURE_SECURE_CONNECTION advertising_start(erase_bonds); #else advertising_start(); #endif DBG_PRINTF("ADV_DONE\r\n"); ada2200_init(); nrf_delay_ms(1); mcp4725_init(); nrf_delay_ms(1); /* Set reset status in RAM only (no FDS write during boot) */ m_reset_status = 1; m_config.reset_status = 1; LOG_PRINTF("START\r\n"); /* W25Q32 Flash test - disabled, use cmd?99,0,0 manually */ // w25q_test_timer_start(); } } else { /* Button still pressed - increment counter and continue polling */ cnt_s++; device_reset = false; if (cnt_s == 150) { /* Indicate long press threshold reached */ uint32_t err_code = bsp_indication_set(BSP_INDICATE_USER_STATE_ON); (void)err_code; } power_ctrl_timers_start(); } } /*============================================================================== * BSP EVENT HANDLER *============================================================================*/ void power_bsp_event_handler(bsp_event_t event) { uint32_t err_code; switch (event) { case BSP_EVENT_SLEEP: go_sleep_mode_enter = true; DBG_PRINTF("BSP_EVENT_sleep\r\n"); main_timer_start(); break; case BSP_EVENT_DISCONNECT: err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); if (err_code != NRF_SUCCESS) { m_reset_status = 2; } else { DBG_PRINTF("Disconnected connection handle %d\r\n", m_conn_handle); } break; case BSP_EVENT_WHITELIST_OFF: if (m_conn_handle == BLE_CONN_HANDLE_INVALID) { err_code = ble_advertising_restart_without_whitelist_wrapper(); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } } break; case BSP_EVENT_KEY_2: break; case BSP_EVENT_POWER_CONTROL: if (processing == false) { DBG_PRINTF("SYSTEM OFF\r\n"); err_code = bsp_indication_set(BSP_INDICATE_USER_STATE_ON); APP_ERROR_CHECK(err_code); go_device_power_off = true; main_timer_start(); } else { DBG_PRINTF("SYSTEM OFF FAIL\r\n"); } break; default: break; } }