0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

利用RT-Thread与MQTT实现智慧班车管理系统的设计

冬至子 来源:Agony_L 作者:Agony_L 2023-10-09 10:55 次阅读

项目硬件开发总结
下面从开发者的角度对本项目进行总结:(因为不是写paper所以比较随意哈~)
项目采用的IoT架构,底层是STM32L475VET6潘多拉开发板+RT-Thread,对于RT-Thread的资源使用情况在论文中也有提到,这里直接截个图:

1.jpg

RT-Thread使用情况详情
内核层的信号量、邮箱、消息队列等机制是用于线程同步以及线程通信的,中断一开始是用于检测PIN设备的IRQ,但是后来去掉了,原因是不好用,确实是用中断的时候自己懒得调试了,程序运行会出现很多问题,所以直接开了个线程,这个后面会讲到。

设备与驱动层中I/O设备模型是最基本的设备Model,所以不再赘述。UART设备是值得一提的哈。因为这个项目需要用到三个串口,即串口1用于Finsh组件调试,串口2用于NB-IoT通信,串口3用于GPS数据的URC解析,但是官方给的BSP中没有添加串口3设备,所以需要自己添加UART设备。

添加UART设备
在RT-Thread文档中心已经给出了详细的添加步骤,所在路径如下:

1.jpg

首先,需要本地安装Cubemx,然后打开裁剪过的BSP目录中的board文件夹
【关于BSP的裁剪工作可以通过ENV工具,使用scons—dist完成】
然后找到该文件夹

1.jpg

打开完成后如果Cube版本与官方制作BSP使用的Cube版本不同时会弹出如下提示框:

1.jpg

找到UART3选项,设置为异步模式

1.jpg

2.jpg

同时,需要注意的是USART3默认使用的端口,潘多拉开发板并没有引出,所以需要到引出的PB10和PB11单独设置端口的模式:

1.jpg

2.jpg

3.jpg

最后,需要打开board文件夹中的Kconfig文件,添加UART3选项:【建议使用Notepad打开,这样打开的文件格式对称,便于复制】

1.jpg

添加完成后,打开ENV工具输入menuconfig命令,依次进入Hardware Drivers Config—->On-chip Peripheral Drivers—->Enable UART就可以看到新添加的UART3选项:

1.jpg

选中后重新生成工程,打开后记得重新编译。这样,UART3设备驱动已经添加成功。

适配软件包
回顾一下本项目需要完成的功能,这里直接贴出了论文截图:(懒了~)

1.jpg

AHT10软件包的使用
将AHT10软件包添加后,只需要关心应用层的逻辑即可。
我的应用层线程初始化都是在main线程中完成的,有关于AHT10数据采集线程的初始化如下:

1.jpg

线程入口函数如下:

static void aht10_thread_entry()
{
rt_device_t dev_temp = RT_NULL;
rt_device_t dev_humi = RT_NULL;
struct tmp_msg msg;
struct rt_sensor_data sensor_data;
rt_size_t res_temp,res_humi;
rt_err_t res;
res = rt_sem_take(send_AHT_sem, RT_WAITING_FOREVER);
if(res != RT_EOK)
{
rt_kprintf("getGps_thread take a nb semaphore, failed.n");
return;
}
dev_temp = rt_device_find("temp_aht");
dev_humi = rt_device_find("humi_aht");
if (dev_temp == RT_NULL)
{
rt_kprintf("Can't find device:dev_tempn");
return;
}
if (rt_device_open(dev_temp, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("open device dev_temp failed!n");
return;
}
if (dev_humi == RT_NULL)
{
rt_kprintf("Can't find dev_humi devicen");
return;
}
if (rt_device_open(dev_humi, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("open device dev_humi failed!n");
return;
}
while(1)
{
res_temp = rt_device_read(dev_temp, 0, &sensor_data, 1);
if (res_temp != 1)
{
rt_kprintf("read temp data failed!");
rt_device_close(dev_temp);
return;
}
else
{
// rt_kprintf("temp:%3dCn",abs(sensor_data.data.temp)/10);
msg.temp_value=(abs(sensor_data.data.temp)/10);
}
rt_thread_mdelay(10);
res_humi = rt_device_read(dev_humi, 0, &sensor_data, 1);
if (res_humi != 1)
{
rt_kprintf("read humi data failed!n");
rt_device_close(dev_humi);
return;
}
else
{
// rt_kprintf("hum:%2d%, timestamp:%5dn",abs(sensor_data.data.humi)/10);
msg.humi_value=abs(sensor_data.data.humi)/10;
}
rt_mq_send(tmp_msg_mq, &msg,sizeof(msg));
// rt_kprintf("======aht10-Thread Send a mq,msg.tem:%d,msg.hum:%d======rn",msg.temp_value,msg.humi_value);
rt_thread_mdelay(300);
}
}
在上述代码中首先是获取NB初始化完成后release的信号量;获取成功后,再获取注册到Sensor框架中的温湿度传感器(注意:这里的温湿度传感器是分开的,可以通过list_device在Finshi终端查看);然后是打开设备并读取温湿度信息;最后将采集到的数据以邮箱机制发送到NB发送线程。这是整个AHT10软件包的添加和数据的读取工作(需要注意:AHT10是通过I2C进行通信的,所以需要开启I2C设备框架)

lwgps软件包的使用
lwgps软件包是一个轻量级的gps的URC解析包,支持NEMA格式。在使用软件包的时候也遇到过不少的坑哈,但是庆幸的是都已经解决了

可以直接使用该作者提供的驱动框架。特别需要注意的是:这里有一个小坑,可能很多人会忽略,软件包已经使用了INIT_APP_EXPORT(lwgps2rtt_init);添加了lwgps的初始化,因此不需要在应用层调用,如果调用会出现我之前出现的报错信息

1.jpg

2.jpg

线程入口函数如下:

static void getGps_thread_entry()
{
lwgps_t gps_info;
struct gps_msg gpsmsg;
float lati , longi;
rt_err_t res;
// res = rt_sem_take(send_Gps_sem, RT_WAITING_FOREVER);
// if(res != RT_EOK)
// {
// rt_kprintf("getGps_thread take a nb semaphore, failed.n");
// return;
// }
while(1)
{
lwgps2rtt_get_gps_info(&gps_info);
gpsmsg.lati_value=(gps_info.latitude);
gpsmsg.longi_value=(gps_info.longitude);
gpsmsg.hour=(gps_info.hours);
// rt_kprintf("GPS-Data:hour-->%drn",gpsmsg.hour);
rt_mq_send(gps_msg_mq,&gpsmsg,sizeof(gpsmsg));
rt_thread_delay(500);
}
}
使用PIN设备——MQ2数据采集
关于MQ2的数据读取,并没有使用到ADC设备,因为我想缩短开发周期赶论文┭┮﹏┭┮。我选用的是MQ2的DO输出模式,通过调整电位器设置阈值,实现原理比较简单哈,不再赘述。同时,它的软件读取工作也比较简单。但是由于项目实时性要求,上行数据流是采用JSON封装的,方便小程序端解析,所以需要持续发送采集数据,因此无论是检测到可燃气体还是没有检测到都要发送消息队列。
详细代码如下:

static void test_thread_entry()
{
char TR_ARRAY[]="true";
char FA_ARRAY[]="false";
struct mq_msg mq2_msg;
while(1)
{
// rt_kprintf("testrn");
if(rt_pin_read(MQ2_PIN_NUM)==PIN_LOW)
{
memcpy(mq2_msg.msg,TR_ARRAY,sizeof(TR_ARRAY));
rt_mq_send(mq2_msg_mq,&mq2_msg,sizeof(mq2_msg));
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
rt_thread_mdelay(500);
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
}
else{
memcpy(mq2_msg.msg,FA_ARRAY,sizeof(FA_ARRAY));
rt_mq_send(mq2_msg_mq,&mq2_msg,sizeof(mq2_msg));
}
rt_thread_mdelay(200);
}
}
使用PIN设备——红外对射数据采集
红外模块采用的“消抖”操作,因为有可能车门位置经过的人会一直停留,所以按照按键消抖处理的,详细的流程不再说明,直接上代码了:

/* 红外检测线程入口函数*/
static void hw_thread_entry(void parameter)
{
static rt_uint8_t hw_up = 1; /
无人标志 /
/
初始化红外对射模块 /
rt_pin_mode(PIN_NUM_ADD, PIN_MODE_INPUT);
rt_pin_mode(PIN_NUM_SUB, PIN_MODE_INPUT);
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
tmp_msg_mb = rt_mb_create("temp_mb0", MB_LEN, RT_IPC_FLAG_FIFO);
while (1)
{
/
检测无人标志 /
if (hw_up && ((rt_pin_read(PIN_NUM_ADD) == PIN_LOW) ||
(rt_pin_read(PIN_NUM_SUB) == PIN_LOW)))
{
rt_thread_mdelay(50); /
延时消抖*/
hw_up = 0;
if (rt_pin_read(PIN_NUM_SUB) == PIN_LOW)
{
rt_kprintf("Having person-SUB!n");
rt_pin_write(LED0_PIN, PIN_HIGH);
if(people_num<=0)
{
rt_kprintf("The number of people is emptyn");
continue;
}
else{
people_num--;
rt_kprintf("The num of people is %drn",people_num);
//rt_mb_send(tmp_msg_mb,people_num);
// rt_mb_send(tmp_msg_mb,people_num);
}
}
else if (rt_pin_read(PIN_NUM_ADD) == PIN_LOW)
{
rt_kprintf("Having person-ADD!n");
rt_pin_write(LED0_PIN, PIN_LOW);//点亮
if(people_num>=30)
{
rt_kprintf("The number of people is full!n");
continue;
}
else{
people_num++;
rt_kprintf("The num of people is %drn",people_num);
// rt_mb_send(tmp_msg_mb,people_num);
}
}
}
else if((rt_pin_read(PIN_NUM_ADD) == PIN_HIGH) &&
(rt_pin_read(PIN_NUM_SUB) == PIN_HIGH))
{
hw_up = 1; /*无人标志 */
// rt_mb_send(tmp_msg_mb,people_num);
}
rt_mb_send(tmp_msg_mb,people_num);
rt_thread_mdelay(100);
}
}
使用AT设备——NB模块初始化与NB模块数据发送
首先需要在项目中添加AT组件,同时添加M5311软件包,添加完成后,在应用层main线程中开启NB初始化以及NB订阅和发送线程(采用MQTT协议)

1.jpg

初始化线程入口函数如下:

//NB初始化线程:新建MQTT机制+连接MQTT服务器
static void NB_mqtt_thread_entery()
{
nb_client = at_client_get("uart2");
nb_resp = at_create_resp(1024, 0, rt_tick_from_millisecond(300));
if(at_obj_exec_cmd(nb_client,nb_resp,arv)!=RT_EOK)
{
LOG_E("The MQTT haven't inited successrn");
}
else{
LOG_E("MQTT HAVE INITED SUCCESSrn");
if(at_obj_exec_cmd(nb_client,nb_resp,"AT+MQTTOPEN=1,1,0,0,0,'',''")!=RT_EOK)
{
LOG_E("The MQTT haven't inited successrn");
}
else{//真正完成了新建MQTT机制和连接服务器
LOG_E("The MQTT haven inited successrn");
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
rt_thread_mdelay(500);
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
rt_sem_release(nb_sem);//释放信号量
rt_sem_release(send_Gps_sem);
rt_sem_release(send_AHT_sem);
}
}
rt_thread_mdelay(1000);
}
订阅和发送线程入口函数如下:

static void NB_mqtt_send_thread_entery()
{
struct tmp_msg msg;
struct gps_msg GPS;
struct mq_msg mq2msg;
static rt_err_t result;
int pep_num=0;
result = rt_sem_take(nb_sem, RT_WAITING_FOREVER);
if(result != RT_EOK)
{
rt_kprintf("NB_mqtt_send_thread take a nb semaphore, failed.n");
return;
}else{
for(;;)
{
if(at_obj_exec_cmd(nb_client,nb_resp,"AT+MQTTSUB="pyr",1")==RT_EOK)//订阅主题
{
rt_kprintf("The NB-IoT have subscribed the pyr topicrn");
break;
}
}
while(1)
{
if((rt_mq_recv(gps_msg_mq,&GPS,sizeof(GPS),RT_WAITING_FOREVER)==RT_EOK)&&
(rt_mq_recv(tmp_msg_mq, &msg, sizeof(msg), RT_WAITING_FOREVER)==RT_EOK)&&
(rt_mq_recv(mq2_msg_mq, &mq2msg, sizeof(mq2msg), RT_WAITING_FOREVER)==RT_EOK)&&
(rt_mb_recv(tmp_msg_mb,(int*)&pep_num,RT_WAITING_FOREVER)==RT_EOK))
{
rt_kprintf("---------->NB Send Thread Receive the data-hour:%d,tmp:%d,mq2:%s,pep_num:%drn",GPS.hour,msg.temp_value,mq2msg.msg,pep_num);
if(at_obj_exec_cmd(nb_client,nb_resp,"AT+MQTTPUB="pyr",1,1,0,0,"{"temp":%d,"hum":%d,"lati":%f,"longi":%f,"mq2":%s,"pep_num":%d}"",msg.temp_value,msg.humi_value,GPS.lati_value,GPS.longi_value,mq2msg.msg,pep_num)!=RT_EOK)
{
LOG_E("Send the MEssage of the MQTT failedrn");
}
// if(at_obj_exec_cmd(nb_client,nb_resp,"AT+MQTTPUB="pyr",1,1,0,0,"{"latitude":37.3862770000,"longitude":117.9898270000,"temp":23,"humi":60,"person":15,"smoke":false}"")!=RT_EOK)
// {
// LOG_E("Send the MEssage of the MQTT failedrn");
// }
}
rt_thread_mdelay(500);
// rt_thread_mdelay(1000); 有延时,所以采用0.5S-Debug测出来的
}
}
}

项目软件开发总结
项目的应用软件使用的是微信小程序,涉及到的内容包括:小程序适配MQTT客户端连接服务器以及订阅和发布消息、小程序云开发模式Serverless、小程序使用Map组件、小程序使用腾讯云SMS服务(个人版)、小程序实现左滑删除样式等内容… …另外,MQTT服务器使用的是EMQ搭建的免费版MQTT服务器。

这是使用的MQTT地址,目前仍然可以使用:EMQ服务器IP地址 ,支持端口号18083、8083、8084、18084访问,同时提供匿名访问.

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • PIN管
    +关注

    关注

    0

    文章

    36

    浏览量

    6315
  • UART接口
    +关注

    关注

    0

    文章

    124

    浏览量

    15280
  • RT-Thread
    +关注

    关注

    31

    文章

    1277

    浏览量

    39972
  • STM32L4
    +关注

    关注

    1

    文章

    42

    浏览量

    9395
  • MQTT协议
    +关注

    关注

    0

    文章

    97

    浏览量

    5353
收藏 人收藏

    评论

    相关推荐

    如何对RT-Thread内核有基本的了解?

    RT-Thread 的时钟管理以时钟节拍为基础,时钟节拍是 RT-Thread 操作系统中最小的时钟单位。
    的头像 发表于 07-19 10:12 7127次阅读
     如何对<b class='flag-5'>RT-Thread</b>内核有基本的了解?

    RT-Thread编程指南

    RT-Thread编程指南——RT-Thread开发组(2015-03-31)。RT-Thread做为国内有较大影响力的开源实时操作系统,本文是RT
    发表于 11-26 16:06 0次下载

    RT-Thread用户手册

    RT-Thread用户手册——本书是RT-Thread的编程手册,用于指导在RT-Thread实时操作系统环境下如何进行编 程。
    发表于 11-26 16:16 0次下载

    基于RT-Thread和ESP8266在实战实现MQTT应用

    应用出来。 在上述的诉求,所以我决定写MQTT的第三篇文章--MQTT的实战。 本次实战,我分别进行两种实战: MQTT.fx客户端 + 腾讯云 利用小熊派 +
    的头像 发表于 05-24 09:18 4946次阅读
    基于<b class='flag-5'>RT-Thread</b>和ESP8266在实战<b class='flag-5'>实现</b><b class='flag-5'>MQTT</b>应用

    RT-thread】如何将RT-thread系统移植到stm32

    设备,RT-Thread 又能使用在线的软件包管理工具,配合系统配置工具实现直观快速的模块化裁剪,无缝地
    发表于 12-08 11:06 21次下载
    【<b class='flag-5'>RT-thread</b>】如何将<b class='flag-5'>RT-thread</b><b class='flag-5'>系统</b>移植到stm32

    RT-Thread全球技术大会:萤石EZloT SDK对RT-Thread的支持以及多芯片平台管理策略

    RT-Thread全球技术大会:萤石EZloT SDK对RT-Thread的支持以及多芯片平台管理策略           审核编辑:彭静  
    的头像 发表于 05-27 11:55 1702次阅读
    <b class='flag-5'>RT-Thread</b>全球技术大会:萤石EZloT SDK对<b class='flag-5'>RT-Thread</b>的支持以及多芯片平台<b class='flag-5'>管理</b>策略

    RT-Thread全球技术大会:RT-Thread电源管理的意义

    RT-Thread全球技术大会:RT-Thread电源管理的意义是为了满足用户对性能需求,尽可能降低系统能耗以延长设备待机时间。           审核编辑:彭静
    的头像 发表于 05-27 15:01 1069次阅读
    <b class='flag-5'>RT-Thread</b>全球技术大会:<b class='flag-5'>RT-Thread</b>电源<b class='flag-5'>管理</b>的意义

    RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍

    RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍             审核编辑:彭静
    的头像 发表于 05-27 16:52 1864次阅读
    <b class='flag-5'>RT-Thread</b>全球技术大会:<b class='flag-5'>RT-Thread</b>对POSIX的<b class='flag-5'>实现</b>情况介绍

    RT-Thread学习笔记 RT-Thread的架构概述

    的种种优越之处。RT-Thread 是一款完全由国内团队开发维护的嵌入式实时操作系统(RTOS),具有完全的自主知识产权。经过 16 个年头的沉淀,伴随着物联网的兴起,它正演变成一个功能强大、组件丰富的物
    的头像 发表于 07-09 11:27 4492次阅读
    <b class='flag-5'>RT-Thread</b>学习笔记 <b class='flag-5'>RT-Thread</b>的架构概述

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_线程管理

    RT-Thread文档_线程管理
    发表于 02-22 18:28 0次下载
    <b class='flag-5'>RT-Thread</b>文档_线程<b class='flag-5'>管理</b>

    RT-Thread文档_时钟管理

    RT-Thread文档_时钟管理
    发表于 02-22 18:28 0次下载
    <b class='flag-5'>RT-Thread</b>文档_时钟<b class='flag-5'>管理</b>

    RT-Thread文档_内存管理

    RT-Thread文档_内存管理
    发表于 02-22 18:30 0次下载
    <b class='flag-5'>RT-Thread</b>文档_内存<b class='flag-5'>管理</b>

    RT-Thread文档_中断管理

    RT-Thread文档_中断管理
    发表于 02-22 18:30 1次下载
    <b class='flag-5'>RT-Thread</b>文档_中断<b class='flag-5'>管理</b>

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 9次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植