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