vshymanskyy / TinyGSM

A small Arduino library for GSM modules, that just works
GNU Lesser General Public License v3.0
1.96k stars 726 forks source link

SIM800 - HTTP POST - image upload issue. #499

Closed renno-bih closed 3 years ago

renno-bih commented 3 years ago

I'm using ESP32 connected to SIM800 module and I'm trying to upload jpeg photo using HTTP POST request. Image size is between 15kb and 30kb and it is not possible to send the whole picture at once. (Limit is 1460 bytes for SIM800). My approach is to split image to 1024 bytes and send them using HTTP POST request. It starts writing 1024 bytes chunks just fine but it always fails sending few 1024b chunks and image arrives corrupted to the server. Debugging shows the following:

.
.
.
AT+CIPSEND=0,1024
....... {lots of data}.....
DATA ACCEPT:0,1024

AT+CIPSEND=0,1024
....... {lots of data}.....
DATA ACCEPT:0,1024

AT+CIPSEND=0,1024
....... {lots of data}.....
0, SEND FAIL

AT+CIPSEND=0,1024
....... {lots of data}.....
DATA ACCEPT:0,1024
.
.
.

It is always 2-3kb that fail to write to server, no matter what signal strength is. This is an example code I'm using:

  String getAll;
  String getBody;
  String head = "--TestBoundary\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"GWF008D15D717C.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
  String tail = "\r\n--TestBoundary--\r\n";

  uint32_t imageLen = ImgMetaData.imSize;
  uint32_t extraLen = head.length() + tail.length();
  uint32_t totalLen = imageLen + extraLen;
  Serial.println("Connecting to server: " + serverName);
  if (client.connect(serverName.c_str(), serverPort)) {
    Serial.println("Connection successful! (Using sim800)");
    client.println("POST " + serverPath + " HTTP/1.1");
    client.println("Host: " + serverName);
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=TestBoundary");
    client.println();
    client.print(head);

    uint8_t *fbBuf = (unsigned char*)tempImageBuffer;
    for (size_t n = 0; n < imageLen; n = n + 1024) {
      if (n + 1024 < imageLen) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
        Serial.println("Sending 1024 bytes");
      }
      else if (imageLen % 1024 > 0) {
        size_t remainder = imageLen % 1024;
        client.write(fbBuf, remainder);
        Serial.println("Sending reminder");
      }
    }
    client.print(tail);
    Serial.println("Sending tail");
    int timoutTimer = 10000;
    long startTimer = millis();
    boolean state = false;
    while ((startTimer + timoutTimer) > millis()) {
      Serial.print(".");
      delay(100);
      while (WiFiclient.available()) {
        char c = WiFiclient.read();
        if (c == '\n') {
          if (getAll.length() == 0) {
            state = true;
          }
          getAll = "";
        }
        else if (c != '\r') {
          getAll += String(c);
        }
        if (state == true) {
          getBody += String(c);
        }
        startTimer = millis();
      }
      if (getBody.length() > 0) {
        break;
      }
    }
    Serial.println();
    client.stop();
    Serial.println(getBody);
  }
  else {
    getBody = "Connection to " + serverName +  " failed.";
    Serial.println(getBody);
  }

I was worried that my HTTP POST request was not formatted correctly. But then I used the same code to upload the same data using ESP32 WiFi (WiFiClient) and server always receive the whole image correctly.

Any idea what is wrong here?

SRGDamia1 commented 3 years ago

I would add a retry on each client write. Keep the result of the client.write; it should return the number of values sent (ie, either 1024 or 0). If it is 0, retry the same chunk before moving on to the next one.

renno-bih commented 3 years ago

Thanks @SRGDamia1 That SOLVED my problem :)

Using this approach I don't have these issues anymore:

if (client.connect(serverName.c_str(), serverPort)) {
      Serial.println("Connection successful! (Using sim800)");
      client.println("POST " + serverPath + " HTTP/1.1");
      client.println("Host: " + serverName);
      client.println("Content-Length: " + String(totalLen));
      client.println("Content-Type: multipart/form-data; boundary=TestBoundary");
      client.println();
      client.print(head);

      uint8_t *fbBuf = (unsigned char*)tempImageBuffer;
      for (size_t n = 0; n < imageLen; n = n + sendChunksSize) {
        int sent_size = 0;
        if (n + sendChunksSize < imageLen) {
          sent_size = client.write(fbBuf, sendChunksSize);
          if (sent_size == sendChunksSize) {
            fbBuf += sendChunksSize;
          }
          else {
            Serial.println("Failed to send chunk of data, retrying");
            n -= sendChunksSize;
          }
        }
        else if (imageLen % sendChunksSize > 0) {
          size_t remainder = imageLen % sendChunksSize;
          sent_size = client.write(fbBuf, remainder);
          while (sent_size != remainder) {
            Serial.println("Failed to send reminder, retrying");
            sent_size = client.write(fbBuf, remainder);
          }
        }
      }
      client.print(tail);
      int timoutTimer = 5000;
      long startTimer = millis();
      boolean state = false;
      while ((startTimer + timoutTimer) > millis()) {
        Serial.print(".");
        delay(100);
        while (client.available()) {
          char c = client.read();
          if (c == '\n') {
            if (getAll.length() == 0) {
              state = true;
            }
            getAll = "";
          }
          else if (c != '\r') {
            getAll += String(c);
          }
          if (state == true) {
            getBody += String(c);
          }
          startTimer = millis();
        }
        if (getBody.length() > 0) {
          break;
        }
      }
      Serial.println();
      client.stop();
      Serial.println(getBody);
    }
    else {
      getBody = "Connection to " + serverName +  " failed.";
      Serial.println(getBody);
    }
  }