- 2024-10-31
-
发表了主题帖:
【2024 DigiKey创意大赛】智慧农业 作品提交
本帖最后由 QingSpace 于 2024-11-1 16:15 编辑
# 一、作品简介
本作品以**ESP32C6**为主控,实现了对于几种农业数据的采集与通过控制相应外设调节这些因素对于农作物影响的功能【基础功能】;在此基础上,又使用APP Inventer制作了蓝牙数据接收APP,使用**树莓派5**搭建Home Assistant服务器,实现了数据的多端同步【进阶功能】;目前,可以通过操作串口屏实现外设的模式切换(自动/开/关),未来将加入通过蓝牙APP和Home Assistant网页实现外设的切换,并进一步实现临界数值的调整【未来功能】。
在本作品中,**nRF52840 Dongle**起到了调试BLE的作用,在电脑上不方便查看BLE的广播数据,使用这款迷你板卡搭配NORDIC官方的软件,就可以很方便地在电脑上查看BLE的数据便于我进行调试。
# 二、系统框图
# 三、各部分功能说明
本程序使用了FreeRTOS,下面将介绍各个任务的代码以及功能
## 3.1 传感器读取任务
```C++
void SensorDataAcquisition(void *arg)
{
// 光线传感器
Wire.begin(LP_I2C_SDA, LP_I2C_SCL);
lightMeter.begin();
// 土壤湿度传感器
pinMode(SOIL_SENSOR, INPUT);
// 温湿度传感器
dht11.setup(DHT11_PIN, DHTesp::DHT11);
while(1)
{
lux = lightMeter.readLightLevel();
soil = 100 * (soil_max - analogRead(SOIL_SENSOR)) / (soil_max - soil_min);
TH = dht11.getTempAndHumidity();
#if UART
Serial.print("Light: ");
Serial.print(lux);
Serial.println(" lx");
Serial.print("Soil: ");
Serial.print(soil);
Serial.println(" %");
Serial.print("Temperature: ");
Serial.print(TH.temperature);
Serial.print(" °C\tHumidity: ");
Serial.print(int(TH.humidity));
Serial.println(" %");
#endif
if (Serial.available() >= 9)
{
// 读取九位十六进制数据
byte data[9];
for (int i = 0; i < 9; i++)
{
data = Serial.read();
}
// 提取第七位(B7)和第八位(B8)
byte B7 = data[6];
byte B8 = data[7];
// 计算二氧化碳浓度
co2 = B7 * 256 + B8;
#if UART
// 输出结果
Serial.print("CO2 Concentration: ");
Serial.print(co2);
Serial.println(" PPM");
#endif
}
sprintf(BT, "%4.1f %2.0f %4.0f %2.0f %4.0f\n", TH.temperature, TH.humidity, lux, soil, co2);
#if UART
Serial.println();
Serial.println(BT);
#endif
delay(100);
}
}
```
任务1主要负责传感器数据的获取传感器的数据,其中光线传感器和温湿度传感器通过函数直接获取,土壤湿度传感器通过电容上的电压值计算出相对湿度,二氧化碳传感器则会通过串口把数据发送给ESP32,而ESP32C6恰好有两个串口,其中一个是LP串口(低功耗串口),它可以当成普通串口用。我将普通串口用作接收二氧化碳传感器的数据,LP串口专门与串口屏通信。
此外,我将所有的串口输出都加了“UART”宏,如果该宏的定义为1,则会在串口上显示各种数据,反之则不会,方便在调试时查看串口数据,调试好后就不需要串口输出了。
## 3.2 蓝牙任务
```C++
/* 以下为BLE部分变量 */
BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
void BLESend(const String& message);
void HMISend();
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer *pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer *pServer) {
deviceConnected = false;
}
};
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String rxValue = pCharacteristic->getValue();
#if UART
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++) {
Serial.print(rxValue);
}
Serial.println();
Serial.println("*********");
}
#endif
}
};
void BLETask(void *arg)
{
// Create the BLE Device
BLEDevice::init("Smart Agriculture");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
pRxCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
#if UART
Serial.println("Waiting a client connection to notify...");
#endif
while(1)
{
BLESend(BT);
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
#if UART
Serial.println("start advertising");
#endif
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}
}
```
此处的代码来自Arduino IDE中的ESP32的示例程序,不做过多讲解。总体上就是负责创建一个BLE设备、服务器、服务和特征,并处理设备连接和断开连接的逻辑等。在连接成功后会发送数据到客户端(即蓝牙APP)。
## 3.3 串口屏任务
```C++
HardwareSerial LP_Serial(1); //这一行在最开始
LP_Serial.begin(115200, SERIAL_8N1, LP_UART_RXD, LP_UART_TXD); //这一行在setup函数中
void HMITask(void *arg)
{
while(1)
{
LP_Serial.printf("data.temp.txt=\"%.1f\"", TH.temperature);
HMISend();
LP_Serial.printf("data.huma.txt=\"%.0f\"", TH.humidity);
HMISend();
LP_Serial.printf("data.lit.txt=\"%.0f\"", lux);
HMISend();
LP_Serial.printf("data.hums.txt=\"%.0f %\"", soil);
HMISend();
LP_Serial.printf("data.co.txt=\"%.0f\"", co2);
HMISend();
delay(1000);
if (LP_Serial.available())
{
String LP_data = LP_Serial.readStringUntil(';');
LP_data.trim();
if (LP_data == "1:0") {
led_state = 0;
} else if (LP_data == "1:1") {
led_state = 1;
} else if (LP_data == "1:2") {
led_state = 2;
} else if (LP_data == "2:0") {
fan_state = 0;
} else if (LP_data == "2:1") {
fan_state = 1;
} else if (LP_data == "2:2") {
fan_state = 2;
} else if (LP_data == "3:0") {
pump_state = 0;
} else if (LP_data == "3:1") {
pump_state = 1;
} else if (LP_data == "3:2") {
pump_state = 2;
}
}
}
}
```
此任务首先需要定义一下LP串口,然后初始化LP串口,并向串口屏不断发送传感器的数据,同时如果接收到串口屏发来的控制指令做出相应处理。外设的状态有:0-关,1-开,2-自动,而“1:2”中前一个数字是外设编号,第二个数字是外设更改后的状态,外设1是LED,外设2是风扇,外设3是水泵。
## 3.4 WiFi/MQTT任务
```C++
/* 以下为WiFi和MQTT部分变量 */
const char *ssid = "Mate 40 Pro"; // Wifi 账号
const char *password = "15239570078gzc"; // wifi 密码
//客户端变量
WiFiClient espClient;
PubSubClient client(espClient);
// 配置消息
char config_temperature[] = "{\"unique_id\":\"Smart-Agriculture-Temperature\",\"name\":\"温度传感器\",\"icon\":\"mdi:thermometer\",\"state_topic\":\"Smart-Agriculture/temperature/state\",\"json_attributes_topic\":\"Smart-Agriculture/temperature/attributes\",\"unit_of_measurement\":\"℃\",\"device\":{\"identifiers\":\"ESP32\",\"manufacturer\":\"QingSpace\",\"model\":\"HA\",\"name\":\"ESP32\",\"sw_version\":\"1.0\"}}";
char config_humidity[] = "{\"unique_id\":\"Smart-Agriculture-Humidity\",\"name\":\"空气湿度传感器\",\"icon\":\"mdi:water-percent\",\"state_topic\":\"Smart-Agriculture/humidity/state\",\"json_attributes_topic\":\"Smart-Agriculture/humidity/attributes\",\"unit_of_measurement\":\"%\",\"device\":{\"identifiers\":\"ESP32\",\"manufacturer\":\"QingSpace\",\"model\":\"HA\",\"name\":\"ESP32\",\"sw_version\":\"1.0\"}}";
char config_light[] = "{\"unique_id\":\"Smart-Agriculture-Light\",\"name\":\"光照传感器\",\"icon\":\"mdi:brightness-5\",\"state_topic\":\"Smart-Agriculture/light/state\",\"json_attributes_topic\":\"Smart-Agriculture/light/attributes\",\"unit_of_measurement\":\"lux\",\"device\":{\"identifiers\":\"ESP32\",\"manufacturer\":\"QingSpace\",\"model\":\"HA\",\"name\":\"ESP32\",\"sw_version\":\"1.0\"}}";
char config_soil_moisture[] = "{\"unique_id\":\"Smart-Agriculture-Soil-Moisture\",\"name\":\"土壤湿度传感器\",\"icon\":\"mdi:water-outline\",\"state_topic\":\"Smart-Agriculture/soil-moisture/state\",\"json_attributes_topic\":\"Smart-Agriculture/soil-moisture/attributes\",\"unit_of_measurement\":\"%\",\"device\":{\"identifiers\":\"ESP32\",\"manufacturer\":\"QingSpace\",\"model\":\"HA\",\"name\":\"ESP32\",\"sw_version\":\"1.0\"}}";
char config_co2[] = "{\"unique_id\":\"Smart-Agriculture-CO2\",\"name\":\"二氧化碳传感器\",\"icon\":\"mdi:molecule-co2\",\"state_topic\":\"Smart-Agriculture/co2/state\",\"json_attributes_topic\":\"Smart-Agriculture/co2/attributes\",\"unit_of_measurement\":\"ppm\",\"device\":{\"identifiers\":\"ESP32\",\"manufacturer\":\"QingSpace\",\"model\":\"HA\",\"name\":\"ESP32\",\"sw_version\":\"1.0\"}}";
// MQTT Broker 服务端连接
const char *mqtt_broker = "192.168.43.179";//mqtt服务器地址
const char *mqtt_username = "QSG";
const char *mqtt_password = "gzc20050414";
const int mqtt_port = 1883;//端口
void MQTTTask(void *arg)
{
// connecting to a WiFi network
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(2000);
#if UART
Serial.println("Connecting to WiFi...");
#endif
}
#if UART
Serial.println("Connected to WiFi");
#endif
//connecting to a mqtt broker 连接服务端
client.setBufferSize(512); // 增加消息缓冲区大小
client.setServer(mqtt_broker, mqtt_port);
while (!client.connected()) {
String client_id = "esp32-client-";
client_id += String(WiFi.macAddress());
Serial.printf("The client %s connects to the public mqtt broker\n", client_id.c_str());
if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
#if UART
Serial.println("Public emqx mqtt broker connected");
#endif
} else {
#if UART
Serial.print("failed with state ");
Serial.print(client.state());//返回连接状态
#endif
delay(2000);
}
}
// 发送初始化配置消息
client.publish("homeassistant/sensor/HA/Smart-Agriculture-Temperature/config", config_temperature);
client.publish("homeassistant/sensor/HA/Smart-Agriculture-Humidity/config", config_humidity);
client.publish("homeassistant/sensor/HA/Smart-Agriculture-Light/config", config_light);
client.publish("homeassistant/sensor/HA/Smart-Agriculture-Soil-Moisture/config", config_soil_moisture);
client.publish("homeassistant/sensor/HA/Smart-Agriculture-CO2/config", config_co2);
while(1)
{
client.publish("Smart-Agriculture/temperature/state", String(TH.temperature).c_str());
client.publish("Smart-Agriculture/humidity/state", String(TH.humidity).c_str());
client.publish("Smart-Agriculture/light/state", String(lux).c_str());
client.publish("Smart-Agriculture/soil-moisture/state", String(soil).c_str());
client.publish("Smart-Agriculture/co2/state", String(co2).c_str());
delay(1000);
}
}
```
此任务的配置部分也是节选自Arduino IDE中ESP32的示例程序,主要说一下发送的信息内容。在每次连接成功后,我们需要先向搭建在树莓派上的MQTT服务器发送传感器的初始化配置信息,如果Home Assistant上没有相应的设备则会添加上去,如果已经有了就不会有变化。然后不断向MQTT服务器发送传感器数据,不断更新网页上的传感器数据。这样可以在相对较远的距离下无线查看传感器数据。不过目前只能在局域网内查看,即ESP32,树莓派和查看数据的设备要再同一个局域网内。
## 3.5 外设控制任务
```C++
void ControlTask(void *arg)
{
pinMode(LED_PIN, OUTPUT);
pinMode(FAN_PIN1, OUTPUT);
pinMode(FAN_PIN2, OUTPUT);
while(1)
{
if(led_state == 2)
{
digitalWrite(LED_PIN, lux < lux_limit ? HIGH : LOW);
}
else
{
digitalWrite(LED_PIN, led_state ? HIGH : LOW);
}
if(fan_state == 2)
{
analogWrite(FAN_PIN1, (TH.temperature > temp_limit ? 1 : 0) * 75);
}
else
{
analogWrite(FAN_PIN1, (fan_state ? 1 : 0) * 75);
}
if(pump_state == 2)
{
analogWrite(PUMP_PIN1, (soil < soil_limit ? 1 : 0) * 255);
}
else
{
analogWrite(PUMP_PIN1, (pump_state ? 1 : 0) * 255);
}
}
}
```
这里就比较简单了,只是根据外设的状态去自动或手动控制外设的运行与否。
# 四、作品源码
https://download.eeworld.com.cn/detail/QingSpace/634862
# 五、作品功能演示视频
# 六、项目总结
在该项目中,我第一次使用到了树莓派,遇到了很多问题,但是都被我一一克服。同时,我也发现了ESP32在无线通信方面的强大优势,也是第一次将ESP32投入实际应用之中,我相信在之后的创意之路上ESP32一定能成为我的好帮手。
# 七、其他
树莓派搭建Home Assistant参考了这篇文章:[[活动资料\] 【2024 DigiKey 创意大赛】环境搭建【Docker + HA + MQTT】 - DigiKey得捷技术专区 - 电子工程世界-论坛 (eeworld.com.cn)](https://bbs.eeworld.com.cn/thread-1295356-1-1.html)
-
上传了资料:
2024 DigiKey 创意大赛 代码
- 2024-10-10
-
回复了主题帖:
【2024 DigiKey 创意大赛】开箱帖:树莓派5、ESP32C6和nRF52840 Dongle
walker2048 发表于 2024-9-23 19:54
nRF52840 Dongle其实不大建议买
我买这个主要是实在不知道买什么了,这个是相对便宜而且看着有点好玩的板子。
-
回复了主题帖:
【2024 DigiKey 创意大赛】开箱帖:树莓派5、ESP32C6和nRF52840 Dongle
walker2048 发表于 2024-9-25 19:17
又贵又没啥用,淘宝有30元不到的替代产品
而且我也不知道怎么在这个板子上编程,我在Keil里把demo的编译搞定了,但是选JLink烧录不行,不知道到底该用什么烧录器
-
回复了主题帖:
【2024 DigiKey 创意大赛】开箱帖:树莓派5、ESP32C6和nRF52840 Dongle
walker2048 发表于 2024-9-25 19:17
又贵又没啥用,淘宝有30元不到的替代产品
我研究了一下,也不太清楚它有什么用。目前的用途是插在电脑上读取ESP32的BLE发送的数据,因为这个在电脑上还得找上位机,这个小东西自己带的有上位机,还不用电脑连蓝牙。那么它一般是用来做什么的呢?
- 2024-09-23
-
发表了主题帖:
【2024 DigiKey 创意大赛】开箱帖:树莓派5、ESP32C6和nRF52840 Dongle
第一次在得捷用访客下单,居然还给我了一个UPS单号,方便我查询物流信息,这待遇以前登录下单时可都没有啊
取件之后惊讶地发现这次居然不是用的硬纸盒
颜色非常讨喜的树莓派外壳
布局,焊接都很优秀的树莓派SBC,果然和之前接触的单片机开发板感觉不一样
ESP32C6
nRF52840 Dongle,意料之外的小,第一次看到这么小的开发板
- 2024-09-12
-
加入了学习《DigiKey 应用说:Raspberry Pi 5 在视频直播中的应用》,观看 Raspberry Pi 5 在视频直播中的应用
- 2024-04-14
-
回复了主题帖:
领取审核名单(第五批): 辞旧,年底清仓,100+板卡来袭,有缘来领
个人信息无误,已知晓需自己支付邮费
- 2024-03-01
-
回复了主题帖:
辞旧:年底清仓,100+板卡来袭,有缘来领
申领板卡:1.基于stm32f103的ufun学习板(第二版本)
我是电子信息类的本科生,最近刚入门stm32,想拥有一块这样的板卡联系触摸控制和各种应用。
我会利用这块板子参与各种项目,比如学校的机器人比赛,各种科技比赛和嵌入式开发的交流。