4、边缘网关配置:树莓派/ESP32 基础环境搭建、GPIO与串口通信、数据初步采集脚本
好,咱们进入第四章。这一章很关键,说白了就是让硬件“开口说话”。
你想想看,传感器数据再准,如果传不上来,那就是一堆废铁。边缘网关就是那个“翻译官”和“快递员”。我习惯用树莓派和ESP32来干这活,成本低、生态好,踩过的坑也多,正好跟你聊聊。
4.1 树莓派基础环境搭建
树莓派这边,我建议直接用Raspberry Pi OS Lite版,不带桌面,省资源。你如果喜欢图形界面,装Desktop版也行,但生产环境我推荐Lite。
第一步:烧录系统
用Raspberry Pi Imager工具,选好型号、SD卡,写入系统。写入后别急着拔卡,先挂载boot分区,创建一个名为 ssh 的空文件,开启SSH。
wpa_supplicant.conf 文件,写好WiFi信息。这样上电就能远程连,不用接显示器。
第二步:基础配置
SSH进去后,第一件事:
sudo raspi-config
这里要打开几个东西:
- Interface Options → SPI、I2C、Serial Port 都启用
- Localisation Options → 设置时区为 Asia/Shanghai
嗯,这里要注意。串口这块有个坑——树莓派的串口默认被蓝牙占用了。我曾经在这上面折腾了一下午,后来发现得改 /boot/config.txt:
dtoverlay=disable-bt
enable_uart=1
改完重启,串口就归你用了。
4.2 ESP32 基础环境搭建
ESP32这边,我推荐用MicroPython。为什么?开发快,调试方便。你如果非要用C++写Arduino也行,但咱们这个课程追求效率,MicroPython更合适。
刷固件步骤:
- 下载MicroPython固件(.bin文件)
- 用
esptool.py擦除原固件:esptool.py --port /dev/ttyUSB0 erase_flash - 写入新固件:
esptool.py --port /dev/ttyUSB0 write_flash 0x1000 firmware.bin
刷完后,用 screen 或 putty 连上去,波特率115200。看到 >>> 提示符,就成了。
4.3 GPIO 通信实战
GPIO是啥?就是芯片的“手脚”,用来跟外界打交道。树莓派和ESP32都有,但用法略有不同。
树莓派GPIO示例(Python):
import RPi.GPIO as GPIO
import time
# 设置模式
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# 定义引脚
LED_PIN = 18
SENSOR_PIN = 23
# 设置方向
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.setup(SENSOR_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
try:
while True:
if GPIO.input(SENSOR_PIN) == GPIO.LOW:
GPIO.output(LED_PIN, GPIO.HIGH)
print("检测到信号!")
else:
GPIO.output(LED_PIN, GPIO.LOW)
time.sleep(0.1)
except KeyboardInterrupt:
GPIO.cleanup()
这段代码我用了很多次。注意那个 pull_up_down 参数,很多新手会忘。不设的话,引脚悬空时电平会乱跳,你想想看,那数据还能准吗?
ESP32 GPIO示例(MicroPython):
from machine import Pin
import time
led = Pin(2, Pin.OUT)
sensor = Pin(4, Pin.IN, Pin.PULL_UP)
while True:
if sensor.value() == 0:
led.value(1)
print("触发")
else:
led.value(0)
time.sleep_ms(100)
ESP32的GPIO更灵活,但要注意:有些引脚(比如GPIO6-11)是连Flash的,别乱用。我刚开始用ESP32时,就踩过这个坑,焊好板子发现怎么都不工作,查了半天才发现是引脚冲突。
4.4 串口通信实战
串口是工业现场最常用的通信方式。RS232、RS485、TTL串口,本质都一样,只是电平标准不同。
树莓派串口(UART0):
import serial
import time
ser = serial.Serial(
port='/dev/ttyAMA0',
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1,
timeout=1
)
# 发送查询指令
ser.write(b'\x01\x03\x00\x00\x00\x01\x84\x0A')
time.sleep(0.5)
# 读取响应
response = ser.read(7)
print(response.hex())
这段代码是读一个Modbus设备的。那个 \x01\x03... 是Modbus RTU报文。我建议你先把报文用十六进制写死,调试通了再封装成函数。
ESP32串口(UART1):
from machine import UART, Pin
import time
uart = UART(1, baudrate=9600, tx=17, rx=16)
uart.init(bits=8, parity=None, stop=1)
# 发送指令
uart.write(b'\x01\x03\x00\x00\x00\x01\x84\x0A')
time.sleep_ms(500)
# 读取数据
if uart.any():
data = uart.read()
print(data)
ESP32的UART1默认引脚是GPIO9和GPIO10,但这两个引脚通常被占用。所以我习惯手动指定tx和rx引脚,用GPIO17和GPIO16,稳得很。
4.5 数据初步采集脚本
好了,前面都是热身。现在咱们写一个完整的采集脚本,把传感器数据读上来,存成CSV文件。
树莓派版:
#!/usr/bin/env python3
import serial
import time
import csv
from datetime import datetime
# 串口配置
ser = serial.Serial('/dev/ttyAMA0', 9600, timeout=1)
# CSV文件
csv_file = open('sensor_data.csv', 'a', newline='')
writer = csv.writer(csv_file)
# 写表头
writer.writerow(['时间', '温度', '湿度', '状态'])
def read_sensor():
"""读取传感器数据"""
# 发送读取指令(假设是Modbus协议)
cmd = b'\x01\x03\x00\x00\x00\x02\xC4\x0B'
ser.write(cmd)
time.sleep(0.3)
if ser.in_waiting >= 7:
resp = ser.read(7)
# 解析数据(简化处理)
temp = resp[3] * 256 + resp[4]
humi = resp[5] * 256 + resp[6]
return temp / 10.0, humi / 10.0, "正常"
return None, None, "读取失败"
try:
while True:
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
temp, humi, status = read_sensor()
if temp is not None:
print(f"{now} | 温度: {temp}°C | 湿度: {humi}% | {status}")
writer.writerow([now, temp, humi, status])
csv_file.flush() # 立即写入磁盘
else:
print(f"{now} | 读取失败,重试中...")
time.sleep(5) # 5秒采集一次
except KeyboardInterrupt:
print("采集结束")
ser.close()
csv_file.close()
这个脚本我用了很久,核心就三点:
- 定时采集:用
time.sleep()控制间隔,工业现场一般5-30秒一次 - 数据落盘:每采集一次就写CSV,防止断电丢数据
- 异常处理:读取失败不崩溃,继续重试
watchdog 服务,ESP32可以用硬件看门狗。
ESP32版(MicroPython):
from machine import UART, Pin, Timer
import time
import json
uart = UART(1, baudrate=9600, tx=17, rx=16)
led = Pin(2, Pin.OUT)
def read_sensor():
"""读取传感器"""
uart.write(b'\x01\x03\x00\x00\x00\x02\xC4\x0B')
time.sleep_ms(300)
if uart.any() >= 7:
data = uart.read(7)
temp = (data[3] << 8 | data[4]) / 10.0
humi = (data[5] << 8 | data[6]) / 10.0
return {"temp": temp, "humi": humi}
return None
def collect_data(timer):
"""定时采集回调"""
data = read_sensor()
if data:
# 这里可以发到MQTT或存到本地
print(json.dumps(data))
led.value(1)
time.sleep_ms(100)
led.value(0)
# 定时器,每5秒采集一次
tim = Timer(0)
tim.init(period=5000, mode=Timer.PERIODIC, callback=collect_data)
print("采集启动...")
ESP32这边,我用了定时器中断,不阻塞主循环。这样你还可以在 while True 里做别的事,比如处理网络请求。
4.6 本章小结
这一章内容不少,但都是实打实的干货。咱们做了三件事:
- 搭好了树莓派和ESP32的基础环境
- 学会了GPIO和串口通信
- 写了一个能跑的数据采集脚本
你想想看,现在你的边缘网关已经能“听”到传感器的声音了。下一章,咱们就让这些数据“飞”上云端。
嗯,最后提醒一句:调试时别怕出错。我每次搭新环境,至少会碰到三五个问题。慢慢排查,每个坑都是经验。加油!