動手實踐丨手把手教你用STM32做一個智能魚缸
摘要:本文基於STM32單片機設計了一款基於物聯網的智能魚缸。
本文分享自華為雲社區《基於STM32+華為雲IOT設計的物聯網魚缸【玩轉華為雲】》,作者: DS小龍哥 。
1. 前言
為了緩解學習、生活、工作帶來的壓力,提升生活品質,許多人喜歡在家中、辦公室等場所養魚。為節省魚友時間、勞力、增加養魚樂趣;為此,本文基於STM32單片機設計了一款基於物聯網的智能魚缸。該魚缸可以實現水溫檢測、水質檢測、自動或手動換水、氛圍燈燈光變換和自動或手動餵食等功能為一體的控制系統,魚缸通過ESP8266連接華為雲IOT物聯網平台,並通過應用側接口開發了上位機APP實現遠程對魚缸參數檢測查看,並能遠程控制。
從功能上分析,需要用到的硬件如下:
(1)STM32系統板
(2)水溫溫度檢測傳感器: 測量水溫
(3)水質檢測傳感器: 測量水中的溶解性固體含量,反應水質。
(4)步進電機: 作為魚飼料投食器
(5)RGB氛圍燈: 採用RGB 3色燈,給魚缸照明。
(6)抽水電動馬達: 用來給魚缸充氧,換水,加水等。
(7)ESP8266 WIFI:設置串口協議的WIFI,內置了TCP/IP協議棧,完善的AT指令,通過簡單的指令就可以聯網通信,但是當前採用的ESP8266沒有燒寫第三方固件,採用原本的原滋原味的官方固件,沒有內置MQTT協議,代碼里連接華為雲物聯網平台需要使用MQTT協議,所以在STM32代碼里通過MQTT協議文檔的字段結構自己實現了MQTT協議,在通過ESP8266的TCP相關的AT指令完成數據發送接收,完成與華為雲IOT平台交互。
水產養殖水質常規檢測的傳感器有哪些?水產養殖水質常規檢測的傳感器有水質ph傳感器、溶解氧傳感器和溫度傳感器。
(1)水質ph傳感器:
ph傳感器是高智能化在線連續監測儀,由傳感器和二次表兩部分組成。可配三複合或兩複合電極,以滿足各種使用場所。配上純水和超純水電極,可適用於電導率小於3μs/cm的水質(如化學補給水、飽和蒸氣、凝結水等)的pH值測量。
(2)溶解氧傳感器:
氧氣的消耗量與存在的氧含量成正比,而氧是通過可透膜擴散進來的。傳感器與專門設計的監測溶氧的測量電路或電腦數據採集系統相連。 溶解氧傳感器能夠空氣校準,一般校準所需時間較長,在使用後要注意保養。如果在養殖水中工作時間過長,就必須定期地清洗膜,對其進行額外保養。
在很多水產養殖中,每天測幾次溶氧就可以了解溶氧情況。對池塘和許多水槽養殖系統。溶氧水平不會變化很快,池塘一般每天檢測2~3次。 對於較高密度養殖系統,增氧泵故障發生可能不到1h就會造成魚蝦等大面積死亡。這些密度高的養殖系統要求有足夠多的裝備或每小時多次自動測量溶氧。
(3)溫度傳感器:
溫度傳感器有多種結構,包括熱電偶、電阻溫度傳感器和熱敏電阻。熱電偶技術成熟,應用領域廣,貨源充足。選擇熱電偶必須滿足溫度範圍要求,且其材料與環境相容。 電阻溫度傳感器(RTDs)的原理為金屬的電阻隨溫度的改變而改變。大多電阻溫度傳感器(RTDs)由鉑、鎳或鎳合金製成,其線性度比熱電偶好,熱切更加穩定,但容易破碎。 熱敏電阻是電阻與溫度具有負相關關係的半導體。熱敏電阻比RTD和熱電偶更靈敏,也更容易破碎,不能承受大的溫差,但這一點在水產養殖中不成問題。
2. 硬件選型
2.1 STM32開發板
主控CPU採用STM32F103RCT6,這顆芯片包括48 KB SRAM、256 KB Flash、2個基本定時器、4個通用定時器、2個高級定時器、51個通用IO口、5個串口、2個DMA控制器、3個SPI、2個I2C、1個USB、1個CAN、3個12位ADC、1個12位DAC、1個SDIO接口,芯片屬於大容量類型,配置較高,整體符合硬件選型設計。當前選擇的這款開發板自帶了一個1.4寸的TFT-LCD彩屏,可以顯示當前傳感器數據以及一些運行狀態信息。
2.2 杜邦線
2.3 PCB板
2.4 步進電機
2.5 抽水馬達
2.6 水溫檢測傳感器
測溫採用DS18B20,DS18B20是常用的數字溫度傳感器,其輸出的是數字信號,具有體積小,硬件開銷低,抗干擾能力強,精度高的特點。
DS18B20數字溫度傳感器接線方便,封裝成後可應用於多種場合,如管道式,螺紋式,磁鐵吸附式,不鏽鋼封裝式,型號多種多樣,有LTM8877,LTM8874等等。
主要根據應用場合的不同而改變其外觀。封裝後的DS18B20可用於電纜溝測溫,高爐水循環測溫,鍋爐測溫,機房測溫,農業大棚測溫,潔凈室測溫,彈藥庫測溫等各種非極限溫度場合。耐磨耐碰,體積小,使用方便,封裝形式多樣,適用於各種狹小空間設備數字測溫和控制領域。
2.7 水質檢測傳感器
TDS (Total Dissolved Solids)、中文名總溶解固體、又稱溶解性固體、又稱溶解性固體總量、表明1升水腫容有多少毫克溶解性固體、一般來說、TDS值越高、表示水中含有溶解物越多、水就越不潔凈、雖然在特定情況下TDS並不能有效反映水質的情況、但作為一種可快速檢測的參數、TDS目前還可以作為有效的在水質情況反映參數來作為參考。常用的TDS檢測設備為TDS筆、雖然價格低廉、簡單易用、但不能把數據傳給控制系統、做長時間的在線監測、並做水質狀況分析、使用專門的儀器、雖然能傳數據、精度也高、但價格很貴、為此這款TDS傳感器模塊、即插即用、使用簡單方便、測量用的激勵源採用交流信號、可有效防止探頭極化、延長探頭壽命的同時、也增加了輸出信號的穩定性、TDS探頭為防水探頭、可長期侵入水中測量、該產品可以應用於生活用水、水培等領域的水質檢測、有了這個傳感器、可輕鬆DIY–套TDS檢測儀了、輕鬆檢測水的潔凈程度。
2.8 ESP8266
■模塊採用串口(LVTTL) 與MCU (或其他串口設備) 通信,內置TCP/IP協議棧,能夠實現串口與WIFI之間的轉換
■模塊支持LVTTL串口, 兼容3…3V和5V單片機系統
■模塊支持串 口轉WIFI STA、串口轉AP和WIFI STA+WIFI AP的模式,從而快速構建串口-WIFI數據傳輸方案
■模塊小巧(19mm*29mm), 通過6個2.54mm間距排針與外部連接
3. 華為雲IOT產品與設備創建
3.1 創建產品
鏈接://www.huaweicloud.com/product/iothub.html
點擊右上角窗口創建產品。
填入產品信息。
接下來創建模型文件:
創建服務。
創建屬性。根據魚缸設備的傳感器屬性來添加屬性。
(1)LED氛圍燈
(2)抽水電機
(3)水質傳感器
(4)水溫溫度計
3.2 創建設備
地址: //console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/device/all-device
點擊右上角創建設備。
按照設備的情況進行填寫信息。
設備創建後保存信息:
{ "device_id": "62cd6da66b9813541d510f64_dev1", "secret": "12345678" }
創建成功。
3.3 設備模擬調試
為了測試設備通信的過程,在設備頁面點擊調試。
選擇設備調試:
3.4 MQTT三元組
為了方便能夠以真實的設備登陸服務器進行測試,接下來需要先了解MQTT協議登錄需要的參數如何獲取,得到這些參數才可以接着進行下一步。
MQTT(Message Queuing Telemetry Transport)是一個基於客戶端-服務器的消息發佈/訂閱傳輸協議,主要應用於計算能力有限,且工作在低帶寬、不可靠的網絡的遠程傳感器和控制設備,適合長連接的場景,如智能路燈等。
MQTTS是MQTT使用TLS加密的協議。採用MQTTS協議接入平台的設備,設備與物聯網平台之間的通信過程,數據都是加密的,具有一定的安全性。
採用MQTT協議接入物聯網平台的設備,設備與物聯網平台之間的通信過程,數據沒有加密,如果要保證數據的私密性可以使用MQTTS協議。
在這裡可以使用華為雲提供的工具快速得到MQTT三元組進行登錄。
//support.huaweicloud.com/devg-iothub/iot_01_2127.html#ZH-CN_TOPIC_0240834853__zh-cn_topic_0251997880_li365284516112
工具的頁面地址:
//iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
根據提示填入信息,然後生成三元組信息即可。 這裡填入的信息就是在創建設備的時候生成的信息。
DeviceId 62cd6da66b9813541d510f64_dev1 DeviceSecret 12345678 ClientId 62cd6da66b9813541d510f64_dev1_0_0_2022071609 Username 62cd6da66b9813541d510f64_dev1 Password a23fb6db6b5bc428971d5ccf64cc8f7767d15ca63bd5e6ac137ef75d175c77bf
3.5 平台接入地址
華為雲的物聯網服務器地址在這裡可以獲取:
//console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home
MQTT (1883) a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
對應的IP地址是: 121.36.42.100
3.6 MQTT的主題訂閱與發佈格式
得到三元組之後,就可以登錄MQTT服務器進行下一步的主題發佈與訂閱。
主題的格式詳情:
//support.huaweicloud.com/api-iothub/iot_06_v5_3004.html
設備消息上報 $oc/devices/{device_id}/sys/messages/up 平台下發消息給設備 $oc/devices/{device_id}/sys/messages/down 上傳的消息格式: { "services": [{ "service_id": "Connectivity", "properties": { "dailyActivityTime": 57 }, "event_time": "20151212T121212Z" }, { "service_id": "Battery", "properties": { "batteryLevel": 80 }, "event_time": "20151212T121212Z" } ] }
根據當前設備的格式總結如下:
ClientId 62cd6da66b9813541d510f64_dev1_0_0_2022071609 Username 62cd6da66b9813541d510f64_dev1 Password a23fb6db6b5bc428971d5ccf64cc8f7767d15ca63bd5e6ac137ef75d175c77bf //訂閱主題: 平台下發消息給設備 $oc/devices/62cd6da66b9813541d510f64_dev1/sys/messages/down //設備上報數據 $oc/devices/62cd6da66b9813541d510f64_dev1/sys/properties/report //上報的屬性消息 (一次可以上報多個屬性,在json里增加就行了) {"services": [{"service_id": "fish","properties":{"LED":1}},{"service_id": "fish","properties":{"motor":1}},{"service_id": "fish","properties":{"水溫":36.2}}]}
3.7 MQTT客戶端模擬設備調試
得到信息之後,將參賽填入軟件進行登錄測試。
數據發送之後,在設備頁面上可以看到設備已經在線了,並且收到了上傳的數據。
4. STM32程序設計
4.1 硬件連線
硬件連接方式: 1. TFT 1.44 寸彩屏接線 GND 電源地 VCC 接5V或3.3v電源 SCL 接PC8(SCL) SDA 接PC9(SDA) RST 接PC10 DC 接PB7 CS 接PB8 BL 接PB11 2. 板載LED燈接線 LED1---PA8 LED2---PD2 3. 板載按鍵接線 K0---PA0 K1---PC5 K2---PA15 4. DS18B20溫度傳感器接線 DQ->PC6 + : 3.3V - : GND 5. 步進電機 ULN2003控制28BYJ-48步進電機接線: ULN2003接線: IN-D: PB15 d IN-C: PB14 c IN-B: PB13 b IN-A: PB12 a + : 5V - : GND 6. 抽水電機 GND---GND VCC---5V AO----PA4 7. 水質檢測傳感器 AO->PA1 + : 3.3V - : GND 8. RGB燈 PC13--R PC14--G PC15--B 9. ATK-ESP8266 WIFI接線 PA2(TX)--RXD 模塊接收腳 PA3(RX)--TXD 模塊發送腳 GND---GND 地 VCC---VCC 電源(3.3V~5.0V)
4.2 硬件原理圖
4.3 漢字取模
4.4 程序下載
下載軟件在資料包里。點擊開始編程之後,點擊開發板的複位鍵即可下載程序進去。
4.5 主要的信息連接代碼
#include "stm32f10x.h" #include "led.h" #include "delay.h" #include "key.h" #include "usart.h" #include <string.h> #include "timer.h" #include "esp8266.h" #include "mqtt.h" #include "oled.h" #include "fontdata.h" #include "bh1750.h" #include "iic.h" #include "sht3x.h" #define ESP8266_WIFI_AP_SSID "aaa" //將要連接的路由器名稱 --不要出現中文、空格等特殊字符 #define ESP8266_AP_PASSWORD "12345678" //將要連接的路由器密碼 //華為雲服務器的設備信息 #define MQTT_ClientID "62cd6da66b9813541d510f64_dev1_0_0_2022071609" #define MQTT_UserName "62cd6da66b9813541d510f64_dev1" #define MQTT_PassWord "a23fb6db6b5bc428971d5ccf64cc8f7767d15ca63bd5e6ac137ef75d175c77bf" //訂閱與發佈的主題 #define SET_TOPIC "$oc/devices/62cd6da66b9813541d510f64_dev1/sys/messages/down" //訂閱 #define POST_TOPIC "$oc/devices/62cd6da66b9813541d510f64_dev1/sys/properties/report" //發佈
4.6 ESP8266主要代碼
u8 ESP8266_IP_ADDR[16]; //255.255.255.255 u8 ESP8266_MAC_ADDR[18]; //硬件地址 /* 函數功能: ESP8266命令發送函數 函數返回值:0表示成功 1表示失敗 */ u8 ESP8266_SendCmd(char *cmd) { int RX_CNT=0; 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_STA&0X8000) { RX_CNT=USART3_RX_STA&0x7FFF; USART3_RX_BUF[RX_CNT]='\0'; USART3_RX_STA=0; if(strstr((char*)USART3_RX_BUF,"OK")) { return 0; } } } } return 1; } /* 函數功能: ESP8266硬件初始化檢測函數 函數返回值:0表示成功 1表示失敗 */ u8 ESP8266_Init(void) { //退出透傳模式 USARTx_StringSend(USART3,"+++"); delay_ms(100); //退出透傳模式 USARTx_StringSend(USART3,"+++"); delay_ms(100); 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_BUF,"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_BUF,"APMAC"); if(p) { p+=7; for(i=0;*p!='"';i++) { ESP8266_MAC_ADDR[i]=*p++; } ESP8266_MAC_ADDR[i]='\0'; } //打印總體信息 printf("當前WIFI模式:AP+TCP服務器\r\n"); printf("當前WIFI熱點名稱:%s\r\n",ssid); printf("當前WIFI熱點密碼:%s\r\n",pass); printf("當前TCP服務器端口號:%d\r\n",port); printf("當前TCP服務器IP地址:%s\r\n",ESP8266_IP_ADDR); printf("當前TCP服務器MAC地址:%s\r\n",ESP8266_MAC_ADDR); return 0; } /* 函數功能: TCP服務器模式下的發送函數 發送指令: */ u8 ESP8266_ServerSendData(u8 id,u8 *data,u16 len) { int RX_CNT=0; 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_STA&0X8000) { RX_CNT=USART3_RX_STA&0x7FFF; USART3_RX_BUF[RX_CNT]='\0'; USART3_RX_STA=0; if(strstr((char*)USART3_RX_BUF,">")) { //繼續發送數據 USARTx_DataSend(USART3,data,len); //等待數據發送成功 for(n=0;n<200;n++) { delay_ms(50); if(USART3_RX_STA&0X8000) { RX_CNT=USART3_RX_STA&0x7FFF; USART3_RX_BUF[RX_CNT]='\0'; USART3_RX_STA=0; if(strstr((char*)USART3_RX_BUF,"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_BUF,">"))) { return 11; } //如果想要退出發送: "+++" } printf("WIFI模式:STA+TCP客戶端\r\n"); printf("Connect_WIFI熱點名稱:%s\r\n",ssid); printf("Connect_WIFI熱點密碼:%s\r\n",pass); printf("TCP服務器端口號:%d\r\n",port); printf("TCP服務器IP地址:%s\r\n",ip); return 0; } /* 函數功能: TCP客戶端模式下的發送函數 發送指令: */ u8 ESP8266_ClientSendData(u8 *data,u16 len) { int RX_CNT=0; 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_STA&0X8000) { RX_CNT=USART3_RX_STA&0x7FFF; USART3_RX_BUF[RX_CNT]='\0'; USART3_RX_STA=0; if(strstr((char*)USART3_RX_BUF,">")) { //繼續發送數據 USARTx_DataSend(USART3,data,len); //等待數據發送成功 for(n=0;n<200;n++) { delay_ms(50); if(USART3_RX_STA&0X8000) { RX_CNT=USART3_RX_STA&0x7FFF; USART3_RX_BUF[RX_CNT]='\0'; USART3_RX_STA=0; if(strstr((char*)USART3_RX_BUF,"SEND OK")) { return 0; } } } } } } } return 1; }