Initial commit: project docs and ignore rules
This commit is contained in:
300
ESP32-C5-Toolkit/main/bt_scanner.c
Normal file
300
ESP32-C5-Toolkit/main/bt_scanner.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* @file bt_scanner.c
|
||||
* @brief NimBLE-based BLE Scanner for ESP32-C5
|
||||
*
|
||||
* Uses NimBLE stack (not Bluedroid) which is required for ESP32-C5
|
||||
*/
|
||||
|
||||
#include "bt_scanner.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "ble_scanner";
|
||||
|
||||
#define MAX_BT_DEVICES 30
|
||||
|
||||
// Global state
|
||||
static bool ble_initialized = false;
|
||||
static bool scan_active = false;
|
||||
static bt_device_t devices[MAX_BT_DEVICES];
|
||||
static int device_count = 0;
|
||||
static SemaphoreHandle_t ble_mutex = NULL;
|
||||
static uint8_t own_addr_type;
|
||||
|
||||
// Forward declarations
|
||||
static void ble_host_task(void *param);
|
||||
static int ble_gap_event(struct ble_gap_event *event, void *arg);
|
||||
|
||||
// Check if device already exists in list
|
||||
static bool device_exists(const uint8_t *addr) {
|
||||
for (int i = 0; i < device_count; i++) {
|
||||
if (memcmp(devices[i].addr, addr, 6) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse device name from advertising data
|
||||
static void parse_adv_name(const uint8_t *data, uint8_t len, char *name_out, size_t name_size) {
|
||||
name_out[0] = '\0';
|
||||
uint8_t pos = 0;
|
||||
|
||||
while (pos < len) {
|
||||
uint8_t field_len = data[pos];
|
||||
if (field_len == 0 || pos + field_len >= len) break;
|
||||
|
||||
uint8_t field_type = data[pos + 1];
|
||||
|
||||
// Complete Local Name (0x09) or Shortened Local Name (0x08)
|
||||
if (field_type == 0x09 || field_type == 0x08) {
|
||||
uint8_t name_len = field_len - 1;
|
||||
if (name_len >= name_size) name_len = name_size - 1;
|
||||
memcpy(name_out, &data[pos + 2], name_len);
|
||||
name_out[name_len] = '\0';
|
||||
return;
|
||||
}
|
||||
pos += field_len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// GAP event handler for scan results
|
||||
static int ble_gap_event(struct ble_gap_event *event, void *arg) {
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_DISC: {
|
||||
// Received advertising report
|
||||
if (device_count >= MAX_BT_DEVICES) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if device already in list
|
||||
if (device_exists(event->disc.addr.val)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(ble_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
bt_device_t *dev = &devices[device_count];
|
||||
|
||||
// Copy address (reverse byte order for display)
|
||||
for (int i = 0; i < 6; i++) {
|
||||
dev->addr[i] = event->disc.addr.val[5 - i];
|
||||
}
|
||||
|
||||
dev->rssi = event->disc.rssi;
|
||||
dev->adv_type = event->disc.event_type;
|
||||
dev->timestamp = (uint32_t)(esp_timer_get_time() / 1000000ULL);
|
||||
|
||||
// Parse name from advertising data
|
||||
struct ble_hs_adv_fields fields;
|
||||
if (ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data) == 0) {
|
||||
if (fields.name != NULL && fields.name_len > 0) {
|
||||
size_t copy_len = fields.name_len < 31 ? fields.name_len : 31;
|
||||
memcpy(dev->name, fields.name, copy_len);
|
||||
dev->name[copy_len] = '\0';
|
||||
} else {
|
||||
strcpy(dev->name, "Unknown");
|
||||
}
|
||||
} else {
|
||||
// Fallback: try manual parsing
|
||||
parse_adv_name(event->disc.data, event->disc.length_data, dev->name, sizeof(dev->name));
|
||||
if (dev->name[0] == '\0') {
|
||||
strcpy(dev->name, "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
device_count++;
|
||||
ESP_LOGI(TAG, "Device %d: %02X:%02X:%02X:%02X:%02X:%02X RSSI:%d %s",
|
||||
device_count,
|
||||
dev->addr[0], dev->addr[1], dev->addr[2],
|
||||
dev->addr[3], dev->addr[4], dev->addr[5],
|
||||
dev->rssi, dev->name);
|
||||
|
||||
xSemaphoreGive(ble_mutex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_DISC_COMPLETE:
|
||||
ESP_LOGI(TAG, "BLE scan complete, found %d devices", device_count);
|
||||
scan_active = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NimBLE host sync callback
|
||||
static void ble_on_sync(void) {
|
||||
int rc;
|
||||
|
||||
// Determine address type
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Failed to ensure address: %d", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Failed to infer address type: %d", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "NimBLE host synced, ready to scan");
|
||||
ble_initialized = true;
|
||||
}
|
||||
|
||||
// NimBLE host reset callback
|
||||
static void ble_on_reset(int reason) {
|
||||
ESP_LOGW(TAG, "NimBLE host reset, reason: %d", reason);
|
||||
ble_initialized = false;
|
||||
}
|
||||
|
||||
// NimBLE host task
|
||||
static void ble_host_task(void *param) {
|
||||
ESP_LOGI(TAG, "NimBLE host task started");
|
||||
nimble_port_run();
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
bool bt_scanner_init(void) {
|
||||
if (ble_initialized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing NimBLE BLE scanner");
|
||||
|
||||
// Create mutex
|
||||
if (ble_mutex == NULL) {
|
||||
ble_mutex = xSemaphoreCreateMutex();
|
||||
if (ble_mutex == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create mutex");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize NimBLE
|
||||
esp_err_t ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to init NimBLE port: %s", esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure NimBLE host
|
||||
ble_hs_cfg.sync_cb = ble_on_sync;
|
||||
ble_hs_cfg.reset_cb = ble_on_reset;
|
||||
|
||||
// Start NimBLE host task
|
||||
nimble_port_freertos_init(ble_host_task);
|
||||
|
||||
// Wait for sync (up to 2 seconds)
|
||||
for (int i = 0; i < 20 && !ble_initialized; i++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
if (!ble_initialized) {
|
||||
ESP_LOGW(TAG, "NimBLE not synced yet, but continuing");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "NimBLE BLE scanner initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bt_scan_start(void) {
|
||||
if (!ble_initialized) {
|
||||
if (!bt_scanner_init()) {
|
||||
ESP_LOGE(TAG, "BLE not initialized");
|
||||
return false;
|
||||
}
|
||||
// Wait a bit more for sync
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
|
||||
if (scan_active) {
|
||||
ESP_LOGW(TAG, "Scan already active");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear previous results
|
||||
if (xSemaphoreTake(ble_mutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
|
||||
device_count = 0;
|
||||
memset(devices, 0, sizeof(devices));
|
||||
xSemaphoreGive(ble_mutex);
|
||||
}
|
||||
|
||||
// Configure scan parameters
|
||||
struct ble_gap_disc_params scan_params = {
|
||||
.itvl = BLE_GAP_SCAN_ITVL_MS(100), // 100ms interval
|
||||
.window = BLE_GAP_SCAN_WIN_MS(100), // 100ms window (continuous)
|
||||
.filter_policy = BLE_HCI_SCAN_FILT_NO_WL,
|
||||
.limited = 0,
|
||||
.passive = 0, // Active scanning (request scan response)
|
||||
.filter_duplicates = 1,
|
||||
};
|
||||
|
||||
// Start discovery (scan for 30 seconds)
|
||||
int rc = ble_gap_disc(own_addr_type, 30000, &scan_params, ble_gap_event, NULL);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Failed to start scan: %d", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
scan_active = true;
|
||||
ESP_LOGI(TAG, "BLE scan started");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bt_scan_stop(void) {
|
||||
if (!scan_active) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int rc = ble_gap_disc_cancel();
|
||||
if (rc != 0 && rc != BLE_HS_EALREADY) {
|
||||
ESP_LOGE(TAG, "Failed to stop scan: %d", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
scan_active = false;
|
||||
ESP_LOGI(TAG, "BLE scan stopped, found %d devices", device_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
int bt_get_devices(bt_device_t *out_devices, int max_devices) {
|
||||
if (out_devices == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
if (xSemaphoreTake(ble_mutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
|
||||
count = (device_count < max_devices) ? device_count : max_devices;
|
||||
memcpy(out_devices, devices, count * sizeof(bt_device_t));
|
||||
xSemaphoreGive(ble_mutex);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Stub implementations for jamming (not implemented)
|
||||
bool bt_jam_start(uint8_t *target_addr, uint32_t duration) {
|
||||
ESP_LOGW(TAG, "BT jamming not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bt_jam_stop(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bt_jam_is_active(void) {
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user