POST-запрос на Arduino MKR1400 GSM не работает
Я пытаюсь отправить POST-запрос с GSM-платы Arduino MKR1400 на сервер с базой данных.
Я протестировал сервер с помощью Postman (https://www.getpostman.com/), и он обрабатывает запрос POST. правильно, данные json загружаются в базу данных нормально.
Проблема в том, что тот же POST-запрос от Arduino MKR GSM 1400 ничего не делает. Я пробовал использовать client.print
и получил тот же результат.
Я не уверен, что делаю неправильно или как решить эту проблему, есть идеи?
Код Arduino:
#include <MKRGSM.h>
#include "Credentials.h"
// Определения для GPRS
const char PINNUMBER[] = SECRET_PINNUMBER;
const char GPRS_APN[] = SECRET_GPRS_APN;
const char GPRS_LOGIN[] = SECRET_GPRS_LOGIN;
const char GPRS_PASSWORD[] = SECRET_GPRS_PASSWORD;
GSMClient client;
GPRS gprs;
GSM gsmAccess;
GSMScanner scanner;
char ip[4] = {xxx,xxx,xxx,xxx};
char ipstring[14];
char path[] = "/atcapp.php";
int port = 80; // порт 80 используется по умолчанию для HTTP
char respName[30] = "Arduino MKR GSM 1400";
char toolName[30] = "Herramienta martillo";
char location[30] = "Mave Aeronautica S.L.";
char tlState[4] = "OUT";
unsigned long int user_id = 4294967295;
unsigned long int tool_id = 1234567899;
unsigned long int tmeStmp = 1532684104;
char notes[100] = "";
char postData[200];
char packetData[500];
void setup() {
Serial.begin(9600);
while(!Serial);
sprintf(ipstring,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
post(respName,user_id,location,toolName,tool_id,tlState,tmeStmp,notes);
}
char post(char* resp,unsigned long int userid,char* location, char* toolName,unsigned long int toolid,char* toolState,unsigned long int timeStamp,char* notes)
{
Serial.println("Connecting to GPRS Network...");
boolean connected = false;
while (!connected) {
if ((gsmAccess.begin(PINNUMBER) == GSM_READY) &&
(gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY)) {
connected = true;
} else {
Serial.println("GPRS connection failed");
delay(1000);
return -1;
}
}
if (client.connect(ip, port)) {
sprintf(packetData,"POST %s HTTP/1.1\nHost: %s\nUser-Agent: Arduino/1.0\nConnection: close\nContent-Length: %d\nContent-Type: application/json\nCache-Control: no-cache\n\n{\n\"responsable\":\"%s\",\n\"user_id\":%lu,\n\"loc\":\"%s\",\n\"tool\":\"%s\",\n\"tool_id\":%lu,\n\"tool_state\":\"%s\",\n\"timestamp\":%lu,\n\"note\":\"%s\"\n}\n",path,ipstring,sizeof(postData),resp,userid,location,toolName,toolid,toolState,timeStamp,notes);
client.beginWrite();
for(int i=0;i<sizeof(packetData);i++){
Serial.write(packetData[i]);
client.write(packetData[i]);
}
client.endWrite();
while(client.connected()){
Serial.println("Client still connected.");
while(client.available()){
Serial.println("Client still available.");
char c = client.read();
Serial.print(c);
}
}
Serial.println("disconnecting.");
client.stop();
Serial.println();
Serial.println("Done.");
return 1;
}else{
Serial.println("Cannot connect to server.");
return 0;
}
}
void loop() {}
Выход последовательного порта:
Я впервые задаю здесь вопрос, любая конструктивная критика для меня более чем ценна. Заранее спасибо,
1 ответ
Лучший ответ:
Скорее всего, проблема связана с заголовком Content-Length
. Вы используете sizeof(postData)
в качестве длины вашего контента, однако postData
представляет собой 200-байтовый массив, который ни для чего не используется. sizeof()
всегда будет возвращать 200
для этого независимо от того, что делает остальная часть вашей программы.
Content-Length
должен быть равен количеству байтов после второго из двух \n
, отделяющих заголовок от тела. Лучший способ вычислить это — сложить длину всех строк, которые вставляются в форматирование тела (используя strlen()
не sizeof()
), а затем добавить к этому постоянное значение, равное количеству статических символов (включая \n
) в теле (возможно, 108?).
int dataLen = 108;
dataLen += strlen(resp);
dataLen += strlen(userid);
dataLen += strlen(location);
dataLen += strlen(toolName);
dataLen += strlen(toolid);
dataLen += strlen(toolState);
dataLen += strlen(timeStamp);
dataLen += strlen(notes);
В качестве альтернативы вы предварительно форматируете тело в отдельную строку и используете для нее strlen()
, чтобы получить длину до начала отправки данных. Конечно, вам не нужно хранить весь заголовок в строке, так как вы можете просто отправить его построчно...
sprintf(packetData,"{\n\"responsable\":\"%s\",\n\"user_id\":%lu,\n\"loc\":\"%s\",\n\"tool\":\"%s\",\n\"tool_id\":%lu,\n\"tool_state\":\"%s\",\n\"timestamp\":%lu,\n\"note\":\"%s\"\n}\n",resp,userid,location,toolName,toolid,toolState,timeStamp,notes);
client.beginWrite();
client.print(F("POST "));
client.print(path);
client.println(F(" HTTP/1.1"));
client.print(F("Host: "));
client.println(ipstring);
client.println(F("User-Agent: Arduino/1.0"));
client.println(F("Connection: close"));
client.print(F("Content-Length: "));
client.println(strlen(packetData));
client.println(F("Content-Type: application/json"));
client.println(F("Cache-Control: no-cache"));
client.println();
client.print(packetData);
client.endWrite();
Обратите внимание на использование F()
вокруг вашего статического текста, чтобы сэкономить большое количество оперативной памяти на платах на базе AVR. На плате на базе ARM это не обязательно, но полезно выработать привычку делать вещи портативными.
Если вы хотите отладить то, что отправляется, я бы предложил создать класс-оболочку "отладки", который включает в себя как client
, так и Serial
:
class TwinPrint : public Print {
private:
Print *_ch1;
Print *_ch2;
public:
TwinPrint(Print &ch1, Print &ch2) : _ch1(&ch1), _ch2(&ch2) {}
size_t write(uint8_t c) {
_ch1->write(c);
_ch2->write(c);
return 1;
}
};
Затем вы можете создать новый объект:
TwinPrint debug(client, Serial);
и напишите об этом, и оно появится одновременно и на client
, и на Serial
:
debug.print(F("POST "));
debug.print(path);
debug.println(F(" HTTP/1.1"));
debug.print(F("Host: "));
debug.println(ipstring);
debug.println(F("User-Agent: Arduino/1.0"));
debug.println(F("Connection: close"));
debug.print(F("Content-Length: "));
debug.println(strlen(packetData));
debug.println(F("Content-Type: application/json"));
debug.println(F("Cache-Control: no-cache"));
debug.println();
debug.print(packetData);
- GSM-модуль Sim800l не может подключить данные gprs
- SIM A9G: +CME ERROR: 53 failure
- Отправить HTTP get запрос от GSM SIM800c и Arduino
- Как настроить модуль TinySine 3G (SIM5320E) для запроса HTTP GET
- HTTP-запрос SIM800L ненадежен
- создание постоянного HTTP-соединения для создания «гладких» данных через GSM-отправку
- ArduinoHTTPClient POST multipart/form-data с SD-карты, возвращающий 400 неверных запросов
- Как управлять MKR1000 из-за пределов локальной сети
Я использовал strlen(), и это сработало! Класс отладки — отличная идея! Значит, функция F() «захватывает» строки из флэш-памяти, а не из SRAM? Большое спасибо, что так быстро ответили!, @Richard Haes Ellis
F()
- это макрос, который заставляет строки оставаться во флэш-памяти, а не копироваться в SRAM (по крайней мере, в системах с гарвардской архитектурой). Затем вызывается другой вариант функцииprint
, который считывает данные непосредственно из флэш-памяти., @Majenko