POST-запрос на Arduino MKR1400 GSM не работает

gsm http mkr1000 strlen

Я пытаюсь отправить 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


1 ответ


Лучший ответ:

0

Скорее всего, проблема связана с заголовком 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);
,

Я использовал strlen(), и это сработало! Класс отладки — отличная идея! Значит, функция F() «захватывает» строки из флэш-памяти, а не из SRAM? Большое спасибо, что так быстро ответили!, @Richard Haes Ellis

F() - это макрос, который заставляет строки оставаться во флэш-памяти, а не копироваться в SRAM (по крайней мере, в системах с гарвардской архитектурой). Затем вызывается другой вариант функции print, который считывает данные непосредственно из флэш-памяти., @Majenko