robotzero1 / esp32cam-timelapse

Create timelapse images using the ESP32-CAM and other ESP32 based camera modules
58 stars 15 forks source link

Have you tried sending the photo directly to Google Drive? #4

Closed FBMinis closed 3 years ago

FBMinis commented 3 years ago

I have been using the following code but using any resolution over VGA results in photos with gray lines:

https://github.com/fustyles/Arduino/tree/master/ESP32-CAM_PIR_GoogleDrive

Some time ago, before finding your repository, I was following the tutorial in Random Nerds Tutorial website to send photos to Telegram periodically. Sometimes the HTTP request would timeout and I would not receive a photo. Then I found your sketches and the "send by binary" routine allows me to send UXGA with quality=10!

So, maybe you can find a way to send to Google Drive in a decent quality.

Here are some photos I have been collecting as a test. Having them in Google Drive allows me to check what's going on everywhere but also to download and make a timelapse with VirtualDub.

https://drive.google.com/drive/folders/156cK59hy2A9GHn?usp=sharing

I have attached the code I use to take a photo and send to Telegram. It uses an external RTC to cut power until it's time to send it. workingESP32CAMds3231rtclibAlarmTelegramVBatOnly.txt

robotzero1 commented 3 years ago

I've tried a few things with this today but not had any success. I noticed if you took the imageFile variable at line 227

  if (i%3==0) imageFile += urlencode(String(output));

and printed it to the serial monitor and then ran it through this: https://codebeautify.org/url-decode-string followed by this: https://base64.guru/converter/decode/image when you have a higher resolution the bottom of the picture was missing.

So somewhere there's no enough space to store the whole base64 string to send it to Google.

I tried creating and sending the buffer as base64 in chunks with and without urlencoding it but I couldn't get it to work. Maybe someone has a script for this somewhere else but I imagine it's not necessary on normal systems with more resources.

In desperation I tried splitting the base64 string into two parts to send. It worked once and then I just got SSL errors in the serial monitor (with debug verbose set) so I think it's just too much data for the ESP32. Maybe with a different partition structure or writing the base64 string to SPIFFs it could work.


String SendCapturedImage2GoogleDrive() {
  const char* myDomain = "script.google.com";
  String getAll = "", getBody = "";
  String partOutput;
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
    return "Camera capture failed";
  }

  Serial.println("Connect to " + String(myDomain));
  WiFiClientSecure client_tcp;

  if (client_tcp.connect(myDomain, 443)) {
    Serial.println("Connection successful");

    char *input = (char *)fb->buf;
    char output[base64_enc_len(3)];
    String imageFile = "data:image/jpeg;base64,";
    String imageFile2 = "";

    for (int i = 0; i < fb->len / 2; i++) {
      base64_encode(output, (input++), 3);
      if (i % 3 == 0) imageFile += urlencode(String(output));
    }

    for (int i = 0; i < fb->len / 2; i++) {
      base64_encode(output, (input++), 3);
      if (i % 3 == 0) imageFile2 += urlencode(String(output));
    }

    String Data = myFoldername + myFilename + myImage;
    Serial.print("image file length: ");
    Serial.println(imageFile.length());
    Serial.print("image file 2 length: ");
    Serial.println(imageFile2.length());
    client_tcp.println("POST " + myScript + " HTTP/1.1");
    client_tcp.println("Host: " + String(myDomain));
    client_tcp.println("Content-Length: " + String(Data.length() + imageFile.length() + imageFile2.length()));
    client_tcp.println("Content-Type: application/x-www-form-urlencoded");
    client_tcp.println("Connection: keep-alive");
    client_tcp.println();

    client_tcp.print(Data);
    int Index;
    for (Index = 0; Index < imageFile.length(); Index = Index + 1000) {
      client_tcp.print(imageFile.substring(Index, Index + 1000));
      client_tcp.flush();
    }

    for (Index = 0; Index < imageFile2.length(); Index = Index + 1000) {
      client_tcp.print(imageFile2.substring(Index, Index + 1000));
      client_tcp.flush();
    }
    esp_camera_fb_return(fb);

    int waitTime = 10000;   // timeout 10 seconds
    long startTime = millis();
    boolean state = false;

    while ((startTime + waitTime) > millis())
    {
      Serial.print(".");
      delay(100);
      while (client_tcp.available())
      {
        char c = client_tcp.read();
        if (state == true) getBody += String(c);
        if (c == '\n')
        {
          if (getAll.length() == 0) state = true;
          getAll = "";
        }
        else if (c != '\r')
          getAll += String(c);
        startTime = millis();
      }
      if (getBody.length() > 0) break;
    }
    client_tcp.stop();
    Serial.println(getBody);
  }
  else {
    getBody = "Connected to " + String(myDomain) + " failed.";
    Serial.println("Connected to " + String(myDomain) + " failed.");
  }

  return getBody;
}
FBMinis commented 3 years ago

Thank you for investing your time into it. It is clear now that this is something above my skills. I have been reading about google apps scripts to try to understand how this works:

https://developers.google.com/apps-script/guides/web https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script