主机:
#include "BLE_master.h"
#include <Arduino.h>
// ==================== 静态成员定义 ====================
const char* BLEMaster::DEVICE_NAME = "GMD1032-BLE-Slave";
const char* BLEMaster::TARGET_SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
const char* BLEMaster::TARGET_CHAR_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
bool BLEMaster::connected = false;
NimBLEClient* BLEMaster::pClient = nullptr;
BLERemoteCharacteristic* BLEMaster::pRemoteCharacteristic = nullptr;
NimBLEAdvertisedDevice* BLEMaster::targetDevice = nullptr;
void (*BLEMaster::dataCallback)(const char*) = nullptr;
bool BLEMaster::shouldConnect = false;
bool BLEMaster::isConnecting = false;
// ==================== 开始扫描 ====================
void BLEMaster::begin() {
Serial.println("🔄 Initializing BLE Master (NimBLE)...");
NimBLEDevice::init("");
NimBLEDevice::deleteAllBonds();
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(new ScanCallback(), true);
pScan->setActiveScan(true);
pScan->setInterval(134.5f);
pScan->setWindow(44.8f);
Serial.println("🔍 Starting continuous BLE scan...");
pScan->start(0, true); // 连续扫描
}
// ==================== 设置数据回调 ====================
void BLEMaster::onDataReceived(void (*callback)(const char*)) {
dataCallback = callback;
}
// ==================== 主循环:状态检查与连接控制 ====================
void BLEMaster::loop() {
if (connected && pClient && pClient->isConnected()) {
return; // 已连接,无需处理
}
if (isConnecting) {
return; // 正在连接,防止重复尝试
}
NimBLEScan* pScan = NimBLEDevice::getScan();
// 如果扫描结束且标记了连接请求,则发起连接
if (shouldConnect && targetDevice != nullptr && !pScan->isScanning()) {
isConnecting = true;
connectToDevice();
shouldConnect = false;
return;
}
// 如果长时间未连接,重启扫描
static unsigned long lastScanStart = 0;
if (!pScan->isScanning() && !connected && millis() - lastScanStart > 5000) {
Serial.println("🔁 Restarting BLE scan...");
pScan->start(30, false);
lastScanStart = millis();
}
}
// ==================== 尝试连接目标设备 ====================
bool BLEMaster::connectToDevice() {
if (!targetDevice) {
Serial.println("❌ No target device to connect.");
isConnecting = false;
return false;
}
Serial.printf("👉 Connecting to: %s [MAC: %s]\n",
targetDevice->getName().c_str(),
targetDevice->getAddress().toString().c_str());
pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(new ClientCallback());
// 快速连接参数 (minInterval=6, maxInterval=12, timeout=100ms)
pClient->setConnectionParams(0x06, 0x0C, 0, 0x64);
// connect() 参数说明:
// advertiseDevice: 扫描到的设备指针
// deleteAttrs: true 表示每次重新发现服务
// isDeleteCall: false
// exchangeMTU: true → 自动协商大包传输
if (!pClient->connect(targetDevice, true, false, true)) {
Serial.println("❌ Connection failed!");
cleanupOnFailure();
return false;
}
Serial.println("✅ Connected! Discovering service...");
BLERemoteService* pRemoteService = pClient->getService(NimBLEUUID(TARGET_SERVICE_UUID));
if (!pRemoteService) {
Serial.println("❌ Service not found");
cleanupOnFailure();
return false;
}
pRemoteCharacteristic = pRemoteService->getCharacteristic(NimBLEUUID(TARGET_CHAR_UUID));
if (!pRemoteCharacteristic) {
Serial.println("❌ Characteristic not found");
cleanupOnFailure();
return false;
}
requestMaxMTU();
// 启用通知
if (pRemoteCharacteristic->canNotify()) {
bool subscribed = pRemoteCharacteristic->subscribe(true,
[](BLERemoteCharacteristic* pBLERC, uint8_t* pData, size_t length, bool isNotify) {
char jsonStr[512] = {0};
size_t copyLen = min(length, sizeof(jsonStr) - 1);
memcpy(jsonStr, pData, copyLen);
jsonStr[copyLen] = '\0';
if (dataCallback) {
dataCallback((const char*)jsonStr);
} else {
Serial.printf("[BLE] Received (%u bytes): %.*s\n", length, copyLen, jsonStr);
}
});
if (subscribed) {
Serial.println("🔔 Notify enabled.");
} else {
Serial.println("⚠️ Subscribe failed!");
}
} else {
Serial.println("❌ Characteristic does not support notification.");
}
connected = true;
isConnecting = false;
return true;
}
// ==================== 清理失败状态 ====================
void BLEMaster::cleanupOnFailure() {
if (pClient) {
pClient->disconnect();
pClient = nullptr;
}
connected = false;
pRemoteCharacteristic = nullptr;
isConnecting = false;
}
// ==================== 请求最大 MTU ====================
void BLEMaster::requestMaxMTU() {
if (pClient && pClient->isConnected()) {
uint16_t mtu = pClient->getMTU();
Serial.printf("📈 Current MTU: %u\n", mtu);
if (!pClient->exchangeMTU()) {
Serial.println("⚠️ MTU exchange failed!");
} else {
Serial.println("📤 MTU request sent (negotiating up to 512)");
}
}
}
// ==================== 广播设备回调 ====================
void BLEMaster::ScanCallback::onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
Serial.printf("🔍 Scanned: %s [%s], RSSI=%d\n",
advertisedDevice->getName().c_str(),
advertisedDevice->getAddress().toString().c_str(),
advertisedDevice->getRSSI());
if (advertisedDevice->isAdvertisingService(NimBLEUUID(BLEMaster::TARGET_SERVICE_UUID))) {
Serial.printf("✅ Target device found via UUID: %s [RSSI=%d]\n",
advertisedDevice->getName().c_str(), advertisedDevice->getRSSI());
if (BLEMaster::targetDevice) {
delete BLEMaster::targetDevice;
}
BLEMaster::targetDevice = new NimBLEAdvertisedDevice(*advertisedDevice);
BLEMaster::shouldConnect = true;
NimBLEDevice::getScan()->stop(); // 停止扫描
}
}
// ✅ 正确实现 onScanEnd:两个参数,const 引用
void BLEMaster::ScanCallback::onScanEnd(const NimBLEScanResults& scanResults, int reason) {
Serial.printf("🏁 Scan ended. Found %d devices. Reason code: %d\n",
scanResults.getCount(), reason);
// 可在此添加逻辑,例如重新开始扫描
// NimBLEDevice::getScan()->start(30, false);
}
// ==================== 客户端连接回调 ====================
void BLEMaster::ClientCallback::onConnect(NimBLEClient* pClient) {
Serial.println("✔ BLE Device connected.");
}
void BLEMaster::ClientCallback::onDisconnect(NimBLEClient* pClient, int reason) {
char addr[64];
snprintf(addr, sizeof(addr), "%s", pClient->getPeerAddress().toString().c_str());
Serial.printf("🔌 Disconnected from %s, reason=%d\n", addr, reason);
BLEMaster::connected = false;
BLEMaster::pRemoteCharacteristic = nullptr;
BLEMaster::pClient = nullptr;
// 可在此添加延时后自动重连逻辑
}
void BLEMaster::ClientCallback::onMTUChange(NimBLEClient* pClient, uint16_t MTU) {
Serial.printf("📊 MTU changed to: %u\n", MTU);
}
从机:
#include <SPI.h>
#include <NimBLEDevice.h> // 替换 BLEDevice.h
#include <NimBLE2904.h> // 替换 BLE2902.h
#include <Arduino.h>
// ==================== 引脚定义 ====================
#define SCK_PIN 13
#define MISO_PIN 11
#define MOSI_PIN 14
#define CS_PIN 12
// ==================== 启用调试模式 ====================
#define DEBUG_GMD1032
// ==================== GMD1032 命令列表 ====================
const uint16_t CMD_START_MEASUREMENT = 0x2A16;
const uint16_t CMD_CLEAR_ADC_RESULTS = 0x243C;
const uint16_t CMD_READ_CFG0 = 0xC08E;
const uint16_t CMD_READ_STS0 = 0xC9B1;
// CELL 分组命令
const uint16_t CMD_READ_CELL_1_3 = 0xD0FE;
const uint16_t CMD_READ_CELL_4_6 = 0xD1F9;
const uint16_t CMD_READ_CELL_7_9 = 0xD2F0;
const uint16_t CMD_READ_CELL_10_12 = 0xD3F7;
const uint16_t CMD_READ_CELL_13_15 = 0xD4E2;
const uint16_t CMD_READ_CELL_16_18 = 0xD5E5;
// DIS 分组命令
const uint16_t CMD_READ_DIS_1_3 = 0xD6EC;
const uint16_t CMD_READ_DIS_4_6 = 0xD7EB;
const uint16_t CMD_READ_DIS_7_9 = 0xD8C6;
const uint16_t CMD_READ_DIS_10_12 = 0xD9C1;
const uint16_t CMD_READ_DIS_13_15 = 0xDAC8;
const uint16_t CMD_READ_DIS_16_18 = 0xDBCF;
// 其他测量命令
const uint16_t CMD_READ_VREF2_V3 = 0xDFD3;
const uint16_t CMD_READ_V5_DIETEMP_VSTK = 0xE06E;
// ==================== SPI 设置 ====================
SPISettings spiSettings(1000000, MSBFIRST, SPI_MODE3); // 1MHz, Mode3 (CPOL=1, CPHA=1)
// ==================== BLE 配置 ====================
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
NimBLECharacteristic* pCharacteristic = nullptr; // 改为 NimBLE 类型
bool deviceConnected = false;
// 共享数据缓冲区
float sharedCellVoltages[16] = {0};
float sharedDisVoltages[16] = {0};
float sharedVref2 = 0.0f, sharedV3 = 0.0f, sharedV5 = 0.0f, sharedDieTemp = 0.0f, sharedVstk = 0.0f;
portMUX_TYPE sensorMutex = portMUX_INITIALIZER_UNLOCKED; // 保护共享数据
// ==================== 回调类声明 ====================
class MyServerCallbacks : public NimBLEServerCallbacks {
// 正确签名:使用 NimBLEConnInfo&
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
deviceConnected = true;
Serial.println("设备接入~");
}
// 正确签名:包含 connInfo 和 reason
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
deviceConnected = false;
Serial.print("设备断开~ 原因代码: ");
Serial.println(reason);
pServer->startAdvertising(); // 重新开始广播
}
};
class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
// 推荐加 const,并保留 override
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override {
std::string value = pCharacteristic->getValue();
if (!value.empty()) {
Serial.printf("📥 客户端 %s 写入数据: %s\n",
std::string(connInfo.getAddress()).c_str(), // 正确调用
value.c_str());
}
}
};
// ==================== 辅助函数:打印二进制 ====================
void printBinary(uint8_t b) {
for (int i = 7; i >= 0; i--) {
Serial.write((b & (1 << i)) ? '1' : '0');
}
}
// ==================== 唤醒芯片 ====================
void wakeUpGMD1032() {
SPI.beginTransaction(spiSettings);
digitalWrite(CS_PIN, LOW);
delayMicroseconds(10);
SPI.transfer(0x00);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
delay(5);
}
// ==================== 发送命令 ====================
bool sendCommand(uint16_t command) {
SPI.beginTransaction(spiSettings);
digitalWrite(CS_PIN, LOW);
SPI.transfer16(command);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
return true;
}
// ==================== 解析逆序字段 ====================
void parseReverseFields(uint8_t* buffer, uint16_t* results, int count) {
for (int i = 0; i < count; i++) {
int bufIdx = i * 2;
results[count - 1 - i] = (buffer[bufIdx] << 8) | buffer[bufIdx + 1];
}
}
// ==================== 读取所有 CELL 电压 ====================
bool readAllCellVoltages(float* cellVoltages) {
const uint16_t cmds[] = {
CMD_READ_CELL_1_3, CMD_READ_CELL_4_6, CMD_READ_CELL_7_9,
CMD_READ_CELL_10_12, CMD_READ_CELL_13_15, CMD_READ_CELL_16_18
};
const int groupCount = 6;
const int perGroup[] = {3, 3, 3, 3, 3, 3};
SPI.beginTransaction(spiSettings);
for (int g = 0; g < groupCount; g++) {
digitalWrite(CS_PIN, LOW);
SPI.transfer16(cmds[g]);
uint8_t buffer[8];
for (int i = 0; i < 8; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(CS_PIN, HIGH);
uint16_t raw[3];
parseReverseFields(buffer, raw, perGroup[g]);
for (int i = 0; i < perGroup[g]; i++) {
int idx = g * 3 + i;
if (idx >= 16) continue;
float voltage = (raw[i] - 10240) * 0.0001f;
cellVoltages[idx] = voltage;
}
}
SPI.endTransaction();
return true;
}
// ==================== 读取所有 DIS 电压 ====================
bool readAllDisVoltages(float* disVoltages) {
const uint16_t cmds[] = {
CMD_READ_DIS_1_3, CMD_READ_DIS_4_6, CMD_READ_DIS_7_9,
CMD_READ_DIS_10_12, CMD_READ_DIS_13_15, CMD_READ_DIS_16_18
};
const int groupCount = 6;
const int perGroup[] = {3, 3, 3, 3, 3, 3};
SPI.beginTransaction(spiSettings);
for (int g = 0; g < groupCount; g++) {
digitalWrite(CS_PIN, LOW);
SPI.transfer16(cmds[g]);
uint8_t buffer[8];
for (int i = 0; i < 8; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(CS_PIN, HIGH);
uint16_t raw[3];
parseReverseFields(buffer, raw, perGroup[g]);
for (int i = 0; i < perGroup[g]; i++) {
int idx = g * 3 + i;
if (idx >= 16) continue;
float voltage = (raw[i] - 10240) * 0.0001f;
disVoltages[idx] = voltage;
}
}
SPI.endTransaction();
return true;
}
// ==================== 读取 VREF2 和 V3 ====================
bool readVref2AndV3(float* vref2, float* v3) {
uint8_t buffer[8];
SPI.beginTransaction(spiSettings);
digitalWrite(CS_PIN, LOW);
SPI.transfer16(CMD_READ_VREF2_V3);
for (int i = 0; i < 8; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
uint16_t raw_v3 = (buffer[0] << 8) | buffer[1];
uint16_t raw_vref2 = (buffer[2] << 8) | buffer[3];
*v3 = (raw_v3 - 10240) * 0.0001f;
*vref2 = (raw_vref2 - 10240) * 0.0001f;
return true;
}
// ==================== 读取 V5 / DieTemp / VSTK ====================
bool readV5DieTempVStk(float* v5, float* dieTemp, float* vstk) {
uint8_t buffer[8];
SPI.beginTransaction(spiSettings);
digitalWrite(CS_PIN, LOW);
SPI.transfer16(CMD_READ_V5_DIETEMP_VSTK);
for (int i = 0; i < 8; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
uint16_t raw_vstk = (buffer[0] << 8) | buffer[1];
uint16_t raw_dieTemp = (buffer[2] << 8) | buffer[3];
uint16_t raw_v5 = (buffer[4] << 8) | buffer[5];
*vstk = (raw_vstk - 10240) * 0.0016f;
*v5 = (raw_v5 - 10240) * 0.0001f;
if (raw_dieTemp >= 8000 && raw_dieTemp <= 30000) {
*dieTemp = raw_dieTemp * 0.27662 - 3105.59;
} else {
*dieTemp = NAN;
}
return true;
}
// ==================== 更新 BLE 特征值(从 Core 1 调用)====================
void updateBLECharacteristic() {
static char jsonBuffer[512];
int offset = 0;
float cell[16];
float dieTemp;
// 安全拷贝共享数据
portENTER_CRITICAL(&sensorMutex);
memcpy(cell, sharedCellVoltages, sizeof(cell));
dieTemp = sharedDieTemp;
portEXIT_CRITICAL(&sensorMutex);
// 构造 JSON: {"cell":[...], "temp": ...}
offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "{\"cell\":[");
for (int i = 0; i < 16; i++) {
offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset,
"%.3f%s", cell[i], (i < 15) ? "," : "");
}
if (!isnan(dieTemp)) {
offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset,
"],\"temp\":%.2f}", dieTemp);
} else {
offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset,
"],\"temp\":null}");
}
// 防溢出兜底
if (offset >= sizeof(jsonBuffer)) {
strcpy(jsonBuffer, "{\"err\":\"long\"}");
}
// 发送到 BLE
if (pCharacteristic && deviceConnected) {
pCharacteristic->setValue(jsonBuffer);
pCharacteristic->notify(); // 触发通知
}
#ifdef DEBUG_GMD1032
Serial.printf("BLE Data: %s\n", jsonBuffer);
#endif
}
// ==================== 核心 0:传感器采集任务 ====================
void sensorTask(void *parameter) {
for (;;) {
yield();
wakeUpGMD1032();
delay(2);
sendCommand(CMD_START_MEASUREMENT);
delay(10);
float cellVoltages[16] = {0};
float disVoltages[16] = {0};
float vref2 = 0.0f, v3 = 0.0f, v5 = 0.0f, dieTemp = 0.0f, vstk = 0.0f;
readAllCellVoltages(cellVoltages);
readAllDisVoltages(disVoltages);
readVref2AndV3(&vref2, &v3);
readV5DieTempVStk(&v5, &dieTemp, &vstk);
// 写入共享变量(加锁)
portENTER_CRITICAL(&sensorMutex);
memcpy(sharedCellVoltages, cellVoltages, sizeof(cellVoltages));
memcpy(sharedDisVoltages, disVoltages, sizeof(disVoltages));
sharedVref2 = vref2;
sharedV3 = v3;
sharedV5 = v5;
sharedDieTemp = dieTemp;
sharedVstk = vstk;
portEXIT_CRITICAL(&sensorMutex);
#ifdef DEBUG_GMD1032
Serial.println("============== GMD1032 全通道数据 ==============");
for (int i = 0; i < 16; i++) {
if (i % 4 == 0) {
Serial.print(" ");
}
Serial.printf("CELL%2d:%5.3fV ", i + 1, cellVoltages[i]);
if ((i + 1) % 4 == 0 || i == 15) {
Serial.println();
}
}
if (!isnan(dieTemp)) {
Serial.printf("芯片温度: %.2f°C\n", dieTemp);
} else {
Serial.println("芯片温度: 无效数据");
}
Serial.println("---------------------------------------------");
#endif
delay(1000); // 每秒采集一次
}
}
// ==================== 核心 1:BLE 服务任务 ====================
void bleTask(void *parameter) {
NimBLEDevice::init("GMD1032-BLE-Slave");
NimBLEServer* pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
NimBLEService* pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY
);
pCharacteristic->addDescriptor(new NimBLE2904());
pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
pCharacteristic->setValue("GMD1032 Ready");
pService->start();
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
// 显式设置广告内容
NimBLEAdvertisementData advertisementData;
advertisementData.setName("GMD1032-BLE-Slave");
pAdvertising->addServiceUUID(NimBLEUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"));
advertisementData.setFlags(0x06); // 通用可发现 + 不支持 BR/EDR
pAdvertising->setAdvertisementData(advertisementData);
// 设置扫描响应(增强名字可见性)
NimBLEAdvertisementData scanResponseData;
scanResponseData.setName("GMD1032-BLE-Slave");
pAdvertising->setScanResponseData(scanResponseData);
pAdvertising->start();
Serial.println("✅ BLE 广播已启动,等待连接... (运行于 Core 1)");
for (;;) {
if (deviceConnected) {
updateBLECharacteristic();
}
delay(1000);
}
}
// ==================== setup() ====================
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 3000);
#ifdef DEBUG_GMD1032
Serial.println("[DEBUG] GMD1032 调试模式已启用");
#endif
// === 初始化 SPI ===
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);
// === 创建两个独立任务,分别绑定到不同核心 ===
xTaskCreatePinnedToCore(sensorTask, "SensorTask", 4096, NULL, 1, NULL, 0); // Core 0
xTaskCreatePinnedToCore(bleTask, "BleTask", 4096, NULL, 2, NULL, 1); // Core 1
}
// ==================== loop() ====================
void loop() {
// 所有功能由 FreeRTOS 任务接管,loop 留空
}
主机输出:
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x28 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3808,len:0x4bc
load:0x403c9700,len:0xbd8
load:0x403cc700,len:0x2a0c
entry 0x403c98d0
uart1_init... baud_rate=115200,rx= 5,tx= 4
🔄 Initializing BLE Master (NimBLE)...
🔍 Starting continuous BLE scan...
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-87
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-82
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-55
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-52
🔍 Scanned: [75:73:22:71:43:11], RSSI=-86
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-86
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-88
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-53
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-55
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-84
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-81
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-56
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-79
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-79
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-74
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-86
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-78
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-57
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-73
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-88
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-53
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-74
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-77
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-82
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-60
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-78
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-81
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-57
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-60
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-81
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-75
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-64
🔍 Scanned: [75:73:22:71:43:11], RSSI=-85
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-81
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-78
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-64
🔍 Scanned: [75:73:22:71:43:11], RSSI=-88
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-85
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-81
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-72
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-60
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-75
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-84
🔍 Scanned: [75:73:22:71:43:11], RSSI=-86
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-72
🔍 Scanned: [75:73:22:71:43:11], RSSI=-87
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-81
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-60
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-77
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-84
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-61
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-65
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-81
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-75
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-78
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-78
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-93
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-63
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-59
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-79
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-82
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-85
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-59
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-79
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-79
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-77
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-75
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-58
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-78
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-76
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-83
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-58
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-62
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-81
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-83
🔍 Scanned: [19:08:52:a1:46:57], RSSI=-62
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-85
🔍 Scanned: [2f:c6:42:20:73:b8], RSSI=-58
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-79
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-86
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-75
🔍 Scanned: [21:6a:3e:dd:db:c2], RSSI=-85
🔍 Scanned: [2f:c6:42:20:73:b8], RSSI=-58
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-83
🔍 Scanned: [2f:c6:42:20:73:b8], RSSI=-62
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-76
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-77
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [75:73:22:71:43:11], RSSI=-86
🔍 Scanned: [04:88:56:c5:1f:d6], RSSI=-76
🔍 Scanned: [2f:c6:42:20:73:b8], RSSI=-75
🔍 Scanned: [75:73:22:71:43:11], RSSI=-88
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-76
🔍 Scanned: [35:f6:52:cf:5d:6f], RSSI=-77
最新发布