You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32-S3基于GPRS搭建WebServer启动崩溃问题求助

问题分析与解决方案

一、基于GPRS搭建WebServer的可行性

理论上可行,但存在明显的局限性,实际场景中很少这么用:

  • 公网访问限制:绝大多数运营商给GPRS分配的是内网NAT IP,外部设备无法直接访问ESP的WebServer,除非运营商提供公网IP并做端口映射,这在个人场景中很难实现;
  • 性能瓶颈:GPRS带宽低、延迟高,WebServer的响应速度和稳定性远不如WiFi/以太网;
  • 更优替代方案:常规远程控制场景中,ESP作为HTTP/MQTT客户端主动向远程服务器请求指令,比作为服务端等待请求更稳定,也无需公网IP。

二、server.begin()崩溃的原因

你使用的AsyncWebServer和AsyncTCP库是为ESP32原生WiFi/以太网设计的,无法适配TinyGSM通过AT指令驱动的GPRS网络栈:

  • 当调用server.begin()时,AsyncWebServer会尝试初始化ESP32原生TCP监听,但此时系统的网络是由GSM模块通过串口AT指令模拟的,两者底层网络实现完全不兼容;
  • 这种不兼容会触发内存溢出或系统底层错误,直接导致ESP32-S3崩溃重启。

三、解决办法

方案1:改用普通WebServer+TinyGSMClient

放弃异步WebServer,使用Arduino自带的WebServer库,配合TinyGSMClient绑定GSM网络,示例代码如下:

#define TINY_GSM_MODEM_M95
#include <TinyGsmClient.h>
#include <WebServer.h>
#include <SoftwareSerial.h>
#include <ArduinoJson.h> // 用于解析JSON请求

const char apn[] = "internet";
const char gprsUser[] = "";
const char gprsPass[] = "";
const int OUTPUT_PIN = 10; // 控制输出的引脚

SoftwareSerial SerialAT(38, 39);
TinyGsm modem(SerialAT);
TinyGsmClient client(modem);
WebServer server(8888);

// 处理根路径请求
void handleRoot() {
  server.send(200, "text/plain", "GPRS WebServer is running");
}

// 处理控制指令的POST请求
void handleControl() {
  if (server.hasArg("plain")) {
    String payload = server.arg("plain");
    StaticJsonDocument<128> doc;
    DeserializationError error = deserializeJson(doc, payload);
    
    if (!error && doc.containsKey("ID") && doc.containsKey("action")) {
      String deviceID = doc["ID"].as<String>();
      String action = doc["action"].as<String>();
      
      // 验证设备ID并执行动作
      if (deviceID == "123456789") {
        if (action == "on") {
          digitalWrite(OUTPUT_PIN, HIGH);
          server.send(200, "text/plain", "Output turned on");
        } else if (action == "off") {
          digitalWrite(OUTPUT_PIN, LOW);
          server.send(200, "text/plain", "Output turned off");
        } else {
          server.send(400, "text/plain", "Invalid action");
        }
      } else {
        server.send(403, "text/plain", "Invalid device ID");
      }
    } else {
      server.send(400, "text/plain", "Invalid JSON payload");
    }
  } else {
    server.send(400, "text/plain", "No payload received");
  }
}

void setup() {
  pinMode(OUTPUT_PIN, OUTPUT);
  delay(15000);
  Serial.begin(115200);
  SerialAT.begin(9600);

  Serial.println("Initializing GSM module...");
  modem.init();
  String modemInfo = modem.getModemInfo();
  Serial.print("Modem: ");
  Serial.println(modemInfo);

  Serial.println("Connecting to GPRS...");
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    Serial.println("Failed to connect to GPRS");
    while (true);
  }

  Serial.println("Connected to GPRS");
  Serial.println(modem.getLocalIP());

  // 注册路由
  server.on("/", handleRoot);
  server.on("/control", HTTP_POST, handleControl);

  // 绑定TinyGSMClient到WebServer
  server.begin(&client);
  Serial.println("WebServer started");
}

void loop() {
  server.handleClient();
}

方案2:改用ESP作为HTTP客户端(更推荐)

放弃让ESP做服务端,改为让ESP定期向你的远程服务器轮询控制指令,或者用MQTT订阅主题,示例代码(HTTP轮询):

#define TINY_GSM_MODEM_M95
#include <TinyGsmClient.h>
#include <SoftwareSerial.h>
#include <ArduinoJson.h>

const char apn[] = "internet";
const char gprsUser[] = "";
const char gprsPass[] = "";
const int OUTPUT_PIN = 10;
const char* SERVER_HOST = "your-server.com"; // 你的远程服务器地址
const int SERVER_PORT = 80;

SoftwareSerial SerialAT(38, 39);
TinyGsm modem(SerialAT);

void setup() {
  pinMode(OUTPUT_PIN, OUTPUT);
  delay(15000);
  Serial.begin(115200);
  SerialAT.begin(9600);

  Serial.println("Initializing GSM module...");
  modem.init();
  String modemInfo = modem.getModemInfo();
  Serial.print("Modem: ");
  Serial.println(modemInfo);

  Serial.println("Connecting to GPRS...");
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    Serial.println("Failed to connect to GPRS");
    while (true);
  }
  Serial.println("Connected to GPRS");
}

void loop() {
  if (modem.isGprsConnected()) {
    TinyGsmClient client(modem);
    if (client.connect(SERVER_HOST, SERVER_PORT)) {
      // 发送查询请求
      client.print("GET /get-command?deviceID=123456789 HTTP/1.1\r\n");
      client.print("Host: ");
      client.print(SERVER_HOST);
      client.print("\r\n");
      client.print("Connection: close\r\n\r\n");

      // 读取响应头,跳过直到空行
      while (client.connected()) {
        String line = client.readStringUntil('\n');
        if (line == "\r") break;
      }

      // 读取响应内容
      String response = client.readStringUntil('\n');
      StaticJsonDocument<64> doc;
      deserializeJson(doc, response);
      
      if (doc.containsKey("action")) {
        String action = doc["action"].as<String>();
        if (action == "on") {
          digitalWrite(OUTPUT_PIN, HIGH);
          Serial.println("Output turned on");
        } else if (action == "off") {
          digitalWrite(OUTPUT_PIN, LOW);
          Serial.println("Output turned off");
        }
      }
      client.stop();
    } else {
      Serial.println("Failed to connect to server");
    }
  } else {
    Serial.println("Reconnecting to GPRS...");
    modem.gprsConnect(apn, gprsUser, gprsPass);
  }
  delay(5000); // 每5秒查询一次
}

内容的提问来源于stack exchange,提问作者BlacAndWite

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 00:20:40