Cant seem to figure out how to convert rgb565 to jpg

I'm trying to read a rbg565 image w esp32-cam and then process it, maybe change some of the pixels and then upload to a web server.
When I try and do the frame2jpg_cb(fb, quality, &buffer_jpeg, NULL);
where the buffer_jpeg supposedly moves the jpg data back into frame buffer it crashes.
Anyone attempted to do something like this ?


Found PSRAM, this will improve performance!

Camera init success

Guru Meditation Error: Core  0 panic'ed (Double exception). 

Core  0 register dump:
PC      : 0x403874da  PS      : 0x00040136  A0      : 0x8037f0d3  A1      : 0x3fcc2150  
A2      : 0x00040136  A3      : 0x00040026  A4      : 0x00000027  A5      : 0x3c0ce6a3  
A6      : 0x00000001  A7      : 0x00000004  A8      : 0x3fcc2210  A9      : 0x00000000  
A10     : 0x0000005c  A11     : 0x00000804  A12     : 0x3fca7de8  A13     : 0x00000000  
A14     : 0x3fcc2650  A15     : 0x3c100908  SAR     : 0x00000004  EXCCAUSE: 0x00000002  
EXCVADDR: 0x00000000  LBEG    : 0x400556d5  LEND    : 0x400556e5  LCOUNT  : 0xfffffffe  


Backtrace: 0x403874d7:0x3fcc2150 0x4037f0d0:0x00060f23 |<-CORRUPTED

Here is the code

#include <ArduinoOTA.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <WebSerial.h>
#include "camera.h"
#include "esp_camera.h"
#include "base64.h"
#include <Adafruit_NeoPixel.h>
 uint16_t jpeg_length = 0;
#define LEFTRIGHT 40
#define UPDOWN 40
#define BACKUP 5

// 1 means use USB  0 means use web
#define SERIALUSB 1

// Replace with your network credentials
//const char *ssid = "omghi2u"; //"OCWirelessIoT";
//const char *password = "SkolVikings"; //"willywonkaoompaloompa";
const char *ssid = "OCWirelessIoT";
const char *password = "willywonkaoompaloompa";

//pick the board - ESP32S3 Dev Module

// Define the pin where the built-in RGB LED is connected
#define LED_PIN 48
// Define the number of LEDs in the strip (usually 1 for built-in LED)
#define NUM_LEDS 1
// Create an instance of the Adafruit_NeoPixel class
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

// Create an instance of AsyncWebServer
AsyncWebServer server(80);

void setup() {
  // Serial and WebSerial setup
  Serial.begin(115200);

  light_on();

  WiFi.begin(ssid, password);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  //WebSerial.begin(&server);
  //WebSerial.onMessage(recvMsg);
  Serial.println("Connected to WiFi");
  String ipString = WiFi.localIP().toString();
  Serial.printf("IP Address: %s\n", ipString);

  Serial.println("ESP Board MAC Address:  ");
  Serial.println(WiFi.macAddress().c_str());

  // Initialize the camera
  Serial.println("init camera before");
  initCamera();
  Serial.println("init camera after");

  // Web server route to serve text
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", "ESP32-CAM Web Server is running!");
  });
  Serial.println("after / dir");

  // Web server route to serve a picture
  server.on("/capture", HTTP_GET, [](AsyncWebServerRequest *request) {
    String photo = capturePhoto();
    if (photo.length() > 0) {

      String html = "<!DOCTYPE html><html lang='en'><head><meta name='viewport' content='width=device-width, initial-scale=1.0'>";
      html += "<title>ESP32-CAM Capture</title></head><body>";
      html += "<h1>ESP32-CAM Web Server</h1>";
      html += "<p>This is a snapshot from ESP32-CAM.</p>";
      html += "<img src='" + photo + "'>";
      html += "'>";
      html += "</body></html>";
      request->send(200, "text/html", html);
      Serial.println("send to capture server");

    } else {
      request->send(500, "text/plain", "Camera capture failed");
    }
  });
  Serial.println("after /capture");

  // Start the server
  server.begin();

  Serial.println("after server begin");

  // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  ArduinoOTA.setHostname("esp32-cam");

  // No authentication by default
  ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH) {
        type = "sketch";
      } else {  // U_SPIFFS
        type = "filesystem";
      }

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) {
        Serial.println("Auth Failed");
      } else if (error == OTA_BEGIN_ERROR) {
        Serial.println("Begin Failed");
      } else if (error == OTA_CONNECT_ERROR) {
        Serial.println("Connect Failed");
      } else if (error == OTA_RECEIVE_ERROR) {
        Serial.println("Receive Failed");
      } else if (error == OTA_END_ERROR) {
        Serial.println("End Failed");
      }
    });

  ArduinoOTA.begin();
}  //end of setup

void loop() {
  // WebSerial works asynchronously
  ArduinoOTA.handle();
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// capture a photo and return the buffer
String capturePhoto() {
  
  light_on();
  delay(300);

  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return "";
  } else {
    Serial.println("Took picture success");
  }
 
  //find angle of red dial
  findangle(fb, 0);
   
   // encode to jpeg
    uint8_t quality = 90;

    frame2jpg_cb(fb, quality, &buffer_jpeg, NULL);

String photo = "data:image/jpeg;base64,";
  photo += base64::encode(fb->buf, fb->len);
  esp_camera_fb_return(fb);
  Serial.println("converted to base64");
  return photo;
}

void recvMsg(uint8_t *data, size_t len) {
  Serial.println("Received Data...");
  String d = "";
  for (int i = 0; i < len; i++) {
    d += char(data[i]);
  }
  Serial.println(d);
  if (d == "ON") {
    light_on();
  }
  if (d == "OFF") {
    light_off();
  }
}

void light_on() {
  strip.setPixelColor(0, strip.Color(255, 255, 255));  // White
  strip.show();
}

void light_off() {
  strip.setPixelColor(0, strip.Color(0, 0, 0));  // White
  strip.show();
}

float findangle(camera_fb_t *fb, int start_angle) {
  Serial.println("starting findangle");

  int center_x = fb->width / 2;
  int center_y = fb->height / 2;
  int r = (3 * center_x) / 7;

  Serial.printf("center x,y:%d  %d\n ", center_x, center_y);
  Serial.printf("radius is %d\n", r);

  int x, y, up, down, left, right, x1, y1, degree;
  uint16_t pixel;
  float angle = -1;
  bool found = false;

  for (int a = 0; a < 360; a++) {
    degree = (start_angle + a - BACKUP) % 360;
    float radians = degree * .0174533;
    x1 = center_x + (r * cos(radians));
    y1 = center_y + (r * sin(radians));
    pixel = ((uint16_t *)fb->buf)[y1 * fb->width + x1];

    if (isRed(pixel)) {
      Serial.printf("angle,x,y,%d,%d,%d,RED\n", degree, x1, y1);

      // found dial
      limitx(x1, left, right, fb->width);
      limity(y1, up, down, fb->height);
      found = true;
    } else {
      Serial.printf("angle,x,y,%d,%d,%d\n", degree, x1, y1);
    }

    //make green dots
    uint16_t *pixels = (uint16_t *)fb->buf;  // Cast buffer to 16-bit
    pixels[(y1 * fb->width) + x1] = 0x07E0;  // Set pixel to green
    esp_camera_fb_return(fb);
  }

  Serial.println("findangle done.");

  return angle;
}


bool isRed(uint16_t pixel) {
  uint8_t r = (pixel >> 11) & 0x1F;
  uint8_t g = (pixel >> 5) & 0x3F;
  uint8_t b = pixel & 0x1F;
  bool red = false;
  // Simple threshold to detect red color
  if (r > 20 && g < 15 && b < 15) {
    red = true;
  }

  //Serial.printf("r-g-b-RED  %d  %d  %d  %d\n",r,g,b,red);
  return red;
}

void limitx(int x, int &left, int &right, int width) {
  left = x - LEFTRIGHT;
  if (left < 0) {
    left = 0;
  }
  right = x + LEFTRIGHT;
  if (right > width) {
    right = width;
  }
}

void limity(int y, int &up, int &down, int height) {
  down = y - UPDOWN;
  if (down < 0) {
    down = 0;
  }
  up = y + UPDOWN;
  if (up > height) {
    up = height;
  }
}




String base64Encode(uint8_t *data, size_t len) {
    return base64::encode(data, len);
}

/**
 * Put JPEG-encoded data back into the original frame
 * (you don't have to modify this)
 */



size_t buffer_jpeg(void *arg, size_t index, const void* data, size_t len) {
 
    if (index == 0) {
        // first MCU block => reset jpeg length
        jpeg_length = 0;
    }

    if (len == 0) {
        // encoding is done
        ((camera_fb_t *)arg)->len = jpeg_length;
        return 0;
    }

    jpeg_length += len;

    // override input data
    memcpy( (uint8_t*) arg + index, (uint8_t*) data, len);

    return len;
}

See this article: ESP32 cam JPEG encoding on the fly

and check for core and/or library version compatibility. You may have to roll back the Arduino core.

Wow...that's where i actually got some of the code but I didn't realize about needing 2.0 Core...
super thanks for the quick response and very helpful tip... I will try that when I get home tonight!

I moved back to 2.0.17
image
but still get the same error- see below...any thoughts on what I'm doing wrong?

Guru Meditation Error: Core  1 panic'ed (StoreProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x40056f60  PS      : 0x00060130  A0      : 0x82002e2c  A1      : 0x3fcb4940  
A2      : 0x00000000  A3      : 0x3fcb4cb8  A4      : 0x00000200  A5      : 0x00000000  
A6      : 0xe0ffd8ff  A7      : 0x464a1000  A8      : 0x00000000  A9      : 0x3fcb4a10  
A10     : 0x3fcb4aa8  A11     : 0x00000002  A12     : 0x3fcb4ea8  A13     : 0x3fca0224  
A14     : 0x3fca00f1  A15     : 0x3fca0202  SAR     : 0x00000018  EXCCAUSE: 0x0000001d  
EXCVADDR: 0x00000000  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x0000001f  


Backtrace: 0x40056f5d:0x3fcb4940 0x42002e29:0x3fcb4950 0x42094779:0x3fcb4970 0x420947b6:0x3fcb4990 0x4203f21d:0x3fcb49b0 0x4203f439:0x3fcb49d0 0x4203f487:0x3fcb49f0 0x4203fe74:0x3fcb4a10 0x4203ff7d:0x3fcb4a40 0x4203eea1:0x3fcb4a60 0x4203f122:0x3fcb4ef0 0x4203f145:0x3fcb4f20 0x420033b6:0x3fcb4f50 0x42003536:0x3fcb4fa0 0x42007bb2:0x3fcb5020 0x4209345b:0x3fcb5060 0x42007c35:0x3fcb5080 0x42003906:0x3fcb50a0 0x42007736:0x3fcb50c0 0x42008469:0x3fcb5150 0x42003906:0x3fcb51a0 0x42007736:0x3fcb51c0 0x42008a76:0x3fcb5250 0x42009791:0x3fcb5290 0x4200998d:0x3fcb52d0 0x4200bf11:0x3fcb52f0 0x4200bf89:0x3fcb5320 0x4200c746:0x3fcb5340

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