I am developing a nRF52832 based project where I want to position people indoor using an smartband (where the nRF52832 is placed). I was planning to use beacons so I can change their names and say, for example, BEACON_1, BEACON_2, etc.. The idea is that the smartband reads the nearby beacons, get its RSSI and DEVICE NAME (or UUID, address, still no idea..) and send this information from the smartband to the Internet (using other technologies).
I started with the ble_appp_multilink_central_pca10040_s132 example using the nRF5_SDK_15.3.0 and I modified it a little bit:
First I modified the static void scan_init(void){} function so the device doesn't connect if NAME filter matches.
init_scan.connect_if_match = false; //NO AUTOMATIC CONNECT IF MATCH
I am constantly scaning devices that matches the first part of its name with "BEACON_". To do so I looked on another post and modified the ble_advdata.c (lines 688-691):, specially these lines from:
if ( (data_offset != 0) && (parsed_name_len != 0) && (strlen(p_target_name) == parsed_name_len) && (memcmp(p_target_name, p_parsed_name, parsed_name_len) == 0))*/ //MATCH ALL NAME
To:
if ( (data_offset != 0) && (parsed_name_len != 0) && (parsed_name_len >= strlen(p_target_name)) && (memcmp(p_target_name, p_parsed_name, strlen(p_target_name)) == 0)) //JUST MATCH FIRST PART OF THE NAME
I'm simulating beacons using the nRFConnect app (from iPhone 13) and creating three different ADVERTISEMENTS beacons (BEACON_1, BEACON_2, BEACON_3). It definitely works and they match correctly with the filter. If I change its name into another, lets say BAACON_1, it doesn't match.
I am printing all this staff through the UART in the scan_-evt_handler(...){}:
static void scan_evt_handler(scan_evt_t const * p_scan_evt)
{
ret_code_t err_code;
switch(p_scan_evt->scan_evt_id)
{
case NRF_BLE_SCAN_EVT_FILTER_MATCH: //CASE ADDED
{
int read_rssi = p_scan_evt->params.filter_match.p_adv_report->rssi;
/*NRF_LOG_INFO("Received advertising from: %02x:%02x:%02x:%02x:%02x:%02x:%02x", p_scan_evt->params.p_not_found->peer_addr.addr[0],
p_scan_evt->params.p_not_found->peer_addr.addr[1],
p_scan_evt->params.p_not_found->peer_addr.addr[2],
p_scan_evt->params.p_not_found->peer_addr.addr[3],
p_scan_evt->params.p_not_found->peer_addr.addr[4],
p_scan_evt->params.p_not_found->peer_addr.addr[5]);*/
//PRINTS PEER ID OF THE CURRENT SCAN
NRF_LOG_RAW_INFO("PEER ID = ")
for (int i = 0; i < BLE_GAP_ADDR_LEN; i++){
NRF_LOG_RAW_INFO("%02x", p_scan_evt->params.connected.p_connected->peer_addr.addr[i]);
}
NRF_LOG_RAW_INFO("\r\n");
//PRINTS RSSI VALUE
NRF_LOG_RAW_INFO("RSSI = %d", read_rssi);
NRF_LOG_RAW_INFO("\r\n");
NRF_LOG_RAW_INFO("\r\n");
//NRF_LOG_INFO("RSSI = %d", read_rssi);
break;
}
case NRF_BLE_SCAN_EVT_CONNECTING_ERROR:
{
err_code = p_scan_evt->params.connecting_err.err_code;
APP_ERROR_CHECK(err_code);
} break;
default:
break;
}
}
I want the full device name, uuid or device address (whatever I need) to be read in the scan_-evt_handler(...){} so I can know which beacon is the one I am receiving from. My plan is to buy cheap beacons from Amazon, AliExpress... and change its Device Name, UUID or whatever its the easiest (here I am open to new ideas...).
Hope everything is well explained. I am new to the BLE world (and also of nRF52 family, ofc) so I don't have much idea between difference in scan, advertisings, services, GAT and all that stuff. I post my entire main.c so you can check it out:
/** * Copyright (c) 2014 - 2019, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /** * @brief BLE LED Button Service central and client application main file. * * This example can be a central for up to 8 peripherals. * The peripheral is called ble_app_blinky and can be found in the ble_peripheral * folder. */ #include <stdint.h> #include <stdio.h> #include <string.h> #include "nordic_common.h" #include "nrf_sdh.h" #include "nrf_sdh_ble.h" #include "app_timer.h" #include "bsp_btn_ble.h" #include "ble.h" #include "ble_hci.h" #include "ble_advertising.h" #include "ble_conn_params.h" #include "ble_db_discovery.h" #include "ble_lbs_c.h" #include "ble_conn_state.h" #include "nrf_ble_gatt.h" #include "nrf_pwr_mgmt.h" #include "nrf_ble_scan.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #define APP_BLE_CONN_CFG_TAG 1 /**< Tag that refers to the BLE stack configuration that is set with @ref sd_ble_cfg_set. The default tag is @ref APP_BLE_CONN_CFG_TAG. */ #define APP_BLE_OBSERVER_PRIO 3 /**< BLE observer priority of the application. There is no need to modify this value. */ #define CENTRAL_SCANNING_LED BSP_BOARD_LED_0 #define CENTRAL_CONNECTED_LED BSP_BOARD_LED_1 #define LEDBUTTON_LED BSP_BOARD_LED_2 /**< LED to indicate a change of state of the Button characteristic on the peer. */ #define LEDBUTTON_BUTTON BSP_BUTTON_0 /**< Button that writes to the LED characteristic of the peer. */ #define BUTTON_DETECTION_DELAY APP_TIMER_TICKS(50) /**< Delay from a GPIOTE event until a button is reported as pushed (in number of timer ticks). */ NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */ BLE_LBS_C_ARRAY_DEF(m_lbs_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT); /**< LED button client instances. */ BLE_DB_DISCOVERY_ARRAY_DEF(m_db_disc, NRF_SDH_BLE_CENTRAL_LINK_COUNT); /**< Database discovery module instances. */ NRF_BLE_SCAN_DEF(m_scan); /**< Scanning Module instance. */ static char const m_target_periph_name[] = "BEACON_"; /**< Name of the device to try to connect to. This name is searched for in the scanning report data. */ /**@brief Function for handling asserts in the SoftDevice. * * @details This function is called in case of an assert in the SoftDevice. * * @warning This handler is only an example and is not meant for the final product. You need to analyze * how your product is supposed to react in case of an 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] p_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(0xDEADBEEF, line_num, p_file_name); } /**@brief Function for initializing the LEDs. * * @details Initializes all LEDs used by the application. */ static void leds_init(void) { bsp_board_init(BSP_INIT_LEDS); } static void scan_evt_handler(scan_evt_t const * p_scan_evt) { ret_code_t err_code; switch(p_scan_evt->scan_evt_id) { case NRF_BLE_SCAN_EVT_FILTER_MATCH: //CASE ADDED { int read_rssi = p_scan_evt->params.filter_match.p_adv_report->rssi; /*NRF_LOG_INFO("Received advertising from: %02x:%02x:%02x:%02x:%02x:%02x:%02x", p_scan_evt->params.p_not_found->peer_addr.addr[0], p_scan_evt->params.p_not_found->peer_addr.addr[1], p_scan_evt->params.p_not_found->peer_addr.addr[2], p_scan_evt->params.p_not_found->peer_addr.addr[3], p_scan_evt->params.p_not_found->peer_addr.addr[4], p_scan_evt->params.p_not_found->peer_addr.addr[5]);*/ //PRINTS PEER ID OF THE CURRENT SCAN NRF_LOG_RAW_INFO("PEER ID = ") for (int i = 0; i < BLE_GAP_ADDR_LEN; i++){ NRF_LOG_RAW_INFO("%02x", p_scan_evt->params.connected.p_connected->peer_addr.addr[i]); } NRF_LOG_RAW_INFO("\r\n"); //PRINTS RSSI VALUE NRF_LOG_RAW_INFO("RSSI = %d", read_rssi); NRF_LOG_RAW_INFO("\r\n"); NRF_LOG_RAW_INFO("\r\n"); //NRF_LOG_INFO("RSSI = %d", read_rssi); break; } case NRF_BLE_SCAN_EVT_CONNECTING_ERROR: { err_code = p_scan_evt->params.connecting_err.err_code; APP_ERROR_CHECK(err_code); } break; default: break; } } /**@brief Function for initializing the scanning and setting the filters. */ static void scan_init(void) { ret_code_t err_code; nrf_ble_scan_init_t init_scan; memset(&init_scan, 0, sizeof(init_scan)); init_scan.connect_if_match = false; //NO AUTOMATIC CONNECT IF MATCH init_scan.conn_cfg_tag = APP_BLE_CONN_CFG_TAG; err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler); APP_ERROR_CHECK(err_code); err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_NAME_FILTER, m_target_periph_name); APP_ERROR_CHECK(err_code); err_code = nrf_ble_scan_filters_enable(&m_scan, NRF_BLE_SCAN_NAME_FILTER, false); APP_ERROR_CHECK(err_code); } /**@brief Function for starting scanning. */ static void scan_start(void) { ret_code_t ret; NRF_LOG_INFO("Start scanning for device name %s.", (uint32_t)m_target_periph_name); ret = nrf_ble_scan_start(&m_scan); APP_ERROR_CHECK(ret); // Turn on the LED to signal scanning. bsp_board_led_on(CENTRAL_SCANNING_LED); } /**@brief Handles events coming from the LED Button central module. * * @param[in] p_lbs_c The instance of LBS_C that triggered the event. * @param[in] p_lbs_c_evt The LBS_C event. */ static void lbs_c_evt_handler(ble_lbs_c_t * p_lbs_c, ble_lbs_c_evt_t * p_lbs_c_evt) { switch (p_lbs_c_evt->evt_type) { case BLE_LBS_C_EVT_DISCOVERY_COMPLETE: { ret_code_t err_code; NRF_LOG_INFO("LED Button Service discovered on conn_handle 0x%x", p_lbs_c_evt->conn_handle); err_code = app_button_enable(); APP_ERROR_CHECK(err_code); // LED Button Service discovered. Enable notification of Button. err_code = ble_lbs_c_button_notif_enable(p_lbs_c); APP_ERROR_CHECK(err_code); } break; // BLE_LBS_C_EVT_DISCOVERY_COMPLETE case BLE_LBS_C_EVT_BUTTON_NOTIFICATION: { NRF_LOG_INFO("Link 0x%x, Button state changed on peer to 0x%x", p_lbs_c_evt->conn_handle, p_lbs_c_evt->params.button.button_state); if (p_lbs_c_evt->params.button.button_state) { bsp_board_led_on(LEDBUTTON_LED); } else { bsp_board_led_off(LEDBUTTON_LED); } } break; // BLE_LBS_C_EVT_BUTTON_NOTIFICATION default: // No implementation needed. break; } } /**@brief Function for handling BLE events. * * @param[in] p_ble_evt Bluetooth stack event. * @param[in] p_context Unused. */ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) { ret_code_t err_code; // For readability. ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt; switch (p_ble_evt->header.evt_id) { // Upon connection, check which peripheral is connected, initiate DB // discovery, update LEDs status, and resume scanning, if necessary. case BLE_GAP_EVT_CONNECTED: { NRF_LOG_INFO("Connection 0x%x established, starting DB discovery.", p_gap_evt->conn_handle); APP_ERROR_CHECK_BOOL(p_gap_evt->conn_handle < NRF_SDH_BLE_CENTRAL_LINK_COUNT); err_code = ble_lbs_c_handles_assign(&m_lbs_c[p_gap_evt->conn_handle], p_gap_evt->conn_handle, NULL); APP_ERROR_CHECK(err_code); err_code = ble_db_discovery_start(&m_db_disc[p_gap_evt->conn_handle], p_gap_evt->conn_handle); if (err_code != NRF_ERROR_BUSY) { APP_ERROR_CHECK(err_code); } // Update LEDs status and check whether it is needed to look for more // peripherals to connect to. bsp_board_led_on(CENTRAL_CONNECTED_LED); if (ble_conn_state_central_conn_count() == NRF_SDH_BLE_CENTRAL_LINK_COUNT) { bsp_board_led_off(CENTRAL_SCANNING_LED); } else { // Resume scanning. bsp_board_led_on(CENTRAL_SCANNING_LED); scan_start(); } } break; // BLE_GAP_EVT_CONNECTED // Upon disconnection, reset the connection handle of the peer that disconnected, update // the LEDs status and start scanning again. case BLE_GAP_EVT_DISCONNECTED: { NRF_LOG_INFO("LBS central link 0x%x disconnected (reason: 0x%x)", p_gap_evt->conn_handle, p_gap_evt->params.disconnected.reason); if (ble_conn_state_central_conn_count() == 0) { err_code = app_button_disable(); APP_ERROR_CHECK(err_code); // Turn off the LED that indicates the connection. bsp_board_led_off(CENTRAL_CONNECTED_LED); } // Start scanning. scan_start(); // Turn on the LED for indicating scanning. bsp_board_led_on(CENTRAL_SCANNING_LED); } break; case BLE_GAP_EVT_TIMEOUT: { // Timeout for scanning is not specified, so only the connection requests can time out. if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { NRF_LOG_DEBUG("Connection request timed out."); } } break; case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { NRF_LOG_DEBUG("BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST."); // Accept parameters requested by peer. err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle, &p_gap_evt->params.conn_param_update_request.conn_params); APP_ERROR_CHECK(err_code); } break; case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { NRF_LOG_DEBUG("PHY update request."); ble_gap_phys_t const phys = { .rx_phys = BLE_GAP_PHY_AUTO, .tx_phys = BLE_GAP_PHY_AUTO, }; 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: { // Disconnect on GATT client timeout event. NRF_LOG_DEBUG("GATT client timeout."); 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: { // Disconnect on GATT server timeout event. NRF_LOG_DEBUG("GATT server timeout."); 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; default: // No implementation needed. break; } } /**@brief LED Button collector initialization. */ static void lbs_c_init(void) { ret_code_t err_code; ble_lbs_c_init_t lbs_c_init_obj; lbs_c_init_obj.evt_handler = lbs_c_evt_handler; for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++) { err_code = ble_lbs_c_init(&m_lbs_c[i], &lbs_c_init_obj); APP_ERROR_CHECK(err_code); } } /**@brief Function for initializing the BLE stack. * * @details Initializes the SoftDevice and the BLE event interrupts. */ static void ble_stack_init(void) { ret_code_t err_code; err_code = nrf_sdh_enable_request(); APP_ERROR_CHECK(err_code); // Configure the BLE stack using the default settings. // Fetch the start address of the application RAM. 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 a handler for BLE events. NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); } /**@brief Function for writing to the LED characteristic of all connected clients. * * @details Based on whether the button is pressed or released, this function writes a high or low * LED status to the server. * * @param[in] button_action The button action (press or release). * Determines whether the LEDs of the servers are ON or OFF. * * @return If successful, NRF_SUCCESS is returned. Otherwise, returns the error code from @ref ble_lbs_led_status_send. */ static ret_code_t led_status_send_to_all(uint8_t button_action) { ret_code_t err_code; for (uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++) { err_code = ble_lbs_led_status_send(&m_lbs_c[i], button_action); if (err_code != NRF_SUCCESS && err_code != BLE_ERROR_INVALID_CONN_HANDLE && err_code != NRF_ERROR_INVALID_STATE) { return err_code; } } return NRF_SUCCESS; } /**@brief Function for handling events from the button handler module. * * @param[in] pin_no The pin that the event applies to. * @param[in] button_action The button action (press or release). */ static void button_event_handler(uint8_t pin_no, uint8_t button_action) { ret_code_t err_code; switch (pin_no) { case LEDBUTTON_BUTTON: err_code = led_status_send_to_all(button_action); if (err_code == NRF_SUCCESS) { NRF_LOG_INFO("LBS write LED state %d", button_action); } break; default: APP_ERROR_HANDLER(pin_no); break; } } /**@brief Function for initializing the button handler module. */ static void buttons_init(void) { ret_code_t err_code; // The array must be static because a pointer to it is saved in the button handler module. static app_button_cfg_t buttons[] = { {LEDBUTTON_BUTTON, false, BUTTON_PULL, button_event_handler} }; err_code = app_button_init(buttons, ARRAY_SIZE(buttons), BUTTON_DETECTION_DELAY); APP_ERROR_CHECK(err_code); } /**@brief Function for handling database discovery events. * * @details This function is a callback function to handle events from the database discovery module. * Depending on the UUIDs that are discovered, this function forwards the events * to their respective services. * * @param[in] p_event Pointer to the database discovery event. */ static void db_disc_handler(ble_db_discovery_evt_t * p_evt) { NRF_LOG_DEBUG("call to ble_lbs_on_db_disc_evt for instance %d and link 0x%x!", p_evt->conn_handle, p_evt->conn_handle); ble_lbs_on_db_disc_evt(&m_lbs_c[p_evt->conn_handle], p_evt); } /** @brief Database discovery initialization. */ static void db_discovery_init(void) { ret_code_t err_code = ble_db_discovery_init(db_disc_handler); APP_ERROR_CHECK(err_code); } /**@brief Function for initializing power management. */ static void power_management_init(void) { ret_code_t err_code; err_code = nrf_pwr_mgmt_init(); APP_ERROR_CHECK(err_code); } /**@brief Function for handling the idle state (main loop). * * @details This function handles any pending log operations, then sleeps until the next event occurs. */ static void idle_state_handle(void) { if (NRF_LOG_PROCESS() == false) { nrf_pwr_mgmt_run(); } } /** @brief Function for initializing the log module. */ static void log_init(void) { ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT(); } /** @brief Function for initializing the timer. */ static void timer_init(void) { ret_code_t err_code = app_timer_init(); APP_ERROR_CHECK(err_code); } /**@brief Function for initializing the GATT module. */ static void gatt_init(void) { ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL); APP_ERROR_CHECK(err_code); } int main(void) { // Initialize. log_init(); timer_init(); leds_init(); buttons_init(); power_management_init(); ble_stack_init(); gatt_init(); db_discovery_init(); lbs_c_init(); ble_conn_state_init(); scan_init(); // Start execution. NRF_LOG_INFO("Multilink example started."); scan_start(); for (;;) { idle_state_handle(); } }
Thank you in advance,
Toni