Page 1 of 4

New ESP32-CAM module

Posted: Tue Oct 09, 2018 2:59 pm
by Deouss
I just ordered ESP32 Camera Development Board with Camera Module OV2640 2MP
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 ;)

Image

Re: New ESP32-CAM module

Posted: Tue Oct 09, 2018 10:51 pm
by chegewara

Re: New ESP32-CAM module

Posted: Wed Oct 10, 2018 11:41 am
by Deouss
Thanks for the example code for the exact model!
Will try as soon as I receive my module )

Re: New ESP32-CAM module

Posted: Fri Nov 02, 2018 7:51 pm
by chegewara
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.

Re: New ESP32-CAM module

Posted: Tue Nov 20, 2018 9:31 am
by denyeau
Hi, I also bought a copy. However, I'm new to ESP32. I have to connect this to the USB port of my computer somehow, right? How do I do that?

I have one of these that I had bought for ESP8266, would that be useful at all? https://www.aliexpress.com/item/USB-swi ... 75353.html

Re: New ESP32-CAM module

Posted: Wed Dec 05, 2018 12:29 pm
by stonemull
yeh you can use one of those to program it though you will need to mod one thing, you need 5v.
So need to connect up the 5v and gnd pin on the opposites corner of the camera module to 5v which is not available on the header of that device but you can find it on the regulator easily enough, then you need rx to the module U0T and tx to camera U0R, you can then open a terminal program and verify the camera is talking, close that, connect camera IO0 (between IO18 and ground, it looks like IO6 on my silkscreen) and hit reset and camera will be ready for an upload.

click on first link "spec" on this page for a bit of camera info https://m.seeedstudio.com/productDetail/3153

Re: New ESP32-CAM module

Posted: Fri Dec 07, 2018 9:41 am
by TheNitek
chegewara wrote:
Fri Nov 02, 2018 7:51 pm
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.
Is this code available somewhere?

Re: New ESP32-CAM module

Posted: Fri Dec 07, 2018 11:02 am
by chegewara
Sorry, its not yet. It was part of paid project, but i hope it can be open sourced soon.

Re: New ESP32-CAM module

Posted: Wed Jan 02, 2019 7:07 pm
by Deouss
OK. So I played a little with the camera module and was able to control capture data however I do not want to use http server as the video gui.
I am able to get full frame in bytes in JPEG format but it is apparently missing some jpeg header data or maybe something else.
Camera uses JPEG as default and when I switched to CAMERA_PF_RGB565 I get error:
E (6861) camera: Failed to allocate frame buffer
E (6861) camera_demo: Camera init failed with error 0x101

I will try to use other camera OV7725 and see if maybe can get BMP format.

Maybe someone knows how to add JPEG header to raw jpeg impage data bytes in c++?
I know the dimensions of the picture and size in bytes

Re: New ESP32-CAM module

Posted: Thu Jan 03, 2019 5:57 am
by chegewara
To change image format on the fly you have to perform full re-init camera. Its not clean code, but this is how i did it:

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;
}