【RT-Thread 作品秀】基于RT-Thread的網(wǎng)絡(luò)照相機(jī)
作者:吳頂頂
概述
隨著科技的進(jìn)步和互聯(lián)網(wǎng)的發(fā)展,基于物聯(lián)網(wǎng)的可拍照設(shè)備也越來越多的融入到人們的生活中來,例如在超市中,管理者利用拍照設(shè)備定時抓取貨架照片,分析貨物狀態(tài),并補(bǔ)充、優(yōu)化貨物擺放;在酒吧里,管理者會利用拍照設(shè)備定時抓拍酒架照片,傳送到網(wǎng)絡(luò)平臺供大眾瀏覽,以招攬更多顧客。本網(wǎng)絡(luò)照相機(jī)基于STM32H7+RTThread平臺,采集攝像頭數(shù)據(jù),并通過無線網(wǎng)絡(luò)傳送到服務(wù)器,提供SD卡配網(wǎng)、手動拍攝、定時拍攝、照片推送等功能,并提供windows上位機(jī)提供控制和照片顯示功能。
主要功能有:
格式化sd卡:格式化sd卡,但是會保留網(wǎng)絡(luò)配置文件,其他文件全部刪除
設(shè)備重啟:重啟設(shè)備
實時拍照:發(fā)送指令給照相機(jī),照相機(jī)拍照,并把照片回傳
定時拍照:照相機(jī)依據(jù)下發(fā)的拍照時間,在時間到達(dá)時拍攝一張照片,并傳給服務(wù)器
按鍵拍照:點(diǎn)擊板上用戶按鈕,拍攝一張照片,并傳給服務(wù)器
定時任務(wù):可以新建/刪除/查詢定時拍照任務(wù),任務(wù)存儲在sd卡中,重啟有效
開發(fā)環(huán)境
硬件:ART-PI(STM32H750主控)+ OV2640模組
RT-Thread版本:4.0.3
SDK 版本:1.0.1
開發(fā)工具及版本:RT-Thread Studio 1.1.5, Qt5.14.0
RT-Thread使用情況概述
內(nèi)核部分:調(diào)度器,信號量,互斥鎖,內(nèi)存管理
調(diào)度器:多任務(wù)調(diào)度
信號量:用于喚醒對應(yīng)任務(wù)
互斥鎖:用于互斥資源獨(dú)占訪問
內(nèi)存管理:動態(tài)內(nèi)存申請與釋放
組件部分:虛擬文件系統(tǒng),IPC,I2C,RTC,NTP
虛擬文件系統(tǒng):文件操作,sd卡、照片文件
IPC:mqtt發(fā)送數(shù)據(jù)需要
I2C:配置攝像頭模塊需要
RTC和NTP:同步時間
軟件包部分:paho mqtt,cJSON,netutils
pahomqtt:用于和服務(wù)器通信
cJSON:解析、封裝mqtt消息
netutils:NTP網(wǎng)絡(luò)對時
其他:base64
用于將圖片文件轉(zhuǎn)換成字符串,便于mqtt傳輸
硬件框架
總體的硬件框架如下圖所示:
本網(wǎng)絡(luò)攝像機(jī)硬件結(jié)果較為簡單,即art-pi連接一個攝像頭模組,art-pi板上用到了AP6212無線模塊,外部內(nèi)存,led指示燈,和sd卡。其中,攝像頭模塊用于采集圖像信號;AP6212用于和服務(wù)器進(jìn)行通信;因一張圖像數(shù)據(jù)量較大,片內(nèi)內(nèi)存不夠,故而使用外部內(nèi)存;led燈用于指示設(shè)備工作狀態(tài);sd卡用于保存網(wǎng)絡(luò)、服務(wù)器、和定時任務(wù)配置。
軟件框架說明
整體的軟件框架如下圖所示,網(wǎng)絡(luò)照相機(jī)內(nèi)部有一個proxy線程,負(fù)責(zé)和云端進(jìn)行通信,在接收到云端消息后會解析,并分發(fā)到其他的線程執(zhí)行,然后將執(zhí)行結(jié)果返回到云端;照相機(jī)發(fā)生了其他的事件,例如用戶按鍵拍照,也會將數(shù)據(jù)傳給proxy線程,proxy線程再將數(shù)據(jù)發(fā)送到云端。用戶通過上位機(jī)終端軟件連接上云服務(wù)器,實現(xiàn)與照相機(jī)的通信及控制。
整個系統(tǒng)支持接入多個照相機(jī),如下圖所示,不同的照相機(jī)通過sd卡配置文件中sn進(jìn)行區(qū)分,上位機(jī)軟件可以顯示所有在線的照相機(jī),但同一時間只支持操作一個。
軟件模塊說明
1. 用戶線程創(chuàng)建流程
如下圖所示為用戶線程創(chuàng)建流程
用戶線程作用描述如下:
main:用于創(chuàng)建sd_card 線程,檢測按鍵事件,閃燈;
sd_card:用于管理與sd卡相關(guān)的工作,包括拍照,網(wǎng)絡(luò)配置,定時任務(wù);
network:負(fù)責(zé)聯(lián)網(wǎng),根據(jù)sd卡的配置文件連接到指定的wifi網(wǎng)絡(luò);
proxy:負(fù)責(zé)啟動mqtt,并管理與云端的通信,其他線程都需要通過proxy線程與云端交互數(shù)據(jù);
event:定時任務(wù)和按鍵任務(wù),在定時時間到達(dá)時,或者用戶按鍵時拍攝照片并通過proxy上傳云端。
2. 通信接口及流程
2.1 MQTT訂閱主題
-
設(shè)備向服務(wù)器訂閱主題:
/ter/query/discovery
,用于接收設(shè)備發(fā)現(xiàn)消息
/ter/sn/request
,用于接收針對該設(shè)備的指令,其中sn
為設(shè)備的SN號,下同 -
客戶端向服務(wù)器訂閱主題:
/dev/response/discovery
,用于接收設(shè)備發(fā)現(xiàn)回復(fù)
/dev/response/will
,用于接收設(shè)備遺囑消息
/dev/sn/response
,用于接收設(shè)備操作指令回復(fù)
/dev/sn/event
,用于接收設(shè)備的通知
2.2 設(shè)備發(fā)現(xiàn)
所有的設(shè)備均訂閱/ter/query/discovery
主題,客戶端向該主題發(fā)布發(fā)現(xiàn)消息,所有收到消息的設(shè)備向/dev/response/discovery
回復(fù)一條消息,而客戶端又訂閱了/dev/response/discovery
主題,故而便可以知道哪些設(shè)備在線了。
設(shè)備連上服務(wù)器的時候,會定義一個遺囑消息,主題為/dev/sn/will
,客戶端訂閱了該主題,當(dāng)設(shè)備因為某些原因掉線,則超過一定時間之后,客戶端便可收到該消息,進(jìn)而知道該設(shè)備掉線。
- 客戶端發(fā)起設(shè)備發(fā)現(xiàn)流程,topic:
/ter/query/discovery
{
"params": {
"code": "0x01",
"param": {}
},
"ecode": 0
}
- 設(shè)備端回復(fù)發(fā)現(xiàn)命令,topic:
/dev/response/discovery
{
"params": {
"code": "0x01",
"sn":"SN-1234",
"param": {
"ip":"192.168.1.100",
"mac":"01:0F:0A:0B"
}
},
"ecode":0
}
- 設(shè)備端掉線通知,topic:
/dev/response/will
{
"params": {
"code": "0x11",
"sn": "SN-1234",
"param": {}
},
"ecode":0
}
2.3 攝像機(jī)操作
客戶端訂閱/dev/sn/response
,并通過 /ter/sn/request
發(fā)送請求指令,設(shè)備端訂閱 /ter/sn/request
,并通過/dev/sn/response
返回操作結(jié)果,進(jìn)而實現(xiàn)客戶端對設(shè)備端的操作。
客戶端和設(shè)備端通過消息中的code
字段區(qū)分不同的操作。
- 設(shè)備重啟指令,topic:
/ter/sn/request
{
"params": {
"code": "0x21",
"param": {}
},
"ecode":0
}
- 設(shè)備重啟指令回復(fù),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卡指令回復(fù),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
}
- 實時截圖回復(fù),topic:
/dev/sn/response
{
"params":{
"code": "0x23",
"sn": "SN-1234",
"param": {
"result":1, //截圖結(jié)果,1-成功,0-失敗
"time":1235,
"pic":"xxxx" //base64編碼后的圖片字符串,成功時才有
}
},
"ecode":0
}
- 下發(fā)截圖定時任務(wù),topic:
/ter/sn/request
{
"params":{
"code": "0x24",
"param": {
"total": 3,
"time_list":[
1234,
1244,
1254
]
}
},
"ecode":0
}
- 下發(fā)截圖定時任務(wù)回復(fù),topic:
/dev/sn/response
{
"params": {
"code":"0x24",
"sn": "SN-1234",
"param":{
"result":0
}
}
"ecode":0
}
- 查詢截圖定時任務(wù),topic:
/ter/sn/request
,sn
為設(shè)備的SN號
{
"params": {
"code": "0x25",
"param":{}
}
"ecode":0
}
- 返回查詢定時任務(wù)結(jié)果,topic:
/dev/sn/response
{
"params": {
"code":"0x25",
"sn":"SN-1234",
"param": {
"result":1, 1--查詢正常,0--查詢出錯
"total":3,
"time_list":[
1234,
1244,
1254
]
}
},
"ecode":0
}
- 刪除截圖定時任務(wù),topic:
/ter/sn/request
{
"params": {
"code": "0x26"
"param":{
"total":3,
"time_list":[
1234,
1244,
1254
]
}
}
"ecode":0
}
- 返回刪除定時任務(wù)結(jié)果,topic:
/dev/sn/response
{
"params": {
"code":"0x26",
"sn":"SN-1234",
"param": {
"result":0
}
},
"ecode":0
}
2.4 攝像機(jī)通知
客戶端訂閱/dev/sn/event
主題,當(dāng)設(shè)備端發(fā)生某些事件(目前只有定時拍照事件和按鍵拍照事件)的時候,向該主題發(fā)布一條消息,客戶端進(jìn)而刷新顯示。
- 定時/按鍵照相通知,topic:
/dev/sn/event
,sn
為設(shè)備的SN號
{
"params": {
"code":"0xF0",
"sn":"SN-1234",
"param": {
"type":0, //1,定時截圖; 2,按鍵截圖
"pic":"xxx"
}
}
"ecode":0
}
演示效果
演示視頻:
拍照功能
定時拍照任務(wù)
實物圖
比賽感悟
本次比賽主要學(xué)習(xí)了RT-Thread嵌入式操作系統(tǒng)的使用方法,學(xué)習(xí)了RT-Thread線程創(chuàng)建以及線程通信方法,學(xué)習(xí)了stm32 dcmi接口獲取攝像頭圖像的方法。
在項目的過程中遇到了很多困,有些解決了,典型的有一下兩個:
- ov2640驅(qū)動,sdk代碼上沒有開啟dma中斷,導(dǎo)致使用sdk源碼始終無法獲取到圖片,困擾了好久,最后不得不去讀stm32芯片手冊,ov2640模塊手冊,并參考開源代碼,最后找到了問題根源,解決了該問題;
- mqtt不能發(fā)送超過512字節(jié)的數(shù)據(jù),原因在于默認(rèn)IPC pipo最大只能傳512字節(jié),而且ringbuf采用了一個16位的變量作為長度,并且最高位作為一個鏡像的標(biāo)記,進(jìn)而理論上一次性最大只能傳輸32768字節(jié)數(shù)據(jù),仍不足以傳輸一張照片,最后通過講ringbuf改為32字節(jié)作為長度,講IPC pipo最大長度設(shè)置位65535,成功解決了該問題。
也有一些問題沒能解決,最后通過其他手段繞過去了,典型有:
- sqlite數(shù)據(jù)有問題,不能用,sqlite數(shù)據(jù)庫可以成功的創(chuàng)建數(shù)據(jù)庫和數(shù)據(jù)表,但是線程一旦進(jìn)入循環(huán),便不能操作了,包括數(shù)據(jù)插入和數(shù)據(jù)表創(chuàng)建,該問題原因未知,最后也沒能解決,便沒有存儲照片索引到本地sd卡;
- sd卡驅(qū)動有些問題,在線程中存儲的照片過多,便會出現(xiàn)錯誤的文件,并且后續(xù)的照片基本都保存不成功,該問題也未能找到原因,沒能解決,便沒有存儲照片到本地,二是作為照片緩存,只保存一張,傳到服務(wù)器。
本次比賽讓我認(rèn)識到學(xué)無止盡,開源世界需要我們每個人都添磚加瓦,才能變得更好。