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
#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
#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
#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" -> 0int 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);elses_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字节sizeres = 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
#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
#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
/* 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
/* 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");elseu1_printf("stop auto failed\n");//设置站点模式if(wifi_config("AT+CWMODE=1",100)==0)u1_printf("Station Mode successfully\n");elseu1_printf("Station Mode failed\n");//连接热点if(wifi_connect("jingjing","00000000")==0)u1_printf("001 AP connect successfully\n");elseu1_printf("001 AP connect failed\n");if(wifi_connecTCP("192.168.43.12",8889)==0)u1_printf("TCP successfully\n");elseu1_printf("TCP failed\n");//设置通透模式if(wifi_config("AT+CIPMODE=1",100)==0)u1_printf("CIPMODE=1 successfully\n");elseu1_printf("CIPMODE=1 failed\n");//设置数据发送if(wifi_config("AT+CIPSEND",100)==0)u1_printf("send successfully\n");elseu1_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
/* 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
/* 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 pinsPA13 (JTMS/SWDIO) ------> DEBUG_JTMS-SWDIOPA14 (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
/* 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
/* 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 ConfigurationPB6 ------> I2C1_SCLPB7 ------> 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 ConfigurationPB6 ------> I2C1_SCLPB7 ------> 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
/* 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
/* 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 ConfigurationPC12 ------> UART5_TXPD2 ------> 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 ConfigurationPA9 ------> USART1_TXPA10 ------> 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 ConfigurationPC12 ------> UART5_TXPD2 ------> 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 ConfigurationPA9 ------> USART1_TXPA10 ------> 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
/* 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 __cplusplusextern "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
/* 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
#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
#include "wifi.h"//AT指令的发送
//参数1: AT命令 参数2: 超时检测
uint32_t wifi_config(char *cmd,uint16_t time)
{//将AT命令发送到串口5u5_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
#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
#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, ®_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, ®_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, ®_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, ®_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
#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
#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, // 目标寄存器地址0x07I2C_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
#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
#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, ®ister_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
QT += core gui
QT += multimedia # 必须添加多媒体模块greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += 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.0SOURCES += \interface.cpp \main.cpp \pack.cpp \widget.cppHEADERS += \interface.h \pack.h \widget.hFORMS += \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 += targetDISTFILES +=RESOURCES += \qrc.qrc
interface.h
#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_OBJECTpublic: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
#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
#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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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
#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
#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
pack.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
#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
/* 注册按钮样式 - 应用到指定按钮 */
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
/* 窗口主样式 - 添加圆角效果 */
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
/* 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)无需特殊样式,默认透明 */