- 2024-11-13
-
加入了学习《【2024 DigiKey创意大赛】+智慧焊接工作台》,观看 智慧焊接工作台
-
加入了学习《基于Raspberry Pi 5的植物生长监管系统》,观看 基于Raspberry Pi 5的植物生长监管系统
- 2024-11-01
-
加入了学习《led立方体》,观看 led立方体
- 2024-10-31
-
发表了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—总结帖
本帖最后由 Wenyou 于 2024-10-31 20:08 编辑
基于Raspberry Pi 5的植物生长监管系统
作者:Wenyou
一、作品简介
系统自动对作物进行监管,根据传感器参数自动进行浇水补光操作。Web页面可以查看当前系统状态,也可以通过页面中的按钮手动开关水阀和补光灯;通过摄像头进行实时监控和定时拍照,照片可用于生成植物生长过程的GIF图片;统计页面可以查看传感器历史数据,直观了解数据变化过程;设置页面可以对系统各项参数进行配置。
本项目用到的物料如下:
树莓派5 4G、ESP32-C6-DevKitC-1-N8:微控制器。
D6T-1A-01:非接触式温度传感器。
土壤湿度传感器、湿度传感器、光照传感器、水滴传感器:可检测对应环境量。
常闭式水电磁阀:通电电磁阀打开,断电关闭。
补光灯:可模拟太阳光,促进植物生长。
二、系统框图
由于手动养殖盆栽费时费力,稍有不慎就会导致植物死亡。因此设计此植物生长监管系统,排除人为因素导致的植物长势不佳问题。系统采用Python Flask框架搭建Web界面,借助MQTT进行数据与控制信号的传输。ESP32-C6采集数据上传给树莓派,树莓派接收数据进行存储与展示,可控制浇水和补光等操作。
三、各部分功能说明
树莓派5 4G:本系统的上位机,是MQTT和Web服务器,同时也是摄像头的载体。负责数据存储与展示,视频监控与照片拍摄。
ESP32:本系统的下位机,采集D6T-1A-01、土壤湿度传感器、光照传感器等传感器数据,控制水电磁阀和补光灯的开关。
Web界面:为系统提供图形化操作界面,负责展示数据,也可对系统进行配置。
四、作品源码
此文件包含本项目所有代码。
基于Raspberry Pi 5的植物生长监管系统源码
五、作品功能演示视频
[localvideo]8b1ffcf3baa0297b6bd8e03500976ca5[/localvideo]
六、项目总结
此植物生长监管系统不仅能有效减少盆栽种植的日常压力,降低养护难度,还能使种植更加科学化、智能化。系统配备的辅助功能,比如摄像头定时拍摄图片,不仅记录了植物的生长轨迹,还可以用于生成GIF图片,增加种植乐趣。通过这次比赛的研发过程,我进一步深入学习了单片机的应用,从基础知识到实际操作都有了更全面的理解,同时也在项目开发中积累了丰富的实战经验,为今后其他有趣项目的开发打下了坚实的基础。
【2024 DigiKey 创意大赛】树莓派+ESP32+欧姆龙温度传感器开箱贴
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—1、介绍&搭建环境
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—2、数据采集&存储
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—3、Web页面
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—4、功能&接线
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—5、组装
-
上传了资料:
基于Raspberry Pi 5的植物生长监管系统源码
- 2024-10-30
-
发表了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—5、组装
本帖最后由 Wenyou 于 2024-10-30 21:19 编辑
现在万事俱备,只差组装,首先拿出花盆和种子。
将旧植株搅碎充当肥料。
撒入种子,翻拌均匀,放在一旁备用。
借助3D打印制作硬件部分的外壳。底部左侧放置电源,右侧放置水电磁阀,上面放洞洞板和继电器。
打印完成。
将各个部件装入盒子。
然后组装浇水模块,给水阀裁剪出长短合适的水管。
用电烙铁给瓶盖戳个洞,插入水管。
检查气密性,看是否漏水,如果漏水需要用热熔胶或者其他手段加固。
[localvideo]8d7cd2cff0a9b8636307c3342463e9d0[/localvideo]所有部件如图。
在花盆内安装好土壤湿度传感器和水管。
将水瓶和补光灯固定在支架上即可。
至此本项目的所有内容都已完成,演示视频与代码将在明日的总结帖中发出,感谢观看。
- 2024-10-29
-
回复了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—3、Web页面
CoderX9527 发表于 2024-10-29 19:23
楼主,啥时候上传代码捏
就这两天,发总结帖的时候
-
回复了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—4、功能&接线
wangerxian 发表于 2024-10-29 19:09
上位机用的是什么写的?
Web页面是用Python Flask写的
-
发表了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—4、功能&接线
本帖最后由 Wenyou 于 2024-10-29 18:10 编辑
传感器数据收集和Web界面数据展示部分完成后,接下来就是系统硬件功能的实现。
根据传感器收集到的数据判断当前环境状态,并作出相应动作。
如土壤干燥则打开水阀,光照强度低则打开补光灯。
在实际接线前,先通过LED灯模拟,可以直观的看到各部件是否按照预期流程运转。
[localvideo]7aadb10c6c0229a5e2603a765f03ceb6[/localvideo]功能实现后就是接线了,继续用面包板不太现实,不便于移动不说,体积也很大。
手边正好有一些洞洞板,之前并没有使用过,所以这次就用洞洞板来焊接,权当练手。
由于是初学者,上手才发现洞洞板真的很不好焊。
温度太高太低,时间太长太短都不行。牺牲了第一块用来练手的板子,第二块正式开始焊接。
在思考半天完美布局无果后选择飞线。
所幸正面看起来还不错,为了方便以后可能遇到的器件更换或PCB移植问题,使用可插拔方式连接各个器件。
将各个元器件都接上去。
用万用表测量,无明显短路问题,那么接下来便是激动人心的上电时刻,项目能否如期完成就看按下电源后会不会看到烟花了。
成功跑起来了!
功能和接线部分完成,便只剩下最终的组装了,感谢观看。
- 2024-10-23
-
加入了学习《 【2024 DigiKey 创意大赛】智能聊天机器人》,观看 智能聊天机器人
- 2024-10-22
-
发表了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—3、Web页面
本帖最后由 Wenyou 于 2024-10-22 18:47 编辑
时隔近一月,今天我来分享Web管理页面部分的内容。Web页面采用Flask框架,通过flask_login实现登录与权限验证,通过flask_sqlalchemy管理数据库,通过dash将数据绘制为图表……这部分内容太多,且为纯Web开发相关。因此代码便不详细一一介绍,感兴趣可以获取后续上传的源代码。下面直接看成果吧。
登录页:
首页:
统计页:
设置页:
主要讲解一下各个页面的功能,登录页顾名思义,用于登录系统,输入正确账号密码即可进入系统。
登录成功后进入首页,首页为系统的主要展示页。最上方是提示信息,接着是头像和系统名称。
绿色一行为导航栏,用于切换系统页面。
三个蓝色框分别为数据展示、指示灯和开关,鼠标悬停即可查看对应图标的注释。
数据展示框中展示当前ESP32通过传感器获取到的温度、湿度和光照强度数据、每隔数秒自动刷新。
指示灯框中为当前模块的实时状态,如果有状态变化,相应指示灯会变亮。
开关用于改变对应模块状态,可以控制水阀和补光灯的开启和关闭。
最下面是视频监控部分,红色方框为摄像头的开关,同时摄像头会每隔一定时间自动保存一张图片,可用于生成延时GIF图片。
切换到统计页。这里可以查看指定时间段内选中字段的数据统计图。
设置页可以修改用户名,并且可以对系统变量进行一些简单设置,例如补光灯和水阀总开关,补光灯阈值,数据展示时从数据库中取值的间隔,首页传感器数据更新间隔等。
Web页面部分的功能暂时就这么多,由于时间关系,先将系统搭建好再开发新的功能。
- 2024-10-12
-
加入了学习《【Follow me第二季第2期】演示视频》,观看 2.放大正弦波信号
-
加入了学习《【Follow me第二季第2期】演示视频》,观看 1.演示矩阵LED,滚动显示hello world
-
加入了学习《【Follow me第二季第2期】演示视频》,观看 3.演示上传温度湿度到HomeAssistant
- 2024-10-09
-
加入了学习《养老院智能看护系统》,观看 养老院智能看护系统
- 2024-09-23
-
发表了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—2、数据采集&存储
本帖最后由 Wenyou 于 2024-9-23 22:10 编辑
这次我来分享数据采集和存储过程,数据采集主要由下位机ESP32完成,采集的数据通过mqtt传输给上位机树莓派5,并由树莓派5存储进sqlite数据库中完成数据获取和存储的过程。
首先搭建mqtt环境,使用emqx作为mqtt服务端。
在https://www.emqx.com/zh/downloads-and-install/broker选择适合自己系统的版本。
页面中给出了下载安装和启动的代码,复制执行即可。
启动成功后通过树莓派的IP地址加上18083端口进入登录界面,使用默认账号密码admin/public登录。
登录成功即可看到mqtt管理界面,这里可以实时显示mqtt的连接数量与信息发布接收情况,便于开发。
服务端安装完成,下面给ESP32安装mqtt。
上节中我们给ESP32安装了CircuitPython开发环境,CircuitPython默认并没有支持mqtt的库,需要我们手动编写或添加,前往https://github.com/adafruit/Adafruit_CircuitPython_Bundle下载对应CircuitPython版本的捆绑包,此捆绑包中包含了大量可能会用到的CircuitPython库。
通过Thonny连接ESP32,然后根据下图所示方式即可上传mqtt库。
mqtt库可能会依赖其他库,缺少什么根据上面的方法上传即可。
库上传好了,找一个示例代码验证连接。
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
import os
import ssl
import time
import ipaddress
import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
#连接WIFI
wifi.radio.connect(os.getenv("SSID"), os.getenv("PASSWORD"))
#mqtt服务端IP
ping_ip = ipaddress.IPv4Address(os.getenv("mqtt_broker"))
ping = wifi.radio.ping(ip=ping_ip)
if ping is None:
print("连接失败")
else:
print(f"Pinging 'mqttserver' took: {ping * 1000} ms")
# Setup a feed named 'photocell' for publishing to a feed
photocell_feed ="/feeds/photocell"
# Setup a feed named 'onoff' for subscribing to changes
onoff_feed = "/feeds/onoff"
def connected(client, userdata, flags, rc):
# This function will be called when the client is connected
# successfully to the broker.
print(f"Connected to Adafruit IO! Listening for topic changes on {onoff_feed}")
# Subscribe to all changes on the onoff_feed.
client.subscribe(onoff_feed)
def disconnected(client, userdata, rc):
# This method is called when the client is disconnected
print("Disconnected from Adafruit IO!")
def message(client, topic, message):
# This method is called when a topic the client is subscribed to
# has a new message.
print(f"New message on topic {topic}: {message}")
# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)
# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
broker=os.getenv("mqtt_broker"),
port=1883,
socket_pool=pool,
)
# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message
# Connect the client to the MQTT broker.
print("Connecting to MQTT...")
mqtt_client.connect()
photocell_val = 0
while True:
# Poll the message queue
mqtt_client.loop(timeout=1)
# Send a new message
print(f"Sending photocell value: {photocell_val}...")
mqtt_client.publish(photocell_feed, photocell_val)
print("Sent!")
photocell_val += 1
time.sleep(2)
接下来进行数据采集。
首先是必选物料中的D6T-1A-01,用来获取温度。
这个模块文档中并没有给出适用于CircuitPython的示例代码,倒是有C写的适用于Raspberry Pi平台的示例,那么我们直接拿来用。
https://github.com/omron-devhub/d6t-2jcieev01-raspberrypi为代码地址。
顺便把D6T-1A-01的代码贴出来。
/*
* MIT License
* Copyright (c) 2019, 2018 - present OMRON Corporation
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/* includes */
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <stdbool.h>
#include <time.h>
/* defines */
#define D6T_ADDR 0x0A // for I2C 7bit address
#define D6T_CMD 0x4C // for D6T-44L-06/06H, D6T-8L-09/09H, for D6T-1A-01/02
#define N_ROW 1
#define N_PIXEL 1
#define N_READ ((N_PIXEL + 1) * 2 + 1)
#define RASPBERRY_PI_I2C "/dev/i2c-1"
#define I2CDEV RASPBERRY_PI_I2C
uint8_t rbuf[N_READ];
double ptat;
double pix_data[N_PIXEL];
/* I2C functions */
/** <!-- i2c_read_reg8 {{{1 --> I2C read function for bytes transfer.
*/
uint32_t i2c_read_reg8(uint8_t devAddr, uint8_t regAddr,
uint8_t *data, int length
) {
int fd = open(I2CDEV, O_RDWR);
if (fd < 0) {
fprintf(stderr, "Failed to open device: %s\n", strerror(errno));
return 21;
}
int err = 0;
do {
if (ioctl(fd, I2C_SLAVE, devAddr) < 0) {
fprintf(stderr, "Failed to select device: %s\n", strerror(errno));
err = 22; break;
}
if (write(fd, ®Addr, 1) != 1) {
fprintf(stderr, "Failed to write reg: %s\n", strerror(errno));
err = 23; break;
}
int count = read(fd, data, length);
if (count < 0) {
fprintf(stderr, "Failed to read device(%d): %s\n",
count, strerror(errno));
err = 24; break;
} else if (count != length) {
fprintf(stderr, "Short read from device, expected %d, got %d\n",
length, count);
err = 25; break;
}
} while (false);
close(fd);
return err;
}
/** <!-- i2c_write_reg8 {{{1 --> I2C read function for bytes transfer.
*/
uint32_t i2c_write_reg8(uint8_t devAddr,
uint8_t *data, int length
) {
int fd = open(I2CDEV, O_RDWR);
if (fd < 0) {
fprintf(stderr, "Failed to open device: %s\n", strerror(errno));
return 21;
}
int err = 0;
do {
if (ioctl(fd, I2C_SLAVE, devAddr) < 0) {
fprintf(stderr, "Failed to select device: %s\n", strerror(errno));
err = 22; break;
}
if (write(fd, data, length) != length) {
fprintf(stderr, "Failed to write reg: %s\n", strerror(errno));
err = 23; break;
}
} while (false);
close(fd);
return err;
}
uint8_t calc_crc(uint8_t data) {
int index;
uint8_t temp;
for (index = 0; index < 8; index++) {
temp = data;
data <<= 1;
if (temp & 0x80) {data ^= 0x07;}
}
return data;
}
/** <!-- D6T_checkPEC {{{ 1--> D6T PEC(Packet Error Check) calculation.
* calculate the data sequence,
* from an I2C Read client address (8bit) to thermal data end.
*/
bool D6T_checkPEC(uint8_t buf[], int n) {
int i;
uint8_t crc = calc_crc((D6T_ADDR << 1) | 1); // I2C Read address (8bit)
for (i = 0; i < n; i++) {
crc = calc_crc(buf[i] ^ crc);
}
bool ret = crc != buf[n];
if (ret) {
fprintf(stderr,
"PEC check failed: %02X(cal)-%02X(get)\n", crc, buf[n]);
}
return ret;
}
/** <!-- conv8us_s16_le {{{1 --> convert a 16bit data from the byte stream.
*/
int16_t conv8us_s16_le(uint8_t* buf, int n) {
uint16_t ret;
ret = (uint16_t)buf[n];
ret += ((uint16_t)buf[n + 1]) << 8;
return (int16_t)ret; // and convert negative.
}
void delay(int msec) {
struct timespec ts = {.tv_sec = msec / 1000,
.tv_nsec = (msec % 1000) * 1000000};
nanosleep(&ts, NULL);
}
void initialSetting(void) {
}
/** <!-- main - Thermal sensor {{{1 -->
* Read data
*/
int main() {
int i;
int16_t itemp;
delay(220);
while(1){
// Read data via I2C
memset(rbuf, 0, N_READ);
uint32_t ret = i2c_read_reg8(D6T_ADDR, D6T_CMD, rbuf, N_READ);
D6T_checkPEC(rbuf, N_READ - 1);
//Convert to temperature data (degC)
ptat = (double)conv8us_s16_le(rbuf, 0) / 10.0;
for (i = 0; i < N_PIXEL; i++) {
itemp = conv8us_s16_le(rbuf, 2 + 2*i);
pix_data[i] = (double)itemp / 10.0;
}
//Output results
printf("PTAT: %4.1f [degC], Temperature: ", ptat);
for (i = 0; i < N_PIXEL; i++) {
printf("%4.1f, ", pix_data[i]);
}
printf("[degC]\n");
delay(100);
}
}
// vi: ft=c:fdm=marker:et:sw=4:tw=80
根据图示连接对应引脚,SDA连接树莓派GPIO2,SCL连接树莓派GPIO3。
示例网站中给出了详细的编译运行步骤。
编译成功后尝试执行d6t-1a,发现报错。
去代码中看一下报错位置,应该是找不到I2C接口。
想起来还没有开启I2C接口,需要先配置一下,使用sudo raspi-config打开配置界面,选择第三项接口配置。
选择第五项I2C。
选择Yes。
如下图所示则开启成功。
重新运行示例代码,这次成功获取到了当前的环境温度,示例程序的采样速率还是很快的,差不多一秒十行。手掌从传感器上方划过,示数可以很快变化。
验证了传感器可用,接下来将它连接到下位机ESP32上进行开发,我们需要获取当前温度并通过mqtt传输给上位机树莓派。
首先进行接线,SDA连接GPIO6,SCL连接GPIO7,需要注意的是ESP32需要外接上拉电阻。
接线如下图所示。
接好线后使用下面的代码进行I2C地址遍历,查询总线上的I2C地址。
import time
import board
import busio
# List of potential I2C busses
ALL_I2C = ("board.I2C()", "board.STEMMA_I2C()", "busio.I2C(board.IO7, board.IO6)")
# Determine which busses are valid
found_i2c = []
for name in ALL_I2C:
try:
print("Checking {}...".format(name), end="")
bus = eval(name)
bus.unlock()
found_i2c.append((name, bus))
print("ADDED.")
except Exception as e:
print("SKIPPED:", e)
# Scan valid busses
if len(found_i2c):
print("-" * 40)
print("I2C SCAN")
print("-" * 40)
while True:
for bus_info in found_i2c:
name = bus_info[0]
bus = bus_info[1]
while not bus.try_lock():
pass
print(
name,
"addresses found:",
[hex(device_address) for device_address in bus.scan()],
)
bus.unlock()
time.sleep(2)
else:
print("No valid I2C bus found.")
可以看到成功查找到了D6T-1A的地址0xa,说明我们的接线没有问题。
接下来进行代码编写。将官方给出的C示例代码,转为适用于CircuitPython的代码。
import time
import board
import busio
D6T_ADDR = 0x0A # I2C设备地址
D6T_CMD = 0x4C # 设备命令
# 数据长度定义
N_PIXEL = 1
N_READ = (N_PIXEL + 1) * 2 + 1
# 初始化I2C总线
i2c = busio.I2C(board.IO7, board.IO6)
# crc校验
def calc_crc(data):
crc = 0
data &= 0xFF # 确保输入为8位
for _ in range(8):
if (crc ^ data) & 0x80:
crc = ((crc << 1) ^ 0x07) & 0xFF
else:
crc = (crc << 1) & 0xFF
data <<= 1
return crc
# PEC校验
def D6T_checkPEC(buf):
crc = calc_crc((D6T_ADDR << 1) | 1)
for b in buf[:-1]:
crc = calc_crc(b ^ crc)
if crc != buf[-1]:
print(f"PEC校验失败: {crc:02X}(calculated) - {buf[-1]:02X}(received)")
return False
return True
def conv8us_s16_le(buf, n):
return buf[n] + (buf[n + 1] << 8)
def read_thermal_data():
rbuf = bytearray(N_READ)
# 锁定总线
if not i2c.try_lock():
print("无法锁定I2C总线")
return None, None
try:
# 发送命令并读取数据
i2c.writeto(D6T_ADDR, bytes([D6T_CMD]))
i2c.readfrom_into(D6T_ADDR, rbuf)
except Exception as e:
print(f"I2C 读取失败: {e}")
return None, None
finally:
# 解锁总线
i2c.unlock()
# 校验PEC
if not D6T_checkPEC(rbuf):
return None, None
# 转换温度数据
ptat = conv8us_s16_le(rbuf, 0) / 10.0
pix_data = [conv8us_s16_le(rbuf, 2 + 2 * i) / 10.0 for i in range(N_PIXEL)]
return ptat, pix_data
def main():
time.sleep(0.22) # 稳定I2C总线
while True:
ptat, pix_data = read_thermal_data()
if ptat is not None:
print(f"PTAT: {ptat:.1f} [degC], Temperature: ", end="")
for pix in pix_data:
print(f"{pix:.1f}, ", end="")
print("[degC]")
else:
print("读取数据时出现错误")
time.sleep(0.1)
if __name__ == '__main__':
main()
然后将数据用mqtt传输给上位机,并由上位机存储进Sqlite数据库。
目前上位机只安装了mqtt服务端,要使用python接收数据,我们还需要安装客户端。
pip install paho-mqtt
ESP32连接mqtt发送数据:
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
import os
import ssl
import time
import ipaddress
import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
#通过d6t1a01采集数据
from d6t1a01 import read_thermal_data
#搜索WIFI
# for network in wifi.radio.start_scanning_networks():
# print(f"SSID:{str(network.ssid, 'utf-8')},RSSI:{network.rssi},Channel:{network.channel}")
# wifi.radio.stop_scanning_networks()
#连接WIFI
print(f"正在连接WIFI")
wifi.radio.connect(os.getenv("SSID"), os.getenv("PASSWORD"))
#mqtt服务端IP
ping_ip = ipaddress.IPv4Address(os.getenv("mqtt_broker"))
ping = wifi.radio.ping(ip=ping_ip)
if ping is None:
print(f"ping {os.getenv('mqtt_broker')} 失败")
else:
print(f"Pinging 'mqttserver' took: {ping * 1000} ms")
# Setup a feed named 'photocell' for publishing to a feed
temperature_feed ="/ESP32/d6t"
# Setup a feed named 'onoff' for subscribing to changes
onoff_feed = "/Pi/onoff"
def connected(client, userdata, flags, rc):
# 连接成功触发
print(f"连接成功,订阅主题: {onoff_feed}")
client.subscribe(onoff_feed)
def disconnected(client, userdata, rc):
# 断开连接触发
print("断开连接!")
def message(client, topic, message):
# 接收消息触发
print(f"接收数据:{topic}: {message}")
# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)
# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
broker=os.getenv("mqtt_broker"),
port=1883,
socket_pool=pool,
)
# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message
# Connect the client to the MQTT broker.
print("正在连接 MQTT...")
mqtt_client.connect()
while True:
# 检查是否有消息到达或其他需要处理的事件
mqtt_client.loop(timeout=1)
temperature = read_thermal_data()
print(f"发送温度数据: {temperature[1][0]}")
mqtt_client.publish(temperature_feed, temperature[1][0])
time.sleep(2)
树莓派5连接mqtt接收数据并存储:
import time
import datetime
import sqlite3
from paho.mqtt import client as mqtt
conn = sqlite3.connect('test.db',check_same_thread=False)
c = conn.cursor()
#创建数据表,执行一次后注释即可,重复执行会报错
c.execute('''CREATE TABLE temp
(id INTEGER PRIMARY KEY AUTOINCREMENT,
temperature float NOT NULL,
time DateTime NOT NULL
);''')
def on_connect(client,userdata,flags,rc):
if rc==0:
print("连接成功")
client.subscribe("/ESP32/d6t")
else:
print("连接失败")
def on_message(client,userdata,msg):
'''接收端回调函数'''
temperature = float(msg.payload.decode('utf-8'))
print(temperature)
c.execute(f'INSERT INTO temp (temperature,time) VALUES ("{temperature}","{datetime.datetime.now()}")')
conn.commit()
if temperature > 28:
client.publish(f"/Pi/onoff",True,0,False)
else:
client.publish(f"/Pi/onoff",False,0,False)
if __name__ == '__main__':
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect("127.0.0.1",1883,60)
client.loop_start() #开启一个独立的循环监听线程 loop_forever()阻塞式线程循环
except KeyboardInterrupt:
client.disconnect()
while True:
time.sleep(1)
运行代码,ESP32每隔两秒与D6T通信采集一次数据,并将数据发送给树莓派5,树莓派5接收数据后将数据存储进数据库,并根据数据的值向ESP32发送控制信号。
数据库中存储温度值与接收时间,此处间隔3秒是因为树莓派还设有1s延时。
以上便是整个系统最核心的部分,后面要做的只是数据包装与模块组装。其他传感器只需照猫画虎即可实现数据采集与存储功能。
下面简单列出其他传感器的数据采集代码,首先是DNT11,用于获取湿度,虽然它也可以获取温度,但我们有D6T-1A-01了,那个更准确。
https://github.com/adafruit/Adafruit_CircuitPython_DHT/releases
import board
import time
import adafruit_dht
def get_Temp_and_Humidity(dht):
try:
temperature = dht.temperature
humidity = dht.humidity
return (temperature, humidity)
except RuntimeError as e:
return (-1, -1)
if __name__ == "__main__":
dht = adafruit_dht.DHT11(board.IO21)
while True:
temperature,humidity = get_Temp_and_Humidity(dht)
print("temperature{:.1f}*c\t Humidity:{}%".format(temperature,humidity))
time.sleep(2.5)
然后是光照传感器,用于接收环境光照强度以判断何时打开补光灯,上面提到的CircuitPython捆绑包中含有适用于此传感器的库。
# SPDX-FileCopyrightText: 2020 Bryan Siepert, written for Adafruit Industries
# SPDX-License-Identifier: Unlicense
import time
import board
import adafruit_bh1750
import busio
i2c = busio.I2C(board.IO22,board.IO23)
sensor = adafruit_bh1750.BH1750(i2c)
while True:
print("%.2f Lux" % sensor.lux)
time.sleep(2)
接着是土壤湿度传感器与水位传感器。土壤湿度传感器用于监测土壤湿度,以判断何时打开水电磁阀浇水。水位传感器用来判断是否有水漫出花盆,防止浇水过多造成溢出。它们的原理相似,所以放在一起来介绍。
import analogio
import board
import digitalio
import time
pin = analogio.AnalogIn(board.IO3)
while True:
print(pin.value)
if(pin.value > 10000):
print("True")
else:
print("False")
time.sleep(1.5)
水位传感器:
土壤湿度传感器:
至此,本系统的数据采集与存储部分介绍完毕,下节计划分享Web界面部分,感谢大家的观看。
- 2024-08-25
-
发表了主题帖:
【2024 DigiKey 创意大赛】基于Raspberry Pi 5的植物生长监管系统—1、介绍&搭建环境
本帖最后由 Wenyou 于 2024-8-25 21:15 编辑
此次创意大赛我的项目是基于Raspberry Pi 5的植物生长监管系统,其实这个项目并不复杂,想必之前也有不少前辈做过类似的项目。我想做这个是因为前段时间心血来潮想养养盆栽,于是选择了听说比较好养的迷迭香入门,结果没过多久它就变成下面这样了。可能是浇水或光照不足导致的,既然我自己靠人力养不好,我就想做一套可以自动浇水补光的植物监管系统,让我可以享受养好盆栽的感觉,同时因为这套系统是自己做的,所以也会有参与感(大概)。不过就算养盆栽的参与感不强,制作嵌入式项目的参与感肯定会爆棚。可能这就是失之东隅,收之桑榆吧。别的不说,起码能提升自己的编程与实践经验。
本系统计划采用软硬件结合的方式实现,可以进行人机交互,提高种植参与感。
本系统分为三个模块:Web管理模块(Flask、Dash)、数据收集模块(传感器)、植物监管模块(摄像头、电磁阀、补光灯)
首先我来介绍一下此项目需要用到的硬件部分,主要是单片机和各种传感器,还有摄像头、水电磁阀、补光灯等。
树莓派5 4G版本,它将作为此系统的上位机,负责发送指令调度各个模块,协调各模块之间的数据流转,同时它将作为Web管理界面的服务器,实现人机交互。
ESP32-C6-DevKitC-1 v1.2 作为此系统的下位机,它负责收集数据传输给上位机,同时负责执行上位机下发的指令。
从左向右依次是:欧姆龙非接触式温度传感器D6T-1A-01、土壤湿度传感器、光照强度传感器、水位传感器,它们负责收集数据。
从左向右依次是:摄像头、水电磁阀(常闭)、补光灯,它们负责具体的植物监管操作。
从左向右依次是:5v电源、4路继电器,它们负责传感器与监管模块硬件的独立供电。
接下来是软件部分,Web管理界面将基于Python Flask框架实现,它可以显示各传感器收集到的数据,通过摄像头对植物进行实时监控,打开、关闭水电磁阀或补光灯等操作。同时使用Dash进行图表绘制,显示历史数据以及变化过程,方便进行数据分析。
最后进行环境搭建,树莓派采用官方最新的Raspberry pi系统,Esp32采用CircuitPython进行开发。
对于像树莓派系统的安装这种流程,网上有非常多详细的教程,我将只进行大概记录,主要记录与此项目相关的部分和解决问题的过程。
首先通过官方烧录工具烧录最新系统,这里选择第一个。
点击NEXT后弹出弹窗,点击编辑设置。
配置账号密码、WiFi、时区并开启SSH。这样烧录成功后树莓派会自动尝试连接WiFi,并且可以通过SSH连接,不需要显示器。
烧录会清空SD卡中的数据,注意不要选错卡了。
等待烧录成功。
烧录成功后,插卡上电,等待自动连接WiFi。
此时即可通过SSH连接到树莓派。
我选择FinalShell作为SSH连接工具。
连接成功后说明系统安装已经没有问题,这里我为了操作方便继续安装xrdp以支持远程桌面连接,sudo apt-get install xrdp。
安装成功后即可通过Windows自带的远程桌面连接工具进行连接。
至此系统安装完成,下面安装Web管理模块需要用到的Flask和Dash库,先安装pipenv库用于创建虚拟环境方便进行项目管理。
首先创建项目文件夹,然后通过点击上方图标或按ctrl+alt+t打开命令行窗口。
尝试从豆瓣源安装pipenv,发现报错。原来是Python担心直接安装在系统中会破坏系统本身的环境,推荐安装在虚拟环境中。
pipenv就是用来创建虚拟环境的,我这里直接安装在系统中。可以看到报错提示中给出了无视此风险的方法,也就是加上:--break-system-packages
加上--break-system-packages后看起来安装成功了,但是依然有许多警告,第一张图中的警告意思是从不信任的源中安装,加上--trusted-host 源地址 表示信任。第二张图提示/home/pi/.local/bin不在环境变量中。所以将提示中的/home/pi/.local/bin添加到环境变量中。
编辑~/.bashrc,在最后加上export PATH=/home/pi/.local/bin:$PATH。保存退出。
执行source .bashrc使配置生效。然后卸载pip uninstall pipenv --break-system-packages,
重新安装pip install pipenv -i http://pypi.douban.com/simple --break-system-packages --trusted-host pypi.douban.com。
这次安装就不会报错了,最上面的警告是连接失败的警告,说明网络不佳。
编辑时我才注意到,为什么加了-i http://pypi.douban.com/simple 还是从默认的源去下载,怪不得会报连接失败的警告。原来是豆瓣源的地址换了,换成了https://pypi.doubanio.com/simple
网上老教程中的源已经不能用了,我用了那么久的豆瓣源原来是在掩耳盗铃。新源是https,也不用加--trusted-host了,所以应该使用这条命令pip install pipenv -i https://pypi.doubanio.com/simple --break-system-packages。
安装成功后进入项目文件夹中,使用pipenv shell在本文件夹创建虚拟环境并进入。可以注意到命令行开头出现了括号和文件夹名称表示此时在虚拟环境中。
在虚拟环境中使用pip安装的库都会安装在此虚拟环境中。
安装flask。
安装dash。
编写一个测试页面,名称为app.py。
在目录下使用flask run运行flask程序,它会默认寻找app.py作为主程序。--host=0.0.0.0表示允许所有ip访问。
在电脑上通过浏览器访问树莓派IP地址的5000端口即可看到刚刚编写的测试页面。
至此树莓派环境部分搭建完成,下面开始搭建ESP32-C6的环境。
这部分颇费周章,我本来想装CircuitPython,没装成;又装了ESP-IDF,不会用;最后还是选择了Arduino。(编辑时补充:这次烧录CircuitPython成功了,还是选CircuitPython)
记录一下踩坑过程吧。
先下载bin文件https://circuitpython.org/board/espressif_esp32c6_devkitc_1_n8/。
1、首先尝试通过Thonny安装CircuitPython,选择固件后提示Unkown,似乎不支持esp32c6。
点开下拉框,其中也没有ESP32-C6。
2、尝试通过浏览器进行CircuitPython烧录(教程:https://learn.adafruit.com/circuitpython-with-esp32-quick-start/web-serial-esptool)
报错,连接失败。
3、尝试通过命令行工具ESPTool烧录。(教程:https://learn.adafruit.com/circuitpython-with-esp32-quick-start/command-line-esptool)
成功读取到了板子的信息。
成功擦除闪存信息。
烧录成功。
这部分内容是我在编辑此贴时跟着教程重新走了一遍,之前尝试时即便烧录成功也无法使用Thonny连接,会报下面的错误。
因此我打算尝试一个新的编辑器Mu,也是教程中推荐的。
在Screen Configuration中搜索mu,勾选后面的install,点击Apply按钮即可开始安装。
安装成功后会出现在菜单栏中。
结果找不到设备,无法连接。
正当我心灰意冷,以为这次要无功而返时,我打开了Thonny。
Thonny连接成功!
那么是什么原因导致之前尝试烧录CircuitPython无法成功呢?之前是在Windows中烧录,固件版本为9.1.1,这次是使用树莓派烧录,固件版本为9.1.2,esptool版本都为4.7.0。
尝试在Windows上复现一下刚才的操作,先烧录9.1.1版本固件。
尴尬了,直接连接成功。那么我知道问题出在哪里了,上次烧录时我看到教程中关于偏移值的提示,加了偏移值和引导程序(bootloader.bin和partition-table.bin),烧录失败后在修改偏移值和引导程序的路上一去不返了,看来CircuitPython是自带引导的,果然大道至简啊。
既然CircuitPython安装成功了,那么剩下的两个方法大概贴下教程地址好了。
4、安装ESP-IDF(教程:https://github.com/espressif/vscode-esp-idf-extension/blob/master/docs/tutorial/install.md)
5、通过Arduino开发(教程:https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html)
那么树莓派和ESP32-C6的环境都搭建成功,本帖也告一段落,下次计划分享数据采集过程。感谢大家的观看。
- 2024-08-10
-
发表了主题帖:
【2024 DigiKey 创意大赛】树莓派+ESP32+欧姆龙温度传感器开箱贴
本帖最后由 Wenyou 于 2024-8-10 00:11 编辑
得捷的产品包装真的很不错,三个小东西发了个大箱子,并且都有独立包装,专业感拉满。
图一 物料独立包装
本次大赛我选择了树莓派5 4G版本,Esp32-C6和欧姆龙温度传感器,作为一个单片机爱好者不管多少次看到这样精美的板子都会觉得赏心悦目。
图二 树莓派与Esp32
图三 欧姆龙温度传感器
传感器由于手头没有合适的线,就先不测试了,下面主要测试一下树莓派和Esp32。
图四 树莓派接线
给树莓派接好线,插上装好系统的SD卡,就可以亮机啦!
图五 树莓派点亮屏幕
接下来是Esp32,正当我考虑该怎么给它写一个点灯程序测试的时候,插上线,它板载的RGB灯自动开始闪了。嗯……一个合格的单片机要学会自己点灯,不错,好板子。
[localvideo]39f54c86133476de5e529b12621485f7[/localvideo]
- 2024-08-02
-
加入了学习《【DigiKey创意大赛】多通道微型气相色谱采集单元》,观看 多通道微型气相色谱采集单元
-
加入了学习《【Follow me第二季第1期】全部任务演示》,观看 全部任务演示2.0