esp32-s3 通过ppp接cat.1模块,当随身路由上网方案

下面是一个较为完整的设计方案文档和参考代码示例,基于 ESP32-S3 使用 ESP-IDF,通过 PPP(Point-to-Point Protocol)与 Cat.1 4G 模块进行数据连接。当设备无 Wi-Fi 可用时,自动切换至通过 4G 模块上网,并可将此网络通过 ESP32-S3 的 Wi-Fi AP 功能对外分享,实现类似“随身路由”的功能。

1. 系统整体架构与设计思路

1.1 硬件组成与连接方式

  • 主控芯片:ESP32-S3
  • 网络模块:Cat.1 4G 模块(通常通过 USB 接口与 ESP32-S3 相连,4G 模块在 USB 上提供 CDC-ACM 通道,ESP32-S3 作为 USB Host)
  • 电源设计:确保 ESP32-S3 和 Cat.1 模块的电源稳定,Cat.1 模块有峰值电流需求(典型值在1~2A范围),请根据模块规格进行电源设计。
  • 其他外设
    • USB Type-C 或 Micro USB 接口,用于连接 Cat.1 模块
    • 指示灯(LED)用于显示当前网络状态(例如 Wi-Fi 已连接/4G 已连接/无网络)
    • 按键用于强制切换网络模式或进行重置

1.2 软件架构与模块划分

系统软件框图可分为以下层次:

  1. ESP-IDF 底层驱动与框架

    • USB Host 驱动(CDC-ACM驱动)
    • PPPoS(PPP-over-Serial)协议栈
    • TCP/IP 栈(LwIP)
    • Wi-Fi STA/AP 驱动
  2. 网络管理模块

    • 网络状态机管理:
      • 首先尝试使用 Wi-Fi STA 连接已知路由器
      • 若 Wi-Fi 不可用(如连接失败或无配置),则启动 4G PPP 链接
    • 当 PPP 建立后,ESP32-S3 获取 IP 并启用 NAT,将 PPP 接口作为 WAN,对外通过 Wi-Fi AP 分享网络
  3. 应用逻辑层

    • 提供状态信息给用户(LED、日志)
    • 配置管理(如 Wi-Fi SSID/密码存储)
    • Web 管理页面(可选,用于配置 Wi-Fi 和查看状态)
      在这里插入图片描述

1.3 工作流程

  1. 系统上电

    • 初始化 NVS、日志和基础硬件
    • 初始化 Wi-Fi 和 PPP,但默认先尝试 Wi-Fi STA 模式连接既有 AP
  2. 尝试 Wi-Fi 连接

    • 若 Wi-Fi STA 成功接入互联网,则通过 Wi-Fi NAT 给 Wi-Fi AP 客户端提供网络。此时不激活 PPP。
    • 若 Wi-Fi STA 超时或失败,则转入 PPP 模式。
  3. PPP 模式建立

    • 启动 USB Host 驱动,识别并挂载4G 模块的 CDC 接口
    • 启动 PPP 协议并拨号(通过AT命令,对应CDC端口发送AT指令建立PPP Session)
    • 获取 IP 后,通过 Wi-Fi AP 对外提供网络
  4. 故障处理与切换

    • 若 PPP 链接断开,尝试重新拨号
    • 若 Wi-Fi STA 配置变更或可用,切换回 Wi-Fi 模式

1.4 相关配置与注意事项

  • NAT功能: ESP-IDF 自带的 LwIP 升级版本支持 SoftAP 下 NAT 功能。需要启用 CONFIG_LWIP_HOOK_IP4_ROUTE_SRCCONFIG_LWIP_L3_IFCONFIG_NAT等相关选项(在 menuconfig 中查看并启用)。
  • USB Host驱动:ESP32-S3 内部有 USB OTG 外设,可在主机模式下使用。需要参考 ESP-IDF 官方 USB Host 示例。
  • PPP配置:ESP-IDF 中提供 PPPoS 示例(即通过串口实现PPP),此处将串口层替换为 USB CDC。相当于在 LwIP 上层实现 PPPoS 但使用 USB CDC 读写函数。

2. 环境准备与配置

2.1 硬件准备

  • ESP32-S3 开发板,确保 USB 引脚(D+、D-)可用作为 USB Host
  • Cat.1 模块(如基于 SIM7600 系列或其它 Cat.1 模块),通过 USB 线连接到 ESP32-S3
  • 外部电源模块,保证4G模块电流供应充足

2.2 软件准备

  • ESP-IDF(建议使用最新稳定版本,如4.4.4或5.x)
  • menuconfig 中进行如下配置:
    1. USB Host:启用 USB Host stack
      • Component config → ESP USB Host Library → USB Host
    2. PPP / LwIP
      • Component config → LWIP → Enable PPP support
      • 启用 NAT相关选项(如需)
    3. Wi-Fi
      • 启用 STA 和 AP 模式
    4. 日志级别:选择适当的日志级别方便调试

2.3 PPP 拨号准备

需要预先了解4G模块的AT命令和拨号指令(一般为:AT+CGDCONT=1,"IP","<APN>"ATD*99# 或根据模块厂商要求)。

3. 核心代码示例说明

下面代码基于 ESP-IDF 示例进行适当改写与整合,仅提供关键片段。实际工程中请分文件结构组织。

3.1 包含头文件与宏定义

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_event.h"
#include "nvs_flash.h"

#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_mac.h"
#include "driver/uart.h"

#include "usb/usb_host.h"
#include "usb/cdc_acm_host.h"
#include "lwip/netif.h"
#include "lwip/pppapi.h"
#include "lwip/pppos.h"
#include "lwip/sio.h"

static const char *TAG = "4G_PPP_ROUTER";

3.2 USB CDC 初始化与 PPP 接口创建

步骤

  1. 初始化 USB Host
  2. 等待 CDC 设备连接
  3. 打开 CDC 设备,获取读写句柄
static cdc_acm_dev_handle_t cdc_dev = NULL;
static int cdc_read_fd = -1;
static int cdc_write_fd = -1;

static void init_usb_cdc(void)
{
    // 初始化 USB 主机库
    esp_err_t ret = usb_host_install(NULL);
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "Waiting for CDC device...");
    // 等待设备连接(可采用事件回调机制,这里简化为轮询)
    while (1) {
        size_t dev_count = 0;
        cdc_acm_host_device_info_t *dev_list = NULL;
        cdc_acm_host_device_list(&dev_list, &dev_count);
        if (dev_count > 0) {
            ESP_LOGI(TAG, "CDC device connected");
            // 打开第一个 CDC 设备
            ret = cdc_acm_host_open(dev_list[0].dev_handle, &cdc_dev);
            if (ret == ESP_OK) {
                free(dev_list);
                break;
            }
        }
        if (dev_list) free(dev_list);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    // 配置CDC波特率、line coding 等,根据模块要求配置
    cdc_acm_line_coding_t line_coding = {
        .dwDTERate = 115200,
        .bCharFormat = CDC_ACM_1_STOP_BITS,
        .bParityType = CDC_ACM_NO_PARITY,
        .bDataBits = 8,
    };
    ret = cdc_acm_host_line_coding_set(cdc_dev, &line_coding);
    ESP_ERROR_CHECK(ret);

    // 开启数据通信
    ret = cdc_acm_host_set_control_line_state(cdc_dev, true, true);
    ESP_ERROR_CHECK(ret);
    ESP_LOGI(TAG, "CDC ACM device is ready");
}

3.3 PPP 层接入

PPP 需要一个 SIO (Serial I/O) 接口函数,用于读写 CDC。我们可以实现 sio_read()sio_write() 来包装 USB CDC 的读写操作。

// 因为PPP需要与底层串口类似的读写接口,这里用CDC的读写函数来模拟
sio_fd_t sio_open(u8_t devnum) {
    // 此处不真正使用devnum,我们只支持一个设备
    return (sio_fd_t) &cdc_dev;
}

u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len) {
    size_t written = 0;
    esp_err_t ret = cdc_acm_host_data_tx(cdc_dev, data, len, &written, 1000);
    return (ret == ESP_OK) ? (u32_t)written : 0;
}

u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len) {
    size_t readed = 0;
    esp_err_t ret = cdc_acm_host_data_rx(cdc_dev, data, len, &readed, 1000);
    return (ret == ESP_OK) ? (u32_t)readed : 0;
}

void sio_read_abort(sio_fd_t fd) {
    // 此处如需要,可调用 cdc_acm_host_data_rx_cancel(cdc_dev);
}

3.4 PPP 会话建立流程

在 PPP 启动前,需要对4G模块发送AT指令设置APN,并发起拨号(有些PPP实现可自动发起拨号,也可以在 PPP层配置chap/pap认证)。这里以手动AT为例。

static ppp_pcb *ppp = NULL;
static esp_netif_t *ppp_netif = NULL;

static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
    struct netif *pppif = ppp_netif(pcb);
    switch (err_code) {
        case PPPERR_NONE: {
            ESP_LOGI(TAG, "PPP connected. IP: %s", ipaddr_ntoa(&pppif->ip_addr));
            break;
        }
        // 其他错误处理逻辑省略...
    }
}

static u32_t ppp_output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) {
    return sio_write((sio_fd_t)ctx, data, len);
}

static void start_ppp_session(void)
{
    sio_fd_t fd = sio_open(0);

    // 对4G模块下发AT指令,设置APN和拨号。例如:
    // AT+CGDCONT=1,"IP","<你的APN>"
    // ATD*99#
    // 这些AT指令发送后将进入PPP协商阶段。
    char *setup_cmds[] = {
        "AT\r\n",
        "AT+CGDCONT=1,\"IP\",\"your_apn\"\r\n",
        "ATD*99#\r\n" // 发起PPP拨号
    };
    for (int i = 0; i < sizeof(setup_cmds)/sizeof(setup_cmds[0]); i++) {
        sio_write(fd, (uint8_t *)setup_cmds[i], strlen(setup_cmds[i]));
        vTaskDelay(pdMS_TO_TICKS(500));
    }

    // 创建PPP
    ppp = pppapi_new();
    ppp_set_default(ppp);
    ppp_set_usepeerdns(ppp, 1);
    ppp_set_notify_phase_callback(ppp, NULL); // 可选
    pppapi_set_status_callback(ppp, ppp_status_cb);

    // 将输出回调设置为ppp_output_cb
    pppapi_over_serial_open(ppp, ppp_output_cb, fd);

    // 启动PPP会话
    pppapi_open(ppp, 0);
}

3.5 Wi-Fi AP 与 NAT 配置

假设您希望设备在无 Wi-Fi STA 成功时自动建立Wi-Fi AP并通过PPP提供网络。这里以简单的 Wi-Fi AP 配置为例。

static esp_netif_t* wifi_ap_netif = NULL;
static esp_netif_t* wifi_sta_netif = NULL;

// 初始化Wi-Fi AP
static void init_wifi_ap(void)
{
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    wifi_ap_netif = esp_netif_create_default_wifi_ap();
    wifi_sta_netif = esp_netif_create_default_wifi_sta();

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = "ESP32_4G_Router",
            .ssid_len = strlen("ESP32_4G_Router"),
            .password = "12345678",
            .max_connection = 5,
            .authmode = WIFI_AUTH_WPA2_PSK
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "Wi-Fi AP started: SSID=ESP32_4G_Router");
}

3.6 NAT 设置

在 PPP 接口获取到 IP 后,需要将该接口作为“外网口”,将 Wi-Fi AP 的网络流量转发到 PPP。ESP-IDF 某些版本支持 esp_netif_transmit() 回调实现NAT,或使用 LwIP NAT 功能。

参考 NAT 示例(ESP-IDF中有 NAT 示例,请根据您的版本查看 examples/protocols/lwip/lwip_nat)。您需要在 menuconfig 中启用 NAT 后,对 netif 进行注册。例如:

// 假设ppp_netif与wifi_ap_netif已经创建
// 使用LWIP自带的NAT功能
#include "lwip/lwip_napt.h"

static void enable_nat(void)
{
    // 启用NAT: 假设PPP接口是ppp0,AP接口是ap0
    struct netif *ap_if = esp_netif_get_netif_impl(wifi_ap_netif);
    struct netif *ppp_if = ppp_netif(ppp);

    // Enable NAT on PPP interface
    ip_napt_enable(ip4_addr_get_u32(&ppp_if->ip_addr), 1);

    // 添加从AP转发到PPP的NAT规则(如果需要具体设置,请参考 NAT 示例)
}

在PPP成功连接回调中调用 enable_nat()

3.7 Wi-Fi STA 回退逻辑

在上电时先尝试 STA 连接,如果STA连接成功则不启动PPP,失败则启动PPP拨号。可参考如下伪代码:

static bool wifi_connected = false;

static void event_handler(void* arg, esp_event_base_t event_base,
                          int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        wifi_connected = false;
        // 尝试重连若超过一定次数则放弃,进入PPP模式
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        wifi_connected = true;
        // 停止PPP,如果有在运行
        if (ppp) {
            pppapi_close(ppp, 0);
        }
    }
}

static void network_manager_task(void *arg)
{
    // 先尝试STA模式
    // 配置STA的SSID与密码(从NVS或编译时写死)
    // 启动STA连接
    // 等待一段时间如10s,如果wifi_connected仍为false,则启动PPP拨号
    vTaskDelay(pdMS_TO_TICKS(10000));
    if (!wifi_connected) {
        ESP_LOGI(TAG, "No Wi-Fi connection, start PPP...");
        init_usb_cdc();
        start_ppp_session();
    }

    vTaskDelete(NULL);
}

3.8 工程目录结构示例

project
├─ main
│  ├─ CMakeLists.txt
│  ├─ main.c
│  ├─ ppp_manager.c
│  ├─ wifi_manager.c
│  └─ usb_cdc.c
└─ CMakeLists.txt

将相关代码分模块存放,main.c 里启动 network_manager_task

4. 测试与调试步骤

  1. 硬件接线完成,上电启动
  2. 串口监视输出日志,查看Wi-Fi STA是否能连接本地路由器
  3. 若Wi-Fi不可用,等待PPP启动日志出现
  4. 查看 PPP 拨号日志,成功后打印分配的IP信息
  5. 使用手机连接到ESP32-S3的Wi-Fi AP,检查是否能上网(PING某个公网地址)

5. 参考资料


以上是一个较为完整和详细的设计与代码参考示例。实际开发中可能需要根据 Cat.1 模块的具体AT指令、实际硬件环境、ESP-IDF版本特性进行相应调整和优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值