From 73344f499c4ba9d4e952f56b7bd5fa463d025185 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Tue, 6 Jun 2023 20:27:08 +0800 Subject: [PATCH] Add bas, to watch battery level and notification. --- firmware/application/src/ble_main.c | 251 +++++++++++++++++++++++++++- firmware/application/src/ble_main.h | 5 + 2 files changed, 252 insertions(+), 4 deletions(-) diff --git a/firmware/application/src/ble_main.c b/firmware/application/src/ble_main.c index 6e676b6..424716b 100644 --- a/firmware/application/src/ble_main.c +++ b/firmware/application/src/ble_main.c @@ -1,5 +1,4 @@ #include "ble_hci.h" -#include "ble_nus.h" #include "ble_advdata.h" #include "ble_advertising.h" #include "ble_conn_params.h" @@ -11,6 +10,7 @@ #include "nrf_sdh_soc.h" #include "nrf_sdh_ble.h" #include "nrf_ble_gatt.h" +#include "nrf_drv_saadc.h" #include "app_timer.h" #include "app_util_platform.h" @@ -18,6 +18,7 @@ #include "syssleep.h" #include "ble_main.h" #include "dataframe.h" +#include "hw_connect.h" #define NRF_LOG_MODULE_NAME ble_main #include "nrf_log.h" @@ -40,18 +41,41 @@ NRF_LOG_MODULE_REGISTER(); #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */ #define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */ +// #define BATTERY_LEVEL_MEAS_INTERVAL APP_TIMER_TICKS(1000) /**< Battery level measurement interval (ticks). This value corresponds to 1 seconds. */ +#define BATTERY_LEVEL_MEAS_INTERVAL APP_TIMER_TICKS(5000) /**< Battery level measurement interval (ticks). This value corresponds to N seconds. */ + +#define ADC_REF_VOLTAGE_IN_MILLIVOLTS 600 //!< Reference voltage (in milli volts) used by ADC while doing conversion. +#define ADC_RES_12BIT 16383 //!< Maximum digital value for 14-bit ADC conversion. +#define ADC_PRE_SCALING_COMPENSATION 12 //!< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage. + +/**@brief Macro to convert the result of ADC conversion in millivolts. + * + * @param[in] ADC_VALUE ADC result. + * + * @retval Result converted to millivolts. + */ +#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\ + ((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_12BIT) * ADC_PRE_SCALING_COMPENSATION) + +APP_TIMER_DEF(m_battery_timer_id); /**< Battery measurement timer. */ +BLE_BAS_DEF(m_bas); /**< Battery service instance. */ BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); /**< BLE NUS service instance. */ NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */ NRF_BLE_QWR_DEF(m_qwr); /**< Context for the Queued Write module.*/ BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */ +uint16_t batt_lvl_in_milli_volts = 0; +uint8_t percentage_batt_lvl = 0; +static nrf_saadc_value_t adc_buf[2]; static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */ static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifier. */ { - {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE} + {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}, + {BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}, }; volatile bool g_is_ble_connected = false; +volatile bool g_is_low_battery_shutdown = false; /**@brief Function for the GAP initialization. @@ -81,6 +105,30 @@ static void gap_params_init(void) APP_ERROR_CHECK(err_code); } +/**@brief Function for handling the Battery Service events. + * + * @details This function will be called for all Battery Service events which are passed to the + | application. + * + * @param[in] p_bas Battery Service structure. + * @param[in] p_evt Event received from the Battery Service. + */ +static void on_bas_evt(ble_bas_t * p_bas, ble_bas_evt_t * p_evt) +{ + switch (p_evt->evt_type) + { + case BLE_BAS_EVT_NOTIFICATION_ENABLED: + break; // BLE_BAS_EVT_NOTIFICATION_ENABLED + + case BLE_BAS_EVT_NOTIFICATION_DISABLED: + break; // BLE_BAS_EVT_NOTIFICATION_DISABLED + + default: + // No implementation needed. + break; + } +} + /**@brief Function for handling the data from the Nordic UART Service. * * @details This function will process the data received from the Nordic UART BLE Service @@ -143,27 +191,128 @@ static void nrf_qwr_error_handler(uint32_t nrf_error) APP_ERROR_HANDLER(nrf_error); } +__INLINE uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) +{ + return (uint32_t)((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min); +} + +//电池电压到百分比计算 +uint32_t BATVOL2PERCENT(uint16_t VOL) +{ + //100% 4.20V 1 + //90 % 4.06V 80%-100% 白 + //80 % 3.98V 1 + //70 % 3.92V 60%-80% 白 + //60 % 3.87V 1 + //50 % 3.82V 40%-60% 白 + //40 % 3.79V 1 + //30 % 3.77V 20%-40% 白 + //20 % 3.74V 1 + //10 % 3.68V 5%-20% 红 + //5 % 3.45V 1 关机 + //0 % 3.00V + //#define P100VOL 4200 + //#define P80VOL 3980 + //#define P60VOL 3870 + //#define P40VOL 3790 + //#define P20VOL 3740 + //#define P5VOL 3450 + + //100% 4.20V 1 + //90 % 4.00V 80%-100% 白 + //80 % 3.89V 1 + //70 % 3.79V 60%-80% 白 + //60 % 3.70V 1 + //50 % 3.62V 40%-60% 白 + //40 % 3.57V 1 + //30 % 3.53V 20%-40% 白 + //20 % 3.51V 1 + //10 % 3.46V 5%-20% 红 + //5 % 3.43V 1 关机 + //0 % 3.00V +#define P100VOL 4200 +#define P80VOL 3890 +#define P60VOL 3700 +#define P40VOL 3570 +#define P20VOL 3510 +#define P5VOL 3230 + + + if(VOL > P80VOL) + { + //80-100 + return map(VOL, P80VOL, P100VOL, 80, 100); + } + else if(VOL > P60VOL) + { + //60-80 + return map(VOL, P60VOL, P80VOL, 60, 80); + } + else if(VOL > P40VOL) + { + //40-60 + return map(VOL, P40VOL, P60VOL, 40, 60); + } + else if(VOL > P20VOL) + { + //20-60 + return map(VOL, P20VOL, P40VOL, 20, 40); + } + else if(VOL > P5VOL) + { + //5-20 + return map(VOL, P5VOL, P20VOL, 5, 20); + } + else + { + //<5 + return 0; + } +} + /**@brief Function for initializing services that will be used by the application. */ static 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. + 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); + + // ------------------------------------------------------------- // Initialize NUS. + ble_nus_init_t nus_init; 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); + + // ------------------------------------------------------------- + // battery service + + ble_bas_init_t bas_init_obj; + + memset(&bas_init_obj, 0, sizeof(bas_init_obj)); + + bas_init_obj.evt_handler = on_bas_evt; + bas_init_obj.support_notification = true; + bas_init_obj.p_report_ref = NULL; + bas_init_obj.initial_batt_level = 100; + + bas_init_obj.bl_rd_sec = SEC_OPEN; + bas_init_obj.bl_cccd_wr_sec = SEC_OPEN; + bas_init_obj.bl_report_rd_sec = SEC_OPEN; + + err_code = ble_bas_init(&m_bas, &bas_init_obj); + APP_ERROR_CHECK(err_code); } /**@brief Function for handling an event from the Connection Parameters Module. @@ -405,10 +554,104 @@ void advertising_start(void) APP_ERROR_CHECK(err_code); } + + +/**@brief Function for handling the ADC interrupt. + * + * @details This function will fetch the conversion result from the ADC, convert the value into + * percentage and send it to peer. + */ +void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event) +{ + if (p_event->type == NRF_DRV_SAADC_EVT_DONE) + { + nrf_saadc_value_t adc_result; + uint32_t err_code; + + adc_result = p_event->data.done.p_buffer[0]; + // NRF_LOG_INFO("ADC sample value = %d", adc_result); + + err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 1); + APP_ERROR_CHECK(err_code); + + batt_lvl_in_milli_volts = ADC_RESULT_IN_MILLI_VOLTS(adc_result) + 100; + NRF_LOG_INFO("batt_lvl_in_milli_volts: %d", batt_lvl_in_milli_volts); + percentage_batt_lvl = BATVOL2PERCENT(batt_lvl_in_milli_volts); + + // if battery service is notification enable, we can send msg to device. + err_code = ble_bas_battery_level_update(&m_bas, percentage_batt_lvl, BLE_CONN_HANDLE_ALL); + if ((err_code != NRF_SUCCESS) && + (err_code != NRF_ERROR_INVALID_STATE) && + (err_code != NRF_ERROR_RESOURCES) && + (err_code != NRF_ERROR_BUSY) && + (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) + ) + { + APP_ERROR_HANDLER(err_code); + } + + // check low battery level, if level == 0, we can try to shutdown. + if (percentage_batt_lvl == 0) { + NRF_LOG_INFO("battery too low, try to shutdown..."); + g_is_low_battery_shutdown = true; + sleep_timer_start(SLEEP_NO_BATTERY_SHUTDOWN); + } else { + g_is_low_battery_shutdown = false; + } + } +} + +/**@brief Function for configuring ADC to do battery level conversion. + */ +static void adc_configure(void) +{ + ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_event_handler); + APP_ERROR_CHECK(err_code); + + nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(BAT_SENSE); + err_code = nrf_drv_saadc_channel_init(0, &config); + APP_ERROR_CHECK(err_code); + + err_code = nrf_drv_saadc_buffer_convert(&adc_buf[0], 1); + APP_ERROR_CHECK(err_code); + + err_code = nrf_drv_saadc_buffer_convert(&adc_buf[1], 1); + APP_ERROR_CHECK(err_code); +} + +/**@brief Function for handling the Battery measurement timer timeout. + * + * @details This function will be called each time the battery level measurement timer expires. + * This function will start the ADC. + * + * @param[in] p_context Pointer used for passing some arbitrary information (context) from the + * app_start_timer() call to the timeout handler. + */ +static void battery_level_meas_timeout_handler(void * p_context) +{ + UNUSED_PARAMETER(p_context); + + ret_code_t err_code; + err_code = nrf_drv_saadc_sample(); + APP_ERROR_CHECK(err_code); +} + +void create_battery_timer(void) { + ret_code_t err_code; + // Create battery timer. + err_code = app_timer_create(&m_battery_timer_id, APP_TIMER_MODE_REPEATED, battery_level_meas_timeout_handler); + APP_ERROR_CHECK(err_code); + // Start battery timer + err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); +} + /** * @brief Function for init ble slave. */ void ble_slave_init(void) { + adc_configure(); // ADC初始化 + create_battery_timer(); // 创建电池电量更新定时器 ble_stack_init(); // BLE协议栈初始化 gap_params_init(); // GAP参数初始化 gatt_init(); // GATT协议初始化 diff --git a/firmware/application/src/ble_main.h b/firmware/application/src/ble_main.h index 5d103fa..875eebf 100644 --- a/firmware/application/src/ble_main.h +++ b/firmware/application/src/ble_main.h @@ -1,6 +1,11 @@ #ifndef BLE_MAIN_H #define BLE_MAIN_H +#include "ble_gatts.h" +#include "ble_nus.h" +#include "ble_bas.h" + + void ble_slave_init(void); void advertising_start(void); void nus_data_reponse(uint8_t *p_data, uint16_t length);