Send POST to API (esp32cam)

Anyone who can and wants to help by answering with examples of codes that really work, I really appreciate it:

I experimented again with an ESP32-cam. This time I intend to send a face photo to an API.

This API has other functions (GET and DELETE) that I can already do. But POST, which sends the photo, I can't.

I've googled dozens of examples.

This API (and many others) show code samples in all programming languages ​​EXCEPT C++

I didn't want this post to get so long but I don't see any other way.

Next, I will show code examples in at least three different languages, but which, in my opinion, are more similar to C++

PHP HTTP V1.0

<?php

$request = new HttpRequest();
$request->setUrl('https://face-recognition18.p.rapidapi.com/register_face');
$request->setMethod(HTTP_METH_POST);

$request->setHeaders([
	'content-type' => 'multipart/form-data; boundary=---011000010111000001101001',
	'x-face-uid' => 'rostomulher',
	'X-RapidAPI-Key' => 'f24a8bec96msh336f26930e2c623p10fc12jsn',
	'X-RapidAPI-Host' => 'face-recognition18.p.rapidapi.com'
]);

$request->setBody('-----011000010111000001101001
Content-Disposition: form-data; name="face_image"


-----011000010111000001101001--

');

try {
	$response = $request->send();

	echo $response->getBody();
} catch (HttpException $ex) {
	echo $ex;
}

HTTP 1.1

POST /register_face HTTP/1.1
Content-Type: multipart/form-data; boundary=---011000010111000001101001
X-Face-Uid: rostomulher
X-Rapidapi-Key: f24a8bec96msh336f26930e2c623p10fc12jsn
X-Rapidapi-Host: face-recognition18.p.rapidapi.com
Host: face-recognition18.p.rapidapi.com
Content-Length: 121

-----011000010111000001101001
Content-Disposition: form-data; name="face_image"


-----011000010111000001101001--

C# HttpClient

var client = new HttpClient();
var request = new HttpRequestMessage
{
	Method = HttpMethod.Post,
	RequestUri = new Uri("https://face-recognition18.p.rapidapi.com/register_face"),
	Headers =
	{
		{ "x-face-uid", "rostomulher" },
		{ "X-RapidAPI-Key", "f24a8bec96msh336f26930e2c623p10fc12jsn" },
		{ "X-RapidAPI-Host", "face-recognition18.p.rapidapi.com" },
	},
	Content = new MultipartFormDataContent
	{
		new StringContent("")
		{
			Headers =
			{
				ContentType = new MediaTypeHeaderValue("application/octet-stream"),
				ContentDisposition = new ContentDispositionHeaderValue("form-data")
				{
					Name = "face_image",
					FileName = "RostoMulher.jpg",
				}
			}
		},
	},
};
using (var response = await client.SendAsync(request))
{
	response.EnsureSuccessStatusCode();
	var body = await response.Content.ReadAsStringAsync();
	Console.WriteLine(body);
}

The relevant part of the code I'm using to try to send the camera photo:

if (client.connect(serverName.c_str(), serverPort)) {
    Serial.println("Connection successful!");    
    String head = "---011000010111000001101001\r\nContent-Disposition: form-data; Name=\"face_image\" \r\n\r\n";
//    String head = "---011000010111000001101001\r\nContent-Disposition: form-data; Name=\"face_image\"; FileName=\"pessoaA.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n---011000010111000001101001\r\n";
    
    uint16_t imageLen = fb->len;
    uint16_t extraLen = head.length() + tail.length();
    uint16_t totalLen = imageLen + extraLen;
  
    client.println("POST " + serverPath + " HTTP/1.1");
    client.println("Host: " + serverName);
    client.println("Content-Type: multipart/form-data; boundary=---011000010111000001101001");
    client.println("x-face-uid:pessoaA");
    client.println("X-RapidAPI-Key:f24a8bec96msh336f26930e2c623p10fc12jsn");
    client.println("Content-Length: " + String(totalLen));
    client.println();
    client.print(head);

    Serial.println(head);
  
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    for (size_t n=0; n<fbLen; n=n+1024) {
      if (n+1024 < fbLen) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
      }
      else if (fbLen%1024>0) {
        size_t remainder = fbLen%1024;
        client.write(fbBuf, remainder);
      }
    }   
    client.print(tail);
    
    esp_camera_fb_return(fb);

And the error I see on the serial monitor:

Connecting to server: face-recognition18.p.rapidapi.com
Connection successful!
---011000010111000001101001
Content-Disposition: form-data; Name="face_image" 


...............
getBody é: 
{"detail":[{"loc":["body","face_image"],"msg":"field required","type":"value_error.missing"}]}

I don't understand down there: detail, loc, msg... there's nothing like that in the code examples. The API doesn't ask for these fields but it seems to be returning demanding these fields.

Please re-read the 1st paragraph of this post.

Thanks

One more example, in Python http.client

I think this would be the best one to follow. I'm trying to understand.

import http.client

conn = http.client.HTTPSConnection("face-recognition18.p.rapidapi.com")

payload = "-----011000010111000001101001\r
Content-Disposition: form-data; name=\"face_image\"\r
\r
\r
-----011000010111000001101001--\r
\r
"

headers = {
    'content-type': "multipart/form-data; boundary=---011000010111000001101001",
    'x-face-uid': "rostomulher",
    'X-RapidAPI-Key': "f24a8bec96msh336f26930e2c623p10fc12jsn",
    'X-RapidAPI-Host': "face-recognition18.p.rapidapi.com"
    }

conn.request("POST", "/register_face", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

I decided to use certificate in the code. Now on the serial monitor I see (verbose) the reception of the connection with certificate. That is, the CA is correct.

I see the sending of the photo from esp32cam, but almost at the end the clean SSL connection occurs and an API does not receive the photo and returns saying that there is no image.

How to resolve this clean SSL connection ?

[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 1024 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 770 bytes...
[V][ssl_client.cpp:295] send_ssl_data(): Writing HTTP request with 18 bytes...
.............
[V][ssl_client.cpp:265] stop_ssl_socket(): Cleaning SSL connection.

{"transaction":{"status":"failed","errcode":1001,"message":"no image found"}}

The posts are getting big. I didn't want to, I ask for the patience of anyone who wants to help.

I successfully sent a photo of a face to the API using Postman. But it's easy there, in one of the Body fields I choose FILE and attach a jpg.

The API asks that the photo, in this case the output of esp32-cam, be inside the String that I called head. Instead of Pedro.jpg it has to be the output binary of esp32-cam.

I say 'should send the photo' because it's not sending because I don't know how to put the output of esp32-cam (fb->buf, fb->len) as a String. I haven't found anything about it yet.

All the other functions that the API offers (rename user, rename group, delete, etc, etc), I managed to do (excerpts that are not in the code below).

I have 2 doubts:

A) From time to time it does not connect because it keeps giving DNS error (-1).

B) How to put the esp32-cam output in the String head ?

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"

const char* test_root_ca= \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDQTCCAimgAwIBAgITBZsG4q5WTP468SQvvG5\n" \
etc
etc
etc
"-----END CERTIFICATE-----\n";

const char* ssid = "zzzzzzzzzzzzzzzzzzzz";
const char* password = "zzzzzzzzzzzzzzzzzzzzz";

String host          = "vbox-recognizer.p.rapidapi.com"; 
String url           = "https://vbox-recognizer.p.rapidapi.com/register";
const int serverPort = 443;

int nowifi,autorizaenvio;

WiFiClientSecure client;

// 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
#define flash 4

void setup() {
  
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
  pinMode(flash,OUTPUT);
  
  Serial.begin(115200);
  delay(1000);

  client.setCACert(test_root_ca);
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);              

  Serial.println("Conectando ao WiFi");
  while (WiFi.status() != WL_CONNECTED) {  
      delay(1000);
      nowifi++;
      Serial.println(nowifi);
       if(nowifi > 3) {
           ESP.restart();
       }
  }

  Serial.println(WiFi.localIP());
  Serial.println(WiFi.RSSI());   

  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;

  // init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_CIF;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_CIF;
    config.jpeg_quality = 10;
    config.fb_count = 1;
  }
  
  // camera init
  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();
  }

  sensor_t * s = esp_camera_sensor_get();
  s->set_brightness(s, 2);
  s->set_contrast(s, 1);
  s->set_whitebal(s, 1);
  s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  s->set_wb_mode(s, 1);
  s->set_aec_value(s, 0);
  s->set_lenc(s, 1);
  s->set_hmirror(s, 0); // disable, 1 - enable 
  
  delay(1000);
}

void loop() {

  if(autorizaenvio == 0) {
     sendPhoto();
  }
  delay(5);
}

String sendPhoto() {  
  String getAll;
  String getBody;

  camera_fb_t * fb = NULL;
   digitalWrite(flash, HIGH);
  fb = esp_camera_fb_get();
   digitalWrite(flash, LOW);
  if(!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
  }

  if (client.connect(host.c_str(), serverPort))  {

    Serial.println("-- CONECTADO --");    

    String head = "sergioboundary\r\nContent-Disposition: form-data;\r\n{\r\n\"image\":\"Pedro.jpg\",\r\n\"identifier\":\"Pedro\",\r\n\"group_name\":\"A\"\r\n} \r\n";
    String tail = "\r\nsergioboundary\r\n";

    uint32_t imageLen = fb->len;
    uint32_t extraLen = head.length() + tail.length();
    uint32_t totalLen = imageLen + extraLen;

    client.println("POST " + url + " HTTP/1.1");
    client.println("Host: " + host);
    client.println("X-RapidAPI-Key:f24a8bec96msh");
    client.println("X-RapidAPI-Host:vbox-recognizer.p.rapidapi.com"),
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=sergioboundary");
    client.println();
    client.print(head);
  
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    for (size_t n=0; n<fbLen; n=n+1024) {
      if (n+1024 < fbLen) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
      }
      else if (fbLen%1024>0) {
        size_t remainder = fbLen%1024;
        client.write(fbBuf, remainder);
      }
    }   
    client.print(tail);
    
    esp_camera_fb_return(fb);
    
    int timoutTimer = 30000;
    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 " + host +  " failed.";
    Serial.println(getBody);
  }
  autorizaenvio = 1;
  return getBody;
}

Someone help ? (I keep searching but I can't find an answer)

I'm exchanging information with API support. I will update here when I achieve success. The only change I made was to first save the espcam photo in SPIFFS and then send it to the API.

I'm still searching the forums and the internet for the correct way to send a simple esp32cam photo to an API.

Binary format.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.