使用Arduino IDE与NodeMCU向Autodesk Tandem发送HTTP数据失败的排查与优化
如何修改代码以通过Arduino IDE向Autodesk Tandem数字孪生流发送HTTP数据?
原代码失败原因分析
- 认证方式错误:Autodesk Tandem API要求使用OAuth 2.0的Bearer令牌,原代码将Basic认证字符串作为
access_token参数拼接在URL中,不符合接口认证规范。 - 缺少令牌自动获取逻辑:硬编码的Basic令牌无法用于时序数据推送,需要通过Client ID和Client Secret向Autodesk认证服务器请求有效Bearer令牌。
- API端点错误:原代码使用的
webhooks/generic端点并非时序数据推送的正确路径,正确端点应为/api/v1/timeseries/models/{modelId}/streams/{streamId}/data。 - Payload格式不符合要求:Tandem时序数据要求包含
timestamp(时间戳)和value字段,原payload的键名和结构不匹配接口定义。 - HTTPS连接问题:ESP8266的HTTPClient默认可能需要验证服务器证书,未处理的话会导致SSL连接失败。
修正后的完整代码
#include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <ArduinoJson.h> const char* ssid = "你的WiFi名称"; const char* password = "你的WiFi密码"; const char* clientID = "你的Autodesk Client ID"; const char* clientSecret = "你的Autodesk Client Secret"; const char* modelID = "urn:adsk.dtt:你的模型ID"; const char* streamID = "你的孪生流ID"; const char* authServer = "developer.api.autodesk.com"; const char* tandemServer = "tandem.autodesk.com"; String bearerToken = ""; unsigned long tokenExpiryTime = 0; void setup() { Serial.begin(115200); delay(10); // 连接WiFi Serial.println(); Serial.print("连接到 "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } Serial.println(""); Serial.println("WiFi连接成功"); } bool getAuthToken() { // 如果令牌未过期,直接返回 if (millis() < tokenExpiryTime) { return true; } HTTPClient http; String authPath = "/authentication/v1/authenticate"; String postData = "client_id=" + String(clientID) + "&client_secret=" + String(clientSecret) + "&grant_type=client_credentials&scope=data:write"; if (!http.begin("https://" + String(authServer) + authPath)) { Serial.println("初始化HTTP连接失败"); http.end(); return false; } http.addHeader("Content-Type", "application/x-www-form-urlencoded"); int httpCode = http.POST(postData); if (httpCode != HTTP_CODE_OK) { Serial.print("获取令牌失败,HTTP代码: "); Serial.println(httpCode); http.end(); return false; } // 解析响应JSON DynamicJsonDocument doc(1024); deserializeJson(doc, http.getStream()); bearerToken = doc["access_token"].as<String>(); int expiresIn = doc["expires_in"].as<int>(); tokenExpiryTime = millis() + (expiresIn - 600) * 1000; // 提前10分钟刷新 Serial.println("令牌获取成功"); http.end(); return true; } void postData() { // 先确保令牌有效 if (!getAuthToken()) { Serial.println("无法获取有效令牌,终止数据推送"); return; } // 构造符合要求的Payload long timestamp = millis() / 1000; // Unix时间戳(秒) float temperature = 25.0 + random(-5, 5) * 0.1; // 模拟温度数据 String payload; DynamicJsonDocument doc(256); doc["timestamp"] = timestamp; doc["value"] = temperature; serializeJson(doc, payload); // 构建正确的API路径 String apiPath = "/api/v1/timeseries/models/" + String(modelID) + "/streams/" + String(streamID) + "/data"; HTTPClient http; if (!http.begin("https://" + String(tandemServer) + apiPath)) { Serial.println("初始化Tandem HTTP连接失败"); http.end(); return; } // 设置认证头和内容类型 http.addHeader("Authorization", "Bearer " + bearerToken); http.addHeader("Content-Type", "application/json"); // 测试阶段禁用证书验证(生产环境建议添加证书) http.setInsecure(); int httpCode = http.POST(payload); if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_NO_CONTENT) { Serial.println("数据发送成功"); } else { Serial.println("数据发送失败"); } // 打印响应信息 Serial.print("HTTP代码: "); Serial.println(httpCode); Serial.print("响应内容: "); Serial.println(http.getString()); http.end(); } void loop() { postData(); delay(60000); // 每分钟推送一次数据 }
关键修改点说明
- 新增OAuth 2.0令牌获取逻辑:
getAuthToken()函数通过Client ID和Secret向Autodesk认证服务器请求Bearer令牌,自动处理令牌过期刷新(提前10分钟刷新避免失效)。 - 修正API端点:使用时序数据推送的官方端点
/api/v1/timeseries/models/{modelId}/streams/{streamId}/data,需替换streamID为你的孪生流ID。 - 调整Payload格式:采用Tandem要求的结构,包含
timestamp(Unix时间戳)和value字段,确保数据能被正确解析。 - 修复认证方式:将Bearer令牌放在
Authorization请求头中,符合OAuth 2.0规范,不再拼接在URL中。 - 处理HTTPS连接:测试阶段用
http.setInsecure()禁用证书验证,生产环境建议添加Tandem服务器的根证书以保证安全性。
内容的提问来源于stack exchange,提问作者rahma mo
相关产品推荐
相关产品推荐

