零知IDE——基于STM32F407VET6和HC-05(ZS-040)蓝牙控制RGB与CRC校验系统

✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!

✔访问零知实验室,获取更多实战项目和教程资源吧!

www.lingzhilab.com

目录

一、硬件系统部分

[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 视频演示)

四、HC-05蓝牙模块工作原理

[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

相关推荐
亿道电子Emdoor3 小时前
【ARM】MDK-授权报错解决:用户/主机不在包含列表中
arm开发·stm32·单片机
jghhh014 小时前
HT16C21 驱动模拟I2C实现
单片机·嵌入式硬件·算法
机器视觉知识推荐、就业指导4 小时前
STM32 外设驱动模块:旋转编码器
stm32·单片机·嵌入式硬件
Jayyih6 小时前
IMX6ULL--EPIT,GPT
单片机·嵌入式硬件
沐欣工作室_lvyiyi6 小时前
基于物联网的智能衣柜系统的设计(论文+源码)
stm32·单片机·嵌入式硬件·物联网·毕业设计·智能衣柜
sheepwjl7 小时前
《嵌入式驱动(二):驱动开发基本概念》
arm开发·驱动开发·单片机·嵌入式硬件·imx6ull·驱动·裸机
来自嵌入式的zyz8 小时前
STM32学习-UART串口通信:物理层/协议层/UART基本架构/代码实战
stm32·嵌入式硬件·学习
La Pulga8 小时前
【STM32】USART串口(上)
c语言·stm32·单片机·嵌入式硬件·mcu
茯苓gao8 小时前
STM32G4 电流环闭环(二) 霍尔有感运行
笔记·stm32·单片机·嵌入式硬件·学习