前言
針對(duì)傳統(tǒng)超市購(gòu)物車結(jié)賬排隊(duì)時(shí)間長(zhǎng)、付款效率低的問(wèn)題,提出了一種更符合現(xiàn)代社會(huì)人們購(gòu)物方式-基于RFID的自助收銀系統(tǒng)。習(xí)慣了快節(jié)奏生活的人們都會(huì)選擇自助收銀機(jī)結(jié)賬,理由顯而易見(jiàn):自助收銀機(jī)結(jié)賬很方便,幾乎不用排隊(duì),也不用近距離和收銀員接觸,在防疫時(shí)期特別感覺(jué)安心。而且自助結(jié)賬對(duì)每件物品的售價(jià)更是一次清晰地核對(duì),最終需支付合計(jì)購(gòu)物支出自己也更加清晰明了;這兩年來(lái),越來(lái)越多的智能設(shè)備應(yīng)用在我們的生活領(lǐng)域里,為我們的生活提供了很多智能和便利。自助收銀機(jī)從幾年前就陸續(xù)涌入到各地商場(chǎng)、超市、便利店,自去年疫情發(fā)生后自助收銀的需求比例更是呈直線上升趨勢(shì)。自助收銀機(jī)的啟用,不僅節(jié)約了超市的人力開(kāi)支成本,也從根本上提升了超市的購(gòu)物支付效率,在這個(gè)快節(jié)奏的社會(huì)里,智能自助收銀機(jī)也從根本上提升了超市等
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-837932.html
基于Linux C++多線程服務(wù)器 + Qt上位機(jī)開(kāi)發(fā) + STM32 + 8266WIFI的智慧無(wú)人超市項(xiàng)目
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-837932.html
?
技術(shù)棧+硬件選型
Linux c++應(yīng)用編程;
Linux socket編程,多線程編程,實(shí)時(shí)信號(hào)(線程通信)
Qt/C++ 客戶端開(kāi)發(fā);
qml(QtLocation)與c++交互?安卓開(kāi)發(fā);
Mysql 數(shù)據(jù)存儲(chǔ);
C語(yǔ)言下位機(jī)開(kāi)發(fā);
stm32c8t6 下位機(jī)(便宜,夠用,市面價(jià)格10r);
RC522(RFID模塊 SPI協(xié)議) 與白卡通信 獲取卡號(hào);
DTH11 溫濕度采集模塊(單總線協(xié)議,市面價(jià)格 4r);
sg90 舵機(jī)模塊(PWM協(xié)議 市面價(jià)9r );
8226 01-s WIFI模塊(uart協(xié)議 市面價(jià)格5r) 連接 C++ 服務(wù)器;
蜂鳴器? 是為了有白卡與RFID交互聲音
GY-NEO6MV2 GPS模塊(uart協(xié)議 市面價(jià)18)
?
?
總設(shè)計(jì)流程
本次設(shè)計(jì)的->基于RFID的自助收銀系統(tǒng)->設(shè)計(jì)主要支持的功能和程序如下: 本項(xiàng)目一共有五個(gè)程序,Linux C++服務(wù)器,Qt管理員端,Qt客戶結(jié)算端,qml安卓端,單片機(jī)下位機(jī)端,客戶端程序都有服務(wù)器斷開(kāi)自動(dòng)重連(單片機(jī)沒(méi)有,有佬會(huì)的話,我想請(qǐng)教一下)
Linux C++服務(wù)器:
? ? ? ?基于 socket 接口編寫(xiě)server服務(wù)端程序,監(jiān)聽(tīng)8888端口,然后創(chuàng)建Mysql數(shù)據(jù)庫(kù)連接,開(kāi)始監(jiān)聽(tīng)。
? ? ?面向?qū)ο蟪绦蛟O(shè)計(jì)中最重要的一個(gè)概念是繼承,所以我編寫(xiě)了一個(gè)基類mythread,他有一個(gè)純虛函數(shù),參數(shù)為一個(gè)定義的一個(gè)參數(shù)類,包含了數(shù)據(jù)庫(kù)封裝好的對(duì)象,需要服務(wù)的客戶端套接字,還有連接的客戶端網(wǎng)絡(luò)的信息結(jié)構(gòu)體,當(dāng)socket客戶端成功接入時(shí),取出該客戶端套字和網(wǎng)絡(luò)信息結(jié)構(gòu)體和主函數(shù)的數(shù)據(jù)庫(kù)對(duì)象來(lái)填充參數(shù)類,然后服務(wù)器端根據(jù)客戶端發(fā)過(guò)來(lái)的第一個(gè)消息判斷該客戶端是上述哪個(gè)客戶端,然后創(chuàng)建相應(yīng)的派生類,填入該參數(shù)類,然后讓mythread指針去指向這個(gè)子類對(duì)象,發(fā)生動(dòng)多態(tài),此時(shí)運(yùn)行階段時(shí)才確定函數(shù)的入口地址,執(zhí)行派生類中的繼承父類的已經(jīng)實(shí)現(xiàn)的純虛函數(shù),此時(shí)派生類創(chuàng)建一個(gè)線程綁定線程處理函數(shù)去服務(wù)處理該socket套接字傳過(guò)來(lái)的消息。綁定不同的線程處理函數(shù)服務(wù)不同的客戶端程序。一共有四種線程處理函數(shù)服務(wù)上述四種客戶端程序,由四個(gè)不同的基類去綁定。
? ? 本項(xiàng)目的一個(gè)最大的特點(diǎn),難點(diǎn),單片機(jī)只負(fù)責(zé)發(fā)送卡號(hào)給服務(wù)器,本項(xiàng)目單片機(jī)傳過(guò)來(lái)的卡號(hào)有以下走向,注冊(cè)商品,注冊(cè)會(huì)員,結(jié)賬,商品入購(gòu)物車,那么我該怎么知道該卡號(hào)是用來(lái)做什么呢,當(dāng)時(shí)困擾了幾天,然后想到Qt的信號(hào)與槽機(jī)制,聯(lián)想到Linux 也有實(shí)時(shí)信號(hào),還可攜帶參數(shù),該信號(hào)是事件發(fā)生時(shí)對(duì)進(jìn)程的通知機(jī)制,也可以把它稱為軟件中斷,Linux 內(nèi)核定義了 31 個(gè)不同的實(shí)時(shí)信號(hào),信號(hào)編號(hào)范圍為 34~64,我直接拿來(lái)做線程通信,所以現(xiàn)在怎樣把他們聯(lián)系起來(lái)呢,當(dāng)時(shí)我想到互斥啥的想法感覺(jué)不好實(shí)現(xiàn),然后我就想到了一個(gè)方法,就是設(shè)置標(biāo)志位,我設(shè)立了一個(gè)全局變量 int RES=34,讓RES的默認(rèn)值為34,34很熟悉吧,信號(hào)編號(hào)范圍為 34~64,當(dāng)單片機(jī)收到卡號(hào)發(fā)給服務(wù)器時(shí),服務(wù)器直接調(diào)用信號(hào)發(fā)送函數(shù),此時(shí)全局變量標(biāo)志位為34,所以直接發(fā)送34這個(gè)實(shí)時(shí)信號(hào),然后觸發(fā)中斷執(zhí)行這個(gè)信號(hào)綁定處理函數(shù)。我們一共有四個(gè)地方需要用到卡號(hào),資源只有一個(gè),所以當(dāng)需要執(zhí)行某個(gè)用到卡號(hào)的操作時(shí),我先判斷該RES的值,如果該值等于34,代表該讀卡器為空閑,我就更改RES為 35 ,然后下次單片機(jī)發(fā)過(guò)來(lái)卡號(hào),我還是直接調(diào)用發(fā)送信號(hào),此時(shí)標(biāo)志位為35 所以此時(shí)執(zhí)行 35 的信號(hào)處理函數(shù),執(zhí)行完函數(shù),需要將RES 置為默認(rèn)值 34,釋放資源,如果當(dāng)請(qǐng)求資源時(shí)RES不等于 34 代表 讀卡器正在被占用,此時(shí)回復(fù)客戶端一個(gè)讀卡器正忙,以此類推,綁定四個(gè)信號(hào)處理函數(shù),處理這四個(gè)操作請(qǐng)求,一定要釋放改為34。我們添加商品到購(gòu)物車,如果一直點(diǎn)擊按鈕獲取來(lái)獲取資源就會(huì)顯得很笨拙,所以默認(rèn)的 34 信號(hào)處理函數(shù)為添加商品入購(gòu)物車,在沒(méi)有人改變標(biāo)志位的情況下,就是執(zhí)行商品入購(gòu)物車。
? ?本項(xiàng)目,因?yàn)槎鄠€(gè)線程對(duì)數(shù)據(jù)進(jìn)行增刪改查,存在競(jìng)爭(zhēng)冒險(xiǎn),所以在執(zhí)行數(shù)據(jù)庫(kù)增刪改操作時(shí),我加入條 Mysql 事務(wù)操作語(yǔ)句,事務(wù)是一個(gè)原子操作,執(zhí)行增刪改操作前 開(kāi)始事務(wù),執(zhí)行結(jié)束,提交事務(wù)。
? ?總結(jié) : 單片機(jī)的所有數(shù)據(jù)全部轉(zhuǎn)發(fā)給服務(wù)器,服務(wù)器跟據(jù)消息的種類,標(biāo)志位,進(jìn)行處理后,分發(fā)給指定客戶端,完成一系列操作。
主循環(huán)如下:
while (1)
{
myret=server.client_socket();
myret.my_sql=sql_typ;
std::cout<<"new connect !!"<<endl;
std::string str=server.readbuf();
int num;
istringstream a(str);
a >> num;
switch (num)
{
{
case 100001:
Mythread *android_thread=new androidthread;
android_thread->thread_start(myret);
std::cout<<"安卓客戶端連接成功"<<std::endl;
}
break;
case 100101:
{
Mythread *admin_thread=new adminthread;
admin_thread->thread_start(myret);
std::cout<<"PC客戶端連接成功"<<std::endl;
}
break;
case 100111:
{
Mythread *cust_thread=new custthread;
cust_thread->thread_start(myret);
std::cout<<"ARM客戶端連接成功"<<std::endl;
}
break;
case 101001:
{
Mythread *mcu_thread=new mcuthread;
mcu_thread->thread_start(myret);
std::cout<<"STM32客戶端連接成功"<<std::endl;
}
break;
default:
std::cout<<"未知的錯(cuò)誤"<<std::endl;
break;
}
}
?
Qt管理員端:
Qt管理員端具有的功能,注冊(cè)商品,注冊(cè)會(huì)員,充值,查看銷售記錄,日志。
首先連接服務(wù)器成功,自動(dòng)發(fā)指令給服務(wù)器,服務(wù)器從數(shù)據(jù)庫(kù)取出數(shù)據(jù)發(fā)給客戶端,初始化,商品,會(huì)員,服務(wù)器上有一個(gè)文本文件記錄銷售記錄,我給服務(wù)器文本大小做了一個(gè)限制,如果大小大于1M清空文件,清除銷售記錄,客戶端可以通過(guò)點(diǎn)擊按鈕發(fā)送一個(gè)指令,獲取文本內(nèi)容顯示銷售記錄,日志的話就是從服務(wù)器運(yùn)行階段開(kāi)始,對(duì)會(huì)員充值,會(huì)員銷毀,商品添加,商品刪除,都會(huì)直接記錄,服務(wù)器退出自動(dòng)銷毀。
Qt客戶結(jié)算端:
Qt客戶結(jié)算端具有的功能:添加商品入購(gòu)物車,結(jié)算,顯示從服務(wù)器端獲取的溫濕度數(shù)據(jù)。
商品入庫(kù)取出價(jià)格,然后相加,點(diǎn)擊結(jié)算按鈕將總價(jià)格發(fā)給服務(wù)器,然后服務(wù)器判斷標(biāo)志位,如果讀卡器被占用則取消結(jié)賬,反之。此時(shí)標(biāo)志位改變,下次刷的卡將充當(dāng)會(huì)員卡進(jìn)行數(shù)據(jù)比對(duì),余額不足則返回?cái)?shù)據(jù)給客戶端,反之。執(zhí)行完畢釋放資源置為34。
qml安卓端:
qml安卓端具有的功能:地圖顯示當(dāng)前手機(jī)與MCU的位置和距離,在售商品查詢,購(gòu)買記錄查詢,姓名號(hào)碼登錄。
這個(gè)安卓端其實(shí)有點(diǎn)畫(huà)蛇添足的意思,我就是想炫耀一下我的GPS模塊,然后地圖顯示當(dāng)前手機(jī)GPS數(shù)據(jù)與MCU的GPS模塊的距離和位置,功能太單調(diào)了,我就加了一個(gè)在售商品查看功能,然后給Mysql添加了1000大小的varcahr字段,存儲(chǔ)當(dāng)前用戶購(gòu)買記錄,加了一個(gè)登錄界面。
? ?qml的教程挺少,之前學(xué)過(guò)一遍,沒(méi)有及時(shí)鞏固,當(dāng)時(shí)寫(xiě)這個(gè)qml真的炸裂,很多坑。想入門qml也簡(jiǎn)單,學(xué)一下qml與C++交互,信號(hào)與槽,函數(shù)互調(diào)。qml界面的話讓gpt去寫(xiě),百度CV。
STM32單片機(jī)開(kāi)發(fā):
stm32具體的功能:stm32c8t6主控芯片,DTH11溫濕度采集發(fā)送給服務(wù)器客戶端顯示,sg90舵機(jī)模擬開(kāi)門,GY-NEO6MV2 GPS 獲取GPS,8226 01-s 與tcp 服務(wù)器數(shù)據(jù)交互,RC522與白卡交互,蜂鳴器提示刷卡成功。
32這調(diào)試是最惡心的,一般調(diào)試是直接通過(guò)串口打印到電腦,但是串口用來(lái)初始化8266了,問(wèn)題就是這個(gè)8266,當(dāng)時(shí)連接服務(wù)器一直連接不上,我就去找原因,有供電原因,還有at指令的原因,供電最好直接從32vcc上引出來(lái),因?yàn)槲沂歉鶕?jù)客戶端程序連接成功后根據(jù)發(fā)過(guò)來(lái)的第一條數(shù)據(jù)來(lái)創(chuàng)建對(duì)應(yīng)線程服務(wù),不知道為啥這個(gè)32程序按復(fù)位鍵的話8266沒(méi)有從第一條指令開(kāi)始運(yùn)行,然后就創(chuàng)建不了對(duì)應(yīng)線程服務(wù),只能斷電,然后在重新燒錄一次,調(diào)試巨麻煩。
GPS的話也是串口通信,重新初始化一個(gè)串口資源就好了,這個(gè)信號(hào)很差必須在陽(yáng)臺(tái)上,有條件的還是買好一點(diǎn)的吧我采集數(shù)據(jù)還有抱著一大堆線接個(gè)充電寶在陽(yáng)臺(tái)調(diào)試。gps數(shù)據(jù)有個(gè)NMEA協(xié)議,需要對(duì)數(shù)據(jù)解析出經(jīng)緯度,有很多類型數(shù)據(jù),最重要的一條就是包含經(jīng)緯度的?
?
"$GPGLL,2547.35222,N,11306.12283,E,111129.00,A,A*6D";
這是當(dāng)時(shí)當(dāng)時(shí)在陽(yáng)臺(tái)調(diào)試出來(lái)的,這里我偷了個(gè)小懶,因?yàn)閏語(yǔ)言字符串處理很雞肋,所以我直接在32這里過(guò)濾出這條消息,一整條發(fā)給服務(wù)器,然后服務(wù)器發(fā)給Qt客戶端,讓Qt的QString去解分割分析處理,分分鐘的事情,封裝好的庫(kù)就是簡(jiǎn)單
?
很簡(jiǎn)單吧!
8266的話我初始化很隨意,快準(zhǔn)狠,直接配置tcp,連接WiFi然連接服務(wù)器,哈哈。
溫濕度舵機(jī)什么的沒(méi)什么好講,一個(gè)單總線寫(xiě)按時(shí)序拉低拉高電平就行了,一個(gè)設(shè)置指定占空比。RC522的話,驅(qū)動(dòng)很復(fù)雜,我水平不太行,寫(xiě)不出來(lái),直接調(diào)廠家的庫(kù)了。
主循環(huán)就這樣了
?
?
最后留言:
本項(xiàng)目一共寫(xiě)了半個(gè)月,遇到很多坑,當(dāng)時(shí)地圖傳的坐標(biāo)經(jīng)緯度傳犯了,調(diào)試了一下午才發(fā)現(xiàn)了這個(gè)問(wèn)題,還有Linux 實(shí)時(shí)信號(hào) 是一個(gè)軟件中斷嘛,然后當(dāng)時(shí)server的while循環(huán)的accept直接導(dǎo)通解除阻塞了,程序直接崩潰。查了很多資料才知道了,最后加一個(gè)goto語(yǔ)句哈哈就解決了。
ret_client Myserver::client_socket()
{
reboot:
ret_client ret;
m_client_socket= accept(m_socket,(struct sockaddr *)&ret.client_struct,&len);
if(m_client_socket==-1)
{
goto reboot;
}
ret.client_socket=m_client_socket;
return ret;
}
這是sql語(yǔ)句啊,本人挺懶的,全用varchar了。
CREATE DATABASE shopping;
CREATE TABLE users(
id VARCHAR(20) unique key,
name VARCHAR(50),
phone VARCHAR(15) unique key,
balance VARCHAR(25),
text VARCHAR(1000)
)
CREATE TABLE me(
pid VARCHAR(20) unique key,
pname VARCHAR(50),
price VARCHAR(15),
brand VARCHAR(25)
)
需要源碼的哥們?nèi)B加評(píng)論郵箱,直接發(fā)郵箱
?
?
?
到了這里,關(guān)于基于Linux C++多線程服務(wù)器 + Qt上位機(jī)開(kāi)發(fā) + STM32 + 8266WIFI的智慧無(wú)人超市的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!