1, Environment introduction
Single chip microcomputer: STM32F103ZET6
Internet access mode: ESP8266 can be used, or other devices can be used instead, as long as TCP protocol is supported. For example: GSM module, wired network card, etc.
Communication protocol with Internet of things cloud platform: Standard MQTT protocol 3.1.1(TCP)
Development software: keil5
Internet of things platform: Tencent IOT Internet of things platform. Tencent's Internet of things platform has more advantages than other manufacturers' Internet of things platforms. Tencent's Internet of things platform can push data to wechat applets. Users can directly use applets to bind devices and complete interaction with devices. Now users basically use wechat, so it is very convenient to use.
Download address of complete code of the project: https://download.csdn.net/download/xiaolong1126626497/18973282





2, Introduction to smart agriculture
Smart agriculture is to apply Internet of things technology to traditional agriculture, use sensors and software to control agricultural production through mobile platform or computer platform, so as to make traditional agriculture more "smart". In addition to precise perception, control and decision-making management, in a broad sense, smart agriculture also includes agricultural e-commerce, food traceability and anti-counterfeiting, agricultural leisure tourism, agricultural information services, etc. The so-called "smart agriculture" is to make full use of the achievements of modern information technology, integrate and apply computer and network technology, Internet of things technology, audio and video technology, 3S technology, wireless communication technology and expert wisdom and knowledge, and realize intelligent management such as agricultural visual remote diagnosis, remote control and disaster early warning.
Smart agriculture is an advanced stage of agricultural production. It integrates emerging Internet, mobile Internet, cloud computing and Internet of things technologies, and relies on various sensor nodes (environmental temperature and humidity, soil moisture, carbon dioxide, image, etc.) and wireless communication networks deployed on the agricultural production site to realize the intelligent perception, intelligent early warning, intelligent decision-making of the agricultural production environment Intelligent analysis and expert online guidance provide precision planting, visual management and intelligent decision-making for agricultural production. "Smart agriculture" is a comprehensive and comprehensive application of cloud computing, sensor network, 3S and other information technologies in agriculture to achieve more complete information-based support, more thorough agricultural information perception, more centralized data resources, wider interconnection, deeper intelligent control and more intimate public services. The integration of "smart agriculture" with modern biotechnology, planting technology and other science and technology is of great significance to the construction of world-class agriculture.
The project uses STM32F103ZET6 + ESP8266 to design an intelligent agricultural management system, which can obtain the temperature and humidity data and illumination data in the air, and judge whether to carry out irrigation according to the air temperature and humidity data in the planting area; The collected temperature, humidity and illumination can be uploaded to Tencent cloud Internet of things platform through ESP8266 + MQTT protocol and pushed to wechat applet for real-time viewing; The motor pumping irrigation can be directly controlled on the applet.
Hardware details:
Main control MCU: stm32f103zet6
Ambient light sensor: BH1750
Temperature and humidity sensor: SHT30
Local OLED display: zhongjingyuan electronics 0.96-inch SPI interface OLED display
Motor: micro DC motor
3, Create Tencent cloud Internet of things platform equipment and configure wechat applet
If you have never used Tencent cloud Internet of things platform before, please see the detailed steps of creation here: https://blog.csdn.net/xiaolong1126626497/article/details/116902653
The following are the key steps to log in to Tencent cloud Internet of things platform and create a smart agriculture device. Some details are not written. Please see the link above for details.
Official website address: https://console.cloud.tencent.com/iotexplorer

















4, Generate the device login information of Tencent Internet of things platform
Logging in using MQTT protocol requires some parameter information, which needs to be generated in the way provided on the official website.

Python sample code:
#!/usr/bin/python # -*- coding: UTF-8 -*- import base64 import hashlib import hmac import random import string import time import sys # Generates a random string of the specified length def RandomConnid(length): return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length)) # Generate parameters required for accessing the Internet of things communication platform def IotHmac(productID, devicename, devicePsk): # 1. Generate connid as a random string to facilitate background positioning connid = RandomConnid(5) # 2. Generate expiration time, which represents the expiration time of the signature. It is a UTF8 string in seconds from 00:00:00 UTC time on January 1, 1970 expiry = int(time.time()) + 30*24*60 * 60 # 3. Generate the clientid part of MQTT in the format ${productid}${devicename} clientid = "{}{}".format(productID, devicename) # 4. Generate the username part of MQTT in the format of ${ClientID}$ {sdkappid};$ {connid};$ {expiry} username = "{};12010126;{};{}".format(clientid, connid, expiry) # 5. Sign the username and generate a token secret_key = devicePsk.encode('utf-8') # convert to bytes data_to_sign = username.encode('utf-8') # convert to bytes secret_key = base64.b64decode(secret_key) # this is still bytes token = hmac.new(secret_key, data_to_sign, digestmod=hashlib.sha256).hexdigest() # 6. Generate the password field according to the rules of the Internet of things communication platform password = "{};{}".format(token, "hmacsha256") return { "clientid" : clientid, "username" : username, "password" : password } if __name__ == '__main__': # The parameters are filled in respectively: Product ID, equipment name and equipment key print(IotHmac("6142CX41XE", "SmartAgriculture","20Y/aAcmj+y6SDDh+ANR9g=="))
The output login parameters are used for MQTT protocol parameters:
{'clientid': '6142CX41XESmartAgriculture', 'username': '6142CX41XESmartAgriculture;12010126;HUA2G;1624271589', 'password': 'a8aadebe9721f70e6f9e14fe56ff1d2b5cac9625fa1f96af2f0e0098597fe78b;hmacsha256'}
5, Test with MQTT software
MQTT client software download address: https://download.csdn.net/download/xiaolong1126626497/18784012
The software is developed by QT. The source code address is: https://blog.csdn.net/xiaolong1126626497/article/details/116779490

Format of equipment theme publishing and definition:

Device message data upload format:
{"method":"report","clientToken":"123","params":{"light":78.4,"temperature":21.4,"humidity":60.8,"motor":1}}
After successful login, you can see that the device is online:

Open wechat applet to view the data uploaded by the device:

6, Write STM32 device side code


6.1 main.c code
#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" /* Hardware connection mode: ESP8266 The serial WIFI module is connected with serial port 3 of STM32. PB10--RXD Module receiving pin PB11--TXD Module sending pin GND---GND land VCC---VCC Power supply (3.3V~5.0V) OLED Wiring: D0----SCK-----PB14 D1----MOSI----PB13 RES—Reset (active low) - PB12 DC---Data and command control pin - PB1 CS---Chip selection pin ----- PA7 Micro DC motor: PB8 Purple light: PB9 LED Hardware connection: PB5 PE5 KEY Hardware connection: PE3 PE4 */ #define ESP8266_ WIFI_ AP_ SSID "CMCC cqvn" / / the name of the router to be connected -- do not appear Chinese, spaces and other special characters #define ESP8266_AP_PASSWORD "99pu58cb" / / password of the router to be connected //Equipment information of Tencent Internet of things server #define MQTT_ClientID "6142CX41XESmartAgriculture" #define MQTT_UserName "6142CX41XESmartAgriculture;12010126;HUA2G;1624271589" #define MQTT_PassWord "a8aadebe9721f70e6f9e14fe56ff1d2b5cac9625fa1f96af2f0e0098597fe78b;hmacsha256" //Topics for subscriptions and publications #define SET_ Topic "$thing / down / property / 6142cx41xe / smartagriculture" / / subscription #define POST_TOPIC "$thing/up/property/6142CX41XE/SmartAgriculture" / / release char mqtt_message[200];//Report data buffer char OLED_ShowBuff[100]; u8 ESP8266_Stat=0; /* Function: temperature and humidity \ light intensity display */ void ShowTemperatureAndHumidity(float temp,float humi,float light) { sprintf(OLED_ShowBuff,"T: %.2f",temp); OLED_ShowString(40,16*0,16,OLED_ShowBuff); sprintf(OLED_ShowBuff,"H: %.2f%%",humi); OLED_ShowString(40,16*1,16,OLED_ShowBuff); sprintf(OLED_ShowBuff,"L: %.2f%%",light); OLED_ShowString(40,16*2,16,OLED_ShowBuff); } /* Function function: ESP8266 display page */ void ESP8266_ShowPageTable(void) { if(ESP8266_Stat)OLED_ShowString(0,16*0,16,"WIFI STAT:ERROR"); else OLED_ShowString(0,16*0,16,"WIFI STAT:OK"); //display string sprintf((char*)OLED_ShowBuff,"%s",ESP8266_WIFI_AP_SSID); OLED_ShowString(0,16*1,16,OLED_ShowBuff); sprintf((char*)OLED_ShowBuff,"%s",ESP8266_AP_PASSWORD); OLED_ShowString(0,16*2,16,OLED_ShowBuff); } int main() { u32 time_cnt=0; u32 i; u8 key; u8 page=0; float temp=0; float humi=0; float light=0; u8 motor_state=0; float Humidity; float Temperature; delay_ms(1000); delay_ms(1000); LED_Init(); KEY_Init(); IIC_Init(); //OLED initialization OLED_Init(0xc8,0xa1); //OLED display initialization - normal display; //Clear screen OLED_Clear(0); USART1_Init(115200); TIMER1_Init(72,20000); //Timeout 20ms USART3_Init(115200);//Serial WIFI TIMER3_Init(72,20000); //Timeout 20ms Init_SHT30(); USART1_Printf("Initializing WIFI One moment please.\n"); if(ESP8266_Init()) { ESP8266_Stat=1; USART1_Printf("ESP8266 Hardware detection error.\n"); } else { //Unencrypted port USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode(ESP8266_WIFI_AP_SSID,ESP8266_AP_PASSWORD,"106.55.124.154",1883,1)); } //2. MQTT protocol initialization MQTT_Init(); //3. Connect to Tencent cloud IOT server while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord)) { USART1_Printf("Server connection failed,Retrying...\n"); delay_ms(500); } USART1_Printf("Server connection succeeded.\n"); //3. Subscribe to topics if(MQTT_SubscribeTopic(SET_TOPIC,0,1)) { USART1_Printf("Topic subscription failed.\n"); } else { USART1_Printf("Topic subscription succeeded.\n"); } while(1) { //Press the key to test unlocking and locking key=KEY_Scan(0); if(key==1) { //Clear screen OLED_Clear(0); //Turn pages if(page>=1) { page=0; } else { page++; } LED1=!LED1; //LEd status light } else if(key==2) { LED1=!LED1; //LEd status light time_cnt=0; //Motor status change MOTOR_DEV=!MOTOR_DEV; //Motor status motor_state=MOTOR_DEV; //fill-in light LIGHT_DEV=!LIGHT_DEV; } //Unlocking mode of wechat applet: receive the data returned by WIFI if(USART3_RX_FLAG) { USART3_RX_BUFFER[USART3_RX_CNT]='\0'; //Print the data returned by wechat applet to the serial port for(i=0;i<USART3_RX_CNT;i++) { USART1_Printf("%c",USART3_RX_BUFFER[i]); } //If the attribute is issued, judge whether to unlock or close the lock if(USART3_RX_CNT>5) { //Use string lookup function if(strstr((char*)&USART3_RX_BUFFER[5],"\"motor\":1")) { LED1=0; //Light up MOTOR_DEV=1; //Turn on the motor motor_state=1; } else if(strstr((char*)&USART3_RX_BUFFER[5],"\"motor\":0")) { LED1=1; //Lights out MOTOR_DEV=0; //Turn off the motor motor_state=0; } } USART3_RX_CNT=0; USART3_RX_FLAG=0; } //Timing and keeping synchronization with wechat applet -- once a second delay_ms(10); time_cnt++; if(time_cnt==50) { time_cnt=0; //Status light -- indicates that the program is still alive LED2=!LED2; //Read light intensity light=Read_BH1750_Data(); //Read temperature and humidity SHT3x_ReadData(&Humidity,&Temperature); humi=Humidity; temp=Temperature; //Upload data sprintf(mqtt_message,"{\"method\":\"report\",\"clientToken\":\"123\",\"params\":{\"temperature\":%f,\"humidity\":%f,\"motor\":%d,\"light\":%f}}", temp,humi,motor_state,light); MQTT_PublishData(POST_TOPIC,mqtt_message,0); //Automatic irrigation according to humidity if(humi<50.0) //Less than 50 automatic irrigation { motor_state=1; //Motor status update MOTOR_DEV=1; //Turn on the motor } } //OLED display if(page==0) { ShowTemperatureAndHumidity(temp,humi,light); } else if(page==1) { ESP8266_ShowPageTable(); } } }
6.2 mqtt.c code
#include "mqtt.h" u8 *mqtt_rxbuf; u8 *mqtt_txbuf; u16 mqtt_rxlen; u16 mqtt_txlen; u8 _mqtt_txbuf[256];//Send data buffer u8 _mqtt_rxbuf[256];//Receive data buffer typedef enum { //name Value Message flow direction describe M_RESERVED1 =0 , // prohibit retain M_CONNECT , // Client to server The client requests to connect to the server M_CONNACK , // Server to client Connection message confirmation M_PUBLISH , // Both directions are allowed Release news M_PUBACK , // Both directions are allowed QoS 1 message publishing receipt confirmation M_PUBREC , // Both directions are allowed Release received (first step of guaranteed delivery) M_PUBREL , // Both directions are allowed Release release (the second step of guaranteed delivery) M_PUBCOMP , // Both directions are allowed QoS 2 message publishing is completed (the third step of ensuring interaction) M_SUBSCRIBE , // Client to server Client subscription request M_SUBACK , // Server to client Subscription request message confirmation M_UNSUBSCRIBE , // Client to server Client unsubscribe request M_UNSUBACK , // Server to client Unsubscribe message confirmation M_PINGREQ , // Client to server Heartbeat request M_PINGRESP , // Server to client Heartbeat response M_DISCONNECT , // Client to server Client disconnect M_RESERVED2 , // prohibit retain }_typdef_mqtt_message; //Connection succeeded. The server responded 20 02 00 00 //Client actively disconnects e0 00 const u8 parket_connetAck[] = {0x20,0x02,0x00,0x00}; const u8 parket_disconnet[] = {0xe0,0x00}; const u8 parket_heart[] = {0xc0,0x00}; const u8 parket_heart_reply[] = {0xc0,0x00}; const u8 parket_subAck[] = {0x90,0x03}; void MQTT_Init(void) { //Buffer assignment mqtt_rxbuf = _mqtt_rxbuf; mqtt_rxlen = sizeof(_mqtt_rxbuf); mqtt_txbuf = _mqtt_txbuf; mqtt_txlen = sizeof(_mqtt_txbuf); memset(mqtt_rxbuf,0,mqtt_rxlen); memset(mqtt_txbuf,0,mqtt_txlen); //Unconditional active disconnection MQTT_Disconnect(); delay_ms(100); MQTT_Disconnect(); delay_ms(100); } /* Function function: login server Function return value: 0 means success, 1 means failure */ u8 MQTT_Connect(char *ClientID,char *Username,char *Password) { u8 i,j; int ClientIDLen = strlen(ClientID); int UsernameLen = strlen(Username); int PasswordLen = strlen(Password); int DataLen; mqtt_txlen=0; //Variable header + Payload each field contains a two byte length identifier DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2); //Fixed header //Control message type mqtt_txbuf[mqtt_txlen++] = 0x10; //MQTT Message Type CONNECT //Remaining length (excluding fixed head) do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); //Variable header //Protocol name mqtt_txbuf[mqtt_txlen++] = 0; // Protocol Name Length MSB mqtt_txbuf[mqtt_txlen++] = 4; // Protocol Name Length LSB mqtt_txbuf[mqtt_txlen++] = 'M'; // ASCII Code for M mqtt_txbuf[mqtt_txlen++] = 'Q'; // ASCII Code for Q mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T //Protocol level mqtt_txbuf[mqtt_txlen++] = 4; // MQTT Protocol version = 4 for version 3.1.1 protocol, the value of the protocol level field is 4(0x04) //Connection flag mqtt_txbuf[mqtt_txlen++] = 0xc2; // conn flags mqtt_txbuf[mqtt_txlen++] = 0; // Keep-alive Time Length MSB mqtt_txbuf[mqtt_txlen++] = 100; // Keep alive time length LSB 100s heartbeat packet keep alive time mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen); mqtt_txlen += ClientIDLen; if(UsernameLen > 0) { mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen); //username length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen); //username length LSB memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen); mqtt_txlen += UsernameLen; } if(PasswordLen > 0) { mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen); //password length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen); //password length LSB memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen); mqtt_txlen += PasswordLen; } memset(mqtt_rxbuf,0,mqtt_rxlen); MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); for(j=0;j<10;j++) { delay_ms(50); if(USART3_RX_FLAG) { memcpy((char *)mqtt_rxbuf,USART3_RX_BUFFER,USART3_RX_CNT); //memcpy for(i=0;i<USART3_RX_CNT;i++)USART1_Printf("%#x ",USART3_RX_BUFFER[i]); USART3_RX_FLAG=0; USART3_RX_CNT=0; } //CONNECT if(mqtt_rxbuf[0]==parket_connetAck[0] && mqtt_rxbuf[1]==parket_connetAck[1]) //Connection succeeded { return 0;//Connection succeeded } } return 1; } /* Function function: MQTT subscription / unsubscribe data packaging function Function parameters: topic theme qos Message level 0: distribute at most once 1: distribute at least once 2: distribute only once whether Subscribe / unsubscribe request package (1 means subscribe, 0 means unsubscribe) Return value: 0 indicates success, 1 indicates failure */ u8 MQTT_SubscribeTopic(char *topic,u8 qos,u8 whether) { u8 i,j; mqtt_txlen=0; int topiclen = strlen(topic); int DataLen = 2 + (topiclen+2) + (whether?1:0);//The length of the variable header (2 bytes) plus the length of the payload //Fixed header //Control message type if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //Message type and flag subscription else mqtt_txbuf[mqtt_txlen++] = 0xA2; //Unsubscribe //Remaining length do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); //Variable header mqtt_txbuf[mqtt_txlen++] = 0; //Message identifier MSB mqtt_txbuf[mqtt_txlen++] = 0x0A; //Message identifier LSB //Payload mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//Subject length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//Subject length LSB memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen); mqtt_txlen += topiclen; if(whether) { mqtt_txbuf[mqtt_txlen++] = qos;//QoS level } for(i=0;i<10;i++) { memset(mqtt_rxbuf,0,mqtt_rxlen); MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); for(j=0;j<10;j++) { delay_ms(50); if(USART3_RX_FLAG) { memcpy((char *)mqtt_rxbuf,(char*)USART3_RX_BUFFER,USART3_RX_CNT); USART3_RX_FLAG=0; USART3_RX_CNT=0; } if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //Subscription succeeded { return 0;//Subscription succeeded } } } return 1; //fail } //MQTT publish data packaging function //Topic topic //Message message //qos message level u8 MQTT_PublishData(char *topic, char *message, u8 qos) { int topicLength = strlen(topic); int messageLength = strlen(message); static u16 id=0; int DataLen; mqtt_txlen=0; //The length of the payload is calculated by subtracting the length of the variable header from the value of the remaining length field in the fixed header //No identifier when QOS is 0 //Data length subject name message identifier payload if(qos) DataLen = (2+topicLength) + 2 + messageLength; else DataLen = (2+topicLength) + messageLength; //Fixed header //Control message type mqtt_txbuf[mqtt_txlen++] = 0x30; // MQTT Message Type PUBLISH //Remaining length do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//Subject length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//Subject length LSB memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//Copy theme mqtt_txlen += topicLength; //message identifier if(qos) { mqtt_txbuf[mqtt_txlen++] = BYTE1(id); mqtt_txbuf[mqtt_txlen++] = BYTE0(id); id++; } memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength); mqtt_txlen += messageLength; MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); return mqtt_txlen; } void MQTT_SentHeart(void) { MQTT_SendBuf((u8 *)parket_heart,sizeof(parket_heart)); } void MQTT_Disconnect(void) { MQTT_SendBuf((u8 *)parket_disconnet,sizeof(parket_disconnet)); } void MQTT_SendBuf(u8 *buf,u16 len) { USARTx_DataSend(USART3,buf,len); }