基于 Arduino ESP8266 的智慧农业大棚项目

基于 Arduino ESP8266 的智慧农业大棚项目
pcfx基于 Arduino ESP8266 的智慧农业大棚项目
引言
为什么做智慧大棚项目
我参加了一个大学大创相关的团队,团队主题是智慧农业大棚,我是负责技术相关的。
虽然这个团队更偏向于做ppt,写调查报告,甚至有点吹牛逼的成分在里面,但是我还是决心坚持做出点成果,能弄出一个技术相关的方案。
这个方案可能在很多计算机大佬面前可能看起来很 ”简单“ 甚至 “弱智”,但是这里面都是我不断自学,不断挑战自我做出来的成果,也同样是我自己成长的一个记录。
希望有一天我成为一个大佬后,看到我的项目也能会心一笑,能轻松说出当年误区和弯路。
ESP8266 的优势
我选择esp8266作为微控制器有如下原因:
- 我有部分arduino uno R3的开发经验,当年小学玩过arduino scratch。而esp8266能使用arduino开发且自带Wi-Fi模块。
- 太极创客(太极创客 – Arduino,人工智能,物联网的应用、开发和学习资料)是我arduino的启蒙老师之一。而他们有完备的esp8266教程。
本文目标
分享硬件组成与基本功能。包括硬件部分的设计,和MQTT网络的连接。
项目硬件介绍
核心控制器:ESP8266
- 我选用的是和ESP8266-NodeMCU硬件参考 – 太极创客教程一致的开发板。串口芯片是cp2102版本。
我对这块开发板其实很满意,作为新手来说已经很足够了。不过美中不足的是它只有一个模拟输入口(A0)。原本我打算用无源光敏电阻来检测光照,但土壤湿度传感器必须占用这个模拟口,所以我最终选择了走
I²C 接口的 GY-302 光照传感器。
传感器部分
下图是我很早之前用 Fritzing画的图,接线可能与后文不太对的上。

实物图:

DHT22
我原先选择的是DHT11传感器,想着刚好有太极创客的DHT11教程可以参考。
但是按照教程一走,发现DHT11的精度实在是不太让人满意,温度只能输出整数 ,故选择了DHT22。
我是用的是github.com/dvarrel/DHT22 DHT22库,在arduino库管理能直接下载。
示例代码:DHT22/examples/dht22test dvarrel/DHT22 · GitHub
核心代码如下:
1
2
3
4
5
6
DHT22 dht22(pinDATA);
//下面两行代码放到loop()
float t = dht22.getTemperature();
float h = dht22.getHumidity();SGP30:空气质量传感器(CO₂、VOC检测)
使用的是GitHub - RobTillaart/SGP30: Arduino library for SGP30 environment sensor库,同样在arduino能直接下。
示例代码:SGP30/examples/SGP30_demo/SGP30_demo.ino at master · RobTillaart/SGP30 · GitHub
核心代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SGP30 SGP;
Wire.begin();
SGP.GenericReset();
//setRelHumidity(float T, float RH) sets the compensation for temperature (5-55°C) and relative humidity (10-95%). These values can be obtained e.g. from an SHT30, DHT22 or similar sensor.
// T in °C
// RH == 0..100
SGP.setRelHumidity(float T, float RH);
Serial.println("\nTVOC \teCO2 \tH2 \tETH");
Serial.print(SGP.getTVOC());
Serial.print("\t");
Serial.print(SGP.getCO2());
Serial.print("\t");
Serial.print(SGP.getH2());
Serial.print("\t");
Serial.print(SGP.getEthanol());
Serial.println();需要注意的是:
- 开机后需要预热一段时间,数值才能趋于稳定。
- H2 和乙醇的获取是带有实验性质的,没有经过校准,只能作为相对参考。
- CO2 单位为 ppm,TVOC 单位为 ppb。H2 和乙醇的单位为 ppm。
- setRelHumidity(float T, float RH)函数填入的是摄氏度和1到100的相对湿度,绝对湿度有其矫正函数可以适用。
GY-302(BH1750光照强度传感器):测量光照,I²C接口
使用GitHub - claws/BH1750 库,也同样能在arduino库管理下载。
我使用了示例里面的BH1750autoadjust 能高精度,自动选择量程(测量时间寄存器),在高亮度下不超量程,防止溢出,在低亮度下能提高灵敏度,测量微弱光线。
除了能选择高精度,选择测量时间寄存器(Measurement Time Register,MTreg) ,还能选择工作模式是单次还是连续。
具体的可以去GitHub上的README.md看看,就不一一列举了。
核心代码:
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
BH1750 lightMeter;
Wire.begin();
lightMeter.begin(BH1750::ONE_TIME_HIGH_RES_MODE);
if (lightMeter.measurementReady(true))
{
float lux = lightMeter.readLightLevel();
if (lux < 0)
{
Serial.println(F("[system] 光照读取错误!"));
}
// 可选:根据光照强度调整 MTreg
if (lux > 40000.0)
{
lightMeter.setMTreg(32);
}
else if (lux > 10.0)
{
lightMeter.setMTreg(69);
}
else
{
lightMeter.setMTreg(138);
}
// 调试打印
Serial.print("[system] 光照: ");
Serial.print(data.light);
Serial.println(" lx");
}注意:
- 返回值为-1:没有从传感器传输有效数据。可能是通信错误。
- 返回值为-2:配置错误。
土壤湿度传感器:测量土壤含水量,模拟信号。
本项目用的是最简单的模拟土壤湿度传感器,按道理来说应该没什么可讲的。但是看资料的时候看到了这一条。

里面 Input voltage range is 0 - 1.0 v引起了我的注意,esp8266 VCC 是3.3v。所以我简单制作了一个分压电路,使用1kΩ和2.2kΩ的电阻进行分压。
使土壤湿度传感器在短路时,分到A0的电压在1.0伏特附近。
arduino对获取模拟口的范围是0-1024
所以经过分压后,当探针断路时,模拟口分压为1v左右,所以获得数值在 左右。
代码:
1 |
|
使用这个函数就能调用analogInPin的模拟数值。

项目设计与功能
各传感器与 ESP8266 的连接方式
- 土壤湿度检测放到了A0模拟口。
- DHT22放到了 GPIO 2口也就是D4
- GY302和SGP30 共用 iic总线
- D1 SCL
- D2 SDA
WiFi 网络与数据传输
我的项目采用mqtt协议传输,使用的代码基于太极创客的mqtt教程。
使用的是
test.mosquitto.org服务器。以下是太极创客关于mqtt传输的示范案例:
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/**********************************************************************
项目名称/Project : 零基础入门学用物联网
程序名称/Program name : publish_ranye_url
团队/Team : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author : CYNO朔
日期/Date(YYYYMMDD) : 20200813
程序目的/Purpose :
本程序旨在演示如何使用PubSubClient库使用ESP8266向MQTT服务器发布信息。
-----------------------------------------------------------------------
本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/esp8266-nodemcu-web-client/http-request/
***********************************************************************/
// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "taichi-maker";
const char* password = "12345678";
const char* mqttServer = "test.ranye-iot.net";
// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/
Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
int count; // Ticker计数用变量
void setup() {
Serial.begin(9600);
//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);
// 连接WiFi
connectWifi();
// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);
// 连接MQTT服务器
connectMQTTServer();
// Ticker定时对象
ticker.attach(1, tickerCount);
}
void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
// 每隔3秒钟发布一次信息
if (count >= 3){
pubMQTTmsg();
count = 0;
}
// 保持心跳
mqttClient.loop();
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}
void tickerCount(){
count++;
}
void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
// 发布信息
void pubMQTTmsg(){
static int value; // 客户端发布信息用数字
// 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
// 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress();
char publishTopic[topicString.length() + 1];
strcpy(publishTopic, topicString.c_str());
// 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
String messageString = "Hello World " + String(value++);
char publishMsg[messageString.length() + 1];
strcpy(publishMsg, messageString.c_str());
// 实现ESP8266向主题发布信息
if(mqttClient.publish(publishTopic, publishMsg)){
Serial.println("Publish Topic:");Serial.println(publishTopic);
Serial.println("Publish message:");Serial.println(publishMsg);
} else {
Serial.println("Message Publish Failed.");
}
}
// ESP8266连接wifi
void connectWifi(){
WiFi.begin(ssid, password);
//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}当然,esp8266也能从服务器订阅消息,但是我的项目暂时对这个功能没要求。
如果需要,可以上太极创客官网学习。
我想增强mqtt的传输安全性,想使用双向TLS验证。
注意:由于我无法访问
test.ranye-iot.net后面的教程都是基于test.mosquitto.org的。修改成双向TLS
我们需要同步时间,时间不同步会导致TLS失败
我们可以在
setup()里面校时
1
2
3
4
5
6
7
8
9
10
11
void setupTime() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
time_t now = time(nullptr);
while (now < 8 * 3600 * 2) { // 等待直到时间同步
delay(500);
now = time(nullptr);
}
Serial.printf("Current time: %s\n", ctime(&now));
}在连接上Wi-Fi后,调用
setupTime();- 将
WiFiClient换成WiFiClientSecureBearSSL
1
2
3
4
5
6
// 原来:WiFiClient wifiClient;
// 改成:
BearSSL::WiFiClientSecure wifiClient;
PubSubClient mqttClient(wifiClient);准备好三个证书
mosquitto.org.crt(CA 根证书)-client.crt(你上传 CSR 后生成的客户端证书)client.key(本地生成的私钥)第一个CA证书在这个页面下载test.mosquitto.org
第三个证书需要自己生成 跟随Generate client certificates for test.mosquitto.org 指引
然后在第二个网址上传自己生成好的CSR,就能生成并下载CRT证书
把它们粘贴进代码里:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 根证书
static const char ca_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
... test.mosquitto.org 的 CA 证书 ...
-----END CERTIFICATE-----
)EOF";
// 客户端证书
static const char client_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
... 你上传 CSR 后下载到的 client.crt ...
-----END CERTIFICATE-----
)EOF";
// 客户端私钥
static const char client_key[] PROGMEM = R"EOF(
-----BEGIN RSA PRIVATE KEY-----
... 你本地生成的 client.key ...
-----END RSA PRIVATE KEY-----
)EOF";初始化BearSSL
1
2
3
4
5
6BearSSL::X509List cert(ca_cert);
wifiClient.setTrustAnchors(&cert);
BearSSL::X509List client_crt(client_cert);
BearSSL::PrivateKey key(client_key);
wifiClient.setClientRSACert(&client_crt, &key);改服务器端口成8884
数据转化成JSON字符串发送
项目代码节选:
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
struct Data {
float air_tmp = 0;
float air_RH = 0;
uint16_t eath_H = 0;
uint16_t light = 0;
uint16_t co2 = 0;
uint16_t tvoc = 0;
float h2 = 0;
float eth = 0;
} data;
void setup() {
Serial.begin(115200);
// 创建 JSON 文档
StaticJsonDocument<256> doc;
}
void loop() {
doc["air_tmp"] = air_tmp;
doc["air_RH"] = air_RH;
doc["eath_H"] = eath_H;
doc["light"] = light;
doc["co2"] = co2;
doc["tvoc"] = voc;
doc["h2"] = h2;
doc["eth"] = eth;
char cstr[256];
serializeJson(doc, cstr, sizeof(cstr));
//到这里数据就转换成JSON格式的字符串 cstr了
Serial.println(cstr);
}
总结与展望
项目成果
- 项目实现了数据采集并实时上传
- 使用SSL加密
- 学习了MQTT协议
可以拓展的方向
- 后端尚未开发,可以开发后端
- 硬件与预想时剔除了水泵,但是扩展板存在电机驱动电路,可以添加上
- 若要添加水泵,可以采用订阅MQTT
参考资料:
- 太极创客 – Arduino,人工智能,物联网的应用、开发和学习资料
- github.com/dvarrel/DHT22
- GitHub - RobTillaart/SGP30: Arduino library for SGP30 environment sensor
- GitHub - claws/BH1750
- ESP8266 Arduino Core Documentation Release 2.4.0
- test.mosquitto.org



