The blocks transfer a short base64String code to ESP32S3 by WiFi

Hi all.
the APP and ESP32 connected but doesn't send the base64String, why?
thanks.

base64String:
/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAAaABoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAdEAEAAgIDAQAAAAAAAAAAAAABAgMAEQQFITEy/8QAFQEBAQAAAAAAAAAAAAAAAAAAAgT/xAAWEQEBAQAAAAAAAAAAAAAAAAABAgP/2gAMAwEAAhEDEQA/AL+AAD//Z

Show us the responseContent.

I'll bet it's failing that if/then test.

1 Like

Thanks.
the attached shown all labels content.

GGH

ESP32 send 'take_picture' when pressed button.

So responseContent was "idle", and the if/then test failed.

But what if it might have momentarily been different, and we missed it?

That calls for logging the incoming responseContent to a global list, and adding a List Picker to show that list on request.

1 Like

Thanks.
added List Picker got:
when button1 click:

ESP32 button pressed:

List Picker results:

new block:

So that definitely established that the if/then was entered, and the POST was issued.

That changes the question to "What happened to the POST?"

I notice you are using Web1 Gets in a Clock Timer, and not stopping the Clock Timer from repeating when you do the POST.

Could the Clock Timer Gets be interfering with the Web1 POSTs?

Is there a completion event for Web1 POSts where you could catch the result, and turn the Timer back on?

1 Like

thanks for the good point.
I added Clock1.TimerEnabled = false before call web1.posttext, and true after received OK from ESP32.
still got :
[GET] /take_picture
:camera_flash: Button Pressed → set takePictureFlag = true
[GET] /take_picture
[POST] /upload_picture
:x: No plain arg received
[GET] /take_picture
why?

If the esp is not responding (taking a picture?) it's time to read the esp code.

I don't see any in this thread.

P.S. Ever see a swim lane diagram?

They help explain back and forth communication.

1 Like

sure.
ESP32 code:



#include <WiFi.h>
#include <WebServer.h>
#include <TFT_eSPI.h>
#include <JPEGDecoder.h>
#include "mbedtls/base64.h"

TFT_eSPI tft = TFT_eSPI();
WebServer server(80);

const char* ssid = "ESP32_TEST";
const char* password = "12345678";

bool takePictureFlag = false;

void setup() {
  SerialSettingS3();

  delay(6000);
  Serial.println("tttt");

  // Button pin (BOOT on S3)
  pinMode(0, INPUT_PULLUP);

  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.println("AP IP: " + IP.toString());

  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);
  tft.drawString("📡 Waiting...", 10, 10, 2);

  // Simple GET trigger
  server.on("/take_picture", HTTP_GET, []() {
    Serial.println("[GET] /take_picture");
    if (takePictureFlag) {
      server.send(200, "text/plain", "take_picture");
      takePictureFlag = false;
    } else {
      server.send(200, "text/plain", "idle");
    }
  });

  // Upload Base64 image
  server.on("/upload_picture", HTTP_POST, []() {
  Serial.println("[POST] /upload_picture");
  
  if (server.hasArg("plain")) {
    String body = server.arg("plain");
    Serial.printf("📩 Received POST. Base64 length = %d\n", body.length());

    displayBase64Image(body);
    server.send(200, "text/plain", "OK");
  } else {
    Serial.println("❌ No plain arg received");
    server.send(400, "text/plain", "Missing body");
  }
});

  server.begin();
  Serial.println("✅ Web server started");

  //SPIFFS.begin();
  if (!SPIFFS.begin(true)) {
    Serial.println("❌ SPIFFS Mount Failed");    tft.drawString("SPIFFS Fail!", 10, 50, 2);
    return;
  }
}

void loop() {
  server.handleClient();

  // Press BOOT button to trigger photo
  if (digitalRead(0) == LOW) {
    takePictureFlag = true;
    Serial.println("📸 Button Pressed → set takePictureFlag = true");
    delay(500);
  }
}

void displayBase64Image(String base64Str) {
  size_t input_len = base64Str.length();
  size_t decoded_len;
  unsigned char* jpeg_buf = (unsigned char*)malloc(input_len);
  if (!jpeg_buf) {
    Serial.println("❌ malloc failed");
    return;
  }

  //........................
  Serial.printf("📦 Incoming Base64 length: %d\n", input_len);
  Serial.printf("📦 Free heap before decode: %d\n", ESP.getFreeHeap());
  //........................

  int res = mbedtls_base64_decode(jpeg_buf, input_len, &decoded_len,
                                  (const unsigned char*)base64Str.c_str(), input_len);

  //......................
  Serial.printf("📦 Free heap after decode: %d\n", ESP.getFreeHeap());
  //.................

  if (res != 0) {
    Serial.printf("❌ Base64 decode error: %d\n", res);
    free(jpeg_buf);
    return;
  }

  if (res == 0) {
    Serial.printf("✅ Base64 decoded: %d bytes\n", decoded_len);
    if (JpegDec.decodeArray(jpeg_buf, decoded_len)) {
      tft.fillScreen(TFT_BLACK);
      while (JpegDec.read()) {
        int x = JpegDec.MCUx * JpegDec.MCUWidth;
        int y = JpegDec.MCUy * JpegDec.MCUHeight;
        tft.pushImage(x, y, JpegDec.MCUWidth, JpegDec.MCUHeight, JpegDec.pImage);
      }
    } else {
      Serial.println("❌ JPEG decode failed");
    }
  } else {
    Serial.printf("❌ Base64 decode error: %d\n", res);
  }

  free(jpeg_buf);
}


void SerialSettingS3() {
  Serial.begin(115200);
  delay(5000);
  Serial.println("setup1");
  delay(5000);
  Serial.println("setup2");

Serial.print("File   : "); Serial.println(FILE);
  const char compile_date[] = DATE " " TIME;
  Serial.print("Compile timestamp: ");
  Serial.println(compile_date);
}

thanks.

I'm a bit out of my depth here, in http traffic.

I looked at
https://ai2.appinventor.mit.edu/reference/components/connectivity.html#Web

to see what you need to set up to do a POST in AI2.

The doc mentions headers, which I did not see in your blocks.

There is also a flag to say if you want to send or receive a file instead of text.

I could not figure out from your code and blocks which side (ESP/AI2) is actually going to take the picture, or are they just saying

  • You take a picture
  • No, YOU take a picture.

A swim lane diagram would help.

1 Like

yes.
swim lane diagram:

thanks.

See the Sending Data section at the bottom of
https://ai2.appinventor.mit.edu/reference/other/json-web-apis.html

You are not setting up Request Headers in your blocks.

Of course, you are not sending JSON, but there should probably be a Content-Type you should be specifying for your application.

Our Web Services collection:

1 Like

thanks.
added a RequestHeaders made it works.

I have no practical experience with base64 conversion.

I have seen Canvas features that might give you a path to do it in AI2, and have seen posts by more advanced Power Users with extensions and JavaScript for that

Start with the Canvas.

1 Like

Thanks.
I'll test more.
now I have problem is the display quality is really bad.