From 852d7b2032e45a091eed638c249d8b48d850cbea Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Tue, 17 May 2022 04:18:21 +0200 Subject: [PATCH] Add Bluetooth BLE scan example --- sdkconfig.defaults | 2 + src/main.rs | 160 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 3 deletions(-) diff --git a/sdkconfig.defaults b/sdkconfig.defaults index f5b63343..85a5c69c 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -22,3 +22,5 @@ CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y # Future: proper back-trace for esp32c3 #CONFIG_ESP_SYSTEM_USE_EH_FRAME=y + +CONFIG_BT_ENABLED=y diff --git a/src/main.rs b/src/main.rs index fc8d4d6c..5e0c117f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,8 +77,7 @@ use esp_idf_hal::i2c; use esp_idf_hal::prelude::*; use esp_idf_hal::spi; -use esp_idf_sys::{self, c_types}; -use esp_idf_sys::{esp, EspError}; +use esp_idf_sys::{self, c_types, esp, esp_nofail, EspError}; use display_interface_spi::SPIInterfaceNoCS; @@ -264,6 +263,9 @@ fn main() -> Result<()> { None, )?))?; + #[cfg(not(feature = "qemu"))] + bluetooth()?; + test_tcp()?; test_tcp_bind()?; @@ -910,7 +912,7 @@ fn heltec_hello_world( .init() .map_err(|e| anyhow::anyhow!("Display error: {:?}", e))?; - led_draw(&mut display)?; + led_draw(&mut display).map_err(|e| anyhow::anyhow!("Display error: {:?}", e))?; display .flush() @@ -1100,6 +1102,158 @@ where Ok(()) } +#[cfg(not(feature = "qemu"))] +fn bluetooth() -> Result<()> { + info!("About to start BLE scan"); + + unsafe extern "C" fn bluetooth_gap_callback( + event: esp_idf_sys::esp_gap_ble_cb_event_t, + param: *mut esp_idf_sys::esp_ble_gap_cb_param_t, + ) { + let mut param = *param; + match event { + esp_idf_sys::esp_gap_ble_cb_event_t_ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT => { + let duration_seconds = 2; + esp_nofail!(esp_idf_sys::esp_ble_gap_start_scanning(duration_seconds)); + } + esp_idf_sys::esp_gap_ble_cb_event_t_ESP_GAP_BLE_SCAN_START_COMPLETE_EVT => { + if param.scan_start_cmpl.status + != esp_idf_sys::esp_bt_status_t_ESP_BT_STATUS_SUCCESS + { + error!( + "BLE scan starting failed, error status: {}", + param.scan_start_cmpl.status + ); + } + debug!("BLE scan started successfully"); + } + esp_idf_sys::esp_gap_ble_cb_event_t_ESP_GAP_BLE_SCAN_RESULT_EVT => { + if param.scan_rst.search_evt + == esp_idf_sys::esp_gap_search_evt_t_ESP_GAP_SEARCH_INQ_RES_EVT + { + let mut advertisment_name_length = 0; + let advertisment_name_raw = esp_idf_sys::esp_ble_resolve_adv_data( + param.scan_rst.ble_adv.as_mut_ptr(), + esp_idf_sys::esp_ble_adv_data_type_ESP_BLE_AD_TYPE_NAME_CMPL as u8, + &mut advertisment_name_length, + ); + debug!( + "BLE scan result advertisement data len {}, scan response len {}", + param.scan_rst.adv_data_len, param.scan_rst.scan_rsp_len + ); + if advertisment_name_length == 0 { + debug!("Reached end of scan results"); + return; + } + let advertisment_name = + std::str::from_utf8_unchecked(std::slice::from_raw_parts( + advertisment_name_raw, + advertisment_name_length as usize, + )); + info!( + "BLE scan found device during advertisment: {:02x?} {}", + ¶m.scan_rst.bda, advertisment_name + ); + } + } + esp_idf_sys::esp_gap_ble_cb_event_t_ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT => { + if param.scan_stop_cmpl.status != esp_idf_sys::esp_bt_status_t_ESP_BT_STATUS_SUCCESS + { + error!( + "BLE scan stopping failed, error status: {}", + param.scan_start_cmpl.status + ); + } + debug!("BLE scan stopped successfully"); + } + esp_idf_sys::esp_gap_ble_cb_event_t_ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT => { + if param.adv_stop_cmpl.status != esp_idf_sys::esp_bt_status_t_ESP_BT_STATUS_SUCCESS + { + error!( + "BLE advertisment stop scanfailed, error status: {}", + param.adv_stop_cmpl.status + ); + } + debug!("BLE advertisment stopped successfully"); + } + esp_idf_sys::esp_gap_ble_cb_event_t_ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT => { + info!("BLE update connection params status = {}, min_int = {}, max_int = {}, conn_int = {}, latency = {}, timeout = {}", + param.update_conn_params.status, + param.update_conn_params.min_int, + param.update_conn_params.max_int, + param.update_conn_params.conn_int, + param.update_conn_params.latency, + param.update_conn_params.timeout); + } + _ => info!("Received unhandled GAP event: {}", event), + } + } + + unsafe { + esp!(esp_idf_sys::esp_bt_controller_mem_release( + esp_idf_sys::esp_bt_mode_t_ESP_BT_MODE_CLASSIC_BT, + ))?; + } + + // Same defaults as the macro at https://github.com/espressif/esp-idf/blob/8377a3fff1/components/bt/include/esp32/include/esp_bt.h#L163 + // TODO This should eventually be nicely wrapped in esp_idf_svc. + let mut bt_controller_config = esp_idf_sys::esp_bt_controller_config_t { + controller_task_stack_size: esp_idf_sys::ESP_TASK_BT_CONTROLLER_STACK as u16, + controller_task_prio: esp_idf_sys::ESP_TASK_BT_CONTROLLER_PRIO as u8, + hci_uart_no: esp_idf_sys::BT_HCI_UART_NO_DEFAULT as u8, + hci_uart_baudrate: esp_idf_sys::BT_HCI_UART_BAUDRATE_DEFAULT, + scan_duplicate_mode: esp_idf_sys::SCAN_DUPLICATE_MODE as u8, + scan_duplicate_type: esp_idf_sys::SCAN_DUPLICATE_TYPE_VALUE as u8, + normal_adv_size: esp_idf_sys::NORMAL_SCAN_DUPLICATE_CACHE_SIZE as u16, + mesh_adv_size: esp_idf_sys::MESH_DUPLICATE_SCAN_CACHE_SIZE as u16, + send_adv_reserved_size: esp_idf_sys::SCAN_SEND_ADV_RESERVED_SIZE as u16, + controller_debug_flag: esp_idf_sys::CONTROLLER_ADV_LOST_DEBUG_BIT, + mode: esp_idf_sys::esp_bt_mode_t_ESP_BT_MODE_BLE as u8, + ble_max_conn: esp_idf_sys::CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF as u8, + bt_max_acl_conn: esp_idf_sys::CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF as u8, + bt_sco_datapath: esp_idf_sys::CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF as u8, + auto_latency: esp_idf_sys::BTDM_CTRL_AUTO_LATENCY_EFF != 0, + bt_legacy_auth_vs_evt: esp_idf_sys::BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF != 0, + bt_max_sync_conn: esp_idf_sys::CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF as u8, + ble_sca: esp_idf_sys::CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF as u8, + pcm_role: esp_idf_sys::CONFIG_BTDM_CTRL_PCM_ROLE_EFF as u8, + pcm_polar: esp_idf_sys::CONFIG_BTDM_CTRL_PCM_POLAR_EFF as u8, + hli: esp_idf_sys::BTDM_CTRL_HLI != 0, + magic: esp_idf_sys::ESP_BT_CONTROLLER_CONFIG_MAGIC_VAL, + }; + + // Same defaults as in https://github.com/espressif/esp-idf/blob/8377a3fff1927862ba5a17d0f9518e1a9e5fc8dd/examples/bluetooth/bluedroid/ble/gatt_client/main/gattc_demo.c#L68 + // TODO This should eventually be nicely wrapped in esp_idf_svc. + let mut ble_scan_params = esp_idf_sys::esp_ble_scan_params_t { + scan_type: esp_idf_sys::esp_ble_scan_type_t_BLE_SCAN_TYPE_ACTIVE, + own_addr_type: esp_idf_sys::esp_ble_addr_type_t_BLE_ADDR_TYPE_PUBLIC, + scan_filter_policy: esp_idf_sys::esp_ble_scan_filter_t_BLE_SCAN_FILTER_ALLOW_ALL, + scan_interval: 0x50, + scan_window: 0x30, + scan_duplicate: esp_idf_sys::esp_ble_scan_duplicate_t_BLE_SCAN_DUPLICATE_DISABLE, + }; + + unsafe { + esp!(esp_idf_sys::esp_bt_controller_init( + &mut bt_controller_config + ))?; + esp!(esp_idf_sys::esp_bt_controller_enable( + esp_idf_sys::esp_bt_mode_t_ESP_BT_MODE_BLE + ))?; + esp!(esp_idf_sys::esp_bluedroid_init())?; + esp!(esp_idf_sys::esp_bluedroid_enable())?; + esp!(esp_idf_sys::esp_ble_gap_register_callback(Some( + bluetooth_gap_callback + )))?; + esp!(esp_idf_sys::esp_ble_gap_set_scan_params( + &mut ble_scan_params + ))?; + } + + thread::sleep(Duration::from_secs(3)); + Ok(()) +} + #[allow(unused_variables)] fn httpd(mutex: Arc<(Mutex>, Condvar)>) -> Result { let server = idf::ServerRegistry::new()