基于STM32L4小熊派设计的云端绿化管理系统(华为云IOT)

描述

1. 设计需求、硬件环境介绍

1.1 项目背景

随着城市化进程的不断加快,绿化管理成为城市建设和环境保护的重要组成部分。为了实现对城市绿化的智能化管理和优化,决定设计一个基于STM32L4小熊派的云端绿化管理系统,利用华为云IoT物联网平台来实现数据的采集、传输和分析。

该系统的设备平台采用小熊派开发板,搭载了意法半导体的低功耗芯片STM32L431。该芯片具有低功耗和高性能的特点,非常适合用于物联网设备。通过配合外部的专业传感器,如温湿度传感器和光照度传感器,系统能够实时获取空气中的温湿度数据和光照度数据。

系统的核心是华为云IoT物联网平台,它提供了丰富的功能和服务,包括设备接入、数据采集、消息通信、云端数据存储和分析等。可以利用该平台的能力来接收来自小熊派设备的数据,并进行云端的数据分析和处理。

当传感器获取到空气中的温湿度数据和光照度数据后,小熊派将数据通过无线通信发送到华为云IoT物联网平台。云平台接收到数据后,会进行实时的数据分析和处理。根据预设的绿化管理规则和条件,系统可以判断当前种植区的空气温湿度是否适合进行灌溉。

当系统判断需要进行灌溉时,它可以通过华为云IoT物联网平台向指定的灌溉设备发送指令,实现自动灌溉操作。同时,系统还可以将实时的温湿度数据和光照度数据存储在云端,以便后续的数据分析和管理。

通过这个云端绿化管理系统,可以实现对城市绿化的智能化管理和优化。通过实时监测和分析空气温湿度和光照度数据,系统可以根据植物的生长需求进行精确的灌溉控制,提高绿化效果,节约水资源。同时,利用华为云IoT物联网平台的强大功能,可以实现设备的远程管理和数据的实时监控,提高绿化管理的效率和便捷性。

本项目利用STM32L4小熊派设计基于华为云IoT物联网平台的云端绿化管理系统,通过实时监测和控制空气温湿度和光照度,实现智能化的绿化管理和优化,为城市绿化工作提供科技支持,促进城市的可持续发展和环境保护。

1.2 实现功能

本项目利用意法半导体的STM32L431微控制器和ESP8266 WiFi模块,配合华为云物联网平台服务器,构建了一个微型绿化管理系统。该系统包括六个功能模块:

  1. 基础系统模块:负责接收和转发各种数据,并控制扫水作业的进行。浇水作业是通过板载电机进行模拟控制。
  2. 温度采集模块:用于采集监测区域的温度数据,并将数据传输给微控制器。
  3. 湿度采集模块:用于采集监测区域的湿度数据,并将数据传输给微控制器。
  4. 光照采集模块:用于采集监测区域的光照数据,并将数据传输给微控制器。
  5. 无线传感器网络模块:负责将采集到的数据上传至云平台,并进行数据下发和交互等操作。
  6. OLED显示屏模块:实时显示监测到的各项数据。

当前项目选用了ESP8266作为无线WiFi通信模块,因其价格低廉、支持串口编程、提供完善的AT指令资料等特点,非常适合学习使用。通过对ESP8266的编程实验,可以学习TCP和MQTT网络编程相关知识。

本项目通过整合各个功能模块,实现了对温度、湿度和光照等数据的采集,并利用这些数据判断是否需要进行灌溉。同时,通过无线传感器网络模块将数据上传至云平台,并在OLED显示屏上实时展示监测到的数据。这个绿化管理系统为学习者提供了一个理想的实践平台,可以加深对TCP、MQTT和物联网技术的理解和应用。

1.3 设备实物图

小熊开发板的设备相关实物图如下:

IOT

IOT

IOT

IOT

2. 创建IOT服务器端产品

2.1 创建产品

直接打开物联网产品页面: https://www.huaweicloud.com/product/iothub.html

IOT

打开产品页面,选择右上角创建产品。

IOT

根据自己情况填写信息。

IOT

创建成功后打开产品详情页面,拉到最下面,点击创建自定义模型文件。

IOT

这里创建模型文件主要就是为了MQTT客户端能够正确的上传传感器数据上来,每个传感器设置一个属性,这个属性就是表示了传感器的数据值类型。

比如: 先添加一个电机,这个电机就是浇水电机,能上报开关状态,云端也能下发命令控制电机,所以需要添加属性和下发的命令。

IOT

添加属性:

IOT

添加命令: 因为电机需要云端远程控制。

IOT

接下来就创建温度、湿度、光照度传感器的属性,这些传感器只是向云端上传数据,不需要下发指令控制,所以只创建属性就行了。

IOT

IOT

IOT

创建完毕效果,一共有4个属性,电机、温度、湿度、光强度:

IOT

2.2 创建设备

选择设备页面,注册设备。

IOT

创建后保持设备密匙等信息,接下来登录服务器时,生成MQTT账号密匙需要用到这些参数。

IOT

当前创建的设备信息如下:

{
     "device_id": "61cd1d97078a93029b84e7b6_1126626497",
     "secret": "1126626497"
 }

2.3 生成MQTT登录账号信息

官微提供的在线小工具: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

按照提示填入数据,生成,非常方便。

IOT

当前生成的信息如下:

ClientId 61cd1d97078a93029b84e7b6_1126626497_0_0_2021123003
 Username 61cd1d97078a93029b84e7b6_1126626497
 Password b219f3a0099fa0284a2671a5c699b67a7cf6d5f7355d9ee8190011f3b64f71b5

3. 使用MQTT客户端模拟测试

为了验证服务器配置是否OK,先使用MQTT客户端软件进行连接测试。

3.1 华为云IOT服务器地址与端口

IOT

端口: 1883
 域名: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
 IP地址: 121.36.42.100

3.2 订阅主题

在产品页面,可以看到主题管理页面,能看到当前设备可以订阅的主题有哪些。

IOT

一般订阅下发的数据:

格式: $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

IOT

服务ID,属性ID在产品页面查看,2.1小节创建产品里就讲了这个属性的作用。

IOT

每次可以单个属性上报,也可以一起上报。

格式: $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 即可找到下载地址。

IOT

发送数据后查看云端,已经登录成功,数据已经上传成功。

IOT

3.5 下发命令

电机设备支持读写,支持下发命令,在设备页面测试。

IOT

IOT

点击确定之后,参看MQTT客户端软件,已经收到了下发的数据。

IOT

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

工程代码:

IOT

IOT

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 */

审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分