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更合适。

刷固件步骤:

  1. 下载MicroPython固件(.bin文件)
  2. esptool.py 擦除原固件:esptool.py --port /dev/ttyUSB0 erase_flash
  3. 写入新固件:esptool.py --port /dev/ttyUSB0 write_flash 0x1000 firmware.bin
注意: 刷固件时,GPIO0要拉低(按住BOOT键),再上电或复位。我第一次刷的时候没注意这个,一直报连接失败,后来才发现是没进下载模式。

刷完后,用 screenputty 连上去,波特率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报文。我建议你先把报文用十六进制写死,调试通了再封装成函数。

避坑指南: 串口通信时,波特率、数据位、校验位必须跟设备一致。我曾经因为校验位设错了,读回来的数据全是乱码,折腾了两天才发现是设备手册上写的是Even校验,我默认设成了None。

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 本章小结

这一章内容不少,但都是实打实的干货。咱们做了三件事:

  1. 搭好了树莓派和ESP32的基础环境
  2. 学会了GPIO和串口通信
  3. 写了一个能跑的数据采集脚本

你想想看,现在你的边缘网关已经能“听”到传感器的声音了。下一章,咱们就让这些数据“飞”上云端。

嗯,最后提醒一句:调试时别怕出错。我每次搭新环境,至少会碰到三五个问题。慢慢排查,每个坑都是经验。加油!