Hi, I am trying to post a Raspberry Pi camera image to Goggle Drive along with some related information to Google Sheets using HTTP MultiPart Request (post) and a Google Script.
The data is successfully recorded in the Google Sheet but the image, although successfully transferred is not what it should be and is not readable as image.
My Project is as below:
1- NodeRed nodes
[{"id":"3630c78a.035728","type":"camerapi-takephoto","z":"5dd68f45.ecadb","filemode":"1","filename":"car-photo.jpeg","filedefpath":"0","filepath":"/home/pi/Pictures/","fileformat":"jpeg","resolution":"3","rotation":"0","fliph":"0","flipv":"0","brightness":"50","contrast":"0","sharpness":"0","quality":"","imageeffect":"none","agcwait":"","name":"Take Photo","x":230,"y":220,"wires":[["57df1ca5.e60f24"]]}]
2- Function node content:
var image = msg.payload;
msg.headers = {};
msg.headers["content-type"] ='multipart/form-data';
msg.payload = {};
msg.payload['myFile'] = image;
msg.payload['myToken'] = 'zrxC';
msg.payload['myFilename'] = 'myCarPic.jpg';
msg.payload['myFoldername'] = 'ESP32-CAM';
msg.payload['Station'] = 'Hawaii';
msg.payload['lat'] = '1,23';
msg.payload['long'] = '4.32';
msg.payload['maxSpeed'] = '40';
msg.payload['radarSpeed'] = '78';
return msg;
2- Google Script
var timeZone = "GMT";
var dateTimeFormat = "dd/MM/yyyy HH:mm:ss";
var logSpreadSheetId = "1W1ywsEkfKNFSqsSdAgQbjFgzHO8LDaxv6mNzTP9h4t8";
var dateTime = Utilities.formatDate(new Date(), timeZone, dateTimeFormat);
function doPost(e) {
var myFoldername = e.parameter.myFoldername;
var myFile = e.parameter.myFile;
var myFilename = Utilities.formatDate(new Date(), timeZone, "ddMMyyyyHHmmss")+".jpg";
var myToken = e.parameter.myToken;
var Station = e.parameter.Station;
var lat = e.parameter.lat;
var long = e.parameter.long;
var maxSpeed = e.parameter.maxSpeed;
var radarSpeed = e.parameter.radarSpeed;
var contentType = myFile.substring(myFile.indexOf(":")+1, myFile.indexOf(";"));
var data = myFile.substring(myFile.indexOf(",")+1);
data = Utilities.base64Decode(data);
var blob = Utilities.newBlob(data, contentType, myFilename);
// Save a captured image to Google Drive.
var folder, folders = DriveApp.getFoldersByName(myFoldername);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(myFoldername);
}
var file = folder.createFile(blob);
file.setDescription("Uploaded by " + myFilename);
var imageID = file.getUrl().substring(file.getUrl().indexOf("/d/")+3,file.getUrl().indexOf("view")-1);
var imageUrl = "https://drive.google.com/uc?id="+imageID;
addLog(myFilename,imageUrl,Station,lat,long,maxSpeed,radarSpeed);
return ContentService.createTextOutput(myFoldername+"/"+myFilename+"\n"+imageUrl+"\n"); //+res);
}
function addLog(myFilename,imageUrl,Station,lat,long,maxSpeed,radarSpeed) {
var spr = SpreadsheetApp.openById(logSpreadSheetId);
var sheet = spr.getSheets()[0];
var data = sheet.getDataRange().getValues();
var pos = sheet.getLastRow();
var rowData = [];
if(!pos>0){
pos = 1;
rowData[0] = "Data";
rowData[1] = "Image";
rowData[2] = "URL";
rowData[3] = "Station";
rowData[4] = "Lat";
rowData[5] = "Long";
rowData[6] = "MaxSpeed";
rowData[7] = "RadarSpeed";
rowData[8] = "NumPlate";
rowData[9] = "Manufacturer";
rowData[10] = "Model";
rowData[11] = "Color";
var newRange = sheet.getRange(pos, 1, 1, rowData.length);
newRange.setValues([rowData]);
}
pos = pos +1;
rowData = [];
rowData[0] = dateTime;
rowData[1] = myFilename;
rowData[2] = imageUrl;
rowData[3] = Station;
rowData[4] = lat;
rowData[5] = long;
rowData[6] = maxSpeed;
rowData[7] = radarSpeed;
var newRange = sheet.getRange(pos, 1, 1, rowData.length);
newRange.setValues([rowData]);
}
3- Google Drive image file:
Using this process the "image" file is always 32bytes.
In the Base64 node I tried "convert buffer to base64" and "Encode as base64", result is the same.
NOTE: The Google Script works fine when I transfer image and data using an ESP32-CAM (encoding the image as base64).
ESP32-CAM c++ Sketch:
/*
ESP32-CAM (Save a captured photo to SD card, Google Drive, LineNotify)
Author : ChungYi Fu (Kaohsiung, Taiwan) 2019-9-16 22:30
https://www.facebook.com/francefu
Google Apps Script
https://github.com/fustyles/webduino/blob/gs/SendCapturedImageToGoogleDriveAndLinenotify_doPost.gs
You must allow anyone and anonymous to execute the google script.
How to add a new script
https://www.youtube.com/watch?v=f46VBqWwUuI
https://script.google.com/home
https://script.google.com/home/executions
https://drive.google.com/drive/my-drive
*/
// Enter your WiFi ssid and password
const char* ssid = "SSID"; //your network SSID
const char* password = "pass"; //your network password
const char* myDomain = "script.google.com";
String myScript = "/macros/s/AKxJLoDA1fycbrJubn4f-YoMGQxqX5oVSZWuPuegxmQ77__KEJzo1O0/exec"; //Google Apps Script ID.
String myLineNotifyToken = "myToken=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; //Line Notify Token
String myFoldername = "&myFoldername=ESP32-CAM";
String myFilename = "";
String myImage = "&myFile=";
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "Base64.h"
#include "esp_camera.h"
#include <EEPROM.h>
// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
// or another board which has PSRAM enabled
//CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
void setup(){
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
delay(10);
WiFi.mode(WIFI_STA);
Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
long int StartTime=millis();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if ((StartTime+10000) < millis()) break;
}
Serial.println("");
Serial.println("STAIP address: ");
Serial.println(WiFi.localIP());
Serial.println("");
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Reset");
delay(1000);
ESP.restart();
}
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_VGA; // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
config.jpeg_quality = 10;
config.fb_count = 1;
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
delay(1000);
ESP.restart();
}
}
void loop(){
EEPROM.begin(sizeof(int)*4);
EEPROM.write(0, EEPROM.read(0)+1);
EEPROM.commit();
myFilename = "&myFilename="+String(EEPROM.read(0))+".jpg";
saveCapturedImage2GoogleDrive();
delay(72000);
}
void saveCapturedImage2GoogleDrive() {
Serial.println("Connect to " + String(myDomain));
WiFiClientSecure client;
if (client.connect(myDomain, 443)) {
Serial.println("Connection successful");
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
return;
}
char *input = (char *)fb->buf;
char output[base64_enc_len(3)];
String imageFile = "data:image/jpeg;base64,";
for (int i=0;i<fb->len;i++) {
base64_encode(output, (input++), 3);
if (i%3==0) imageFile += urlencode(String(output));
}
String Data = myLineNotifyToken+myFoldername+myFilename+"&Station=RioFundo"+"&lat=12.30"+"&long=23.1"+"&maxSpeed=60"+"&radarSpeed=80"+myImage;
Serial.print("Data to Post: ");
Serial.println(Data);
esp_camera_fb_return(fb);
Serial.println("Send a captured image to Google Drive.");
client.println("POST " + myScript + " HTTP/1.1");
client.println("Host: " + String(myDomain));
client.println("Content-Length: " + String(Data.length()+imageFile.length()));
client.println("Content-Type: application/x-www-form-urlencoded");
client.println();
client.print(Data);
int Index;
for (Index = 0; Index < imageFile.length(); Index = Index+1000) {
client.print(imageFile.substring(Index, Index+1000));
}
Serial.println("Waiting for response from Google Drive");
long int StartTime=millis();
while (!client.available()) {
Serial.print(".");
delay(100);
if ((StartTime+10000) < millis()) {
Serial.println();
Serial.println("No response.");
break;
}
}
Serial.println();
while (client.available()) {
Serial.print(char(client.read()));
}
client.stop();
myDomain = "api.openalpr.com";
myScript = "/v2/recognize";
Data = "secret_key=mySecretOpenALPRKey&recognize_vehicle=1&country=br&return_image=0&topn=10";
if (client.connect(myDomain, 443)){
Serial.println("Connection to api.openalpr.com - OK");
client.println("POST " + myScript ); //+ " HTTP/1.1");
client.println("Host: " + String(myDomain));
client.println("Content-Length: " + String(Data.length()+imageFile.length()));
client.println("Content-Type: application/x-www-form-urlencoded");
client.println();
client.print(Data);
Serial.println("Posting Image...");
for (int i = 0; i < imageFile.length(); i=i+1000) {
client.print(imageFile.substring(i, i+1000));
}
Serial.println("Image sent to openalpr.com...");
}else{
Serial.println("Connection to api.openalpr.com - Fail");
}
Serial.println("Waiting for response from api.openalpr.com");
StartTime=millis();
while (!client.available()) {
Serial.print(".");
delay(100);
if ((StartTime+10000) < millis()) {
Serial.println();
Serial.println("No response.");
break;
}
}
Serial.println();
while (client.available()) {
Serial.print(char(client.read()));
}
client.stop();
}else {
Serial.println("Connected to " + String(myDomain) + " failed.");
}
}
//https://github.com/zenmanenergy/ESP8266-Arduino-Examples/
String urlencode(String str){
String encodedString="";
char c;
char code0;
char code1;
char code2;
for (int i =0; i < str.length(); i++){
c=str.charAt(i);
if (c == ' '){
encodedString+= '+';
} else if (isalnum(c)){
encodedString+=c;
} else{
code1=(c & 0xf)+'0';
if ((c & 0xf) >9){
code1=(c & 0xf) - 10 + 'A';
}
c=(c>>4)&0xf;
code0=c+'0';
if (c > 9){
code0=c - 10 + 'A';
}
code2='\0';
encodedString+='%';
encodedString+=code0;
encodedString+=code1;
//encodedString+=code2;
}
yield();
}
return encodedString;
}
*------------------------------------------------------------------
Assistance welcome.
Thanks