一、前言
上一篇博客提到《正点原子STM32f103ZE精英开发板实现基于ESP8266 WIFI模块温湿度数据上传至乐联网平台》,通过单片机获取采集的温湿度数据信息结合ESP8266-WIFI模块上传至乐联网平台。但是,平台的缺陷比较多: 数据上传经常中断,稳定性差;不支持双向收发控制;无Android移动端app支持,便捷性有待提升等。
为了优化这种情况,本文选用的硬件在原有的基础上增加了YL-69土壤湿度传感器,用于采集土壤湿度信息;硬件显示模块为TFT-LCD显示屏,对采集的土壤温湿度数据实时显示以及查看ESP8266-WiFi模块的运行状;云端物联网平台选用性价比更高的机智云平台,附带Android二次开发app实现数据信息的采集+存储+可视化功能。
注意,与上一篇博客不同的是,ESP8266-WIFI模块是烧录“机智云固件”—本质是基于TCP/IP通信的MQTT协议,再深挖就是客户端与服务器的双向通信,MQTT协议基本上是各大物联网平台都支持的通信协议,包括阿里云平台、百度云平台、腾讯云平台等等。
本系统旨在利用单片机和各种传感器实现数据采集,结合相应的通信协议将采集的数据上传至云端,并且开发app接收云端数据信息,能够实现数据双向控制、移动端数据的存储、查询等可视化分析功能。初步可应用于林业物联网,本设计灵感源于常州茅山森林土测(导师项目)。
本系统建议掌握基于c语言的keil嵌入式软件编程、Java语言开发、Web前端基础、Android开发、MYSQL数据库语言等。
二、准备工作
本系统兼具硬件、云平台和移动终端app三大方向,总体流程主要包括数据的采集、传输、接收、控制、存储、查询等功能。硬件模块包括STM32F103ZET6开发板,选用YL-69土壤湿度传感器、DHT11温湿度传感器、LED指示灯、警报器以及ESP8266-WIFI模块,采集数据显示模块为TTL-LCD显示屏。具体模块实物图如下:
1.一块正点原子STM32f103ZET6精英开发板
2.正点原子的ATK-ESP8266 -WIFI串口模块
3.DH11温湿度传感器
4.YL-69土壤湿度模块
5.TFT-LCD显示模块
6.ATK-USB-TTL模块
用于为ESP8266烧录使用,电脑上需安装CH340驱动程序
·注意:烧录需要使用USB-UART模块与ESP8266-WIFI模块连接;开始烧录的时候它会要求你给WIFI模块复位,如设备无自动复位,只需要使RST接一下低电平触发即可,IO-0需置低电平
·ESP8266烧录机智云固件
通过flash下载工具ESPFlashDownloadTool烧录机智云固件
机智云GAgent固件链接请点击[(GAgent for ESP8266 04020034)]:https://download.gizwits.com/zh-cn/p/92/94
烧录工具及固件链接:https://pan.baidu.com/s/1xp6mlmHo2xwitFcn7FiVQw
提取码:dprr
附硬件总工程原理图:
三、实现流程
1.keil编程
基本的机智云注册不再熬述,可以参考其他博客。那么接下来生成MCU代码包,MCU开发中:硬件方案为独立MCU方案,选择其他平台,输入基本信息中的密钥即可下载代码包了。注意,代码包中几个文件(Gizwits文件和Utils文件)需要移植到原有工程中去。
·keil中主函数代码块如下:
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "adc.h"
#include "timer.h"
#include "usart3.h"
#include "gizwits_product.h"
#include "dht11.h"
#include "lcd.h"
#include "beep.h"
/* 用户区当前设备状态结构体*/
dataPoint_t currentDataPoint;
u8 temperature;
u8 humidity;
//WIFI连接状态
//wifi_sta 0: 断开
// 1: 已连接
u8 wifi_sta=0;
float soil_moisture_temp_avrg=0;
int soil_moisture;
/* -------------------------------- begin -------------------------------- */
/**
* @Name Gizwits_Init
* @brief
* @param None
* @retval
* @author JackyFeng
* @Data 2022-01-16
* 1. ...
* <modify staff>:
* <data> :
* <description> :协议初始化
* 2. ...
**/
/* -------------------------------- end -------------------------------- */
void Gizwits_Init(void)
{
TIM3_Int_Init(9,7199);//1MS系统定时
usart3_init(9600);//WIFI初始化
memset((uint8_t*)¤tDataPoint, 0, sizeof(dataPoint_t));//设备状态结构体初始化
gizwitsInit();//缓冲区初始化
}
/* -------------------------------- begin -------------------------------- */
/**
* @Name userHandle
* @brief
* @param None
* @retval
* @author JackyFeng
* @Data 2022-01-16
* 1. ...
* <modify staff>:
* <data> :
* <description> :用户WiFi处理
* 2. ...
**/
/* -------------------------------- end -------------------------------- */
void userHandle(void)
{
u8 t=0;
static u8 time=10;
if(wifi_sta)
{
time++;
if(time==10)
{
time=0;
gizwitsGetNTP();//请求网络时间
}
delay_ms(80);
}
if(!wifi_sta)
{
if(time!=0) time=0;
}
soil_moisture=Get_Adc_Average(1,10);
if(t%10==0)
{
POINT_COLOR=BLUE;
DHT11_Read_Data(&temperature,&humidity); //读取温湿度值
LCD_ShowNum(30+40,190,temperature,2,16); //显示温度
LCD_ShowNum(30+40,230,humidity,2,16); //显示湿度
LCD_ShowNum(30+40,270,soil_moisture,2,16); //显示土壤湿度
currentDataPoint.valuetemp= temperature;
currentDataPoint.valuewet=humidity;
currentDataPoint.valuesoil_moisture=soil_moisture;
}
delay_ms(10);
t++;
if(t==20)
{
t=0;
}
}
int main(void)
{//需要接入稳定电压
int key;
u8 wifi_con=0;//记录wifi连接状态 1:连接 0:断开
delay_init();
BEEP_Init();
KEY_Init( );
LED_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(9600);
DHT11_Init();
Gizwits_Init();
T_Adc_Init();
LCD_Init();
printf("KEY1:AirLink连接模式\t KEY_UP:复位\r\n\r\n");
LCD_ShowString(160,70,200,16,16,"Forest soil temp and humi");
POINT_COLOR=RED;
LCD_ShowString(30,110,200,16,16,"DHT11 OK");
LCD_ShowString(30,150,200,16,16,"YL-69 OK");
POINT_COLOR=BLUE;
LCD_ShowString(30,190,200,16,16,"Temp: C");
LCD_ShowString(30,230,200,16,16,"Humi: %");
LCD_ShowString(30,270,200,16,16,"Soil: %");
while(1)
{
userHandle();
printf("当前温度:%d度 当前湿度:%d 土壤湿度:%d\r\n",temperature,humidity,soil_moisture);
if(wifi_con!=wifi_sta)
{
wifi_con=wifi_sta;
wifi_con?printf("connect"):printf("close");
}
gizwitsHandle((dataPoint_t *)¤tDataPoint);//上报当前数据协议处理
key = KEY_Scan(0);
if(key==KEY1_PRES)//KEY1按键
{
BEEP=0;
LED0 = 0;
LED1 = 1;
printf("WIFI进入SOFTAP连接模式\r\n");
gizwitsSetMode(WIFI_SOFTAP_MODE);
LCD_ShowString(30,310,200,16,16,"WIFI_SOFTAP_MODE");
delay_ms(30);
}
if(key==WKUP_PRES)//KEY_UP按键
{
BEEP=1;
LED1 = 0;
LED0 = 1;
printf("WIFI复位,请重新配置连接\r\n");
gizwitsSetMode(WIFI_RESET_MODE);
wifi_sta=0;
delay_ms(50);
}
delay_ms(30);
if(wifi_sta==0)
{
POINT_COLOR=BLUE;
LCD_ShowString(30,350,250,16,24,"WIFI OFF DISCONNECT");
}
else
{
POINT_COLOR=RED;
LCD_ShowString(30,350,250,16,24,"WIFI OK CONNECT ");
}
}
}
2.机智云台开发
1.布置相关节点,确保所用的温湿度传感器、土壤湿度传感器、LED、蜂鸣器等参数设置正确。
2.启动开发板及插入ESP8266-WiFi模块,配置入网后可以看到,Web云端可视化界面:
3.Android开发
1.云平台需要生成Android框架包,主要用于后期二次开发。在“应用开发”中选择Android平台,选择相应的应用名称和密钥生成代码包并下载。
2.接下来需要用到Android Studio(AS)开发环境对app界面开发设计,对重要布局文件activity_gos_device_control.xml开发设计,最外层布局为线下布局,内部各节点(LED、Beep、Wet、Temp、soil_moisture)为相对布局,设置好各个位置的TextView组件、Button按钮、EditText组件等等
3.xml布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/header_toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="horizontal"
android:background="@drawable/esp"
>
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_color"
android:fadingEdge="vertical"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:scrollbars="vertical">
<!-- LED1(布尔可写) -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:background="@drawable/border_body_iot"
android:padding="10dp">
<TextView
android:id="@+id/tv_pic_led1"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="10dp"
android:background="@drawable/ic_led_dark" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="58dp"
android:text="LED1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="240dp"
android:text="关闭" />
<Switch
android:id="@+id/sw_bool_LED1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="270dp"
android:textOff="关闭"
android:textOn="开启" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="315dp"
android:text="开启" />
</RelativeLayout>
<-- LED2(布尔可写) -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:background="@drawable/border_body_iot"
android:padding="10dp">
<TextView
android:id="@+id/tv_pic_led2"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="10dp"
android:background="@drawable/ic_led_dark" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="58dp"
android:text="LED2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="240dp"
android:text="关闭" />
<Switch
android:id="@+id/sw_bool_LED2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="270dp"
android:textOff="关闭"
android:textOn="开启" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="315dp"
android:text="开启" />
</RelativeLayout>
<!-- beep(布尔可写) -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:background="@drawable/border_body_iot"
android:padding="10dp">
<TextView
android:id="@+id/tv_pic_beep"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="10dp"
android:background="@drawable/ic_beep" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="58dp"
android:text="警报器" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="240dp"
android:text="关闭" />
<Switch
android:id="@+id/sw_bool_beep"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="270dp"
android:textOff="关闭"
android:textOn="开启" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="315dp"
android:text="开启" />
</RelativeLayout>
<!-- temp(数值只读) -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:background="@drawable/border_body_iot"
android:padding="10dp">
<TextView
android:id="@+id/tv_pic_temp"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="10dp"
android:background="@drawable/ic_temp_dark" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="58dp"
android:text="温 度 " />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="308dp"
android:text="℃" />
<TextView
android:id="@+id/tv_data_temp"
android:layout_width="110dp"
android:layout_height="match_parent"
android:layout_marginLeft="240dp"
android:gravity="center"
android:textColor="@color/alert_blue"
android:textSize="16sp" />
</RelativeLayout>
<!-- wet(数值只读) -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:background="@drawable/border_body_iot"
android:padding="10dp">
<TextView
android:id="@+id/tv_pic_wet"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="10dp"
android:background="@drawable/ic_humidity_dark" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="58dp"
android:text="湿 度 " />
<TextView
android:id="@+id/tv_data_wet"
android:layout_width="110dp"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="240dp"
android:gravity="center"
android:textColor="@color/green"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="308dp"
android:text=" %" />
</RelativeLayout>
<!-- soil_moisture(数值只读) -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:background="@drawable/border_body_iot"
android:padding="10dp">
<TextView
android:id="@+id/tv_pic_soil_moisture"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="10dp"
android:background="@drawable/ic_soil_moisture_dark" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="58dp"
android:text="土 湿 " />
<TextView
android:id="@+id/tv_data_soil_moisture"
android:layout_width="110dp"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="240dp"
android:gravity="center"
android:textColor="@color/green"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="308dp"
android:text=" %" />
</RelativeLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/btn_nor_down"
android:orientation="horizontal">
<TextView
android:layout_width="55dp"
android:layout_height="match_parent"
android:background="@drawable/ic_green_energy_logo"
/>
<TextView
android:layout_width="55dp"
android:layout_height="match_parent"
android:background="@drawable/ic_green_energy_logo"
/>
<TextView
android:layout_width="55dp"
android:layout_height="match_parent"
android:background="@drawable/ic_green_energy_logo"
/>
<TextView
android:layout_width="55dp"
android:layout_height="match_parent"
android:background="@drawable/ic_green_energy_logo"
/>
<Button
android:id="@+id/startTimer"
android:layout_width="50dip"
android:layout_height="50dip"
android:layout_gravity="center"
android:layout_marginLeft="2dp"
android:background="@drawable/btn_cycle_red"
android:text="更新"
android:textColor="@color/blue" />
<Button
android:id="@+id/startTimer2"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="center"
android:layout_marginLeft="2dp"
android:background="@drawable/btn_cycle_red"
android:gravity="center"
android:onClick="dialog_judge"
android:text="删库"
android:textColor="@color/blue" />
<Button
android:id="@+id/music_btn"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="center"
android:layout_marginLeft="2dp"
android:background="@drawable/btn_cycle_red"
android:text="其他"
android:textColor="@color/blue" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true">
<EditText
android:id="@+id/edit1"
android:layout_width="410dp"
android:layout_height="wrap_content"
android:background="@drawable/border_edit"
android:hint="请输入查询ID..."
android:imeOptions="actionDone"
android:inputType="textVisiblePassword"
android:paddingStart="60dp"
android:paddingLeft="60dp"
android:paddingTop="10dp"
android:paddingEnd="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp"
android:singleLine="true" />
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/edit1"
android:layout_alignLeft="@id/edit1"
android:layout_alignTop="@id/edit1"
android:layout_alignBottom="@id/edit1"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:padding="12dp"
android:src="@drawable/ic_id_card" />
</RelativeLayout>
<include
android:id="@+id/tableItem"
layout="@layout/wettemp_theme" />
<ListView
android:id="@+id/listview_body"
android:layout_width="match_parent"
android:layout_height="210dp"
android:background="@color/white"
android:divider="@color/gray"
android:dividerHeight="1dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
</ScrollView>
</LinearLayout>
4.Java代码较多,重点需了解Handler处理机制,故不讲解。
四、数据可视化分析
1.LCD显示采集数据+app显示采集数据
LCD显示屏可显示采集的空气温度、空气湿度、土壤湿度信息,可以检查ESP8266的联网状态,若配置入网成功则显示 红色字体 WIFI OK CONNECT字符。
2.app显示采集数据+app数据库显示:
内置SQLite数据库,此数据库支持查询功能,更新数据支持ID自增,可显示采集数据时间点、空气温度、空气湿度、土壤湿度信息
实测视频:https://www.bilibili.com/video/bv15t4y1H7N3
五、小结
(1)结合STM32单片机、传感器来采集土壤温湿度数据,数据上传云端,云平台能够实现数据的存储和可视化功能,用户通过移动终端app接收机智云平台下发的数据包,并且手机端app拥有独立数据库,以便于对采集数据进行快速检索和分析。
(2)提高数据采集的便携性,大大减少了数据处理的时间损耗,极大提高了数据采集的效率。
(3)本系统旨在利用单片机和各种传感器实现数据采集,结合相应的通信协议将采集的数据上传至云端,并且开发app接收云端数据信息,能够实现数据双向控制、移动端数据的存储、查询等可视化分析功能,各大模块相互独立又相辅相成作为一个物联网控制系统。初步可应用于林业。