【RT-Thread 作品秀】基于RT-Thread的网络照相机
作者:吴顶顶
概述
随着科技的进步和互联网的发展,基于物联网的可拍照设备也越来越多的融入到人们的生活中来,例如在超市中,管理者利用拍照设备定时抓取货架照片,分析货物状态,并补充、优化货物摆放;在酒吧里,管理者会利用拍照设备定时抓拍酒架照片,传送到网络平台供大众浏览,以招揽更多顾客。本网络照相机基于STM32H7+RTThread平台,采集摄像头数据,并通过无线网络传送到服务器,提供SD卡配网、手动拍摄、定时拍摄、照片推送等功能,并提供windows上位机提供控制和照片显示功能。
主要功能有:
格式化sd卡:格式化sd卡,但是会保留网络配置文件,其他文件全部删除
设备重启:重启设备
实时拍照:发送指令给照相机,照相机拍照,并把照片回传
定时拍照:照相机依据下发的拍照时间,在时间到达时拍摄一张照片,并传给服务器
按键拍照:点击板上用户按钮,拍摄一张照片,并传给服务器
定时任务:可以新建/删除/查询定时拍照任务,任务存储在sd卡中,重启有效
开发环境
硬件:ART-PI(STM32H750主控)+ OV2640模组
RT-Thread版本:4.0.3
SDK 版本:1.0.1
开发工具及版本:RT-Thread Studio 1.1.5, Qt5.14.0
RT-Thread使用情况概述
内核部分:调度器,信号量,互斥锁,内存管理
调度器:多任务调度
信号量:用于唤醒对应任务
互斥锁:用于互斥资源独占访问
内存管理:动态内存申请与释放
虚拟文件系统:文件操作,sd卡、照片文件
IPC:mqtt发送数据需要
I2C:配置摄像头模块需要
RTC和NTP:同步时间
软件包部分:paho mqtt,cJSON,netutils
其他:base64
用于将图片文件转换成字符串,便于mqtt传输
硬件框架
总体的硬件框架如下图所示:
本网络摄像机硬件结果较为简单,即art-pi连接一个摄像头模组,art-pi板上用到了AP6212无线模块,外部内存,led指示灯,和sd卡。其中,摄像头模块用于采集图像信号;AP6212用于和服务器进行通信;因一张图像数据量较大,片内内存不够,故而使用外部内存;led灯用于指示设备工作状态;sd卡用于保存网络、服务器、和定时任务配置。
软件框架说明
整体的软件框架如下图所示,网络照相机内部有一个proxy线程,负责和云端进行通信,在接收到云端消息后会解析,并分发到其他的线程执行,然后将执行结果返回到云端;照相机发生了其他的事件,例如用户按键拍照,也会将数据传给proxy线程,proxy线程再将数据发送到云端。用户通过上位机终端软件连接上云服务器,实现与照相机的通信及控制。
整个系统支持接入多个照相机,如下图所示,不同的照相机通过sd卡配置文件中sn进行区分,上位机软件可以显示所有在线的照相机,但同一时间只支持操作一个。
软件模块说明
1. 用户线程创建流程
如下图所示为用户线程创建流程
用户线程作用描述如下:
main:用于创建sd_card 线程,检测按键事件,闪灯;
sd_card:用于管理与sd卡相关的工作,包括拍照,网络配置,定时任务;
network:负责联网,根据sd卡的配置文件连接到指定的wifi网络;
proxy:负责启动mqtt,并管理与云端的通信,其他线程都需要通过proxy线程与云端交互数据;
event:定时任务和按键任务,在定时时间到达时,或者用户按键时拍摄照片并通过proxy上传云端。
2. 通信接口及流程
2.1 MQTT订阅主题
-
设备向服务器订阅主题:
/ter/query/discovery
,用于接收设备发现消息
/ter/sn/request
,用于接收针对该设备的指令,其中sn
为设备的SN号,下同 -
客户端向服务器订阅主题:
/dev/response/discovery
,用于接收设备发现回复
/dev/response/will
,用于接收设备遗嘱消息
/dev/sn/response
,用于接收设备操作指令回复
/dev/sn/event
,用于接收设备的通知
2.2 设备发现
所有的设备均订阅/ter/query/discovery
主题,客户端向该主题发布发现消息,所有收到消息的设备向/dev/response/discovery
回复一条消息,而客户端又订阅了/dev/response/discovery
主题,故而便可以知道哪些设备在线了。
设备连上服务器的时候,会定义一个遗嘱消息,主题为/dev/sn/will
,客户端订阅了该主题,当设备因为某些原因掉线,则超过一定时间之后,客户端便可收到该消息,进而知道该设备掉线。
- 客户端发起设备发现流程,topic:
/ter/query/discovery
{
"params": {
"code": "0x01",
"param": {}
},
"ecode": 0
}
- 设备端回复发现命令,topic:
/dev/response/discovery
{
"params": {
"code": "0x01",
"sn":"SN-1234",
"param": {
"ip":"192.168.1.100",
"mac":"01:0F:0A:0B"
}
},
"ecode":0
}
- 设备端掉线通知,topic:
/dev/response/will
{
"params": {
"code": "0x11",
"sn": "SN-1234",
"param": {}
},
"ecode":0
}
2.3 摄像机操作
客户端订阅/dev/sn/response
,并通过 /ter/sn/request
发送请求指令,设备端订阅 /ter/sn/request
,并通过/dev/sn/response
返回操作结果,进而实现客户端对设备端的操作。
客户端和设备端通过消息中的code
字段区分不同的操作。
- 设备重启指令,topic:
/ter/sn/request
{
"params": {
"code": "0x21",
"param": {}
},
"ecode":0
}
- 设备重启指令回复,topic:
/dev/sn/response
{
"params": {
"code": "0x21",
"sn": "SN-1234",
"param": {
"action":1 //1,重启;0,不重启
}
},
"ecode":0
}
- 格式化sd卡指令,topic:
/ter/sn/request
{
"params": {
"code": "0x22",
"param": {}
},
"ecode":0
}
- 格式化sd卡指令回复,topic:
/dev/sn/response
{
"params": {
"code": "0x22",
"sn": "SN-1234",
"param": {
"result":1 //1,成功;0,失败
}
},
"ecode":0
}
- 实时截图指令,topic:
/ter/sn/request
{
"params": {
"code": "0x23",
"param": {}
},
"ecode":0
}
- 实时截图回复,topic:
/dev/sn/response
{
"params":{
"code": "0x23",
"sn": "SN-1234",
"param": {
"result":1, //截图结果,1-成功,0-失败
"time":1235,
"pic":"xxxx" //base64编码后的图片字符串,成功时才有
}
},
"ecode":0
}
- 下发截图定时任务,topic:
/ter/sn/request
{
"params":{
"code": "0x24",
"param": {
"total": 3,
"time_list":[
1234,
1244,
1254
]
}
},
"ecode":0
}
- 下发截图定时任务回复,topic:
/dev/sn/response
{
"params": {
"code":"0x24",
"sn": "SN-1234",
"param":{
"result":0
}
}
"ecode":0
}
- 查询截图定时任务,topic:
/ter/sn/request
,sn
为设备的SN号
{
"params": {
"code": "0x25",
"param":{}
}
"ecode":0
}
- 返回查询定时任务结果,topic:
/dev/sn/response
{
"params": {
"code":"0x25",
"sn":"SN-1234",
"param": {
"result":1, 1--查询正常,0--查询出错
"total":3,
"time_list":[
1234,
1244,
1254
]
}
},
"ecode":0
}
- 删除截图定时任务,topic:
/ter/sn/request
{
"params": {
"code": "0x26"
"param":{
"total":3,
"time_list":[
1234,
1244,
1254
]
}
}
"ecode":0
}
- 返回删除定时任务结果,topic:
/dev/sn/response
{
"params": {
"code":"0x26",
"sn":"SN-1234",
"param": {
"result":0
}
},
"ecode":0
}
2.4 摄像机通知
客户端订阅/dev/sn/event
主题,当设备端发生某些事件(目前只有定时拍照事件和按键拍照事件)的时候,向该主题发布一条消息,客户端进而刷新显示。
- 定时/按键照相通知,topic:
/dev/sn/event
,sn
为设备的SN号
{
"params": {
"code":"0xF0",
"sn":"SN-1234",
"param": {
"type":0, //1,定时截图; 2,按键截图
"pic":"xxx"
}
}
"ecode":0
}
演示效果
演示视频:
拍照功能
定时拍照任务
实物图
比赛感悟
本次比赛主要学习了RT-Thread嵌入式操作系统的使用方法,学习了RT-Thread线程创建以及线程通信方法,学习了stm32 dcmi接口获取摄像头图像的方法。
在项目的过程中遇到了很多困,有些解决了,典型的有一下两个:
- ov2640驱动,sdk代码上没有开启dma中断,导致使用sdk源码始终无法获取到图片,困扰了好久,最后不得不去读stm32芯片手册,ov2640模块手册,并参考开源代码,最后找到了问题根源,解决了该问题;
- mqtt不能发送超过512字节的数据,原因在于默认IPC pipo最大只能传512字节,而且ringbuf采用了一个16位的变量作为长度,并且最高位作为一个镜像的标记,进而理论上一次性最大只能传输32768字节数据,仍不足以传输一张照片,最后通过讲ringbuf改为32字节作为长度,讲IPC pipo最大长度设置位65535,成功解决了该问题。
也有一些问题没能解决,最后通过其他手段绕过去了,典型有:
- sqlite数据有问题,不能用,sqlite数据库可以成功的创建数据库和数据表,但是线程一旦进入循环,便不能操作了,包括数据插入和数据表创建,该问题原因未知,最后也没能解决,便没有存储照片索引到本地sd卡;
- sd卡驱动有些问题,在线程中存储的照片过多,便会出现错误的文件,并且后续的照片基本都保存不成功,该问题也未能找到原因,没能解决,便没有存储照片到本地,二是作为照片缓存,只保存一张,传到服务器。
本次比赛让我认识到学无止尽,开源世界需要我们每个人都添砖加瓦,才能变得更好。