/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. * * The information contained herein is property of Nordic Semiconductor ASA. * Terms and conditions of usage are described in detail in NORDIC * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. * * Licensees are granted free, non-transferable use of the information. NO * WARRANTY of ANY KIND is provided. This heading must NOT be removed from * the file. * */ /** @example examples/ble_peripheral/ble_app_hrs/main.c * * @brief Heart Rate Service Sample Application main file. * * This file contains the source code for a sample application using the Heart Rate service * (and also Battery and Device Information services). This application uses the * @ref srvlib_conn_params module. */ #include #include #include "nordic_common.h" #include "nrf.h" #include "nrf_delay.h" #include "nrf51_bitfields.h" #include "ble.h" #include "ble_hci.h" #include "ble_srv_common.h" #include "ble_advdata.h" #include "ble_gatt.h" #include "ble_bas.h" #include "ble_hrs.h" #include "ble_dis.h" #ifdef BLE_DFU_APP_SUPPORT #include "ble_dfu.h" #include "dfu_app_handler.h" #endif // BLE_DFU_APP_SUPPORT #include "ble_conn_params.h" #include "boards.h" #include "ble_sensorsim.h" #include "softdevice_handler.h" #include "device_manager.h" #include "pstorage.h" #include "app_error.h" #include "app_trace.h" #include "app_timer.h" #include "app_gpiote.h" #include "bsp.h" #include "phone_remote.h" #include "LiquidCrystal.h" #include #define dbg printf #define IS_SRVC_CHANGED_CHARACT_PRESENT 0 /**< Include or not the service_changed characteristic. if not enabled, the server's database cannot be changed for the lifetime of the device*/ // BOARD_PCA10031 doesn't have any buttons according to the BSP #ifndef BOARD_PCA10031 #define WAKEUP_BUTTON_ID 0 /**< Button used to wake up the application. */ #define BOND_DELETE_ALL_BUTTON_ID 1 /**< Button used for deleting all bonded centrals during startup. */ #endif #define DEVICE_NAME "Woot-51" #define MANUFACTURER_NAME "NordicSemiconductor" #define APP_ADV_INTERVAL 40 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 25 ms). */ #define APP_ADV_TIMEOUT_IN_SECONDS 180 /**< The advertising timeout in units of seconds. */ #define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */ #define APP_TIMER_MAX_TIMERS (6+BSP_APP_TIMERS_NUMBER) /**< Maximum number of simultaneously created timers. */ #define APP_TIMER_OP_QUEUE_SIZE 4 /**< Size of timer operation queues. */ #define SENSOR_CONTACT_DETECTED_INTERVAL APP_TIMER_TICKS(5000, APP_TIMER_PRESCALER) /**< Sensor Contact Detected toggle interval (ticks). */ #define MIN_CONN_INTERVAL MSEC_TO_UNITS(500, UNIT_1_25_MS) /**< Minimum acceptable connection interval (0.5 seconds). */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(1000, UNIT_1_25_MS) /**< Maximum acceptable connection interval (1 second). */ #define SLAVE_LATENCY 0 /**< Slave latency. */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds). */ #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000, APP_TIMER_PRESCALER) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */ #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 SEC_PARAM_TIMEOUT 30 /**< Timeout for Pairing Request or Security Request (in seconds). */ #define SEC_PARAM_BOND 1 /**< Perform bonding. */ #define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */ #define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */ #define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */ #define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */ #define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */ #define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */ static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ static ble_gap_adv_params_t m_adv_params; /**< Parameters to be passed to the stack when starting advertising. */ static dm_application_instance_t m_app_handle; /**< Application identifier allocated by device manager */ static bool m_memory_access_in_progress = false; /**< Flag to keep track of ongoing operations on persistent memory. */ static app_timer_id_t hello_timer; void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) { app_error_handler(DEAD_BEEF, line_num, p_file_name); } static uint32_t seconds = 0; static void hello_timer_handler(void* data) { uint32_t ticks; app_timer_cnt_get(&ticks); uint32_t ms = (1000 * (1 + APP_TIMER_PRESCALER) * ticks) / APP_TIMER_CLOCK_FREQ; seconds++; printf("hello world, seconds=%" PRIu32 ", ticks=%" PRIu32 ", ms=%" PRIu32 "\r\n", seconds, ticks, ms); // liquid_crystal_clear(); // char buf0[8]; // liquid_crystal_set_cursor(0, 1); uint32_t value = 1337; app_timer_cnt_get(&value); char buf1[9]; // snprintf(buf1, sizeof(buf1), "%" PRIu32, value); int size = snprintf(buf1, sizeof(buf1), "%" PRIu32, seconds); // liquid_crystal_write_string("hello!"); liquid_crystal_write_string_len(buf1, size); // liquid_crystal_write_string(buf1); buf1[8] = '\0'; printf("buf: %s\r\n", buf1); } static void timers_init(void) { uint32_t err_code; // Initialize timer module. APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false); err_code = app_timer_create(&hello_timer, APP_TIMER_MODE_REPEATED, hello_timer_handler); APP_ERROR_CHECK(err_code); } static void timers_start() { uint32_t err_code; uint32_t ticks = APP_TIMER_TICKS(1000, APP_TIMER_PRESCALER); err_code = app_timer_start(hello_timer, ticks, NULL); APP_ERROR_CHECK(err_code); } 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)); APP_ERROR_CHECK(err_code); err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT); 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); } 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; ble_uuid_t adv_uuids[] = {}; // Build and set advertising data. memset(&advdata, 0, sizeof(advdata)); advdata.name_type = BLE_ADVDATA_FULL_NAME; advdata.include_appearance = true; advdata.flags.size = sizeof(flags); advdata.flags.p_data = &flags; advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]); advdata.uuids_complete.p_uuids = adv_uuids; err_code = ble_advdata_set(&advdata, NULL); APP_ERROR_CHECK(err_code); // Initialize advertising parameters (used when starting advertising). memset(&m_adv_params, 0, sizeof(m_adv_params)); m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND; m_adv_params.p_peer_addr = NULL; // Undirected advertisement. m_adv_params.fp = BLE_GAP_ADV_FP_ANY; m_adv_params.interval = APP_ADV_INTERVAL; m_adv_params.timeout = APP_ADV_TIMEOUT_IN_SECONDS; } /**@brief Function for initializing services that will be used by the application. * * @details Initialize the Heart Rate, Battery and Device Information services. */ static void services_init(void) { uint32_t err_code = NRF_SUCCESS; // err_code = sm_add_services(); APP_ERROR_CHECK(err_code); } static void advertising_start(void) { uint32_t err_code; uint32_t count; // 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; } err_code = sd_ble_gap_adv_start(&m_adv_params); APP_ERROR_CHECK(err_code); err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING); APP_ERROR_CHECK(err_code); } 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); } 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.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); } static void on_ble_evt(ble_evt_t * p_ble_evt) { uint32_t err_code; 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; break; case BLE_GAP_EVT_DISCONNECTED: err_code = bsp_indication_set(BSP_INDICATE_IDLE); APP_ERROR_CHECK(err_code); advertising_start(); break; case BLE_GAP_EVT_TIMEOUT: if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISEMENT) { printf("re-starting advertisement\n"); advertising_start(); /* err_code = bsp_indication_set(BSP_INDICATE_IDLE); APP_ERROR_CHECK(err_code); // Go to system-off mode (this function will not return; wakeup will cause a reset). err_code = sd_power_system_off(); APP_ERROR_CHECK(err_code); */ } break; default: // No implementation needed. break; } } static void ble_evt_dispatch(ble_evt_t * p_ble_evt) { // sm_on_ble_evt(p_ble_evt); dm_ble_evt_handler(p_ble_evt); on_ble_evt(p_ble_evt); } static void sys_evt_dispatch(uint32_t sys_evt) { pstorage_sys_event_handler(sys_evt); } static void ble_stack_init(void) { uint32_t err_code; // Initialize the SoftDevice handler module. SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false); 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); err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch); APP_ERROR_CHECK(err_code); err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch); APP_ERROR_CHECK(err_code); } static uint32_t device_manager_evt_handler(dm_handle_t const * p_handle, dm_event_t const * p_event, api_result_t event_result) { APP_ERROR_CHECK(event_result); switch(p_event->event_id) { } return NRF_SUCCESS; } 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); init_data.clear_persistent_data = false; // was true 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.sec_param.timeout = SEC_PARAM_TIMEOUT; register_param.sec_param.bond = SEC_PARAM_BOND; register_param.sec_param.mitm = SEC_PARAM_MITM; register_param.sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; register_param.sec_param.oob = SEC_PARAM_OOB; register_param.sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; register_param.sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; 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); } static void power_manage(void) { uint32_t err_code = sd_app_evt_wait(); APP_ERROR_CHECK(err_code); } int main(void) { app_trace_init(); dbg("Phone remote starting...\r\n"); ble_stack_init(); timers_init(); APP_GPIOTE_INIT(2); uint32_t err_code = bsp_init(BSP_INIT_LED | BSP_INIT_BUTTONS, APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), NULL); APP_ERROR_CHECK(err_code); liquid_crystal_init(false, true, false); liquid_crystal_reset(); /* liquid_crystal_display(true, false, false); liquid_crystal_write_string("01234567890123456"); */ /* char chars[] = "abcdefghijklmopqrstuvwxyz"; for(int i = 0; i < 10; i++) { nrf_delay_ms(200); liquid_crystal_write_char(chars[i]); } nrf_delay_ms(1000); liquid_crystal_clear(); */ liquid_crystal_display(true, false, false); nrf_delay_ms(500); liquid_crystal_display(false, false, false); nrf_delay_ms(500); liquid_crystal_display(true, false, false); nrf_delay_ms(500); liquid_crystal_display(false, false, false); nrf_delay_ms(500); liquid_crystal_display(true, true, true); nrf_delay_ms(500); // liquid_crystal_return_home(); liquid_crystal_set_cursor(1, 1); liquid_crystal_write_string("hello!"); /* device_manager_init(); gap_params_init(); advertising_init(); services_init(); conn_params_init(); advertising_start(); */ timers_start(); for (;; ) { power_manage(); } }