- 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
-
加入了学习《【得捷电子Follow me】第2期》,观看 【得捷电子Follow me】第2期
-
加入了学习《【Follow me第二季第1期】任务提交-使用makecode开发(JavaScript)》,观看 【Follow me第二季第1期】任务提交-使用makecode开发(JavaScript)