1. 工业IoT边缘网关最小可运行框架搭建
任务定义
本案例面向工业IoT边缘网关开发的第一阶段:硬件平台选型与接口识别。目标是为后续固件开发选定一款具体、可获取的硬件平台,并完整识别其核心接口资源,形成一份可直接指导驱动开发与外围电路设计的接口清单。任务边界限定为:选定平台、识别接口、输出接口映射关系,不涉及具体业务代码编写。
平台选型结论
选用ESP32-WROOM-32E模组作为边缘网关核心处理器。选型理由:双核240MHz主频满足边缘协议转换算力需求;集成Wi-Fi 802.11 b/g/n与蓝牙4.2,可同时承担上行云连接与下行近场调试;34个可编程GPIO、3路UART、2路I2C、1路SPI、2路12-bit SAR ADC、1路DAC,接口种类覆盖工业传感器采集、继电器控制、RS485总线扩展等典型网关外设需求;模组内置4MB Flash,支持OTA固件升级。开发板选用ESP32-DevKitC V4作为调试载体,其板载USB-UART桥接芯片CP2102提供串口烧录与日志通道。
接口资源识别
下表列出本网关设计中将实际使用的接口及其功能分配。该映射关系是后续各案例(RS485采集、Modbus协议栈移植、MQTT上云等)的硬件基础。
| 接口类型 | 物理引脚 | 功能分配 | 电气特性 |
|---|---|---|---|
| UART0 | GPIO1(TX0), GPIO3(RX0) | 日志输出/固件烧录 | 3.3V TTL,默认115200-8-N-1 |
| UART2 | GPIO17(TX2), GPIO16(RX2) | RS485总线(外接MAX3485) | 3.3V TTL,经收发器转差分 |
| I2C0 | GPIO21(SDA), GPIO22(SCL) | 温湿度传感器SHT30 | 3.3V,上拉4.7kΩ,速率400kHz |
| SPI2 | GPIO18(SCK), GPIO19(MISO), GPIO23(MOSI), GPIO5(CS) | SD卡存储(数据记录) | 3.3V,模式0,速率10MHz |
| ADC1_CH0 | GPIO36 | 4-20mA电流环采样(外接采样电阻) | 0-3.3V模拟输入,12-bit |
| GPIO输出 | GPIO25, GPIO26 | 继电器控制(外接ULN2003驱动) | 3.3V推挽输出,最大40mA |
| GPIO输入 | GPIO27 | 干接点状态检测(外接光耦隔离) | 3.3V输入,内部上拉 |
| 电源 | 5V/VIN, 3V3, GND | 系统供电与对外供电 | USB 5V输入,板载LDO输出3.3V/500mA |
接口识别验证代码
以下代码用于验证上述接口映射是否与硬件实际连接一致。程序依次初始化各接口并执行回环或状态读取测试,结果通过UART0输出。该代码仅做接口可用性确认,不包含业务逻辑。
// file: hw_verify.c
// 依赖:ESP-IDF v4.4 及以上版本
// 构建:idf.py build flash monitor
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/i2c.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#define UART2_TX_PIN 17
#define UART2_RX_PIN 16
#define I2C_SDA_PIN 21
#define I2C_SCL_PIN 22
#define SPI_SCK_PIN 18
#define SPI_MISO_PIN 19
#define SPI_MOSI_PIN 23
#define SPI_CS_PIN 5
#define RELAY1_PIN 25
#define RELAY2_PIN 26
#define DI_PIN 27
#define ADC_PIN ADC1_CHANNEL_0 // GPIO36
// UART2 回环测试:将TX2与RX2短接后,发送0xA5应收到0xA5
static void uart2_loopback_test(void) {
uart_config_t uart_cfg = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(UART_NUM_2, &uart_cfg);
uart_set_pin(UART_NUM_2, UART2_TX_PIN, UART2_RX_PIN, -1, -1);
uart_driver_install(UART_NUM_2, 256, 256, 0, NULL, 0);
uint8_t tx_data = 0xA5, rx_data = 0;
uart_write_bytes(UART_NUM_2, &tx_data, 1);
int len = uart_read_bytes(UART_NUM_2, &rx_data, 1, pdMS_TO_TICKS(100));
printf("[UART2] %s (发送0x%02X, 接收0x%02X)\n",
(len == 1 && rx_data == tx_data) ? "PASS" : "FAIL", tx_data, rx_data);
uart_driver_delete(UART_NUM_2);
}
// I2C0 扫描:SHT30地址为0x44,检测到则通信正常
static void i2c_scan_test(void) {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_SDA_PIN,
.scl_io_num = I2C_SCL_PIN,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000
};
i2c_param_config(I2C_NUM_0, &conf);
i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (0x44 << 1) | I2C_MASTER_WRITE, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(100));
i2c_cmd_link_delete(cmd);
printf("[I2C0] %s (SHT30地址0x44)\n", (ret == ESP_OK) ? "PASS" : "FAIL");
i2c_driver_delete(I2C_NUM_0);
}
// SPI2 写读测试:CS拉低后向MOSI发0x55,MISO应收到0x55(短接MOSI与MISO)
static void spi_loopback_test(void) {
spi_bus_config_t bus_cfg = {
.mosi_io_num = SPI_MOSI_PIN,
.miso_io_num = SPI_MISO_PIN,
.sclk_io_num = SPI_SCK_PIN,
.quadwp_io_num = -1,
.quadhd_io_num = -1
};
spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
spi_device_interface_config_t dev_cfg = {
.clock_speed_hz = 10 * 1000 * 1000,
.mode = 0,
.spics_io_num = SPI_CS_PIN,
.queue_size = 1
};
spi_device_handle_t spi_dev;
spi_bus_add_device(SPI2_HOST, &dev_cfg, &spi_dev);
uint8_t tx_data = 0x55, rx_data = 0;
spi_transaction_t trans = {
.length = 8,
.tx_buffer = &tx_data,
.rx_buffer = &rx_data
};
esp_err_t ret = spi_device_transmit(spi_dev, &trans);
printf("[SPI2] %s (发送0x%02X, 接收0x%02X)\n",
(ret == ESP_OK && rx_data == tx_data) ? "PASS" : "FAIL", tx_data, rx_data);
spi_bus_remove_device(spi_dev);
spi_bus_free(SPI2_HOST);
}
// GPIO输出与输入测试:将GPIO25与GPIO27短接,输出高电平后读取DI引脚
static void gpio_di_test(void) {
gpio_config_t out_cfg = {
.pin_bit_mask = (1ULL << RELAY1_PIN) | (1ULL << RELAY2_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&out_cfg);
gpio_config_t in_cfg = {
.pin_bit_mask = (1ULL << DI_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&in_cfg);
gpio_set_level(RELAY1_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(10));
int level = gpio_get_level(DI_PIN);
printf("[GPIO] %s (GPIO25输出高, GPIO27读到%d)\n", (level == 1) ? "PASS" : "FAIL", level);
gpio_set_level(RELAY1_PIN, 0);
}
// ADC 电压读取:GPIO36悬空或接已知电压,打印原始值与换算电压
static void adc_read_test(void) {
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC_PIN, ADC_ATTEN_DB_11);
esp_adc_cal_characteristics_t cal;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &cal);
int raw = adc1_get_raw(ADC_PIN);
uint32_t voltage = esp_adc_cal_raw_to_voltage(raw, &cal);
printf("[ADC1] raw=%d, voltage=%dmV (GPIO36)\n", raw, voltage);
}
void app_main(void) {
printf("=== 硬件接口识别验证开始 ===\n");
uart2_loopback_test();
i2c_scan_test();
spi_loopback_test();
gpio_di_test();
adc_read_test();
printf("=== 验证结束 ===\n");
}
接口连接拓扑
验证方法与判定标准
硬件准备:将GPIO17与GPIO16短接(UART2回环);GPIO23与GPIO19短接(SPI回环);GPIO25与GPIO27短接(GPIO输出→输入测试);GPIO36悬空或接已知直流电压(如1.5V电池正极,负极接GND)。
执行步骤:
- 使用ESP-IDF环境执行
idf.py set-target esp32后编译烧录:idf.py build flash monitor; - 观察串口终端输出,逐项核对测试结果。
判定标准:终端依次打印以下内容即判定接口识别与映射验证通过:
[UART2] PASS (发送0xA5, 接收0xA5)
[I2C0] PASS (SHT30地址0x44)
[SPI2] PASS (发送0x55, 接收0x55)
[GPIO] PASS (GPIO25输出高, GPIO27读到1)
[ADC1] raw=xxx, voltage=yyymV (GPIO36)
其中ADC的raw与voltage数值取决于GPIO36所接电压,若悬空则读数不定属正常现象。若某项打印FAIL,需检查对应物理连接或引脚映射配置。