
0.系列教程
- 我用STM32MP1做了個疫情監控平台1—交叉編譯環境搭建
- 我用STM32MP1做了個疫情監控平台2—Qt環境搭建
- 我用STM32MP1做了個疫情監控平台3—疫情監控平台實作
- 我用STM32MP1做了個疫情監控平台4—功能完善界面重新設計
1.前言
之前我使用桌面版本Qt實作了肺炎疫情監控平台:基于Qt的新冠肺炎疫情資料實時監控平台(開源小項目)。既然Qt是跨平台的,正好手裡有一塊米爾科技的YA157C開發闆,那麼能不能在嵌入式平台實作一下呢?
桌面Linux版本的運作效果:
YA157C開發闆實作效果:
2.資料接口的擷取
疫情監控平台的實作,簡單的說,就是資料的展示,而資料從哪裡來呢?
現在很多網際網路公司都做了自己的疫情監控平台,我這裡采用的是騰訊新聞的資料源,資料内容很豐富,也比較穩定。
資料來源:實時更新:新冠肺炎疫情最新動态
接口位址的擷取方法可以參考:基于Qt的新冠肺炎疫情資料實時監控平台(開源小項目)
如果把所有的資料放在一個接口裡,資料量會很大,是以騰訊把資料分成了幾個接口
#包含最新疫情資料、各省市最新資料、其他國家最新資料
https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5
#包含曆史資料
https://view.inews.qq.com/g2/getOnsInfo?name=disease_other
#最新的辟謠資訊
https://vp.fact.qq.com/loadmore?page=0
#辟謠資訊詳情
https://vp.fact.qq.com/miniArtData?id=a2141851348ee5f3772c761e25bb57d7
目前隻顯示了一些基本的資料,是以我們隻使用到了
https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5
這個接口中的
chinaTotal
和
chinaAdd
這兩組資料。
這個接口包括很多資料,全國累計和新增的最新資料,各省市其他國家的最新資料等等。檔案大小大概在160KB,液晶屏是7寸IPS屏,1024x600分辨率的,還是比較大的,可以顯示很多資訊,後續版本會添加更多資料顯示的。
資料格式:
{
"ret": 0,
"data": {
"lastUpdateTime": "2020-03-04 11:12:04",
"chinaTotal": {
"confirm": 80422,
"heal": 49914,
"dead": 2984,
"nowConfirm": 27524,
"suspect": 520,
"nowSevere": 6416
},
"chinaAdd": {
"confirm": 120,
"heal": 2654,
"dead": 38,
"nowConfirm": -2572,
"suspect": -67,
"nowSevere": -390
},
...........其他資料.............
"isShowAdd": true
}
}
3.Qt界面的實作
之前的桌面應用程式中,是使用的是Qt5版本開發的,Qt5自帶QJson解析類,而Qt 4沒有帶QJson。為了适配帶有Qt 4庫的闆子,我使用了第三方JSON解析庫。這裡選擇的是小巧的cJSON解析庫:cJSON download | SourceForge.net
如果你的闆子是Qt 4的庫,那麼程式不用修改,直接交叉編譯運作即可使用。
隻包含兩個檔案:cJSON.c和cJSON.h,把這兩個檔案添加到工程裡就行了。
整個工程代碼也很簡單:GET接口位址,把接收到的資料儲存到本地,調用cJSON解析資料檔案,把解析出的資料顯示,資料檔案删除。代碼可以到文章末尾開源位址擷取。下面介紹幾個關鍵部分代碼的實作:
3.1 JSON資料的解析
//打開儲存的JSON資料檔案,并調用解析函數
void Dialog::parseData(QString filename)
{
QFile file(filename);
if(!file.open(QIODevice::ReadOnly))
{
qDebug() << "file open failed";
return;
}
QByteArray allData = file.readAll();
file.close();
// qDebug() << allData;
getData(allData);
file.remove(); //删除檔案
return;
}
//把資料解析出來并顯示在标簽上
void Dialog::getData(QByteArray str)
{
cJSON *ret_obj;
cJSON *root_obj;
root_obj = cJSON_Parse(str); //建立JSON解析對象,傳回JSON格式是否正确
if (!root_obj)
{
disInfo("JSON format error");
qDebug() << "json format error";
}
else
{
disInfo("json format ok");
qDebug() << "json format ok";
ret_obj = cJSON_GetObjectItem(root_obj, "ret");
if(cJSON_IsNumber(ret_obj))
{
int ret = 1;
ret = ret_obj->valueint;
// qDebug() << ret_obj->valueint;
}
char *data_str = cJSON_GetObjectItem(root_obj, "data")->valuestring;
cJSON *data_obj = cJSON_Parse(data_str);
if(!data_obj)
{
qDebug() << "data json err";
cnt_error++;
QString error = "err:" + QString::number(cnt_error);
ui->lbe_error->setText(error);
}
else
{
qDebug() << "data json ok";
char *lastUpdateTime = cJSON_GetObjectItem(data_obj, "lastUpdateTime")->valuestring;
qDebug() << lastUpdateTime;
ui->lbe_update_time->setText(lastUpdateTime);
cJSON *chinaTotal_obj = cJSON_GetObjectItem(data_obj, "chinaTotal");
int chinaTotal_confirm = cJSON_GetObjectItem(chinaTotal_obj, "confirm")->valueint;
int chinaTotal_heal = cJSON_GetObjectItem(chinaTotal_obj, "heal")->valueint;
int chinaTotal_dead = cJSON_GetObjectItem(chinaTotal_obj, "dead")->valueint;
int chinaTotal_nowConfirm = cJSON_GetObjectItem(chinaTotal_obj, "nowConfirm")->valueint;
int chinaTotal_suspect = cJSON_GetObjectItem(chinaTotal_obj, "suspect")->valueint;
int chinaTotal_nowSevere = cJSON_GetObjectItem(chinaTotal_obj, "nowSevere")->valueint;
ui->lbe_total_confirm->setNum(chinaTotal_confirm);
ui->lbe_total_heal->setNum(chinaTotal_heal);
ui->lbe_total_dead->setNum(chinaTotal_dead);
ui->lbe_total_nowConfirm->setNum(chinaTotal_nowConfirm);
ui->lbe_total_suspect->setNum(chinaTotal_suspect);
ui->lbe_total_nowSevere->setNum(chinaTotal_nowSevere);
cJSON *chinaAdd_obj = cJSON_GetObjectItem(data_obj, "chinaAdd");
int chinaAdd_confirm = cJSON_GetObjectItem(chinaAdd_obj, "confirm")->valueint;
int chinaAdd_heal = cJSON_GetObjectItem(chinaAdd_obj, "heal")->valueint;
int chinaAdd_dead = cJSON_GetObjectItem(chinaAdd_obj, "dead")->valueint;
int chinaAdd_nowConfirm = cJSON_GetObjectItem(chinaAdd_obj, "nowConfirm")->valueint;
int chinaAdd_suspect = cJSON_GetObjectItem(chinaAdd_obj, "suspect")->valueint;
int chinaAdd_nowSevere = cJSON_GetObjectItem(chinaAdd_obj, "nowSevere")->valueint;
lbeDisplay(ui->lbe_add_confirm, chinaAdd_confirm);
lbeDisplay(ui->lbe_add_heal, chinaAdd_heal);
lbeDisplay(ui->lbe_add_dead, chinaAdd_dead);
lbeDisplay(ui->lbe_add_nowConfirm, chinaAdd_nowConfirm);
lbeDisplay(ui->lbe_add_suspect, chinaAdd_suspect);
lbeDisplay(ui->lbe_add_nowSevere, chinaAdd_nowSevere);
}
// cJSON_Delete(ret_obj);
// cJSON_Delete(data_obj);
cJSON_Delete(root_obj);//釋放記憶體
disInfo("更新完成");
cnt_success++;
QString success = "ok:" + QString::number(cnt_success);
ui->lbe_success->setText(success);
}
}
//資料的顯示
void Dialog::lbeDisplay(QLabel *lbe, int num)
{
if(num > 0)
lbe->setText("+" + QString::number(num));
else
lbe->setText(QString::number(num));
}
3.2 擷取本地IP位址
//forexample:192.168.1.111
QString Dialog::GetLocalmachineIP()
{
QString ipAddress;
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
for(QHostAddress &addr : ipAddressesList)
{
// 找到不是本地ip,并且是ipv4協定,并且不是169開頭的第一個位址
if(addr != QHostAddress::LocalHost && addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.toString().startsWith("169"))
{
ipAddress = addr.toString();
break;
}
}
// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
return ipAddress;
}
桌面Linux版本的運作效果:
4.在開發闆上運作Qt程式
如果在桌面運作正常,就可以使用ya157c建構套件來編譯工程,生成可以在開發闆上運作的程式,然後使用scp指令傳輸到開發闆上。
#使用網線把開發闆連接配接上路由器
#使用udhcpc自動擷取IP位址
udhcpc
#檢視擷取到的ip位址
ifconfig
#确認連接配接到網際網路
ping www.baidu.com
#如果有回複資料,說明已經成功連接配接上網際網路
#使用scp指令或共享目錄的方式把可執行檔案傳輸到開發闆上
scp qte_2019_ncov [email protected]:/home/root
#執行程式
./qte_2019_ncov
最終效果
這個版本是上一個版本的,右上角沒有顯示開發闆的IP位址和成功失敗次數統計,最新版本的程式中已經添加了這個功能。
桌面Linux版效果:
5.使用無線子產品聯網
YA157C開發闆已經闆載了一個WiFi & 藍牙模組——AP6212,可以直接連接配接無線網,這樣就不需要使用網線的方式聯網了。
#關閉eth0
ifconfig eth0 down
#啟用wlan0
rfkill unblock wifi
ifconfig wlan0 up
#在目前檔案夾生成WiFi配置檔案
wpa_passphrase "M6_Note" "qwert125" > wifi.conf
#檢視生成的WiFi配置資訊
cat wifi.conf
#加載WiFi配置檔案
wpa_supplicant -B -c wifi.conf -i wlan0
#掃描附近的WiFi資訊
iw dev wlan0 scan | grep SSID
#自動擷取IP位址
udhcpc -i wlan0
#設定DNS
echo "nameserver 114.114.114.114" > /etc/resolv.conf
#連接配接網際網路
iw wlan0 link
#測試網絡連接配接
ping www.wangchaochao.top
為了友善快捷的連接配接WiFi,可以把以上指令寫成一個shell腳本,需要連接配接WiFi時,直接執行這個腳本就可以了。先在本地生成WiFi配置資訊:
connect_wifi.sh腳本檔案内容:
#!/bin/bash
WF_SSID="M6_Note"
WF_PASSWORD="qwert125"
#關閉eth0
ifconfig eth0 down
#使能wlan0
rfkill unblock wifi
ifconfig wlan0 up
#輸出WiFi資訊
echo "WiFi_SSID:$WF_SSID"
echo "WiFi_PASSWORD:$WF_PASSWORD"
#在目前檔案夾生成WiFi配置資訊
wpa_passphrase $WF_SSID $WF_PASSWORD > $WF_SSID.conf
#加載WiFi配置檔案
wpa_supplicant -B -c /home/root/$WF_SSID.conf -i wlan0
#掃描附近的WiFi
iw dev wlan0 scan | grep SSID
#自動擷取IP位址
udhcpc -i wlan0
#配置DNS
echo "nameserver 114.114.114.114" > /etc/resolv.conf
#連接配接網絡
iw wlan0 link
echo "WiFi連接配接成功"
WiFi賬号和密碼修改一下,就可以直接使用了。
6.代碼下載下傳
整個Qt工程代碼已經開源在Github,Qt4/Qt5相容。如果下載下傳速度很慢,可以選擇國内的Gitee速度會快很多。
#Github
https://github.com/whik/qte_2019_ncov
#Gitee
https://gitee.com/whik/qte_2019_ncov
目前界面還比較簡單,7寸的顯示屏可以顯示很多内容,之後會盡量完善界面資訊,歡迎大家關注!
系列教程
- 我用STM32MP1做了個疫情監控平台1—交叉編譯環境搭建
- 我用STM32MP1做了個疫情監控平台2—Qt環境搭建
- 我用STM32MP1做了個疫情監控平台3—疫情監控平台實作
- 我用STM32MP1做了個疫情監控平台4—功能完善界面重新設計