カメラ

8225N V2.0と記載されたカメラを接続しました。OV2640(?)で注文したものだと思いますが、ネットではまがい物も多く出回ってるので確かなことは分かりません。カメラの素性がよくわからないのですが、その辺は勘弁してください。

プログラム

『WebSocket通信 ローカルサーバー vs ESP32』で作成したプログラムをベースにカメラを追加してます。追加したのは『setup_camera』関数と『ShowCamera』関数の2つです

『setup_camera』関数は、Arduinoのライブラリやグーグルから頂きものなので詳しくは分かりませんが、なんとなくの理解で使ってます。config.xclk_freq_hz に関しては20Mhzでは動作しきれない時があります。8Mhz程度がとりあえずちょうどいいかもしれません

『ShowCamera』で Arudino IDE のシリアルモニタへ出力してます。RGB565のフォーマットは以下の通りなので、RGBはビット操作で分離し簡易的に2値化してます

#include <ArduinoWebsockets.h>
#include <WiFi.h>
#include "esp_camera.h"											// 『C:\・・・\Arduino\hardware』のもっと深い所に定義されている
#include "System.h"

using namespace websockets;

void setup_camera();
void ShowCamera();

/****************************************************************************************************/
/*                                                                                                  */
/*                                             定義関係                                             */
/*                                                                                                  */
/****************************************************************************************************/
/*************************/
/*      通信系定義       */
/*************************/
WebsocketsClient Client;
const char*      Ssid       = C_STR_SSID;						// SSID
const char*      Password   = C_STR_PASSWORD;					// Password
const char*      ServerHost = C_STR_HOST;						// server adress
const uint16_t   ServerPort = C_DAT_PORT;						// server port

/****************************************************************************************************/
/*                                                                                                  */
/*                                           コールバックイベント                                           */
/*                                                                                                  */
/****************************************************************************************************/
/*************************/
/*    接続関係のイベント    */
/*************************/
// event:受信イベント
// data :受信データ
void onEventsCallback( WebsocketsEvent event, String data ) 
{
	switch( event )
	{
		case WebsocketsEvent::ConnectionOpened:
			Serial.println( "Event is opened" );
			break;
		case WebsocketsEvent::ConnectionClosed:
			Serial.println( "Event is closed" );
			break;
		case WebsocketsEvent::GotPing:
			break;
		case WebsocketsEvent::GotPong:
			break;
	}
}
/*************************/
/*    メッセージ受信イベント    */
/*************************/
// message:受信イベント
void onMessagesCallback( WebsocketsMessage message )
{
	char*  data   = (char*)message.c_str();					// uint_8 とすると プラス演算で文字列の変更されてしまうので char とする事
	int    length = message.data().length();					// 受信データ長

	/*************************/
	/*     デバッグ用表示     */
	/*************************/
	if( message.isBinary() ) Serial.println( "Binary" );
	if( message.isText()   ) Serial.println( "Text"   );
	Serial.print(  "length " );
	Serial.println( length   );
	for(int index = 0; index < length; index++ )
	{
		Serial.print( data[ index ] );
	}
	Serial.println( "" );
}
/****************************************************************************************************/
/*                                                                                                  */
/*                                             関数定義                                             */
/*                                                                                                  */
/****************************************************************************************************/
/*************************/
/*      起動セットアップ      */
/*************************/
void setup()
{
	Serial.begin( 115200 );

	/*************************/
	/*       Wifi接続        */
	/*************************/
	WiFi.begin( Ssid, Password );

	for( int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++ )// ウエイト
	{
		Serial.print( "." );
		delay( 1000 );
	}
	if( WiFi.status() != WL_CONNECTED )							// ウエイト消化で接続チェック
	{
		Serial.println( "No Wifi!" );							// 未接続
		return;
	}
	Serial.println( "Connected to Wifi, Connecting to server." );// Wifi接続完了

	/*************************/
	/*    Websockets接続     */
	/*************************/
	Client.onMessage( onMessagesCallback );					// メッセージ受信イベントセット
	Client.onEvent( onEventsCallback );							// 受信イベントセット

	bool connected = Client.connect( ServerHost, ServerPort, "/" );
	if( connected )												// 接続チェック
	{
		Serial.println( "Connected!"   );
		Client.send(    "Hello Server" );						// 接続完了
	}
	else
	{
		Serial.println( "Not Connected!" );						// 未接続
	}
	/*************************/
	/*     カメラのセットアップ      */
	/*************************/
	setup_camera();
}
/*************************/
/*     カメラのセットアップ      */
/*************************/
// 参考
// Arduino の『CameraWebServer』ライブラリのexampleプログラム
// https://qiita.com/moritalous/items/311fb0b881bc1198ed5f
// https://www.mgo-tec.com/blog-entry-m5camera-serial-monitor.html#title03
void setup_camera()
{
	camera_config_t config;

	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_sscb_sda = SIOD_GPIO_NUM;
	config.pin_sscb_scl = SIOC_GPIO_NUM;
	config.pin_pwdn     = PWDN_GPIO_NUM;
	config.pin_reset    = RESET_GPIO_NUM;
	config.xclk_freq_hz = 8000000;
//	config.xclk_freq_hz = 20000000;
	config.pixel_format = PIXFORMAT_RGB565;
	config.frame_size   = FRAMESIZE_QQVGA;
	config.jpeg_quality = 12;
	config.fb_count     = 1;

	esp_err_t err = esp_camera_init( &config );
	if( err != ESP_OK )
	{
		Serial.printf( "Camera init failed with error 0x%x", err );
		return;
	}

// 必要に応じて下記コメントアウト部分を検討する
//	sensor_t* s = esp_camera_sensor_get();
//	if( s->id.PID == OV3660_PID )								// 最初は上下反転しており、色が少し飽和している
//	{
//		s->set_vflip(      s, 1 );								// 上下を戻す
//		s->set_brightness( s,  1 );								// 少しだけ明るくする
//		s->set_saturation( s, -2 );								// 彩度を下げる
//	}
//	s->set_framesize( s, FRAMESIZE_QVGA );						// 初期フレームレートを高くするには、フレームサイズをダウンさせる

//#if defined( CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM )
//	s->set_vflip(   s, 1 );
//	s->set_hmirror( s, 1 );
//#endif
}
/*************************/
/*       無限ループ        */
/*************************/
void loop()
{
	// WebSocketメッセージ受信待ち
	if( Client.available() )
	{
		Client.poll();
	}

	ShowCamera();

	delay( 500 );
}
/*************************/
/*  画像のシリアルコンソール出力  */
/*************************/
void ShowCamera()
{
	camera_fb_t* fb = esp_camera_fb_get();

	if( !fb )
	{
		Serial.println( "Camera capture failed" );
		return;
	}

	int length = fb->width * 2;
	for( int y = 0; y < fb->height; y++ )
	{
		// fb->width はピクセル
		// PIXFORMAT_RGB565 は1ピクセル2バイト
		for( int x = 0; x < length; x += 2 )
		{
			uint8_t upper = *( fb->buf + y * length + x     );
			uint8_t lower = *( fb->buf + y * length + x + 1 );
			uint8_t red   = ( upper >> 3 ) & 0x1f;
			uint8_t green = (( upper << 3 ) & 0x38 ) | (( lower >> 5 ) & 0x07 );
			uint8_t blue  = lower & 0x1f;

			if( red < 16 && green < 32 && blue < 16 )
			{
				Serial.print( '#' );
			}
			else
			{
				Serial.print( ' ' );
			}
		}
		Serial.println();
	}
	Serial.println( "------------------------------------------------------------" );
	esp_camera_fb_return( fb );
}

『setup_camera』関数内の定義は以下の通り

********* esp_camera.h での定義 *********
#define PWDN_GPIO_NUM		-1
#define RESET_GPIO_NUM		-1
#define XCLK_GPIO_NUM		21
#define SIOD_GPIO_NUM		26
#define SIOC_GPIO_NUM		27

#define Y9_GPIO_NUM			35
#define Y8_GPIO_NUM			34
#define Y7_GPIO_NUM			39
#define Y6_GPIO_NUM			36
#define Y5_GPIO_NUM			19
#define Y4_GPIO_NUM			18
#define Y3_GPIO_NUM			5
#define Y2_GPIO_NUM			4
#define VSYNC_GPIO_NUM		25
#define HREF_GPIO_NUM		23
#define PCLK_GPIO_NUM		22

実行結果

手書き文字をカメラに読ませて、シリアルモニタに表示した結果です