initial commit

This commit is contained in:
jhChun
2026-04-08 16:58:54 +09:00
commit 82e33d8bf9
2578 changed files with 1590432 additions and 0 deletions

View File

@@ -0,0 +1,615 @@
/*******************************************************************************
* @file ble_core.c
* @brief BLE Core Functions - Stack, GAP, GATT, Advertising
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details BLE stack initialization, GAP/GATT configuration, advertising.
******************************************************************************/
#include "ble_core.h"
#include "ble_data_tx.h"
#include "power_ctrl.h"
#include "device_config.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 "ble_nus.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 "app_error.h"
#include "bsp.h"
#include "debug_print.h"
#include "main_timer.h"
#include "battery_saadc.h"
#include "measurements.h"
#include "power_control.h"
#include "main.h"
#include <cmd_parse.h>
#if FEATURE_SECURE_CONNECTION
#include "peer_manager.h"
#include "peer_manager_handler.h"
#include "nrf_ble_lesc.h"
#endif
#if BLE_DFU_ENABLED
#include "ble_dfu.h"
#include "nrf_pwr_mgmt.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"
#include "ble_conn_state.h"
#endif
#include <string.h>
/*==============================================================================
* BLE SERVICE 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);
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;
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}
};
/*==============================================================================
* FORWARD DECLARATIONS
*============================================================================*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context);
static void on_adv_evt(ble_adv_evt_t ble_adv_evt);
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt);
static void conn_params_error_handler(uint32_t nrf_error);
static void nus_data_handler(ble_nus_evt_t * p_evt);
static void nrf_qwr_error_handler(uint32_t nrf_error);
void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt);
#if BLE_DFU_ENABLED
static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event);
static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void * p_context);
static void advertising_config_get(ble_adv_modes_config_t * p_config);
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event);
#endif
/*==============================================================================
* BLE STACK INITIALIZATION
*============================================================================*/
void ble_stack_init(void)
{
ret_code_t err_code;
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
/* Configure BLE stack using default settings */
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);
/* Enable BLE stack */
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
/* Register BLE event handler */
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
/*==============================================================================
* GAP INITIALIZATION
*============================================================================*/
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);
/* Set device name from EEPROM serial number */
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *) SERIAL_NO,
strlen(SERIAL_NO));
APP_ERROR_CHECK(err_code);
/* Configure preferred connection parameters */
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
/* Set static passkey for pairing */
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
}
/*==============================================================================
* GATT INITIALIZATION
*============================================================================*/
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;
}
}
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);
}
/*==============================================================================
* CONNECTION PARAMETERS
*============================================================================*/
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
uint32_t err_code;
if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
{
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);
}
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);
}
/*==============================================================================
* NUS DATA HANDLER
*============================================================================*/
extern which_cmd_t cmd_type_t;
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("recv :%s \n", p_evt->params.rx_data.p_data);
DBG_PRINTF("length %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);
}
}
/*==============================================================================
* QWR ERROR HANDLER
*============================================================================*/
static void nrf_qwr_error_handler(uint32_t nrf_error)
{
APP_ERROR_HANDLER(nrf_error);
}
/*==============================================================================
* DFU HANDLERS
*============================================================================*/
#if BLE_DFU_ENABLED
NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);
NRF_SDH_STATE_OBSERVER(m_buttonless_dfu_state_obs, 0) =
{
.handler = buttonless_dfu_sdh_state_observer,
};
static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
{
switch (event)
{
case NRF_PWR_MGMT_EVT_PREPARE_DFU:
DBG_PRINTF("Power management wants to reset to DFU mode.\r\n");
break;
default:
return true;
}
DBG_PRINTF("Power management allowed to reset to DFU mode.\r\n");
return true;
}
static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void * p_context)
{
if (state == NRF_SDH_EVT_STATE_DISABLED)
{
nrf_power_gpregret2_set(BOOTLOADER_DFU_SKIP_CRC);
nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
}
}
static void advertising_config_get(ble_adv_modes_config_t * p_config)
{
memset(p_config, 0, sizeof(ble_adv_modes_config_t));
p_config->ble_adv_fast_enabled = true;
p_config->ble_adv_fast_interval = APP_ADV_INTERVAL;
p_config->ble_adv_fast_timeout = APP_ADV_DURATION;
}
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
{
switch (event)
{
case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:
{
DBG_PRINTF("Device is preparing to enter bootloader mode.\r\n");
ble_adv_modes_config_t config;
advertising_config_get(&config);
config.ble_adv_on_disconnect_disabled = true;
ble_advertising_modes_config_set(&m_advertising, &config);
uint32_t conn_count = ble_conn_state_for_each_connected(disconnect, NULL);
DBG_PRINTF("Disconnected %d links.\r\n", conn_count);
break;
}
case BLE_DFU_EVT_BOOTLOADER_ENTER:
DBG_PRINTF("Device will enter bootloader mode.\r\n");
break;
case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED:
DBG_PRINTF("Request to enter bootloader mode failed asynchroneously.\r\n");
break;
case BLE_DFU_EVT_RESPONSE_SEND_ERROR:
DBG_PRINTF("Request to send a response to client failed.\r\n");
APP_ERROR_CHECK(false);
break;
default:
DBG_PRINTF("Unknown event from ble_dfu_buttonless.\r\n");
break;
}
}
void dfu_init(void)
{
ret_code_t err_code = ble_dfu_buttonless_async_svci_init();
APP_ERROR_CHECK(err_code);
}
#endif
/*==============================================================================
* SERVICES INITIALIZATION
*============================================================================*/
void services_init(void)
{
uint32_t err_code;
ble_nus_init_t nus_init;
nrf_ble_qwr_init_t qwr_init = {0};
/* Initialize Queued Write Module */
qwr_init.error_handler = nrf_qwr_error_handler;
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
APP_ERROR_CHECK(err_code);
/* Initialize Nordic UART Service */
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
/* Initialize DFU service */
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
}
/*==============================================================================
* WRAPPER FUNCTIONS FOR STATIC VARIABLES
*============================================================================*/
uint32_t ble_nus_data_send_wrapper(uint8_t *p_data, uint16_t *p_length, uint16_t conn_handle)
{
return ble_nus_data_send(&m_nus, p_data, p_length, conn_handle);
}
uint32_t ble_advertising_restart_without_whitelist_wrapper(void)
{
return ble_advertising_restart_without_whitelist(&m_advertising);
}
/*==============================================================================
* ADVERTISING
*============================================================================*/
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
uint32_t err_code;
switch (ble_adv_evt)
{
case BLE_ADV_EVT_FAST:
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
APP_ERROR_CHECK(err_code);
break;
case BLE_ADV_EVT_IDLE:
/* Advertising timeout - enter sleep mode */
go_sleep_mode_enter = true;
main_timer_start();
break;
default:
break;
}
}
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
void advertising_start(bool erase_bonds)
{
extern void delete_bonds(void);
DBG_PRINTF("adv_start(erase=%d)\r\n", erase_bonds);
if (erase_bonds == true) {
bond_data_delete = false;
DBG_PRINTF("delete_bonds\r\n");
delete_bonds();
/* Advertising started by PM_EVT_PEERS_DELETE_SUCCEEDED event */
} else {
DBG_PRINTF("ble_adv_start...\r\n");
ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
DBG_PRINTF("err=%d\r\n", err_code);
APP_ERROR_CHECK(err_code);
}
}
#else
void advertising_start(void)
{
uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
/* Ignore INVALID_STATE - already advertising or not ready */
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
APP_ERROR_CHECK(err_code);
}
}
#endif
/*==============================================================================
* DISCONNECT
*============================================================================*/
void disconnect(uint16_t conn_handle, void * p_context)
{
UNUSED_PARAMETER(p_context);
ret_code_t err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
if (err_code != NRF_SUCCESS)
{
DBG_PRINTF("Failed to disconnect connection. Connection handle: %d Error: %d\r\n", conn_handle, err_code);
}
else
{
DBG_PRINTF("Disconnected connection handle %d\r\n", conn_handle);
}
}
/*==============================================================================
* BLE EVENT HANDLER
*============================================================================*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
uint32_t err_code;
extern void ble_data_tx_complete(void);
#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("Disconnected\r\n");
ble_connection_st = 0;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
/* Put device to sleep on disconnect if active */
if (device_status == true) {
LED_ALLOFF();
PD_ALLOFF();
if (device_sleep_mode() == 0) {
device_status = false;
}
}
/* Restart advertising to allow reconnection */
/* Delay to let PC complete disconnect processing */
nrf_delay_ms(100);
#if FEATURE_SECURE_CONNECTION
advertising_start(false);
#else
advertising_start();
#endif
DBG_PRINTF("Advertising restarted\r\n");
break;
case BLE_GAP_EVT_CONNECTED:
{
DBG_PRINTF("Connected\r\n");
ble_connection_st = 1;
battery_timer_start();
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);
/* Set TX power to +8 dBm for connected state */
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);
break;
}
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
{
DBG_PRINTF("PHY update request.\r\n");
/* Use 1M PHY only for better PC compatibility */
ble_gap_phys_t const phys =
{
.rx_phys = BLE_GAP_PHY_1MBPS,
.tx_phys = BLE_GAP_PHY_1MBPS,
};
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:
DBG_PRINTF("Client Timeout.\r\n");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_TIMEOUT:
DBG_PRINTF("Server Timeout.\r\n");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_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
/* Reject pairing when security is disabled */
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:
{
/* Display passkey for user verification */
char passkey[7];
memcpy(passkey, p_ble_evt->evt.gap_evt.params.passkey_display.passkey, 6);
passkey[6] = 0;
DBG_PRINTF("Passkey: %s\r\n", passkey);
} break;
case BLE_GAP_EVT_AUTH_KEY_REQUEST:
#if FEATURE_STATIC_PASSKEY
/* Provide static passkey for authentication */
if (p_ble_evt->evt.gap_evt.params.auth_key_request.key_type == BLE_GAP_AUTH_KEY_TYPE_PASSKEY)
{
err_code = sd_ble_gap_auth_key_reply(p_ble_evt->evt.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:
/* Handled by LESC module */
break;
#endif
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
/* TX complete - ready for next transmission */
ble_data_tx_complete();
break;
default:
break;
}
}

View File

@@ -0,0 +1,123 @@
/*******************************************************************************
* @file ble_core.h
* @brief BLE Core Functions - Stack, GAP, GATT, Advertising
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details BLE stack initialization, GAP/GATT configuration, advertising.
******************************************************************************/
#ifndef BLE_CORE_H
#define BLE_CORE_H
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_advertising.h"
#include "nrf_ble_gatt.h"
/*==============================================================================
* CONSTANTS
*============================================================================*/
#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 /* 40 ms in 0.625 ms units */
#if FEATURE_NO_SLEEP
#define APP_ADV_DURATION 0 /* Infinite advertising */
#else
#define APP_ADV_DURATION 18000 /* 180 seconds */
#endif
/* Connection Parameters */
#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
/* GATT Constants */
#define OPCODE_LENGTH 1
#define HANDLE_LENGTH 2
/*==============================================================================
* EXTERNAL VARIABLES
*============================================================================*/
extern uint16_t m_conn_handle;
extern uint16_t m_ble_nus_max_data_len;
/*==============================================================================
* FUNCTION PROTOTYPES
*============================================================================*/
/**
* @brief Initialize BLE SoftDevice stack
*/
void ble_stack_init(void);
/**
* @brief Initialize GAP parameters
*/
void gap_params_init(void);
/**
* @brief Initialize GATT module
*/
void gatt_init(void);
/**
* @brief Initialize connection parameters module
*/
void conn_params_init(void);
/**
* @brief Initialize BLE advertising
*/
void advertising_init(void);
/**
* @brief Start BLE advertising
* @param erase_bonds If true, delete bonds before advertising
*/
#if FEATURE_SECURE_CONNECTION
void advertising_start(bool erase_bonds);
#else
void advertising_start(void);
#endif
/**
* @brief Disconnect a connection
* @param conn_handle Connection handle to disconnect
* @param p_context Unused context pointer
*/
void disconnect(uint16_t conn_handle, void *p_context);
/**
* @brief Initialize BLE services (NUS, DFU)
*/
void services_init(void);
/**
* @brief Wrapper for ble_nus_data_send (accesses static m_nus)
*/
uint32_t ble_nus_data_send_wrapper(uint8_t *p_data, uint16_t *p_length, uint16_t conn_handle);
/**
* @brief Wrapper for ble_advertising_restart_without_whitelist (accesses static m_advertising)
*/
uint32_t ble_advertising_restart_without_whitelist_wrapper(void);
#if BLE_DFU_ENABLED
/**
* @brief Initialize DFU async SVCI
*/
void dfu_init(void);
#endif
#endif /* BLE_CORE_H */

View File

@@ -0,0 +1,223 @@
/*******************************************************************************
* @file ble_data_tx.c
* @brief BLE Data Transmission Functions
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details BLE NUS data transmission with CRC16 checksum.
******************************************************************************/
#include "ble_data_tx.h"
#include "ble_core.h"
#include "ble_nus.h"
#include "crc16.h"
#include "nrf_delay.h"
#include "app_error.h"
#include "debug_print.h"
#include <string.h>
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
volatile bool ble_connection_st = 0;
volatile bool data_tx_in_progress = false;
uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN];
/*==============================================================================
* PRIVATE VARIABLES
*============================================================================*/
static uint8_t m_tx_buffer[BLE_NUS_MAX_DATA_LEN];
static uint16_t m_tx_len = 0;
static bool m_tx_in_progress = false;
/*==============================================================================
* DATA TRANSMISSION FUNCTIONS
*============================================================================*/
void data_tx_handler(char const *p_data_to_send)
{
if (m_tx_in_progress)
{
return;
}
/* Find string length by locating '\r' terminator */
char const *p_end_char = strchr(p_data_to_send, '\r');
if (p_end_char == NULL)
{
DBG_PRINTF("TX data has no '\\r' terminator. Aborting send.");
return;
}
uint16_t data_len = p_end_char - p_data_to_send;
/* Validate length (need space for 2-byte CRC) */
if (data_len > (BLE_NUS_MAX_DATA_LEN - 2))
{
DBG_PRINTF("TX data is too long to fit CRC. Len: %d", data_len);
return;
}
/* Prepare buffer with data and CRC16 */
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;
/* Attempt to send */
uint32_t err_code;
if (ble_connection_st == BLE_CONNECTED_ST)
{
m_tx_in_progress = true;
err_code = ble_nus_data_send_wrapper( m_tx_buffer, &m_tx_len, m_conn_handle);
if (err_code == NRF_SUCCESS)
{
DBG_PRINTF("NUS data sent to SoftDevice buffer.");
}
else if (err_code == NRF_ERROR_RESOURCES)
{
DBG_PRINTF("NUS buffer full. Will retry later.");
}
else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND)
{
ble_connection_st = BLE_DISCONNECTED_ST;
DBG_PRINTF("Failed to send NUS data, device not connected.");
m_tx_in_progress = false;
}
else
{
m_tx_in_progress = false;
APP_ERROR_CHECK(err_code);
}
}
}
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) {
DBG_PRINTF("Lost!\r\n");
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_wrapper( 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;
}
}
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length)
{
binary_tx_handler(ble_bin_buff, length);
}
/*==============================================================================
* DATA FORMATTING FUNCTIONS
*============================================================================*/
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);
}
}
/*==============================================================================
* TX COMPLETE HANDLER (called from ble_evt_handler)
*============================================================================*/
void ble_data_tx_complete(void)
{
m_tx_in_progress = false;
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* @file ble_data_tx.h
* @brief BLE Data Transmission Functions
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details BLE NUS data transmission with CRC16 checksum.
* - ASCII data transmission
* - Binary data transmission
* - Data formatting functions
******************************************************************************/
#ifndef BLE_DATA_TX_H
#define BLE_DATA_TX_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "main.h"
/*==============================================================================
* EXTERNAL VARIABLES
*============================================================================*/
extern volatile bool ble_connection_st;
extern volatile bool data_tx_in_progress;
extern uint8_t ble_bin_buffer[];
/*==============================================================================
* FUNCTION PROTOTYPES
*============================================================================*/
/**
* @brief Send ASCII data over BLE NUS with CRC
* @param p_data_to_send Null-terminated string ending with '\r'
*/
void data_tx_handler(char const *p_data_to_send);
/**
* @brief Send binary data over BLE NUS with CRC
* @param ble_bin_buff Binary data buffer
* @param length Number of 16-bit words to send
*/
void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length);
/**
* @brief Format single 16-bit value with 4-character tag
* @param buffer Output buffer (minimum 6 bytes)
* @param tag 4-character tag string
* @param value 16-bit value to format
*/
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value);
/**
* @brief Format 16-bit array with 4-character tag
* @param buffer Output buffer
* @param tag 4-character tag string
* @param data_array Array of 16-bit values
* @param length Number of elements in array
*/
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
/**
* @brief Format 8-bit array with 4-character tag
* @param buffer Output buffer
* @param tag 4-character tag string
* @param data_array Array of 8-bit values
* @param length Number of bytes in array
*/
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length);
/**
* @brief Format ASCII string with 4-character tag
* @param buffer Output buffer
* @param tag 4-character tag string
* @param data_ascii ASCII string data
* @param length String length
*/
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
#endif /* BLE_DATA_TX_H */

View File

@@ -0,0 +1,199 @@
/*******************************************************************************
* @file ble_security.c
* @brief BLE Security - Peer Manager, LESC, Bonding
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details Peer Manager initialization and security event handling.
******************************************************************************/
#include "sdk_config.h"
#include "ble_security.h"
#if FEATURE_SECURE_CONNECTION
#include "ble_core.h"
#include "ble_data_tx.h"
#include "power_ctrl.h"
#include "device_config.h"
#include "peer_manager.h"
#include "peer_manager_handler.h"
#include "nrf_ble_lesc.h"
#include "ble_conn_state.h"
#include "app_error.h"
#include "debug_print.h"
#include "battery_saadc.h"
#include "ble_quick_security.h"
#include <string.h>
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
bool erase_bonds = false;
/*==============================================================================
* PRIVATE VARIABLES
*============================================================================*/
static pm_peer_id_t m_peer_to_be_deleted = PM_PEER_ID_INVALID;
static uint8_t c_addr[6];
/*==============================================================================
* PEER MANAGER EVENT HANDLER
*============================================================================*/
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;
/* Standard Peer Manager handlers */
pm_handler_on_pm_evt(p_evt);
pm_handler_disconnect_on_sec_failure(p_evt);
pm_handler_flash_clean(p_evt);
/* Security module (automatic mode handling) */
ble_security_quick_pm_handler(p_evt);
/* Application-specific event handling */
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);
/* Accept if: MITM protected OR dev mode (no security) */
if (conn_sec_status.mitm_protected || BLE_DEV_MODE)
{
DBG_PRINTF("Link secured. Role: %d, conn_handle: %d, Procedure: %d\r\n",
ble_conn_state_role(p_evt->conn_handle),
p_evt->conn_handle,
p_evt->params.conn_sec_succeeded.procedure);
/* Start battery monitoring after secure connection */
ble_connection_st = 1;
battery_timer_start();
}
else
{
/* Security insufficient - disconnect (production mode) */
DBG_PRINTF("Link security FAILED\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("Security failed: peer_id=%d, error=%d\r\n",
p_evt->peer_id,
p_evt->params.conn_sec_failed.error);
/* Auto-retry if key missing (supports rebonding) */
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:
/* Restart advertising after bond deletion */
advertising_start(false);
break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
/* Allow repairing for rebonding support */
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:
{
/* Store peer address for tracking */
return_code = pm_peer_data_bonding_load(p_evt->peer_id, &peer_bonding_data);
if (return_code == NRF_SUCCESS)
{
DBG_PRINTF("Peer updated: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
peer_bonding_data.peer_ble_id.id_addr_info.addr[5],
peer_bonding_data.peer_ble_id.id_addr_info.addr[4],
peer_bonding_data.peer_ble_id.id_addr_info.addr[3],
peer_bonding_data.peer_ble_id.id_addr_info.addr[2],
peer_bonding_data.peer_ble_id.id_addr_info.addr[1],
peer_bonding_data.peer_ble_id.id_addr_info.addr[0]);
memcpy(c_addr,
peer_bonding_data.peer_ble_id.id_addr_info.addr,
sizeof(c_addr));
DBG_PRINTF("Stored c_addr[3]: %02x\r\n", c_addr[3]);
m_reset_status = 10;
}
else
{
DBG_PRINTF("Failed to load peer data: error=%d\r\n", return_code);
m_reset_status = 10;
}
}
break;
default:
break;
}
}
/*==============================================================================
* PUBLIC FUNCTIONS
*============================================================================*/
void peer_manager_init(void)
{
ret_code_t err_code;
/* Security mode auto-configuration */
ble_security_quick_init(BLE_DEV_MODE);
/* Register PM event handler */
err_code = pm_register(pm_evt_handler);
APP_ERROR_CHECK(err_code);
DBG_PRINTF("BLE Security initialized (mode=%d)\r\n", BLE_DEV_MODE);
}
void delete_bonds(void)
{
ret_code_t err_code;
DBG_PRINTF("Erase bonds!\r\n");
err_code = pm_peers_delete();
APP_ERROR_CHECK(err_code);
}
void security_idle_state_handle(void)
{
ret_code_t err_code;
err_code = nrf_ble_lesc_request_handler();
APP_ERROR_CHECK(err_code);
}
#endif /* FEATURE_SECURE_CONNECTION */

View File

@@ -0,0 +1,69 @@
/*******************************************************************************
* @file ble_security.h
* @brief BLE Security - Peer Manager, LESC, Bonding
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details Peer Manager initialization and security event handling.
******************************************************************************/
#ifndef BLE_SECURITY_H
#define BLE_SECURITY_H
#include <stdint.h>
#include <stdbool.h>
#if FEATURE_SECURE_CONNECTION
#include "ble_gap.h"
/*==============================================================================
* CONSTANTS
*============================================================================*/
#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
#define BLE_DEV_MODE 1 /* 1=Dev mode, 0=Production */
#endif
/*==============================================================================
* FUNCTION PROTOTYPES
*============================================================================*/
#if FEATURE_SECURE_CONNECTION
/*==============================================================================
* EXTERNAL VARIABLES (SECURE CONNECTION ONLY)
*============================================================================*/
extern bool erase_bonds;
/**
* @brief Initialize Peer Manager with security configuration
*/
void peer_manager_init(void);
/**
* @brief Delete all bond information from flash
*/
void delete_bonds(void);
/**
* @brief Handle idle state for LESC
*/
void security_idle_state_handle(void);
#endif
#endif /* BLE_SECURITY_H */

View File

@@ -0,0 +1,19 @@
/*******************************************************************************
* @file ble_services.c
* @brief BLE Services - NUS and DFU (Stub file)
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details This is now a stub file. All service initialization code has been
* moved to ble_core.c to avoid static variable access issues with
* Nordic SDK macros (BLE_NUS_DEF, NRF_BLE_QWR_DEF, etc.).
*
* See ble_core.c for:
* - services_init()
* - dfu_init()
* - NUS data handler
* - DFU event handlers
******************************************************************************/
/* Empty stub - all code moved to ble_core.c */

View File

@@ -0,0 +1,33 @@
/*******************************************************************************
* @file ble_services.h
* @brief BLE Services - NUS and DFU
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-01-30
* @copyright (c) 2025 Medithings Inc. All rights reserved.
*
* @details Nordic UART Service and DFU service initialization.
******************************************************************************/
#ifndef BLE_SERVICES_H
#define BLE_SERVICES_H
#include <stdint.h>
#include <stdbool.h>
/*==============================================================================
* FUNCTION PROTOTYPES
*============================================================================*/
/**
* @brief Initialize BLE services (NUS, DFU)
*/
void services_init(void);
#if BLE_DFU_ENABLED
/**
* @brief Initialize DFU async SVCI
*/
void dfu_init(void);
#endif
#endif /* BLE_SERVICES_H */