I plan to experiment with camera transmission/reception.
Maybe someone has already tried that model and can share the insides )
Hopefully it is going to work fine


Is this code available somewhere?I have to say it was really fun to playing with this board. My final result is esp32 http server with options to change resolution and jpeg quality(compression level) from website on the fly and capture image on demand.
Code: Select all
// from httpd handler
set_frame_size(camera_config->frame_size);
set_pixel_format(camera_config->pixel_format, camera_config->jpeg_quality);
reinit_dma();
Code: Select all
// camera.c modifications
esp_err_t allocate_fb() {
s_state->fb = (uint8_t*) calloc(s_state->fb_size, 1);
if (s_state->fb == NULL) {
ESP_LOGE(TAG, "Failed to allocate frame buffer");
esp_err_t err = ESP_ERR_NO_MEM;
return err;
}
ESP_LOGI(TAG,
"in_bpp: %d, fb_bpp: %d, fb_size: %d, mode: %d, width: %d height: %d, quality: %u",
s_state->in_bytes_per_pixel, s_state->fb_bytes_per_pixel,
s_state->fb_size, s_state->sampling_mode, s_state->width,
s_state->height, s_state->config.jpeg_quality);
ESP_LOGW(TAG, "Allocating frame buffer (%d bytes)", s_state->fb_size);
heap_caps_print_heap_info(MALLOC_CAP_SPIRAM);
return ESP_OK;
}
esp_err_t set_jpeg_quality(int qp) {
if(qp == 0)
qp = s_state->config.jpeg_quality;
else
s_state->config.jpeg_quality = qp;
ESP_LOGI(TAG, "jpeg quality --> %u", qp);
int compression_ratio_bound;
if (qp <= 10) {
compression_ratio_bound = 5;
} else if (qp <= 30) {
compression_ratio_bound = 10;
} else {
compression_ratio_bound = 20;
}
(*s_state->sensor.set_quality)(&s_state->sensor, qp);
size_t equiv_line_count = s_state->height / compression_ratio_bound;
s_state->fb_size = s_state->width * equiv_line_count * 2 /* bpp */;
return ESP_OK;
}
esp_err_t set_frame_size(camera_framesize_t cam_frame_size) {
framesize_t frame_size = (framesize_t) cam_frame_size;
s_state->width = resolution[frame_size][0];
s_state->height = resolution[frame_size][1];
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
ESP_LOGE(TAG, "Failed to set frame size --> %u", frame_size);
esp_err_t err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
return err;
}
ESP_LOGI(TAG, "set frame size --> %u", frame_size);
return ESP_OK;
}
esp_err_t set_pixel_format(camera_pixelformat_t format, int qp) {
if(s_state->fb != NULL) {
free(s_state->fb);
s_state->fb = NULL;
heap_caps_print_heap_info(MALLOC_CAP_SPIRAM);
}
esp_err_t err;
pixformat_t pix_format = (pixformat_t)format;
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
if (pix_format == PIXFORMAT_GRAYSCALE) {
s_state->fb_size = s_state->width * s_state->height * 3;
if (is_hs_mode()) {
s_state->sampling_mode = SM_0A0B_0B0C;
s_state->dma_filter = &dma_filter_grayscale_highspeed;
} else {
s_state->sampling_mode = SM_0A0B_0C0D;
s_state->dma_filter = &dma_filter_grayscale;
}
s_state->in_bytes_per_pixel = 2; // camera sends YUYV
s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8
} else if (pix_format == PIXFORMAT_RGB565) {
s_state->fb_size = s_state->width * s_state->height * 3;
if (is_hs_mode()) {
s_state->sampling_mode = SM_0A0B_0B0C;
} else {
s_state->sampling_mode = SM_0A00_0B00;
}
s_state->in_bytes_per_pixel = 2; // camera sends RGB565 (2 bytes)
s_state->fb_bytes_per_pixel = 3; // frame buffer stores RGB888
s_state->dma_filter = &dma_filter_rgb565;
ESP_LOGW(TAG, " set RGB565 size --> %u", s_state->fb_size);
} else if (pix_format == PIXFORMAT_JPEG) {
if (s_state->sensor.id.PID != OV2640_PID) {
ESP_LOGE(TAG, "JPEG format is only supported for ov2640");
err = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
set_jpeg_quality(qp);
s_state->dma_filter = &dma_filter_jpeg;
if (is_hs_mode()) {
s_state->sampling_mode = SM_0A0B_0B0C;
} else {
s_state->sampling_mode = SM_0A00_0B00;
}
s_state->in_bytes_per_pixel = 2;
s_state->fb_bytes_per_pixel = 2;
ESP_LOGW(TAG, "set JPEG frame size --> %u", s_state->fb_size);
} else {
ESP_LOGE(TAG, "Requested format is not supported");
err = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
allocate_fb();
if(s_state->dma_filter_task != NULL){
vTaskDelete(s_state->dma_filter_task);
vTaskDelay(10/portTICK_PERIOD_MS);
if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10,
&s_state->dma_filter_task, 1)) {
ESP_LOGE(TAG, "Failed to create DMA filter task");
err = ESP_ERR_NO_MEM;
goto fail;
}
}
return ESP_OK;
fail:
return err;
}
void reinit_dma() {
dma_desc_deinit();
dma_desc_init();
}
esp_err_t camera_init(camera_config_t* config) {
if (!s_state) {
return ESP_ERR_INVALID_STATE;
}
if (s_state->sensor.id.PID == 0) {
return ESP_ERR_CAMERA_NOT_SUPPORTED;
}
// memcpy(&s_state->config, config, sizeof(*config));
s_state->config = *config;
esp_err_t err = ESP_OK;
framesize_t frame_size = (framesize_t) config->frame_size;
pixformat_t pix_format = (pixformat_t) config->pixel_format;
ESP_LOGD(TAG, "Setting frame size to %dx%d", s_state->width, s_state->height);
if (set_frame_size(config->frame_size) != 0) {
ESP_LOGE(TAG, "Failed to set frame size");
err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
goto fail;
}
// s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
#if ENABLE_TEST_PATTERN
/* Test pattern may get handy
if you are unable to get the live image right.
Once test pattern is enable, sensor will output
vertical shaded bars instead of live image.
*/
s_state->sensor.set_colorbar(&s_state->sensor, 1);
ESP_LOGD(TAG, "Test pattern enabled");
#endif
err = set_pixel_format(config->pixel_format, 0);
if(err) {
ESP_LOGE(TAG, "Requested format is not supported");
err = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
ESP_LOGD(TAG,
"in_bpp: %d, fb_bpp: %d, fb_size: %d, mode: %d, width: %d height: %d",
s_state->in_bytes_per_pixel, s_state->fb_bytes_per_pixel,
s_state->fb_size, s_state->sampling_mode, s_state->width,
s_state->height);
ESP_LOGW(TAG, "Initializing I2S and DMA");
i2s_init();
err = dma_desc_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize I2S and DMA");
goto fail;
}
s_state->data_ready = xQueueCreate(16, sizeof(size_t));
s_state->frame_ready = xSemaphoreCreateBinary();
if (s_state->data_ready == NULL || s_state->frame_ready == NULL) {
ESP_LOGE(TAG, "Failed to create semaphores");
err = ESP_ERR_NO_MEM;
goto fail;
}
if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10,
&s_state->dma_filter_task, 1)) {
ESP_LOGE(TAG, "Failed to create DMA filter task");
err = ESP_ERR_NO_MEM;
goto fail;
}
ESP_LOGD(TAG, "Initializing GPIO interrupts");
gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_NEGEDGE); // GPIO ISR
gpio_intr_enable(s_state->config.pin_vsync);
err = gpio_isr_register(&gpio_isr, (void*) TAG,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM,
&s_state->vsync_intr_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "gpio_isr_register failed (%x)", err);
goto fail;
}
// skip at least one frame after changing camera settings
while (gpio_get_level(s_state->config.pin_vsync) == 0) {
;
}
while (gpio_get_level(s_state->config.pin_vsync) != 0) {
;
}
while (gpio_get_level(s_state->config.pin_vsync) == 0) {
;
}
s_state->frame_count = 0;
ESP_LOGD(TAG, "Init done");
return ESP_OK;
fail: camera_deinit();
return err;
}
Users browsing this forum: ChatGPT-User, Qwantbot, Semrush [Bot] and 4 guests