基於STM32+華為雲IOT設計智能稱重系統
摘要:選擇部署多個重量傳感器和必要的算法、通過WiFi 通信模塊、GPS定位模塊,採集車輛稱重數據一地理位置信息,並通過網絡發送至雲平台,設計圖形化UI界面展示稱重、地圖位置等重要信息,實現對稱重系統的遠程監測。
本文分享自華為雲社區《基於STM32+華為雲IOT設計智能稱重系統》,作者:DS小龍哥
伴隨着網絡技術,各種通訊技術,傳感器技術的飛速發展,物聯網技術成為了當今技術領域發展為迅速的技術。而物聯網技術的核心仍然是以互聯網技術為基礎的,物聯網是新一代信息技術的重要組成部分,也是信息化時代的重要發展階段。物聯網通過智能感知、識別技術與普適計算等通信感知技術,廣泛應用於網絡的融合中,也因此被稱為繼計算機、互聯網之後世界信息產業發展的第三次浪潮。
本設計的模型來源於物流、礦山、高速公路等場合,車輛稱重地螃的智能化升級要求,設計基於物聯網的智能在線稱重方案,開發智能稱重控制器,合理選擇部署多個重量傳感器和必要的算法、通過WIFF通信模塊、GPS定位模塊,採集車輛重數據一地理位置信息,並通過網絡發送至雲平台,設計圖形化UI界面展示稱重、地圖位置等重要信息,實現對稱重系統的遠程監測。
隨着物聯網技術的逐步發展和日趨成熟,物聯網技術是一個大而廣的應用技術,並非僅僅局限於延伸應用。相信對地磅來說必然會有更多創新的應用實踐。總的來說,地磅現代化、信息化、智能化一定緊隨物聯網技術的發展,而物聯網技術的發展也必將促使地磅興起新的技術革命。
1.設計的技術與硬件選項總結:
(1)雲端通信模塊採用ESP8266-WIFI
(2)聯網通信模塊採用:ESP8266
(3)GPS模塊:採用ATGM336H雙模GPS模塊
(4)電子秤模塊:用於稱重
(5)物聯網雲平台:採用華為雲物聯網平台
設計總結:
(1)採用ESP8266連接OneNet上傳稱重數據和GPS數據到雲端(採用HTTP協議)
雲端上顯示2個數據:GPS定位數據–地圖顯示,稱重傳感器的數據值
(2)3個稱重傳感器接一個秤面稱重計算平均值
(3)本地OLED顯示屏顯示GPS經緯度數據、稱重傳感器的數據值。
(4)OLED設計一個頁面顯示並設置當前的報警上限。通過按鍵進行加減
當稱重的閥值超出了設置閥值,蜂鳴器報警。
2. 硬件選型
2.1 STM32F103C8T6
STM32F103C8T6是一款基於ARM Cortex-M 內核STM32系列的32位的微控制器,程序存儲器容量是64KB,需要電壓2V~3.6V,工作溫度為-40°C ~ 85°C。
2.2 電子秤傳感器
HX711 是一款專為高精度稱重傳感器而設計的24位A/D 轉換器芯片。
2.3 ESP8266-wifi
2.4 GPS模塊
2.5 蜂鳴器
3. 創建雲端產品與設備
3.1 創建產品
地址://www.huaweicloud.com/?locale=zh-cn
3.2 創建設備
地址: //console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/device/all-device
3.3 自定義模型數據
鏈接://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product/7211833377cf435c8c0580de390eedbe/product-detail/6277d70223aaf461a0f72a56
這個模型數據就是設備要上傳的數據。
{ "device_id": "6277d70223aaf461a0f72a56_weigh", "secret": "12345678" } 服務ID: weigh 屬性名稱 數據類型 訪問方式 描述 weigh int(整型) 可讀 重量 GPS string(字符串) 可讀 GPS定位信息
3.4 MQTT密匙生成
創建完產品、設備之後,接下來就需要知道如何通過MQTT協議登陸華為雲服務器。
屬性上報格式:
//support.huaweicloud.com/api-iothub/iot_06_v5_3010.html
MQTT設備登陸密匙生成地址: //iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
DeviceId 6277d70223aaf461a0f72a56_weigh DeviceSecret 12345678 ClientId 6277d70223aaf461a0f72a56_weigh_0_0_2022050814 Username 6277d70223aaf461a0f72a56_weigh Password 0a3d097c6449b8526a562006a74c8c5e61ce63d6c831ea291560736a3332cf77
華為雲物聯網平台的域名是: 161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
華為雲物聯網平台的IP地址是:121.36.42.100
在軟件里參數填充正確之後,就看到設備已經連接成功了。
接下來打開設備頁面,可以看到設備已經在線了。
3.5 主題訂閱與發佈
//訂閱主題: 平台下發消息給設備 $oc/devices/6277d70223aaf461a0f72a56_weigh/sys/messages/down //設備上報數據 $oc/devices/6277d70223aaf461a0f72a56_weigh/sys/properties/report //上報的屬性消息 (一次可以上報多個屬性,在json里增加就行了) {"services": [{"service_id": "weigh","properties":{"GPS":"lat:12.345,lng:45.678"}}]}
通過MQTT客戶端軟件模擬上報測試:
查看控制台頁面,數據已經上傳成功了。
3.6 應用側開發
為了更方便的展示設備數據,與設備完成交互,還需要開發一個配套的上位機,官方提供了應用側開發的API接口、SDK接口,為了方便通用一點,我這裡採用了API接口完成數據交互,上位機軟件採用QT開發。
幫助文檔地址: //support.huaweicloud.com/api-iothub/iot_06_v5_0034.html
設備屬性就是設備上傳的傳感器狀態數據信息,應用側提供了API接口,可以主動向設備端下發請求指令;設備端收到指令之後需要按照約定的數據格式上報數據;所以,要實現應用層與設備端的數據交互,需要應用層與設備端配合才能完成。
在使用接口時,最好先使用華為自己的調試接口測試。
//apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ListProperties
上位機軟件採用Qt框架設計,Qt是一個跨平台的C++圖形用戶界面應用程序框架。Qt是一個1991年由Qt Company開發的跨平台C++圖形用戶界面應用程序開發框架。它既可以開發GUI程序,也可用於開發非GUI程序,比如控制台工具和服務器。簡單來說,QT可以很輕鬆的幫你做帶界面的軟件,甚至不需要你投入很大精力。
QT官網: //www.qt.io/
4. STM32設備端開發
4.1 程序下載
4.2 原理圖
4.3 硬件接線
(1)OLED顯示屏接線: D0----(SCK)------------------->>PB14 D1----(MOSI)------------------>>PB13 RES—(複位腳低電平有效)-------->>PB12 DC--(數據和命令控制管腳)------>>PB1 CS--(片選引腳)---------------->>PA7 GND--------------------------->>GND VCC--------------------------->>3.3V或者5V (2)ATK-ESP8266 WIFI接線 PA2(TX)--RXD 模塊接收腳 PA3(RX)--TXD 模塊發送腳 GND---GND 地 VCC---VCC 電源(3.3V~5.0V) (3)外接蜂鳴器模塊: 高電平響 BEEP----->PB8 (4)外接按鍵: KEY1 -PB3 按下是低電平 清零 KEY2 -PB2 按下是低電平 翻頁 KEY3 -PB6 按下是低電平 加 KEY4 -PB7 按下是低電平 減 (5)外接LED燈模塊: LED1-PB4 低電平亮 LED2-PB5 低電平亮 (6)稱重傳感器1 VCC--->5V SCK--->PA4 時序控制腳--對STM32--輸出模式 DT---->PA5 輸出輸出腳-對STM32--輸入模式 GND--->GND (7)稱重傳感器2 VCC--->5V SCK--->PA11 時序控制腳--對STM32--輸出模式 DT---->PA12 輸出輸出腳-對STM32--輸入模式 GND--->GND (8)稱重傳感器3 VCC--->5V SCK--->PA6 時序控制腳--對STM32--輸出模式 DT---->PA8 輸出輸出腳-對STM32--輸入模式 GND--->GND (9)GPS模塊接線說明 GND----GND VCC---3.3V PB11----GPS_TX PB10----GPS_RX (--)板載LED燈:低電平亮 LED1--PC13 BEEP2--PC14 (--)板載按鍵: KEY1--PA0 按下為高電平
4.4 MQTT連接代碼
#include "stm32f10x.h" #include "led.h" #include "delay.h" #include "key.h" #include "usart.h" #include <string.h> #include "timer.h" #include "bluetooth.h" #include "esp8266.h" #include "mqtt.h" //華為物聯網服務器的設備信息 #define MQTT_ClientID "61b9ba3a2b2aa20288c1e7f1_QQ1126626497_0_0_2021121510" #define MQTT_UserName "61b9ba3a2b2aa20288c1e7f1_QQ1126626497" #define MQTT_PassWord "385ce91dfe7da5b7431868d5d87e7998163c493344040935d5a00024d6324242" //訂閱與發佈的主題 #define SET_TOPIC "$oc/devices/61b9ba3a2b2aa20288c1e7f1_QQ1126626497_0_0_2021121510/sys/messages/down" //訂閱 #define POST_TOPIC "$oc/devices/61b9ba3a2b2aa20288c1e7f1_QQ1126626497_0_0_2021121510/sys/properties/report" //發佈 char mqtt_message[200];//上報數據緩存區 int main() { u32 time_cnt=0; u32 i; u8 key; LED_Init(); BEEP_Init(); KEY_Init(); USART1_Init(115200); TIMER1_Init(72,20000); //超時時間20ms USART2_Init(9600);//串口-藍牙 TIMER2_Init(72,20000); //超時時間20ms USART3_Init(115200);//串口-WIFI TIMER3_Init(72,20000); //超時時間20ms USART1_Printf("正在初始化WIFI請稍等.\n"); if(ESP8266_Init()) { USART1_Printf("ESP8266硬件檢測錯誤.\n"); } else { //非加密端口 USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode("CMCC-Cqvn","99pu58cb","121.36.42.100",1883,1)); } //2. MQTT協議初始化 MQTT_Init(); //3. 連接華為服務器 while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord)) { USART1_Printf("服務器連接失敗,正在重試...\n"); delay_ms(500); } USART1_Printf("服務器連接成功.\n"); //3. 訂閱主題 if(MQTT_SubscribeTopic(SET_TOPIC,0,1)) { USART1_Printf("主題訂閱失敗.\n"); } else { USART1_Printf("主題訂閱成功.\n"); } .........
4.5 ESP8266代碼
#include "esp8266.h" u8 ESP8266_IP_ADDR[16]; //255.255.255.255 u8 ESP8266_MAC_ADDR[18]; //硬件地址 /* 函數功能: ESP8266命令發送函數 函數返回值:0表示成功 1表示失敗 */ u8 ESP8266_SendCmd(char *cmd) { u8 i,j; for(i=0;i<10;i++) //檢測的次數--發送指令的次數 { USARTx_StringSend(USART3,cmd); for(j=0;j<100;j++) //等待的時間 { delay_ms(50); if(USART3_RX_FLAG) { USART3_RX_BUFFER[USART3_RX_CNT]='\0'; USART3_RX_FLAG=0; USART3_RX_CNT=0; if(strstr((char*)USART3_RX_BUFFER,"OK")) { return 0; } } } } return 1; } /* 函數功能: ESP8266硬件初始化檢測函數 函數返回值:0表示成功 1表示失敗 */ u8 ESP8266_Init(void) { //退出透傳模式 USARTx_StringSend(USART3,"+++"); delay_ms(50); return ESP8266_SendCmd("AT\r\n"); } /* 函數功能: 一鍵配置WIFI為AP+TCP服務器模式 函數參數: char *ssid 創建的熱點名稱 char *pass 創建的熱點密碼 (最少8位) u16 port 創建的服務器端口號 函數返回值: 0表示成功 其他值表示對應錯誤值 */ u8 ESP8266_AP_TCP_Server_Mode(char *ssid,char *pass,u16 port) { char *p; u8 i; char ESP8266_SendCMD[100]; //組合發送過程中的命令 /*1. 測試硬件*/ if(ESP8266_SendCmd("AT\r\n"))return 1; /*2. 關閉回顯*/ if(ESP8266_SendCmd("ATE0\r\n"))return 2; /*3. 設置WIFI模式*/ if(ESP8266_SendCmd("AT+CWMODE=2\r\n"))return 3; /*4. 複位*/ ESP8266_SendCmd("AT+RST\r\n"); delay_ms(1000); delay_ms(1000); delay_ms(1000); /*5. 關閉回顯*/ if(ESP8266_SendCmd("ATE0\r\n"))return 5; /*6. 設置WIFI的AP模式參數*/ sprintf(ESP8266_SendCMD,"AT+CWSAP=\"%s\",\"%s\",1,4\r\n",ssid,pass); if(ESP8266_SendCmd(ESP8266_SendCMD))return 6; /*7. 開啟多連接*/ if(ESP8266_SendCmd("AT+CIPMUX=1\r\n"))return 7; /*8. 設置服務器端口號*/ sprintf(ESP8266_SendCMD,"AT+CIPSERVER=1,%d\r\n",port); if(ESP8266_SendCmd(ESP8266_SendCMD))return 8; /*9. 查詢本地IP地址*/ if(ESP8266_SendCmd("AT+CIFSR\r\n"))return 9; //提取IP地址 p=strstr((char*)USART3_RX_BUFFER,"APIP"); if(p) { p+=6; for(i=0;*p!='"';i++) { ESP8266_IP_ADDR[i]=*p++; } ESP8266_IP_ADDR[i]='\0'; } //提取MAC地址 p=strstr((char*)USART3_RX_BUFFER,"APMAC"); if(p) { p+=7; for(i=0;*p!='"';i++) { ESP8266_MAC_ADDR[i]=*p++; } ESP8266_MAC_ADDR[i]='\0'; } //打印總體信息 USART1_Printf("當前WIFI模式:AP+TCP服務器\n"); USART1_Printf("當前WIFI熱點名稱:%s\n",ssid); USART1_Printf("當前WIFI熱點密碼:%s\n",pass); USART1_Printf("當前TCP服務器端口號:%d\n",port); USART1_Printf("當前TCP服務器IP地址:%s\n",ESP8266_IP_ADDR); USART1_Printf("當前TCP服務器MAC地址:%s\n",ESP8266_MAC_ADDR); return 0; } /* 函數功能: TCP服務器模式下的發送函數 發送指令: */ u8 ESP8266_ServerSendData(u8 id,u8 *data,u16 len) { u8 i,j,n; char ESP8266_SendCMD[100]; //組合發送過程中的命令 for(i=0;i<10;i++) { sprintf(ESP8266_SendCMD,"AT+CIPSEND=%d,%d\r\n",id,len); USARTx_StringSend(USART3,ESP8266_SendCMD); for(j=0;j<10;j++) { delay_ms(50); if(USART3_RX_FLAG) { USART3_RX_BUFFER[USART3_RX_CNT]='\0'; USART3_RX_FLAG=0; USART3_RX_CNT=0; if(strstr((char*)USART3_RX_BUFFER,">")) { //繼續發送數據 USARTx_DataSend(USART3,data,len); //等待數據發送成功 for(n=0;n<200;n++) { delay_ms(50); if(USART3_RX_FLAG) { USART3_RX_BUFFER[USART3_RX_CNT]='\0'; USART3_RX_FLAG=0; USART3_RX_CNT=0; if(strstr((char*)USART3_RX_BUFFER,"SEND OK")) { return 0; } } } } } } } return 1; } /* 函數功能: 配置WIFI為STA模式+TCP客戶端模式 函數參數: char *ssid 創建的熱點名稱 char *pass 創建的熱點密碼 (最少8位) char *p 將要連接的服務器IP地址 u16 port 將要連接的服務器端口號 u8 flag 1表示開啟透傳模式 0表示關閉透傳模式 函數返回值:0表示成功 其他值表示對應的錯誤 */ u8 ESP8266_STA_TCP_Client_Mode(char *ssid,char *pass,char *ip,u16 port,u8 flag) { char ESP8266_SendCMD[100]; //組合發送過程中的命令 //退出透傳模式 //USARTx_StringSend(USART3,"+++"); //delay_ms(50); /*1. 測試硬件*/ if(ESP8266_SendCmd("AT\r\n"))return 1; /*2. 關閉回顯*/ if(ESP8266_SendCmd("ATE0\r\n"))return 2; /*3. 設置WIFI模式*/ if(ESP8266_SendCmd("AT+CWMODE=1\r\n"))return 3; /*4. 複位*/ ESP8266_SendCmd("AT+RST\r\n"); delay_ms(1000); delay_ms(1000); delay_ms(1000); /*5. 關閉回顯*/ if(ESP8266_SendCmd("ATE0\r\n"))return 5; /*6. 配置將要連接的WIFI熱點信息*/ sprintf(ESP8266_SendCMD,"AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,pass); if(ESP8266_SendCmd(ESP8266_SendCMD))return 6; /*7. 設置單連接*/ if(ESP8266_SendCmd("AT+CIPMUX=0\r\n"))return 7; /*8. 配置要連接的TCP服務器信息*/ sprintf(ESP8266_SendCMD,"AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",ip,port); if(ESP8266_SendCmd(ESP8266_SendCMD))return 8; /*9. 開啟透傳模式*/ if(flag) { if(ESP8266_SendCmd("AT+CIPMODE=1\r\n"))return 9; //開啟 if(ESP8266_SendCmd("AT+CIPSEND\r\n"))return 10; //開始透傳 if(!(strstr((char*)USART3_RX_BUFFER,">"))) { return 11; } //如果想要退出發送: "+++" } //打印總體信息 USART1_Printf("當前WIFI模式:STA+TCP客戶端\n"); USART1_Printf("當前連接的WIFI熱點名稱:%s\n",ssid); USART1_Printf("當前連接的WIFI熱點密碼:%s\n",pass); USART1_Printf("當前連接的TCP服務器端口號:%d\n",port); USART1_Printf("當前連接的TCP服務器IP地址:%s\n",ip); return 0; } /* 函數功能: TCP客戶端模式下的發送函數 發送指令: */ u8 ESP8266_ClientSendData(u8 *data,u16 len) { u8 i,j,n; char ESP8266_SendCMD[100]; //組合發送過程中的命令 for(i=0;i<10;i++) { sprintf(ESP8266_SendCMD,"AT+CIPSEND=%d\r\n",len); USARTx_StringSend(USART3,ESP8266_SendCMD); for(j=0;j<10;j++) { delay_ms(50); if(USART3_RX_FLAG) { USART3_RX_BUFFER[USART3_RX_CNT]='\0'; USART3_RX_FLAG=0; USART3_RX_CNT=0; if(strstr((char*)USART3_RX_BUFFER,">")) { //繼續發送數據 USARTx_DataSend(USART3,data,len); //等待數據發送成功 for(n=0;n<200;n++) { delay_ms(50); if(USART3_RX_FLAG) { USART3_RX_BUFFER[USART3_RX_CNT]='\0'; USART3_RX_FLAG=0; USART3_RX_CNT=0; if(strstr((char*)USART3_RX_BUFFER,"SEND OK")) { return 0; } } } } } } } return 1; }