#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;
BLEMaster::BLEMaster() {}
void BLEMaster::begin() {
Serial.println(“Initializing BLE Master (NimBLE)…”);
NimBLEDevice::init(""); NimBLEScan* pScan = NimBLEDevice::getScan(); pScan->setScanCallbacks(new AdvertisedDeviceCallback()); // ✅ 固定写法 pScan->setActiveScan(true); pScan->start(5000, false); // 扫描20秒
}
void BLEMaster::onDataReceived(void (callback)(const char)) {
dataCallback = callback;
}
void BLEMaster::loop() {
if (connected && pClient && pClient->isConnected()) {
return;
}
if (targetDevice != nullptr && !connected) { Serial.printf("Connecting to: %s\n", targetDevice->getName().c_str()); // 使用工厂方法创建 client pClient = NimBLEDevice::createClient(); pClient->setClientCallbacks(new ClientCallback()); if (!pClient->connect(targetDevice)) { Serial.println("Connection failed!"); pClient->disconnect(); // 不要 delete pClient = nullptr; return; } Serial.println("Connected! Discovering service..."); BLERemoteService* pRemoteService = pClient->getService(NimBLEUUID(TARGET_SERVICE_UUID)); if (pRemoteService == nullptr) { Serial.println("Service not found"); pClient->disconnect(); pClient = nullptr; return; } pRemoteCharacteristic = pRemoteService->getCharacteristic(NimBLEUUID(TARGET_CHAR_UUID)); if (pRemoteCharacteristic == nullptr) { Serial.println("Characteristic not found"); pClient->disconnect(); pClient = nullptr; return; } // 请求大 MTU requestMaxMTU(); // 订阅通知 使用 subscribe() 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); 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; }
}
// 回调实现
// ==================== AdvertisedDeviceCallback 实现 ====================
void BLEMaster::AdvertisedDeviceCallback::onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
// 转换为非 const(如果需要保存副本)
if (strcmp(advertisedDevice->getName().c_str(), BLEMaster::DEVICE_NAME) == 0) {
Serial.printf(“Found: %s [RSSI=%d]\n”,
advertisedDevice->getName().c_str(),
advertisedDevice->getRSSI());
// 删除旧设备 if (BLEMaster::targetDevice) { delete BLEMaster::targetDevice; } // 创建副本(注意:不能直接赋值 const 对象) BLEMaster::targetDevice = new NimBLEAdvertisedDevice(*advertisedDevice); // 停止扫描 NimBLEDevice::getScan()->stop(); }
}
// ==================== ClientCallback 实现 ====================
void BLEMaster::ClientCallback::onConnect(NimBLEClient* pClient) {
Serial.println(“Device connected”);
}
void BLEMaster::ClientCallback::onDisconnect(NimBLEClient* pClient, int reason) {
char addrStr[64] = {0};
snprintf(addrStr, sizeof(addrStr), “%s”, pClient->getPeerAddress().toString().c_str());
Serial.printf(“🔌 Disconnected from: %s, reason=%d\n”, addrStr, reason);
BLEMaster::connected = false; BLEMaster::pRemoteCharacteristic = nullptr; BLEMaster::pClient = nullptr; // 注意:不要 delete pClient,库会自动清理
}
void BLEMaster::ClientCallback::onMTUChange(NimBLEClient* pClient, uint16_t MTU) {
Serial.printf(“MTU 已更新为: %u\n”, MTU);
}
// 请求 MTU
void BLEMaster::requestMaxMTU() {
if (pClient && pClient->isConnected()) {
uint16_t mtu = pClient->getMTU();
Serial.printf(“当前 MTU: %u\n”, mtu);
// 发起 MTU 协商请求(异步操作) if (!pClient->exchangeMTU()) { Serial.println("MTU exchange failed!"); } else { Serial.println("已发送 MTU 请求 = 512"); } }
}
上述为BLE_master.cpp
#include “main.h”
#include <WiFi.h>
#include “esp_uart_driver.h”
#include “uart_msg_analy.h”
#include “network_app.h”
#include “spi_slave_server.h”
#include “BLE_master.h”
uint32_t loop_test_timer_cnt=0;
#define UART1_TX_PIN 4
#define UART1_RX_PIN 5
BLEMaster bleMaster;
// 自定义处理接收到的数据
void handleData(const char* data) {
Serial.println(“[JSON] 收到数据:”);
Serial.println(data);
// 可在此处添加 JSON 解析逻辑(例如用 ArduinoJson) // 示例:提取 cell 和 temp /* #include <ArduinoJson.h> StaticJsonDocument<512> doc; DeserializationError err = deserializeJson(doc, data); if (!err) { JsonArray cell = doc["cell"]; float temp = doc["temp"]; Serial.printf("温度: %.2f°C\n", temp); for (int i = 0; i < 16; i++) { Serial.printf("CELL%d: %.3fV ", i+1, cell[i]); } Serial.println(); } else { Serial.printf("JSON 解析失败: %s\n", err.c_str()); } */
}
void setup()
{
delay(100); // 让 ROM 日志输出完成
int httpCode;
uart0_init(115200, (void *)0);// 初始化主串口
// 需要放在Setup里初始化
uart1_init(115200, UART1_RX_PIN,UART1_TX_PIN,(void *)uart1_msg_analy);// 配置第二个串口
// 初始化随机数生成器
esp_random(); // 初始化随机数生成器
//wifi_app_init();// 启动WiFi功能
//spi_slave_server_init();//初始化SPI从机模式
//Serial.printf(“spi project done… %s %s\r\n”, DATE, TIME);//打印初始化完成信息
bleMaster.begin();
bleMaster.onDataReceived(handleData); // 设置回调
}
int test_cnt = 0;
char receivedChar = 0;
void loop()
{
uart_process();
bleMaster.loop(); // 持续检查连接状态
delay(1000);
}
boolean doDelayMillisTime(uint32_t interval, uint32_t *previousMillis, boolean state)//非阻塞式定时器;state:状态翻转标志
{
uint32_t currentMillis = (uint32_t)SYSTEM_TIMER_MS();//获取系统时间
if (currentMillis - *previousMillis >= interval)// interval 定时间隔(ms);previousMillis 存储上次触发时间的指针
{
*previousMillis = currentMillis;
state = !state;
}
return state;
}
void printf_log_hex(char *hex_name, uint8_t *data, uint32_t len)//十六进制数据打印工具
{
Serial.printf(“%s ,len is:%d \r\n”, hex_name, len);
for (int i = 0; i < len; i++)
{
Serial.printf(“%02x “, data[i]);//单字节总显示2位
}
Serial.printf(”\r\n”);//保证终端正确换行
}
上述为main.cpp
#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()); // 添加 CCCD pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); pCharacteristic->setValue("GMD1032 Ready"); pService->start(); NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->addServiceUUID(NimBLEUUID(SERVICE_UUID)); 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)…
无法找到从机,但手机通过app可以正常连接到从机并接收数据
最新发布