1

Im trying to send a POST request from the Arduino MKR1400 GSM board to a server with a database.

I've tested the server using Postman (https://www.getpostman.com/) and it handles the POST request correctly, the json data gets uploaded to database fine.

The problem is that the same POST request from the Arduino MKR GSM 1400 does nothing. I've tried using client.print and I get the same result.

I'm not sure what I'm doing wrong or how to debug this issue, any ideas?

Arduino code:

#include <MKRGSM.h>
#include "Credentials.h"

// Definiciones para el 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; // port 80 is the default for 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() {}

Serial port output:

enter image description here

It's my first time ever asking a question here, any contructive criticism is more than valuable to me. Thank you in advance,

1 Answer 1

0

The problem most likely stems from your Content-Length header. You are using sizeof(postData) as the length of your content - however postData is a 200 byte array which is not used for anything. sizeof() will always return 200 for that regardless of what the rest of your program is doing.

Content-Length must be equal to the number of bytes after the second of the two \n separating the header from the body. The best way to calculate that is up add up the length of all the strings that get inserted into the body formatting (using strlen() not sizeof()), and then add to that a constant value which is equal to the number of static characters (including \n) in the body (I make that 108 possibly?).

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);

Alternatively you pre-format the body into a separate string and use strlen() on that to get the length before starting to send data. Of course, you don't need to store all the header in the string, since you can just send that line-by-line...

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();

Note the use of F() around your static text to save copious amounts of RAM on AVR-based boards. Not really necessary on an ARM-based board, but a good thing to get into the habit of to keep things portable.

If you want to debug what is being sent I would suggest making a "debug" wrapper class that comprises both client and 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;
        }
};

You can then construct a new object:

TwinPrint debug(client, Serial);

and do your writing to that and it will come out on both client and Serial at once:

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);
2
  • I used strlen() and it worked! The debug class is a great idea! So the F() function "grabs" the strings from flash instead of the SRAM? Thank you soo much for answering so soon! Aug 7, 2018 at 10:13
  • F() is a macro that forces the strings to stay in flash instead of being copied to SRAM (on Harvard architecture systems, anyway). It then causes a different variant of the print function to be called which reads the data directly from flash.
    – Majenko
    Aug 7, 2018 at 10:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.