ESP32向Django服务器发送POST请求失败及视图实现咨询
ESP32 POST请求到ngrok托管的Django服务器返回错误码-1的排查与解决方案
一、错误码-1的核心原因与排查步骤
ESP32 HttpClient库返回错误码-1,本质是TCP连接建立失败,结合ngrok+Django的场景,按以下顺序排查:
- 先确认ESP32 WiFi连接状态:必须在
WiFi.status() == WL_CONNECTED的前提下再发起请求,别上来就发 - 验证ngrok URL有效性:用浏览器直接访问生成的ngrok地址,能打开Django页面才算正常;如果打不开,检查ngrok是否在运行,本地Django服务是否启动
- 不要给ngrok URL加端口:ngrok已经把本地端口(比如8000)映射到它的公网域名上,直接用
http://xxxx.ngrok.io就行,加:8000会直接报错 - 关闭ESP32的代理配置:如果之前设过
client.setProxy(),直接删掉,代理会阻断公网连接 - 检查Django的跨域与主机限制:
- 测试阶段把
ALLOWED_HOSTS = ['*']加到settings.py,避免ngrok请求被拦截 - 装
django-cors-headers并配置CORS_ALLOW_ALL_ORIGINS = True,防止跨域拦截
- 测试阶段把
二、ngrok端口的正确用法
ngrok的核心是端口映射,举个例子:
- 本地Django跑在
127.0.0.1:8000,启动ngrok的命令是ngrok http 8000 - 生成的公网地址类似
http://abc123-45-67-89-01.ngrok.io,直接用这个地址访问,不需要加任何端口 - ngrok会自动把这个域名的HTTP请求(默认80端口)转发到你本地的8000端口
三、处理ESP32 POST请求的Django视图示例
假设ESP32发送JSON格式的传感器数据,视图这么写:
# views.py from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt import json # 测试阶段关闭CSRF验证,上线后换CSRF白名单或Token验证 @csrf_exempt def receive_sensor_data(request): if request.method != 'POST': return JsonResponse({'status': 'error', 'msg': '只接受POST请求'}, status=405) try: # 解析ESP32发送的JSON数据 sensor_data = json.loads(request.body) # 这里写数据入库逻辑,比如存到Model里 # from .models import SensorRecord # SensorRecord.objects.create(temp=sensor_data['temp'], hum=sensor_data['hum']) return JsonResponse({'status': 'success', 'msg': '数据已接收'}, status=200) except json.JSONDecodeError: return JsonResponse({'status': 'error', 'msg': '数据格式错误,需JSON'}, status=400)
然后在urls.py配置路由:
# urls.py from django.urls import path from . import views urlpatterns = [ path('api/sensor-data/', views.receive_sensor_data, name='sensor_data'), ]
四、ESP32代码修正要点
原代码大概率是URL或请求头的问题,修正后的示例:
#include <WiFi.h> #include <HttpClient.h> const char* WIFI_SSID = "你的WiFi名称"; const char* WIFI_PWD = "你的WiFi密码"; // 替换成你的ngrok地址+路由 const char* SERVER_URL = "http://abc123.ngrok.io/api/sensor-data/"; WiFiClient wifiClient; HttpClient client(wifiClient); void setup() { Serial.begin(9600); // 确保WiFi连接成功 WiFi.begin(WIFI_SSID, WIFI_PWD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi已连接"); } void loop() { // 读取Arduino Uno传来的串口数据 if (Serial.available() > 0) { String rawData = Serial.readStringUntil('\n'); Serial.print("收到Uno数据:"); Serial.println(rawData); // 构造JSON格式数据(根据你的rawData格式解析,这里是示例) String jsonData = "{\"temp\":26.5,\"hum\":62}"; // 发送POST请求 client.beginRequest(); client.post(SERVER_URL); // 必须设置Content-Type,否则Django解析不了 client.sendHeader("Content-Type", "application/json"); client.sendHeader("Content-Length", String(jsonData.length())); client.beginBody(); client.print(jsonData); client.endRequest(); // 打印响应信息排查问题 int statusCode = client.responseStatusCode(); String response = client.responseBody(); Serial.print("响应状态码:"); Serial.println(statusCode); Serial.print("响应内容:"); Serial.println(response); if (statusCode == -1) { Serial.println("连接失败!检查WiFi、SERVER_URL是否正确,ngrok是否在运行"); } } delay(3000); }
关键修正:
- 先等WiFi连接成功再发请求
- 正确设置
Content-Type: application/json请求头 - 直接用不带端口的ngrok URL
- 增加响应打印,方便定位问题
五、Ethernet Shield替代方案的注意事项
如果换用Ethernet Shield,逻辑和ESP32一致,注意两点:
- 确保Shield获取到有效IP(用
Ethernet.localIP()打印确认) - 同样直接使用ngrok的公网URL,不要加本地端口
内容的提问来源于stack exchange,提问作者Nolulamo Tshuma
相关产品推荐
相关产品推荐

