Add bas, to watch battery level and notification.

This commit is contained in:
dxl
2023-06-06 20:27:08 +08:00
parent ff75014db1
commit 73344f499c
2 changed files with 252 additions and 4 deletions
+247 -4
View File
@@ -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协议初始化
+5
View File
@@ -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);