stm32底层项目20251011

1、stm32准备工作

1.1软件

QT6.7

Ubuntu20.04

STM32CubeMx

keil软件

串口调试工具

1.2硬件

STM32U575RITx

1.3项目框图

2、stm32项目现象

2.1QT软件现象

2.2Ubuntu服务器现象

2.3串口调试工具现象

3、stm32项目代码

3.1Ubuntu服务器代码

dbhelper.cpp

cpp 复制代码
#include "dbhelper.h"
#include <iostream>
#include <cstring>

using std::string;
using std::cerr;
using std::cout;
using std::endl;

// 构造函数:打开数据库连接
// 参数:dbname - 数据库文件名
dbHelper::dbHelper(const string& dbname) {
    // 尝试打开SQLite数据库
    if (sqlite3_open(dbname.data(), &db) != SQLITE_OK) {
        // 打开失败,输出错误信息
        cerr << "无法打开数据库: " << sqlite3_errmsg(db) << endl;
    } else {
        // 打开成功,输出成功信息
        cout << "成功打开数据库: " << dbname << endl;
    }
}

// 析构函数:关闭数据库连接
dbHelper::~dbHelper() {
    sqlite3_close(db);
}

// 用户注册功能
// 参数:name - 用户名,pswd - 密码
// 返回值:注册成功返回true,失败返回false
bool dbHelper::regist(const string& name, const string& pswd) {
    // SQL插入语句,使用参数占位符(?)
    const char* sql = "INSERT INTO user(name, pswd) VALUES(?, ?)";
    sqlite3_stmt* stmt = nullptr;
    
    // 准备SQL语句
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
        cerr << "准备注册语句失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    // 绑定参数:用户名和密码
    sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, pswd.c_str(), -1, SQLITE_TRANSIENT);
    
    // 执行SQL语句
    int res = sqlite3_step(stmt);
    // 释放语句资源
    sqlite3_finalize(stmt);
    
    // 检查执行结果
    if (res != SQLITE_DONE) {
        cerr << "注册失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    cout << "用户注册成功: " << name << endl;
    return true;
}

// 用户登录功能
// 参数:name - 用户名,pswd - 密码
// 返回值:登录成功返回true,失败返回false
bool dbHelper::login(const string& name, const string& pswd) {
    // SQL查询语句,根据用户名查询密码
    const char* sql = "SELECT pswd FROM user WHERE name = ?";
    sqlite3_stmt* stmt = nullptr;
    
    // 准备SQL语句
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
        cerr << "准备登录语句失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    // 绑定参数:用户名
    sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_TRANSIENT);
    
    // 执行SQL查询
    int res = sqlite3_step(stmt);
    if (res == SQLITE_ROW) {
        // 获取数据库中的密码
        const char* db_pswd = (const char*)sqlite3_column_text(stmt, 0);
        // 比较密码是否匹配
        bool success = (db_pswd != nullptr && pswd == db_pswd);
        // 释放语句资源
        sqlite3_finalize(stmt);
        
        cout << "登录尝试: " << name << " - " << (success ? "成功" : "失败") << endl;
        return success;
    }
    
    // 释放语句资源
    sqlite3_finalize(stmt);
    cerr << "用户不存在: " << name << endl;
    return false;
}

// 保存日程功能
// 参数:username - 用户名,date - 日期,event - 事件内容
// 返回值:保存成功返回true,失败返回false
bool dbHelper::saveSchedule(const string& username, const string& date, const string& event) {
    // SQL插入语句
    const char* sql = "INSERT INTO schedules(username, date, event) VALUES(?, ?, ?)";
    sqlite3_stmt* stmt = nullptr;
    
    cout << "尝试保存日程: " << username << ", " << date << ", " << event << endl;
    
    // 准备SQL语句
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
        cerr << "准备保存日程语句失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    // 绑定参数:用户名、日期、事件内容
    sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, date.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, event.c_str(), -1, SQLITE_TRANSIENT);
    
    // 执行SQL语句
    int res = sqlite3_step(stmt);
    // 释放语句资源
    sqlite3_finalize(stmt);
    
    // 检查执行结果
    if (res != SQLITE_DONE) {
        cerr << "保存日程失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    cout << "日程保存成功" << endl;
    return true;
}

// 更新日程功能
// 参数:username - 用户名,oldDate - 原日期,newDate - 新日期,newEvent - 新事件内容
// 返回值:更新成功返回true,失败返回false
bool dbHelper::updateSchedule(const string& username, const string& oldDate, 
                            const string& newDate, const string& newEvent) {
    // SQL更新语句,根据用户名和原日期更新为新日期和新事件
    const char* sql = "UPDATE schedules SET date=?, event=? WHERE username=? AND date=?";
    sqlite3_stmt* stmt = nullptr;
    
    cout << "尝试更新日程: " << username << ", " << oldDate << " -> " << newDate << endl;
    
    // 准备SQL语句
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
        cerr << "准备更新日程语句失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    // 绑定参数:新日期、新事件、用户名、原日期
    sqlite3_bind_text(stmt, 1, newDate.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, newEvent.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, username.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 4, oldDate.c_str(), -1, SQLITE_TRANSIENT);
    
    // 执行SQL语句
    int res = sqlite3_step(stmt);
    // 释放语句资源
    sqlite3_finalize(stmt);
    
    // 检查执行结果和影响的行数
    if (res != SQLITE_DONE || sqlite3_changes(db) == 0) {
        cerr << "更新日程失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    cout << "日程更新成功" << endl;
    return true;
}

// 删除日程功能
// 参数:username - 用户名,date - 日期
// 返回值:删除成功返回true,失败返回false
bool dbHelper::deleteSchedule(const string& username, const string& date) {
    // SQL删除语句,根据用户名和日期删除日程
    const char* sql = "DELETE FROM schedules WHERE username=? AND date=?";
    sqlite3_stmt* stmt = nullptr;
    
    cout << "尝试删除日程: " << username << ", " << date << endl;
    
    // 准备SQL语句
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
        cerr << "准备删除日程语句失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    // 绑定参数:用户名、日期
    sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, date.c_str(), -1, SQLITE_TRANSIENT);
    
    // 执行SQL语句
    int res = sqlite3_step(stmt);
    // 释放语句资源
    sqlite3_finalize(stmt);
    
    // 检查执行结果和影响的行数
    if (res != SQLITE_DONE || sqlite3_changes(db) == 0) {
        cerr << "删除日程失败: " << sqlite3_errmsg(db) << endl;
        return false;
    }
    
    cout << "日程删除成功" << endl;
    return true;
}

// 查询日程功能
// 参数:username - 用户名(实际未使用),date - 日期(可选,为空时查询所有日期)
// 返回值:包含日期和事件内容的键值对向量
std::vector<std::pair<std::string, std::string>> 
dbHelper::querySchedules(const std::string& username, const std::string& date) {
    // 存储查询结果的向量
    std::vector<std::pair<std::string, std::string>> result;
    sqlite3_stmt* stmt = nullptr;
    
    // 输出查询信息
    cout << "执行日程查询: ";
    if (!date.empty()) {
        cout << "date=" << date;
    }
    cout << endl;

    // 根据日期参数选择不同的SQL查询语句
    // 注意:此函数实际上忽略了username参数,查询所有用户的日程
    const char* sql = date.empty() ? 
        "SELECT date, event FROM schedules ORDER BY date" :  // 查询所有日期的所有日程
        "SELECT date, event FROM schedules WHERE date=?";    // 查询指定日期的所有日程
    
    // 准备SQL语句
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
        cerr << "准备查询语句失败: " << sqlite3_errmsg(db) << endl;
        return result;
    }
    
    // 只绑定日期参数(如果有),不再绑定用户名
    if (!date.empty()) {
        sqlite3_bind_text(stmt, 1, date.c_str(), -1, SQLITE_TRANSIENT);
    }
    
    // 遍历查询结果集
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        // 获取日期和事件内容
        const char* sdate = (const char*)sqlite3_column_text(stmt, 0);
        const char* event = (const char*)sqlite3_column_text(stmt, 1);
        
        // 确保数据不为空后添加到结果向量
        if (sdate && event) {
            result.emplace_back(sdate, event);
            cout << "查询到日程: " << sdate << " - " << event << endl;
        } else {
            cerr << "查询到空数据,跳过" << endl;
        }
    }
    
    // 释放语句资源
    sqlite3_finalize(stmt);
    
    // 输出查询结果统计
    if (result.empty()) {
        cout << "未查询到匹配的日程" << endl;
    } else {
        cout << "共查询到 " << result.size() << " 条日程" << endl;
    }
    
    return result;
}

dbhelper.h

cpp 复制代码
#ifndef _dbhelper_h_
#define _dbhelper_h_

#include <sqlite3.h>
#include <iostream>
#include <vector>

// 一个数据库刀层负责:打开关闭读取写入一个数据中所有表单的所有操作


class dbHelper{
private:
	sqlite3* db;
	sqlite3_stmt* stmt;
public:
	dbHelper(const std::string& dbname = "");
	~dbHelper();

	// dbHelper 操作的数据库中所有能够操作的表单的函数
	bool regist(const std::string& name,const std::string& pswd);
	bool login(const std::string& name,const std::string& pswd);

	// 写针对别的表单的操作函数
	// 如果说要上设计模式,什么开闭原则之类的,也在这里派生就行了
	// 总而言之,数据库刀层目的很简单:将针对数据库的所有操作,集中在一起,方便查询修改
 /**
     * @brief 保存日程到数据库
     * @param username 用户名
     * @param date 日期
     * @param event 日程内容
     * @return 是否成功
     */
    bool saveSchedule(const std::string& username, const std::string& date, const std::string& event);
    
    /**
     * @brief 更新日程
     * @param username 用户名
     * @param oldDate 原日期
     * @param newDate 新日期
     * @param newEvent 新日程内容
     * @return 是否成功
     */
    bool updateSchedule(const std::string& username, const std::string& oldDate, 
                       const std::string& newDate, const std::string& newEvent);
    
    /**
     * @brief 删除日程
     * @param username 用户名
     * @param date 日期
     * @return 是否成功
     */
    bool deleteSchedule(const std::string& username, const std::string& date);
    
    /**
     * @brief 查询日程
     * @param username 用户名
     * @param date 日期(空字符串表示查询所有)
     * @return 日程列表(日期,内容)的vector
     */
    std::vector<std::pair<std::string, std::string>> 
        querySchedules(const std::string& username, const std::string& date = "");
};

#endif

epoll_server.cpp

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <iostream>
#include <map>
#include <list>
#include <vector>
#include <dirent.h>

#include "pack.h"
#include "dbhelper.h"


using namespace std;


void sendOnlineListToClient(int target_client);

bool client_handler(int client,dbHelper& db);


struct User{
	string username;
	FILE* fp;
	string videoTarget; // 视频目标用户
	// 处理分包时需要以下3个数据
	Pack readed_pack; // 用来暂存已经读取到的部分协议包 以及 用来拼接未读取的协议包
	int readed_pack_size = 0; // 用来记录已读协议包的大小
	int unreaded_pack_size = 0; // 用来记录未读协议包的大小

	 // 处理4个字节的size 发生分包的可能性
	int readed_size = 0;
	int readed_size_size = 0;
	int unreaded_size_size = 0;
};

template <class T1,class T2>
class mymap:public map<T1,T2>{
public:
    // 重命名查找函数,避免与operator[]冲突
    int findByUsername(const string& name){
        for(const auto& ele:*this){
            if(ele.second.username == name){
                return ele.first;
            }
        }
        return -1;
    }

    T2& operator[](const T1& key){
        return this->map<T1,T2>::operator[](key);
    }
};
pthread_mutex_t map_mutex = PTHREAD_MUTEX_INITIALIZER;
mymap<int,User> m;// 键为:客户端套接字,值为 User 对象
// 这个map的作用为:存储所有在线用户的套接字信息和用户的相关信息

// 全局变量
int mcu_server;  // 单片机专用服务器socket
int mcu_count=0;

// 在main函数中创建单片机专用端口
int create_mcu_server(short port) {
    int server = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;    
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    
    if(bind(server,(struct sockaddr*)&addr,sizeof(addr)) == -1){
        perror("bind mcu server");
        return -1;
    }
    
    listen(server, 10);
    return server;
}


int main(int argc, const char *argv[])
{
	if(argc < 2){
		printf("请末端口号\n");
		return 1;
	}

	dbHelper db("user.db");

	short port = atoi(argv[1]);
	// "abc123" -> 0
	int server = socket(AF_INET,SOCK_STREAM,0);
	
    // 创建单片机专用服务器(端口号+1)
    mcu_server = create_mcu_server(port + 1);
    if(mcu_server == -1) {
        printf("创建单片机服务器失败\n");
    } else {
        printf("单片机服务器监听端口: %d\n", port + 1);
    }
    
	struct sockaddr_in addr = {0};
	addr.sin_family = AF_INET;	
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr("0.0.0.0");

	if(bind(server,(struct sockaddr*)&addr,sizeof(addr)) == -1){
		perror("bind");
		return 1;
	}

	listen(server,10);

	// 创建epoll监视列表
	int epfd = epoll_create1(EPOLL_CLOEXEC);

	// 将 0 和 server 添加进入监视列表
	//struct epoll_event epoll_stdin = {.events = EPOLLIN ,data:{fd:server}};
	struct epoll_event epoll_stdin = {events : EPOLLIN , data:{fd:0}};
	struct epoll_event epoll_server = {events : EPOLLIN , data:{fd : server}};

	epoll_ctl(epfd,EPOLL_CTL_ADD,0,&epoll_stdin);
	epoll_ctl(epfd,EPOLL_CTL_ADD,server,&epoll_server);
	    if(mcu_server != -1) {
        struct epoll_event epoll_mcu_server = {events : EPOLLIN , data:{fd : mcu_server}};
        epoll_ctl(epfd,EPOLL_CTL_ADD,mcu_server,&epoll_mcu_server);
    }



	while(1){
		// 提前准备一个激活列表
		struct epoll_event list[20] = {0};
		int count = epoll_wait(epfd,list,20,-1);

		for(int i=0;i<count;i++){
			// list里面全都是激活的描述符,最多判断一下 ,以何种方式激活的

			// 将激活的具体描述符单独取出
			int fd = list[i].data.fd;
			if(fd == 0){
				char buf[1024] = "";
				scanf("%s",buf);
				getchar();
				printf("键盘输入数据:%s\n",buf);
				continue;
			}

            if(fd == server || fd == mcu_server){
                printf("有客户端连接\n");
                struct sockaddr_in client_addr = {0};
                socklen_t client_len = sizeof(client_addr);
                int client = accept(fd,(struct sockaddr*)&client_addr,&client_len);
                printf("新连接的客户端的ip = %s\n",inet_ntoa(client_addr.sin_addr));
                printf("新连接的客户端的port = %d\n",ntohs(client_addr.sin_port));
                
                pthread_mutex_lock(&map_mutex);
                
                if(fd == mcu_server) {
                    // 单片机专用端口的连接,自动标记为单片机
                    mcu_count++;
                    m[client].username = "MCU_" + std::to_string(client);
                    printf("单片机客户端 %d 通过专用端口连接\n", client);
                } else {
                    // 主端口的连接,等待登录
                    printf("普通客户端,等待登录\n");
                }
                
                pthread_mutex_unlock(&map_mutex);
                
                struct epoll_event epoll_client = {events : EPOLLIN , data:{fd : client}};
                epoll_ctl(epfd,EPOLL_CTL_ADD,client,&epoll_client);
                continue;
            }

			// 剩下的都是客户端描述符
            bool res = client_handler(fd,db);
            if(res == false){
                epoll_ctl(epfd,EPOLL_CTL_DEL,fd,nullptr);
                pthread_mutex_lock(&map_mutex);
                m.erase(fd);
                if(m[fd].username.find("MCU_")==0){
                    mcu_count--;
                }
                pthread_mutex_unlock(&map_mutex);
            }
		}
		
	}
	return 0;
}

bool client_handler(int client, dbHelper& db) {
    while (1) {
        int size = 0;
        int res = 0;
        Pack pack;

if (m[client].username.find("MCU_") == 0) {
        char buf[4096] = {0};
        int res = recv(client, buf, sizeof(buf) - 1, MSG_DONTWAIT);
        if (res > 0) {
            //成功接收单片机发送来的字符串
            string mcu_message(buf,res);
            cout << "收到单片机消息: " << mcu_message << endl;
            size_t pos=0;
            while((pos=mcu_message.find('\n'))!=string::npos){
                string s_message=mcu_message.substr(0,pos);
                mcu_message.erase(0,pos+1);
                if(s_message.empty()) continue;
                Pack s_pack;
                if(s_message.find("TEM")!=string::npos)
                    s_pack.setType(TYPE_TEM_HUM);
                else if(s_message.find("bpm")!=string::npos)
                    s_pack.setType(TYPE_HEART);
                else if(s_message.find("detected")!=string::npos)
                    s_pack.setType(TYPE_DETECTED);
                else if(s_message.find("away")!=string::npos)
                    s_pack.setType(TYPE_AWAY);
                else if(s_message.find("lux")!=string::npos)
                    s_pack.setType(TYPE_LUX);
                else
                    s_pack.setType(TYPE_ERROR);
                s_pack<<s_message;
                for(auto it = m.begin(); it != m.end(); ){
					if(it->second.username.find("MCU_") != 0){
						int error = 0;
						socklen_t len = sizeof(error);
						
						if(getsockopt(it->first, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error == 0) {
							// 发送数据
							ssize_t sent = send(it->first, (char*)&s_pack, s_pack.size(), MSG_DONTWAIT | MSG_NOSIGNAL);
							if(sent == -1) {
								cout<<"客户端 "<<it->first<<" 发送失败,可能已断开连接"<<endl;
								close(it->first);
								it = m.erase(it);  // 正确删除并移动迭代器
							} else {
								cout<<"成功转发数据给客户端 "<<it->first<<",发送字节数: "<<sent<<endl;
								++it;  // 移动到下一个元素
							}
						} else {
							cout<<"客户端 "<<it->first<<" 套接字无效,进行清理"<<endl;
							close(it->first);
							it = m.erase(it);  // 正确删除并移动迭代器
						}
					} else {
						++it;  // 移动到下一个元素
					}
				}
            }
            return true;
        }
    }

        if (m[client].unreaded_pack_size != 0) {
            res = recv(client, (char*)&m[client].readed_pack + m[client].readed_pack_size, 
                      m[client].unreaded_pack_size, MSG_DONTWAIT);
            if (res != m[client].unreaded_pack_size) {
                m[client].readed_pack_size += res;
                m[client].unreaded_pack_size -= res;
                break;
            }
            pack = m[client].readed_pack;
            m[client].unreaded_pack_size = 0;
        } else {
            if (m[client].unreaded_size_size != 0) {
                recv(client, (char*)&m[client].readed_size + m[client].readed_size_size, 
                      m[client].unreaded_size_size, MSG_DONTWAIT);
                size = m[client].readed_size;
                m[client].unreaded_size_size = 0;
            } else {
                // 读取4字节size
                res = recv(client, (char*)&size, 4, MSG_DONTWAIT);
                if (res == -1) {
                    return true;
                } else if (res == 0) {
                    cout << "4字节recv结束" << endl;
                    return false;
                }
                // 处理size分包
                if (res != 4) {
                    // 修正点:原memcpy参数错误,应拷贝res字节(而非readed_size_size)
                    memcpy(&m[client].readed_size, &size, res);  
                    m[client].readed_size_size = res;
                    m[client].unreaded_size_size = 4 - res;
                    break;
                }
            }

            // 读取size-4字节的包内容
            res = recv(client, (char*)&pack + 4, size - 4, MSG_DONTWAIT);
            if (res == 0) {
                cout << "size-4字节recv结束" << endl;
                return false;
            } else if (res == -1) {
                return true;
            }
            pack.setSize(size);

            // 处理包内容分包
            if (res != size - 4) {
                cout << "发生分包" << endl;
                memcpy(&m[client].readed_pack, &pack, res + 4);
                m[client].readed_pack_size = res + 4;
                m[client].unreaded_pack_size = size - m[client].readed_pack_size;
                break;
            }
        }

        // 2. 修复switch大括号不匹配:补全闭合}
        switch (pack.getType()) {
            case TYPE_REGIST: {
                vector<string> list = pack.readAll();
                string name = list[0];
                string pswd = list[1];
                bool res = db.regist(name, pswd);
                pack.setBack(res ? BACK_SUCCESS : BACK_ERR);
                pack >> client;
                break;
            }
            case TYPE_LOGIN: {
                vector<string> list = pack.readAll();
                string name = list[0];
                string pswd = list[1];
                bool res = db.login(name, pswd);
                if (res) {
                    pack.setBack(BACK_SUCCESS);
                    m[client].username = name;
                } else {
                    pack.setBack(BACK_ERR);
                }
                pack >> client;
                break;
            }
            case TYPE_ORDER: {
                if(mcu_count==0){
                    cout<<"当前无被控端连接"<<endl;
                    pack.setType(TYPE_NOMCU);
                    pack>>client;
                    break;
                }
                vector<string> list = pack.readAll();
                cout << "收到客户端发来的命令:" << list[0] << endl;
                string command = list[0];
                cout << "当前在线客户端数量:" << m.size() << endl;
                // 发送给所有MCU客户端
                for (auto& ele : m) {
                    if (ele.second.username.find("MCU_") == 0) {
                        ssize_t sent = send(ele.first, command.c_str(), command.length(), 0);
                        cout << "已发送指令给单片机 " << ele.first << ": " << command 
                             << " (长度:" << command.length() << ", 实际发送:" << sent << ")" << endl;
                    }
                }
                break;
            }
            // 建议添加default,处理未知类型的包
            default: {
                cout << "未知的包类型:" << pack.getType() << endl;
                break;
            }
        }  // 修正点:补全switch的闭合大括号
    }  // 修正点:补全while(1)的闭合大括号

    // 3. 修复返回值缺失:while循环意外退出时的默认返回值
    return false;
}  // 修正点:补全client_handler函数的闭合大括号

pack.cpp

cpp 复制代码
#include "pack.h"
#include <unistd.h>  // 包含 usleep 函数的声明

Pack::Pack()
{

}

void Pack::setType(Type type)
{
    this->type = type;
}

void Pack::setBack(Back back)
{
    this->back = back;
}

void Pack::setSize(int size)
{
    pack_size = size;
}

int Pack::size()
{
    return pack_size;
}

Type Pack::getType()
{
    return type;
}

Back Pack::getBack()
{
    return back;
}

void Pack::append(const string &val)
{
    const char* p = val.data();
    short len = strlen(p); // strlen ( char*)
    *(short*)(buf+used) = len;
    used += 2;

    memcpy(buf+used,p,len);
    used += len;

    pack_size = 12 + used;
}

void Pack::append(const char *p, int size)
{
    memset(buf,0,4096);
    memcpy(buf,p,size);
    pack_size = 12 + size;
}

vector<string> Pack::readAll()
{
    vector<string> list;
    int readed_size = 0;// 准备一个记录已读多少自己的数据,方便指针偏移,跳过已读部分
    while(1){
        short size = *(short*)(buf+readed_size);
        if(size == 0){break;}
        readed_size += 2;
        
        char temp[size + 1];
        memset(temp,0,size+1);
        memcpy(temp,buf+readed_size,size);
        readed_size += size;
        
        string str = temp;
        list.push_back(str);
    }
    return list;
}

void Pack::readAll(char* buf,int size){
	// 参数buf需要调用者提前准备一下
	memcpy(buf,this->buf,size);
}

Pack &Pack::operator<<(const string &val)
{
    append(val);
    return *this;
}

void Pack::operator>>(int client)
{
	send(client,(char*)this,pack_size,0);
}

pack.h

cpp 复制代码
#ifndef PACK_H
#define PACK_H

#include <cstring>
#include <vector>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>


enum Type {
    TYPE_REGIST,
    TYPE_LOGIN,
    TYPE_ORDER,
    TYPE_TEM_HUM,  //温湿度数据
    TYPE_HEART,
    TYPE_ERROR,
    TYPE_DETECTED,
    TYPE_AWAY,
    TYPE_LUX,
    TYPE_NOMCU
};
// 协议包发给服务器之后,服务器要处理,如果处理成功,服务器发给客户端的协议包里面的Back,就是SUCCESS
enum Back{
    BACK_SUCCESS,
    BACK_ERR,
    BACK_USER_NOT_ONLINE,  // 添加这个枚举值
};

using std::string;
using std::vector;

class Pack
{
	
private:
    int pack_size = 12;// 记录整个协议包大小,方便服务器知道当前发过去的协议包有多大
    Type type;
    Back back;
    char buf[4096] = "";
    int used = 0;
public:
    Pack();
    void append(const string& val);// 将外部的字符串数据,写入协议包的buf里面
	void append(const char* p,int size);
    vector<string> readAll();// 读取协议包的buf所有字符串
	void readAll(char* buf,int size);
    Pack& operator<<(const string& val);
    void operator>>(int client);
    
    void setType(Type type);
    void setBack(Back back);
    void setSize(int size);

    int size(); // pack_size的get接口
    Type getType();
    Back getBack();
};

#endif // PACK_H

3.2STM32CubeMX中的配置&&Keil中的代码

main.h

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32u5xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

main.c

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "icache.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "wifi.h"
#include "ap3216c.h"
#include "max30102.h"
#include "sht20.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t RecvBuf1[2048];
uint8_t RecvBuf5[2048];
uint8_t get_bpm_count=0; //已采集样本数
uint8_t bpm_flag=0; //标记是否需要继续采集
extern volatile uint32_t UART5_RecvCount;
extern volatile uint32_t USART1_RecvCount;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void SystemPower_Config(void);
/* USER CODE BEGIN PFP */


/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
volatile int flag=0;
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* Configure the System Power */
  SystemPower_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_UART5_Init();
  MX_USART1_UART_Init();
  MX_I2C1_Init();
  MX_ICACHE_Init();
  /* USER CODE BEGIN 2 */
	//开启串口5的接收中断
	HAL_UART_Receive_IT(&huart5,RecvBuf5,sizeof(RecvBuf5));
	//开启5空闲中断
	__HAL_UART_ENABLE_IT(&huart5, UART_IT_IDLE);
			//开启串口1的接收中断
	HAL_UART_Receive_IT(&huart1,RecvBuf1,sizeof(RecvBuf1));
	//开启1空闲中断
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
	
	//设置不自动连接
	if(wifi_config("AT+CWAUTOCONN=0",100)==0)
		u1_printf("stop auto successfully\n");
	else
		u1_printf("stop auto failed\n");
	
	//设置站点模式
	if(wifi_config("AT+CWMODE=1",100)==0)
		u1_printf("Station Mode successfully\n");
	else
		u1_printf("Station Mode failed\n");

	//连接热点
	if(wifi_connect("jingjing","00000000")==0)
		u1_printf("001 AP connect successfully\n");
	else
		u1_printf("001 AP connect failed\n");
	
	if(wifi_connecTCP("192.168.43.12",8889)==0)
		u1_printf("TCP successfully\n");
	else
		u1_printf("TCP failed\n");
	
		//设置通透模式
	if(wifi_config("AT+CIPMODE=1",100)==0)
		u1_printf("CIPMODE=1 successfully\n");
	else
		u1_printf("CIPMODE=1 failed\n");
	
	//设置数据发送
		if(wifi_config("AT+CIPSEND",100)==0)
	u1_printf("send	successfully\n");
	else
		u1_printf("send failed\n");
	uint16_t value=0;
	uint16_t temp,hum;
    reset();
    MAX30102_Init();
    MAX30102_Data_Init();  // 初始化数据结构

		uint32_t ir_adc,red_adc;
	  uint8_t heart_rate, spo2;
	// 在main函数中添加信号质量变量
uint8_t signal_quality = 0;
	int i;
	
		if(checkmode()==-1)
		{
			u1_printf("SPO2 ERROR\n");
		}
		u5_printf("The controlled end is connected successfully.\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
				if(flag==1)
				{

					u1_printf("%s\r\n",(char*)RecvBuf5);
					if(strstr((char*)RecvBuf5,"open light")!=NULL)
					{
						HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,GPIO_PIN_SET);
						u5_printf("open light success~\n");
					}
					else if(strstr((char*)RecvBuf5,"close light")!=NULL)
					{
						HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,GPIO_PIN_RESET);
						u5_printf("close light success~\n");
					}
					else if(strstr((char*)RecvBuf5,"sht20")!=NULL)
					{
						u5_printf("get data success~\n");
						temp=SHT20_Get_Data(TEMP_CMD);
						hum=SHT20_Get_Data(HUM_CMD);
						SHT20_Digital_to_Analog(temp,hum);
						
					}
					else if(strstr((char*)RecvBuf5,"fanon")!=NULL)
					{
						HAL_GPIO_WritePin(GPIOC,GPIO_PIN_6,GPIO_PIN_SET);
						u5_printf("open fan success~\n");
					}
					else if(strstr((char*)RecvBuf5,"fanoff")!=NULL)
					{
						HAL_GPIO_WritePin(GPIOC,GPIO_PIN_6,GPIO_PIN_RESET);
						u5_printf("close fan success~\n");
					}
        else if(strstr((char*)RecvBuf5, "bpm") !=NULL)
        {
						if(!bpm_flag) {
								// 开始新一轮采样
								bpm_flag = 1;
								get_bpm_count = 0;
								u5_printf("Started collecting heart rate samples...\n");
						} else {
								// 已经在采集过程中,告知用户等待
								u5_printf("Still collecting samples (%d/%d)...\n", get_bpm_count, 30);
						}
        }
					else if(strstr((char*)RecvBuf5,"ap3216")!=NULL)
					{
						    
					}
			memset(RecvBuf5,0,sizeof(RecvBuf5));
			flag=0;
			
		}
		
		else if(flag==2)
		{


			u5_printf("%s\r\n",(char*)RecvBuf1);
			memset(RecvBuf1,0,sizeof(RecvBuf1));
			flag=0;

		}
		uint16_t ap3216c_value = Get_Data();
		//u5_printf("%d lux\n",Get_ALS_Data());
		

if(bpm_flag)
{
    // 读取MAX30102数据
    if(MAX30102_Read_Data(&ir_adc, &red_adc) == 0)
    {
			
        // 评估信号质量
        if(ir_adc > 1000 && red_adc > 1000) {
            signal_quality = 2; // 良好
            get_bpm_count++;
        } else if(ir_adc > 500 && red_adc > 500) {
            signal_quality = 1; // 一般
            get_bpm_count++;
        } else {
            signal_quality = 0; // 较差,不计入有效样本
        }
        
        // 只有信号质量好时才更新数据
        if(signal_quality >= 1) {
            MAX30102_Update_Data(ir_adc, red_adc);
        }
        
        // 检查是否已采集足够样本
        if(get_bpm_count >= 30) {
            // 获取计算结果
            MAX30102_Get_Results(&heart_rate, &spo2);
            
            // 发送完整结果
            u5_printf("Heart rate measurement completed!\n");
            u5_printf("Signal Quality: %d/2, Heart Rate: %d bpm, SpO2: %d%%\n", 
                     signal_quality, heart_rate, spo2);
            
            // 重置标志位,结束本次采样
            get_bpm_count = 0;
            bpm_flag = 0;
        }
    }
    
    // 添加短暂延时,控制采样频率
    HAL_Delay(66);  // 约15Hz采样率 (1000ms/15≈66ms)
}
		
		else
    {
        // 非心率监测时的延时
        HAL_Delay(100);
        
        // 定期读取AP3216数据(可选,避免过于频繁)
        static uint32_t last_ap3216_time = 0;
        if(HAL_GetTick() - last_ap3216_time > 1000) {  // 每秒读取一次
            uint16_t ap3216c_value = Get_Data();
            uint16_t als_value = Get_ALS_Data();
            // 可以选择性地打印,避免串口输出过多
            u5_printf("%d lux\n", als_value);
            last_ap3216_time = HAL_GetTick();
        }
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_0;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
  RCC_OscInitStruct.PLL.PLLMBOOST = RCC_PLLMBOOST_DIV4;
  RCC_OscInitStruct.PLL.PLLM = 3;
  RCC_OscInitStruct.PLL.PLLN = 10;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 1;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_1;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK3;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief Power Configuration
  * @retval None
  */
static void SystemPower_Config(void)
{

  /*
   * Disable the internal Pull-Up in Dead Battery pins of UCPD peripheral
   */
  HAL_PWREx_DisableUCPDDeadBattery();
/* USER CODE BEGIN PWR */
/* USER CODE END PWR */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

gpio.h

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.h
  * @brief   This file contains all the function prototypes for
  *          the gpio.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __GPIO_H__
#define __GPIO_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_GPIO_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */

gpio.c

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.c
  * @brief   This file provides code for the configuration
  *          of all used GPIO pins.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "gpio.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** Configure pins
     PA13 (JTMS/SWDIO)   ------> DEBUG_JTMS-SWDIO
     PA14 (JTCK/SWCLK)   ------> DEBUG_JTCK-SWCLK
*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4|GPIO_PIN_6, GPIO_PIN_RESET);

  /*Configure GPIO pins : PC4 PC6 */
  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

}

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

i2c.h

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    i2c.h
  * @brief   This file contains all the function prototypes for
  *          the i2c.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __I2C_H__
#define __I2C_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

extern I2C_HandleTypeDef hi2c1;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_I2C1_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __I2C_H__ */

i2c.c

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    i2c.c
  * @brief   This file provides code for the configuration
  *          of the I2C instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "i2c.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

I2C_HandleTypeDef hi2c1;

/* I2C1 init function */
void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.Timing = 0x30909DEC;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Analogue filter
  */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Digital filter
  */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(i2cHandle->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */

  /* USER CODE END I2C1_MspInit 0 */

  /** Initializes the peripherals clock
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
    PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**I2C1 GPIO Configuration
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* I2C1 clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();
  /* USER CODE BEGIN I2C1_MspInit 1 */

  /* USER CODE END I2C1_MspInit 1 */
  }
}

void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle)
{

  if(i2cHandle->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspDeInit 0 */

  /* USER CODE END I2C1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_I2C1_CLK_DISABLE();

    /**I2C1 GPIO Configuration
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);

    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7);

  /* USER CODE BEGIN I2C1_MspDeInit 1 */

  /* USER CODE END I2C1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

usart.h

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.h
  * @brief   This file contains all the function prototypes for
  *          the usart.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "stdarg.h"
#include "string.h"

/* USER CODE END Includes */

extern UART_HandleTypeDef huart5;

extern UART_HandleTypeDef huart1;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_UART5_Init(void);
void MX_USART1_UART_Init(void);

/* USER CODE BEGIN Prototypes */

void TransByte(UART_HandleTypeDef *huartx,uint8_t ch);
void u1_printf(char *fmt,...);
void u5_printf(char *fmt,...);

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __USART_H__ */

usart.c

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.c
  * @brief   This file provides code for the configuration
  *          of the USART instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

UART_HandleTypeDef huart5;
UART_HandleTypeDef huart1;

/* UART5 init function */
void MX_UART5_Init(void)
{

  /* USER CODE BEGIN UART5_Init 0 */

  /* USER CODE END UART5_Init 0 */

  /* USER CODE BEGIN UART5_Init 1 */

  /* USER CODE END UART5_Init 1 */
  huart5.Instance = UART5;
  huart5.Init.BaudRate = 115200;
  huart5.Init.WordLength = UART_WORDLENGTH_8B;
  huart5.Init.StopBits = UART_STOPBITS_1;
  huart5.Init.Parity = UART_PARITY_NONE;
  huart5.Init.Mode = UART_MODE_TX_RX;
  huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart5.Init.OverSampling = UART_OVERSAMPLING_16;
  huart5.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart5.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart5.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart5) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart5, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart5, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart5) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN UART5_Init 2 */

  /* USER CODE END UART5_Init 2 */

}
/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(uartHandle->Instance==UART5)
  {
  /* USER CODE BEGIN UART5_MspInit 0 */

  /* USER CODE END UART5_MspInit 0 */

  /** Initializes the peripherals clock
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_UART5;
    PeriphClkInit.Uart5ClockSelection = RCC_UART5CLKSOURCE_PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    /* UART5 clock enable */
    __HAL_RCC_UART5_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**UART5 GPIO Configuration
    PC12     ------> UART5_TX
    PD2     ------> UART5_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF8_UART5;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF8_UART5;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

    /* UART5 interrupt Init */
    HAL_NVIC_SetPriority(UART5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(UART5_IRQn);
  /* USER CODE BEGIN UART5_MspInit 1 */

  /* USER CODE END UART5_MspInit 1 */
  }
  else if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */

  /** Initializes the peripherals clock
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
    PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==UART5)
  {
  /* USER CODE BEGIN UART5_MspDeInit 0 */

  /* USER CODE END UART5_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_UART5_CLK_DISABLE();

    /**UART5 GPIO Configuration
    PC12     ------> UART5_TX
    PD2     ------> UART5_RX
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_12);

    HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);

    /* UART5 interrupt Deinit */
    HAL_NVIC_DisableIRQ(UART5_IRQn);
  /* USER CODE BEGIN UART5_MspDeInit 1 */

  /* USER CODE END UART5_MspDeInit 1 */
  }
  else if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
uint8_t USART_TxBuff[1024];

//给不同的串口发送数据
void TransByte(UART_HandleTypeDef *huartx,uint8_t ch)
{
  //检测发送缓存区是否满足发送的条件
   while(!(huartx->Instance->ISR & (1<<7))){}
      huartx->Instance->TDR = ch;
}

//不定长参数 ...
// 像printf的函数, 它的参数是随时变化的,随着打印%的格式越多,参数越多
// printf("%s %d %x\n",ch,numd.numx,);
void u1_printf(char *fmt,...)
{
    //1. 不定长的变量
    va_list ap;
  //2. 用于初始化一个 va_list 变量,以便在可变参数函数中访问可变数量的参数。
  //参数1: 访问可变参数的类型(就是 va_list变量)
  //参数2: 可变参数列表之前的最后一个已知的固定参数
  va_start(ap,fmt);
  //3. 将不定长格式 格式化输出到数组
  // fmt = "字符串"     ap 不定长参数
  // fmt = "%s  %d  %x"   ap = ch,numd,numx 指定格式 -> "\"hello\",10,0x32"
  vsprintf((char *)USART_TxBuff,fmt,ap);
  //4. 释放 va_list变量
  va_end(ap);

  uint32_t i = 0;
  //发送数组的实际长度
  uint32_t length = strlen((char *)USART_TxBuff);
  //发送到指定的串口
  while(i < length)
  {
    //通过循环, 8bit一次, 发送过去
    TransByte(&huart1,USART_TxBuff[i]);
    i++;
  }
   
}

void u5_printf(char *fmt,...)
{
   //1. 不定长的变量
    va_list ap;
  //2. 用于初始化一个 va_list 变量,以便在可变参数函数中访问可变数量的参数。
  //参数1: 访问可变参数的类型(就是 va_list变量)
  //参数2: 可变参数列表之前的最后一个已知的固定参数
  va_start(ap,fmt);
  //3. 将不定长格式 格式化输出到数组
  // fmt = "字符串"     ap 不定长参数
  // fmt = "%s  %d  %x"   ap = ch,numd,numx 指定格式 -> "\"hello\",10,0x32"
  vsprintf((char *)USART_TxBuff,fmt,ap);
  //4. 释放 va_list变量
  va_end(ap);

  uint32_t i = 0;
  //发送数组的实际长度
  uint32_t length = strlen((char *)USART_TxBuff);
  //发送到指定的串口
  while(i < length)
  {
    //通过循环, 8bit一次, 发送过去
      TransByte(&huart5,USART_TxBuff[i]);
    i++;
  }
  
}
/* USER CODE END 1 */

stm32u5xx_it.h

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32u5xx_it.h
  * @brief   This file contains the headers of the interrupt handlers.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
 ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32U5xx_IT_H
#define __STM32U5xx_IT_H

#ifdef __cplusplus
 extern "C" {
#endif

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void USART1_IRQHandler(void);
void UART5_IRQHandler(void);
/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

#ifdef __cplusplus
}
#endif

#endif /* __STM32U5xx_IT_H */

stm32u5xx_it.c

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32u5xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32u5xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern uint8_t RecvBuf1[2048];
extern uint8_t RecvBuf5[2048];
extern volatile int flag;

// 添加接收计数变量
volatile uint32_t UART5_RecvCount = 0;
volatile uint32_t USART1_RecvCount = 0;
/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart5;
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Prefetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32U5xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32u5xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!=0)
    {
        __HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF);
        huart1.pRxBuffPtr=RecvBuf1;
        
        //如果逻辑处理
        flag=2;
        
        HAL_UART_Receive_IT(&huart1,RecvBuf1,sizeof(RecvBuf1));
    }
  /* USER CODE END USART1_IRQn 1 */
}

/**
  * @brief This function handles UART5 global interrupt.
  */
void UART5_IRQHandler(void)
{
  /* USER CODE BEGIN UART5_IRQn 0 */

  /* USER CODE END UART5_IRQn 0 */
  HAL_UART_IRQHandler(&huart5);
  /* USER CODE BEGIN UART5_IRQn 1 */
	if(__HAL_UART_GET_FLAG(&huart5,UART_FLAG_IDLE)!=0)
	{
		__HAL_UART_CLEAR_FLAG(&huart5,UART_CLEAR_IDLEF);
		huart5.pRxBuffPtr=RecvBuf5;
		
		//如果逻辑处理
		flag=1;
		
		HAL_UART_Receive_IT(&huart5,RecvBuf5,sizeof(RecvBuf5));
	}
  /* USER CODE END UART5_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

wifi.h

cpp 复制代码
#ifndef _WIFI_H_
#define _WIFI_H_

#include "stdio.h"
#include "usart.h"
#include "string.h"

// 用于串口5空闲中断和接收中断接收数据的容器
extern uint8_t RecvBuf5[2048];

// 配置ESP8266的函数
// 参数:cmd:AT指令,time:超时检测
uint32_t wifi_config(char *cmd,uint16_t time);

// 用于ESP8266连接WiFi热点的函数
// 参数:ssdi:WiFi名称,password:WiFi密码
uint32_t wifi_connect(char * ssid,char * password);

// 用于ESP8266连接TCP服务器的函数
// 参数:IP:服务器IP地址,port:服务器端口号
uint32_t wifi_connecTCP(char *IP,int Port);

// 用于ESP8266发送TCP数据(透传模式 + wifi_config函数)
// 参数:data:要发送的数据
uint32_t TCP_send(char *data);
#endif

wifi.c

cpp 复制代码
#include "wifi.h"

//AT指令的发送
//参数1: AT命令    参数2: 超时检测 
uint32_t wifi_config(char *cmd,uint16_t time)
{
	  //将AT命令发送到串口5
     u5_printf("%s\r\n",cmd);

	while(time--)
	{
	   if(strstr((char *)RecvBuf5,"OK")!= NULL)
	   {
	       break;
	   }
		 u1_printf("time:%d\n", time);
	   HAL_Delay(50);
     }
	 memset(RecvBuf5,0,sizeof(RecvBuf5));
	 
	 //如果检测到OK, 则返回0 表示成功
	 if(time > 0)
	 {
	    return 0;
	 }
	 else  //未检测到OK, 返回-1, 表示失败
	 {
	    return -1;
	 }	 
}

//wifi连接
uint32_t wifi_connect(char * ssid,char * password)
{
     u5_printf("AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,password);
	 uint16_t time = 300;
	 uint16_t i = 0;
	 while(time--)
	 {
	     if(strstr((char *)RecvBuf5,"OK") != NULL)
		 {
		    break;
		 }
		 u1_printf("wifi connect.......%d\n",i++);
		 HAL_Delay(100);
	 }
	  memset(RecvBuf5,0,sizeof(RecvBuf5));
	  //如果检测到OK, 则返回0 表示成功
	 if(time > 0)
	 {
	    return 0;
	 }
	 else  //未检测到OK, 返回-1, 表示失败
	 {
	    return -1;
	 }	
}

//连接TCP服务器
uint32_t wifi_connecTCP(char *IP,int Port)
{
     u5_printf("AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",IP,Port);
	  uint16_t time = 70;
	  uint16_t i = 0;
	 while(time--)
	 {
		 if(strstr((char *)RecvBuf5,"OK") != NULL)
		 {
		     break;
		 }
		 u1_printf("TCP connect.......%d\n",i++);
	     HAL_Delay(100);
	 }
	 	  //如果检测到OK, 则返回0 表示成功
	 if(time > 0)
	 {
	    return 0;
	 }
	 else  //未检测到OK, 返回-1, 表示失败
	 {
	    return -1;
	 }
}

uint32_t TCP_send(char *data)
{
	 u5_printf("%s",data);
	 return 0;
}

ap3216.h

cpp 复制代码
#include "stdint.h"
#include "i2c.h"

//AP3216三合一传感器的7位从机地址
#define AP3216_ADDRESS 0x1E

#define AP3216_ADDRESS_W ((0x1E<<1) | 0)
#define AP3216_ADDRESS_R ((0x1E<<1) | 1)

//距离传感器地址
#define DIS_CMD 0x0E

//软件复位
void reset();

uint32_t Get_Data();
uint32_t Get_ALS_Data();

ap3216.c

cpp 复制代码
#include "ap3216c.h"
#include "wifi.h"

uint8_t object_detected_flag=0;

void reset()
{
	uint8_t buf[2]={0x00,0x40};
	HAL_I2C_Master_Transmit(&hi2c1,AP3216_ADDRESS_W,buf,2,100);
	HAL_Delay(10);
	buf[1]=0x03; //开启环境光传感器
	HAL_I2C_Master_Transmit(&hi2c1,AP3216_ADDRESS_W,buf,2,100);
	buf[0]=0x20;
	buf[1]=0x09;
	HAL_I2C_Master_Transmit(&hi2c1,AP3216_ADDRESS_W,buf,2,100);
	buf[0]=0x21;
	buf[1]=0x12;
	HAL_I2C_Master_Transmit(&hi2c1,AP3216_ADDRESS_W,buf,2,100);
}

uint32_t Get_Data() {
    uint16_t value = 0;
    uint8_t buf[2] = {0};  // buf[0]存储0x0E数据,buf[1]存储0x0F数据
    uint8_t reg_addr;
    
    // 1. 读取PS低字节寄存器(0x0E)
    reg_addr = 0x0E;
    HAL_I2C_Master_Transmit(&hi2c1, AP3216_ADDRESS_W, &reg_addr, 1, 100);  // 发送寄存器地址
    HAL_I2C_Master_Receive(&hi2c1, AP3216_ADDRESS_R, &buf[0], 1, 100);  // 读数据(地址+读标志)
    
    // 2. 读取PS高字节寄存器(0x0F)
    reg_addr = 0x0F;
    HAL_I2C_Master_Transmit(&hi2c1, AP3216_ADDRESS_W, &reg_addr, 1, 100);
    HAL_I2C_Master_Receive(&hi2c1, AP3216_ADDRESS_R, &buf[1], 1, 100);
    
    // 3. 校验数据有效性(IR_OF位:0x0E的bit6或0x0F的bit6)
    if ((buf[0] & 0x40) != 0 || (buf[1] & 0x40) != 0) {
        u1_printf("Data Invalid\n");
        return 0;
    }
    
    // 4. 正确拼接10位数据:高字节(0x0F)的bit5~0(6位) + 低字节(0x0E)的bit3~0(4位)
    value = ((buf[1] & 0x3F) << 4) | (buf[0] & 0x0F);
    
    // 5. 物体状态判断(0x0E的bit7:1=靠近,0=远离)
    uint8_t obj_status = (buf[0] & 0x80) ? 1 : 0;
    if (obj_status!=object_detected_flag) 
		{	
			object_detected_flag=obj_status;
			if(obj_status)
			{
					// 物体靠近时的逻辑(如点亮LED、发送通知等)
					u5_printf("Object detected!\n");
				//HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,GPIO_PIN_SET);
			}
			else
			{
				u5_printf("Object away!\n");
				//HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,GPIO_PIN_RESET);
    }
	}
    return value;
}

uint32_t Get_ALS_Data()
{
    uint8_t buf[2];
    uint8_t reg_addr;
    
    // 读取ALS低字节(0x0C)
    reg_addr = 0x0C;
    HAL_I2C_Master_Transmit(&hi2c1, AP3216_ADDRESS_W, &reg_addr, 1, 100);
    HAL_I2C_Master_Receive(&hi2c1, AP3216_ADDRESS_R, &buf[0], 1, 100);
    
    // 读取ALS高字节(0x0D)
    reg_addr = 0x0D;
    HAL_I2C_Master_Transmit(&hi2c1, AP3216_ADDRESS_W, &reg_addr, 1, 100);
    HAL_I2C_Master_Receive(&hi2c1, AP3216_ADDRESS_R, &buf[1], 1, 100);
    
    // 组合16位ALS数据(高8位 + 低8位)
    return (buf[1] << 8) | buf[0];
}
    

max30102.h

cpp 复制代码
#ifndef __MAX30102_H
#define __MAX30102_H

#define MAX30102_ADDRESS 0x57
#define MAX30102_ADDRESS_W 0xAE
#define MAX30102_ADDRESS_R 0xAF
#include "stdint.h"
#include "i2c.h"
#include <stdbool.h>



void MAX30102_Init();

uint32_t checkmode();

uint8_t MAX30102_Get_Data_Count(uint8_t* data_count);

uint8_t MAX30102_Read_Data(uint32_t *ir_adc, uint32_t *red_adc);

void MAX30102_Data_Init();
bool MAX30102_Update_Data(uint32_t ir_value, uint32_t red_value);
void MAX30102_Get_Results(uint8_t *heart_rate, uint8_t *spo2);

#endif

max30102.c

cpp 复制代码
#include "max30102.h"


uint8_t r_p,w_p;

// 添加数据缓冲区和计算参数
// 改进数据结构体定义
typedef struct {
    uint32_t ir_buffer[100];    // 红外数据缓冲区
    uint32_t red_buffer[100];   // 红光数据缓冲区
    int32_t ir_dc_value;        // 红外直流分量
    int32_t red_dc_value;       // 红光直流分量
    int32_t ir_ac_value;        // 红外交流分量
    int32_t red_ac_value;       // 红光交流分量
    int8_t buffer_index;        // 缓冲区索引
    uint8_t heart_rate;         // 心率值
    uint8_t spo2;               // 血氧饱和度值
    bool calculating;           // 是否正在计算
    uint8_t valid_sample_count; // 有效样本计数
    uint8_t prev_peak_detected; // 上一次峰值检测状态
} MAX30102_Data_t;

MAX30102_Data_t max30102_data;

// 初始化数据结构
void MAX30102_Data_Init() {
    for(int i=0; i<100; i++) {
        max30102_data.ir_buffer[i] = 0;
        max30102_data.red_buffer[i] = 0;
    }
    max30102_data.ir_dc_value = 0;
    max30102_data.red_dc_value = 0;
    max30102_data.ir_ac_value = 0;
    max30102_data.red_ac_value = 0;
    max30102_data.buffer_index = 0;
    max30102_data.heart_rate = 0;
    max30102_data.spo2 = 0;
    max30102_data.calculating = false;
    max30102_data.valid_sample_count = 0;
    max30102_data.prev_peak_detected = 0;
}

void MAX30102_Init()
{
    uint8_t buf[2] = {0};
    uint8_t reg_addr;
        
        //软复位    
        buf[0]=0x09;
        buf[1]=0x40;
        HAL_I2C_Master_Transmit(&hi2c1,MAX30102_ADDRESS_W,buf,2,100);
        
        HAL_Delay(10);
        //配置为心率和血氧模式
        buf[1]=0x03;
        HAL_I2C_Master_Transmit(&hi2c1,MAX30102_ADDRESS_W,buf,2,100);
        //配置ADC范围和采样率
        buf[0]=0x0A;
        buf[1]=0x2A;
        HAL_I2C_Master_Transmit(&hi2c1,MAX30102_ADDRESS_W,buf,2,100);
        
        //配置LED电流
        //1.红色LED电流
        buf[0]=0x0C;
        buf[1]=0x1F;
        HAL_I2C_Master_Transmit(&hi2c1,MAX30102_ADDRESS_W,buf,2,100);
        //2.红外LED电流
        buf[0]=0x0D;
        buf[1]=0x1F;
        HAL_I2C_Master_Transmit(&hi2c1,MAX30102_ADDRESS_W,buf,2,100);
        //配置FIFO缓存
        buf[0]=0x08;
        buf[1]=0x4F;
        HAL_I2C_Master_Transmit(&hi2c1,MAX30102_ADDRESS_W,buf,2,100);
        
        //中断配置
        buf[0] = 0x02;
    buf[1] = 0x40;  // 只使能PPG_RDY_EN (新数据就绪)
    HAL_I2C_Master_Transmit(&hi2c1, MAX30102_ADDRESS_W, buf, 2, 100);
    
    // 温度传感器中断可以关闭(如果不使用温度)
    buf[0] = 0x03;
    buf[1] = 0x00;  // 禁用温度中断
    HAL_I2C_Master_Transmit(&hi2c1, MAX30102_ADDRESS_W, buf, 2, 100);
    
    HAL_Delay(50);
}

uint32_t checkmode()
{
    uint8_t spo2=0,register_t;
    //确认SpO2模式激活
    HAL_I2C_Mem_Read(&hi2c1, MAX30102_ADDRESS_R, 0x09, 
                I2C_MEMADD_SIZE_8BIT, &spo2, 1, 100);

    if((spo2 & 0x07)!=0x03)
    {
        return -1;
    }

    return 0;
    
    
}
uint8_t MAX30102_Get_Data_Count(uint8_t* data_count)
{
    //获取读写指针的值,计算数据可用量
    HAL_I2C_Mem_Read(&hi2c1, MAX30102_ADDRESS_R, 0x04, 
                             I2C_MEMADD_SIZE_8BIT, &w_p, 1, 100);
    HAL_I2C_Mem_Read(&hi2c1, MAX30102_ADDRESS_R, 0x06, 
                             I2C_MEMADD_SIZE_8BIT, &r_p, 1, 100);
    
    *data_count=(w_p-r_p)&0x1F;
    
    return 0;
}

// 移动平均滤波函数
uint32_t moving_average_filter(uint32_t new_value, uint32_t* buffer, int8_t* index, int window_size) {
    buffer[*index] = new_value;
    *index = (*index + 1) % window_size;
    
    uint64_t sum = 0;
    for(int i=0; i<window_size; i++) {
        sum += buffer[i];
    }
    return sum / window_size;
}

// 优化后的数据更新函数
bool MAX30102_Update_Data(uint32_t ir_value, uint32_t red_value) {
    static uint32_t ir_filter_buffer[10];
    static uint32_t red_filter_buffer[10];
    static int8_t ir_filter_index = 0;
    static int8_t red_filter_index = 0;
    
    // 对原始数据进行移动平均滤波
    uint32_t filtered_ir = moving_average_filter(ir_value, ir_filter_buffer, &ir_filter_index, 10);
    uint32_t filtered_red = moving_average_filter(red_value, red_filter_buffer, &red_filter_index, 10);
    
    // 存储滤波后的数据
    max30102_data.ir_buffer[max30102_data.buffer_index] = filtered_ir;
    max30102_data.red_buffer[max30102_data.buffer_index] = filtered_red;
    max30102_data.buffer_index = (max30102_data.buffer_index + 1) % 100;
    
    // 增加有效样本计数
    max30102_data.valid_sample_count++;
    
    // 每收集30个样本就尝试计算一次,而不是等缓冲区满
    if(max30102_data.valid_sample_count >= 30) {
        max30102_data.calculating = true;
        
        // 计算直流分量(平均值)
        uint64_t ir_sum = 0, red_sum = 0;
        int valid_samples = 0;
        for(int i=0; i<100; i++) {
            if(max30102_data.ir_buffer[i] > 0) {
                ir_sum += max30102_data.ir_buffer[i];
                red_sum += max30102_data.red_buffer[i];
                valid_samples++;
            }
        }
        
        if(valid_samples > 0) {
            max30102_data.ir_dc_value = ir_sum / valid_samples;
            max30102_data.red_dc_value = red_sum / valid_samples;
            
            // 寻找峰值计算交流分量
            int32_t ir_max = 0, ir_min = 0x7FFFFFFF;
            int32_t red_max = 0, red_min = 0x7FFFFFFF;
            for(int i=0; i<100; i++) {
                if(max30102_data.ir_buffer[i] > 0) {
                    if(max30102_data.ir_buffer[i] > ir_max) ir_max = max30102_data.ir_buffer[i];
                    if(max30102_data.ir_buffer[i] < ir_min) ir_min = max30102_data.ir_buffer[i];
                    if(max30102_data.red_buffer[i] > red_max) red_max = max30102_data.red_buffer[i];
                    if(max30102_data.red_buffer[i] < red_min) red_min = max30102_data.red_buffer[i];
                }
            }
            max30102_data.ir_ac_value = (ir_max - ir_min) / 2;
            max30102_data.red_ac_value = (red_max - red_min) / 2;
            
            // 改进的心率血氧算法
            if(max30102_data.ir_dc_value > 50 && max30102_data.red_dc_value > 50) {
                // 计算AC/DC比值
                float ir_ratio = (float)max30102_data.ir_ac_value / max30102_data.ir_dc_value;
                float red_ratio = (float)max30102_data.red_ac_value / max30102_data.red_dc_value;
                
                // 血氧饱和度计算(更精确的经验公式)
                float ratio = red_ratio / ir_ratio;
                if(ratio > 0.02 && ratio < 1.8) {
                    max30102_data.spo2 = 104 - 17 * ratio;
                    if(max30102_data.spo2 < 70) max30102_data.spo2 = 70;
                    if(max30102_data.spo2 > 100) max30102_data.spo2 = 100;
                }
                
                // 改进的峰值检测算法
                int peak_count = 0;
                // 动态阈值计算
                int threshold = max30102_data.ir_dc_value + max30102_data.ir_ac_value / 3; // 更灵活的阈值
                
                // 更严格的峰值检测
                for(int i=3; i<97; i++) {
                    if(max30102_data.ir_buffer[i] > threshold &&
                       max30102_data.ir_buffer[i] > max30102_data.ir_buffer[i-1] &&
                       max30102_data.ir_buffer[i] > max30102_data.ir_buffer[i+1] &&
                       max30102_data.ir_buffer[i] > max30102_data.ir_buffer[i-2] &&
                       max30102_data.ir_buffer[i] > max30102_data.ir_buffer[i+2] &&
                       max30102_data.ir_buffer[i] > max30102_data.ir_buffer[i-3] &&
                       max30102_data.ir_buffer[i] > max30102_data.ir_buffer[i+3]) {
                        
                        // 更大的峰值间距要求
                        if(i - max30102_data.prev_peak_detected > 15) {
                            peak_count++;
                            max30102_data.prev_peak_detected = i;
                        }
                    }
                }
                
                // 更合理的心率计算(根据实际采样时间调整)
                if(peak_count > 0) {
                    // 假设每30个样本大约需要2秒(15Hz采样率)
                    float sampling_time = (float)max30102_data.valid_sample_count / 15.0; // 秒
                    max30102_data.heart_rate = (uint8_t)((float)peak_count * 60.0 / sampling_time);
                    
                    if(max30102_data.heart_rate < 50) max30102_data.heart_rate = 50;
                    if(max30102_data.heart_rate > 160) max30102_data.heart_rate = 160;
                }
            }
        }
        
        // 重置有效样本计数
        max30102_data.valid_sample_count = 0;
        max30102_data.calculating = false;
        return true;    // 有新的计算结果
    }
    return false;       // 尚未计算完成
}

void MAX30102_Get_Results(uint8_t *heart_rate, uint8_t *spo2) {
    *heart_rate = max30102_data.heart_rate;
    *spo2 = max30102_data.spo2;
}

uint8_t MAX30102_Read_Data(uint32_t *ir_adc, uint32_t *red_adc)
{
    uint8_t data[6]={0};
    uint8_t data_count;
    uint8_t register_t;
    
    // 强制读取数据,不依赖数据计数检查
    // 直接读取FIFO数据
    HAL_I2C_Mem_Read(
        &hi2c1,          // I2C句柄
        MAX30102_ADDRESS_R,     // 从机地址
        0x07,    // 目标寄存器地址0x07
        I2C_MEMADD_SIZE_8BIT,  // 寄存器地址长度(8位)
        data,              // 接收缓冲区
        6,                     // 读取字节数量(1个SpO2样本=6字节)
        100                    // 超时时间(100ms)
    );
    
    // 组装为18位ADC值
    *ir_adc = ((uint32_t)data[0] << 16) | ((uint32_t)data[1] << 8) | data[2];
    *red_adc = ((uint32_t)data[3] << 16) | ((uint32_t)data[4] << 8) | data[5];
    
    // 更新读指针
    r_p = (r_p + 1) & 0x1F;  // 指针循环递增
    HAL_I2C_Mem_Write(&hi2c1, MAX30102_ADDRESS_W, 0x06, 
                     I2C_MEMADD_SIZE_8BIT, &r_p, 1, 100);
    
    return 0;  // 读取成功
}

sht20.h

cpp 复制代码
#ifndef __SHT20_H
#define __SHT20_H
 
#include "stdint.h"
#include "i2c.h"
 
//STH20从机7位地址
#define SHT20_ADDRESS 0x40
//写权限寻址信号
#define SHT20_ADDRESS_W	((SHT20_ADDRESS << 1)|0)
//读权限寻址信号
#define SHT20_ADDRESS_R	((SHT20_ADDRESS << 1)|1)
 
//温度数据寄存器地址
#define TEMP_CMD 0xE3
 
//湿度数据寄存器地址
#define HUM_CMD 0xE5  
 
//温湿度数据获取函数
uint32_t SHT20_Get_Data(uint8_t register_t);
 
//串口显示函数
void SHT20_Digital_to_Analog(uint16_t temp_digital,uint16_t hum_digital);
 
#endif

sht20.c

cpp 复制代码
#include "sht20.h"
#include "main.h"
#include "wifi.h"
char tem_hum_val[20];
/*
 *函数:SHT20_Get_Data
 *功能:获取SHT20空气温湿度传感器采集到的数据(数字量)
 *参数:空气温度数据寄存器的地址/空气湿度数据寄存器的地址
 *返回值:获取到的SHT20传感器的值
 */
uint32_t SHT20_Get_Data(uint8_t register_t)
{
	uint16_t data;
	uint8_t buf[2]={0x00, 0x00};
	//主机发送写权限寻址信号+8位寄存器地址
	HAL_I2C_Master_Transmit(&hi2c1, SHT20_ADDRESS_W, &register_t, 1, 5);
	
	//主机发送写权限寻址信号和8位寄存器地址后,开始读取数据
	HAL_I2C_Master_Receive(&hi2c1, SHT20_ADDRESS_R, buf, 2, 100);
	
	data=(buf[0]<<8 | buf[1]);
	
	return data;
}
 
/*
 * 函数:SHT20_Digiatl_To_Analog
 * 功能:将温湿度数字量转换为模拟量并串口显示
 * 参数:tem_digital:温度数字量    hum_digital:湿度数字量
 * 返回值:无
 */
void SHT20_Digital_to_Analog(uint16_t temp_digital,uint16_t hum_digital)
{
	float tem_analog, hum_analog;
	
	tem_analog = temp_digital * 175.72 /65536 - 46.85;
	hum_analog = hum_digital *125 / 65536 - 6;
	u1_printf("TEM=%.2f, HUM=%.2f\n",tem_analog,hum_analog);
	u5_printf("TEM=%.2f, HUM=%.2f\n",tem_analog,hum_analog);
	//sprintf(tem_hum_val,"TEM=%.2f, HUM=%.2f",tem_analog,hum_analog);
}

3.3QT桌面应用软件代码

mypro.pro

cpp 复制代码
QT       += core gui
QT       += multimedia  # 必须添加多媒体模块

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17
QT += network
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    interface.cpp \
    main.cpp \
    pack.cpp \
    widget.cpp

HEADERS += \
    interface.h \
    pack.h \
    widget.h

FORMS += \
    interface.ui \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

DISTFILES +=

RESOURCES += \
    qrc.qrc

interface.h

cpp 复制代码
#ifndef INTERFACE_H
#define INTERFACE_H

#include <QWidget>
#include <QTcpSocket>
#include <QCloseEvent>
#include <QMediaPlayer>  // 添加这一行
#include <QAudioOutput>  // Qt 6需要添加这个头文件

namespace Ui {
class Interface;
}

class Interface : public QWidget
{
    Q_OBJECT

public:
    explicit Interface(QWidget *parent = nullptr);
    ~Interface();
    void setClient(QTcpSocket* client);
    void closeEvent(QCloseEvent *event) override;
    void setLoginInterface(QWidget* w);
    void setLineEdit(QString str);
    void setLineEdit_bpm(QString str);
    void setlabel_detected(int flag);
    void setlabel_lux(QString str);


private slots:
    void on_pushButton_openlight_clicked();

    void on_pushButton_closelight_clicked();

    void on_pushButton_tem_hum_clicked();

    void on_pushButton_fanon_clicked();

    void on_pushButton_fanoff_clicked();

    void on_pushButton_bpm_clicked();

    void on_pushButton_green_clicked();


private:
    Ui::Interface *ui;
    QTcpSocket* client;
    QWidget* loginInterface;
    QMediaPlayer* player;       // 添加播放器成员变量
    QAudioOutput* audioOutput;  // Qt 6需要音频输出对象
};

#endif // INTERFACE_H

pack.h

cpp 复制代码
#ifndef PACK_H
#define PACK_H

#include <QTcpSocket>
#include <QObject>
#include <vector>
#include <string>
#include <QString>
#include <QStringList>

enum Type {
    TYPE_REGIST,
    TYPE_LOGIN,
    TYPE_ORDER,
    TYPE_TEM_HUM,  //温湿度数据
    TYPE_HEART,
    TYPE_ERROR,
    TYPE_DETECTED,
    TYPE_AWAY,
    TYPE_LUX,
    TYPE_NOMCU
};

// 协议包发给服务器之后,服务器要处理,如果处理成功,服务器发给客户端的协议包里面的Back,就是SUCCESS
enum Back{
    BACK_SUCCESS,
    BACK_ERR
};

class Pack
{
private:
    int pack_size = 12;// 记录整个协议包大小,方便服务器知道当前发过去的协议包有多大
    Type type;
    Back back;
    char buf[4096] = "";
    int used = 0;
    QString list;
public:
    Pack();
    void setType(Type type);
    void setBack(Back back);
    void setSize(int size);
    int size();
    Type getType()const;
    Back getBack()const;
    void append(const QString &val);
    void append(const char *p, int size);
    QStringList readAll()const;
    QByteArray readAll(int size);
    // 新增:读取数据到指定缓冲区
    void readAll(char* buf, int size) const; // 添加此行
    QByteArray packData() const;
    void clear(); // 添加方法声明
    Pack &operator<<(const QString &val);
    void operator>>(QTcpSocket *client);
};

#endif // PACK_H

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include "pack.h"
#include <QMessageBox>
#include "interface.h"
#include <QFile>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void handleLoginResponse(const Pack& pack);
    bool eventFilter(QObject *obj, QEvent *event);
    Interface* getInterface();

private slots:
    void on_pushButton_connect_clicked();

    void on_pushButton_login_clicked();

private:
    Ui::Widget *ui;
    QTcpSocket* client;
    QHostAddress ip;
    int port;

    Interface* face;

    Pack readed_pack;
    int readed_pack_size = 0;
    int unreaded_pack_size = 0;
    int readed_size = 0;
    int readed_size_size = 0;
    int unreaded_size_size = 0;

private slots:
    void onReadyRead();
    void on_pushButton_register_clicked();
};
#endif // WIDGET_H

interface.cpp

cpp 复制代码
#include "interface.h"
#include "ui_interface.h"
#include "pack.h"
#include <QFile>
#include <QMediaPlayer>  // 添加这一行
#include <QAudioOutput>  // Qt 6需要添加这个头文件
#include <QDebug>

Interface::Interface(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Interface)
{
    ui->setupUi(this);


    // 在这里添加窗口样式设置
    // setWindowFlags(Qt::FramelessWindowHint);
    // setAttribute(Qt::WA_TranslucentBackground);

    client = new QTcpSocket(this);


    // 初始化音频输出对象
    audioOutput = new QAudioOutput(this);

    // 初始化播放器并关联音频输出
    player = new QMediaPlayer(this);
    player->setAudioOutput(audioOutput);

    // 设置默认音量(0.0到1.0之间的值)
    audioOutput->setVolume(1.0);  // 相当于70%音量


    QFile qssFile(":/qss/styleinterface2.qss");  // QSS文件路径
    if (qssFile.open(QFile::ReadOnly | QFile::Text)) {
        QTextStream stream(&qssFile);
        this->setStyleSheet(stream.readAll());  // 设置给整个应用
        qssFile.close();
    }


    // 加载图标
    QIcon icon(":/qss/Smart_det.png"); // 如果图标在资源文件中,使用资源路径;如果是本地文件,使用绝对或相对路径
    // 设置窗口图标
    this->setWindowIcon(icon);

    // 设置窗口标题为"智能监测系统"
    this->setWindowTitle("智能监测系统");
}

Interface::~Interface()
{
    // 清理资源,按相反的创建顺序删除
    delete player;       // 删除播放器
    delete audioOutput;  // 删除音频输出

    delete ui;
}

void Interface::setClient(QTcpSocket *client)
{
    this->client = client;
}

void Interface::closeEvent(QCloseEvent *event)
{
    this->loginInterface->show();
}

void Interface::setLoginInterface(QWidget *w)
{
    loginInterface = w;
}

void Interface::setLineEdit(QString str)
{
    ui->lineEdit_tem_hum->setText(str);
}

void Interface::setLineEdit_bpm(QString str)
{
    ui->lineEdit_bpm->setText(str);
}


void Interface::setlabel_detected(int flag)
{
    // 保存原始样式以便恢复
    static QString originalStyle = ui->label_detected->styleSheet();
    static QString originalText = ui->label_detected->text();

    // 设置警告样式
    if(flag==1){
        ui->label_detected->setText("                                    ⚠️ 有障碍物靠近!⚠");
        ui->label_detected->setStyleSheet("color: white; background-color: red; font-weight: bold; font-size: 15px; border: 1px solid white; border-radius: 10px;text-align: center;");
        ui->label_detected->setVisible(true);
        //qDebug() << "11111111";  // 简单输出文本


        // Qt 6版本播放音乐的代码
        qDebug() << "准备播放音乐...";

        // 使用绝对路径
        //player->setSource(QUrl::fromLocalFile("C:/Users/Administrator/Desktop/25051MCU/stm32/008/stm32/QT/mypro/qss/music.mp3"));
        // 在这里添加这行代码!!!
        QFile file("./qss/music.mp3");
        if (file.exists()) {
            qDebug() << "音乐文件存在";
            player->setSource(QUrl::fromLocalFile("./qss/music.mp3"));
        } else {
            qDebug() << "警告:音乐文件不存在于路径 ./qss/music.mp3";
            // 可以选择使用绝对路径作为备选
            player->setSource(QUrl::fromLocalFile("C:/Users/Administrator/Desktop/25051MCU/stm32/008/stm32/QT/mypro/qss/music.mp3"));
        }

        // 播放音乐
        player->play();

        // 检查播放器状态
        connect(player, &QMediaPlayer::playbackStateChanged, [=](QMediaPlayer::PlaybackState newState) {
            if (newState == QMediaPlayer::PlayingState) {
                qDebug() << "音乐开始播放";
            } else if (newState == QMediaPlayer::StoppedState) {
                qDebug() << "音乐停止播放";
            }
        });

        // 错误处理
        connect(player, &QMediaPlayer::errorOccurred, [=](QMediaPlayer::Error error, const QString &errorString) {
            qDebug() << "播放器错误:" << errorString;
        });



    }
    // 恢复原状
    if(flag==2){
        ui->label_detected->setText(originalText);
        ui->label_detected->setStyleSheet(originalStyle);
        //qDebug() << "2222222";  // 简单输出文本


        // 停止播放音乐
        if (player && player->playbackState() == QMediaPlayer::PlayingState) {
            player->stop();
            qDebug() << "音乐已停止";
        }

    };
}

void Interface::on_pushButton_openlight_clicked()
{
    //先初始化颜色的
    // 修改pushButton_green按钮的样式
    QString styleSheet_g = "QPushButton#pushButton_green { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #246748, \
                                     stop: 1 #15C671); \
       border: none;\
    }";
    ui->pushButton_green->setStyleSheet(styleSheet_g);

    QString styleSheet_r = "QPushButton#pushButton_red { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #6C2424, \
                                     stop: 1 #C11717); \
        border: none;\
    }";
    ui->pushButton_red->setStyleSheet(styleSheet_r);

    Pack pack;
    pack.setType(TYPE_ORDER);
    QString order="open light";
    qDebug()<<"send success";
    pack<<order;
    pack>>client;

    // 修改pushButton_green按钮的样式
    QString styleSheet = "QPushButton#pushButton_green { \
        background-color: #90EE90; /* 浅绿色 */ \
        border: 1px solid white; \
    }";
    ui->pushButton_green->setStyleSheet(styleSheet);
}


void Interface::on_pushButton_closelight_clicked()
{
    //先初始化颜色的
    // 修改pushButton_green按钮的样式
    QString styleSheet_g = "QPushButton#pushButton_green { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #246748, \
                                     stop: 1 #15C671); \
        border: none;\
    }";
    ui->pushButton_green->setStyleSheet(styleSheet_g);

    QString styleSheet_r = "QPushButton#pushButton_red { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #6C2424, \
                                     stop: 1 #C11717); \
        border: none;\
    }";
    ui->pushButton_red->setStyleSheet(styleSheet_r);

    Pack pack;
    pack.setType(TYPE_ORDER);
    QString order="close light";
    qDebug()<<"send success";
    pack<<order;
    pack>>client;


    // 修改pushButton_green按钮的样式
    QString styleSheet = "QPushButton#pushButton_red { \
                         background-color: #ff3a3a; /* 浅红色 */ \
        border: 1px solid white; \
    }";
    ui->pushButton_red->setStyleSheet(styleSheet);
}


void Interface::on_pushButton_tem_hum_clicked()
{
    Pack pack;
    pack.setType(TYPE_ORDER);
    QString order="sht20";
    qDebug()<<"send success";
    pack<<order;
    pack>>client;
}


void Interface::on_pushButton_fanon_clicked()
{
    //先初始化颜色的
    // 修改pushButton_green2按钮的样式
    QString styleSheet_g = "QPushButton#pushButton_green2 { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #246748, \
                                     stop: 1 #15C671); \
        border: none;\
    }";
    ui->pushButton_green2->setStyleSheet(styleSheet_g);

    QString styleSheet_r = "QPushButton#pushButton_red2 { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #6C2424, \
                                     stop: 1 #C11717); \
        border: none;\
    }";
    ui->pushButton_red2->setStyleSheet(styleSheet_r);

    Pack pack;
    pack.setType(TYPE_ORDER);
    QString order="fanon";
    qDebug()<<"send success";
    pack<<order;
    pack>>client;


    // 修改pushButton_green按钮的样式
    QString styleSheet = "QPushButton#pushButton_green2 { \
        background-color: #90EE90; /* 浅绿色 */ \
        border: 1px solid white; \
    }";
    ui->pushButton_green2->setStyleSheet(styleSheet);
}


void Interface::on_pushButton_fanoff_clicked()
{

    //先初始化颜色的
    // 修改pushButton_green2按钮的样式
    QString styleSheet_g = "QPushButton#pushButton_green2 { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #246748, \
                                     stop: 1 #15C671); \
        border: none;\
    }";
    ui->pushButton_green2->setStyleSheet(styleSheet_g);

    QString styleSheet_r = "QPushButton#pushButton_red2 { \
        background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, \
                                     stop: 0 #6C2424, \
                                     stop: 1 #C11717); \
        border: none;\
    }";
    ui->pushButton_red2->setStyleSheet(styleSheet_r);

    Pack pack;
    pack.setType(TYPE_ORDER);
    QString order="fanoff";
    qDebug()<<"send success";
    pack<<order;
    pack>>client;

    // 修改pushButton_green按钮的样式
    QString styleSheet = "QPushButton#pushButton_red2 { \
        background-color: #ff3a3a; /* 浅红色 */ \
        border: 1px solid white; \
    }";
    ui->pushButton_red2->setStyleSheet(styleSheet);

}


void Interface::on_pushButton_bpm_clicked()
{
    Pack pack;
    pack.setType(TYPE_ORDER);
    QString order="bpm";
    qDebug()<<"send success";
    pack<<order;
    pack>>client;
    this->setLineEdit_bpm("正在测量,请耐性等待......");
}



void Interface::on_pushButton_green_clicked()
{

}

void Interface::setlabel_lux(QString str)
{
    ui->label_lux->setText(str);
}

main.cpp

cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

pack.cpp

cpp 复制代码
#include "pack.h"

Pack::Pack()
{

}

void Pack::setType(Type type)
{
    this->type = type;
}

void Pack::setBack(Back back)
{
    this->back = back;
}

void Pack::setSize(int size)
{
    pack_size = size;
}

int Pack::size()
{
    return pack_size;
}

Type Pack::getType()const
{
    return type;
}

Back Pack::getBack()const
{
    return back;
}

void Pack::append(const QString &val)
{
    const char* p = val.toStdString().data();
    short len = strlen(p); // strlen ( char*)
    *(short*)(buf+used) = len;
    used += 2;

    memcpy(buf+used,p,len);
    used += len;

    pack_size = 12 + used;
}

void Pack::append(const char *p, int size)
{
    memset(buf,0,4096);
    memcpy(buf,p,size);
    pack_size = 12 + size;
}

QStringList Pack::readAll()const
{
    QStringList list;
    int readed_size = 0;// 准备一个记录已读多少自己的数据,方便指针偏移,跳过已读部分
    while(1){
        short size = *(short*)(buf+readed_size);
        if(size == 0){break;}
        readed_size += 2;

        char temp[size + 1];
        memset(temp,0,size+1);
        memcpy(temp,buf+readed_size,size);
        readed_size += size;

        QString str = QString::fromStdString(temp);
        list << str;
    }
    return list;
}

QByteArray Pack::readAll(int size)
{
    //QByteArray arr(buf); // arr {char* p} 构造函数拷贝的数据,只会从头拷贝到第一个结束符,非常容易出现数据不全的情况
    //return arr;
    return QByteArray::fromRawData(buf,size);
}

void Pack::readAll(char *buf, int size) const
{
    memcpy(buf, this->buf, size); // 拷贝指定大小的数据到外部buf
}

Pack &Pack::operator<<(const QString &val)
{
    append(val);
    return *this;
}

void Pack::operator>>(QTcpSocket *client)
{
    client->write((char*)this,pack_size);
}

void Pack::clear()
{
    // 重置Pack对象的状态
    type = static_cast<Type>(0);
    back = static_cast<Back>(0);
    pack_size = 0;
    used = 0;
    memset(buf, 0, sizeof(buf));
}
QByteArray Pack::packData() const
{
    QByteArray data;
    // 先写入包大小(4字节)
    data.append((char*)&pack_size, 4);
    // 写入类型(假设Type为4字节)
    data.append((char*)&type, 4);
    // 写入返回状态(假设Back为4字节)
    data.append((char*)&back, 4);
    // 写入实际数据
    data.append(buf, used);
    return data;
}

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPixmap>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    client = new QTcpSocket(this);
    face = new Interface;
    face ->setLoginInterface(this);
    QObject::connect(client, &QTcpSocket::readyRead, this, &Widget::onReadyRead);
    face->setClient(client);
    //QFile qssFile(":/qss/style.qss");  // QSS文件路径
    QFile qssFile(":/qss/UserInterface.qss");  // 修改为
    if (qssFile.open(QFile::ReadOnly | QFile::Text)) {
        QTextStream stream(&qssFile);
        this->setStyleSheet(stream.readAll());  // 设置给整个应用
        qssFile.close();
    }

    // 加载图标
    QIcon icon(":/qss/Smart_det.png"); // 如果图标在资源文件中,使用资源路径;如果是本地文件,使用绝对或相对路径
    // 设置窗口图标
    this->setWindowIcon(icon);

    // 设置窗口标题为"智能监测系统"
    this->setWindowTitle("智能监测系统");


    ui->icon->setObjectName("imageLabel");

    // 设置图片(替换为你的图片路径)
    QPixmap pixmap(":/qss/SPM.png");

    // 关键设置:图片自动适应QLabel大小,保持比例
    ui->icon->setPixmap(pixmap.scaled(
        ui->icon->size(),  // 缩放至QLabel当前大小
        Qt::KeepAspectRatio, // 保持宽高比
        Qt::SmoothTransformation // 平滑缩放
        ));

    // 设置图片在QLabel中居中对齐
    ui->icon->setAlignment(Qt::AlignCenter);

    // 允许QLabel根据内容自动调整大小
    ui->icon->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    ui->icon->adjustSize(); // 调整QLabel大小以适应图片

    // 当QLabel大小变化时,重新缩放图片(通过事件过滤器实现)
    ui->icon->installEventFilter(this);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleLoginResponse(const Pack &pack)
{
    switch(pack.getType()) {
    case TYPE_REGIST:{
        if(pack.getBack() == BACK_SUCCESS){
            QMessageBox::information(this,"注册","注册成功");
        }else{
            QMessageBox::critical(this,"注册","该账号已存在");
        }
        break;
    }
    case TYPE_LOGIN:{
        if(pack.getBack() == BACK_SUCCESS){
            QMessageBox::information(this,"登录","登录成功");

            face->show();
            this->hide();
        }else{
            QMessageBox::critical(this,"登录","账号或密码错误");
        }
        break;
    }
    }
}

bool Widget::eventFilter(QObject *obj, QEvent *event)
{
    if (obj->objectName() == "imageLabel" && event->type() == QEvent::Resize) {
        QLabel *label = qobject_cast<QLabel*>(obj);
        if (label && !label->pixmap().isNull()) {
            // 重新缩放图片以适应新的QLabel大小
            label->setPixmap(label->pixmap().scaled(
                label->size(),
                Qt::KeepAspectRatio,
                Qt::SmoothTransformation
                ));
        }
    }
    return QWidget::eventFilter(obj, event);
}

Interface *Widget::getInterface()
{
    return face;
}

void Widget::on_pushButton_connect_clicked()
{
    ip.setAddress(ui->lineEdit_ip->text());
    port = ui->lineEdit_port->text().toInt();
    client->connectToHost(ip,port);
    if(client->waitForConnected(2000)){
        //如果2秒内连接上服务器
        QMessageBox::information(this,"连接","连接服务器成功");
    }else{
        QMessageBox::warning(this,"连接","连接失败,请检查网络");
    }
}

void Widget::on_pushButton_login_clicked()
{
    qDebug() << "登录按钮被点击";
    // 检查当前socket状态,仅在未连接时发起连接
    if (client->state() != QTcpSocket::ConnectedState) {
        ip.setAddress(ui->lineEdit_ip->text());
        port = ui->lineEdit_port->text().toInt();
        client->connectToHost(ip, port);

        // 等待连接结果(超时2秒)
        if (!client->waitForConnected(2000)) {
            QMessageBox::warning(this, "连接", "连接失败,请检查网络");
            return;
        }
    }
    // 连接成功后发送登录数据
    QString name = ui->lineEdit_name->text();
    QString pswd = ui->lineEdit_password->text();
    Pack pack;

    pack.setType(TYPE_LOGIN);
    pack << name << pswd;
    pack >> client;
}

void Widget::onReadyRead()
{
    while(client->bytesAvailable()) {
        int res = 0;
        int size = 0;
        Pack pack;



        if(unreaded_pack_size != 0){
            res = client->read((char*)&readed_pack + readed_pack_size,unreaded_pack_size);
            if(res != unreaded_pack_size){
                // 这里只要更新下已读大小和未读大小,保证下一次进入当前分支的时候,能够根据已读大小和未读大小,读取对应的数据以及存放到对应的地方
                readed_pack_size += res;
                unreaded_pack_size -= res;
                break;
            }

            pack = readed_pack;
            // 处理完分包记得 unreaded_pack_size = 0;
            unreaded_pack_size = 0;
        }else{

            if(unreaded_size_size != 0){
                client->read((char*)&readed_size + readed_size_size,unreaded_size_size);
                size = readed_size;
                unreaded_size_size = 0;
            }else{

                // else 部分为正常读取数据:先读4字节,再读剩余字节数,可能处理分包
                res = client->read((char*)&size,4);
                if(res == 0){break;}

                if(res != 4){
                    readed_size = size;
                    readed_size_size = res;
                    unreaded_size_size = 4 - res;
                    break;
                }
            }


            res = client->read((char*)&pack+4,size-4);
            if(res == 0){break;}

            pack.setSize(size);


            if(res != size-4){
                // 如果实际读取到的字节数 != 想要读取的字节数
                // 说明发生了分包
                readed_pack = pack;// 将已经读取的协议包缓存在每个客户端专属的缓存区 readed_pack 里面
                readed_pack_size = res + 4;
                unreaded_pack_size = size - readed_pack_size;
                break;
            }
        }
        switch(pack.getType()){
            case TYPE_REGIST:
            case TYPE_LOGIN:{
                handleLoginResponse(pack);
                break;
            }
            case TYPE_TEM_HUM:
            {
                QStringList val=pack.readAll();
                qDebug()<<val[0];
                this->getInterface()->setLineEdit(val[0]);
                break;
            }
            case TYPE_HEART:
            {
                QStringList val=pack.readAll();
                qDebug()<<val[0];
                this->getInterface()->setLineEdit_bpm(val[0]);
                break;
            }
            case TYPE_ERROR:
            {
                //程序待定
                break;

            }
            case TYPE_DETECTED:
            {
                this->getInterface()->setlabel_detected(1);
                break;
            }
            case TYPE_AWAY:
            {
                this->getInterface()->setlabel_detected(2);
                break;
            }
            case TYPE_LUX:
            {
                QStringList val=pack.readAll();
                //qDebug()<<val[0];
                this->getInterface()->setlabel_lux(val[0]);
                break;
            }
            case TYPE_NOMCU:
            {
                QMessageBox::warning(this,"发送指令失败","当前无被控端连接");
                break;
            }
        }

    }
}


void Widget::on_pushButton_register_clicked()
{
    // 检查当前socket状态,仅在未连接时发起连接
    if (client->state() != QTcpSocket::ConnectedState) {
        ip.setAddress(ui->lineEdit_ip->text());
        port = ui->lineEdit_port->text().toInt();
        client->connectToHost(ip, port);

        // 等待连接结果(超时2秒)
        if (!client->waitForConnected(2000)) {
            QMessageBox::warning(this, "连接", "连接失败,请检查网络");
            return;
        }
    }

    // 连接成功后发送注册数据
    QString name = ui->lineEdit_name->text();
    QString pswd = ui->lineEdit_password->text();
    Pack pack;

    pack.setType(TYPE_REGIST);
    pack << name << pswd;
    pack >> client;
}

UserInterface.css

cpp 复制代码
/* 注册按钮样式 - 应用到指定按钮 */
QPushButton#pushButton_register,
QPushButton#pushButton_login{
    background-color: #e68a00;  /* 橙色背景 */
    color: white;               /* 白色文字 */
    border-radius: 6px;         /* 圆角半径 */
    padding: 6px 12px;          /* 内边距 */
    font-size: 14px;            /* 字体大小 */
    border: none;               /* 无边框 */
    border: 4px solid #FFFFFF;
}

/* 按钮悬停效果 */
QPushButton#pushButton_register:hover,
QPushButton#pushButton_login:hover {
    background-color: #f39c12;  /* 稍亮的橙色 */
    border: 4px solid #FFFFFF;
}

/* 按钮按下效果 */
QPushButton#pushButton_register:pressed,
QPushButton#pushButton_login:pressed {
    background-color: #d35400;  /* 稍暗的橙色 */
    border: 4px solid #FFFFFF;
}


/* 账号、密码、IP和端口输入框圆角样式 */
QLineEdit#lineEdit_name, QLineEdit#lineEdit_password, QLineEdit#lineEdit_ip, QLineEdit#lineEdit_port {
    border: 2px solid #e68a00;  /* 设置边框颜色和宽度 */
    border-radius: 10px;        /* 设置圆角半径 */
    padding: 5px 10px;          /* 设置内边距 */
    background-color: white;    /* 设置背景色 */
    font-size: 15px;            /* 设置字体大小 */
}

/* 输入框获得焦点时的样式 */
QLineEdit#lineEdit_name:focus, QLineEdit#lineEdit_password:focus, QLineEdit#lineEdit_ip:focus, QLineEdit#lineEdit_port:focus {
    border-color: #e68a00;      /* 焦点时边框颜色变深 */
    outline: none;              /* 移除默认轮廓 */
}


QPushButton#pushButton_connect {
    border: none;
    background-color: #f0f0f0; /* 设置为浅灰色,与界面背景一致 */
    width: 32px;
    height: 32px;
    padding: 0;
    border-radius: 4px; /* 轻微圆角 */
    transition: background-color 0.2s; /* 平滑过渡效果 */
}

QPushButton#pushButton_connect:hover {
    background-color: #e0e0e0; /* 悬停时稍深灰色 */
}

QPushButton#pushButton_connect:pressed {
    background-color: #d0d0d0; /* 按下时更深灰色 */
    transform: translateY(1px); /* 轻微下沉效果 */
}


/* 为label_2添加圆角边框和黑色透明背景 */
QLabel#label_2 {
    border: 6px solid #e68a00;  /* 橙色边框 */
    border-radius: 8px;         /* 圆角半径,值越大圆角越明显 */
    padding: 5px;               /* 内边距,使内容不紧贴边框 */
    background-color: rgba(0, 0, 0, 0.5);  /* 黑色透明背景,透明度50% */
}

styleInterface.css

cpp 复制代码
/* 窗口主样式 - 添加圆角效果 */
QWidget#Interface {
    border-radius: 20px;
    background-color: #F2F2F2;
}


/* 游戏手柄风格小按钮基础样式 - 优化文字显示 */
QPushButton#pushButton_openlight,
QPushButton#pushButton_closelight,
QPushButton#pushButton_fanoff,
QPushButton#pushButton_fanon,
QPushButton#pushButton_tem_hum
{
    min-height: 20px;
    max-height: 20px;
    /* 减小内边距,让文字有更多空间 */
    padding: 4px 8px;
    /* 缩小字体以适应小按钮 */
    font-size: 14px;
    font-weight: bold; /* 加粗文字增强可读性 */
    color: white;
    background-color:#e68a00;

    border: 2px solid #FFFFFF;
    border-radius: 6px; /* 适当减小圆角,适配小按钮 */

    /* 平滑过渡动画 */
    transition: all 0.2s ease-in-out;
    /* 确保文字不换行且完整显示 */
    white-space: nowrap;
    min-width: 50px; /* 设置最小宽度,避免过度挤压 */

}


QPushButton#pushButton_bpm
{

    padding: 4px 8px;
    /* 缩小字体以适应小按钮 */
    font-size: 14px;
    font-weight: bold; /* 加粗文字增强可读性 */
    color: white;
    background-color:#e68a00;

    border: 2px solid #FFFFFF;
    border-radius: 6px; /* 适当减小圆角,适配小按钮 */

    /* 平滑过渡动画 */
    transition: all 0.2s ease-in-out;
    /* 确保文字不换行且完整显示 */
    white-space: nowrap;
}


QPushButton#pushButton_green,
QPushButton#pushButton_red,
QPushButton#pushButton_green2,
QPushButton#pushButton_red2
{
    border-radius: 15px;
    min-width: 30px;
    min-height: 30px;
    max-width: 30px;
    max-height: 30px;
    background-color: red;
    color: white;
    padding: 0px;
}

/* 绿灯按钮特定样式 */
QPushButton#pushButton_green,
QPushButton#pushButton_green2
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #246748,
                               stop: 1 #15C671);
}

/* 红灯按钮特定样式 */
QPushButton#pushButton_red,
QPushButton#pushButton_red2
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #6C2424,
                               stop: 1 #C11717);
}



/* 鼠标悬浮效果 */
QPushButton:hover {
    transform: scale(1.05);
    /* 增强亮度但保持可读性 */
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #5a5a5a,
                               stop: 1 #3a3a3a);
}

QPushButton#pushButton_openlight:hover,
QPushButton#pushButton_fanon:hover,
QPushButton#pushButton_tem_hum:hover,
QPushButton#pushButton_bpm:hover
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #246748,
                               stop: 1 #15C671);
}

QPushButton#pushButton_closelight:hover,
QPushButton#pushButton_fanoff:hover
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #ff6a6a,
                               stop: 1 #ff4a4a);
}

/* 按下效果 */
QPushButton:pressed {
    transform: scale(0.95);
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #3a3a3a,
                               stop: 1 #1a1a1a);
}

QPushButton#pushButton_openlight:pressed,
QPushButton#pushButton_fanon:pressed,
QPushButton#pushButton_tem_hum:pressed,
QPushButton#pushButton_bpm:pressed
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                stop: 0 #246748,
                                stop: 1 #15C671);
}

QPushButton#pushButton_closelight:pressed,
QPushButton#pushButton_fanoff:pressed
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #ff4a4a,
                               stop: 1 #ff2a2a);
}

/* 禁用状态 */
QPushButton:disabled {
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #7a7a7a,
                               stop: 1 #5a5a5a);
    color: #aaaaaa;
    border-color: #4a4a4a;
}


/* 账号、密码、IP和端口输入框圆角样式 */
QLineEdit#lineEdit_bpm,
QLineEdit#lineEdit_tem_hum,
QLineEdit#label_lux
{
    border: 2px solid #e68a00;  /* 设置边框颜色和宽度 */
    border-radius: 10px;        /* 设置圆角半径 */
    padding: 5px 10px;          /* 设置内边距 */
    background-color: white;    /* 设置背景色 */
    font-size: 11px;            /* 设置字体大小 */
    font-weight:bold;
}

QLineEdit#lineEdit_bpm
{
    border: 2px solid #e68a00;  /* 设置边框颜色和宽度 */
    border-radius: 10px;        /* 设置圆角半径 */
    padding: 5px 10px;          /* 设置内边距 */
    background-color: white;    /* 设置背景色 */
    font-size: 15px;            /* 设置字体大小 */
    font-weight:bold;
}

/* 输入框获得焦点时的样式 */
QLineEdit#lineEdit_bpm:focus,
QLineEdit#lineEdit_tem_hum:focus,
QLineEdit#label_lux:focus
{
    border-color: #e68a00;      /* 焦点时边框颜色变深 */
    outline: none;              /* 移除默认轮廓 */
}

QLineEdit#label_lux
{
    font-size: 15px;            /* 设置字体大小 */
    text-align: center;
    padding: 4px 0;  /* 上下内边距为4px,左右为0,辅助垂直居中 */
}


/* 账号、密码、IP和端口输入框圆角样式 */
QLabel#label_detected
{
    border: 2px solid #e68a00;  /* 设置边框颜色和宽度 */
    border-radius: 10px;        /* 设置圆角半径 */
    padding: 5px 10px;          /* 设置内边距 */
    background-color: white;    /* 设置背景色 */
    font-size: 15px;            /* 设置字体大小 */
    text-align: center;
    color:#6C2424;
    font-weight: bold; /* 粗体效果 */
}



QLabel#label_lux2
{
    min-height: 20px;
    max-height: 20px;
    /* 减小内边距,让文字有更多空间 */
    padding: 4px 8px;
    /* 缩小字体以适应小按钮 */
    font-size: 14px;
    font-weight: bold; /* 加粗文字增强可读性 */
    color: white;
    background-color:#e68a00;

    border: 2px solid #FFFFFF;
    border-radius: 6px; /* 适当减小圆角,适配小按钮 */

    /* 平滑过渡动画 */
    transition: all 0.2s ease-in-out;
    /* 确保文字不换行且完整显示 */
    white-space: nowrap;
    min-width: 50px; /* 设置最小宽度,避免过度挤压 */

}


QLabel#label_lux2:hover
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #246748,
                               stop: 1 #15C671);
}

QLabel#label_lux2:pressed
{
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                stop: 0 #246748,
                                stop: 1 #15C671);
}

style.css

cpp 复制代码
/* 1. 主窗口Widget:设置渐变背景,营造柔和视觉 */
QWidget#Widget {
    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                               stop: 0 #f5f7fa,
                               stop: 1 #e4e9f2);
}

/* 2. QPushButton 通用样式:基础外观 +  hover/pressed 交互 */
QPushButton {
    background-color: #409eff;   /* 主色调:蓝色 */
    color: white;                /* 文字色:白色 */
    border: none;                /* 取消默认边框 */
    border-radius: 4px;          /* 圆角:柔化边缘 */
    padding: 6px 12px;           /* 内边距:控制按钮大小 */
    font-size: 14px;             /* 字体大小 */
    transition: background-color 0.3s ease, transform 0.1s ease; /* 过渡动画:让交互更流畅 */
}

QPushButton:hover {
    background-color: #66b1ff;   /*  hover时:颜色变浅 */
}

QPushButton:pressed {
    background-color: #2d8cf0;   /*  pressed时:颜色变深 */
    transform: scale(0.98);      /*  pressed时:轻微缩小,模拟按压感 */
}

QPushButton:disabled {
    background-color: #c0c4cc;   /*  禁用时:浅灰色 */
    color: #909399;
}

/* 3. 特定按钮的个性化颜色(登录、注册、连接按钮区分视觉) */
QPushButton#pushButton_login {
    background-color: #67c23a; /* 登录按钮:成功绿色 */
}

QPushButton#pushButton_login:hover {
    background-color: #85ce61;
}

QPushButton#pushButton_login:pressed {
    background-color: #5daf34;
}

QPushButton#pushButton_register {
    background-color: #e6a23c; /* 注册按钮:警告黄色 */
}

QPushButton#pushButton_register:hover {
    background-color: #f0b861;
}

QPushButton#pushButton_register:pressed {
    background-color: #cf9236;
}

QPushButton#pushButton_connect {
    background-color: #f56c6c; /* 连接按钮:危险红色 */
}

QPushButton#pushButton_connect:hover {
    background-color: #f78989;
}

QPushButton#pushButton_connect:pressed {
    background-color: #e05656;
}

/* 4. QLineEdit 样式:正常/焦点状态区分 */
QLineEdit {
    border: 1px solid #dcdfe6;   /* 正常边框:浅灰色 */
    border-radius: 4px;          /* 圆角 */
    padding: 6px;                /* 内边距 */
    background-color: white;     /* 背景白 */
    transition: border-color 0.3s ease; /* 边框颜色过渡 */
}

QLineEdit:focus {
    border-color: #409eff;       /* 焦点时:蓝色边框 */
    outline: none;               /* 取消默认焦点外框 */
    box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); /* 焦点阴影:增强视觉焦点 */
}

/* 5. QLabel(图标组件):若需调整图标容器样式 */
QLabel#icon {
    min-width: 32px;   /* 图标最小宽高(根据实际图标调整) */
    min-height: 32px;
    /* 若图标为背景图,可添加:
    background-image: url(:/path/to/icon.png);
    background-repeat: no-repeat;
    background-position: center;
    */
}

/* 布局间隔(Spacer)无需特殊样式,默认透明 */
相关推荐
小刘爱玩单片机8 小时前
【嵌入式简单外设篇】-光敏模块
stm32·单片机·嵌入式硬件
迎風吹頭髮11 小时前
UNIX下C语言编程与实践62-UNIX UDP 编程:socket、bind、sendto、recvfrom 函数的使用
c语言·单片机·unix
DIY全栈开发12 小时前
《MCU职位》面试问题
单片机·嵌入式硬件·面试
清风66666615 小时前
基于单片机的智能点滴输液速度与液位控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计
电子工程师成长日记-C5117 小时前
基于51单片机的交通灯智能调节系统
单片机·嵌入式硬件·51单片机
点灯小铭17 小时前
基于单片机的智能水瓶温度控制系统
单片机·嵌入式硬件·毕业设计·课程设计
点灯小铭17 小时前
基于单片机的四点位水位控制与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计
是大强17 小时前
肖特基二极管作用及应用
单片机·嵌入式硬件
是大强18 小时前
stm32 vdd引脚和vss引脚连锡会短路
stm32·单片机·嵌入式硬件