- BLE peripheral applications - dr_piezo and bladder_patch projects Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1504 lines
47 KiB
C
1504 lines
47 KiB
C
/*******************************************************************************
|
|
* @file main.c
|
|
* @brief Medithings Bladder Patch Firmware
|
|
* @version 1.17
|
|
* @date 2025-12-09
|
|
* @author Charles KWON
|
|
*
|
|
* @note Build Modes:
|
|
* DEBUG_MINIMAL_BOOT = 1 : Power + BLE only (no sensors, no EEPROM)
|
|
* DEBUG_MINIMAL_BOOT = 0 : Full initialization
|
|
* BLE_DEV_MODE = 1 : No security (fast pairing)
|
|
* BLE_DEV_MODE = 0 : Full security (passkey required)
|
|
******************************************************************************/
|
|
|
|
/*==============================================================================
|
|
* INCLUDES
|
|
*============================================================================*/
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "nordic_common.h"
|
|
#include "nrf.h"
|
|
#include "ble_hci.h"
|
|
#include "ble_advdata.h"
|
|
#include "ble_advertising.h"
|
|
#include "ble_srv_common.h"
|
|
#include "ble_conn_params.h"
|
|
#include "nrf_sdh.h"
|
|
#include "nrf_sdh_soc.h"
|
|
#include "nrf_sdh_ble.h"
|
|
#include "nrf_ble_gatt.h"
|
|
#include "nrf_ble_qwr.h"
|
|
#include "app_timer.h"
|
|
#include "ble_nus.h"
|
|
#include "app_uart.h"
|
|
#include "app_util_platform.h"
|
|
#include "bsp_btn_ble.h"
|
|
#include "nrf_pwr_mgmt.h"
|
|
#include "nrf_delay.h"
|
|
#include "math.h"
|
|
#include "crc16.h"
|
|
|
|
#if defined (UART_PRESENT)
|
|
#include "nrf_uart.h"
|
|
#endif
|
|
#if defined (UARTE_PRESENT)
|
|
#include "nrf_uarte.h"
|
|
#endif
|
|
|
|
#include "nrf_log.h"
|
|
#include "nrf_log_ctrl.h"
|
|
#include "nrf_log_default_backends.h"
|
|
|
|
#if BLE_DFU_ENABLED
|
|
#include "nrf_pwr_mgmt.h"
|
|
#include "ble_dfu.h"
|
|
#include "nrf_power.h"
|
|
#include "nrf_bootloader_info.h"
|
|
#include "nrf_dfu_ble_svci_bond_sharing.h"
|
|
#include "nrf_svci_async_function.h"
|
|
#include "nrf_svci_async_handler.h"
|
|
#endif
|
|
|
|
#include "system_interface.h"
|
|
#include "main.h"
|
|
#include "app_raw_main.h"
|
|
#include "main_timer.h"
|
|
#include "ad5272_i2c.h"
|
|
#include "ada2200_spi.h"
|
|
#include "power_control.h"
|
|
#include "tmp235_q1.h"
|
|
#include "mcp4725_i2c.h"
|
|
#include "measurements.h"
|
|
#include "fds.h"
|
|
#include "battery_saadc.h"
|
|
#include "mcp4725_adc.h"
|
|
#include "meas_pd_voltage_simple.h"
|
|
#include "meas_pd_voltage_half.h"
|
|
#include "meas_pd_voltage_full.h"
|
|
#include "full_agc.h"
|
|
#include "meas_pd_voltage_custom.h"
|
|
#include "meas_pd_imm.h"
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
#include "peer_manager.h"
|
|
#include "peer_manager_handler.h"
|
|
#include "nrf_ble_lesc.h"
|
|
#include "ble_quick_security.h"
|
|
#include "cat_interface.h"
|
|
#include "ir_i2c.h"
|
|
#include "i2c_manager.h"
|
|
#endif
|
|
|
|
#include "nrf_crypto.h"
|
|
#include <cmd_parse.h>
|
|
#include "debug_print.h"
|
|
|
|
/*==============================================================================
|
|
* BUILD CONFIGURATION
|
|
*============================================================================*/
|
|
#define BLE_DEV_MODE 1 /**< 1: DEV (no security), 0: PROD (full security) */
|
|
#define DEBUG_MINIMAL_BOOT 1 /**< 1: Power+BLE only, 0: Full boot */
|
|
|
|
/*==============================================================================
|
|
* HARDWARE PIN DEFINITIONS
|
|
*============================================================================*/
|
|
#define POWER_HOLD NRF_GPIO_PIN_MAP(0,8)
|
|
#define POWER_BUTTON NRF_GPIO_PIN_MAP(1,8)
|
|
|
|
/*==============================================================================
|
|
* BLE CONFIGURATION
|
|
*============================================================================*/
|
|
#define APP_BLE_CONN_CFG_TAG 1
|
|
#define NUS_SERVICE_UUID_TYPE BLE_UUID_TYPE_VENDOR_BEGIN
|
|
#define APP_BLE_OBSERVER_PRIO 3
|
|
#define APP_ADV_INTERVAL 64
|
|
|
|
#if FEATURE_NO_SLEEP
|
|
#define APP_ADV_DURATION 0
|
|
#else
|
|
#define APP_ADV_DURATION 18000
|
|
#endif
|
|
|
|
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS)
|
|
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(75, UNIT_1_25_MS)
|
|
#define SLAVE_LATENCY 0
|
|
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
|
|
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000)
|
|
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000)
|
|
#define MAX_CONN_PARAMS_UPDATE_COUNT 3
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
#define LESC_DEBUG_MODE 0
|
|
#define SEC_PARAM_BOND 1
|
|
#define SEC_PARAM_MITM 1
|
|
#if FEATURE_STATIC_PASSKEY
|
|
#define SEC_PARAM_LESC 0
|
|
#else
|
|
#define SEC_PARAM_LESC 1
|
|
#endif
|
|
#define SEC_PARAM_KEYPRESS 0
|
|
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY
|
|
#define SEC_PARAM_OOB 0
|
|
#define SEC_PARAM_MIN_KEY_SIZE 7
|
|
#define SEC_PARAM_MAX_KEY_SIZE 16
|
|
#define PASSKEY_TXT_LENGTH 8
|
|
#define PASSKEY_LENGTH 6
|
|
#endif
|
|
|
|
/*==============================================================================
|
|
* SYSTEM CONSTANTS
|
|
*============================================================================*/
|
|
#define DEAD_BEEF 0xDEADBEEF
|
|
#define UART_TX_BUF_SIZE 16384
|
|
#define UART_RX_BUF_SIZE 512
|
|
#define POWER_ON_DELAY 5
|
|
#define POWER_OFF_DELAY 3000
|
|
#define POWER_RESET_DELAY 2000
|
|
#define LED_NUM 24
|
|
#define AES_KEY_SIZE 16
|
|
#define AES_BLOCK_SIZE 16
|
|
|
|
/*==============================================================================
|
|
* BLE INSTANCES
|
|
*============================================================================*/
|
|
BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);
|
|
NRF_BLE_GATT_DEF(m_gatt);
|
|
NRF_BLE_QWR_DEF(m_qwr);
|
|
BLE_ADVERTISING_DEF(m_advertising);
|
|
|
|
/*==============================================================================
|
|
* 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);
|
|
|
|
/*==============================================================================
|
|
* STATIC VARIABLES
|
|
*============================================================================*/
|
|
#if FEATURE_SECURE_CONNECTION
|
|
static pm_peer_id_t m_peer_to_be_deleted = PM_PEER_ID_INVALID;
|
|
#endif
|
|
|
|
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;
|
|
static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;
|
|
static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}};
|
|
|
|
static uint8_t m_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
|
static uint16_t m_tx_len = 0;
|
|
static volatile bool m_tx_in_progress = false;
|
|
static volatile bool m_tx_complete_pending = false; /* TX completion wait flag */
|
|
static uint8_t c_addr[6];
|
|
|
|
static char * roles_str[] = {"INVALID_ROLE", "CENTRAL", "PERIPHERAL"};
|
|
|
|
/*==============================================================================
|
|
* GLOBAL VARIABLES
|
|
*============================================================================*/
|
|
uint8_t m_encrypted_text[AES_BLOCK_SIZE];
|
|
uint8_t m_encrypted_text2[AES_BLOCK_SIZE];
|
|
uint8_t m_decrypted_text[AES_BLOCK_SIZE];
|
|
|
|
volatile uint8_t Sj_type;
|
|
volatile bool processing;
|
|
bool power_off_duble_prohibit = false;
|
|
volatile bool power_state = false;
|
|
|
|
extern bool go_device_power_off;
|
|
extern bool go_sleep_mode_enter;
|
|
extern bool go_NVIC_SystemReset;
|
|
extern bool ble_got_new_data;
|
|
extern bool motion_data_once;
|
|
extern bool adc_enabled;
|
|
extern bool con_single;
|
|
extern bool info4;
|
|
extern uint16_t led_pd_dac_v[LED_NUM];
|
|
extern uint8_t pd_adc_count;
|
|
extern int8_t c_max;
|
|
extern uint8_t simple_samples_in_buffer;
|
|
extern uint8_t full_samples_in_buffer;
|
|
extern uint8_t custom_samples_in_buffer;
|
|
extern bool pd_adc_custom_a_start;
|
|
extern bool pd_adc_custom_start;
|
|
extern bool full_agc_a_start;
|
|
extern uint8_t add_cycle;
|
|
extern bool motion_raw_data_enabled;
|
|
|
|
uint16_t cnt_s;
|
|
char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
|
uint16_t ble_bin_buff[BLE_NUS_MAX_DATA_LEN/2];
|
|
which_cmd_t cmd_type_t;
|
|
|
|
bool device_status = false;
|
|
bool device_reset = true;
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
bool erase_bonds;
|
|
#endif
|
|
|
|
volatile bool ble_connection_st;
|
|
volatile bool data_tx_in_progress = false;
|
|
|
|
extern char m_static_passkey[6];
|
|
extern char SERIAL_NO[12];
|
|
extern bool bond_data_delete;
|
|
uint8_t m_reset_status;
|
|
|
|
extern uint8_t m_pd_adc_cnt;
|
|
extern uint16_t m_pd_delay_us;
|
|
extern uint32_t m_life_cycle;
|
|
|
|
/*==============================================================================
|
|
* SUPPRESS WARNINGS
|
|
*============================================================================*/
|
|
#if FEATURE_SECURE_CONNECTION
|
|
static void __attribute__((unused)) suppress_unused_warnings(void)
|
|
{
|
|
(void)m_peer_to_be_deleted;
|
|
(void)roles_str;
|
|
(void)c_addr;
|
|
}
|
|
#endif
|
|
|
|
/*==============================================================================
|
|
* POWER MANAGEMENT
|
|
*============================================================================*/
|
|
static void power_hold_init(void)
|
|
{
|
|
NRF_P0->DIRSET = (1 << 8);
|
|
NRF_P0->OUTSET = (1 << 8);
|
|
}
|
|
|
|
static void power_control_handler(on_off_cont_t device_power_st)
|
|
{
|
|
if (device_power_st == OFF) {
|
|
nrf_gpio_pin_clear(POWER_HOLD);
|
|
DBG_PRINTF("[PWR] OFF\r\n");
|
|
} else if (device_power_st == ON) {
|
|
nrf_gpio_pin_set(POWER_HOLD);
|
|
DBG_PRINTF("[PWR] ON\r\n");
|
|
}
|
|
}
|
|
|
|
/*==============================================================================
|
|
* GPIO INITIALIZATION
|
|
*============================================================================*/
|
|
static void minimal_gpio_init(void)
|
|
{
|
|
nrf_gpio_cfg_input(POWER_BUTTON, NRF_GPIO_PIN_NOPULL);
|
|
nrf_gpio_cfg_output(POWER_HOLD);
|
|
nrf_gpio_pin_set(POWER_HOLD);
|
|
power_gpio_init();
|
|
|
|
DBG_PRINTF("[GPIO] Minimal OK (BTN=%d)\r\n", nrf_gpio_pin_read(POWER_BUTTON));
|
|
}
|
|
|
|
#if !DEBUG_MINIMAL_BOOT
|
|
static void full_gpio_init(void)
|
|
{
|
|
minimal_gpio_init();
|
|
|
|
LED_CONFIG();
|
|
LED_ALLOFF();
|
|
PD_CONFIG();
|
|
PD_ALLOFF();
|
|
trig_r_CONFIG();
|
|
trig_SW(false);
|
|
GAIN_SW_CONFIG();
|
|
AGC_GAIN_SW(false);
|
|
eeprom_control(OFF);
|
|
|
|
DBG_PRINTF("[GPIO] Full OK\r\n");
|
|
}
|
|
#endif
|
|
|
|
/*==============================================================================
|
|
* CONFIGURATION LOADING
|
|
*============================================================================*/
|
|
static void load_default_config(void)
|
|
{
|
|
memset(SERIAL_NO, 0, 16);
|
|
memcpy(SERIAL_NO, "2025MEDIP0001", 13);
|
|
|
|
memset(m_static_passkey, 0, 16);
|
|
memcpy(m_static_passkey, "123456", 6);
|
|
|
|
m_pd_delay_us = 8000;
|
|
m_pd_adc_cnt = 8;
|
|
m_reset_status = 1;
|
|
bond_data_delete = 1;
|
|
|
|
DBG_PRINTF("[CFG] Default (S/N=%s)\r\n", SERIAL_NO);
|
|
}
|
|
|
|
#if !DEBUG_MINIMAL_BOOT
|
|
static void load_eeprom_config(void)
|
|
{
|
|
DBG_PRINTF("[CFG] EEPROM loading...\r\n");
|
|
|
|
eeprom_initialize();
|
|
nrf_delay_us(10);
|
|
eeprom_init_values_read();
|
|
eeprom_uninitialize();
|
|
nrf_delay_us(50);
|
|
|
|
#if BLE_DEV_MODE
|
|
memset(SERIAL_NO, 0, 16);
|
|
memcpy(SERIAL_NO, "2005MEDIP001", 12);
|
|
DBG_PRINTF("[CFG] DEV S/N=%s\r\n", SERIAL_NO);
|
|
#else
|
|
if (!is_valid_serial_no(SERIAL_NO))
|
|
{
|
|
memset(SERIAL_NO, 0, 16);
|
|
memcpy(SERIAL_NO, "2025AAAAP000", 12);
|
|
}
|
|
|
|
char const init_pass[6] = "123456";
|
|
memset(m_static_passkey, 0, 16);
|
|
memcpy(m_static_passkey, (uint8_t *)init_pass, 6);
|
|
#endif
|
|
|
|
m_pd_delay_us = 8000;
|
|
m_pd_adc_cnt = 8;
|
|
m_reset_status = 1;
|
|
bond_data_delete = BLE_DEV_MODE ? 1 : 0;
|
|
|
|
DBG_PRINTF("[CFG] EEPROM OK\r\n");
|
|
}
|
|
#endif
|
|
|
|
/*==============================================================================
|
|
* TIMER CALLBACKS
|
|
*============================================================================*/
|
|
static void main_s(void * p_context);
|
|
static void t_power_off_timeout_handler(void * p_context);
|
|
static void PM_s(void * p_context);
|
|
|
|
static void t_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("[PWR] Off timeout\r\n");
|
|
bsp_indication_set(BSP_INDICATE_USER_STATE_OFF);
|
|
power_control_handler(OFF);
|
|
}
|
|
|
|
static void PM_s(void * p_context)
|
|
{
|
|
UNUSED_PARAMETER(p_context);
|
|
APP_ERROR_CHECK(app_timer_stop(m_PM_timer_id));
|
|
|
|
if (m_reset_status == 5) {
|
|
DBG_PRINTF("[PM] Kill\r\n");
|
|
sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
|
}
|
|
}
|
|
|
|
/*==============================================================================
|
|
* TIMER FUNCTIONS
|
|
*============================================================================*/
|
|
static void timers_init(void)
|
|
{
|
|
ret_code_t err_code = app_timer_init();
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
APP_ERROR_CHECK(app_timer_create(&m_power_on_delay_timer_id, APP_TIMER_MODE_SINGLE_SHOT, main_s));
|
|
APP_ERROR_CHECK(app_timer_create(&m_power_off_delay_timer_id, APP_TIMER_MODE_SINGLE_SHOT, t_power_off_timeout_handler));
|
|
APP_ERROR_CHECK(app_timer_create(&m_PM_timer_id, APP_TIMER_MODE_SINGLE_SHOT, PM_s));
|
|
|
|
main_timer_init();
|
|
battery_timer_init();
|
|
imm_check_timer_init();
|
|
m48_check_timer_init();
|
|
full_agc_timer_init();
|
|
full_agc_send_timer_init();
|
|
mea_send_timer_init();
|
|
power_timer_init();
|
|
|
|
#if FEATURE_PRINTF
|
|
full_timer_init();
|
|
#endif
|
|
}
|
|
|
|
static void 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);
|
|
}
|
|
|
|
/*==============================================================================
|
|
* BLE DFU HANDLER
|
|
*============================================================================*/
|
|
#if BLE_DFU_ENABLED
|
|
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
|
|
{
|
|
switch (event)
|
|
{
|
|
case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:
|
|
DBG_PRINTF("[DFU] Prepare\r\n");
|
|
break;
|
|
case BLE_DFU_EVT_BOOTLOADER_ENTER:
|
|
DBG_PRINTF("[DFU] Enter\r\n");
|
|
break;
|
|
case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED:
|
|
DBG_PRINTF("[DFU] Failed\r\n");
|
|
break;
|
|
case BLE_DFU_EVT_RESPONSE_SEND_ERROR:
|
|
DBG_PRINTF("[DFU] Error\r\n");
|
|
APP_ERROR_CHECK(false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*==============================================================================
|
|
* BLE GAP FUNCTIONS
|
|
*============================================================================*/
|
|
static void gap_params_init(void)
|
|
{
|
|
uint32_t err_code;
|
|
ble_gap_conn_params_t gap_conn_params;
|
|
ble_gap_conn_sec_mode_t sec_mode;
|
|
#if FEATURE_STATIC_PASSKEY
|
|
ble_opt_t ble_opt;
|
|
#endif
|
|
|
|
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
|
|
|
|
err_code = sd_ble_gap_device_name_set(&sec_mode,
|
|
(const uint8_t *) SERIAL_NO,
|
|
strlen(SERIAL_NO));
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
|
|
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
|
|
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
|
|
gap_conn_params.slave_latency = SLAVE_LATENCY;
|
|
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
|
|
|
|
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
#if FEATURE_STATIC_PASSKEY
|
|
ble_opt.gap_opt.passkey.p_passkey = (const uint8_t *)m_static_passkey;
|
|
err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &ble_opt);
|
|
APP_ERROR_CHECK(err_code);
|
|
#endif
|
|
}
|
|
|
|
/*==============================================================================
|
|
* BLE SERVICE FUNCTIONS
|
|
*============================================================================*/
|
|
static void nrf_qwr_error_handler(uint32_t nrf_error)
|
|
{
|
|
APP_ERROR_HANDLER(nrf_error);
|
|
}
|
|
|
|
/* Forward declaration for async MAA TX ready handler */
|
|
extern bool maa_async_on_tx_ready(void);
|
|
extern bool maa_async_is_busy(void);
|
|
|
|
static void nus_data_handler(ble_nus_evt_t * p_evt)
|
|
{
|
|
if (p_evt->type == BLE_NUS_EVT_RX_DATA)
|
|
{
|
|
cmd_type_t = CMD_BLE;
|
|
DBG_PRINTF("[NUS] RX len=%d\r\n", p_evt->params.rx_data.length);
|
|
received_command_process(p_evt->params.rx_data.p_data, CMD_BLE, p_evt->params.rx_data.length);
|
|
}
|
|
else if (p_evt->type == BLE_NUS_EVT_TX_RDY)
|
|
{
|
|
/* BLE TX buffer has space - continue async MAA transmission */
|
|
if (maa_async_is_busy())
|
|
{
|
|
maa_async_on_tx_ready();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void services_init(void)
|
|
{
|
|
uint32_t err_code;
|
|
ble_nus_init_t nus_init;
|
|
nrf_ble_qwr_init_t qwr_init = {0};
|
|
|
|
qwr_init.error_handler = nrf_qwr_error_handler;
|
|
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
memset(&nus_init, 0, sizeof(nus_init));
|
|
nus_init.data_handler = nus_data_handler;
|
|
err_code = ble_nus_init(&m_nus, &nus_init);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
#if BLE_DFU_ENABLED
|
|
ble_dfu_buttonless_init_t dfus_init = {0};
|
|
dfus_init.evt_handler = ble_dfu_evt_handler;
|
|
err_code = ble_dfu_buttonless_init(&dfus_init);
|
|
APP_ERROR_CHECK(err_code);
|
|
#endif
|
|
}
|
|
|
|
/*==============================================================================
|
|
* BLE CONNECTION PARAMETERS
|
|
*============================================================================*/
|
|
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
|
|
{
|
|
if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
|
|
{
|
|
uint32_t err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
|
|
static void conn_params_error_handler(uint32_t nrf_error)
|
|
{
|
|
APP_ERROR_HANDLER(nrf_error);
|
|
}
|
|
|
|
static void conn_params_init(void)
|
|
{
|
|
uint32_t err_code;
|
|
ble_conn_params_init_t cp_init;
|
|
|
|
memset(&cp_init, 0, sizeof(cp_init));
|
|
cp_init.p_conn_params = NULL;
|
|
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
|
|
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
|
|
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
|
|
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
|
|
cp_init.disconnect_on_fail = false;
|
|
cp_init.evt_handler = on_conn_params_evt;
|
|
cp_init.error_handler = conn_params_error_handler;
|
|
|
|
err_code = ble_conn_params_init(&cp_init);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
|
|
/*==============================================================================
|
|
* BLE STACK INITIALIZATION
|
|
*============================================================================*/
|
|
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context);
|
|
|
|
static void ble_stack_init(void)
|
|
{
|
|
ret_code_t err_code;
|
|
|
|
err_code = nrf_sdh_enable_request();
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
uint32_t ram_start = 0;
|
|
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
err_code = nrf_sdh_ble_enable(&ram_start);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
|
|
}
|
|
|
|
/*==============================================================================
|
|
* BLE GATT
|
|
*============================================================================*/
|
|
void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
|
|
{
|
|
if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
|
|
{
|
|
m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
|
|
}
|
|
}
|
|
|
|
static void gatt_init(void)
|
|
{
|
|
ret_code_t err_code;
|
|
|
|
err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
|
|
/*==============================================================================
|
|
* BLE ADVERTISING
|
|
*============================================================================*/
|
|
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
|
|
{
|
|
switch (ble_adv_evt)
|
|
{
|
|
case BLE_ADV_EVT_FAST:
|
|
bsp_indication_set(BSP_INDICATE_ADVERTISING);
|
|
DBG_PRINTF("[ADV] Fast\r\n");
|
|
break;
|
|
case BLE_ADV_EVT_IDLE:
|
|
DBG_PRINTF("[ADV] Idle\r\n");
|
|
go_sleep_mode_enter = true;
|
|
main_timer_start();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void advertising_init(void)
|
|
{
|
|
uint32_t err_code;
|
|
ble_advertising_init_t init;
|
|
|
|
memset(&init, 0, sizeof(init));
|
|
|
|
init.advdata.name_type = BLE_ADVDATA_FULL_NAME;
|
|
init.advdata.include_appearance = true;
|
|
|
|
#if FEATURE_NO_SLEEP
|
|
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
|
|
#else
|
|
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
|
|
#endif
|
|
|
|
init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
|
|
init.srdata.uuids_complete.p_uuids = m_adv_uuids;
|
|
|
|
init.config.ble_adv_fast_enabled = true;
|
|
init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
|
|
init.config.ble_adv_fast_timeout = APP_ADV_DURATION;
|
|
init.evt_handler = on_adv_evt;
|
|
|
|
err_code = ble_advertising_init(&m_advertising, &init);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
|
|
}
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
static void delete_bonds(void)
|
|
{
|
|
ret_code_t err_code;
|
|
DBG_PRINTF("[PM] Erase bonds\r\n");
|
|
err_code = pm_peers_delete();
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
|
|
static void advertising_start(bool erase_bonds_flag)
|
|
{
|
|
if (erase_bonds_flag == true)
|
|
{
|
|
bond_data_delete = false;
|
|
delete_bonds();
|
|
}
|
|
else
|
|
{
|
|
ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
#else
|
|
static void advertising_start(void)
|
|
{
|
|
uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
#endif
|
|
|
|
/*==============================================================================
|
|
* BLE SECURITY (PEER MANAGER)
|
|
*============================================================================*/
|
|
#if FEATURE_SECURE_CONNECTION
|
|
static void pm_evt_handler(pm_evt_t const * p_evt)
|
|
{
|
|
pm_peer_data_bonding_t peer_bonding_data;
|
|
uint32_t return_code;
|
|
ret_code_t err_code;
|
|
|
|
pm_handler_on_pm_evt(p_evt);
|
|
pm_handler_disconnect_on_sec_failure(p_evt);
|
|
pm_handler_flash_clean(p_evt);
|
|
ble_security_quick_pm_handler(p_evt);
|
|
|
|
switch (p_evt->evt_id)
|
|
{
|
|
case PM_EVT_CONN_SEC_SUCCEEDED:
|
|
{
|
|
pm_conn_sec_status_t conn_sec_status;
|
|
err_code = pm_conn_sec_status_get(p_evt->conn_handle, &conn_sec_status);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
if (conn_sec_status.mitm_protected || BLE_DEV_MODE)
|
|
{
|
|
DBG_PRINTF("[PM] Secured\r\n");
|
|
ble_connection_st = 1;
|
|
battery_timer_start();
|
|
}
|
|
else
|
|
{
|
|
DBG_PRINTF("[PM] Sec FAIL\r\n");
|
|
err_code = pm_peer_id_get(m_conn_handle, &m_peer_to_be_deleted);
|
|
APP_ERROR_CHECK(err_code);
|
|
err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_FAILED:
|
|
DBG_PRINTF("[PM] Sec failed\r\n");
|
|
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING)
|
|
{
|
|
err_code = pm_conn_secure(p_evt->conn_handle, true);
|
|
if (err_code != NRF_ERROR_INVALID_STATE)
|
|
{
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PM_EVT_PEERS_DELETE_SUCCEEDED:
|
|
advertising_start(false);
|
|
break;
|
|
|
|
case PM_EVT_CONN_SEC_CONFIG_REQ:
|
|
{
|
|
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
|
|
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
|
|
}
|
|
break;
|
|
|
|
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
|
|
{
|
|
return_code = pm_peer_data_bonding_load(p_evt->peer_id, &peer_bonding_data);
|
|
if (return_code == NRF_SUCCESS)
|
|
{
|
|
memcpy(c_addr, peer_bonding_data.peer_ble_id.id_addr_info.addr, sizeof(c_addr));
|
|
}
|
|
m_reset_status = 10;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void peer_manager_init(void)
|
|
{
|
|
ret_code_t err_code;
|
|
|
|
ble_security_quick_init(BLE_DEV_MODE);
|
|
|
|
err_code = pm_register(pm_evt_handler);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
DBG_PRINTF("[PM] Init (mode=%d)\r\n", BLE_DEV_MODE);
|
|
}
|
|
#endif
|
|
|
|
/*==============================================================================
|
|
* BLE EVENT HANDLER
|
|
*============================================================================*/
|
|
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
|
|
{
|
|
uint32_t err_code;
|
|
#if FEATURE_STATIC_PASSKEY
|
|
ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
|
|
#endif
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
pm_handler_secure_on_connection(p_ble_evt);
|
|
#endif
|
|
|
|
switch (p_ble_evt->header.evt_id)
|
|
{
|
|
case BLE_GAP_EVT_DISCONNECTED:
|
|
DBG_PRINTF("[BLE] Disconnected\r\n");
|
|
ble_connection_st = 0;
|
|
m_conn_handle = BLE_CONN_HANDLE_INVALID;
|
|
m_tx_in_progress = false;
|
|
|
|
if (device_status == true)
|
|
{
|
|
LED_ALLOFF();
|
|
PD_ALLOFF();
|
|
if (device_sleep_mode() == 0)
|
|
{
|
|
device_status = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BLE_GAP_EVT_CONNECTED:
|
|
DBG_PRINTF("[BLE] Connected\r\n");
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
ble_connection_st = 1;
|
|
battery_timer_start();
|
|
#endif
|
|
|
|
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
|
|
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 8);
|
|
|
|
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
#if DEBUG_MINIMAL_BOOT
|
|
DBG_PRINTF("[BLE] Minimal mode\r\n");
|
|
#endif
|
|
break;
|
|
|
|
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
|
|
{
|
|
ble_gap_phys_t const phys = {
|
|
.rx_phys = BLE_GAP_PHY_AUTO,
|
|
.tx_phys = BLE_GAP_PHY_AUTO,
|
|
};
|
|
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
break;
|
|
|
|
case BLE_GATTC_EVT_TIMEOUT:
|
|
case BLE_GATTS_EVT_TIMEOUT:
|
|
DBG_PRINTF("[BLE] Timeout\r\n");
|
|
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gap_evt.conn_handle,
|
|
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
|
APP_ERROR_CHECK(err_code);
|
|
break;
|
|
|
|
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
|
|
#if !FEATURE_SECURE_CONNECTION
|
|
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
|
|
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
|
|
NULL, NULL);
|
|
APP_ERROR_CHECK(err_code);
|
|
#endif
|
|
break;
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
case BLE_GAP_EVT_PASSKEY_DISPLAY:
|
|
{
|
|
char passkey[PASSKEY_LENGTH + 1];
|
|
memcpy(passkey, p_ble_evt->evt.gap_evt.params.passkey_display.passkey, PASSKEY_LENGTH);
|
|
passkey[PASSKEY_LENGTH] = 0;
|
|
DBG_PRINTF("[BLE] Passkey: %s\r\n", passkey);
|
|
}
|
|
break;
|
|
|
|
case BLE_GAP_EVT_AUTH_KEY_REQUEST:
|
|
#if FEATURE_STATIC_PASSKEY
|
|
if (p_gap_evt->params.auth_key_request.key_type == BLE_GAP_AUTH_KEY_TYPE_PASSKEY)
|
|
{
|
|
err_code = sd_ble_gap_auth_key_reply(p_gap_evt->conn_handle,
|
|
BLE_GAP_AUTH_KEY_TYPE_PASSKEY,
|
|
(const uint8_t *)m_static_passkey);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
|
|
break;
|
|
#endif
|
|
|
|
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
|
|
m_tx_in_progress = false;
|
|
m_tx_complete_pending = false; /* Signal TX completion to waiting functions */
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*==============================================================================
|
|
* BSP EVENT HANDLER
|
|
*============================================================================*/
|
|
void bsp_event_handler(bsp_event_t event)
|
|
{
|
|
uint32_t err_code;
|
|
|
|
switch (event)
|
|
{
|
|
case BSP_EVENT_SLEEP:
|
|
DBG_PRINTF("[BSP] Sleep\r\n");
|
|
go_sleep_mode_enter = true;
|
|
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;
|
|
}
|
|
break;
|
|
|
|
case BSP_EVENT_WHITELIST_OFF:
|
|
if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
|
|
{
|
|
err_code = ble_advertising_restart_without_whitelist(&m_advertising);
|
|
if (err_code != NRF_ERROR_INVALID_STATE)
|
|
{
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BSP_EVENT_POWER_CONTROL:
|
|
if (processing == false)
|
|
{
|
|
DBG_PRINTF("[BSP] Power\r\n");
|
|
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
|
|
go_device_power_off = true;
|
|
main_timer_start();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*==============================================================================
|
|
* UTILITY FUNCTIONS
|
|
*============================================================================*/
|
|
static void buttons_leds_init(bool * p_erase_bonds)
|
|
{
|
|
bsp_event_t startup_event;
|
|
|
|
uint32_t err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
err_code = bsp_btn_ble_init(NULL, &startup_event);
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
*p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
|
|
}
|
|
|
|
static void log_init(void)
|
|
{
|
|
ret_code_t err_code = NRF_LOG_INIT(NULL);
|
|
APP_ERROR_CHECK(err_code);
|
|
NRF_LOG_DEFAULT_BACKENDS_INIT();
|
|
}
|
|
|
|
void uart_event_handle(app_uart_evt_t * p_event)
|
|
{
|
|
static uint8_t data_array[BLE_NUS_MAX_DATA_LEN] = {0};
|
|
static uint8_t index = 0;
|
|
|
|
switch (p_event->evt_type)
|
|
{
|
|
case APP_UART_DATA_READY:
|
|
UNUSED_VARIABLE(app_uart_get(&data_array[index]));
|
|
index++;
|
|
|
|
if ((data_array[index - 1] == '\n') ||
|
|
(data_array[index - 1] == '\r') ||
|
|
(index >= m_ble_nus_max_data_len))
|
|
{
|
|
if (index > 1)
|
|
{
|
|
cmd_type_t = CMD_UART;
|
|
received_command_process(data_array, CMD_UART, index);
|
|
}
|
|
index = 0;
|
|
}
|
|
break;
|
|
|
|
case APP_UART_COMMUNICATION_ERROR:
|
|
break;
|
|
|
|
case APP_UART_FIFO_ERROR:
|
|
APP_ERROR_HANDLER(p_event->data.error_code);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void uart_init(void)
|
|
{
|
|
uint32_t err_code;
|
|
app_uart_comm_params_t const comm_params =
|
|
{
|
|
.rx_pin_no = RX_PIN_NUMBER,
|
|
.tx_pin_no = TX_PIN_NUMBER,
|
|
.rts_pin_no = RTS_PIN_NUMBER,
|
|
.cts_pin_no = CTS_PIN_NUMBER,
|
|
.flow_control = APP_UART_FLOW_CONTROL_DISABLED,
|
|
.use_parity = false,
|
|
#if defined (UART_PRESENT)
|
|
.baud_rate = NRF_UART_BAUDRATE_1000000
|
|
#else
|
|
.baud_rate = NRF_UARTE_BAUDRATE_1000000
|
|
#endif
|
|
};
|
|
|
|
APP_UART_FIFO_INIT(&comm_params,
|
|
UART_RX_BUF_SIZE,
|
|
UART_TX_BUF_SIZE,
|
|
uart_event_handle,
|
|
APP_IRQ_PRIORITY_LOWEST,
|
|
err_code);
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
|
|
static void power_management_init(void)
|
|
{
|
|
ret_code_t err_code;
|
|
err_code = nrf_pwr_mgmt_init();
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
|
|
static void idle_state_handle(void)
|
|
{
|
|
#if FEATURE_SECURE_CONNECTION
|
|
ret_code_t err_code;
|
|
err_code = nrf_ble_lesc_request_handler();
|
|
APP_ERROR_CHECK(err_code);
|
|
#endif
|
|
|
|
if (NRF_LOG_PROCESS() == false)
|
|
{
|
|
nrf_pwr_mgmt_run();
|
|
}
|
|
}
|
|
|
|
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
|
|
{
|
|
app_error_handler(DEAD_BEEF, line_num, p_file_name);
|
|
}
|
|
|
|
void sleep_mode_enter(void)
|
|
{
|
|
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
|
|
DBG_PRINTF("[SYS] Sleep\r\n");
|
|
APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL));
|
|
}
|
|
|
|
void device_power_off(void)
|
|
{
|
|
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
|
|
APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL));
|
|
}
|
|
|
|
/*==============================================================================
|
|
* DATA TRANSMISSION
|
|
*============================================================================*/
|
|
void data_tx_handler(char const *p_data_to_send)
|
|
{
|
|
if (m_tx_in_progress) return;
|
|
|
|
char const *p_end_char = strchr(p_data_to_send, '\r');
|
|
if (p_end_char == NULL) return;
|
|
|
|
uint16_t data_len = p_end_char - p_data_to_send;
|
|
if (data_len > (BLE_NUS_MAX_DATA_LEN - 2)) return;
|
|
|
|
memcpy(m_tx_buffer, p_data_to_send, data_len);
|
|
uint16_t crc = crc16_compute(m_tx_buffer, data_len, NULL);
|
|
m_tx_buffer[data_len] = (uint8_t)(crc & 0xFF);
|
|
m_tx_buffer[data_len + 1] = (uint8_t)((crc >> 8) & 0xFF);
|
|
m_tx_len = data_len + 2;
|
|
|
|
if (ble_connection_st == BLE_CONNECTED_ST)
|
|
{
|
|
m_tx_in_progress = true;
|
|
uint32_t err_code = ble_nus_data_send(&m_nus, m_tx_buffer, &m_tx_len, m_conn_handle);
|
|
|
|
if (err_code == NRF_SUCCESS) {
|
|
// OK
|
|
} else if (err_code == NRF_ERROR_RESOURCES) {
|
|
// Retry later
|
|
} else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND) {
|
|
ble_connection_st = BLE_DISCONNECTED_ST;
|
|
m_conn_handle = BLE_CONN_HANDLE_INVALID;
|
|
m_tx_in_progress = false;
|
|
} else {
|
|
m_tx_in_progress = false;
|
|
APP_ERROR_CHECK(err_code);
|
|
}
|
|
}
|
|
}
|
|
|
|
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value)
|
|
{
|
|
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
|
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
|
|
|
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
|
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
|
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
|
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
|
buffer[5] = (uint8_t)(value & 0xFF);
|
|
buffer[4] = (uint8_t)((value >> 8) & 0xFF);
|
|
}
|
|
|
|
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length)
|
|
{
|
|
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
|
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
|
|
|
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
|
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
|
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
|
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
buffer[4 + i * 2 + 1] = (uint8_t)(data_array[i] & 0xFF);
|
|
buffer[4 + i * 2] = (uint8_t)((data_array[i] >> 8) & 0xFF);
|
|
}
|
|
}
|
|
|
|
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length)
|
|
{
|
|
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
|
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
|
|
|
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
|
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
|
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
|
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
buffer[4 + i] = (uint8_t)(data_array[i] & 0xFF);
|
|
}
|
|
}
|
|
|
|
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length)
|
|
{
|
|
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
|
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
|
|
|
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
|
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
|
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
|
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
buffer[4 + i] = (uint8_t)(data_ascii[i] & 0xFF);
|
|
}
|
|
}
|
|
|
|
void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length)
|
|
{
|
|
uint32_t err_code;
|
|
static uint8_t tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0};
|
|
|
|
if (ble_connection_st == 0) return;
|
|
|
|
data_tx_in_progress = true;
|
|
|
|
if (length * sizeof(uint16_t) > (BLE_NUS_MAX_DATA_LEN - 2)) return;
|
|
|
|
if (ble_connection_st == BLE_CONNECTED_ST)
|
|
{
|
|
memcpy(tx_buffer, ble_bin_buff, length * sizeof(uint16_t));
|
|
uint16_t crc = crc16_compute(tx_buffer, length * sizeof(uint16_t), NULL);
|
|
tx_buffer[length * sizeof(uint16_t)] = (uint8_t)(crc & 0xFF);
|
|
tx_buffer[length * sizeof(uint16_t) + 1] = (uint8_t)((crc >> 8) & 0xFF);
|
|
|
|
uint16_t total_len = length * sizeof(uint16_t) + 2;
|
|
|
|
do {
|
|
err_code = ble_nus_data_send(&m_nus, tx_buffer, &total_len, m_conn_handle);
|
|
if ((err_code != NRF_ERROR_INVALID_STATE) &&
|
|
(err_code != NRF_ERROR_RESOURCES) &&
|
|
(err_code != NRF_ERROR_NOT_FOUND))
|
|
{
|
|
nrf_delay_ms(10);
|
|
}
|
|
APP_ERROR_CHECK(err_code);
|
|
} while (err_code == NRF_ERROR_RESOURCES);
|
|
|
|
data_tx_in_progress = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Safe BLE binary transmission with proper error handling
|
|
*
|
|
* Unlike binary_tx_handler which may assert on errors,
|
|
* this function properly handles NRF_ERROR_RESOURCES by retrying
|
|
* and returns gracefully on connection errors.
|
|
*
|
|
* @param ble_bin_buff Data buffer to send
|
|
* @param length Length in uint16_t words (actual bytes = length * 2)
|
|
*/
|
|
|
|
/**
|
|
* @brief Delay for BLE operations - simple and safe
|
|
*
|
|
* Uses pure nrf_delay_ms() which is interrupt-safe.
|
|
* BLE stack runs on interrupts, so TX completion events
|
|
* are processed even during delay.
|
|
*
|
|
* NOTE: Do NOT use __WFE() or sd_app_evt_wait() here!
|
|
* They conflict with SoftDevice power management.
|
|
*
|
|
* @param ms Delay in milliseconds
|
|
*/
|
|
void dr_sd_delay_ms(uint32_t ms)
|
|
{
|
|
if (ms == 0) return;
|
|
nrf_delay_ms(ms);
|
|
}
|
|
|
|
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length)
|
|
{
|
|
uint32_t err_code;
|
|
static uint8_t tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0};
|
|
uint16_t retry_count = 0;
|
|
const uint16_t MAX_RETRIES = 100; /* Max retries (~500ms at 5ms each) */
|
|
|
|
if (ble_connection_st == 0) return;
|
|
|
|
data_tx_in_progress = true;
|
|
|
|
if (length * sizeof(uint16_t) > (BLE_NUS_MAX_DATA_LEN - 2)) {
|
|
data_tx_in_progress = false;
|
|
return;
|
|
}
|
|
|
|
if (ble_connection_st == BLE_CONNECTED_ST)
|
|
{
|
|
memcpy(tx_buffer, ble_bin_buff, length * sizeof(uint16_t));
|
|
uint16_t crc = crc16_compute(tx_buffer, length * sizeof(uint16_t), NULL);
|
|
tx_buffer[length * sizeof(uint16_t)] = (uint8_t)(crc & 0xFF);
|
|
tx_buffer[length * sizeof(uint16_t) + 1] = (uint8_t)((crc >> 8) & 0xFF);
|
|
|
|
uint16_t total_len = length * sizeof(uint16_t) + 2;
|
|
|
|
/* Retry loop - allow SoftDevice to process events between retries */
|
|
do {
|
|
uint16_t send_len = total_len; /* MUST reset each iteration - ble_nus_data_send modifies it! */
|
|
err_code = ble_nus_data_send(&m_nus, tx_buffer, &send_len, m_conn_handle);
|
|
|
|
if (err_code == NRF_SUCCESS) {
|
|
/* TX queued successfully */
|
|
break;
|
|
} else if (err_code == NRF_ERROR_RESOURCES) {
|
|
/* BLE TX queue full - wait for connection event to complete TX */
|
|
/* Use small delay to allow BLE stack to process TX complete events */
|
|
nrf_delay_ms(5); /* Wait ~5ms for TX slot to free up */
|
|
retry_count++;
|
|
} else if (err_code == NRF_ERROR_INVALID_STATE ||
|
|
err_code == NRF_ERROR_NOT_FOUND) {
|
|
DBG_PRINTF("[BLE TX] Disconnected\r\n");
|
|
data_tx_in_progress = false;
|
|
return;
|
|
} else {
|
|
DBG_PRINTF("[BLE TX] Err:0x%X\r\n", err_code);
|
|
data_tx_in_progress = false;
|
|
return;
|
|
}
|
|
} while (retry_count < MAX_RETRIES);
|
|
|
|
if (retry_count >= MAX_RETRIES) {
|
|
DBG_PRINTF("[BLE TX] FAIL %u retries\r\n", retry_count);
|
|
data_tx_in_progress = false;
|
|
/* Don't set ble_connection_st = 0 here - just drop this packet and continue */
|
|
return;
|
|
}
|
|
|
|
data_tx_in_progress = false;
|
|
}
|
|
}
|
|
|
|
/*==============================================================================
|
|
* MAIN TIMER CALLBACK (BUTTON HANDLING)
|
|
*============================================================================*/
|
|
static void main_s(void * p_context)
|
|
{
|
|
UNUSED_PARAMETER(p_context);
|
|
APP_ERROR_CHECK(app_timer_stop(m_power_on_delay_timer_id));
|
|
|
|
bool button_released = nrf_gpio_pin_read(POWER_BUTTON);
|
|
|
|
if (button_released)
|
|
{
|
|
if ((cnt_s < 150) && (m_reset_status != 2))
|
|
{
|
|
DBG_PRINTF("[BTN] Short->OFF\r\n");
|
|
bsp_indication_set(BSP_INDICATE_USER_STATE_OFF);
|
|
power_control_handler(OFF);
|
|
cnt_s = 0;
|
|
}
|
|
else if (cnt_s > 1000)
|
|
{
|
|
DBG_PRINTF("[BTN] Long->Reset\r\n");
|
|
power_control_handler(ON);
|
|
nrf_delay_ms(100);
|
|
bond_data_delete = true;
|
|
eeprom_initialize();
|
|
nrf_delay_ms(10);
|
|
eeprom_write_byte(0x0060, (uint8_t)bond_data_delete);
|
|
nrf_delay_ms(20);
|
|
const char pass_init[6] = "123456";
|
|
eeprom_write_encrypted(0x0020, (uint8_t *)pass_init, 6);
|
|
nrf_delay_ms(10);
|
|
eeprom_init_values_read();
|
|
eeprom_uninitialize();
|
|
nrf_delay_ms(1000);
|
|
go_device_power_off = true;
|
|
main_timer_start();
|
|
}
|
|
else if (cnt_s > 150 || (m_reset_status == 2))
|
|
{
|
|
DBG_PRINTF("[BTN] Boot (cnt=%d)\r\n", cnt_s);
|
|
device_reset = false;
|
|
|
|
power_control_handler(ON);
|
|
battery_timer_start();
|
|
|
|
#if DEBUG_MINIMAL_BOOT
|
|
DBG_PRINTF("[BOOT] Minimal\r\n");
|
|
#if FEATURE_SECURE_CONNECTION
|
|
advertising_start(erase_bonds);
|
|
#else
|
|
advertising_start();
|
|
#endif
|
|
DBG_PRINTF("[BOOT] ADV started\r\n");
|
|
#else
|
|
DBG_PRINTF("[BOOT] Full\r\n");
|
|
icm42670_init();
|
|
nrf_delay_ms(2);
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
advertising_start(erase_bonds);
|
|
#else
|
|
advertising_start();
|
|
#endif
|
|
|
|
ada2200_init();
|
|
nrf_delay_ms(1);
|
|
mcp4725_init();
|
|
nrf_delay_ms(1);
|
|
ad5272_i2c_init();
|
|
|
|
m_reset_status = 1;
|
|
eeprom_write_byte(0x0065, m_reset_status);
|
|
nrf_delay_ms(10);
|
|
eeprom_init_values_read();
|
|
#endif
|
|
m_reset_status = 1;
|
|
DBG_PRINTF("[BOOT] Ready\r\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cnt_s++;
|
|
device_reset = false;
|
|
|
|
if (cnt_s == 150) {
|
|
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
|
|
DBG_PRINTF("[BTN] 1.5s\r\n");
|
|
}
|
|
|
|
timers_start();
|
|
}
|
|
}
|
|
|
|
/*==============================================================================
|
|
* MAIN FUNCTION
|
|
*============================================================================*/
|
|
int main(void)
|
|
{
|
|
bool erase_bonds_local = false;
|
|
|
|
// PHASE 0: 전원 유지
|
|
power_hold_init();
|
|
|
|
// PHASE 1: 기본 시스템
|
|
if (power_off_duble_prohibit) return 0;
|
|
cnt_s = 0;
|
|
|
|
uart_init();
|
|
log_init();
|
|
|
|
DBG_PRINTF("\r\n========================================\r\n");
|
|
DBG_PRINTF(" Medithings v1.17 [%s]\r\n", DEBUG_MINIMAL_BOOT ? "MIN" : "FULL");
|
|
DBG_PRINTF("========================================\r\n");
|
|
DBG_PRINTF("[0] PWR_HOLD\r\n");
|
|
|
|
// PHASE 2: GPIO
|
|
DBG_PRINTF("[1] GPIO\r\n");
|
|
#if DEBUG_MINIMAL_BOOT
|
|
minimal_gpio_init();
|
|
#else
|
|
full_gpio_init();
|
|
#endif
|
|
|
|
info4 = false;
|
|
|
|
// PHASE 3: 타이머
|
|
DBG_PRINTF("[2] Timers\r\n");
|
|
timers_init();
|
|
|
|
// PHASE 4: 설정
|
|
DBG_PRINTF("[3] Config\r\n");
|
|
#if DEBUG_MINIMAL_BOOT
|
|
load_default_config();
|
|
#else
|
|
load_eeprom_config();
|
|
#endif
|
|
|
|
// PHASE 5: 버튼/LED
|
|
DBG_PRINTF("[4] Buttons\r\n");
|
|
buttons_leds_init(&erase_bonds_local);
|
|
|
|
#if FEATURE_SECURE_CONNECTION
|
|
erase_bonds = erase_bonds_local;
|
|
#endif
|
|
|
|
// PHASE 6: BLE
|
|
DBG_PRINTF("[5] BLE\r\n");
|
|
|
|
power_management_init();
|
|
DBG_PRINTF(" pwr OK\r\n");
|
|
|
|
ble_stack_init();
|
|
DBG_PRINTF(" stack OK\r\n");
|
|
|
|
gap_params_init();
|
|
DBG_PRINTF(" gap OK\r\n");
|
|
|
|
gatt_init();
|
|
DBG_PRINTF(" gatt OK\r\n");
|
|
|
|
services_init();
|
|
DBG_PRINTF(" svc OK\r\n");
|
|
|
|
advertising_init();
|
|
DBG_PRINTF(" adv OK\r\n");
|
|
|
|
conn_params_init();
|
|
DBG_PRINTF(" conn OK\r\n");
|
|
|
|
// PHASE 7: 보안
|
|
DBG_PRINTF("[6] Security\r\n");
|
|
#if FEATURE_SECURE_CONNECTION
|
|
peer_manager_init();
|
|
#endif
|
|
|
|
// PHASE 8: 완료
|
|
DBG_PRINTF("\r\n========================================\r\n");
|
|
DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO);
|
|
DBG_PRINTF("========================================\r\n\r\n");
|
|
|
|
// PHASE 9: 시작
|
|
timers_start();
|
|
|
|
// MAIN LOOP
|
|
for (;;)
|
|
{
|
|
idle_state_handle();
|
|
}
|
|
}
|