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
相关产品推荐
相关产品推荐

