Hey All,
I am currently trying to have a Analog Signal come in through P0.05 which then gets converted via the onboard ADC and send out a Service Characteristic. I am using a modified version of the ble-app-lbs but I am stuck with how to get the ADC_IRQHandler to fire(which in another application it was but here I am having trouble) along with pass that through my service into the characteristic.
The board is a nRF51 DK Board
Also not sure why my Notifications for the service is not working.
I apologize for the long code, not sure if you needed to see the other two or not.
Any advice/information would help so much.
Thank you -Nate
Here is the code that I am using
Main.c
> #include <stdint.h>
> #include <string.h>
> #include "nordic_common.h"
> #include "nrf.h"
> #include "nrf_adc.h"
> #include "app_error.h"
> #include "nrf_gpio.h"
> #include "nrf51_bitfields.h"
> #include "app_util_platform.h"
> #include "ble.h"
> #include "ble_hci.h"
> #include "ble_srv_common.h"
> #include "ble_advdata.h"
> #include "ble_dis.h"
> #include "ble_bas.h"
> #include "ble_conn_params.h"
> #include "app_scheduler.h"
> #include "softdevice_handler.h"
> #include "app_timer.h"
> #include "device_manager.h"
> #include "pstorage.h"
> #include "app_gpiote.h"
> #include "bsp.h"
> #include "ble_adcconvert.h"
> #include "ble_advertising.h"
> #include "boards.h"
> #include "bsp.h"
> #include "app_trace.h"
>
> #define IS_SRVC_CHANGED_CHARACT_PRESENT 1
> #define WAKEUP_BUTTON_ID 0
> /**< Button used to wake up the
> application. */
>
>
> #define DEVICE_NAME "EPSensor"
> #define APP_ADV_INTERVAL 6000
> #define APP_ADV_TIMEOUT_IN_SECONDS 0
>
> #define APP_TIMER_PRESCALER 0
> #define APP_TIMER_MAX_TIMERS 6
> #define APP_TIMER_OP_QUEUE_SIZE 4
>
> #define SAMPLE_RATE_MEAS_INTERVAL APP_TIMER_TICKS(4,APP_TIMER_PRESCALER)
>
> #define ADC_REF_VOLTAGE_IN_MILLIVOLTS 1200
> #define ADC_PRE_SCALING_COMPENSATION 3
> #define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
> ((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / 255)
> * ADC_PRE_SCALING_COMPENSATION)
>
> #define BODY_VOLTAGE_INCREMENT 12000
>
> #define MIN_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS)
> /**< Minimum acceptable connection
> interval (8 msec). */
> #define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, 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,
> APP_TIMER_PRESCALER)
> #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000,
> APP_TIMER_PRESCALER) /**< 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 APP_GPIOTE_MAX_USERS 3
> /**< Maximum number of users of the
> GPIOTE handler. */
>
>
> #define DEAD_BEEF 0xDEADBEEF
> /**< Value used as error code on stack
> dump, can be used to identify stack
> location on stack unwind. */
>
>
> static ble_adc_t m_adc;
> static ble_gap_sec_params_t
> m_sec_params;
> /**< Security requirements for this
> application. */ static uint16_t
> m_conn_handle =
> BLE_CONN_HANDLE_INVALID; /**<
> Handle of the current connection. */
>
>
> static dm_application_instance_t
> m_app_handle;
> /**< Application identifier allocated
> by device manager */
>
> static
> app_timer_id_t m_sampling_timer_id; /**< Biopotential sample acquisition
> timer. */
>
> static
> app_gpiote_user_id_t m_sampling_gpiote_id; static bool
> m_memory_access_in_progress = false;
> /**< Flag to keep track of ongoing
> operations on persistent memory. */
>
> static uint8_t adc_sample;
>
>
> // YOUR_JOB: Modify these according to
> requirements (e.g. if other event
> types are to pass through //
> the scheduler).
> #define SCHED_MAX_EVENT_DATA_SIZE sizeof(app_timer_event_t)
> /**< Maximum size of scheduler events.
> Note that scheduler BLE stack events
> do not contain any data, as the events
> are being pulled from the stack in the
> event handler. */
> #define SCHED_QUEUE_SIZE 10
> /**< Maximum number of events in the
> scheduler queue. */
>
>
>
> /**@brief Callback function for
> asserts in the SoftDevice. * *
> @details This function will be called
> in case of an assert in the
> SoftDevice. * * @warning This
> handler is an example only and does
> not fit a final product. You need to
> analyze * how your product
> is supposed to react in case of
> Assert. * @warning On assert from the
> SoftDevice, the system can only
> recover on reset. * * @param[in]
> line_num Line number of the failing
> ASSERT call. * @param[in] file_name
> File name of the failing ASSERT call.
> */ void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
> {
> app_error_handler(DEAD_BEEF, line_num, p_file_name); }
>
> /**@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
> ADC_IRQHandler(void) {
> nrf_adc_conversion_event_clean();
>
> adc_sample = nrf_adc_result_get();
>
> //ble_adc_convert_add(&m_adc,
> (int16_t)adc_sample);
> // trigger next ADC conversion
> nrf_adc_start(); }
>
> /**@brief Function for configuring ADC
> to do battery level conversion. */
> static void adc_configure(void) {
> uint32_t err_code;
> nrf_adc_config_t adc_config = NRF_ADC_CONFIG_DEFAULT;
>
> // Configure ADC
> adc_config.reference = NRF_ADC_CONFIG_REF_VBG;
> adc_config.resolution = NRF_ADC_CONFIG_RES_8BIT;
> adc_config.scaling = NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD;
> nrf_adc_configure(&adc_config);
>
> nrf_adc_input_select(NRF_ADC_CONFIG_INPUT_6);
>
> // Enable ADC interrupt
> nrf_adc_int_enable(ADC_INTENSET_END_Enabled
> << ADC_INTENSET_END_Pos);
> err_code = sd_nvic_ClearPendingIRQ(ADC_IRQn);
> APP_ERROR_CHECK(err_code);
>
> err_code = sd_nvic_SetPriority(ADC_IRQn,
> NRF_APP_PRIORITY_HIGH);
> APP_ERROR_CHECK(err_code);
>
> err_code = sd_nvic_EnableIRQ(ADC_IRQn);
> APP_ERROR_CHECK(err_code); }
>
>
> /**@brief Function for handling the
> sample acquisition timer timeout. *
> * @details This function will be called each time the sampling timer
> expires. * * @param[in] p_context
> Pointer used for passing some
> arbitrary information (context) from
> the *
> app_start_timer() call to the timeout
> handler. */ static void
> sampling_timer_timeout_handler(void *
> p_context) {
> UNUSED_PARAMETER(p_context); }
>
> /**@brief Function for the Timer
> initialization. * * @details
> Initializes the timer module. */
> static void timers_init(void) {
> uint32_t err_code;
>
> // Initialize timer module, making it use the scheduler
> APP_TIMER_INIT(APP_TIMER_PRESCALER,
> APP_TIMER_MAX_TIMERS,
> APP_TIMER_OP_QUEUE_SIZE, true);
>
> /* YOUR_JOB: Create any timers to be used by the application.
> Below is an example of how to create a timer.
> For every new timer needed, increase the value of the
> macro APP_TIMER_MAX_TIMERS by
> one. */
> err_code = app_timer_create(&m_sampling_timer_id,
> APP_TIMER_MODE_REPEATED,
> sampling_timer_timeout_handler);
> APP_ERROR_CHECK(err_code); }
>
> /**@brief Function for starting
> advertising. */ static void
> advertising_start(void) {
> uint32_t err_code; uint32_t count;
> ble_gap_adv_params_t adv_params;
>
> // Verify if there is any flash
> access pending, if yes delay starting
> advertising until
> // it's complete.
> err_code = pstorage_access_status_get(&count);
> APP_ERROR_CHECK(err_code);
>
> if (count != 0)
> {
> m_memory_access_in_progress = true;
> return;
> }
>
> // Start advertising
> memset(&adv_params, 0, sizeof(adv_params));
>
> adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
> adv_params.p_peer_addr = NULL;
> adv_params.fp = BLE_GAP_ADV_FP_ANY;
> adv_params.interval = APP_ADV_INTERVAL;
> adv_params.timeout = APP_ADV_TIMEOUT_IN_SECONDS;
>
> err_code = sd_ble_gap_adv_start(&adv_params);
> APP_ERROR_CHECK(err_code);
> err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
> APP_ERROR_CHECK(err_code); }
>
> /**@brief Function for the GAP
> initialization. * * @details This
> function sets up all the necessary GAP
> (Generic Access Profile) parameters of
> the * device including the
> device name, appearance, and the
> preferred connection parameters. */
> 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;
>
> BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
>
> err_code = sd_ble_gap_device_name_set(&sec_mode,
> (const uint8_t *)DEVICE_NAME,
> strlen(DEVICE_NAME));
> printf(DEVICE_NAME);
> 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); }
>
> /**@brief Function for putting the
> chip into sleep mode. * * @note This
> function will not return. */
>
> 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:
>
> break;
> default:
> break;
> } }
>
> /**@brief Function for initializing
> the Advertising functionality. * *
> @details Encodes the required
> advertising data and passes it to the
> stack. * Also builds a
> structure to be passed to the stack
> when starting advertising. */ static
> void advertising_init(void) {
> uint32_t err_code;
> ble_advdata_t advdata;
> uint8_t flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
>
> // YOUR_JOB: Use UUIDs for service(s) used in your application.
>
> ble_uuid_t adv_uuids[] =
> {
> {BLE_UUID_ADC_CONVERT_SERVICE, BLE_UUID_TYPE_BLE}
> };
>
> // Build and set advertising data
> memset(&advdata, 0, sizeof(advdata));
>
> advdata.name_type = BLE_ADVDATA_FULL_NAME;
> advdata.include_appearance = true;
> advdata.flags = flags;
> advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) /
> sizeof(adv_uuids[0]);
> advdata.uuids_complete.p_uuids = adv_uuids;
> ble_adv_modes_config_t options = {0}; options.ble_adv_fast_enabled =
> BLE_ADV_FAST_ENABLED;
> options.ble_adv_fast_interval =
> APP_ADV_INTERVAL;
> options.ble_adv_fast_timeout =
> APP_ADV_TIMEOUT_IN_SECONDS;
>
> err_code = ble_advertising_init(&advdata,
> &options, on_adv_evt, NULL);
> APP_ERROR_CHECK(err_code); }
>
> static void advertising_stop(void)
> {
> uint32_t err_code;
>
> err_code = sd_ble_gap_adv_stop();
> APP_ERROR_CHECK(err_code);
>
> err_code = bsp_indication_set(BSP_INDICATE_IDLE);
> APP_ERROR_CHECK(err_code); }
>
>
> /**@brief Function for handling the
> Connection Parameters Module. * *
> @details This function will be called
> for all events in the Connection
> Parameters Module which *
> are passed to the application. *
> @note All this function does is to
> disconnect. This could have been done
> by simply * setting
> the disconnect_on_fail config
> parameter, but instead we use the
> event * handler
> mechanism to demonstrate its use. *
> * @param[in] p_evt Event received from the Connection Parameters Module.
> */ 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);
> } }
>
>
> /**@brief Function for handling a
> Connection Parameters error. * *
> @param[in] nrf_error Error code
> containing information about what went
> wrong. */ static void
> conn_params_error_handler(uint32_t
> nrf_error) {
> APP_ERROR_HANDLER(nrf_error); }
>
> /**@brief Function for handling the
> Biopotential Measurement Service
> events. * * @details This function
> will be called for all Biopotential
> Measurement Service events which |
> are passed to the application. * *
> @param[in] p_bms Biopotential
> Measurement Service structure. *
> @param[in] p_evt Event received
> from the Biopotential Measurement
> Service. */ static void
> on_adc_evt(ble_adc_t * p_adc,
> ble_adc_evt_t *p_evt) {
> uint32_t err_code;
>
> switch (p_evt->evt_type)
> {
> case BLE_ADC_EVT_NOTIFICATION_ENABLED:
> // Start transmit timer
> err_code = app_timer_start(m_sampling_timer_id,
> SAMPLE_RATE_MEAS_INTERVAL, NULL);
> APP_ERROR_CHECK(err_code);
> break;
>
> case BLE_ADC_EVT_NOTIFICATION_DISABLED:
> // Stop transmit timer
> err_code = app_timer_stop(m_sampling_timer_id);
> APP_ERROR_CHECK(err_code);
> break;
>
> case BLE_ADC_EVT_SAMPLE_RATE_CHANGE:
> err_code = app_timer_stop(m_sampling_timer_id);
> APP_ERROR_CHECK(err_code);
> err_code = app_timer_start(m_sampling_timer_id,
> APP_TIMER_TICKS(4,
> APP_TIMER_PRESCALER), NULL);
> APP_ERROR_CHECK(err_code);
>
> } }
>
> /**@brief Function for handling the
> Application's BLE Stack events. * *
> @param[in] p_ble_evt Bluetooth
> stack event. */ static void
> on_ble_evt(ble_evt_t * p_ble_evt) {
> uint32_t err_code;
> static ble_gap_evt_auth_status_t m_auth_status;
> bool master_id_matches;
>
> switch (p_ble_evt->header.evt_id)
> {
> case BLE_GAP_EVT_CONNECTED:
> err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
> APP_ERROR_CHECK(err_code);
> m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
>
> // Start sample acquisition timer
> err_code = app_timer_start(m_sampling_timer_id,
> SAMPLE_RATE_MEAS_INTERVAL, NULL);
> APP_ERROR_CHECK(err_code);
> break;
>
> case BLE_GAP_EVT_DISCONNECTED:
> err_code = bsp_indication_set(BSP_INDICATE_IDLE);
> APP_ERROR_CHECK(err_code);
> m_conn_handle = BLE_CONN_HANDLE_INVALID;
>
> // Stop sample acquisition timer
> err_code = app_timer_stop(m_sampling_timer_id);
> APP_ERROR_CHECK(err_code);
>
> advertising_start();
> break;
>
>
> case BLE_GATTS_EVT_SYS_ATTR_MISSING:
> err_code = sd_ble_gatts_sys_attr_set(m_conn_handle,
> NULL,
> 0,
> BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS |
> BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS);
> APP_ERROR_CHECK(err_code);
> break;
> default:
> // No implementation needed.
> break;
> } }
>
>
> /**@brief Function for dispatching a
> BLE stack event to all modules with a
> BLE stack event handler. * *
> @details This function is called from
> the scheduler in the main loop after a
> BLE stack * event has been
> received. * * @param[in] p_ble_evt
> Bluetooth stack event. */ static void
> ble_evt_dispatch(ble_evt_t *
> p_ble_evt) {
> dm_ble_evt_handler(p_ble_evt);
> on_ble_evt(p_ble_evt);
> ble_conn_params_on_ble_evt(p_ble_evt);
> ble_adc_on_ble_evt(&m_adc,
> p_ble_evt); }
>
> /**@brief Function for handling the
> Application's system events. * *
> @param[in] sys_evt system event.
> */ static void on_sys_evt(uint32_t sys_evt) {
> switch(sys_evt)
> {
> case NRF_EVT_FLASH_OPERATION_SUCCESS:
> case NRF_EVT_FLASH_OPERATION_ERROR:
>
> if (m_memory_access_in_progress)
> {
> m_memory_access_in_progress = false;
> advertising_start();
> }
> break;
>
> default:
> // No implementation needed.
> break;
> } }
>
> /**@brief Function for dispatching a
> system event to interested modules. *
> * @details This function is called from the System event interrupt
> handler after a system *
> event has been received. * *
> @param[in] sys_evt System stack
> event. */ static void
> sys_evt_dispatch(uint32_t sys_evt) {
> pstorage_sys_event_handler(sys_evt);
> on_sys_evt(sys_evt); }
>
>
>
> /**@brief Function for initializing
> services that will be used by the
> application. */ static void
> services_init(void) { uint32_t
> err_code; ble_adc_init_t adc_init;
> uint8_t sample_rate;
> //adc_sample = nrf_adc_result_get();
> memset(&adc_init, 0, sizeof(adc_init));
> adc_init.p_sample_rate = &adc_sample; adc_init.evt_handler =
> on_adc_evt;
> adc_init.support_notification =
> true;
>
> BLE_GAP_CONN_SEC_MODE_SET_OPEN(&adc_init.adc_sample_rate_attr_md.read_perm);
>
>
> err_code = ble_adc_init(&m_adc,&adc_init);
>
> APP_ERROR_CHECK(err_code);
> }
>
> /**@brief Function for starting
> timers.
> */ static void timers_start(void) {
> uint32_t err_code;
> err_code = app_timer_start(m_sampling_timer_id,
> SAMPLE_RATE_MEAS_INTERVAL, NULL);
> APP_ERROR_CHECK(err_code); }
>
> /**@brief Function for initializing
> the Connection Parameters module. */
> 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
> = m_adc.sample_rate_handles.cccd_handle;
> 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); }
>
> /**@brief Function for initializing
> the BLE stack. * * @details
> Initializes the SoftDevice and the BLE
> event interrupt. */ static void
> ble_stack_init(void) {
> uint32_t err_code;
>
> // Initialize the SoftDevice handler module.
> SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_RC_250_PPM_TEMP_4000MS_CALIBRATION,
> false);
>
> // Enable BLE stack
> ble_enable_params_t ble_enable_params;
> memset(&ble_enable_params, 0, sizeof(ble_enable_params));
>
> ble_enable_params.gatts_enable_params.service_changed
> = IS_SRVC_CHANGED_CHARACT_PRESENT;
> err_code = sd_ble_enable(&ble_enable_params);
> APP_ERROR_CHECK(err_code);
>
> // Register with the SoftDevice handler module for BLE events.
> err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
> APP_ERROR_CHECK(err_code);
>
> // Register with the SoftDevice handler module for BLE events.
> err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
> APP_ERROR_CHECK(err_code); }
>
>
> /**@brief Function for the Event
> Scheduler initialization. */ static
> void scheduler_init(void) {
> APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); }
>
>
> /**@brief Function for initializing
> the GPIOTE handler module. */ static
> void gpiote_init(void) { uint32_t
> err_code;
>
> APP_GPIOTE_INIT(APP_GPIOTE_MAX_USERS);
> }
>
> /**@brief Function for handling the
> Device Manager events. * *
> @param[in] p_evt Data associated to
> the device manager event. */ static
> uint32_t
> device_manager_evt_handler(dm_handle_t
> const * p_handle,
> dm_event_t const * p_event,
> ret_code_t event_result) {
> APP_ERROR_CHECK(event_result);
>
>
> return NRF_SUCCESS; }
>
>
> /**@brief Function for the Device
> Manager initialization. */ static
> void device_manager_init(void) {
> uint32_t err_code;
> dm_init_param_t init_data;
> dm_application_param_t register_param;
>
> // Initialize persistent storage module.
> err_code = pstorage_init();
> APP_ERROR_CHECK(err_code);
>
> // Clear all bonded centrals if the Bonds Delete button is pushed.
> //err_code = bsp_button_is_pressed(BOND_DELETE_ALL_BUTTON_ID,&(init_data.clear_persistent_data));
> //APP_ERROR_CHECK(err_code);
>
> err_code = dm_init(&init_data);
> APP_ERROR_CHECK(err_code);
>
> memset(®ister_param.sec_param, 0, sizeof(ble_gap_sec_params_t));
>
>
> register_param.evt_handler = device_manager_evt_handler;
> register_param.service_type = DM_PROTOCOL_CNTXT_GATT_SRVR_ID;
>
> err_code = dm_register(&m_app_handle,
> ®ister_param);
> APP_ERROR_CHECK(err_code); }
>
> /**@brief Function for the Power
> manager. */ static void
> power_manage(void) {
> uint32_t err_code = sd_app_evt_wait();
> APP_ERROR_CHECK(err_code); }
>
>
> /**@brief Function for application
> main entry. */ int main(void) {
> //uint8_t regs[ADS1291_2_NUM_REGS];
> uint32_t err_code;
>
> // Initialize ble_stack_init();
> timers_init();
> gpiote_init(); device_manager_init();
> scheduler_init();
> gap_params_init();
> advertising_init();
> services_init(); adc_configure();
> conn_params_init(); nrf_adc_start();
>
> // Start execution
> timers_start();
> advertising_start();
>
> // Enter main loop
> for (;;)
> {
> app_sched_execute();
> power_manage();
> } }
>
> /** * @} */
>
ble_adcconvert.h
#ifndef OUR_ADC_SERVICE_H__
> #define OUR_ADC_SERVICE_H__
>
> #include <stdint.h>
> #include <stdbool.h>
> #include "ble.h"
> #include "ble_srv_common.h"
>
> // Base UUID
> #define BMS_UUID_BASE {0x23, 0xD1, 0x13, 0xEF, 0x5F, 0x78, 0x23, 0x15,
> 0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00,
> 0x00, 0x00};
>
> // Service UUID
> #define BLE_UUID_ADC_CONVERT_SERVICE 0x1140
>
> // Characteristic UUIDs
> #define BLE_UUID_SENSOR_DATA 0x2828
>
> #define BLE_BMS_MAX_BUFFERED_MEASUREMENTS 30
>
> typedef enum {
> BLE_ADC_EVT_NOTIFICATION_ENABLED, /**< Voltage value notification
> enabled event. */
> BLE_ADC_EVT_NOTIFICATION_DISABLED, /**< Voltage value notification
> disabled event. */
> BLE_ADC_EVT_SAMPLE_RATE_CHANGE }
> ble_adc_evt_type_t;
>
> /**@brief Biopotential Measurement
> Service event. */ typedef struct {
> ble_adc_evt_type_t evt_type; /**< Type of event. */ }
> ble_adc_evt_t;
>
> typedef struct ble_adc_s ble_adc_t;
>
>
> /**@brief Biopotential Measurement
> Service event handler type. */ typedef
> void (*ble_adc_evt_handler_t)
> (ble_adc_t * p_hrs, ble_adc_evt_t *
> p_evt);
>
> typedef struct {
> ble_adc_evt_handler_t evt_handler;
> /**< Event handler to be called for
> handling events in the Biopotential
> Measurement Service. */ uint8_t
> * p_sample_rate;
> /**< If not NULL, initial value of the
> Sample Rate characteristic. */
> ble_srv_security_mode_t
> adc_sample_rate_attr_md;
> /**< Initial security level for Sample Rate attribute */
> bool
> support_notification; }
> ble_adc_init_t;
>
>
> typedef struct ble_adc_s {
> ble_adc_evt_handler_t evt_handler;
> /**< Event handler to be called for
> handling events in the Biopotential
> Measurement Service. */
> uint16_t service_handle;
> /**< Handle of Biopotential
> Measurement Service (as provided by
> the BLE stack). */
> ble_gatts_char_handles_t
> sample_rate_handles; uint16_t
> conn_handle; uint8_t
> bvm_count; /**< Handles related to
> the Sample Rate characteristic. */
> /**< Handles related to the Body
> Sensor Location characteristic. */
> uint8_t
> uuid_type; uint8_t
> sample_rate;
> uint16_t adc_buffer[BLE_BMS_MAX_BUFFERED_MEASUREMENTS];
> } ble_adc_t;
>
> /**@brief Initialize the Biopotential
> Measurement Service. * * @param[out]
> p_adc * @param[in]
> p_adc_init * * @return
> NRF_SUCCESS on successful
> initialization of service, otherwise
> an error code. */ uint32_t
> ble_adc_init(ble_adc_t * p_adc, const
> ble_adc_init_t * p_adc_init);
>
> /**@brief Biopotential Measurement
> Service BLE stack event handler. * *
> @details Handles all events from the
> BLE stack of interest to the
> Biopotential Measurement Service. *
> * @param[in] p_adc ADC Sensor Measurement Service structure. *
> @param[in] p_ble_evt Event received
> from the BLE stack. */ void
> ble_adc_on_ble_evt(ble_adc_t * p_adc,
> ble_evt_t * p_ble_evt);
>
> /**@brief Function for sending sensor
> voltage measurement if notification
> has been enabled. * * @details The
> application calls this function after
> having performed a voltage
> measurement. * If
> notification has been enabled, the
> voltage measurement data is encoded
> and sent to * the client. *
> * @param[in] p_adc ADC Sensor Service structure. * *
> @return NRF_SUCCESS on success,
> otherwise an error code. */ uint32_t
> ble_adc_sensor_measurement_send(ble_adc_t * p_adc);
>
> /**@brief Function for adding a Body
> Voltage Measurement to the buffer. *
> * @details All buffered voltage measurements will be included in the
> next biopotential *
> measurement message, up to the maximum
> number of measurements that will fit
> into the * message. If the
> buffer is full, the oldest measurement
> in the buffer will be deleted. * *
> @param[in] p_bms Biopotential
> Measurement Service structure. *
> @param[in] bvm_val New voltage
> measurement (will be buffered until
> the next *
> connection interval). * * @return
> NRF_SUCCESS on success, otherwise an
> error code. */ uint32_t
> ble_adc_convert_add(ble_adc_t * p_adc,
> int16_t adc_val);
>
> /**@brief Function for checking if
> Body Voltage Measurement buffer is
> full. * * @param[in] p_bms
> Biopotential Measurement Service
> structure. * * @return true if
> Body Voltage Measurement buffer is
> full, false otherwise. */ uint32_t
> ble_adc_sample_rate_set(ble_adc_t *
> p_adc, uint8_t sample_rate); bool
> ble_adc_convert_buffer_is_full(ble_adc_t
> * p_adc);
>
>
>
> #endif
ble_adcconvert.c
#include "ble_advdata.h"
#include "ble_advertising.h"
#include "pstorage.h"
#include "nrf_soc.h"
#include "app_trace.h"
#include "nordic_common.h"
#define LOG app_trace_log
static bool m_advertising_start_pending = false; /**< Flag to keep track of ongoing operations on persistent memory. */
static ble_gap_addr_t m_peer_address; /**< Address of the most recently connected peer, used for direct advertising. */
static ble_advdata_t m_advdata; /**< Used by the initialization function to set name, appearance, and UUIDs and advertising flags visible to peer devices. */
static ble_adv_evt_t m_adv_evt; /**< Advertising event propogated to the main application. The event is either a transaction to a new advertising mode, or a request for whitelist or peer address.. */
static ble_advertising_evt_handler_t m_evt_handler; /**< Handler for the advertising events. Can be initialized as NULL if no handling is implemented on in the main application. */
static ble_advertising_error_handler_t m_error_handler; /**< Handler for the advertising error events. */
static ble_adv_mode_t m_adv_mode_current; /**< Variable to keep track of the current advertising mode. */
static uint8_t m_direct_adv_cnt; /**< Counter of direct advertising retries. */
static bool m_whitelist_temporarily_disabled = false; /**< Flag to keep track of temporary disabling of the whitelist. */
static ble_gap_whitelist_t m_whitelist; /**< Struct that points to whitelisted addresses. */
static ble_gap_addr_t * mp_whitelist_addr[BLE_GAP_WHITELIST_ADDR_MAX_COUNT]; /**< Pointer to a list of addresses. Pointed to by the whitelist */
static ble_gap_irk_t * mp_whitelist_irk[BLE_GAP_WHITELIST_IRK_MAX_COUNT]; /**< Pointer to a list of Identity Resolving Keys (IRK). Pointed to by the whitelist */
static bool m_whitelist_reply_expected = false; /**< Flag to verify that whitelist is only set when it is requested. */
static bool m_peer_addr_reply_expected = false; /**< Flag to verify that. */
static ble_adv_modes_config_t m_adv_modes_config; /**< Struct to keep track of disabled and enabled advertising modes, as well as time-outs and intervals.*/
static ble_advdata_manuf_data_t m_manuf_specific_data; /**< Manufacturer specific data structure*/
static uint8_t m_manuf_data_array[BLE_GAP_ADV_MAX_SIZE]; /**< Array to store the Manufacturer specific data*/
static ble_advdata_service_data_t m_service_data; /**< Service data structure. */
static uint8_t m_service_data_array[BLE_GAP_ADV_MAX_SIZE]; /**< Array to store the service data. */
static ble_advdata_conn_int_t m_slave_conn_int; /**< Connection interval range structure.*/
static int8_t m_tx_power_level; /**< TX power level*/
/**@brief Function for checking that the whitelist has entries.
*/
static bool whitelist_has_entries(ble_gap_whitelist_t const * whitelist)
{
if ((whitelist->addr_count != 0) || (whitelist->irk_count != 0))
{
return true;
}
return false;
}
/**@brief Function for setting the stored peer address back to zero.
*/
static void ble_advertising_peer_address_clear()
{
memset(&m_peer_address, 0, sizeof(m_peer_address));
}
/**@brief Function for checking if an address is non-zero. Used to determine if
*/
static bool peer_address_exists(uint8_t const * address)
{
uint32_t i;
for (i = 0; i < BLE_GAP_ADDR_LEN; i++)
{
if (address[i] != 0)
{
return true;
}
}
return false;
}
uint32_t ble_advertising_init(ble_advdata_t const * p_advdata,
ble_adv_modes_config_t const * p_config,
ble_advertising_evt_handler_t const evt_handler,
ble_advertising_error_handler_t const error_handler)
{
uint32_t err_code;
if((p_advdata == NULL) || p_config == NULL)
{
return NRF_ERROR_NULL;
}
m_adv_mode_current = BLE_ADV_MODE_IDLE;
m_evt_handler = evt_handler;
m_error_handler = error_handler;
m_adv_modes_config = *p_config;
// If interval or timeout is 0, disable the mode.
if (m_adv_modes_config.ble_adv_fast_interval == 0 || m_adv_modes_config.ble_adv_fast_timeout == 0)
{
m_adv_modes_config.ble_adv_fast_enabled = false;
}
if (m_adv_modes_config.ble_adv_slow_interval == 0 || m_adv_modes_config.ble_adv_slow_timeout == 0)
{
m_adv_modes_config.ble_adv_slow_enabled = false;
}
ble_advertising_peer_address_clear();
// Prepare Whitelist. Address and IRK double pointers point to allocated arrays.
m_whitelist.pp_addrs = mp_whitelist_addr;
m_whitelist.pp_irks = mp_whitelist_irk;
// Copy and set advertising data.
memset(&m_advdata, 0, sizeof(m_advdata));
// Copy advertising data.
m_advdata.name_type = p_advdata->name_type;
m_advdata.include_appearance = p_advdata->include_appearance;
m_advdata.flags = p_advdata->flags;
m_advdata.short_name_len = p_advdata->short_name_len;
/*
if(p_advdata->uuids_complete != NULL)
{
m_advdata.uuids_complete = p_advdata->uuids_complete;
}
*/
m_advdata.uuids_complete = p_advdata->uuids_complete;
m_advdata.uuids_more_available = p_advdata->uuids_more_available;
m_advdata.uuids_solicited = p_advdata->uuids_solicited;
if(p_advdata->p_manuf_specific_data != NULL)
{
m_advdata.p_manuf_specific_data = &m_manuf_specific_data;
m_manuf_specific_data.data.p_data = m_manuf_data_array;
m_advdata.p_manuf_specific_data->company_identifier =
p_advdata->p_manuf_specific_data->company_identifier;
m_advdata.p_manuf_specific_data->data.size = p_advdata->p_manuf_specific_data->data.size;
for(uint32_t i = 0; i < m_advdata.p_manuf_specific_data->data.size; i++)
{
m_manuf_data_array[i] = p_advdata->p_manuf_specific_data->data.p_data[i];
}
}
if(p_advdata->p_service_data_array != NULL)
{
m_service_data.data.p_data = m_service_data_array;
m_advdata.p_service_data_array = &m_service_data;
m_advdata.p_service_data_array->data.p_data = m_service_data_array;
m_advdata.p_service_data_array->data.size = p_advdata->p_service_data_array->data.size;
m_advdata.p_service_data_array->service_uuid = p_advdata->p_service_data_array->service_uuid;
for(uint32_t i = 0; i < m_advdata.p_service_data_array->data.size; i++)
{
m_service_data_array[i] = p_advdata->p_service_data_array->data.p_data[i];
}
m_advdata.service_data_count = p_advdata->service_data_count;
}
if(p_advdata->p_slave_conn_int != NULL)
{
m_advdata.p_slave_conn_int = &m_slave_conn_int;
m_advdata.p_slave_conn_int->max_conn_interval = p_advdata->p_slave_conn_int->max_conn_interval;
m_advdata.p_slave_conn_int->min_conn_interval = p_advdata->p_slave_conn_int->min_conn_interval;
}
if(p_advdata->p_tx_power_level != NULL)
{
m_advdata.p_tx_power_level = &m_tx_power_level;
m_advdata.p_tx_power_level = p_advdata->p_tx_power_level;
}
err_code = ble_advdata_set(&m_advdata, NULL);
return err_code;
}
uint32_t ble_advertising_start(ble_adv_mode_t advertising_mode)
{
uint32_t err_code;
ble_gap_adv_params_t adv_params;
uint32_t count;
m_adv_mode_current = advertising_mode;
// Verify if there is any flash access pending. If there is, delay starting advertising until
// it is complete.
err_code = pstorage_access_status_get(&count);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
if (count != 0)
{
m_advertising_start_pending = true;
return NRF_SUCCESS;
}
// Fetch the peer address.
ble_advertising_peer_address_clear();
if ( (m_adv_modes_config.ble_adv_directed_enabled)
&& m_adv_mode_current == BLE_ADV_MODE_DIRECTED)
{
if (m_evt_handler != NULL)
{
m_peer_addr_reply_expected = true;
m_evt_handler(BLE_ADV_EVT_PEER_ADDR_REQUEST);
}
else
{
m_peer_addr_reply_expected = false;
}
}
// If a mode is disabled, continue to the next mode. I.e fast instead of direct, slow instead of fast, idle instead of slow.
if ( (m_adv_mode_current == BLE_ADV_MODE_DIRECTED)
&&(!m_adv_modes_config.ble_adv_directed_enabled || !peer_address_exists(m_peer_address.addr)))
{
m_adv_mode_current = BLE_ADV_MODE_FAST;
}
if (!m_adv_modes_config.ble_adv_fast_enabled && m_adv_mode_current == BLE_ADV_MODE_FAST)
{
m_adv_mode_current = BLE_ADV_MODE_SLOW;
}
if (!m_adv_modes_config.ble_adv_slow_enabled && m_adv_mode_current == BLE_ADV_MODE_SLOW)
{
m_adv_mode_current = BLE_ADV_MODE_IDLE;
m_adv_evt = BLE_ADV_EVT_IDLE;
}
// Fetch the whitelist.
if ( (m_evt_handler != NULL)
&& (m_adv_mode_current == BLE_ADV_MODE_FAST || m_adv_mode_current == BLE_ADV_MODE_SLOW)
&& (m_adv_modes_config.ble_adv_whitelist_enabled)
&& (!m_whitelist_temporarily_disabled))
{
m_whitelist_reply_expected = true;
m_evt_handler(BLE_ADV_EVT_WHITELIST_REQUEST);
}
else
{
m_whitelist_reply_expected = false;
}
// Initialize advertising parameters with default values.
memset(&adv_params, 0, sizeof(adv_params));
adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
adv_params.p_peer_addr = NULL;
adv_params.fp = BLE_GAP_ADV_FP_ANY;
adv_params.p_whitelist = NULL;
// Set advertising parameters and events according to selected advertising mode.
switch (m_adv_mode_current)
{
case BLE_ADV_MODE_DIRECTED:
LOG("[ADV]: Starting direct advertisement.\r\n");
adv_params.p_peer_addr = &m_peer_address; // Directed advertising.
adv_params.type = BLE_GAP_ADV_TYPE_ADV_DIRECT_IND;
adv_params.timeout = 0; // High dutycycle.
adv_params.interval = 0; // High dutycycle.
m_adv_evt = BLE_ADV_EVT_DIRECTED;
break;
case BLE_ADV_MODE_FAST:
adv_params.timeout = m_adv_modes_config.ble_adv_fast_timeout;
adv_params.interval = m_adv_modes_config.ble_adv_fast_interval;
if ( whitelist_has_entries(&m_whitelist)
&& m_adv_modes_config.ble_adv_whitelist_enabled
&& !m_whitelist_temporarily_disabled)
{
adv_params.fp = BLE_GAP_ADV_FP_FILTER_CONNREQ;
adv_params.p_whitelist = &m_whitelist;
m_advdata.flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;
err_code = ble_advdata_set(&m_advdata, NULL);
if(err_code != NRF_SUCCESS)
{
return err_code;
}
m_adv_evt = BLE_ADV_EVT_FAST_WHITELIST;
LOG("[ADV]: Starting fast advertisement with whitelist.\r\n");
}
else
{
m_adv_evt = BLE_ADV_EVT_FAST;
LOG("[ADV]: Starting fast advertisement.\r\n");
}
break;
case BLE_ADV_MODE_SLOW:
adv_params.interval = m_adv_modes_config.ble_adv_slow_interval;
adv_params.timeout = m_adv_modes_config.ble_adv_slow_timeout;
if ( whitelist_has_entries(&m_whitelist)
&& m_adv_modes_config.ble_adv_whitelist_enabled
&& !m_whitelist_temporarily_disabled)
{
adv_params.fp = BLE_GAP_ADV_FP_FILTER_CONNREQ;
adv_params.p_whitelist = &m_whitelist;
m_advdata.flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;
err_code = ble_advdata_set(&m_advdata, NULL);
if(err_code != NRF_SUCCESS)
{
return err_code;
}
m_adv_evt = BLE_ADV_EVT_SLOW_WHITELIST;
LOG("[ADV]: Starting slow advertisement with whitelist.\r\n");
}
else
{
m_adv_evt = BLE_ADV_EVT_SLOW;
LOG("[ADV]: Starting slow advertisement.\r\n");
}
break;
default:
break;
}
if (m_adv_mode_current != BLE_ADV_MODE_IDLE)
{
err_code = sd_ble_gap_adv_start(&adv_params);
if(err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (m_evt_handler != NULL)
{
m_evt_handler(m_adv_evt);
}
return NRF_SUCCESS;
}
void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt)
{
static uint16_t current_slave_link_conn_handle = BLE_CONN_HANDLE_INVALID;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
#ifdef S130
if (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_PERIPH)
{
current_slave_link_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
#else
current_slave_link_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
#endif
break;
// Upon disconnection, whitelist will be activated and direct advertising is started.
case BLE_GAP_EVT_DISCONNECTED:
{
uint32_t err_code;
m_whitelist_temporarily_disabled = false;
m_direct_adv_cnt = m_adv_modes_config.ble_adv_directed_timeout;
if (p_ble_evt->evt.gap_evt.conn_handle == current_slave_link_conn_handle)
{
err_code = ble_advertising_start(BLE_ADV_MODE_DIRECTED);
if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL))
{
m_error_handler(err_code);
}
}
break;
}
// Upon time-out, the next advertising mode is started, i.e. go from fast to slow or from slow to idle.
case BLE_GAP_EVT_TIMEOUT:
if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISING)
{
switch (m_adv_mode_current)
{
case BLE_ADV_MODE_DIRECTED:
LOG("[ADV]: Timed out from directed advertising.\r\n");
if ((m_direct_adv_cnt > 0) && peer_address_exists(m_peer_address.addr))
{
uint32_t err_code;
m_direct_adv_cnt--;
LOG("[ADV]: Remaining direct advertising attempts: %d\r\n",
m_direct_adv_cnt);
err_code = ble_advertising_start(BLE_ADV_MODE_DIRECTED);
if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL))
{
m_error_handler(err_code);
}
}
else
{
uint32_t err_code;
err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL))
{
m_error_handler(err_code);
}
}
break;
case BLE_ADV_MODE_FAST:
{
uint32_t err_code;
m_adv_evt = BLE_ADV_EVT_FAST;
LOG("[ADV]: Timed out from fast advertising, starting slow advertising.\r\n");
err_code = ble_advertising_start(BLE_ADV_MODE_SLOW);
if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL))
{
m_error_handler(err_code);
}
break;
}
case BLE_ADV_MODE_SLOW:
m_adv_evt = BLE_ADV_EVT_IDLE;
LOG("[ADV]: Timed out from slow advertising, stopping advertising.\r\n");
if (m_evt_handler != NULL)
{
m_evt_handler(m_adv_evt);
}
break;
default:
// No implementation needed.
break;
}
}
break;
default:
// No implementation needed.
break;
}
}
void ble_advertising_on_sys_evt(uint32_t sys_evt)
{
uint32_t err_code = NRF_SUCCESS;
switch (sys_evt)
{
case NRF_EVT_FLASH_OPERATION_SUCCESS:
// Fall through.
//When a flash operation finishes, advertising no longer needs to be pending.
case NRF_EVT_FLASH_OPERATION_ERROR:
if (m_advertising_start_pending)
{
m_advertising_start_pending = false;
err_code = ble_advertising_start(m_adv_mode_current);
if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL))
{
m_error_handler(err_code);
}
}
break;
default:
// No implementation needed.
break;
}
}
uint32_t ble_advertising_peer_addr_reply(ble_gap_addr_t * p_peer_address)
{
if(m_peer_addr_reply_expected == false)
{
return NRF_ERROR_INVALID_STATE;
}
m_peer_address.addr_type = p_peer_address->addr_type;
for (int i = 0; i < BLE_GAP_ADDR_LEN; i++)
{
m_peer_address.addr[i] = p_peer_address->addr[i];
}
m_peer_addr_reply_expected = false;
return NRF_SUCCESS;
}
uint32_t ble_advertising_whitelist_reply(ble_gap_whitelist_t * p_whitelist)
{
uint32_t i;
if(m_whitelist_reply_expected == false)
{
return NRF_ERROR_INVALID_STATE;
}
m_whitelist.addr_count = p_whitelist->addr_count;
m_whitelist.irk_count = p_whitelist->irk_count;
for (i = 0; i < m_whitelist.irk_count; i++)
{
mp_whitelist_irk[i] = p_whitelist->pp_irks[i];
}
for (i = 0; i < m_whitelist.addr_count; i++)
{
mp_whitelist_addr[i] = p_whitelist->pp_addrs[i];
}
m_whitelist_reply_expected = false;
return NRF_SUCCESS;
}
uint32_t ble_advertising_restart_without_whitelist(void)
{
uint32_t err_code;
if( m_adv_modes_config.ble_adv_whitelist_enabled == BLE_ADV_WHITELIST_ENABLED
&& !m_whitelist_temporarily_disabled)
{
if (m_adv_mode_current != BLE_ADV_MODE_IDLE)
{
err_code = sd_ble_gap_adv_stop();
if(err_code != NRF_SUCCESS)
{
return err_code;
}
}
m_whitelist_temporarily_disabled = true;
err_code = ble_advertising_start(m_adv_mode_current);
if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL))
{
m_error_handler(err_code);
}
}
return NRF_SUCCESS;
}