百度网站网址是多少,做网站是什么鬼,怎么制作自己的小程序,微信小程序格泰网站建设下面给你一个基于 ESP-IDF(v5.x) 的完整示例#xff1a;在 ESP32-C3 上同时扫描附近 Wi-Fi 与蓝牙#xff08;BLE#xff09;广播#xff0c;把结果以 JSON 结构统一输出到串口#xff0c;并且可可选通过 MQTT 上报到服务器#xff08;打开一个宏即可#xff09;。日志默…下面给你一个基于 ESP-IDF(v5.x) 的完整示例在 ESP32-C3 上同时扫描附近 Wi-Fi 与蓝牙BLE广播把结果以 JSON 结构统一输出到串口并且可可选通过 MQTT 上报到服务器打开一个宏即可。日志默认中文。示例考虑了功耗与速率限制避免长时间占用射频。
功能概述Wi-Fi 扫描主动/被动可选返回 SSID、BSSID、信道、RSSI、加密类型。BLE 扫描NimBLE解析设备地址、RSSI、广告类型、部分厂商数据。统一打包 JSON每个周期输出一帧 {ts, wifi_list, ble_list}。MQTT 可选上报定义 ENABLE_MQTT 后会将 JSON 发布到 scan/uplink 主题。节流与功耗可配置扫描周期、Wi-Fi dwell time、BLE 扫描窗口/间隔支持在两次扫描间隙小休眠。
目录结构示例工程
scaniot/├─ main/│ ├─ CMakeLists.txt│ └─ app_main.c├─ CMakeLists.txt└─ sdkconfig.defaults 可选预置扫描/MQTT参数
main/CMakeLists.txt
idf_component_register(SRCS app_main.cINCLUDE_DIRS .)
顶层 CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(scaniot)app_main.c
复制即可编译根据注释修改 Wi-Fi/MQTT 参数。#include stdio.h
#include string.h
#include inttypes.h
#include time.h#include freertos/FreeRTOS.h
#include freertos/task.h
#include freertos/event_groups.h#include esp_event.h
#include esp_log.h
#include esp_system.h
#include esp_err.h
#include nvs_flash.h#include esp_wifi.h
#include esp_netif.h#include cJSON.h// ---- 可选使能 MQTT 上报 ----
#define ENABLE_MQTT 1 // 1启用MQTT上报0仅串口输出#if ENABLE_MQTT
#include mqtt_client.h
#endif// ---- NimBLE BLE 扫描 ----
#include nimble/nimble_port.h
#include nimble/nimble_port_freertos.h
#include host/ble_hs.h
#include host/ble_gap.h
#include host/util/util.hstatic const char *TAG ScanIOT;// 可配参数
#define WIFI_SCAN_ACTIVE 1 // 1主动扫描 0被动扫描
#define WIFI_SCAN_MAX_AP 24 // 单次最多AP记录
#define WIFI_SCAN_CHANNEL_TIME_MS 110 // 主动扫描每信道停留(ms)// BLE 扫描参数窗口/间隔单位均为 0.625ms
#define BLE_SCAN_ITVL 0x0060 // 60 * 0.625ms 37.5ms
#define BLE_SCAN_WINDOW 0x0030 // 30 * 0.625ms 18.75ms
#define BLE_SCAN_DURATION_SEC 5 // 每轮BLE扫描时长(s)#define SCAN_PERIOD_SEC 15 // 每轮合并扫描周期(s)// Wi-Fi 连接若需要MQTT
static const char *WIFI_SSID YourAP;
static const char *WIFI_PASS YourPassword;// MQTT 参数
#if ENABLE_MQTT
static const char *MQTT_BROKER_URI mqtt://192.168.1.100:1883;
static const char *MQTT_TOPIC scan/uplink;
static esp_mqtt_client_handle_t s_mqtt NULL;
#endif// 实用时间戳
static int64_t epoch_millis(void) {struct timespec ts;clock_gettime(CLOCK_REALTIME, ts);return (int64_t)ts.tv_sec * 1000 ts.tv_nsec / 1000000;
}// BLE 扫描收集
typedef struct {char addr[18];int rssi;uint8_t adv_type;char name[32];
} ble_item_t;#define BLE_LIST_MAX 64
static ble_item_t g_ble_list[BLE_LIST_MAX];
static int g_ble_cnt 0;// 提取设备名若有
static void parse_name_from_adv(const uint8_t *data, uint8_t len, char out[32]) {out[0] 0;uint8_t i 0;while (i 1 len) {uint8_t l data[i];if (l 0 || i l len) break;uint8_t type data[i1];if (type 0x09 || type 0x08) { // Complete/Shortened Local Nameuint8_t copy l - 1;if (copy 31) copy 31;memcpy(out, data[i2], copy);out[copy] 0;return;}i (l 1);}
}static int ble_gap_event_cb(struct ble_gap_event *event, void *arg) {switch (event-type) {case BLE_GAP_EVENT_DISC:if (g_ble_cnt BLE_LIST_MAX) {ble_item_t *it g_ble_list[g_ble_cnt];uint8_t addr[6];ble_addr_t a event-disc.addr;memcpy(addr, a.val, 6);snprintf(it-addr, sizeof(it-addr),%02X:%02X:%02X:%02X:%02X:%02X,addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);it-rssi event-disc.rssi;it-adv_type event-disc.event_type;parse_name_from_adv(event-disc.data, event-disc.length_data, it-name);}return 0;default:return 0;}
}static void ble_host_sync(void) {// 设定随机地址如需要ble_hs_id_infer_auto(0, NULL);
}static void ble_host_task(void *param) {nimble_port_run(); // 不会返回nimble_port_freertos_deinit();
}// 启动一轮 BLE 扫描
static esp_err_t do_ble_scan_seconds(int duration_sec) {g_ble_cnt 0;struct ble_gap_disc_params params {.itvl BLE_SCAN_ITVL,.window BLE_SCAN_WINDOW,.filter_policy 0,.passive 0,.limited 0,.filter_duplicates 1,};int rc ble_gap_disc(BLE_OWN_ADDR_PUBLIC, duration_sec * 1000, params, ble_gap_event_cb, NULL);if (rc ! 0) {ESP_LOGE(TAG, BLE scan start failed rc%d, rc);return ESP_FAIL;}// 简单等待扫描结束vTaskDelay(pdMS_TO_TICKS(duration_sec * 1000));ble_gap_disc_cancel();return ESP_OK;
}// Wi-Fi 初始化/扫描
static EventGroupHandle_t s_wifi_evt;
#define WIFI_EVT_CONNECTED BIT0static void wifi_event_handler(void* arg, esp_event_base_t base, int32_t id, void* data) {if (base WIFI_EVENT id WIFI_EVENT_STA_START) {esp_wifi_connect();} else if (base WIFI_EVENT id WIFI_EVENT_STA_DISCONNECTED) {xEventGroupClearBits(s_wifi_evt, WIFI_EVT_CONNECTED);ESP_LOGW(TAG, Wi-Fi断开重连中…);esp_wifi_connect();} else if (base IP_EVENT id IP_EVENT_STA_GOT_IP) {xEventGroupSetBits(s_wifi_evt, WIFI_EVT_CONNECTED);ESP_LOGI(TAG, 已获取IP);}
}static void wifi_init_sta(void) {ESP_ERROR_CHECK(esp_netif_init());ESP_ERROR_CHECK(esp_event_loop_create_default());esp_netif_create_default_wifi_sta();wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(cfg));s_wifi_evt xEventGroupCreate();ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL));ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL));wifi_config_t wc {0};strncpy((char*)wc.sta.ssid, WIFI_SSID, sizeof(wc.sta.ssid));strncpy((char*)wc.sta.password, WIFI_PASS, sizeof(wc.sta.password));wc.sta.threshold.authmode WIFI_AUTH_WPA2_PSK;wc.sta.sae_pwe_h2e WPA3_SAE_PWE_BOTH;ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, wc));ESP_ERROR_CHECK(esp_wifi_start());
}static int wifi_scan_collect(cJSON *wifi_arr) {wifi_scan_config_t sc {0};
#if WIFI_SCAN_ACTIVEsc.scan_type WIFI_SCAN_TYPE_ACTIVE;sc.scan_time.active.min WIFI_SCAN_CHANNEL_TIME_MS;sc.scan_time.active.max WIFI_SCAN_CHANNEL_TIME_MS 40;
#elsesc.scan_type WIFI_SCAN_TYPE_PASSIVE;sc.scan_time.passive WIFI_SCAN_CHANNEL_TIME_MS 60;
#endifESP_ERROR_CHECK(esp_wifi_scan_start(sc, true));uint16_t ap_num WIFI_SCAN_MAX_AP;wifi_ap_record_t aps[WIFI_SCAN_MAX_AP];ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(ap_num, aps));for (int i 0; i ap_num; i) {cJSON *item cJSON_CreateObject();cJSON_AddStringToObject(item, ssid, (const char*)aps[i].ssid);char bssid[18];snprintf(bssid, sizeof(bssid), %02X:%02X:%02X:%02X:%02X:%02X,aps[i].bssid[0], aps[i].bssid[1], aps[i].bssid[2],aps[i].bssid[3], aps[i].bssid[4], aps[i].bssid[5]);cJSON_AddStringToObject(item, bssid, bssid);cJSON_AddNumberToObject(item, rssi, aps[i].rssi);cJSON_AddNumberToObject(item, primary, aps[i].primary);cJSON_AddNumberToObject(item, auth, aps[i].authmode);cJSON_AddItemToArray(wifi_arr, item);}return ap_num;
}// MQTT可选
#if ENABLE_MQTT
static void mqtt_start(void) {esp_mqtt_client_config_t cfg {.broker.address.uri MQTT_BROKER_URI,.session.protocol_ver MQTT_PROTOCOL_V_3_1_1,.credentials.client_id esp32c3-scanner,.task.priority 5,};s_mqtt esp_mqtt_client_init(cfg);esp_mqtt_client_start(s_mqtt);
}static void mqtt_publish_json(const char *topic, const char *json) {if (!s_mqtt) return;int msg_id esp_mqtt_client_publish(s_mqtt, topic, json, 0, 0, 0);if (msg_id 0) {ESP_LOGI(TAG, MQTT已发布msg_id%d, bytes%d, msg_id, (int)strlen(json));} else {ESP_LOGW(TAG, MQTT发布失败);}
}
#endif// 主任务合并扫描并输出/上报
static void scan_cycle_task(void *arg) {while (1) {int64_t start_ms epoch_millis();ESP_LOGI(TAG, 新一轮扫描开始 );// 1) BLEESP_LOGI(TAG, 开始 BLE 扫描 %ds…, BLE_SCAN_DURATION_SEC);if (do_ble_scan_seconds(BLE_SCAN_DURATION_SEC) ! ESP_OK) {ESP_LOGW(TAG, BLE 扫描失败);}// 2) Wi-FicJSON *root cJSON_CreateObject();cJSON_AddNumberToObject(root, ts, (double)(epoch_millis()));cJSON *wifi_arr cJSON_CreateArray();int wifi_cnt wifi_scan_collect(wifi_arr);cJSON_AddItemToObject(root, wifi_list, wifi_arr);cJSON *ble_arr cJSON_CreateArray();for (int i 0; i g_ble_cnt; i) {cJSON *o cJSON_CreateObject();cJSON_AddStringToObject(o, addr, g_ble_list[i].addr);cJSON_AddNumberToObject(o, rssi, g_ble_list[i].rssi);cJSON_AddNumberToObject(o, adv_type, g_ble_list[i].adv_type);if (g_ble_list[i].name[0]) {cJSON_AddStringToObject(o, name, g_ble_list[i].name);}cJSON_AddItemToArray(ble_arr, o);}cJSON_AddItemToObject(root, ble_list, ble_arr);char *json cJSON_PrintUnformatted(root);// 串口输出printf(%s\n, json);// 可选MQTT上报#if ENABLE_MQTTmqtt_publish_json(MQTT_TOPIC, json);#endifcJSON_Delete(root);free(json);int64_t elapsed epoch_millis() - start_ms;int64_t sleep_ms SCAN_PERIOD_SEC * 1000 - elapsed;if (sleep_ms 0) sleep_ms 0;ESP_LOGI(TAG, 本轮完成WiFi%d, BLE%d用时% PRId64 ms休息% PRId64 ms,wifi_cnt, g_ble_cnt, elapsed, sleep_ms);vTaskDelay(pdMS_TO_TICKS((uint32_t)sleep_ms));}
}// NimBLE 初始化
static void ble_init(void) {ESP_ERROR_CHECK(nimble_port_init());ble_hs_cfg.reset_cb NULL;ble_hs_cfg.sync_cb ble_host_sync;// 可设置GAP首选参数等…nimble_port_freertos_init(ble_host_task);
}// app_main
void app_main(void) {esp_err_t ret nvs_flash_init();if (ret ESP_ERR_NVS_NO_FREE_PAGES || ret ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase());ESP_ERROR_CHECK(nvs_flash_init());}ESP_LOGI(TAG, 系统启动);// 初始化 BLE Hostble_init();// 若启用 MQTT需要联网
#if ENABLE_MQTTwifi_init_sta();// 等待联网最多10秒if (xEventGroupWaitBits(s_wifi_evt, WIFI_EVT_CONNECTED, false, true, pdMS_TO_TICKS(10000)) 0) {ESP_LOGW(TAG, 10秒内未联网后续只串口输出。);} else {mqtt_start();}
#else// 即便不联网也可以做 Wi-Fi 扫描需要初始化 netif wifi但无需配置AP信息ESP_ERROR_CHECK(esp_netif_init());ESP_ERROR_CHECK(esp_event_loop_create_default());esp_netif_create_default_wifi_sta();wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(cfg));ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));ESP_ERROR_CHECK(esp_wifi_start());
#endifxTaskCreatePinnedToCore(scan_cycle_task, scan_cycle, 6144, NULL, 5, NULL, 0);
}sdkconfig 关键点如用 menuconfig 配Component config - Bluetooth选择 NimBLE而非 Bluedroid适度调低 NimBLE max connections保守内存Component config - Wi-Fi若仅扫描可不开 Wi-Fi NVS flash 节省写入Partition Table默认 factory 足够如启用 OTA/MQTT 大日志可适当放宽 nvs/phy
上位机示例输出一行一帧 JSON
{ts: 1731285230123,wifi_list:[{ssid:Home,bssid:AA:BB:CC:DD:EE:FF,rssi:-45,primary:1,auth:3}, ...],ble_list:[{addr:12:34:56:78:9A:BC,rssi:-67,adv_type:0,name:MiBand}, ...]
}调优建议射频复用与时间片示例采用“先 BLE 后 Wi-Fi”的顺序避免两者同时占用 RF减少丢包。速率限制SCAN_PERIOD_SEC ≥ 10s 较稳妥过于频繁会影响系统与空口。功耗将 BLE_SCAN_WINDOW 设为 BLE_SCAN_ITVL * 0.5~0.6并在两轮之间 vTaskDelay。离线场景可考虑 esp_light_sleep_start() 做轻睡。数据量可为 wifi_list/ble_list 设置 RSSI 阈值过滤如 -90dBm 丢弃或限制最大数量。MQTT 可靠性生产环境请启用 session.keepalive、LWT、QoS1/2 与重连回退。合规性采集与上报他人设备信息需遵守当地法律法规与隐私政策生产前务必征得授权。
如果你希望把上报方式改为 HTTP(S) / WebSocket或者把Wi-Fi 凭据用 BluFi/配网 App 来下发你常用的 CozyLife / BluFi 流程我可以直接把上面工程改成对应版本并补上 sdkconfig.defaults 与分区表。