esp32-camera: Corrupted image when I send it to a FTP server in a separate task

thoraz
Posts: 27
Joined: Tue Jan 28, 2020 4:02 pm

esp32-camera: Corrupted image when I send it to a FTP server in a separate task

Postby thoraz » Wed Oct 19, 2022 9:34 am

I want to send an image captured whit OV2640 to a FTP server when my system power-up. In my application I have some indipendent FreeRTOS task, so I created a task that manage the take & send of the picture. But when I receive the picture on the FTP server, it is corrupted: I can open it but seems that the picture buffer is not completely sent. I get something like this

Image

But if immediately after the capture I get the same picture buffer via serial on my pc it's all ok:

Image

I think that the issue is the memory management in FreeRTOS task and esp32-camera library: after the picture capture, I start the WiFi connection routine, that has vTaskDelay(), and soon after I start the FTP session.

Does anyone have any helpful guidance on why and how to fix it?

My camera setup is

Code: Select all

void Camera_Setup( void )
{
	// Turn-off the 'brownout detector'
	WRITE_PERI_REG( RTC_CNTL_BROWN_OUT_REG, 0 );
	camera_config_t config;
	esp_err_t err;
	
	pictureBuffer = NULL;
	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_sccb_sda = SIOD_GPIO_NUM;
	config.pin_sccb_scl = SIOC_GPIO_NUM;
	config.pin_pwdn = PWDN_GPIO_NUM;
	config.pin_reset = RESET_GPIO_NUM;
	config.xclk_freq_hz = 8000000;
	config.pixel_format = PIXFORMAT_JPEG;
	config.frame_size = FRAMESIZE_QVGA;
	config.jpeg_quality = 10;
	config.fb_location = CAMERA_FB_IN_PSRAM;
	config.fb_count = 1;
	config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;

	err = esp_camera_init( &config );

	if ( err != ESP_OK )
	{
		ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "Camera init failed with error 0x%x", err );
		esp_restart();
	}
	else
	{
		ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Camera init succeeded" );
	}
}
and the part of my application code

Code: Select all

#include <Arduino.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include "ArduinoJson.h"
#include <ESP32_FTPClient.h>
#include "esp_camera.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "time.h"

camera_fb_t *pictureBuffer;
ulong pictureTime;
char pictureName[ 128 ];
TaskHandle_t submissionTaskHandle;

extern nodeStatus_t nodeStatus;
extern wifiCredentials_t wifiCredentials;
extern ftpCredentials_t ftpCredentials;

static boolean Ftp_Send( void )
{
    uint16_t maxConnectionWaiting;
    ulong timeFromPicture;
    struct tm timeinfo;
    ESP32_FTPClient ftp( ftpCredentials.server, ftpCredentials.port, ftpCredentials.user, ftpCredentials.pass, 5000, 2 );

	maxConnectionWaiting = 0;
	ftp.OpenConnection();

	while ( ftp.isConnected() == false )
	{
		maxConnectionWaiting++;
		vTaskDelay( 1000 / portTICK_PERIOD_MS );

		if ( maxConnectionWaiting > FTP_CONNECTION_MAX_WAITING )
		{
			ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "FTP connection failed!" );
			return false;
		}
	}

	timeFromPicture = millis() - pictureTime;

	if ( !getLocalTime( &timeinfo ) )
	{
		ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "Failed to obtain time for the picture name. Using local time instead" );
		sprintf( pictureName, "%d.jpg", millis() );
	}
	else
	{
		timeinfo.tm_sec -= ( int )( timeFromPicture / 1000 );
		sprintf( pictureName, "%d_%02d_%02d_%02d_%02d_%02d_%s.jpg", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, nodeStatus.nodeId );
	}
	
	ftp.ChangeWorkDir( "images" );
	ftp.InitFile( "Type I" );
	ftp.NewFile( pictureName );
	ftp.WriteData( pictureBuffer->buf, pictureBuffer->len );
	ftp.CloseFile();
	ftp.CloseConnection();
	memset( pictureName, 0, sizeof( pictureName ) );
	return true;
}


static boolean Wifi_Connect( void )
{
    uint16_t maxConnectionTrials;
	uint16_t maxConnectionWaiting;

    ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Attempting WiFi connection to %s", wifiCredentials.ssid );
    
	maxConnectionTrials = 0;

	while ( maxConnectionTrials < WIFI_CONNECTION_MAX_TRIALS )
	{
		WiFi.begin( wifiCredentials.ssid, wifiCredentials.pass );
    	maxConnectionWaiting = 0;

		while ( WiFi.status() != WL_CONNECTED )
		{
			maxConnectionWaiting++; 
			vTaskDelay( 1000 / portTICK_PERIOD_MS );

			if ( maxConnectionWaiting > WIFI_CONNECTION_MAX_WAITING )
			{
				ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "WiFi connection failed!" );
				break;
			}
		}

		if (  WiFi.status() == WL_CONNECTED )
		{
			ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Connected! My IP is %s", WiFi.localIP().toString().c_str() );		
			return true;
		}
		else
		{
			maxConnectionTrials++;
			ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Try again in a few seconds..." );
			WiFi.disconnect();        
			vTaskDelay( 5000 / portTICK_PERIOD_MS );
		}
	}

	ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "Somethings gone wrong in attempting WiFi connection" );
	return false;
}


static void Get_Picture_Buffer( void )
{
	if ( pictureBuffer == NULL )
	{
		ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Capturing image..." );
		pictureBuffer = esp_camera_fb_get();
	}
	else
	{
		ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "Picture buffer is not empty" );
	}
}


static void Release_Picture_Buffer( void )
{
	if ( pictureBuffer != NULL )
	{
		esp_camera_fb_return( pictureBuffer );
		pictureBuffer = NULL;
		ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Camera buffer released" );
	}
	else
	{
		ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Camera buffer NULL, nothing to release" );
	}
}


static void Take_Send_Picture_Task( void *pvParameters )
{
	Get_Picture_Buffer();
	pictureTime = millis();

	if ( !pictureBuffer )
	{
		ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "Camera capture failed" );
	}
	else
	{
		ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Done! Size of the image %d bytes", pictureBuffer->len );

		if ( pictureBuffer->format != PIXFORMAT_JPEG )
		{
			ESP_LOGW( LOG_TAG_PICTURE_MANAGING, "Capture Error: Non-JPEG image returned by camera module" );
		}
		else
		{
			ESP_LOGI( LOG_TAG_PICTURE_MANAGING, "Capture OK: JPEG image returned by camera module" );

			if ( Wifi_Connect() == true )
			{
				Led_Status_Set( STATUS_LED_CONNECTED );		// This function updates the status led task
				Init_Time( TIMEZONE );                      // This function updates the local time
				
				if ( Ftp_Send() == true )
				{
					// Other code after FTP submission
				}
			}

			Release_Picture_Buffer();
		}
	}

	vTaskDelete( NULL );
}


void Start_Picture_Submission( void )
{
	submissionTaskHandle = NULL;
    xTaskCreate( Take_Send_Picture_Task, "Take_Send_Picture_Task", TASK_TAKE_SEND_PICTURE_DEPTH, NULL, TASK_TAKE_SEND_PICTURE_PRIORITY, &submissionTaskHandle );
}

thoraz
Posts: 27
Joined: Tue Jan 28, 2020 4:02 pm

Re: esp32-camera: Corrupted image when I send it to a FTP server in a separate task

Postby thoraz » Wed Oct 19, 2022 10:57 am

I've just tryed to implement the application without the freeRTOS, but the issue persists! So it seems confined in the use of WiFi or FTP libraries. :?

EDIT:
It seems that the corruption is after the WiFi connection. To get the picture via serial I use this routine that allows me to have a hex rappresentation of binary files:

Code: Select all

char buffer[3];
for ( int i = 0; i < pictureBuffer->len; i++ )
{
	sprintf( buffer, "%02x", pictureBuffer->buf[ i ] );
	Serial.print( buffer );
}
. I use it after the esp_camera_fb_get() and after the Wifi_Connect(). I got the following images

Image

and

Image

. If i look at the hex values of binary files I notice in some parts a 4-bit shift in the corrupted image:

the correct image

Image

and the corrupted image

Image

. A closer look:

Image

Image

thoraz
Posts: 27
Joined: Tue Jan 28, 2020 4:02 pm

Re: esp32-camera: Corrupted image when I send it to a FTP server in a separate task

Postby thoraz » Tue Oct 25, 2022 12:23 pm

I found that the issue is due to the PSRAM communication. It seems that the the SDx signals sometimes are very noisy, so I suppose it's not a firmware problem but an hardware one instead. I configured the OV2640 to allocate the buffer in DRAM rather than in PSRAM and now the pictures are good, even if at max resolution resolution 800x600. Just to be sure I want to show you the connections of my custom board so if anyone find some point to fix... . I used a PSRAM APS6404L-3SQR-SN with an ESP32-WROOM_32U (in the schematic is 32D but acctually is 32U), for the chip select -> IO16 and for the clock -> IO17.

Image

Image

wuyuanyi
Posts: 28
Joined: Wed Mar 09, 2022 11:54 am

Re: esp32-camera: Corrupted image when I send it to a FTP server in a separate task

Postby wuyuanyi » Thu Oct 27, 2022 6:16 pm

Hi thoraz,
Interesting findings. Can you also share your PCB layout?

thoraz
Posts: 27
Joined: Tue Jan 28, 2020 4:02 pm

Re: esp32-camera: Corrupted image when I send it to a FTP server in a separate task

Postby thoraz » Thu Nov 03, 2022 10:29 am

wuyuanyi wrote:
Thu Oct 27, 2022 6:16 pm
Hi thoraz,
Interesting findings. Can you also share your PCB layout?

Sure. This is the top

Image

and this is the bottom

Image

Who is online

Users browsing this forum: tonystuart and 89 guests