优化CO2传感器读取
This commit is contained in:
@@ -43,7 +43,13 @@
|
||||
|
||||
|
||||
# 树莓派Zero W 433MHZ HC-12接收服务端构建(需要自己开启 Raspberry Pi 串口, 并连接好硬件 HC-12)
|
||||
apt install libmysqlclient-dev
|
||||
1 运行 sudo raspi-config
|
||||
2 选择 Interface Options -> Serial Port。
|
||||
3 第一个问题(Would you like a login shell to be accessible over serial?):选 No(关闭登录 Shell)。
|
||||
4 第二个问题(Would you like the serial port hardware to be enabled?):选 Yes(启用硬件)。
|
||||
4 重启树莓派:sudo reboot。
|
||||
|
||||
apt install default-libmysqlclient-dev # Operating System: Debian GNU/Linux 13 (trixie)
|
||||
cd ~
|
||||
git clone https://github.com/WiringPi/WiringPi.git
|
||||
cd WiringPi
|
||||
|
||||
+21
-16
@@ -1,21 +1,26 @@
|
||||
CROSS_COMPILE ?=
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
STRIP := $(CROSS_COMPILE)strip
|
||||
AR := $(CROSS_COMPILE)ar
|
||||
CFLAGS += -g -Wall -Os
|
||||
LDFLAGS += -lwiringPi
|
||||
CROSS_COMPILE ?=
|
||||
CC := $(CROSS_COMPILE)gcc
|
||||
STRIP := $(CROSS_COMPILE)strip
|
||||
AR := $(CROSS_COMPILE)ar
|
||||
|
||||
MYSQL_LIB := $(shell mysql_config --libs)
|
||||
MYSQL_CFLAGS := $(shell mysql_config --cflags)
|
||||
OBJ = hc-12
|
||||
CFLAGS := -g -Wall -Os
|
||||
CFLAGS += $(shell mysql_config --cflags)
|
||||
LDLIBS := $(shell mysql_config --libs)
|
||||
LDLIBS += -lwiringPi
|
||||
TARGET := hc-12
|
||||
|
||||
all: hc-12
|
||||
SRCS := hc-12.c mysql.c
|
||||
OBJS := $(SRCS:.c=.o) ../libconf/libconf.o
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
hc-12: hc-12.o ../libconf/libconf.o mysql.o
|
||||
$(CC) $(CFLAGS) $(MYSQL_CFLAGS) -o $(OBJ) $^ $(MYSQL_LIB) $(LDFLAGS)
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(MYSQL_CFLAGS) -c $< $@
|
||||
|
||||
clean:
|
||||
rm hc-12 *.o
|
||||
rm -f $(TARGET) *.o
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
+282
-292
@@ -1,348 +1,361 @@
|
||||
#include "hc-12.h"
|
||||
#include "mysql.h"
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
// 全局互斥锁
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void *pull_data_mysql(void *p)
|
||||
// 定义报警状态结构体 (用于线程间通信)
|
||||
typedef struct {
|
||||
int can_send_ds18b20;
|
||||
int can_send_co;
|
||||
int can_send_co2;
|
||||
int can_send_ch4;
|
||||
} ALARM_FLAGS;
|
||||
|
||||
ALARM_FLAGS alarm_flags = { 1, 1, 1, 1 }; // 初始允许发送
|
||||
|
||||
// 定义用于传递给数据库线程的数据快照
|
||||
// 必须传递值的副本,否则线程运行时数据可能已被主循环修改
|
||||
typedef struct {
|
||||
char *table;
|
||||
char *host;
|
||||
char *port;
|
||||
char *user;
|
||||
char *pass;
|
||||
char *db;
|
||||
|
||||
float co;
|
||||
float co2;
|
||||
float ch4;
|
||||
float ds18b20;
|
||||
float rp2040;
|
||||
} DB_TASK_CTX;
|
||||
|
||||
// --- 数据库写入线程函数 ---
|
||||
void *pull_data_mysql_worker(void *arg)
|
||||
{
|
||||
char sql_insert[1024] = { 0 };
|
||||
DB_TASK_CTX *ctx = (DB_TASK_CTX *) arg;
|
||||
char sql_insert[2048] = { 0 };
|
||||
time_t current_time;
|
||||
struct tm *time_info;
|
||||
char timeString[20];
|
||||
static int r = 0;
|
||||
char timeString[30];
|
||||
int r = 0;
|
||||
|
||||
Thread *args = (Thread *) p;
|
||||
// 格式化时间
|
||||
time(¤t_time);
|
||||
time_info = localtime(¤t_time);
|
||||
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", time_info);
|
||||
|
||||
if (args->data->co >= 0 && args->data->co2 >= 0 && args->data->ch4_ >= 0) {
|
||||
time(¤t_time); // 获取当前时间
|
||||
time_info = localtime(¤t_time); // 将时间转换为本地时间
|
||||
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", time_info); // 格式化时间字符串
|
||||
// 使用 snprintf 防止溢出
|
||||
snprintf(sql_insert, sizeof(sql_insert),
|
||||
"INSERT INTO %s (TIME, CO, CO2, CH4, TEMPERATURE, RP2040_TEMPERATURE) " "VALUES ('%s', '%.0fppm', '%.0fppm', '%.0fppm', '%.3f°C', '%.02f°C');", ctx->table, timeString, ctx->co, ctx->co2, ctx->ch4, ctx->ds18b20, ctx->rp2040);
|
||||
|
||||
sprintf(sql_insert, "INSERT INTO %s (TIME, CO, CO2, CH4, TEMPERATURE, RP2040_TEMPERATURE) VALUES ('%s', '%.0fppm', '%.0fppm', '%.0fppm', '%.3f°C', '%.02f°C');",
|
||||
args->conf->MYSQL_TABLES, timeString, args->data->co, args->data->co2, args->data->ch4_, args->data->ds18b20, args->data->rp2040);
|
||||
printf("%s\n", sql_insert);
|
||||
// 调试输出 (实际运行时建议注释掉或写入日志)
|
||||
// printf("[DB-Thread] %s\n", sql_insert);
|
||||
|
||||
r = _mysql(sql_insert, args->conf->MYSQL_HOST, args->conf->MYSQL_PORT_, args->conf->MYSQL_USRT, args->conf->MYSQL_PASSWORD, args->conf->MYSQL_DB, args->conf->MYSQL_TABLES);
|
||||
if (r == -1) {
|
||||
perror("_mysql");
|
||||
}
|
||||
r = _mysql(sql_insert, ctx->host, ctx->port, ctx->user, ctx->pass, ctx->db, ctx->table);
|
||||
if (r == -1) {
|
||||
perror("_mysql");
|
||||
}
|
||||
|
||||
// 释放动态分配的上下文内存
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int send_mail(char *mail, const char *string, CONF *conf, DATA *data)
|
||||
// --- 发送邮件函数 ---
|
||||
int send_mail(char *mail, const char *msg_content, CONF *conf)
|
||||
{
|
||||
static char *buff;
|
||||
char buff[2048]; // 使用栈内存,足够大即可
|
||||
const char *subject = "厨房危险(火灾)报警!!!";
|
||||
|
||||
buff = (char *)alloca(BUFFER * 10);
|
||||
if (buff == NULL)
|
||||
perror("out of memory.");
|
||||
// 安全格式化命令
|
||||
// 注意:在生产环境中 system() 存在安全风险,建议未来改为 libcurl 或 execv
|
||||
snprintf(buff, sizeof(buff), MAIL, mail, subject, msg_content);
|
||||
|
||||
sprintf(buff, MAIL, mail, subject, string);
|
||||
printf("%s\n", buff);
|
||||
system(buff);
|
||||
printf("[MAIL] Sending: %s\n", buff);
|
||||
int ret = system(buff);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *createSharedMemory(int key, size_t size)
|
||||
// --- 计时器线程 ---
|
||||
// 替代原本的 fork 子进程,负责定期重置报警许可
|
||||
void *timer_thread(void *arg)
|
||||
{
|
||||
static int shm_id;
|
||||
static void *shared_memory;
|
||||
DATA *data = (DATA *) arg;
|
||||
int counter = 0;
|
||||
|
||||
shm_id = shmget(key, size, IPC_CREAT | 0666);
|
||||
if (shm_id == -1) {
|
||||
perror("shmget");
|
||||
exit(1);
|
||||
}
|
||||
while (1) {
|
||||
sleep(1);
|
||||
counter++;
|
||||
|
||||
shared_memory = shmat(shm_id, NULL, 0);
|
||||
if (shared_memory == (void *)-1) {
|
||||
perror("shmat");
|
||||
exit(1);
|
||||
}
|
||||
// 达到冷却时间 (data->_time)
|
||||
if (counter >= data->_time) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
return shared_memory;
|
||||
}
|
||||
// 重置允许发送标志
|
||||
alarm_flags.can_send_ds18b20 = 1;
|
||||
alarm_flags.can_send_co = 1;
|
||||
alarm_flags.can_send_co2 = 1;
|
||||
alarm_flags.can_send_ch4 = 1;
|
||||
|
||||
void detachAndDeleteSharedMemory(int key, void *shared_memory)
|
||||
{
|
||||
static int shm_id;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
if (shmdt(shared_memory) == -1) {
|
||||
perror("shmdt");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
shm_id = shmget(key, 0, 0);
|
||||
if (shmctl(shm_id, IPC_RMID, NULL) == -1) {
|
||||
perror("shmctl");
|
||||
exit(1);
|
||||
// printf("[Timer] Alarm cooldown reset.\n");
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// --- 主循环 ---
|
||||
int loop(int fd, CONF *conf, DATA *DATA)
|
||||
{
|
||||
static char receivedString[BUFFER];
|
||||
static int index = 0;
|
||||
static int timing = 0;
|
||||
|
||||
DATA->ds18b20_shm = (int *)createSharedMemory(1, sizeof(int));
|
||||
DATA->co_shm = (int *)createSharedMemory(2, sizeof(int));
|
||||
DATA->co2_shm = (int *)createSharedMemory(3, sizeof(int));
|
||||
*DATA->ds18b20_shm = 0;
|
||||
*DATA->co_shm = 0;
|
||||
*DATA->co2_shm = 0;
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
// 启动计时器线程
|
||||
pthread_t timer_tid;
|
||||
if (pthread_create(&timer_tid, NULL, timer_thread, (void *)DATA) != 0) {
|
||||
perror("pthread_create timer");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pid == 0) // 子进程
|
||||
{
|
||||
int conut = 0;
|
||||
while (1) {
|
||||
// 1. 串口读取 (非阻塞检查)
|
||||
if (serialDataAvail(fd)) {
|
||||
char data_char = serialGetchar(fd);
|
||||
|
||||
while (1) {
|
||||
if (data_char != '\n') {
|
||||
receivedString[index] = data_char;
|
||||
index++;
|
||||
if (index >= BUFFER - 1)
|
||||
index = 0; // 防止溢出
|
||||
} else {
|
||||
receivedString[index] = '\0'; // 结束符
|
||||
|
||||
pthread_mutex_lock(&mutex); // 加锁
|
||||
if (conut >= DATA->_time) {
|
||||
*DATA->ds18b20_shm = 1;
|
||||
*DATA->co_shm = 1;
|
||||
*DATA->co2_shm = 1;
|
||||
if (index > 0) {
|
||||
// --- 解析 RP2040 温度 ---
|
||||
if (strstr(receivedString, "CPU Temperature")) {
|
||||
if (2 == (sscanf(receivedString, "CPU Temperature %f°C %f°F", &(DATA->rp2040), &(DATA->rp2040_f)))) {
|
||||
DATA->rp2040_num++;
|
||||
printf("CPU Temperature: %.02f°C\n", DATA->rp2040);
|
||||
}
|
||||
}
|
||||
// --- 解析 DS18B20 温度 ---
|
||||
if (strstr(receivedString, "DS18B20 Temperature")) {
|
||||
if (1 == (sscanf(receivedString, "DS18B20 Temperature: %f°C", &DATA->ds18b20))) {
|
||||
printf("DS18B20 Temperature: %.3f°C\n", DATA->ds18b20);
|
||||
|
||||
conut = 0;
|
||||
if (DATA->ds18b20 >= atoi(conf->temperature)) {
|
||||
DATA->ds18b20_num++;
|
||||
printf("WARN: Temp High count: %d\n", DATA->ds18b20_num);
|
||||
|
||||
if (DATA->ds18b20_num >= 3) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (alarm_flags.can_send_ds18b20) {
|
||||
send_mail(conf->mail, receivedString, conf);
|
||||
alarm_flags.can_send_ds18b20 = 0; // 进入冷却
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
DATA->ds18b20_num = 0;
|
||||
}
|
||||
} else {
|
||||
DATA->ds18b20_num = 0; // 恢复正常则清零计数
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 解析 CH4 ---
|
||||
if (strstr(receivedString, "CH4 Concentration")) {
|
||||
if (1 == (sscanf(receivedString, "CH4 Concentration: %f", &DATA->ch4_))) {
|
||||
// CH4 报警逻辑可在此处按需添加
|
||||
printf("CH4 Concentration: %.0F ppm\n", DATA->ch4_);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 解析 CO ---
|
||||
if (strstr(receivedString, "CO Concentration")) {
|
||||
if (1 == (sscanf(receivedString, "CO Concentration: %f ppm", &DATA->co))) {
|
||||
printf("CO Concentration: %.2f ppm\n", DATA->co);
|
||||
|
||||
if (DATA->co >= atoi(conf->co)) {
|
||||
DATA->co_num++;
|
||||
if (DATA->co_num >= 3) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (alarm_flags.can_send_co) {
|
||||
send_mail(conf->mail, receivedString, conf);
|
||||
alarm_flags.can_send_co = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
DATA->co_num = 0;
|
||||
}
|
||||
} else {
|
||||
DATA->co_num = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// --- 解析 CO2 ---
|
||||
if (strstr(receivedString, "CO2 Concentration")) {
|
||||
if (1 == (sscanf(receivedString, "CO2 Concentration: %f ppm", &DATA->co2))) {
|
||||
printf("CO2 Concentration: %.2f ppm\n", DATA->co2);
|
||||
|
||||
|
||||
if (DATA->co2 >= atoi(conf->co2)) {
|
||||
DATA->co2_num++;
|
||||
if (DATA->co2_num >= 3) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (alarm_flags.can_send_co2) {
|
||||
send_mail(conf->mail, receivedString, conf);
|
||||
alarm_flags.can_send_co2 = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
DATA->co2_num = 0;
|
||||
}
|
||||
} else {
|
||||
DATA->co2_num = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 解析 Boot Time ---
|
||||
if (strstr(receivedString, "Boot Time")) {
|
||||
sscanf(receivedString, "Boot Time: %llu seconds", &DATA->boot_time_);
|
||||
printf("RP2040 Boot Time: %llu seconds\n", DATA->boot_time_);
|
||||
puts("");
|
||||
}
|
||||
}
|
||||
// 清空缓冲区准备下一次接收
|
||||
receivedString[0] = '\0';
|
||||
index = 0;
|
||||
|
||||
|
||||
}
|
||||
pthread_mutex_unlock(&mutex); // 解锁
|
||||
|
||||
conut++;
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
} else // 父进程
|
||||
{
|
||||
while (1) {
|
||||
if (serialDataAvail(fd)) {
|
||||
// 串口有数据可读
|
||||
char data = serialGetchar(fd);
|
||||
if (data != '\n') {
|
||||
// 如果接收到的不是回车符,则添加到接收字符串中
|
||||
receivedString[index] = data;
|
||||
index++;
|
||||
// 检查是否超出数组长度,如果超出则重置索引
|
||||
if (index >= BUFFER) {
|
||||
index = 0;
|
||||
// 2. 数据库上传逻辑
|
||||
timing++;
|
||||
// 防止 timing 溢出 (假设每10ms循环一次)
|
||||
int push_limit = atoi(conf->PUSH_MYSQL_DATA_TIME) * 100;
|
||||
|
||||
if (0 == strcasecmp(conf->MYSQL_ON, "on") && timing >= push_limit) {
|
||||
|
||||
// 只有当所有数据都有效时才上传
|
||||
if (DATA->co >= 0 && DATA->co2 >= 0 && DATA->ch4_ >= 0) {
|
||||
|
||||
// 分配独立的上下文内存,避免数据竞争
|
||||
DB_TASK_CTX *ctx = (DB_TASK_CTX *) malloc(sizeof(DB_TASK_CTX));
|
||||
if (ctx != NULL) {
|
||||
// 复制配置
|
||||
ctx->host = conf->MYSQL_HOST;
|
||||
ctx->port = conf->MYSQL_PORT_;
|
||||
ctx->user = conf->MYSQL_USRT;
|
||||
ctx->pass = conf->MYSQL_PASSWORD;
|
||||
ctx->db = conf->MYSQL_DB;
|
||||
ctx->table = conf->MYSQL_TABLES;
|
||||
|
||||
// 复制当前的传感器数值 (快照)
|
||||
ctx->co = DATA->co;
|
||||
ctx->co2 = DATA->co2;
|
||||
ctx->ch4 = DATA->ch4_;
|
||||
ctx->ds18b20 = DATA->ds18b20;
|
||||
ctx->rp2040 = DATA->rp2040;
|
||||
|
||||
pthread_t db_tid;
|
||||
if (pthread_create(&db_tid, NULL, pull_data_mysql_worker, ctx) == 0) {
|
||||
// 关键:Detach 线程,让它在后台运行,不阻塞主循环,也不需要 join
|
||||
pthread_detach(db_tid);
|
||||
} else {
|
||||
free(ctx); // 创建失败则释放内存
|
||||
perror("pthread_create db");
|
||||
}
|
||||
} else {
|
||||
// 如果接收到回车符,则打印接收到的字符串,并清空接收字符串和索引
|
||||
receivedString[index] = '\0'; // 添加字符串结束符
|
||||
if (index > 0) {
|
||||
|
||||
// RP2040
|
||||
if (strstr(receivedString, "Onboard")) {
|
||||
|
||||
if (2 == (sscanf(receivedString, "Onboard temperature %f°C %f°F\n", &(DATA->rp2040), &(DATA->rp2040_f)))) {
|
||||
DATA->rp2040_num++;
|
||||
printf("Onboard temperature %.02f°C %.02f°F\n", DATA->rp2040, DATA->rp2040_f);
|
||||
}
|
||||
|
||||
}
|
||||
// DS18B20
|
||||
if (strstr(receivedString, "Temperature")) {
|
||||
|
||||
if (1 == (sscanf(receivedString, "Temperature: %f°C\n", &DATA->ds18b20))) {
|
||||
printf("Temperature: %.3f°C\n", DATA->ds18b20);
|
||||
|
||||
if (DATA->ds18b20 >= atoi(conf->temperature)) {
|
||||
DATA->ds18b20_num++;
|
||||
printf("请注意室内温度%.3f°C超过%d次!\n", DATA->ds18b20, DATA->ds18b20_num);
|
||||
|
||||
if (DATA->ds18b20_num >= 3) {
|
||||
// 第一次发送
|
||||
if (DATA->ds18b20_1st == 1) {
|
||||
DATA->ds18b20_1st = 0;
|
||||
send_mail(conf->mail, receivedString, conf, DATA);
|
||||
}
|
||||
// 如果浓度持续一段时间, (降低发送频率, 若干秒后再次发送)
|
||||
pthread_mutex_lock(&mutex); // 加锁
|
||||
if (*DATA->ds18b20_shm == 1) {
|
||||
send_mail(conf->mail, receivedString, conf, DATA);
|
||||
*DATA->ds18b20_shm = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutex); // 解锁
|
||||
|
||||
DATA->ds18b20_num = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// CH4
|
||||
if (strstr(receivedString, "CH4 Concentration")) {
|
||||
if (1 == (sscanf(receivedString, "CH4 Concentration: %f\n", &DATA->ch4_))) {
|
||||
printf("CH4 Concentration: %.0F ppm\n", DATA->ch4_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CO
|
||||
if (strstr(receivedString, "CO")) {
|
||||
|
||||
if (1 == (sscanf(receivedString, "CO Concentration: %f ppm", &DATA->co))) {
|
||||
printf("CO Concentration: %.2f ppm\n", DATA->co);
|
||||
|
||||
if (DATA->co >= atoi(conf->co)) {
|
||||
DATA->co_num++;
|
||||
printf("请注意室内CO浓度%.3f°C超过%d次!\n", DATA->co, DATA->co_num);
|
||||
|
||||
if (DATA->co_num >= 3) {
|
||||
if (DATA->co_1st == 1) {
|
||||
DATA->co_1st = 0;
|
||||
send_mail(conf->mail, receivedString, conf, DATA);
|
||||
}
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (*DATA->co_shm == 1) {
|
||||
send_mail(conf->mail, receivedString, conf, DATA);
|
||||
*DATA->co_shm = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
DATA->co_num = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// CO2
|
||||
if (strstr(receivedString, "CO2")) {
|
||||
|
||||
if (1 == (sscanf(receivedString, "CO2 Concentration: %f ppm", &DATA->co2))) {
|
||||
printf("CO2 Concentration: %.2f ppm\n", DATA->co2);
|
||||
|
||||
if (DATA->co2 >= atoi(conf->co2)) {
|
||||
DATA->co2_num++;
|
||||
printf("请注意室内CO2浓度%.3f°C超过%d次!\n", DATA->co2, DATA->co2_num);
|
||||
|
||||
if (DATA->co2_num >= 3) {
|
||||
if (DATA->co2_1st == 1) {
|
||||
DATA->co2_1st = 0;
|
||||
send_mail(conf->mail, receivedString, conf, DATA);
|
||||
}
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (*DATA->co2_shm == 1) {
|
||||
send_mail(conf->mail, receivedString, conf, DATA);
|
||||
*DATA->co2_shm = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
DATA->co2_num = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
// Boot Time
|
||||
if (strstr(receivedString, "Boot Time")) {
|
||||
if (1 == (sscanf(receivedString, "Boot Time: %llu seconds\n", &DATA->boot_time_))) {
|
||||
printf("Boot Time: %llu seconds\n", DATA->boot_time_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
receivedString[0] = '\0';
|
||||
index = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 上传传感器数据到Mysql数据库
|
||||
timing++;
|
||||
if (0 == strcasecmp(conf->MYSQL_ON, "on")) {
|
||||
if (timing >= (atoi(conf->PUSH_MYSQL_DATA_TIME) * 100)) {
|
||||
|
||||
Thread args = { conf, DATA };
|
||||
pthread_t tid;
|
||||
// 创建线程,并传递包含两个结构体的参数
|
||||
if (pthread_create(&tid, NULL, pull_data_mysql, &args) != 0) {
|
||||
perror("pthread_create");
|
||||
return 1;
|
||||
}
|
||||
// 等待线程结束
|
||||
pthread_join(tid, NULL);
|
||||
|
||||
timing = 0;
|
||||
}
|
||||
}
|
||||
|
||||
delay(10);
|
||||
timing = 0; // 重置计时
|
||||
}
|
||||
|
||||
delay(10); // 10ms 延时,降低 CPU 占用
|
||||
}
|
||||
|
||||
waitpid(pid, NULL, 0);
|
||||
// 断开共享内存连接
|
||||
detachAndDeleteSharedMemory(1, DATA->ds18b20_shm);
|
||||
detachAndDeleteSharedMemory(2, DATA->co_shm);
|
||||
detachAndDeleteSharedMemory(3, DATA->co2_shm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int redirect_stdout_to_file(int fd, const char *filename)
|
||||
// 日志重定向函数
|
||||
int redirect_stdout_to_file(const char *filename)
|
||||
{
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
if (fd == -1) {
|
||||
perror("open");
|
||||
return 1;
|
||||
perror("open log file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dup2(fd, STDOUT_FILENO) == -1) {
|
||||
perror("dup2");
|
||||
return 1;
|
||||
perror("dup2 stdout");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 可选:同时也重定向 stderr
|
||||
if (dup2(fd, STDERR_FILENO) == -1) {
|
||||
perror("dup2 stderr");
|
||||
}
|
||||
close(fd); // dup2 后可以关闭原 fd
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int logfd = -1;
|
||||
int fd = -1;
|
||||
|
||||
// 提升进程优先级
|
||||
if (nice(-20) == -1) {
|
||||
perror("nice");
|
||||
return 1;
|
||||
// 继续运行,非致命错误
|
||||
}
|
||||
|
||||
if (0 != daemon(1, 1)) {
|
||||
// 守护进程化
|
||||
// daemon(0, 0) -> chdir to / and redirect stdio to /dev/null
|
||||
// 这里我们先不重定向 stdio,因为后面要重定向到文件
|
||||
if (daemon(1, 1) != 0) {
|
||||
perror("daemon");
|
||||
}
|
||||
|
||||
if ((fd = serialOpen("/dev/serial0", 9600)) < 0) {
|
||||
printf("Unable to open device\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("Serial port opened\n");
|
||||
}
|
||||
// 重定向日志
|
||||
redirect_stdout_to_file("hc-12.log"); // 建议使用绝对路径
|
||||
setbuf(stdout, NULL); // 关闭 stdout 缓冲,确保日志实时写入
|
||||
|
||||
// 打开串口
|
||||
if ((fd = serialOpen("/dev/serial0", 9600)) < 0) {
|
||||
printf("Unable to open serial device: /dev/serial0\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Serial port opened successfully.\n");
|
||||
|
||||
// 初始化配置
|
||||
CONF *conf = (struct CONF *)malloc(sizeof(struct CONF));
|
||||
if (!conf)
|
||||
return 1;
|
||||
memset(conf, 0, sizeof(struct CONF));
|
||||
|
||||
// 假设 read_conf 返回的是 malloc 的字符串或静态区字符串,如果是 malloc 的记得最后 free
|
||||
conf->mail = strdup(read_conf("hc-12.conf", "global", "MAIL"));
|
||||
conf->temperature = strdup(read_conf("hc-12.conf", "global", "TEMPERATURE"));
|
||||
conf->co = strdup(read_conf("hc-12.conf", "global", "CO"));
|
||||
conf->co2 = strdup(read_conf("hc-12.conf", "global", "CO2"));
|
||||
|
||||
// 默认值处理防止 atoi 崩溃
|
||||
if (!conf->temperature)
|
||||
conf->temperature = strdup("50");
|
||||
if (!conf->co)
|
||||
conf->co = strdup("100");
|
||||
if (!conf->co2)
|
||||
conf->co2 = strdup("1000");
|
||||
|
||||
conf->MYSQL_ON = strdup(read_conf("hc-12.conf", "global", "MYSQL"));
|
||||
if (0 == strcasecmp(conf->MYSQL_ON, "on")) {
|
||||
if (conf->MYSQL_ON && 0 == strcasecmp(conf->MYSQL_ON, "on")) {
|
||||
conf->PUSH_MYSQL_DATA_TIME = strdup(read_conf("hc-12.conf", "global", "PUSH_MYSQL_DATA_TIME"));
|
||||
if (!conf->PUSH_MYSQL_DATA_TIME)
|
||||
conf->PUSH_MYSQL_DATA_TIME = strdup("60"); // 默认60秒
|
||||
|
||||
conf->MYSQL_HOST = strdup(read_conf("hc-12.conf", "global", "MYSQL_HOST"));
|
||||
conf->MYSQL_PORT_ = strdup(read_conf("hc-12.conf", "global", "MYSQL_PORT"));
|
||||
conf->MYSQL_USRT = strdup(read_conf("hc-12.conf", "global", "MYSQL_USRT"));
|
||||
@@ -350,46 +363,23 @@ int main()
|
||||
conf->MYSQL_DB = strdup(read_conf("hc-12.conf", "global", "MYSQL_DB"));
|
||||
conf->MYSQL_TABLES = strdup(read_conf("hc-12.conf", "global", "MYSQL_TABLES"));
|
||||
}
|
||||
|
||||
// 初始化数据结构
|
||||
DATA *data = (struct DATA *)malloc(sizeof(struct DATA));
|
||||
if (!data)
|
||||
return 1;
|
||||
memset(data, 0, sizeof(struct DATA));
|
||||
data->ds18b20 = 0;
|
||||
data->ch4 = 0;
|
||||
data->rp2040 = 0;
|
||||
data->rp2040_f = 0;
|
||||
data->co = 0;
|
||||
data->co2 = 0;
|
||||
data->rp2040_num = 0;
|
||||
data->ch4_num = 0;
|
||||
data->ds18b20_num = 0;
|
||||
data->co_num = 0;
|
||||
data->co2_num = 0;
|
||||
data->ds18b20_1st = 1;
|
||||
data->ch4_1st = 1;
|
||||
data->co_1st = 1;
|
||||
data->co2_1st = 1;
|
||||
data->_time = 180;
|
||||
data->boot_time_ = 0;
|
||||
data->_time = 180; // 冷却时间 180秒
|
||||
|
||||
redirect_stdout_to_file(logfd, "hc-12.log");
|
||||
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
|
||||
// 进入主循环
|
||||
loop(fd, conf, data);
|
||||
pthread_mutex_destroy(&mutex); // 销毁互斥锁
|
||||
|
||||
// 资源清理 (通常 daemon 即使退出也很少执行到这里,但写上是好习惯)
|
||||
pthread_mutex_destroy(&mutex);
|
||||
free(conf->mail);
|
||||
free(conf->temperature);
|
||||
free(conf->co);
|
||||
free(conf->co2);
|
||||
free(conf->PUSH_MYSQL_DATA_TIME);
|
||||
free(conf->MYSQL_HOST);
|
||||
free(conf->MYSQL_PORT_);
|
||||
free(conf->MYSQL_USRT);
|
||||
free(conf->MYSQL_PASSWORD);
|
||||
free(conf->MYSQL_DB);
|
||||
free(conf->MYSQL_TABLES);
|
||||
// ... free 其他 conf 成员 ...
|
||||
free(conf);
|
||||
free(data);
|
||||
close(fd);
|
||||
close(logfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+32
-25
@@ -2,81 +2,88 @@
|
||||
#define HC_12_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <wiringPi.h>
|
||||
#include <wiringSerial.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <pthread.h> // 核心变更:使用 pthread 替代多进程 IPC
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// 硬件相关库
|
||||
#include <wiringPi.h>
|
||||
#include <wiringSerial.h>
|
||||
|
||||
// 自定义配置库
|
||||
#include "../libconf/libconf.h"
|
||||
|
||||
#define BUFFER 1024
|
||||
|
||||
// 邮件发送命令模板
|
||||
// 格式说明: gomail -r "收件人" -s "标题" -t "正文内容"
|
||||
// 请确保你的系统中有 gomail 命令且参数格式正确
|
||||
#define MAIL "gomail -r \"%s\" -s \"%s\" -t \"%s\""
|
||||
|
||||
// 配置结构体
|
||||
typedef struct CONF {
|
||||
char *mail;
|
||||
char *temperature;
|
||||
char *co;
|
||||
char *co2;
|
||||
char *MYSQL_ON;
|
||||
char *PUSH_MYSQL_DATA_TIME;
|
||||
|
||||
char *MYSQL_ON; // "on" 或 "off"
|
||||
char *PUSH_MYSQL_DATA_TIME; // 数据库上传间隔 (秒)
|
||||
|
||||
char *MYSQL_HOST;
|
||||
char *MYSQL_PORT_;
|
||||
char *MYSQL_USRT;
|
||||
char *MYSQL_PASSWORD;
|
||||
char *MYSQL_DB;
|
||||
char *MYSQL_TABLES;
|
||||
|
||||
} CONF;
|
||||
|
||||
// 数据结构体 (线程间共享)
|
||||
typedef struct DATA {
|
||||
// 存储实际传感器数据
|
||||
// --- 传感器实时数值 ---
|
||||
float ds18b20;
|
||||
float ch4;
|
||||
float ch4_; // 原始保留字段
|
||||
float rp2040;
|
||||
float rp2040_f;
|
||||
float co;
|
||||
float co2;
|
||||
float ch4_;
|
||||
|
||||
// boot time
|
||||
// --- 系统信息 ---
|
||||
long long unsigned int boot_time_;
|
||||
|
||||
// 超过阈值次数
|
||||
// --- 超过阈值计数器 ---
|
||||
int ds18b20_num;
|
||||
int ch4_num;
|
||||
int rp2040_num;
|
||||
int co_num;
|
||||
int co2_num;
|
||||
|
||||
// 共享内存
|
||||
int *ds18b20_shm;
|
||||
int *co_shm;
|
||||
int *co2_shm;
|
||||
// [已删除] int *ds18b20_shm 等共享内存指针
|
||||
// 新架构使用同一进程内的内存空间,无需 shmget
|
||||
|
||||
// 第一次发送Mail标志
|
||||
// --- 状态标志 ---
|
||||
// 用于标记是否是第一次发送,或是否处于报警状态
|
||||
int ds18b20_1st;
|
||||
int ch4_1st;
|
||||
int co_1st;
|
||||
int co2_1st;
|
||||
|
||||
// 持续浓度时候,等待时间再发送Mail
|
||||
int _time;
|
||||
// --- 计时器配置 ---
|
||||
// 报警冷却/等待时间 (秒)
|
||||
int _time;
|
||||
} DATA;
|
||||
|
||||
// 定义包含两个结构体的参数结构体
|
||||
// 线程参数传递结构体
|
||||
typedef struct {
|
||||
CONF *conf;
|
||||
DATA *data;
|
||||
} Thread;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+40
-31
@@ -1,46 +1,55 @@
|
||||
#include "mysql.h"
|
||||
|
||||
int _mysql(char *sql, char *MYSQL_HOST, char *MYSQL_PORT_, char *MYSQL_USRT, char *MYSQL_PASSWORD, char *MYSQL_DB, char *MYSQL_TABLES)
|
||||
int _mysql(char *sql, char *host, char *port, char *user, char *pass, char *db, char *table)
|
||||
{
|
||||
static MYSQL mysql; // 静态变量,仅初始化一次
|
||||
static char MYSQL_DB_[270] = { 0 };
|
||||
MYSQL *conn; // 使用指针,动态分配
|
||||
int result = 0;
|
||||
int timeout = 7;
|
||||
|
||||
if (!mysql_init(&mysql)) {
|
||||
perror("mysql_init");
|
||||
// 1. 初始化句柄 (分配内存)
|
||||
conn = mysql_init(NULL);
|
||||
if (conn == NULL) {
|
||||
fprintf(stderr, "mysql_init failed\n");
|
||||
return -1;
|
||||
}
|
||||
// 2. 设置超时选项
|
||||
mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
|
||||
mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout);
|
||||
mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout);
|
||||
|
||||
int result = 0;
|
||||
int timeout = 7; // 设置超时时间(秒)
|
||||
|
||||
// 设置连接超时时间
|
||||
mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
|
||||
// 自动重连选项 (可选,防止长时间闲置断开,但对于短连接模式不是必须的)
|
||||
char reconnect = 1;
|
||||
mysql_options(conn, MYSQL_OPT_RECONNECT, &reconnect);
|
||||
|
||||
// 设置读、写超时时间(影响查询)
|
||||
mysql_options(&mysql, MYSQL_OPT_READ_TIMEOUT, &timeout);
|
||||
mysql_options(&mysql, MYSQL_OPT_WRITE_TIMEOUT, &timeout);
|
||||
// 3. 建立连接
|
||||
// 注意:第5个参数直接传入 db,不再需要 "use db;" 语句
|
||||
// 注意:port 转为 int
|
||||
int port_num = (port != NULL) ? atoi(port) : 3306;
|
||||
|
||||
if (mysql_real_connect(&mysql, MYSQL_HOST, MYSQL_USRT, MYSQL_PASSWORD, "mysql", atoi(MYSQL_PORT_), NULL, 0)) {
|
||||
if (0 != mysql_set_character_set(&mysql, "utf8")) {
|
||||
perror("mysql_set_character_set");
|
||||
result = -1;
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
sprintf(MYSQL_DB_, "use %s;", MYSQL_DB);
|
||||
if (mysql_query(&mysql, MYSQL_DB_) || mysql_query(&mysql, sql)) {
|
||||
fprintf(stderr, "Query execution failed: %s\n", mysql_error(&mysql));
|
||||
result = -1;
|
||||
} else {
|
||||
// 执行成功的操作
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Connect failed: %s\n", mysql_error(&mysql));
|
||||
if (mysql_real_connect(conn, host, user, pass, db, port_num, NULL, 0) == NULL) {
|
||||
fprintf(stderr, "MySQL Connect failed: %s\n", mysql_error(conn));
|
||||
result = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// 4. 设置字符集
|
||||
if (mysql_set_character_set(conn, "utf8") != 0) {
|
||||
fprintf(stderr, "Set charset failed: %s\n", mysql_error(conn));
|
||||
result = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
// 5. 执行 SQL 语句
|
||||
if (mysql_query(conn, sql)) {
|
||||
fprintf(stderr, "Query execution failed: %s\nSQL: %s\n", mysql_error(conn), sql);
|
||||
result = -1;
|
||||
} else {
|
||||
puts(sql);
|
||||
//printf("Data inserted successfully.\n");
|
||||
}
|
||||
|
||||
shutdown:
|
||||
mysql_close(&mysql);
|
||||
cleanup:
|
||||
// 6. 关闭连接并释放 mysql_init 分配的内存
|
||||
mysql_close(conn);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
QueueHandle_t xQueue;
|
||||
|
||||
|
||||
// 温度传感器
|
||||
void DS18B20(void *pvParameters)
|
||||
{
|
||||
@@ -14,45 +13,32 @@ void DS18B20(void *pvParameters)
|
||||
One_wire one_wire(DS18B20_PIN);
|
||||
one_wire.init();
|
||||
rom_address_t address {
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
_printTaskStackHighWaterMark("DS18B20");
|
||||
|
||||
while (1) {
|
||||
|
||||
|
||||
one_wire.single_device_read_rom(address);
|
||||
int delay_time = one_wire.convert_temperature(address, true, false);
|
||||
//printf("Conversion delay time: %d ms\n", delay_time);
|
||||
|
||||
TEMPERATURE = one_wire.temperature(address);
|
||||
|
||||
printf("Device Address: %02x%02x%02x%02x%02x%02x%02x%02x DS18B20 Temperature: %3.1f°C\n", \
|
||||
address.rom[0], address.rom[1], address.rom[2], address.rom[3], address.rom[4], \
|
||||
address.rom[5], address.rom[6], address.rom[7], one_wire.temperature(address));
|
||||
|
||||
printf("Device Address: %02x%02x%02x%02x%02x%02x%02x%02x DS18B20 Temperature: %3.1f°C\n", address.rom[0], address.rom[1], address.rom[2], address.rom[3], address.rom[4], address.rom[5], address.rom[6], address.rom[7], one_wire.temperature(address));
|
||||
|
||||
if (TEMPERATURE == -1000) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (TEMPERATURE != 85) {
|
||||
|
||||
sprintf(TEMPERATURE_TEMP, "Temperature: %.3f°C\n", TEMPERATURE);
|
||||
_HC_12(TEMPERATURE_TEMP);
|
||||
memset(TEMPERATURE_TEMP, 0, BUFER);
|
||||
TEMPERATURE = -1;
|
||||
|
||||
sprintf(TEMPERATURE_TEMP, "DS18B20 Temperature: %.3f°C\n", TEMPERATURE);
|
||||
_HC_12(TEMPERATURE_TEMP);
|
||||
memset(TEMPERATURE_TEMP, 0, BUFER);
|
||||
TEMPERATURE = -1;
|
||||
}
|
||||
/*
|
||||
// 发送数据到队列
|
||||
xQueueSend(xQueue, &TEMPERATURE, portMAX_DELAY);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
*/
|
||||
//_printTaskStackHighWaterMark("DS18B20");
|
||||
//watchdog_update(); // 喂狗
|
||||
vTaskDelay(pdMS_TO_TICKS(5000)); // 非阻塞延时
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(7000)); // 非阻塞延时
|
||||
}
|
||||
|
||||
return ;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,56 +10,49 @@ void MH_Z14B_INIT()
|
||||
gpio_set_function(UART1_RX_PIN, GPIO_FUNC_UART);
|
||||
uart_set_hw_flow(UART1, false, false);
|
||||
uart_set_format(UART1, DATA_BITS, STOP_BITS, PARITY);
|
||||
|
||||
return ;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// CO2
|
||||
static uint16_t MH_Z14B(int *MH_Z14B_DATA_IS_OK)
|
||||
{
|
||||
// 0x86 读气体浓度值
|
||||
*MH_Z14B_DATA_IS_OK = 0;
|
||||
uint8_t CMD[9] = { 0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79 };
|
||||
|
||||
// 清空接收缓冲区,防止旧数据干扰
|
||||
while (uart_is_readable(UART1))
|
||||
uart_getc(UART1);
|
||||
|
||||
uart_write_blocking(UART1, CMD, 9);
|
||||
//sleep_ms(200);
|
||||
vTaskDelay(pdMS_TO_TICKS(200)); // 非阻塞延时
|
||||
|
||||
// 读取
|
||||
uint8_t CO2_DATA[9] = { 0 };
|
||||
uart_read_blocking(UART1, CO2_DATA, 9);
|
||||
int bytes_read = 0;
|
||||
unsigned long start_time = xTaskGetTickCount();
|
||||
|
||||
// CO2 浓度
|
||||
uint16_t CO2_CONC = (256 * CO2_DATA[2]) + CO2_DATA[3];
|
||||
// 自定义超时读取(例如 500ms 超时)
|
||||
while (bytes_read < 9) {
|
||||
if (uart_is_readable(UART1)) {
|
||||
CO2_DATA[bytes_read++] = uart_getc(UART1);
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(10)); // 等待一会儿
|
||||
}
|
||||
|
||||
// 校验
|
||||
uint8_t CHECKSUM = (0xFF - (CO2_DATA[1] + CO2_DATA[2] + CO2_DATA[3] + CO2_DATA[4] + \
|
||||
CO2_DATA[5] + CO2_DATA[6] + CO2_DATA[7])) + 1;
|
||||
// 超时判断
|
||||
if ((xTaskGetTickCount() - start_time) > pdMS_TO_TICKS(500)) {
|
||||
printf("Error: UART Timeout!\n");
|
||||
return 0; // 读取失败
|
||||
}
|
||||
}
|
||||
|
||||
// 校验逻辑...
|
||||
uint8_t CHECKSUM = (0xFF - (CO2_DATA[1] + CO2_DATA[2] + CO2_DATA[3] + CO2_DATA[4] + CO2_DATA[5] + CO2_DATA[6] + CO2_DATA[7])) + 1;
|
||||
|
||||
if (CO2_DATA[8] == CHECKSUM && CO2_DATA[1] == 0x86) {
|
||||
//printf("CHECKSUM: %X = %X\n", CO2_DATA[8], CHECKSUM);
|
||||
//printf("CO2 Concentration: %d ppm\n", CO2_CONC);
|
||||
*MH_Z14B_DATA_IS_OK = 1;
|
||||
}
|
||||
/*
|
||||
else {
|
||||
// 校准传感器 零点 (ZERO)
|
||||
uint8_t ZERO[] = { 0XFF, 0X01, 0X87, 0X00, 0X00, 0X00, 0X00, 0X00, 0X78 };
|
||||
uart_write_blocking(UART1, ZERO, 9);
|
||||
//sleep_ms(200);
|
||||
vTaskDelay(pdMS_TO_TICKS(200)); // 非阻塞延时
|
||||
|
||||
// 校准传感器 跨度点 (SPAN)
|
||||
uint8_t SPAN[] = { 0XFF, 0X01, 0X88, 0X07, 0XD0, 0X00, 0X00, 0X00, 0XA0 };
|
||||
uart_write_blocking(UART1, SPAN, 9);
|
||||
//sleep_ms(200);
|
||||
vTaskDelay(pdMS_TO_TICKS(200)); // 非阻塞延时
|
||||
|
||||
*MH_Z14B_DATA_IS_OK = 0;
|
||||
printf("CO2 concentration reading failed!\n");
|
||||
return (uint16_t) ((CO2_DATA[2] << 8) | CO2_DATA[3]);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
return CO2_CONC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CO2(void *pvParameters)
|
||||
@@ -68,11 +61,10 @@ void CO2(void *pvParameters)
|
||||
int MH_Z14B_DATA_IS_OK = 0;
|
||||
char CO2_DATA_TEMP[BUFER] = { 0 };
|
||||
MH_Z14B_INIT();
|
||||
_printTaskStackHighWaterMark("CO2");
|
||||
|
||||
while (1) {
|
||||
CO2_DATA = MH_Z14B(&MH_Z14B_DATA_IS_OK);
|
||||
if (CO2_DATA != -1 && MH_Z14B_DATA_IS_OK == 1) {
|
||||
if (CO2_DATA != -1 && MH_Z14B_DATA_IS_OK == 1) {
|
||||
printf("CO2 Concentration: %d ppm\n", CO2_DATA);
|
||||
|
||||
snprintf(CO2_DATA_TEMP, BUFER, "CO2 Concentration: %d ppm\n", CO2_DATA);
|
||||
@@ -80,15 +72,15 @@ void CO2(void *pvParameters)
|
||||
memset(CO2_DATA_TEMP, 0, BUFER);
|
||||
} else {
|
||||
printf("CO2 reading failed!!!");
|
||||
vTaskDelay(pdMS_TO_TICKS(3000)); // 非阻塞延时
|
||||
vTaskDelay(pdMS_TO_TICKS(3000)); // 非阻塞延时
|
||||
}
|
||||
|
||||
|
||||
//_printTaskStackHighWaterMark("CO2");
|
||||
watchdog_update(); // 喂狗
|
||||
|
||||
watchdog_update(); // 喂狗
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(5000)); // 非阻塞延时
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return ;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "hardware/pwm.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
|
||||
#define UART1 uart1
|
||||
#define BAUD_RATE 9600
|
||||
#define DATA_BITS 8
|
||||
|
||||
@@ -13,7 +13,7 @@ int ZC13_INIT()
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ZC13_PIO_UART_TX_DATA(PIO pio, uint sm, uint8_t * DATA, int DATA_LEN)
|
||||
int ZC13_PIO_UART_TX_DATA(PIO pio, uint sm, uint8_t *DATA, int DATA_LEN)
|
||||
{
|
||||
|
||||
for (int i = 0; i < DATA_LEN; i++) {
|
||||
@@ -24,7 +24,7 @@ int ZC13_PIO_UART_TX_DATA(PIO pio, uint sm, uint8_t * DATA, int DATA_LEN)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ZC13_PIO_UART_RX_DATA(PIO pio, uint sm, uint8_t * DATA, int DATA_LEN)
|
||||
int ZC13_PIO_UART_RX_DATA(PIO pio, uint sm, uint8_t *DATA, int DATA_LEN)
|
||||
{
|
||||
char c = '\0';
|
||||
int received_count = 0;
|
||||
@@ -106,19 +106,17 @@ void CH4(void *pvParameters)
|
||||
{
|
||||
ZC13_INIT();
|
||||
char CH4_DATA[BUFER] = { 0 };
|
||||
_printTaskStackHighWaterMark("CH4");
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
||||
while (1) {
|
||||
|
||||
snprintf(CH4_DATA, BUFER, "CH4 Concentration: %d\n", ZC13("ZC05"));
|
||||
_HC_12(CH4_DATA);
|
||||
memset(CH4_DATA, 0, BUFER);
|
||||
|
||||
|
||||
//_printTaskStackHighWaterMark("CH4");
|
||||
//watchdog_update(); // 喂狗
|
||||
vTaskDelay(pdMS_TO_TICKS(5000)); // 非阻塞延时
|
||||
vTaskDelay(pdMS_TO_TICKS(5000)); // 非阻塞延时
|
||||
}
|
||||
|
||||
return ;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -21,27 +21,26 @@ static uint16_t ZE07CO(int ANSWER, int *ZE07_CO_DATA_IS_OK)
|
||||
static uint8_t _ANSWER[9] = { 0xFF, 0x01, 0x78, 0x41, 0x00, 0x00, 0x00, 0x00, 0x46 };
|
||||
uart_write_blocking(UART0, _ANSWER, 9);
|
||||
sleep_ms(100);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (ANSWER == 2) {
|
||||
// 主动上传模式
|
||||
static uint8_t _ANSWER[9] = { 0xFF, 0x01, 0x78, 0x40, 0x00, 0x00, 0x00, 0x00, 0x47 };
|
||||
uart_write_blocking(UART0, _ANSWER, 9);
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
// 读取
|
||||
uint8_t CO_DATA[9] = { 0 };
|
||||
uart_read_blocking(UART0, CO_DATA, 9);
|
||||
sleep_ms(100);
|
||||
|
||||
|
||||
/*
|
||||
for(int i=0; i<9; i++) {
|
||||
printf("0X%X ", CO_DATA[i]);
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
for(int i=0; i<9; i++) {
|
||||
printf("0X%X ", CO_DATA[i]);
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
|
||||
// CO 浓度
|
||||
uint16_t CO_CONC = (256 * CO_DATA[4]) + CO_DATA[5];
|
||||
@@ -64,26 +63,23 @@ void CO(void *pvParameters)
|
||||
{
|
||||
uint16_t CO_DATA = -1;
|
||||
int ZE07_CO_DATA_IS_OK = 0;
|
||||
_printTaskStackHighWaterMark("CO");
|
||||
|
||||
while(1)
|
||||
{
|
||||
|
||||
while (1) {
|
||||
ZE07CO_INIT();
|
||||
|
||||
|
||||
CO_DATA = ZE07CO(2, &ZE07_CO_DATA_IS_OK);
|
||||
if (CO_DATA != -1 && ZE07_CO_DATA_IS_OK == 1) {
|
||||
printf("CO Concentration: %d ppm\n", CO_DATA);
|
||||
|
||||
|
||||
char CO_DATA_TEMP[BUFER] = { 0 };
|
||||
sprintf(CO_DATA_TEMP, "CO Concentration: %d ppm\n", CO_DATA);
|
||||
_HC_12(CO_DATA_TEMP);
|
||||
|
||||
|
||||
}
|
||||
|
||||
//_printTaskStackHighWaterMark("CO");
|
||||
//watchdog_update(); // 喂狗
|
||||
vTaskDelay(pdMS_TO_TICKS(5000)); // 非阻塞延时
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "hardware/pwm.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
|
||||
#define UART0 uart0
|
||||
#define BAUD_RATE 9600
|
||||
#define DATA_BITS 8
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
|
||||
#include "common.hpp"
|
||||
#include "HC-12.hpp"
|
||||
#include "boot_time.hpp"
|
||||
|
||||
// 建议将常量定义在头文件或文件顶部
|
||||
static const uint32_t SEND_INTERVAL_SEC = 5;
|
||||
|
||||
void BOOT_TIME(void *pvParameters)
|
||||
{
|
||||
(void)pvParameters;
|
||||
uint32_t _HC_12_SEND_NUM = 0;
|
||||
char BOOT_TIME_TEMP[BUFER]; // 不需要初始化为0,snprintf会处理
|
||||
|
||||
_printTaskStackHighWaterMark("BOOT_TIME");
|
||||
|
||||
int _HC_12_SEND_NUM = 0;
|
||||
char BOOT_TIME_TEMP[BUFER] = { 0 };
|
||||
|
||||
// 用于精确计时的变量
|
||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||
|
||||
while (1) {
|
||||
// 获取自开机以来的微秒数
|
||||
uint64_t uptime_us = time_us_64();
|
||||
uint64_t total_uptime_sec = uptime_us / 1000000;
|
||||
// 使用 vTaskDelayUntil 保证精确的 1 秒周期
|
||||
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));
|
||||
|
||||
// 打印开机秒数
|
||||
printf("Boot Time: %llu seconds\n", total_uptime_sec);
|
||||
// 获取秒数 (uint32_t 对于开机时间足够了,支持136年)
|
||||
uint32_t total_uptime_sec = (uint32_t)(time_us_64() / 1000000ULL);
|
||||
|
||||
{
|
||||
_HC_12_SEND_NUM++;
|
||||
if (_HC_12_SEND_NUM > 5) {
|
||||
sprintf(BOOT_TIME_TEMP, "Boot Time: %llu seconds\n", total_uptime_sec);
|
||||
// 打印调试信息
|
||||
printf("Boot Time: %lu seconds\n", total_uptime_sec);
|
||||
|
||||
_HC_12_SEND_NUM++;
|
||||
if (_HC_12_SEND_NUM >= SEND_INTERVAL_SEC) {
|
||||
// 使用 snprintf 保证安全,且不需要 memset
|
||||
int len = snprintf(BOOT_TIME_TEMP, sizeof(BOOT_TIME_TEMP),
|
||||
"Boot Time: %lu seconds\n", total_uptime_sec);
|
||||
|
||||
if (len > 0 && len < (int)sizeof(BOOT_TIME_TEMP)) {
|
||||
_HC_12(BOOT_TIME_TEMP);
|
||||
memset(BOOT_TIME_TEMP, 0, BUFER);
|
||||
_HC_12_SEND_NUM = 0;
|
||||
}
|
||||
|
||||
_HC_12_SEND_NUM = 0;
|
||||
}
|
||||
|
||||
_printTaskStackHighWaterMark("BOOT_TIME");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000)); // 每秒延迟
|
||||
vTaskDelay(pdMS_TO_TICKS(1000)); // 非阻塞延时
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
#include "boot_time.hpp"
|
||||
|
||||
#ifndef PICO_DEFAULT_LED_PIN
|
||||
#warning pio/hello_pio example requires a board with a regular LED
|
||||
#define PICO_DEFAULT_LED_PIN 25
|
||||
#warning pio/hello_pio example requires a board with a regular LED
|
||||
#define PICO_DEFAULT_LED_PIN 25
|
||||
#endif
|
||||
|
||||
void Led_Blinky(void *pvParameters)
|
||||
@@ -62,24 +62,19 @@ void CPU(void *pvParameters)
|
||||
adc_set_temp_sensor_enabled(true);
|
||||
adc_select_input(4); // Input 4 is the onboard temperature sensor.
|
||||
|
||||
_printTaskStackHighWaterMark("CPU_");
|
||||
|
||||
while (1) {
|
||||
const float conversionFactor = 3.3f / (1 << 12);
|
||||
|
||||
float adc = (float)adc_read() * conversionFactor;
|
||||
float tempC = 27.0f - (adc - 0.706f) / 0.001721f;
|
||||
|
||||
printf("CPU temperature %.02f°C %.02f°F\n", tempC, (tempC * 9 / 5 + 32));
|
||||
|
||||
|
||||
printf("CPU Temperature %.02f°C %.02f°F\n", tempC, (tempC * 9 / 5 + 32));
|
||||
|
||||
char ONBOARD_TEMPERATURE_TEMP[BUFER] = { 0 };
|
||||
sprintf(ONBOARD_TEMPERATURE_TEMP, "Onboard temperature %.02f°C %.02f°F\n", tempC, (tempC * 9 / 5 + 32));
|
||||
sprintf(ONBOARD_TEMPERATURE_TEMP, "CPU Temperature %.02f°C %.02f°F\n", tempC, (tempC * 9 / 5 + 32));
|
||||
_HC_12(ONBOARD_TEMPERATURE_TEMP);
|
||||
|
||||
_printTaskStackHighWaterMark("CPU_");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(3000)); // 非阻塞延时
|
||||
vTaskDelay(pdMS_TO_TICKS(5000)); // 非阻塞延时
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +89,7 @@ int main(int argc, char *argv[])
|
||||
} else {
|
||||
printf("Clean boot\n");
|
||||
}
|
||||
|
||||
watchdog_enable(8300, 1); // 8秒检测是否重新加载看门狗计数器. (不更新计数器则重启硬件, 最高8秒)
|
||||
//watchdog_start_tick(12);
|
||||
|
||||
@@ -112,51 +108,48 @@ int main(int argc, char *argv[])
|
||||
TaskHandle_t CO2_xHandle = NULL;
|
||||
TaskHandle_t BOOT_TIME_xHandle = NULL;
|
||||
|
||||
|
||||
// 板载CPU温度
|
||||
xReturned = xTaskCreate(CPU, "CPU task", 512, NULL, tskIDLE_PRIORITY, &CPU_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("CPU() Task Error!");
|
||||
}
|
||||
|
||||
// 板载LED闪烁
|
||||
xReturned = xTaskCreate(Led_Blinky, "Blinky task", 512, NULL, tskIDLE_PRIORITY, &Led_Blinky_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("Blinky() Task Error!");
|
||||
}
|
||||
/*
|
||||
// 板载LED闪烁
|
||||
xReturned = xTaskCreate(Led_Blinky, "Blinky task", 512, NULL, tskIDLE_PRIORITY, &Led_Blinky_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("Blinky() Task Error!");
|
||||
}
|
||||
*/
|
||||
|
||||
// DS18B20
|
||||
xReturned = xTaskCreate(DS18B20, "DS18B20 task", 1024, NULL, tskIDLE_PRIORITY, &DS18B20_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("DS18B20() Task Error!");
|
||||
}
|
||||
|
||||
// CH4
|
||||
xReturned = xTaskCreate(CH4, "CH4 task", 2048, NULL, tskIDLE_PRIORITY, &CH4_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("CH4() Task Error!");
|
||||
}
|
||||
|
||||
// CO
|
||||
xReturned = xTaskCreate(CO, "CO task", 2048, NULL, tskIDLE_PRIORITY, &CO_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("CO() Task Error!");
|
||||
}
|
||||
|
||||
// CO2
|
||||
xReturned = xTaskCreate(CO2, "CO2 task", 2048, NULL, tskIDLE_PRIORITY, &CO2_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("CO2() Task Error!");
|
||||
}
|
||||
|
||||
// Boot Time
|
||||
xReturned = xTaskCreate(BOOT_TIME, "BOOT_TIME task", 512, NULL, tskIDLE_PRIORITY, &BOOT_TIME_xHandle);
|
||||
if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
|
||||
printf("CO2() Task Error!");
|
||||
}
|
||||
|
||||
|
||||
vTaskStartScheduler();
|
||||
while (1) {};
|
||||
while (1) {
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user