ESP32通过Google App Script向Google Sheet传数据多次后连接失败如何解决
故障成因
- 核心原因:全局定义的
WiFiClientSecure对象未主动释放连接资源。ESP32的可用TCP socket数量上限通常为4~5个,你每次调用client.connect()后没有执行client.stop()主动销毁连接,即使HTTP头指定了Connection: close,底层socket资源仍会被占用,3~4次请求后socket被占满,后续连接自然失败。 - 次要问题1:响应判断逻辑不匹配。GAS端返回的是纯文本
success,但ESP32端判断逻辑为line.startsWith("{\"state\":\"success\""),永远会判定为请求失败,且你仅读取了第一行响应内容,剩余数据残留在client缓冲区,会干扰下一次请求的解析。 - 次要问题2:HTTP方法使用不规范。用GET请求执行数据写入操作,部分场景下可能触发Google的缓存策略,导致请求被拦截。
可行解决方案
必改修复项
- 每次请求结束后强制释放连接资源
在sendData函数的所有返回路径末尾添加client.stop(),确保不管请求成功还是失败,都释放socket资源。修改后sendData的示例代码:
void sendData(float SValue1, float SValue2) { Serial.println("=========="); Serial.print("connecting to "); Serial.println(host); // 连接前先关闭旧连接,避免残留 if(client.connected()){ client.stop(); } if (!client.connect(host, httpsPort)) { Serial.println("connection failed"); client.stop(); // 连接失败也要释放资源 return; } //---- Post Data 原有逻辑不变 String url; url += "/macros/s/" + GAS_ID + "/exec?"; url += "id=" + String(GAS_Sheet); url += "&temperature=" + String(SValue1, 2); url += "&humidity=" + String(SValue2, 2); Serial.print("requesting URL: "); Serial.println(url); client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: BuildFailureDetectorESP8266\r\n" + "Connection: close\r\n\r\n"); Serial.println("request sent"); //---- Wait Echo 逻辑优化:读完所有响应头 while (client.connected() || client.available()) { String line = client.readStringUntil('\n'); if (line == "\r") { Serial.println("headers received"); break; } } String line = client.readStringUntil('\n'); // 修改判断逻辑和GAS返回对齐 if (line == "success") { Serial.println("ESP-32 data upload successfull!"); } else { Serial.println("ESP-32 data upload failed"); } Serial.print("reply was : "); Serial.println(line); // 强制释放连接 client.stop(); Serial.println("closing connection"); Serial.println("=========="); Serial.println(); }
- 可选对齐GAS返回格式
如果你不想修改ESP32的判断逻辑,可以修改GAS端返回JSON格式和原有代码对齐:
function doGet(e){ // 原有逻辑完全不变 var ss = SpreadsheetApp.getActive(); var sheet = ss.getSheetByName(e.parameter["id"]); var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; var lastRow = sheet.getLastRow(); var cell = sheet.getRange('a1'); var col = 0; var d = new Date(); for (i in headers){ if (headers[i] == "Timestamp") { val = d.toDateString() + ", " + d.toLocaleTimeString(); } else { val = e.parameter[headers[i]]; } cell.offset(lastRow, col).setValue(val); col++; } // 仅修改返回内容为JSON格式 return ContentService.createTextOutput(JSON.stringify({"state":"success"})).setMimeType(ContentService.MimeType.JSON); }
可选优化项
- 将
WiFiClientSecure改为sendData函数内的局部变量,每次请求新建对象,用完自动销毁,从根源避免资源残留问题。 - 将GET请求改为POST请求传输数据,符合HTTP语义,同时避免Google缓存拦截。
- 增加请求超时判断,避免长时间阻塞在响应读取步骤。
内容的提问来源于stack exchange,提问作者Jatu Tung
相关产品推荐
相关产品推荐

