✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
目录
[1.1 硬件清单](#1.1 硬件清单)
[1.2 接线方案](#1.2 接线方案)
[1.3 具体接线图](#1.3 具体接线图)
[1.4 连接实物图](#1.4 连接实物图)
[2.1 引脚定义与CRC-8检验表](#2.1 引脚定义与CRC-8检验表)
[2.2 蓝牙数据处理与协议解析](#2.2 蓝牙数据处理与协议解析)
[2.3 温湿度数据返回与CRC校验](#2.3 温湿度数据返回与CRC校验)
[2.4 系统完整代码](#2.4 系统完整代码)
[3.1 HC-05蓝牙模块配置](#3.1 HC-05蓝牙模块配置)
[3.2 使用电脑连接](#3.2 使用电脑连接)
[3.3 功能测试](#3.3 功能测试)
[3.4 视频演示](#3.4 视频演示)
[4.1 工作模式](#4.1 工作模式)
[4.2 工作原理](#4.2 工作原理)
[4.3 通信原理](#4.3 通信原理)
[Q1: 蓝牙连接不稳定怎么办?](#Q1: 蓝牙连接不稳定怎么办?)
[Q2: RGB灯光颜色不正确?](#Q2: RGB灯光颜色不正确?)
[Q3: 如何提高蓝牙数据传输可靠性?](#Q3: 如何提高蓝牙数据传输可靠性?)
(1)项目概述
本项目基于STM32F407VET6主控芯片和HC-05蓝牙模块,实现了一个集环境监测、RGB灯光控制和蓝牙通信于一体的智能系统。系统通过DHT11传感器实时采集环境温湿度数据,通过HC-05蓝牙模块与手机APP通信,同时使用ST7789显示屏展示系统状态和传感器数据。用户可以通过手机APP远程控制RGB灯光颜色,并获取环境数据
(2)项目难点及解决方案
问题描述1:蓝牙传输RGB数据时出现丢包现象
解决方案:实现自定义通信协议,使用#和$作为数据帧起始和结束标志、添加数据校验机制
问题描述2:发送获取温湿度指令后,蓝牙助手没有实际数据
解决方案:对温湿度数据字符串进行CRC计算、将浮点数转换为整数处理,构建数据字符串
一、硬件系统部分
1.1 硬件清单
组件 | 型号 | 数量 |
---|---|---|
主控板 | 零知增强板(STM32F407VET6) | 1 |
蓝牙模块 | HC-05 | 1 |
温湿度传感器 | DHT11 | 1 |
显示屏 | ST7789 (240x320) | 1 |
RGB LED | 共阴RGB LED | 1 |
1.2 接线方案
组件 | 引脚 | 零知增强板引脚 |
---|---|---|
HC-05蓝牙模块 | TX | 17 (SOFT_RX) |
HC-05蓝牙模块 | RX | 16 (SOFT_TX) |
ST7789显示屏 | CS | 53 |
ST7789显示屏 | DC | 9 |
ST7789显示屏 | RST | 8 |
ST7789显示屏 | SDA | 51 |
ST7789显示屏 | SCL | 52 |
DHT11传感器 | DATA | 6 |
RGB LED | R | 2 |
RGB LED | G | 3 |
RGB LED | B | 4 |
注意:HC-05蓝牙模块接入系统 +5V供电,其他组件使用3.3V电源
1.3 具体接线图
TFT显示屏采用硬件SPI、HC-05蓝牙模块使用软件串口通信
1.4 连接实物图

二、代码讲解部分
2.1 引脚定义与CRC-8检验表
cpp
// 蓝牙设置
#define SOFT_RX 17
#define SOFT_TX 16
SoftwareSerial BTSerial(SOFT_RX, SOFT_TX);
#define DEBUGSerial Serial
// ST7789显示屏引脚定义
#define TFT_CS 53
#define TFT_DC 9
#define TFT_RST 8
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// DHT11温湿度传感器
#define DHTPIN 6
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
// RGB LED引脚
#define LED_R 2
#define LED_G 3
#define LED_B 4
// 颜色定义
#define DARKGRAY 0x7BEF
#define LIGHTBLUE 0x867D
// 全局变量
const unsigned int BTRxBufferLength = 100;
char BTRxBuffer[BTRxBufferLength];
unsigned int BTBufferCount = 0;
int redValue = 0;
int greenValue = 0;
int blueValue = 0;
float temperature = 0;
float humidity = 0;
unsigned long lastUpdateTime = 0;
bool btConnected = false;
unsigned long lastBtActivity = 0;
// 错误统计
unsigned int crcErrorCount = 0;
unsigned int dataErrorCount = 0;
unsigned int successfulTransmissions = 0;
// CRC-8校验表(用于数据完整性验证)
const uint8_t crc8_table[256] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
2.2 蓝牙数据处理与协议解析
cpp
void processBTCommand() {
DEBUGSerial.print("Received: ");
DEBUGSerial.println(BTRxBuffer);
// 检查是否为获取温湿度请求
if (strstr(BTRxBuffer, "#GET_DATA$") != NULL) {
sendSensorData();
clrRxBuffer();
return;
}
// 检查RGB指令
if (strstr(BTRxBuffer, "#RGB:") != NULL) {
// 解析RGB值,例如: "#RGB:255,128,64$"
char* ptr = strstr(BTRxBuffer, "#RGB:");
if (ptr != NULL) {
ptr += 5; // 跳过"#RGB:"
char* endPtr = strchr(ptr, '$');
if (endPtr != NULL) {
*endPtr = '\0'; // 替换$为字符串结束符
// 解析RGB值
int r = atoi(strtok(ptr, ","));
int g = atoi(strtok(NULL, ","));
int b = atoi(strtok(NULL, ","));
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
setRGB(r, g, b);
BTSerial.print("#ACK:RGB_SET$/n");
} else {
BTSerial.print("#ERR:INVALID_RGB$/n");
}
}
}
clrRxBuffer();
return;
}
// 预设颜色指令处理...
}
使用自定义协议格式#指令$确保数据传输完整性;支持多种指令类型:温湿度请求、RGB设置、预设颜色选择
2.3 温湿度数据返回与CRC校验
cpp
void sendSensorData() {
// 将浮点数转换为整数处理,避免浮点数格式化问题
int temp_int = (int)(temperature * 10); // 保留一位小数
int hum_int = (int)(humidity * 10); // 保留一位小数
// 构建数据字符串
char dataStr[30];
snprintf(dataStr, sizeof(dataStr), "T%d,H%d", temp_int, hum_int);
// 计算CRC校验码
uint8_t crc = calculateCRC8(dataStr, strlen(dataStr));
// 发送带CRC校验的数据
char dataBuffer[50];
snprintf(dataBuffer, sizeof(dataBuffer), "#DATA:%s,%02X$", dataStr, crc);
BTSerial.println(dataBuffer);
DEBUGSerial.print("Sent sensor data: ");
DEBUGSerial.println(dataBuffer);
successfulTransmissions++;
}
void sendSystemStatus() {
char statusBuffer[100];
snprintf(statusBuffer, sizeof(statusBuffer),
"#STATUS:T%.1f,H%.1f,RGB:%d,%d,%d,BT:%d,OK:%d,CRC_ERR:%d,DATA_ERR:%d$",
temperature, humidity, redValue, greenValue, blueValue,
btConnected ? 1 : 0, successfulTransmissions, crcErrorCount, dataErrorCount);
BTSerial.println(statusBuffer);
DEBUGSerial.print("Sent system status: ");
DEBUGSerial.println(statusBuffer);
}
实时统计传输成功率和错误率、对温湿度数据字符串进行CRC计算,将CRC校验码附加到数据包中一起发送,接收端可以重新计算CRC进行数据验证
2.4 系统完整代码
cpp
#include <SoftwareSerial.h>
#include <DHT.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <Fonts/FreeSans18pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
// 蓝牙设置
#define SOFT_RX 17
#define SOFT_TX 16
SoftwareSerial BTSerial(SOFT_RX, SOFT_TX);
#define DEBUGSerial Serial
// ST7789显示屏引脚定义
#define TFT_CS 53
#define TFT_DC 9
#define TFT_RST 8
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// DHT11温湿度传感器
#define DHTPIN 6
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
// RGB LED引脚
#define LED_R 2
#define LED_G 3
#define LED_B 4
// 颜色定义
#define DARKGRAY 0x7BEF
#define LIGHTBLUE 0x867D
// 全局变量
const unsigned int BTRxBufferLength = 100;
char BTRxBuffer[BTRxBufferLength];
unsigned int BTBufferCount = 0;
int redValue = 0;
int greenValue = 0;
int blueValue = 0;
float temperature = 0;
float humidity = 0;
unsigned long lastUpdateTime = 0;
bool btConnected = false;
unsigned long lastBtActivity = 0;
// 错误统计
unsigned int crcErrorCount = 0;
unsigned int dataErrorCount = 0;
unsigned int successfulTransmissions = 0;
// CRC-8校验表(用于数据完整性验证)
const uint8_t crc8_table[256] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
// CRC-8计算函数
uint8_t calculateCRC8(const char *data, size_t length) {
uint8_t crc = 0;
for (size_t i = 0; i < length; i++) {
crc = crc8_table[crc ^ data[i]];
}
return crc;
}
void setup() {
// 初始化LED引脚
pinMode(LED_R, OUTPUT);
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
setRGB(0, 0, 0);
// 初始化串口通信
BTSerial.begin(9600);
DEBUGSerial.begin(115200); // 提高调试串口波特率以便输出更多信息
DEBUGSerial.println("\n=== RGB Environment Monitor System ===");
DEBUGSerial.println("System initializing...");
DEBUGSerial.println("Debug serial initialized at 115200 baud");
DEBUGSerial.println("Bluetooth serial initialized at 9600 baud");
// 初始化DHT传感器
dht.begin();
DEBUGSerial.println("DHT11 sensor initialized");
// 初始化ST7789显示屏
tft.init(240, 320);
tft.setRotation(3);
tft.invertDisplay(false);
tft.fillScreen(ST77XX_BLACK);
DEBUGSerial.println("ST7789 display initialized");
// 绘制UI界面
drawUI();
DEBUGSerial.println("UI drawn successfully");
// 读取一次传感器数据
readDHT();
// 显示初始数据
updateDisplay();
DEBUGSerial.println("System initialization complete");
DEBUGSerial.println("Waiting for Bluetooth connections...");
DEBUGSerial.println("Available commands:");
DEBUGSerial.println(" #GET_DATA$ - Get sensor data");
DEBUGSerial.println(" #RGB:R,G,B$ - Set RGB color (0-255)");
DEBUGSerial.println(" #COLOR:N$ - Set preset color (1-8)");
DEBUGSerial.println(" #STATUS$ - Get system status");
}
void loop() {
// 处理蓝牙数据
while (BTSerial.available()) {
char buffer = BTSerial.read();
lastBtActivity = millis();
// 检测蓝牙连接状态
if (!btConnected) {
btConnected = true;
DEBUGSerial.println("Bluetooth connected");
updateBtStatus();
}
// 处理数据包
if (buffer == '#') { // 数据包开始
BTBufferCount = 0;
BTRxBuffer[BTBufferCount++] = buffer;
DEBUGSerial.println("Packet start detected");
}
else if (buffer == '$') { // 数据包结束
if (BTBufferCount < BTRxBufferLength - 1) {
BTRxBuffer[BTBufferCount++] = buffer;
BTRxBuffer[BTBufferCount] = '\0';
DEBUGSerial.print("Packet received: ");
DEBUGSerial.println(BTRxBuffer);
processBTCommand();
} else {
DEBUGSerial.println("ERROR: Receive buffer overflow");
}
BTBufferCount = 0;
}
else if (BTBufferCount > 0 && BTBufferCount < BTRxBufferLength - 1) {
BTRxBuffer[BTBufferCount++] = buffer;
}
}
// 检查蓝牙连接状态(5秒无活动认为断开)
if (btConnected && millis() - lastBtActivity > 5000) {
btConnected = false;
DEBUGSerial.println("Bluetooth disconnected (timeout)");
updateBtStatus();
}
// 每2秒更新一次传感器数据和显示
if (millis() - lastUpdateTime > 2000) {
readDHT();
updateDisplay();
lastUpdateTime = millis();
// 定期输出状态信息
static unsigned long lastStatusTime = 0;
if (millis() - lastStatusTime > 10000) {
lastStatusTime = millis();
DEBUGSerial.println("\n=== System Status ===");
DEBUGSerial.print("Temperature: "); DEBUGSerial.print(temperature); DEBUGSerial.println(" °C");
DEBUGSerial.print("Humidity: "); DEBUGSerial.print(humidity); DEBUGSerial.println(" %");
DEBUGSerial.print("RGB: "); DEBUGSerial.print(redValue);
DEBUGSerial.print(", "); DEBUGSerial.print(greenValue);
DEBUGSerial.print(", "); DEBUGSerial.println(blueValue);
DEBUGSerial.print("BT Connected: "); DEBUGSerial.println(btConnected ? "Yes" : "No");
DEBUGSerial.print("Successful transmissions: "); DEBUGSerial.println(successfulTransmissions);
DEBUGSerial.print("CRC errors: "); DEBUGSerial.println(crcErrorCount);
DEBUGSerial.print("Data errors: "); DEBUGSerial.println(dataErrorCount);
}
}
delay(10);
}
void drawUI() {
// 绘制标题
tft.setFont(&FreeSans18pt7b);
tft.setTextColor(ST77XX_WHITE);
tft.setCursor(40, 30);
tft.print("RGB Monitoring");
// 绘制分隔线
tft.drawFastHLine(10, 50, 220, DARKGRAY);
// 绘制温湿度标签(同一行)
tft.setFont(&FreeSans12pt7b);
tft.setTextColor(LIGHTBLUE);
tft.setCursor(20, 85);
tft.print("Temp:");
tft.setCursor(180, 85);
tft.print("Humi:");
// 绘制蓝牙状态标签
tft.setCursor(20, 120);
tft.print("BT Status:");
// 绘制RGB值标签
tft.setCursor(20, 155);
tft.print("RGB Value:");
// 绘制当前颜色标题
tft.setCursor(20, 190);
tft.print("Current Color:");
// 绘制调色板
drawColorPalette();
}
void updateDisplay() {
// 更新温度值(局部刷新)
tft.fillRect(100, 65, 60, 30, ST77XX_BLACK);
tft.setFont(&FreeSans12pt7b);
tft.setTextColor(ST77XX_WHITE);
tft.setCursor(100, 85);
tft.print(temperature, 1);
tft.print("C");
// 更新湿度值(局部刷新)
tft.fillRect(250, 65, 60, 30, ST77XX_BLACK);
tft.setCursor(250, 85);
tft.print(humidity, 1);
tft.print("%");
// 更新RGB值(局部刷新)
tft.fillRect(150, 135, 150, 30, ST77XX_BLACK);
tft.setFont(&FreeSans9pt7b);
tft.setCursor(150, 155);
tft.print(redValue);
tft.print(", ");
tft.print(greenValue);
tft.print(", ");
tft.print(blueValue);
// 更新当前颜色显示
tft.fillRect(180, 165, 40, 40, color565(redValue, greenValue, blueValue));
tft.drawRect(180, 165, 40, 40, ST77XX_WHITE);
// 更新蓝牙状态
updateBtStatus();
}
void updateBtStatus() {
// 更新蓝牙连接状态显示
tft.fillRect(150, 105, 20, 20, ST77XX_BLACK);
if (btConnected) {
tft.fillCircle(160, 115, 10, ST77XX_GREEN);
} else {
tft.fillCircle(160, 115, 10, ST77XX_RED);
}
}
void drawColorPalette() {
// 绘制预定义颜色调色板
int colors[8][3] = {
{255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {255, 255, 0},
{255, 0, 255}, {0, 255, 255}, {255, 255, 255}, {0, 0, 0}
};
int x = 20;
int y = 210;
int size = 25;
int spacing = 8;
for (int i = 0; i < 8; i++) {
tft.fillRect(x + i*(size+spacing), y, size, size,
color565(colors[i][0], colors[i][1], colors[i][2]));
tft.drawRect(x + i*(size+spacing), y, size, size, ST77XX_WHITE);
}
}
void processBTCommand() {
DEBUGSerial.print("Processing command: ");
DEBUGSerial.println(BTRxBuffer);
// 检查数据包完整性(至少包含起始和结束标记)
if (BTBufferCount < 3) {
DEBUGSerial.println("ERROR: Packet too short");
BTSerial.println("#ERR:SHORT_PACKET$");
clrRxBuffer();
return;
}
// 检查是否为获取温湿度请求
if (strstr(BTRxBuffer, "#GET_DATA$") != NULL) {
DEBUGSerial.println("Command: GET_DATA");
sendSensorData();
clrRxBuffer();
return;
}
// 检查状态请求
if (strstr(BTRxBuffer, "#STATUS$") != NULL) {
DEBUGSerial.println("Command: STATUS");
sendSystemStatus();
clrRxBuffer();
return;
}
// 检查RGB指令
if (strstr(BTRxBuffer, "#RGB:") != NULL) {
DEBUGSerial.println("Command: RGB");
char* ptr = strstr(BTRxBuffer, "#RGB:");
if (ptr != NULL) {
ptr += 5;
char* endPtr = strchr(ptr, '$');
if (endPtr != NULL) {
*endPtr = '\0';
// 解析RGB值
char* r_str = strtok(ptr, ",");
char* g_str = strtok(NULL, ",");
char* b_str = strtok(NULL, ",");
if (r_str != NULL && g_str != NULL && b_str != NULL) {
int r = atoi(r_str);
int g = atoi(g_str);
int b = atoi(b_str);
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
setRGB(r, g, b);
DEBUGSerial.print("RGB set to: ");
DEBUGSerial.print(r); DEBUGSerial.print(", ");
DEBUGSerial.print(g); DEBUGSerial.print(", ");
DEBUGSerial.println(b);
BTSerial.println("#ACK:RGB_SET$");
successfulTransmissions++;
} else {
DEBUGSerial.println("ERROR: Invalid RGB values");
BTSerial.println("#ERR:INVALID_RGB$");
dataErrorCount++;
}
} else {
DEBUGSerial.println("ERROR: RGB parameter parsing failed");
BTSerial.println("#ERR:PARSE_ERROR$");
dataErrorCount++;
}
}
}
clrRxBuffer();
return;
}
// 预设颜色指令
const char* colorCommands[] = {"#COLOR:1$", "#COLOR:2$", "#COLOR:3$", "#COLOR:4$",
"#COLOR:5$", "#COLOR:6$", "#COLOR:7$", "#COLOR:8$"};
int colors[8][3] = {
{255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {255, 255, 0},
{255, 0, 255}, {0, 255, 255}, {255, 255, 255}, {0, 0, 0}
};
for (int i = 0; i < 8; i++) {
if (strstr(BTRxBuffer, colorCommands[i]) != NULL) {
DEBUGSerial.print("Command: COLOR ");
DEBUGSerial.println(i + 1);
setRGB(colors[i][0], colors[i][1], colors[i][2]);
BTSerial.println("#ACK:COLOR_SET$");
successfulTransmissions++;
clrRxBuffer();
return;
}
}
// 未知指令
DEBUGSerial.println("ERROR: Unknown command");
BTSerial.println("#ERR:UNKNOWN_CMD$");
dataErrorCount++;
clrRxBuffer();
}
void sendSensorData() {
// 将浮点数转换为整数处理,避免浮点数格式化问题
int temp_int = (int)(temperature * 10); // 保留一位小数
int hum_int = (int)(humidity * 10); // 保留一位小数
// 构建数据字符串
char dataStr[30];
snprintf(dataStr, sizeof(dataStr), "T%d,H%d", temp_int, hum_int);
// 计算CRC校验码
uint8_t crc = calculateCRC8(dataStr, strlen(dataStr));
// 发送带CRC校验的数据
char dataBuffer[50];
snprintf(dataBuffer, sizeof(dataBuffer), "#DATA:%s,%02X$", dataStr, crc);
BTSerial.println(dataBuffer);
DEBUGSerial.print("Sent sensor data: ");
DEBUGSerial.println(dataBuffer);
successfulTransmissions++;
}
void sendSystemStatus() {
char statusBuffer[100];
snprintf(statusBuffer, sizeof(statusBuffer),
"#STATUS:T%.1f,H%.1f,RGB:%d,%d,%d,BT:%d,OK:%d,CRC_ERR:%d,DATA_ERR:%d$",
temperature, humidity, redValue, greenValue, blueValue,
btConnected ? 1 : 0, successfulTransmissions, crcErrorCount, dataErrorCount);
BTSerial.println(statusBuffer);
DEBUGSerial.print("Sent system status: ");
DEBUGSerial.println(statusBuffer);
}
void setRGB(int r, int g, int b) {
// 使用PWM控制LED亮度
analogWrite(LED_R, r);
analogWrite(LED_G, g);
analogWrite(LED_B, b);
redValue = r;
greenValue = g;
blueValue = b;
// 更新显示
updateDisplay();
}
void readDHT() {
// 读取温湿度数据
float newTemp = dht.readTemperature();
float newHum = dht.readHumidity();
// 检查数据是否有效
if (!isnan(newTemp)) {
temperature = newTemp;
} else {
DEBUGSerial.println("ERROR: Invalid temperature reading");
temperature = NULL; // 错误标识
}
if (!isnan(newHum)) {
humidity = newHum;
} else {
DEBUGSerial.println("ERROR: Invalid humidity reading");
humidity = NULL; // 错误标识
}
DEBUGSerial.print("DHT11 read - Temp: ");
DEBUGSerial.print(temperature);
DEBUGSerial.print("°C, Hum: ");
DEBUGSerial.print(humidity);
DEBUGSerial.println("%");
}
uint16_t color565(uint8_t r, uint8_t g, uint8_t b) {
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
void clrRxBuffer(void) {
memset(BTRxBuffer, 0, BTRxBufferLength);
BTBufferCount = 0;
}
系统流程图

三、项目结果演示
3.1 HC-05蓝牙模块配置
(1)HC-05模块连接USB-TTL与PC通信
|---------|-----------|
| USB-TTL | HC-05蓝牙模块 |
| VCC(5V) | VCC |
| GND | GND |
| RXD | TXD |
| TXD | RXD |
(2)将HC-05进入AT模式(按住模块按钮上电)
将HC-05与CH340连接后长按着按钮给模块上电,led灯快闪。进入命令响应模式,可以用AT对蓝牙模块工作的配置
(3)使用串口工具发送AT指令配置模块

设置通信波特率为38400,并勾选上发送新行,蓝牙模块的初始化配置指令如下:
AT+NAME=HC-05 < 修改蓝牙模块名称为HC-05 >
AT+ROLE=0 < 蓝牙模式为从模式 >
AT+CMODE=1 < 蓝牙连接模式为任意地址连接模式,也就是说 该模块可以被任意蓝牙设备连接 >
AT+PSWD=1234 < 蓝牙配对密码为1234 >
AT+UART=9600,0,0 < 蓝牙通信串口波特率为9600,停止位1位, 无校验位 >
3.2 使用电脑连接
(1)蓝牙透传模块配对
给蓝牙透传模块上电时led闪烁,系统将搜索到蓝牙设备"HC-05",输入配对密码"1234",电脑与透传模块将建立起连接

(2)连接虚拟串口
如果以前没有安装过蓝牙串口设备,则系统将自动安装驱动并生成虚拟串口,点击配置,选择合适的COM口。使用串口调试工具打开这个端口(我使用的是COM9)的时候蓝牙模块的LED会由快闪变为双闪,这个时候虚拟串口可以通过无线与PC进行通信

请注意:使用安卓手机连接,首先搜索并配对HC-05模块,下载HC蓝牙助手APP,连接已配对的设备
3.3 功能测试
发送"# +命令+ $"帧格式进行相关指令操作
(1)发送#GET_DATA$获取温湿度数据

系统返回温湿度数据帧:"#DATA:T305,H690,DB$",可以解析为(表示30.5℃、69.0湿度、CRC校验码DB)
(2)设置RGB颜色
发送: #RGB:255,128,64$
接收: #ACK:RGB_SET$

显示当前RGB设置值、颜色表盘,并设置RGB灯颜色
3.4 视频演示
HC-05蓝牙模块控制RGB
选择传出方向的蓝牙虚拟串口COM9,验证RGB值设置、温湿度数据帧和系统状态获取功能
四、HC-05蓝牙模块工作原理
HC-05是一款主从一体蓝牙串口模块,支持蓝牙2.0+EDR规范,具有成本低、体积小、功耗低、收发灵敏性高等优点
蓝牙协议栈分层结构:

4.1 工作模式
(1)AT指令模式
配置模块参数,按住按钮上电或发送特定指令,配置设备名称、配置PIN码、波特率

常用的几种AT指令:(串口助手选上发送新行)
指令名 | 响应 | 含义 |
---|---|---|
AT+RESET | OK | 模块复位 |
AT+ORGL | OK | 恢复默认状态 |
AT+ADDR? | +ADDR:Param OK | 获得蓝牙模块地址 |
AT+NAME=Param | OK | 设置设备名称 Parm:想要设置的名字 |
AT+NAME? | +NAME:Param OK | 获得设备v |
AT+PSWD=Param | OK | 设置模块密码 Parm:想要设置的密码;,默认为"1234" |
AT+PSWD? | +PSWD:Param OK | 获得模块密码 |
AT+UART=Param1 ,Param2,Param3 | OK | 设置串口参数 Param1:波特率 ; Param2:停止位 ;Param3:校验位 |
AT+UART? | +UART:Param1,Param2,Param3 OK | 获得串口参数 |
AT+ROLE=Param | +ROLE:Param OK | Param:参数取值如下:0---从角色;1---主角色;2---回环角色;默认值:0 |
AT指令错误返回:
错误码 | 说明 |
---|---|
0 | AT 命令错误 |
1 | 指令结果为默认值 |
2 | PSKEY 写错误 |
3 | 设备名称太长(超过 32 个字 |
(2)数据透传模式
正常工作时模式,实现串口数据无线传输

4.2 工作原理
HC-05模块基于蓝牙协议栈,实现串口数据到无线信号的转换。当模块接收到串口数据时,会将其打包成蓝牙数据包发送;接收到蓝牙数据时,会解包并通过串口输出

4.3 通信原理
HC-05使用串行通信与主控制器交互,数据格式为:1位起始位、8位数据位、1位停止位,无奇偶校验位。模块支持多种波特率,默认9600bps。

五、常见问题解答
Q1: 蓝牙连接不稳定怎么办?
A: 确保HC-05模块供电稳定:
避免与WiFi等2.4GHz设备干扰,检查天线是否完好
Q2: RGB灯光颜色不正确?
A: 确认RGB LED是共阴还是共阳类型,检查限流电阻值是否合适
Q3: 如何提高蓝牙数据传输可靠性?
A: 可以添加数据校验机制:
CRC校验,或使用更稳定的通信协议
项目资源整合:
串口/蓝牙助手下载链接:开发工具
数据手册:HC-05 datasheet