智能配电箱监控系统开发笔记
一、项目概述
这是一个基于多线程邮箱通信机制的智能配电箱监控系统,实现了以下功能:
多线程通信:使用自定义邮箱系统进行线程间通信
数据库存储:使用SQLite实时存储传感器数据
报警功能:实时监测电压、电流、温度等参数,触发报警
图形显示:使用FrameBuffer在屏幕上显示监控界面
二、核心技术原理
2.1 线程邮箱通信机制
// 核心数据结构 typedef struct { char name_of_sender[32]; // 发送者名称 char data[256]; // 数据内容 } MAIL_DATA; typedef struct { // 邮箱系统相关数据结构 } MBS; // 核心API函数 MBS* create_mail_box_system(); // 创建邮箱系统 void register_to_mail_system(MBS* mbs, const char* name, void* (*func)(void*)); // 注册线程 int send_msg(MBS* mbs, const char* to_name, MAIL_DATA* data); // 发送消息 int recv_msg(MBS* mbs, MAIL_DATA* data); // 接收消息 void wait_all_end(MBS* mbs); // 等待所有线程结束 void destroy_mail_box_system(MBS* mbs); // 销毁邮箱系统2.2 工作流程
数据线程(data_th) → 生成传感器数据 ↓ (通过邮箱发送) 显示线程(show_th) → 在屏幕上显示数据 ↓ (通过邮箱发送) 数据库 → 保存历史数据 ↓ (检查阈值) 报警系统 → 触发报警记录
三、系统架构设计
3.1 线程设计
1. 显示线程 (show_th)
功能:接收传感器数据并显示在屏幕上
特点:
使用FrameBuffer直接写屏
支持中文字符显示
实时更新监控界面
2. 数据线程 (data_th)
功能:生成模拟传感器数据
特点:
模拟真实电压、电流、温度波动
支持随机时间间隔
自动触发报警检测
3.2 数据库设计
数据表结构
-- 传感器数据表 CREATE TABLE sensor_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, time DATETIME DEFAULT CURRENT_TIMESTAMP, voltage REAL, -- 电压值 (单位: V) current REAL, -- 电流值 (单位: A) temperature REAL -- 温度值 (单位: °C) ); -- 报警记录表 CREATE TABLE alarm_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, time DATETIME DEFAULT CURRENT_TIMESTAMP, alarm_type TEXT, -- 报警类型: '过压'/'欠压'/'过流'/'高温' value REAL, -- 实际测量值 threshold REAL -- 报警阈值 );
四、最终代码实现
#include "framebuffer.h" #include "head.h" #include "linkque.h" #include "list.h" #include "mailbox.h" #include "utf.h" #include <fcntl.h> #include <pthread.h> #include <signal.h> // 信号头文件 #include <sqlite3.h> // 数据库头文件 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> // ========== 全局变量 ========== MBS *g_mbs; // 邮箱系统句柄 int g_running = 1; // 程序运行标志 sqlite3 *g_db = NULL; // 数据库句柄 // ========== 信号处理 ========== void signal_handler(int sig) { printf("\n收到停止信号,正在退出...\n"); g_running = 0; } // ========== 数据库初始化 ========== void init_database() { int rc = sqlite3_open("sensor.db", &g_db); if (rc != SQLITE_OK) { printf("数据库打开失败\n"); return; } // 创建两个表:传感器数据表和报警记录表 char *sql = // 第一个表:传感器数据表 "CREATE TABLE IF NOT EXISTS sensor_data (" "id INTEGER PRIMARY KEY AUTOINCREMENT," // 第1列:自动编号(1,2,3...) "time DATETIME DEFAULT CURRENT_TIMESTAMP," // 第2列:自动记录时间 "voltage REAL," // 第3列:电压值(小数) "current REAL," // 第4列:电流值(小数) "temperature REAL" // 第5列:温度值(小数) ");" // 第二个表:报警记录表 "CREATE TABLE IF NOT EXISTS alarm_log (" "id INTEGER PRIMARY KEY AUTOINCREMENT," // 第1列:自动编号 "time DATETIME DEFAULT CURRENT_TIMESTAMP," // 第2列:报警时间 "alarm_type TEXT," // 第3列:报警类型(文字,如"高温") "value REAL," // 第4列:实际测量值(如实际温度45) "threshold REAL" // 第5列:报警阈值(如温度阈值40) ");"; char *err_msg = 0; rc = sqlite3_exec(g_db, sql, 0, 0, &err_msg); if (rc != SQLITE_OK) { printf("创建表失败: %s\n", err_msg); sqlite3_free(err_msg); } } // ========== 保存传感器数据到数据库 ========== void save_to_database(float voltage, float current, float temp) { char sql[256]; snprintf(sql, sizeof(sql), "INSERT INTO sensor_data (voltage, current, temperature) " "VALUES (%.2f, %.2f, %.2f);", voltage, current, temp); // 执行插入命令 sqlite3_exec(g_db, sql, 0, 0, NULL); } // ========== 保存报警记录到数据库 ========== void save_alarm_to_database(const char *alarm_type, float value, float threshold) { char sql[256]; snprintf(sql, sizeof(sql), "INSERT INTO alarm_log (alarm_type, value, threshold) " "VALUES ('%s', %.2f, %.2f);", alarm_type, value, threshold); sqlite3_exec(g_db, sql, 0, 0, NULL); } // ========== 显示线程函数 ========== void *show_th(void *arg) { MAIL_DATA data; // 创建一个邮件变量 // 初始化UTF-8字库 UTF8_INFO utf8_info; bzero(&utf8_info, sizeof(UTF8_INFO)); strcpy(utf8_info.path, ZIKUK_FILE_BIG); utf8_info.width = 32; utf8_info.height = 32; init_utf8(&utf8_info); // 初始化FrameBuffer int fb_fd = init_fb("/dev/fb0"); if (fb_fd == -1) { printf("FrameBuffer初始化失败\n"); uninit_utf8(&utf8_info); // 清理字库 return NULL; } while (g_running) { bzero(&data, sizeof(MAIL_DATA)); // 清空邮件内容 // 接收邮件,ret=0表示有邮件,ret=1表示没邮件 int ret = recv_msg(g_mbs, &data); if (1 == ret) { usleep(100000); // 100毫秒,不要用sleep(1) continue; } // 显示位置(每次重置) int x_pos = 50; int y_pos = 50; int line_height = 40; // 清屏为黑色 draw_clear(0x000000); // 显示标题 draw_utf8_str(&utf8_info, x_pos, y_pos, "智能配电箱监控系统", 0xFF0000, 0x000000); y_pos += line_height; // 显示传感器数据 draw_utf8_str(&utf8_info, x_pos, y_pos, data.data, 0xFFFFFF, 0x000000); y_pos += line_height; // 显示时间(绿色字) time_t now = time(NULL); struct tm *tm_info = localtime(&now); char time_str[32]; strftime(time_str, sizeof(time_str), "时间: %Y-%m-%d %H:%M:%S", tm_info); draw_utf8_str(&utf8_info, x_pos, y_pos, time_str, 0x00FF00, 0x0000FF); // 绘制背景图片 draw_bmp(0, 0, "./res/1.bmp", 800, 600); } // 清理显示资源 if (fb_fd != -1) { uninit_utf8(&utf8_info); uninit_fb(fb_fd); } printf("显示线程退出\n"); return NULL; } // ========== 数据生成线程函数 ========== void *data_th(void *arg) { // 设置随机种子 srand(time(NULL)); MAIL_DATA data; int data_count = 0; while (g_running) { // 生成传感器数据(模拟真实波动) float voltage = 220.0 + (rand() % 21 - 10); // 210-230V float current = 5.0 + (rand() % 31) * 0.1; // 5-8A float temperature = 25.0 + (rand() % 21); // 25-45°C // 保存到数据库 save_to_database(voltage, current, temperature); // 检查报警条件 int has_alarm = 0; // 电压报警检查 if (voltage > 235.0) { save_alarm_to_database("过压", voltage, 235.0); has_alarm = 1; printf("⚠️ 过压报警: %.1fV > 235.0V\n", voltage); } if (voltage < 200.0) { save_alarm_to_database("欠压", voltage, 200.0); has_alarm = 1; printf("⚠️ 欠压报警: %.1fV < 200.0V\n", voltage); } // 电流报警检查 if (current > 10.0) { save_alarm_to_database("过流", current, 10.0); has_alarm = 1; printf("⚠️ 过流报警: %.1fA > 10.0A\n", current); } // 温度报警检查 if (temperature > 60.0) { save_alarm_to_database("高温", temperature, 60.0); has_alarm = 1; printf("⚠️ 高温报警: %.1f°C > 60.0°C\n", temperature); } // 发送给显示线程 bzero(&data, sizeof(data)); // 清空邮件内容 sprintf(data.data, "电压:%.1fV 电流:%.1fA 温度:%.1f°C", voltage, current, temperature); // 设置发送者名称 strcpy(data.name_of_sender, "data"); // 发送给show线程 send_msg(g_mbs, "show", &data); // 随机等待0-2秒 sleep(rand() % 3); data_count++; if (data_count % 10 == 0) { printf("✅ 已生成 %d 条传感器数据\n", data_count); } } printf("数据线程退出\n"); return NULL; } // ========== 主函数 ========== int main(int argc, char **argv) { printf("=======================================\n"); printf(" 智能配电箱监控系统 v1.0\n"); printf("=======================================\n"); // 设置信号处理 signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); // 初始化数据库 printf("初始化数据库...\n"); init_database(); // 创建邮箱系统 printf("创建邮箱系统...\n"); g_mbs = create_mail_box_system(); if (!g_mbs) { printf("创建邮箱系统失败!\n"); return -1; } // 注册线程到邮箱系统 printf("注册显示线程...\n"); register_to_mail_system(g_mbs, "show", show_th); printf("注册数据线程...\n"); register_to_mail_system(g_mbs, "data", data_th); printf("\n系统启动完成!\n"); printf("运行中...按 Ctrl+C 停止\n"); printf("=======================================\n\n"); // 等待所有线程结束 wait_all_end(g_mbs); // 清理资源 printf("\n清理资源...\n"); if (g_db) { sqlite3_close(g_db); printf("数据库已关闭\n"); } destroy_mail_box_system(g_mbs); printf("邮箱系统已销毁\n"); printf("程序安全退出\n"); return 0; }五、编译和运行说明
5.1 编译命令
# 需要链接的库:pthread, sqlite3 gcc -o smart_powerbox main.c mailbox.c linkque.c list.c framebuffer.c utf.c \ -lpthread -lsqlite3 -lm
5.2 运行说明
# 1. 运行程序 ./smart_powerbox # 2. 查看数据库(另一个终端) sqlite3 sensor.db # 3. 在sqlite3中查看数据 sqlite> SELECT * FROM sensor_data; sqlite> SELECT * FROM alarm_log; sqlite> .exit
六、技术要点总结
6.1 多线程通信优势
解耦:各线程独立工作,通过邮箱通信
实时性:数据生成和显示分离,互不影响
扩展性:方便添加新功能模块
6.2 数据库设计要点
时间戳:自动记录数据时间
数据类型:使用REAL存储浮点数
报警记录:单独建表,便于查询分析
6.3 显示技术要点
FrameBuffer:直接操作显示缓冲区
UTF-8支持:显示中文字符
定时刷新:实时更新显示内容
6.4 报警逻辑
阈值设置:根据实际需求设定
实时检测:每次生成数据时检查
记录保存:所有报警都有历史记录
七、扩展建议
7.1 功能扩展
增加配置文件:支持参数配置
添加日志系统:记录程序运行状态
支持远程访问:添加Web界面
7.2 性能优化
数据库优化:使用事务批量插入
内存管理:合理控制内存使用
线程调度:优化线程优先级
八、注意事项
权限问题:FrameBuffer操作需要root权限
资源清理:确保所有资源正确释放
错误处理:添加更完善的错误处理机制
信号安全:确保信号处理函数可重入
这个系统展示了如何将多线程、数据库、图形显示等技术综合应用,构建一个完整的嵌入式监控系统。