• 基于STM32设计的智慧浇花系统(华为云IOT)


    1. 前言

    随着社会的不断发展和人们生活水平的逐渐提高,人们逐渐追求高质量的生活,很多人都会选择在家里或办公室种植一些花卉以净化家庭空气,陶冶情操,但是很多人忙于工作、学习、出差、旅游或者一些其他的原因,不能及时地对花卉进行照料,短时间内导致很多花卉因缺水分而影响正常生长,长时间不照料有些名贵的花卉直接死亡。基于上述状况,提出了此基于物联网的智慧浇花系统。该系统采用工业级高精度土壤温湿度传感器采集花盆中的土壤温湿度,环境的温度湿度,通过ESP8266 WIFI实时上传当前的土壤温湿度、环境光照度等数据到华为云物联网云平台,可以通过 app实时查看花卉的土壤湿度、环境温度等信息,并且本地通过OLED显示屏实时显示这些信息,可以设定某种花适宜的生长的土壤湿度条件,实现自动控制给花浇水,即能让花卉生长在适宜的湿度下,与目前市场上的自动浇花系统相比,该系统的特点具有远程控制,低成本、极高的资源利用率、操作简单和反应灵敏等。

    2. 整体系统设计

    主控MCU选择STM32F103芯片,通过土壤湿度传感器、环境温湿度传感器,检测整个周边环境信息,再通过ESP8266 WIFI传递到物联网平台。程序里可以预设湿度阀值,当检测到土壤湿度低于阀值就自动浇花。在手机APP上可以实现远程控制水泵浇花,本地在搭载一个TFT小尺寸显示屏,可以实时显示测量检测的数据,在办公室里也可以通过TFT彩屏显示屏解周围环境的信息。

    image-20220314220440225

    image-20220314220612175

    9)ZPR5ZTLKT1V945T)LI420.jpg

    image-20220612231630969

    image-20220612231655496

    image-20220612231713008

    3. 硬件选型

    3.1 STM32开发板

    image-20220310112203970

    image-20220314225132797

    3.2 土壤湿度传感器

    image-20220310113015073

    3.3 ESP8266 WIFI

    image-20220310112928724

    3.4 环境温湿度

    image-20220310113210331

    3.5 水泵电机

    image-20220315005148887

    image-20220310113457534

    4. 华为云物联网平台

    采用现成的物联网云平台,可以方便设备上云,完成设备与上位机之间双向通信,不需要自己搭建服务器,还可以通过云端的存储桶存储数据,分析数据等。

    4.1 创建产品

    (1)登录官网

    登录官网,找到物联网产品

    https://www.huaweicloud.com

    选择产品、IOT物联网、设备接入

    image-20220314152340383

    image-20220314152436051

    (2)创建产品

    选择产品选项栏,点击右上角创建产品;先创建产品、再创建设备。一个产品下可以创建多个设备。

    image-20220314154410232

    (3)填写产品的信息

    image-20220314154643551

    (4)创建成功

    点击确定,产品创建成功后,点击查看详情,保存好产品ID等信息

    image-20220314154713911

    (5)创建服务器ID

    点击自定义模型按钮,创建服务器ID

    image-20220314154845545

    image-20220314154941078

    (6)添加属性

    添加的这个属性非常重要,就是设置设备与服务器之间数据传输的属性,设备端哪些传感器需要上传数据到服务器,这里就按照传感器的数据类型给它添加属性。

    【1】添加环境温度属性:温度属性是设备主动上传,这里就设置为只读属性。

    image-20220314155109865

    【2】添加环境湿度属性:湿度属性是设备主动上传,这里就设置为只读属性。

    image-20220314155619873

    【3】添加环境湿度属性:湿度属性是设备主动上传,这里就设置为只读属性。

    image-20220314155714699

    【4】添加灌溉电机的属性:电机需要下发指令控制,就设置为只读只写属性。

    image-20220314160413622

    【5】所有的属性如下

    image-20220314160519969

    4.2 创建设备

    产品创建好之后,在下面创建对应的设备。这个设备创建一般可以采用服务器提供的接口,在应用端开发引导界面自动创建,这里采用手动形式创建,方便了解设备创建流程,需要填充什么属性参数等等。

    (1)创建设备

    选择所有设备选项卡、跳转到设备页面、点击右上角创建设备。

    image-20220314160626012

    (2)单设备注册

    根据提示填充设备参数。

    image-20220314160955567

    (3)保存设备密匙

    创建成功后,保存设备密匙,后续生成MQTT登录密码、账号需要使用。

    image-20220314161241229

    {
        "device_id": "622ef2e8c4e6a958e356fe84_STM32",
        "secret": "12345678"
    }
    
    • 1
    • 2
    • 3
    • 4

    image-20220314161535617

    (4)查看设备详情

    image-20220314161630794

    4.3 生成MQTT登录参数

    华为云提供的MQTT账户信息生成在线小工具:

    https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com

    image-20220314161923312

    生成密匙:

    image-20220314161949887

    ClientId  622ef2e8c4e6a958e356fe84_STM32_0_0_2022031408
    Username  622ef2e8c4e6a958e356fe84_STM32
    Password  c9bc1837fc5cba8728a5e7ad625e422799e9a96975e3836a0054ea8e00f9ae5d
    
    • 1
    • 2
    • 3

    4.4 主题订阅格式

    在上面的步骤中,产品、设备、MQTT登录信息都已经创建好,接下来要考虑的的就是,如何登录华为云物联网平台,登录之后数据如何交互等问题。

    (1)云平台地址

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

    (2)订阅主题

    格式: $oc/devices/{device_id}/sys/messages/down
    //订阅主题: 平台下发消息给设备
    $oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/messages/down
    
    • 1
    • 2
    • 3

    (3)上报主题

    格式: $oc/devices/{device_id}/sys/properties/report
    //设备上报主题请求
    $oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/properties/report
    //上报的数据格式如下
    {"services": [{"service_id": "STM32","properties":
    {"temp":12,"humidity":21,"soil_moisture":15,"motor":1}}]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    参数说明:
    device_id 设备ID这个字段,在创建设备时生成的。

    service_id 这个字段填服务ID,这个ID在产品页面下面自定义模型的位置可以看到,这个服务ID就在创建
    数据模型时设置的。

    后面的{}里表示上传到服务器的数据属性字段。这个属性的名称是自己在自定义模型页面自己填的。

    image-20220314163822196

    4.5 软件模拟测试

    为了验证云端设备的情况,这里先采用MQTT客户端软件模拟设备,登录云端服务器,测试数据交互。

    MQTT客户端软件下载地址: https://download.csdn.net/download/xiaolong1126626497/18784012

    (1)登录服务器

    登录服务器,测试主题上报,订阅等功能。

    image-20220314164327705

    (2)查看上传的数据

    image-20220314164503380

    (3)消息跟踪

    在设备详情页面开启消息跟踪,可以查看设备与服务器交互的过程,调试比较方便,如果数据不对,或者其他问题,在服务器消息跟踪页面可以快速定位问题。

    image-20220314164600727

    5. 上位机软件开发

    为了方便查看设备的环境温湿度、土壤湿度信息,远程控制电机浇花,还需要开发一款可视化界面软件。

    华为云物联网平台提供了应用端开发的API接口,可以通过对应的API完成数据交互。

    5.1 应用端开发接口

    (1)查询设备列表

    查询设备列表这个接口是查询在这个产品目录下有几个设备,返回设置设备的列表信息。

    前面也说过,一个产品下可以创建很多个设备。

    帮助文档地址: https://support.huaweicloud.com/api-iothub/iot_06_v5_0048.html

    官方提供了API接口,可以直接获取产品下面的所有设备详细信息返回。

    关于请求参数,返回结果的字段含义,在帮助文档里有详细介绍。

    接口的在线调试地址:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ListDevices

    第一次使用接口时,可以使用在线调试接口测试参数,交互过程。

    URL格式: /v5/iot/{project_id}/devices
    示例:
    https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/611f253373344433821fb63448091eec/devices
    
    • 1
    • 2
    • 3

    响应的数据:

    {
     "devices": [
      {
       "app_id": "e91f518007424be0bbe2763d0e2e1c0d",
       "app_name": "DefaultApp_622ezesm",
       "device_id": "622ef2e8c4e6a958e356fe84_STM32",
       "node_id": "STM32",
       "gateway_id": "622ef2e8c4e6a958e356fe84_STM32",
       "device_name": "浇花设备",
       "node_type": "GATEWAY",
       "description": null,
       "fw_version": null,
       "sw_version": null,
       "device_sdk_version": null,
       "product_id": "622ef2e8c4e6a958e356fe84",
       "product_name": "浇花神器",
       "status": "OFFLINE",
       "tags": []
      }
     ],
     "page": {
      "count": 1,
      "marker": "622ef8e6b617d2412bae4c7e"
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    image-20220314170401019

    (2)查询设备属性

    通过查询属性的接口,可以主动请求获取设备详细属性。

    流程是:应用层调用这个API接口---->请求服务器----->请求客户端设备------>返回给服务器----->返回给应用层调用处。

    文档地址:https://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html

    image-20220314171229746

    在线调试接口:https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IoTDA&api=ListProperties

    采用MQTT客户端软件模拟,设备登录,再使用上面的调试接口调试。

    image-20220314171455527

    image-20220314171834632

    image-20220314171852537

    数据总结:

    设备端响应的主题格式:
    $oc/devices/{device_id}/sys/properties/get/response/request_id={request_id}
    
    示 例:
    $oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/properties/get/response/request_id=43408b9d-41d1-48a6-a2e7-ece314dbd0a3
    
    设备端响应的数据格式:
    {"services": [{"service_id": "STM32","properties":{"temp":14,"humidity":22,"soil_moisture":16,"motor":0}}]}
    
    应用端上位机收到设备端的响应数据:
    {
     "response": {
      "services": [
       {
        "service_id": "STM32",
        "properties": {
         "temp": 14,
         "humidity": 22,
         "soil_moisture": 16,
         "motor": 0
        }
       }
      ]
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    (3)修改设备属性

    设备的产品模型中定义了物联网平台可向设备下发的属性,应用服务器可调用此接口向指定设备下属性。平台负责将属性以同步方式发送给设备,并将设备执行属性结果同步返回。

    文档地址: https://support.huaweicloud.com/api-iothub/iot_06_v5_0035.html

    在线调试地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=UpdateProperties

    请求URL: 
    https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/611f253373344433821fb63448091eec/devices/622ef2e8c4e6a958e356fe84_STM32/properties
    
    请求体:
    {
     "services": [
      {
       "service_id": "STM32",
       "properties": {
        "motor": 1
       }
      }
     ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image-20220314173005178

    设备响应格式:https://support.huaweicloud.com/api-iothub/iot_06_v5_3008.html

    image-20220314212257609

    image-20220314212155389

    响应的主题格式:
    $oc/devices/{device_id}/sys/properties/set/response/request_id={request_id}
    
    示例: 
    $oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/properties/set/response/request_id=ad492c4f-7498-4243-b6ae-93089eda1945
     
    响应的数据:
    {"result_code": 0,"result_desc": "success"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    image-20220314212647992

    5.2 创建IAM账户

    创建一个IAM账户,方便接下来使用API接口访问华为云服务时,生成token登录密匙。

    地址:https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users

    (1)创建用户

    image-20220314174027401

    (2)填充参数

    image-20220314174139690

    (3)完成创建

    image-20220314174215442

    User     Name	Access Key Id	Secret Access Key
    ds_abc	IQJXEPFKUALTIBKYKJ0Q	xweOS1ohe9c5KQ2fqyYPhwmdv37WicxGOaybi1FC
    
    • 1
    • 2

    5.3 开发上位机软件

    (1)开发环境介绍

    上位机软件采用Qt框架设计,Qt是一个跨平台的C++图形用户界面应用程序框架。Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。简单来说,QT可以很轻松的帮你做带界面的软件,甚至不需要你投入很大精力。

    QT官网: https://www.qt.io/

    image-20220314143105032

    (2)学习教程

    QT入门实战专栏: https://blog.csdn.net/xiaolong1126626497/category_11400392.html

    (3)实现效果

    image-20220314220429689

    image-20220314220612175

    image-20220314221035015

    (4)核心代码

    //手动浇花
    void Widget::on_pushButton_ditu_clicked()
    {
        //修改属性
        function_select=2;
    
        QString requestUrl;
        QNetworkRequest request;
    
        //设置请求地址
        QUrl url;
    
        //修改属性的地址
        requestUrl="https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/611f253373344433821fb63448091eec/devices/622ef2e8c4e6a958e356fe84_STM32/properties";
    
        //设置数据提交格式
        request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
    
        //设置token
        request.setRawHeader("X-Auth-Token",Token);
    
        //构造请求
        url.setUrl(requestUrl);
    
        request.setUrl(url);
    
        //打包请求参数赋值
        QString post_param=QString("{\"service_id\":\"STM32\",\"properties\":{\"motor\":1}}");
        //发送请求
        manager->put(request, post_param.toUtf8());
    }
    
    //查询所有设备
    void Widget::Get_AllDevice()
    {
        //查询设备列表
        function_select=1;
    
        QString requestUrl;
        QNetworkRequest request;
    
        //设置请求地址
        QUrl url;
    
        //设备列表请求地址
        requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices?product_id=%3&is_cascade_query=false&limit=10&marker=ffffffffffffffffffffffff&offset=0")
                     .arg(SERVER_ID)
                .arg(PROJECT_ID)
                .arg(Product_id);
    
        //设置数据提交格式
        request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
    
        //设置token
        request.setRawHeader("X-Auth-Token",Token);
    
        //构造请求
        url.setUrl(requestUrl);
    
        request.setUrl(url);
    
        //发送请求
        manager->get(request);
    }
    
    //查询设备属性
    void Widget::Get_device_properties()
    {
        //表示查询设备属性
        function_select=0;
    
        QString requestUrl;
        QNetworkRequest request;
    
        //设置请求地址
        QUrl url;
    
        if(device_id_lis.size()<=0)
        {
            //显示错误代码
            QMessageBox::information(this,"提示","未选择设备,请先获取设备列表\n选择设备后重试.",QMessageBox::Ok,QMessageBox::Ok);
            return;
        }
    
        //获取token请求地址
        requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices/%3/properties?service_id=%4")
                     .arg(SERVER_ID)
                .arg(PROJECT_ID)
                .arg(device_id_lis.at(ui->comboBox->currentIndex()))
                .arg(service_id);
    
        //自己创建的TCP服务器,测试用
        //requestUrl="http://10.0.0.6:8080";
    
        //设置数据提交格式
        request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
    
        //设置token
        request.setRawHeader("X-Auth-Token",Token);
    
        //构造请求
        url.setUrl(requestUrl);
    
        request.setUrl(url);
    
        //发送请求
        manager->get(request);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    6. STM32设备端开发

    基于STM32+物联网设计的浇花神器-(华为云IOT)(STM32设备端源代码).zip

    https://download.csdn.net/download/xiaolong1126626497/85623120

    基于STM32+物联网设计的浇花神器-(华为云IOT)(上位机源码与可执行文件).zip

    https://download.csdn.net/download/xiaolong1126626497/85623171

    6.1 设备实物图

    image-20220314225450773.png
    image-20220315004128180.png
    image-20220315012115581.png
    9)ZPR5ZTLKT1V945T)LI420.jpg
    image.png
    1.jpg
    image-20220315015914129.png
    image-20220315015941794.png
    image-20220315020302958.png

    6.3 运行过程

    (1)下载程序

    image-20220315015018948

    (2)正常上电串口调试助手输出。

    image-20220315012115581

    (3)登录成功后,设备上线:

    image-20220315013202574

    (4)浇水测试

    image-20220315015914129

    (5)更新数据

    image-20220315020302958

    image-20220315020317962

    6.4 土壤湿度传感器代码

    image-20220315004128180

    #include "adc.h"
    
    /*
    函数功能:初始化ADC1为规则通道
    转换通道1-->PA1
    */
    void AdcInit(void)
    {
    	/*开启端口时钟*/
    	RCC->APB2ENR|=1<<2;  //PA
    	GPIOA->CRL&=0xFFFFFF0F;
    	GPIOA->CRL|=0x00000000; //配置PA1为模拟输入
    
    	/*开启ADC1的外设时钟*/
    	RCC->APB2ENR|=1<<9; 
    
    	/*开启ADC1的外设复位时钟*/
    	RCC->APB2RSTR|=1<<9;     //开始复位
    	RCC->APB2RSTR&=~(1<<9);  //停止复位
    		
    	/*配置ADC预分频器*/
    	RCC->CFGR&=~(0X3<<14);  //清除预分频寄存器
    	RCC->CFGR|=0x2<<14;     //配置ADC时钟为12MHZ.注意:ADC的时钟不能超过14MHZ
    	
    
    	/*ADC相关的基本配置*/
    	ADC1->CR1&=~(0xF<<16);   //ADC工作在独立模式
    	ADC1->CR2|=1<<20;        //使用外部事件启动转换。
    	ADC1->CR2|=0X7<<17;      //选择启动规则通道组转换的外部事件
    	ADC1->CR2&=~(1<<11);     //0:数据右对齐;
    	ADC1->CR2&=~(1<<1);      //0:单次转换模式;
    	
    	/*ADC输入通道配置*/
    	ADC1->SQR1&=~(0XF<<20);  //规则通道序列长度  : 1个转换
    	
    	/*ADC通道1采样时间配置*/
    	ADC1->SMPR2&=~(0x7<<(3*1));   
    	ADC1->SMPR2|=0x7<<(3*1); //通道1的采样时间239.5周期
    	ADC1->CR2|=1<<0;         //1:开启ADC并启动转换。
    	
    	ADC1->CR2|=1<<3;         //1:初始化校准寄存器。
    	while(ADC1->CR2&1<<3){}   //等待校准结束 
    	ADC1->CR2|=1<<2;         //1:开始校准。
    	while(ADC1->CR2&1<<2){}  //等待校准结束
    }
    
    
    
    /*
    获取ch通道的ADC转换值
    ch的范围:0-17
    16 :内部温度
    17 :内部参考电压值
    0-15 :外部输入引脚
    */
    u16 GetAdcCHxDATA(u8 ch)
    {
    	ADC1->SQR3&=0XFFFFFFE0;  //E=:1110  清除通道号
    	ADC1->SQR3|=ch;          //设置要转换的通道
    	ADC1->CR2|=1<<22;        //开始转换规则通道
    	while(!ADC1->SR&1<<1){}  //等待规则通道转换完毕
    	return ADC1->DR;         //返回采集的值
    }
    
    /*
    dat=temp*3.3/4095;        //转换的电压值
    dat=(1.43-dat)/0.0043+25; //实际温度=(1.43-测出的电压)/0.0043+25;
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    6.5 DHT11温湿度传感器代码

    #include "dht11.h"
    #include "delay.h"
    
    //复位DHT11
    void DHT11_Rst(void)	   
    {                 
    	  DHT11_IO_OUT(); 	//SET OUTPUT
        DHT11_DQ_OUT=0; 	//拉低DQ
        DelayMs(20);    	//拉低至少18ms
        DHT11_DQ_OUT=1; 	//DQ=1 
    	  DelayUs(30);     	//主机拉高20~40us
    }
    //等待DHT11的回应
    //返回1:未检测到DHT11的存在
    //返回0:存在
    u8 DHT11_Check(void) 	   
    {   
    	u8 retry=0;
    	DHT11_IO_IN();//SET INPUT	 
        while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
    	{
    		retry++;
    		DelayUs(1);
    	};	 
    	if(retry>=100)return 1;
    	else retry=0;
        while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
    	{
    		retry++;
    		DelayUs(1);
    	};
    	if(retry>=100)return 1;	    
    	return 0;
    }
    //从DHT11读取一个位
    //返回值:1/0
    u8 DHT11_Read_Bit(void) 			 
    {
     	u8 retry=0;
    	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
    	{
    		retry++;
    		DelayUs(1);
    	}
    	retry=0;
    	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
    	{
    		retry++;
    		DelayUs(1);
    	}
    	DelayUs(40);//等待40us
    	if(DHT11_DQ_IN)return 1;
    	else return 0;		   
    }
    
    //从DHT11读取一个字节
    //返回值:读到的数据
    u8 DHT11_Read_Byte(void)    
    {        
        u8 i,dat;
        dat=0;
    	for (i=0;i<8;i++) 
    	{
       		dat<<=1; 
    	    dat|=DHT11_Read_Bit();
        }						    
        return dat;
    }
    
    
    //从DHT11读取一次数据
    //temp:温度值(范围:0~50°)
    //humi:湿度值(范围:20%~90%)
    //返回值:0,正常;1,读取失败
    u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
    {        
     	u8 buf[5];
    	u8 i;
    	DHT11_Rst();
    	//printf("------------------------\r\n");
    	if(DHT11_Check()==0)
    	{
    		for(i=0;i<5;i++)//读取40位数据
    		{
    			buf[i]=DHT11_Read_Byte();
    		}
    		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
    		{
    			*humi=buf[0];
    			*temp=buf[2];
    		}
    	}else return 1;
    	return 0;	    
    }
    
    
    //初始化DHT11的IO口 DQ 同时检测DHT11的存在
    //返回1:不存在
    //返回0:存在    	 
    u8 DHT11_Init(void)
    {
    	RCC->APB2ENR|=1<<2;    //使能PORTA口时钟 
    	GPIOA->CRL&=0XFF0FFFFF;//PORTA.5 推挽输出
    	GPIOA->CRL|=0X00300000;
    	GPIOA->ODR|=1<<5;      //输出1				    
    	DHT11_Rst();
    	return DHT11_Check();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    6.6 主函数逻辑代码

    #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 "motor.h"
    #include "oled.h"
    #include "dht11.h"
    #include "adc.h"
    
    /*
    硬件连接方式:
    
    1. ESP8266 WIFI接线
    ATK-ESP8266串口WIFI模块与STM32的串口2相连接。
    PA2(TX)--RXD 模块接收脚
    PA3(RX)--TXD 模块发送脚
    GND---GND 地
    VCC---VCC 电源(3.3V~5.0V)
    
    2. TFT 1.44 寸彩屏接线
    GND   电源地
    VCC   接5V或3.3v电源
    SCL   接PC8(SCL)
    SDA   接PC9(SDA)
    RST   接PBPC10
    DC    接PB7
    CS    接PB8
    BL	  接PB11
    
    3. DHT11 温湿度
    VCC--VCC
    GND---GND
    DAT---PA5 
    
    4. 浇水电机
    GND---GND
    VCC---5V
    AO----PA4
    
    
    5. 土壤湿度
    GND---GND
    VCC---3.3V
    AO----PA1
    
    
    6. 板载LED灯接线
    LED1---PA8
    LED2---PD2
    
    
    7. 板载按键接线
    K0---PA0 
    K1---PC5 
    K2---PA15
    
    */
    
    //华为物联网服务器的设备信息
    #define MQTT_ClientID "622ef2e8c4e6a958e356fe84_STM32_0_0_2022031408"
    #define MQTT_UserName "622ef2e8c4e6a958e356fe84_STM32"
    #define MQTT_PassWord "c9bc1837fc5cba8728a5e7ad625e422799e9a96975e3836a0054ea8e00f9ae5d"
    
    //订阅与发布的主题
    #define SET_TOPIC  "$oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/messages/down"  //订阅
    #define POST_TOPIC "$oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/properties/report"  //发布
    
    //设置连接的路由器信息
    #define CONNECT_WIFI  "Xiaomi_meizi6"   //将要连接的路由器名称 --不要出现中文、空格等特殊字符
    #define CONNECT_PASS "12170307yu"       //将要连接的路由器密码
    
    #define CONNECT_SERVER_IP "121.36.42.100"   //服务器IP地址
    #define CONNECT_SERVER_PORT 1883            //服务器端口号
    
    char mqtt_message[200];//上报数据缓存区
    
    
    //JTAG模式设置,用于设置JTAG的模式
    //mode:jtag,swd模式设置;00,全使能;01,使能SWD;10,全关闭;	   
    #define JTAG_SWD_DISABLE   0X02
    #define SWD_ENABLE         0X01
    #define JTAG_SWD_ENABLE    0X00		  
    void JTAG_Set(u8 mode)
    {
    	u32 temp;
    	temp=mode;
    	temp<<=25;
    	RCC->APB2ENR|=1<<0;     //开启辅助时钟	   
    	AFIO->MAPR&=0XF8FFFFFF; //清除MAPR的[26:24]
    	AFIO->MAPR|=temp;       //设置jtag模式
    } 
    
    
    u32 motor_time_cnt=0;
    u8 motor_flag=0; //灌溉标志
    
    //开启抽水点击、浇花
    void open_motor()
    {
        motor_flag=1; //开始灌溉标志
        motor_time_cnt=0; //计数器--定时
        MOTOR=1; //开始灌溉
    }
    
    //关闭抽水电机
    void close_motor()
    {
         motor_flag=0;
         MOTOR=0; //停止灌溉
    }
    
    u8 temp;
    u8 humidity;
    u16 soil_moisture;
    char request_id[100];
    char data_buff[300];
    
    int main()
    {
        u8 esp8266_state=0;
        u8 key;
        u8 i;
        u32 time_cnt=0;
    
        
        JTAG_Set(JTAG_SWD_DISABLE); //释放PA15
        LED_Init();
        KEY_Init();
        USART1_Init(115200);
        Moto_Init();  //电机初始化
        USART2_Init(115200);//串口-WIFI
        TIMER2_Init(72,20000); //超时时间20ms
        DHT11_Init();		//初始化DHT11
        AdcInit();  //土壤湿度传感器--ADC检测
        Lcd_Init(); //LCD初始化
        Lcd_Clear(0); //清屏为黑色
        LCD_LED_SET;//通过IO控制背光亮		
        
        printf("正在初始化WIFI请稍等.\r\n");
        
        for(i=0;i<5;i++)
        {
            if(ESP8266_Init()==0)
            {
                esp8266_state=1;
                break;
            }
            else
            {
                esp8266_state=0;
                printf("ESP8266硬件检测错误.\n");  
            }
        }
      
       if(esp8266_state)
       {
           printf("准备连接服务器....\r\n");
            //非加密端口
            printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode(CONNECT_WIFI,CONNECT_PASS,CONNECT_SERVER_IP,CONNECT_SERVER_PORT,1));
             
            //2. MQTT协议初始化	
            MQTT_Init(); 
    
            //3. 连接服务器  
            for(i=0;i<5;i++)
            {
                if(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord)==0)
                {
                    esp8266_state=1;
                    break;
                }
                esp8266_state=0;
                printf("服务器连接失败,正在重试...\n");
                delay_ms(500);
            }
            printf("服务器连接成功.\n");
    
            //3. 订阅主题
            if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
            {
               printf("主题订阅失败.\n");
            }
            else
            {
               printf("主题订阅成功.\n");
            } 
       }
    	 
    	 while(1)
    	 {
            //按键可以测试浇花
            key=KEY_Scan(0);
            if(key==2)
            {
                LED1=0;  //亮灯--表示开锁
                time_cnt=0;
                
                //开始浇水
                open_motor();
                
                printf("启动电机灌溉..\r\n");
            }
            else if(key==3)
            {
                LED2=!LED2;
            } 
    
            DelayMs(10);
            time_cnt++;
            motor_time_cnt++;
            
            //如果需要灌溉
            if(motor_flag)
            {
                //5秒后停止灌溉
                if(motor_time_cnt>500)
                {
                    close_motor();
                    printf("灌溉电机停止..\r\n");
                }
            }
             
            if(time_cnt>=100)
            {
                time_cnt=0;
                LED1=!LED1;
                
                soil_moisture=GetAdcCHxDATA(1);   //获取一次转换值    //2099
                printf("土壤湿度:%d\r\n",soil_moisture);
                if(DHT11_Read_Data(&temp,&humidity))
                {
                    printf("温度读取失败.\r\n");
                }
                printf("环境温度:%d\r\n环境湿度:%d\r\n",temp,humidity);
                
                //Lcd_Clear(0); //清屏为黑色
                
                sprintf(mqtt_message,"H1:%d",soil_moisture);
                Gui_DrawFont_GBK16(0,16*0,RED,GRAY0,(u8*)mqtt_message);	
                
                sprintf(mqtt_message,"H2:%d",humidity);
                Gui_DrawFont_GBK16(0,16*1,RED,GRAY0,(u8*)mqtt_message);
                
                sprintf(mqtt_message,"T2:%d",temp);
                Gui_DrawFont_GBK16(0,16*2,RED,GRAY0,(u8*)mqtt_message);
                
                //土壤湿度越高,ADC得到的值越小
                
                //土壤比较干,启动浇水
                if(soil_moisture>4090)
                {
                     //开始浇水
                     open_motor();
                }
                
                
                //上报数据
                sprintf(mqtt_message,"{\"services\": [{\"service_id\":\"STM32\",\"properties\":{\"temp\":%d,\"humidity\":%d,\"soil_moisture\":%d}}]}",
                temp,humidity,soil_moisture);
                MQTT_PublishData(POST_TOPIC,mqtt_message,0);
                printf("更新数据.\r\n");
                
            }
    			 
            // 接收WIFI返回的数据
            if(USART2_RX_FLAG)
            {
                USART2_RX_BUFFER[USART2_RX_CNT]='\0';
                
    		    printf("UART3收到数据.....\r\n");
                //向串口打印服务器返回的数据
                for(i=0;i<USART2_RX_CNT;i++)
                {
                    printf("%c",USART2_RX_BUFFER[i]);
                }
                
                //如果是下发了属性
                if(USART2_RX_CNT>5)
                {
                    //使用字符串查找函数
                    //设置属性,浇水
                    //"motor":1
                    if(strstr((char*)&USART2_RX_BUFFER[5],"properties/set/request_id"))
                    {
                        char *p=NULL;
                        p=strstr((char*)&USART2_RX_BUFFER[5],"request_id");
                        if(p)
                        {        
                            //解析数据
                            //$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/get/request_id=5f359b5c-542f-460e-9f51-85e82150ff4a{"service_id":"gps"} 
                            strncpy(request_id,p,47);      
                        }
                        
                          //上报数据
                        sprintf(mqtt_message,"{\"result_code\": 0,\"result_desc\": \"success\"}");
                        
                        sprintf(data_buff,"$oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/properties/set/response/%s",
                        request_id);
                        
                        MQTT_PublishData(data_buff,mqtt_message,0);
                        
                        printf("发布主题:%s\r\n",data_buff);
                        printf("发布数据:%s\r\n",mqtt_message);
                        
                        //开始浇水
                        open_motor();
                        
                    }
                    //获取属性
                    else if(strstr((char*)&USART2_RX_BUFFER[5],"properties/get/request_id"))
                    {
                        char *p=NULL;
                        p=strstr((char*)&USART2_RX_BUFFER[5],"request_id");
                        if(p)
                        {        
                            //解析数据
                            //$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/get/request_id=5f359b5c-542f-460e-9f51-85e82150ff4a{"service_id":"gps"} 
                            strncpy(request_id,p,47);      
                        }
                        
                        //上报数据
                        sprintf(mqtt_message,"{\"services\": [{\"service_id\":\"STM32\",\"properties\":{\"temp\":%d,\"humidity\":%d,\"soil_moisture\":%d}}]}",
                        humidity,temp,soil_moisture);
                        
                        sprintf(data_buff,"$oc/devices/622ef2e8c4e6a958e356fe84_STM32/sys/properties/get/response/%s",
                        request_id);
                        
                        MQTT_PublishData(data_buff,mqtt_message,0);
                        
                        printf("发布主题:%s\r\n",data_buff);
                        printf("发布数据:%s\r\n",mqtt_message);
                    }
               
                    USART2_RX_CNT=0;
                    USART2_RX_FLAG=0;
                }
            }
    	 }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344

    6.7 硬件接线说明

    1. ESP8266 WIFI接线
    ATK-ESP8266串口WIFI模块与STM32的串口2相连接。
    PA2(TX)--RXD 模块接收脚
    PA3(RX)--TXD 模块发送脚
    GND---GND 地
    VCC---VCC 电源(3.3V~5.0V)
     
    2. TFT 1.44 寸彩屏接线
    GND   电源地
    VCC   接5V或3.3v电源
    SCL   接PC8(SCL)
    SDA   接PC9(SDA)
    RST   接PBPC10
    DC    接PB7
    CS    接PB8
    BL	  接PB11
    
    3. DHT11 温湿度
    VCC--VCC
    GND---GND
    DAT---PA5 
    
    4. 浇水电机
    GND---GND
    VCC---5V
    AO----PA4
    
    
    5. 土壤湿度
    GND---GND
    VCC---3.3V
    AO----PA1
    
    
    6. 板载LED灯接线
    LED1---PA8
    LED2---PD2
    
    
    7. 板载按键接线
    K0---PA0 
    K1---PC5 
    K2---PA15
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
  • 相关阅读:
    来聊聊关于低代码平台的三俩事
    Netty入门-Channel
    基于Java+SpringBoot+Vue物流管理小程序系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】
    net-java-php-python-网络安全教育学习网站计算机毕业设计程序
    【Unity3D】Tank对战
    Redis基础入门教程 - 概览
    【R语言文本挖掘】:主题模型(LDA)
    JavaList集合根据ParentId递归【无限套娃】
    Docker学习——Dock镜像
    maven 3.8.3 安装并配置的步骤
  • 原文地址:https://blog.csdn.net/xiaolong1126626497/article/details/125251827