背景介绍
- 温度、湿度、光照是农业养殖中十分重要的参数,该方案基于农业的大棚温室养殖,用星火一号开发板制作了一套物联网温室报警系统。
- 本方案使用RT-Thread Studio实现软件工程、配置、调试。
- 本项目作为华南理工大学嵌入式夏令营结营任务,项目成员来自华南理工大学大二微电子、集成电路、智能制造专业。
硬件选择
外设与传感器选择
在这个方案中,我们需要使用以下外设和传感器:
- STM32F407芯片:作为主控制器,负责整个系统的控制和数据处理。它具有足够的处理能力和丰富的外设接口。
- AHT10温湿度传感器:用于检测温度和湿度数据。我们使用I2C协议与STM32F407芯片进行通信,将采集的温湿度数据传输给主控制器。
- BH1750光照传感器:用于检测光照强度。它也通过I2C协议与STM32F407芯片进行通信,将采集的光照数据传输给主控制器。
- RW007 Wifi模块:用于无线数据传输。它是一款高性价SPI的Wi-Fi模块,提供基于802.11b/g/n的高速无线数据传输功能,减低高速Wi-Fi模块的应用门槛。
技术功能
线程传输设置
在线程之间传输的数据包括:
- 从AHT10温湿度传感器读取的温度和湿度数据。
- 从BH1750光照传感器读取的光照强度数据。
- 阈值数据,用于设置温度、湿度和光照的报警阈值。
- 报警状态,用于判断是否触发蜂鸣器报警。
在物联网温室报警系统中,主控制器线程可以通过共享内存或消息队列来接收传感器数据和阈值数据。然后,通过网络模块将数据上传到云平台时,可以使用套接字进行数据传输。此外,通过信号量或互斥量进行同步,将数据传输给报警线程。报警线程根据接收到的数据进行判断并控制蜂鸣器开关。
程序运行逻辑在这里插入代码片
程序运行步骤为:
- 主控制器线程读取传感器数据,并将数据通过网络模块上传到云平台。
- 云平台接收到数据后,进行分析和处理。根据预设的阈值进行判断,确定是否触发报警。
- 云平台生成相应的信号,并将信号发送回主控制器。
- 主控制器线程接收到云平台发送的信号,根据信号控制蜂鸣器的开关状态。
通过这种方式,可以实现远程监控和控制功能。主控制器将传感器数据上传到云平台,云平台进行分析后,返回信号给主控制器来控制蜂鸣器。这样,可以在云端实现更复杂的数据分析和决策,并实现远程控制的功能。
程序流程图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRtGuRuX-1690302870959)(系统流程图.png)]
项目进展
线程
rt_thread1_entry 云平台报警线程
rt_thread1_entry 云平台报警线程
rt_thread2_entry 本地执行命令线程
semaphore_sample 信号量初始化
OneNet数据可视化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nmQvDYnx-1690302870960)(温度.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kZNzNJn6-1690302870960)(湿度.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YAQY0UJ-1690302870960)(亮度.png)]
效果图
未来与展望
实现OneNet分析数据、传输命令
当前项目是利用OneNet监测、传输数据给星火一号,再进行本地分析、发出控制命令。如果可以做进一步的优化,可以直接使用OneNet进行数据分析。
利用OneNet监测、传输数据给星火一号,再进行本地分析、发出控制命令。如果可以做进一步的优化,可以直接使用OneNet进行数据分析。
代码块
线程
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-07-23 DELL the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include "allheads.h"
#define THREAD_PRIORITY 25
#define THREAD_TIMESLICE 5
/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;
static rt_sem_t stop_sem = RT_NULL;
ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread1_entry(void *parameter)
{
static rt_uint8_t count = 0;
while(1)
{
gzic_hot();
/* 云平台提醒报警,就释放报警信号量 */
if(dynamic_sem == RT_NULL)/*注:改为接收到云平台的报警标志*/
{
rt_kprintf("t1 release a dynamic semaphore.\n");
rt_sem_release(dynamic_sem);
}
if(dynamic_sem == RT_NULL)/*注:改为接收到云平台的停止报警标志*/
{
rt_kprintf("t1 release a stop semaphore.\n");
rt_sem_release(stop_sem);
}
}
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread2_entry(void *parameter)
{
static rt_err_t result1;
static rt_err_t result2;
static rt_uint8_t number = 0;
while(1)
{
/* 永久方式等待信号量,获取到信号量,则执行报警/停止的操作 */
result1 = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);
result2 = rt_sem_take(stop_sem, RT_WAITING_FOREVER);
if (result1 != RT_EOK)
{
rt_kprintf("t2 take a dynamic semaphore.\n");
FBI_warning();
rt_sem_delete(dynamic_sem);
return;
}
if(result2 != RT_EOK)
{
rt_kprintf("t2 take a stop semaphore.\n");
FBI_warningstop();
rt_sem_delete(stop_sem);
return;
}
}
}
/* 信号量示例的初始化 */
int semaphore_sample(void)
{
/* 创建一个动态信号量,初始值是 0 */
dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO);
stop_sem = rt_sem_create("ssem", 0, RT_IPC_FLAG_PRIO);
if (dynamic_sem == RT_NULL)
{
rt_kprintf("create dynamic semaphore failed.\n");
return -1;
}
else
{
rt_kprintf("create done. dynamic semaphore value = 0.\n");
}
if (stop_sem == RT_NULL)
{
rt_kprintf("create stop semaphore failed.\n");
return -1;
}
else
{
rt_kprintf("create done. stop semaphore value = 0.\n");
}
rt_thread_init(&thread1,
"thread1",
rt_thread1_entry,
RT_NULL,
&thread1_stack[0],
sizeof(thread1_stack),
THREAD_PRIORITY, THREAD_TIMESLICE);
rt_thread_startup(&thread1);
rt_thread_init(&thread2,
"thread2",
rt_thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
THREAD_PRIORITY-1, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (&thread2 != RT_NULL)
rt_thread_startup(&thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(semaphore_sample, semaphore sample);