1. 設計需求、硬件環境介紹
1.1 項目背景
隨著城市化進程的不斷加快,綠化管理成為城市建設和環境保護的重要組成部分。為了實現對城市綠化的智能化管理和優化,決定設計一個基于STM32L4小熊派的云端綠化管理系統,利用華為云IoT物聯網平臺來實現數據的采集、傳輸和分析。
該系統的設備平臺采用小熊派開發板,搭載了意法半導體的低功耗芯片STM32L431。該芯片具有低功耗和高性能的特點,非常適合用于物聯網設備。通過配合外部的專業傳感器,如溫濕度傳感器和光照度傳感器,系統能夠實時獲取空氣中的溫濕度數據和光照度數據。
系統的核心是華為云IoT物聯網平臺,它提供了豐富的功能和服務,包括設備接入、數據采集、消息通信、云端數據存儲和分析等。可以利用該平臺的能力來接收來自小熊派設備的數據,并進行云端的數據分析和處理。
當傳感器獲取到空氣中的溫濕度數據和光照度數據后,小熊派將數據通過無線通信發送到華為云IoT物聯網平臺。云平臺接收到數據后,會進行實時的數據分析和處理。根據預設的綠化管理規則和條件,系統可以判斷當前種植區的空氣溫濕度是否適合進行灌溉。
當系統判斷需要進行灌溉時,它可以通過華為云IoT物聯網平臺向指定的灌溉設備發送指令,實現自動灌溉操作。同時,系統還可以將實時的溫濕度數據和光照度數據存儲在云端,以便后續的數據分析和管理。
通過這個云端綠化管理系統,可以實現對城市綠化的智能化管理和優化。通過實時監測和分析空氣溫濕度和光照度數據,系統可以根據植物的生長需求進行精確的灌溉控制,提高綠化效果,節約水資源。同時,利用華為云IoT物聯網平臺的強大功能,可以實現設備的遠程管理和數據的實時監控,提高綠化管理的效率和便捷性。
本項目利用STM32L4小熊派設計基于華為云IoT物聯網平臺的云端綠化管理系統,通過實時監測和控制空氣溫濕度和光照度,實現智能化的綠化管理和優化,為城市綠化工作提供科技支持,促進城市的可持續發展和環境保護。
1.2 實現功能
本項目利用意法半導體的STM32L431微控制器和ESP8266 WiFi模塊,配合華為云物聯網平臺服務器,構建了一個微型綠化管理系統。該系統包括六個功能模塊:
- 基礎系統模塊:負責接收和轉發各種數據,并控制掃水作業的進行。澆水作業是通過板載電機進行模擬控制。
- 溫度采集模塊:用于采集監測區域的溫度數據,并將數據傳輸給微控制器。
- 濕度采集模塊:用于采集監測區域的濕度數據,并將數據傳輸給微控制器。
- 光照采集模塊:用于采集監測區域的光照數據,并將數據傳輸給微控制器。
- 無線傳感器網絡模塊:負責將采集到的數據上傳至云平臺,并進行數據下發和交互等操作。
- OLED顯示屏模塊:實時顯示監測到的各項數據。
當前項目選用了ESP8266作為無線WiFi通信模塊,因其價格低廉、支持串口編程、提供完善的AT指令資料等特點,非常適合學習使用。通過對ESP8266的編程實驗,可以學習TCP和MQTT網絡編程相關知識。
本項目通過整合各個功能模塊,實現了對溫度、濕度和光照等數據的采集,并利用這些數據判斷是否需要進行灌溉。同時,通過無線傳感器網絡模塊將數據上傳至云平臺,并在OLED顯示屏上實時展示監測到的數據。這個綠化管理系統為學習者提供了一個理想的實踐平臺,可以加深對TCP、MQTT和物聯網技術的理解和應用。
1.3 設備實物圖
小熊開發板的設備相關實物圖如下:
2. 創建IOT服務器端產品
2.1 創建產品
直接打開物聯網產品頁面: https://www.huaweicloud.com/product/iothub.html
打開產品頁面,選擇右上角創建產品。
根據自己情況填寫信息。
創建成功后打開產品詳情頁面,拉到最下面,點擊創建自定義模型文件。
這里創建模型文件主要就是為了MQTT客戶端能夠正確的上傳傳感器數據上來,每個傳感器設置一個屬性,這個屬性就是表示了傳感器的數據值類型。
比如: 先添加一個電機,這個電機就是澆水電機,能上報開關狀態,云端也能下發命令控制電機,所以需要添加屬性和下發的命令。
添加屬性:
添加命令: 因為電機需要云端遠程控制。
接下來就創建溫度、濕度、光照度傳感器的屬性,這些傳感器只是向云端上傳數據,不需要下發指令控制,所以只創建屬性就行了。
創建完畢效果,一共有4個屬性,電機、溫度、濕度、光強度:
2.2 創建設備
選擇設備頁面,注冊設備。
創建后保持設備密匙等信息,接下來登錄服務器時,生成MQTT賬號密匙需要用到這些參數。
當前創建的設備信息如下:
{
"device_id": "61cd1d97078a93029b84e7b6_1126626497",
"secret": "1126626497"
}
2.3 生成MQTT登錄賬號信息
官微提供的在線小工具: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
按照提示填入數據,生成,非常方便。
當前生成的信息如下:
ClientId 61cd1d97078a93029b84e7b6_1126626497_0_0_2021123003
Username 61cd1d97078a93029b84e7b6_1126626497
Password b219f3a0099fa0284a2671a5c699b67a7cf6d5f7355d9ee8190011f3b64f71b5
3. 使用MQTT客戶端模擬測試
為了驗證服務器配置是否OK,先使用MQTT客戶端軟件進行連接測試。
3.1 華為云IOT服務器地址與端口
端口: 1883
域名: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
IP地址: 121.36.42.100
3.2 訂閱主題
在產品頁面,可以看到主題管理頁面,能看到當前設備可以訂閱的主題有哪些。
一般訂閱下發的數據:
格式: $oc/devices/{device_id}/sys/messages/down
//訂閱主題: 平臺下發消息給設備
$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/messages/down
3.3 上報主題數據
官方文檔介紹: https://support.huaweicloud.com/devg-iothub/iot_01_2127.html
服務ID,屬性ID在產品頁面查看,2.1小節創建產品里就講了這個屬性的作用。
每次可以單個屬性上報,也可以一起上報。
格式: $oc/devices/{device_id}/sys/properties/report
//設備上報主題請求
$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/properties/report
?
//上報的數據格式如下
//電機開狀態反饋
{"services": [{"service_id": "motor","properties":{"motor":1}}]}
?
//電機關狀態反饋
{"services": [{"service_id": "motor","properties":{"motor":0}}]}
?
//溫度上報
{"services": [{"service_id": "motor","properties":{"SHT30_H":14}}]}
?
//濕度上報
{"services": [{"service_id": "motor","properties":{"SHT30_L":70}}]}
?
//光照強度上報
{"services": [{"service_id": "motor","properties":{"BH1750":80}}]}
?
//也可以一起上報
{"services": [{"service_id": "motor","properties":{"motor":1}},{"service_id": "motor","properties":{"SHT30_H":15}},{"service_id": "motor","properties":{"SHT30_L":70}},{"service_id": "motor","properties":{"BH1750":80}}]}
3.4 登錄服務器
按照軟件提示,填入相關數據即可。
如需要也需要使用和我一樣的同款軟件,打開百度搜索MQTT客戶端_v2.4(協議3.1.1).exe
即可找到下載地址。
發送數據后查看云端,已經登錄成功,數據已經上傳成功。
3.5 下發命令
電機設備支持讀寫,支持下發命令,在設備頁面測試。
點擊確定之后,參看MQTT客戶端軟件,已經收到了下發的數據。
len:174,Data:l$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/commands/request_id=390ce15d-6e69-4021-b83a-5e953eea874c{"paras":{"motor":1},"service_id":"motor","command_name":"motor"}
4. 設備端上華為云IOT
工程代碼:
工程代碼較多,這里就貼出main.c全部代碼:
項目源碼:https://download.csdn.net/download/xiaolong1126626497/81993720
#include "main.h"
#include "stm32l4xx_hal.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "E53_IA1.h"
#include "lcd.h"
#include "spi.h"
#include "mqtt.h"
#include "esp8266.h"
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
void SystemClock_Config(void);
#define ESP8266_WIFI_AP_SSID "CMCC-Cqvn" //將要連接的路由器名稱 --不要出現中文、空格等特殊字符
#define ESP8266_AP_PASSWORD "99pu58cb" //將要連接的路由器密碼
//華為云IOT物聯網服務器的設備信息
#define MQTT_ClientID "61cd1d97078a93029b84e7b6_1126626497_0_0_2021123003"
#define MQTT_UserName "61cd1d97078a93029b84e7b6_1126626497"
#define MQTT_PassWord "b219f3a0099fa0284a2671a5c699b67a7cf6d5f7355d9ee8190011f3b64f71b5"
//訂閱與發布的主題
#define SET_TOPIC "$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/messages/down" //訂閱
#define POST_TOPIC "$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/properties/report" //發布
//保存溫濕度、光照強度
E53_IA1_Data_TypeDef E53_IA1_Data;
//顯示文本
char lcd_text_str[50];
UART_HandleTypeDef at_usart;
//低功耗串口初始化
int32_t at_usart_init(void)
{
at_usart.Instance = LPUART1;
at_usart.Init.BaudRate = 115200;
at_usart.Init.WordLength = UART_WORDLENGTH_8B;
at_usart.Init.StopBits = UART_STOPBITS_1;
at_usart.Init.Parity = UART_PARITY_NONE;
at_usart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
at_usart.Init.Mode = UART_MODE_RX | UART_MODE_TX;
if(HAL_UART_Init(&at_usart) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
// __HAL_UART_CLEAR_FLAG(usart, UART_FLAG_TC);
__HAL_UART_ENABLE_IT(&at_usart, UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&at_usart, UART_IT_RXNE);
HAL_NVIC_EnableIRQ(LPUART1_IRQn); //使能USART1中斷通道
HAL_NVIC_SetPriority(LPUART1_IRQn, 3, 3); //搶占優先級3,子優先級3
return 0;
}
unsigned char ESP8266_RecvBuf[MAX_RECV_CNT];
unsigned int ESP8266_Recv_cnt=0;
unsigned int ESP8266_Recv_flag=0;
void LPUART1_IRQHandler()
{
//接收到數據
if(__HAL_UART_GET_FLAG(&at_usart, UART_FLAG_RXNE) != RESET)
{
if(ESP8266_Recv_cnt< MAX_RECV_CNT-1)
{
ESP8266_RecvBuf[ESP8266_Recv_cnt++] = (uint8_t)(at_usart.Instance- >RDR & 0x00FF);
}
else
{
ESP8266_Recv_flag=1;
}
}
else if (__HAL_UART_GET_FLAG(&at_usart, UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&at_usart);
ESP8266_Recv_flag=1;
}
}
void AT_SendData(unsigned char *p,unsigned int len)
{
int i=0;
for(i=0;i< len;i++)
{
while((LPUART1- >ISR & 0X40) == 0); //循環發送,直到發送完畢
LPUART1- >TDR = p[i];
}
}
char mqtt_message[200];
int main(void)
{
int i=0;
int cnt=0;
int motor_state=0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI2_Init();
MX_USART1_UART_Init();
at_usart_init();
//初始化硬件
Init_E53_IA1();
LCD_Init();
LCD_Clear(BLACK);//清屏為黑色
LCD_ShowString(0, 00, 240, 32, 32, "Init ESP8266");//顯示字符串,字體大小32*32
if(ESP8266_Init())
{
printf("ESP8266硬件檢測錯誤.n");
LCD_Clear(BLACK);//清屏為黑色
LCD_ShowString(0, 00, 240, 32, 32, "ESP8266 ERROR");//顯示字符串,字體大小32*32
}
else
{
LCD_Clear(BLACK);//清屏為黑色
LCD_ShowString(0, 00, 240, 32, 32, "ESP8266 OK");//顯示字符串,字體大小32*32
printf("準備連接到指定的服務器.n");
//非加密端口
printf("WIFI:%drn",ESP8266_STA_TCP_Client_Mode(ESP8266_WIFI_AP_SSID,ESP8266_AP_PASSWORD,"106.55.124.154",1883,1));
}
//2. MQTT協議初始化
MQTT_Init();
//3. 連接華為云IOT服務器
while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord))
{
printf("服務器連接失敗,正在重試...n");
HAL_Delay(500);
}
printf("服務器連接成功.n");
//3. 訂閱主題
if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
{
printf("主題訂閱失敗.n");
}
else
{
printf("主題訂閱成功.n");
}
while (1)
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)//查詢按鍵KEY1低電平
{
HAL_Delay(10);//消抖
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)//查詢按鍵KEY1低電平
{
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);//亮
//補光燈亮
HAL_GPIO_WritePin(IA1_Light_GPIO_Port, IA1_Light_Pin, GPIO_PIN_SET);
//電機轉
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
motor_state=1;
}
}
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)//查詢按鍵KEY2低電平
{
HAL_Delay(10);//消抖
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)//查詢按鍵KEY2低電平
{
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);//滅
//補光燈滅
HAL_GPIO_WritePin(IA1_Light_GPIO_Port, IA1_Light_Pin, GPIO_PIN_RESET);
//電機停
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_RESET);
motor_state=0;
}
}
cnt++;
HAL_Delay(10);
if(cnt >=100)
{
cnt=0;
E53_IA1_Read_Data();
printf("光照強度:%d %%rn", (int)E53_IA1_Data.Lux);
printf("濕度:%d %%rn",(int)E53_IA1_Data.Humidity);
printf("溫度:%d ℃rn", (int)E53_IA1_Data.Temperature);
sprintf(lcd_text_str,"L: %d %%",(int)E53_IA1_Data.Lux);
LCD_ShowString(40, 50+10+32*1, 240, 32, 32,lcd_text_str);
sprintf(lcd_text_str,"H: %d %%",(int)E53_IA1_Data.Humidity);
LCD_ShowString(40, 50+10+32*2, 240, 32, 32,lcd_text_str);
sprintf(lcd_text_str,"T: %d C",(int)E53_IA1_Data.Temperature);
LCD_ShowString(40, 50+10+32*3, 240, 32, 32,lcd_text_str);
//切換引腳的狀態
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
//上傳數據
sprintf(mqtt_message,"{"services": [{"service_id": "motor","properties":{"motor":%d}},"
"{"service_id": "motor","properties":{"SHT30_H":%d}},{"service_id": "motor","properties":"
"{"SHT30_L":%d}},{"service_id": "motor","properties":{"BH1750":%d}}]}",
motor_state,(int)E53_IA1_Data.Humidity,(int)E53_IA1_Data.Temperature,(int)E53_IA1_Data.Lux);
MQTT_PublishData(POST_TOPIC,mqtt_message,0);
//根據濕度自動灌溉
if((int)E53_IA1_Data.Humidity< 50) //小于50自動灌溉
{
printf("自動灌溉....n");
motor_state=1; //電機狀態更新
//電機轉
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
}
}
//接收到數據
if(ESP8266_Recv_flag)
{
//如果是下發了屬性,判斷是開鎖還是關鎖
if(ESP8266_Recv_cnt >5)
{
ESP8266_RecvBuf[ESP8266_Recv_cnt]='?';
//使用字符串查找函數
if(strstr((char*)&ESP8266_RecvBuf[5],""machine":1"))
{
motor_state=1; //電機狀態更新
//電機轉
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
printf("開啟電機...n");
}
else if(strstr((char*)&ESP8266_RecvBuf[5],""machine":0"))
{
//電機停
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_RESET);
motor_state=0;
printf("關閉電機...n");
}
for(i=0;i< ESP8266_Recv_cnt;i++)printf("%c",ESP8266_RecvBuf[i]);
ESP8266_Recv_cnt=0;
}
ESP8266_Recv_flag=0;
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 40;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @param file: The file name as string.
* @param line: The line in file as a number.
* @retval None
*/
void _Error_Handler(char *file, int line)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %drn", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
審核編輯:湯梓紅
-
嵌入式
+關注
關注
5125文章
19438瀏覽量
313047 -
STM32
+關注
關注
2283文章
10986瀏覽量
361270 -
TCP
+關注
關注
8文章
1395瀏覽量
80116 -
開發板
+關注
關注
25文章
5389瀏覽量
100869 -
IOT
+關注
關注
187文章
4268瀏覽量
200025 -
華為云
+關注
關注
3文章
2753瀏覽量
18006
發布評論請先 登錄
相關推薦
華為聯合合作伙伴發布一站式物聯網IoT開發工具小熊派BearPi,解決行業物聯網產品開發痛點
HC2019,華為聯合合作伙伴發布一站式物聯網IoT開發工具小熊派BearPi,解決行業物聯網產品開發痛點
小熊派華為物聯網操作系統LiteOS內核教程01-IoT-Studio介紹及安裝
【小熊派IOT開發板試用連載】基于小熊派的智控路燈
【小熊派IOT開發板試用連載】基于小熊派IOT開發板車載定位系統開發
【小熊派IoT開發板試用連載】--環境搭建
【小熊派IOT開發板試用連載】5、華為IOT Booster應用開發
開發實踐丨用小熊派STM32開發板模擬自動售貨機 精選資料推薦
開發實踐丨用小熊派STM32開發板模擬自動售貨機 精選資料分享
小熊派ART-PIFPGA核心板
基于STM32L431設計的云端綠化管理系統(ESP8266+阿里云物聯網平臺)

評論