Serial Camera Module

Ooops forgot to include the link:

This one:

http://www.tinyclr.com/forum/topic?id=9085

Maybe this link can help you:

http://www.tinyclr.com/forum/topic?id=9085&page=1#msg90982

Something strange with SerCam L2
It very Slow (more even USB_Camera)

Why?

i try realize requred Driver from zero point (http://www.linksprite.com/upload/file/1333187137.pdf)

And no difference (small speed up)


	public partial class Program
	{
		SerCam camera;
		GT.Timer timer;

		void ProgramStarted()
		{
			Debug.Print( "Program Started" );

			camera = new SerCam( 4 );
			camera.PictureCaptured += new SerCam.PictureCapturedEventHandler( camera_PictureCaptured );

			//camera.BaudRate = SerCam.BaudRateEnum.Rate_115200;
			camera.ImageSize = ImageSizeEnum.QVGA_320x240;
			//camera.Compression = 0xFF;
			camera.Setup();

			timer = new GT.Timer( 100, GT.Timer.BehaviorType.RunContinuously );
			timer.Tick += new GT.Timer.TickEventHandler( timer_Tick );
			timer.Start();
		}

		void timer_Tick( GT.Timer timer )
		{
			if ( camera.StartTakePicture() )
			{
				Thread.Sleep( 500 );
			}
		}

		void camera_PictureCaptured( SerCam sender, Bitmap picture )
		{
		    display.SimpleGraphics.DisplayImage( picture, 0, 0 );
		}
	}



	//I couldnt get it to work higher than 614400.
	//I worked backwards from the published baud rate values and came up with this equation:
	//((27E6 / baudrate) * 16 ) – 256
	//Here are some undocumented values that I have tested successfully:

	//uint8_t cmd_slow_baud_230400[] = { 0x56, 0x00, 0x24, 0x03, 0x01, 0x06, 0x53 };  
	//uint8_t cmd_slow_baud_307200[] = { 0x56, 0x00, 0x24, 0x03, 0x01, 0x04, 0x7e };  
	//uint8_t cmd_slow_baud_460800[] = { 0x56, 0x00, 0x24, 0x03, 0x01, 0x02, 0xa9 };  
	////614400 works but a little flaky  
	//uint8_t cmd_slow_baud_614400[] = { 0x56, 0x00, 0x24, 0x03, 0x01, 0x01, 0xbf }; 

	public enum BaudRateEnum : ushort
	{
		Rate_9600 = 0xC8AE, // 0xAE, 0xC8
		Rate_19200 = 0xE456, // 0x56, 0xE4
		Rate_38400 = 0xF22A, // 0x2A, 0xF2
		Rate_57600 = 0xC41C, // 0x1C, 0xC4
		Rate_115200 = 0xA60D, // 0x0D, 0xA6
		Rate_230400 = 0x5306, // 0x06, 0x53
		Rate_307200 = 0x7E04, // 0x04, 0x7E
		Rate_460800 = 0xA902, // 0x02, 0xA9
	}

	public enum ImageSizeEnum : byte
	{
		VGA_640x640 = 0x00,
		QVGA_320x240 = 0x11,
		QQVGA_160x120 = 0x22,
	}



	public class SerCam: Module
	{
		private Serial _SerialPort;

		private static byte[] _CMD_Reset = new byte[] { 0x56, 0x00, 0x26, 0x00 };
		private static byte[] _CMR_Reset = new byte[] { 0x76, 0x00, 0x26, 0x00, 0x00 };

		// last byte (CMD/CMR) = CompressionRatio (0x36 default) (0x00-0xFF)
		private static byte[] _CMD_Compression = new byte[] { 0x56, 0x00, 0x31, 0x05, 0x01, 0x01, 0x12, 0x04, 0x36 };
		private static byte[] _CMR_Compression = new byte[] { 0x76, 0x00, 0x31, 0x00, 0x00 };

		// last byte (CMD) = ImageSize (0x00:640x480, 0x11:320x240, 0x22:160x120)
		private static byte[] _CMD_ImageSize = new byte[] { 0x56, 0x00, 0x31, 0x05, 0x04, 0x01, 0x00, 0x19, 0x11 };
		private static byte[] _CMR_ImageSize = new byte[] { 0x76, 0x00, 0x31, 0x00, 0x00 };

		// last 2 bytes (CMD) = ChangeBaud (0x0DA6 - 115200)
		private static byte[] _CMD_Baud = new byte[] { 0x56, 0x00, 0x24, 0x03, 0x01, 0x0D, 0xA6 };
		private static byte[] _CMR_Baud = new byte[] { 0x76, 0x00, 0x24, 0x00, 0x00 };

		// "Init end\r\n"
		private byte[] _InitEnd = new byte[] { 0x49, 0x6E, 0x69, 0x74, 0x20, 0x65, 0x6E, 0x64, 0x0D, 0x0A };

		// StartTakePicture
		private static byte[] _CMD_StartTakePicture = new byte[] { 0x56, 0x00, 0x36, 0x01, 0x00 };
		private static byte[] _CMR_StartTakePicture = new byte[] { 0x76, 0x00, 0x36, 0x00, 0x00 };

		// Last byte (CMR) = Length in bytes of FileSize (need Read(length))
		private static byte[] _CMD_ReadFileSize = new byte[] { 0x56, 0x00, 0x34, 0x01, 0x00 };
		private static byte[] _CMR_ReadFileSize = new byte[] { 0x76, 0x00, 0x34, 0x00, 0x04 };

		// StopTakePicture
		private static byte[] _CMD_StopTakePicture = new byte[] { 0x56, 0x00, 0x36, 0x01, 0x03 };
		private static byte[] _CMR_StopTakePicture = new byte[] { 0x76, 0x00, 0x36, 0x00, 0x00 };

		// CMD start read
		// Interval Time = 0x0BB8 * 0.01ms = 3000 (3ms)
		private static byte[] _CMD_ReadData = new byte[] 
		{ 
			0x56, 0x00, 0x32, 0x0C, 0x00, 0x0A, 
			0x00, 0x00, 0x00, 0x00, // last 2 = MH ML (Starting address)
			0x00, 0x00, 0x00, 0x00, // last 2 = KH KL (Length of JPEG file)
			0xB8, 0x0B //last 2 = XX XX (interval) (0x00 0x0A recommended)
		};
		private static byte[] _CMR_ReadData = new byte[] { 0x76, 0x00, 0x32, 0x00, 0x00 };

		#region Accessors 

		/// <summary>
		/// Resolution<para/>
		/// !!! Need Setup for Apply
		/// </summary>
		public ImageSizeEnum ImageSize
		{
			get { return ( ImageSizeEnum )_CMD_ImageSize[ 8 ]; }
			set { _CMD_ImageSize[ 8 ] = ( byte )value; }
		}

		/// <summary>
		/// Jpeg Compression 0-255<para/>
		/// !!! Need Setup for Apply
		/// </summary>
		public byte Compression
		{
			get { return _CMD_Compression[ 8 ]; }
			set { _CMD_Compression[ 8 ] = value; }
		}

		///// <summary>
		///// Resolution<para/>
		///// !!! Need Setup for Apply
		///// </summary>
		//public BaudRateEnum BaudRate
		//{
		//    get { return ( BaudRateEnum )Utility.ExtractValueFromArray( _CMD_Baud, 5, 2 ); }
		//    set { Utility.InsertValueIntoArray( _CMD_Baud, 5, 2, ( uint )value ); }
		//}

		#endregion

		public SerCam( int socketNum )
		{
			var socket = Socket.GetSocket( socketNum, true, this, null );
			socket.EnsureTypeIsSupported( 'U', this );

			// Defaults
			this.ImageSize = ImageSizeEnum.VGA_640x640;
			this.Compression = 0x36;
			//this.BaudRate = BaudRateEnum.Rate_115200;

			_SerialPort = new Serial( socket, 115200, Serial.SerialParity.None, Serial.SerialStopBits.One, 8, Serial.HardwareFlowControl.NotRequired, this );
			_SerialPort.ReadTimeout = 50000;
			_SerialPort.WriteTimeout = 50000;
			_SerialPort.Open();

			Setup();
		}

		#region Setup

		public void Setup()
		{
			Setup_CleanBuffers();

			Setup_Reset();

			Setup_Resolution();
			Setup_Compression();
			Setup_BaudRate();

			_SerialPort.Close();
			_SerialPort.Open();

			Setup_Reset();

			Debug.Print( "SerCam.Setup done" );
		}

		private void Setup_Reset()
		{
			// Initializing stages:
			// 1) send Reset command
			// get Reset reply (optional)
			// 2) wait for ( InitEnd )

			_SerialPort.Write( _CMD_Reset, 0, _CMD_Reset.Length );
			WaitForReply( _CMR_Reset );

			// Last 10 is _InitEnd
			var buffer = new byte[ 80 ];
			var cnt = 0;
			var done = false;

			while ( !done ) // this crap =)
			{
				Thread.Sleep( 10 );
				cnt += ReadBytes( buffer, cnt );

				// Check last 10 bytes ("Init end")
				if ( cnt > 10 ) // _InitEnd_Reply.Length
				{
					var offset_start = cnt - 10; // _InitEnd_Reply.Length

					done = AreEquals( buffer, offset_start, _InitEnd, 0, 10 );
				}
			}

			Debug.Print( "SerCam.Setup_Reinit done" );
		}

		/// <summary>
		/// Setup Resolution
		/// </summary>
		private void Setup_Resolution()
		{
			_SerialPort.Write( _CMD_ImageSize, 0, _CMD_ImageSize.Length );
			WaitForReply( _CMR_ImageSize );

			Debug.Print( "SerCam.Setup_Resolution done" );
		}
		/// <summary>
		/// Setup Compression
		/// </summary>
		private void Setup_Compression()
		{
			_SerialPort.Write( _CMD_Compression, 0, _CMD_Compression.Length );
			WaitForReply( _CMR_Compression );

			Debug.Print( "SerCam.Setup_Compression done" );
		}

		/// <summary>
		/// Setup Current Resolution
		/// </summary>
		private void Setup_BaudRate()
		{
			_SerialPort.Write( _CMD_Baud, 0, _CMD_Baud.Length );
			WaitForReply( _CMR_Baud );

			Debug.Print( "SerCam.Setup_BaudRate done" );
		}

		/// <summary>
		/// Setup clear In/Out buffers
		/// </summary>
		private void Setup_CleanBuffers()
		{
			_SerialPort.DiscardInBuffer();
			_SerialPort.DiscardOutBuffer();

			Debug.Print( "SerCam.Setup_CleanBuffers done" );
		}

		#endregion

		Thread th;

		public bool StartTakePicture()
		{
			if ( th == null )
			{
				th = new Thread( new ThreadStart( TakePicture_Internal ) );
				th.Start();
				return true;
			}
			return false;
		}

		private static int _SyncInterval = 30;

		private void TakePicture_Internal()
		{
			//Setup_Reset();
			Start_TakePicture();

			var fileSize = Start_ReadFileSize();

			int offset = 0;
			int size = fileSize[ 0 ] << 24 | fileSize[ 1 ] << 16 | fileSize[ 2 ] << 8 | fileSize[ 3 ];

			Debug.Print( "SerCam.ReadFileSize: " + size );

			if ( size > 0 )
			{
				// Set Interval = 3ms (0x0B8B * 0.01 = 30)
				_CMD_ReadData[ 14 ] = 0xB8;
				_CMD_ReadData[ 15 ] = 0x0B;

				var buffer = new byte[ size ];

				ReadChunk( buffer, ref offset, size );

				Thread.Sleep( _SyncInterval );

				WaitForReply( _CMR_ReadData );

				OnPictureCapturedEvent( this, new Bitmap( buffer, Bitmap.BitmapImageType.Jpeg ) );
			}

			Stop_TakePicture();
			
			th = null;
		}

		/// <summary>
		/// Start Taking Pictures
		/// </summary>
		private void Start_TakePicture()
		{
			_SerialPort.Write( _CMD_StartTakePicture, 0, _CMD_StartTakePicture.Length );
			WaitForReply( _CMR_StartTakePicture );

			Debug.Print( "SerCam.Start_TakePicture done" );
		}

		/// <summary>
		/// Stop Taking Pictures
		/// </summary>
		private void Stop_TakePicture()
		{
			_SerialPort.Write( _CMD_StopTakePicture, 0, _CMD_StopTakePicture.Length );
			WaitForReply( _CMR_StopTakePicture );

			Debug.Print( "SerCam.Stop_TakePicture done" );
		}

		/// <summary>
		/// Start Taking Pictures
		/// </summary>
		private byte[] Start_ReadFileSize()
		{
			_SerialPort.Write( _CMD_ReadFileSize, 0, _CMD_ReadFileSize.Length );
			WaitForReply( _CMR_ReadFileSize );
			// and 4 bytes of Data
			var result = WaitForReply( 4 );

			Debug.Print( "SerCam.Read_TakePicture done" );
			return result;
		}

		private void ReadChunk( byte[] buffer, ref int offset, int length )
		{
			// Set Address
			_CMD_ReadData[ 6 ] = ( byte )( offset >> 24 );
			_CMD_ReadData[ 7 ] = ( byte )( offset >> 16 );
			_CMD_ReadData[ 8 ] = ( byte )( offset >> 8 );
			_CMD_ReadData[ 9 ] = ( byte )offset;

			// Set ShunkSize
			_CMD_ReadData[ 10 ] = ( byte )( length >> 24 );
			_CMD_ReadData[ 11 ] = ( byte )( length >> 16 );
			_CMD_ReadData[ 12 ] = ( byte )( length >> 8 );
			_CMD_ReadData[ 13 ] = ( byte )length;

			// Request data
			_SerialPort.Write( _CMD_ReadData, 0, _CMD_ReadData.Length );
			// OK Reply
			WaitForReply( _CMR_ReadData );

			Thread.Sleep( _SyncInterval );

			ReadBytes( buffer, offset, length );
			offset += length;
		}


		/// <summary>
		/// Cycle of read Images
		/// </summary>
		private byte[] Loop_ReadImage( int fileSize, int delayMS )
		{
			_SerialPort.Write( _CMD_ReadData, 0, _CMD_ReadData.Length );

			WaitForReply( _CMR_ReadData );
			
			if ( delayMS != 0 )
				Thread.Sleep( delayMS );

			var result = WaitForReply( fileSize );

			if ( result != null )
			{
				if ( delayMS != 0 )
					Thread.Sleep( delayMS );

				WaitForReply( _CMR_ReadData );

				Debug.Print( "SerCam.Loop_ReadImage done" );
			}
			else
			{
				Debug.Print( "SerCam.Loop_ReadImage leak" );
			}
			return result;
		}

		#region Internal Stuff

		private byte[] WaitForReply( int length )
		{
			var buffer = new byte[ length ];
			var cnt = 0;
			var leak = 0;

			while ( leak < length )
			{
				leak++;
				cnt += ReadBytes( buffer, cnt );
				if ( cnt == length )
				{
					return buffer;
				}
			}
			return null;
			//throw new Exception( "Leak" );
		}

		private void WaitForReply( byte[] reply )
		{
			var buffer = new byte[ reply.Length ];
			var cnt = 0;
			var done = false;
			var leak = 10000;

			while ( !done && leak > 0 )
			{
				cnt += ReadBytes( buffer, cnt );

				if ( cnt == reply.Length )
				{
					done = AreEquals( buffer, 0, reply, 0, reply.Length );
				}
				// else can be leak
				leak--;
			}
		}

		private void ReadBytes( byte[] data, int offset, int count )
		{
			_SerialPort.Read( data, offset, count );
		}

		private int ReadBytes( byte[] buff, int offset )
		{
			int byteread = _SerialPort.BytesToRead;
			if ( byteread + offset > buff.Length )
			{
				byteread = buff.Length - offset;
			}
			if ( byteread > 0 )
			{
				// integer times of 8
				var integerTimes = byteread / 8;
				if ( integerTimes > 0 )
				{
					byteread = integerTimes * 8;
				}
				_SerialPort.Read( buff, offset, byteread );
			}
			return byteread;
		}

		private bool AreEquals( byte[] buffer, int offset, byte[] buffer2, int offset2, int length )
		{
			if ( offset + length > buffer.Length )
				return false;

			if ( offset2 + length > buffer2.Length )
				return false;

			for ( int i = 0; i < length; i++ )
			{
				if ( buffer[ offset + i ] != buffer2[ offset2 + i ] )
				{
					return false;
				}
			}
			return true;
		}

		#endregion

		#region Events
		/// <summary>
		/// Represents the delegate that is used for the <see cref="PictureCaptured"/> event.
		/// </summary>
		/// <param name="sender">The <see cref="SerCam"/> object that raised the event.</param>
		/// <param name="picture">A <see cref="T:Gadgeteer.Picture"/> containing the captured image.</param>
		public delegate void PictureCapturedEventHandler( SerCam sender, Bitmap picture );

		/// <summary>
		/// Event raised when the <see cref="Camera"/> has completed an image capture.
		/// </summary>
		/// <remarks>
		/// Handle the <see cref="PictureCaptured"/> event to process image data
		/// after you call the <see cref="TakePicture"/> method. These methods 
		/// process the image data from the camera asynchronously, 
		/// and raise this event when the processing is complete.
		/// </remarks>
		public event PictureCapturedEventHandler PictureCaptured;

		private PictureCapturedEventHandler OnPictureCaptured;

		/// <summary>
		/// Raises the <see cref="PictureCaptured"/> event.
		/// </summary>
		/// <param name="sender">The <see cref="SerCam"/> that raised the event.</param>
		/// <param name="picture">A <see cref="T:Gadgeteer.Picture"/> containing the captured image.</param>
		protected virtual void OnPictureCapturedEvent( SerCam sender, Bitmap picture )
		{
			if ( OnPictureCaptured == null )
				OnPictureCaptured = new PictureCapturedEventHandler( OnPictureCapturedEvent );
			if ( Program.CheckAndInvoke( PictureCaptured, OnPictureCaptured, sender, picture ) )
			{
				PictureCaptured( sender, picture );
			}
		}
		#endregion
	}

Anyone have more than 1 fps with SerCam?

We added info to the product page. The camera will give you about 0.5FPS.

@ Volhv - we played with 115200 bps speed:

we tested it with the following extras:
-display it on an SPI 320x240 TFT as well (it means extra processing time for native bitmap converting, sending to SPI TFT, etc): but this extra takes only 60-70 millisec / picture

So, the results (FEZ Hydra, .NET 4.2 QFE2):

160*120 SIZE_QQVGA=0x22=34dec
CompressionRatio: CAM and TFT, mSec: JPEG fileSize:
0 1715 16720
5 1660 16330
15 1100 10410
30 650 5700
60 407 3300
120 280 1984
255 270 1900

it means: the smallest size QQVGA (160x120 pixels), with a lowest quality jpeg (max. compression = 255): you should get apprx. 3 FPS.
But if you use 160x120 with higher quality (compression ratio = 15 only) you get apprx. 1 FPS with this smallest size QQVGA pictures as well.

Based on my opinion the main bottleneck is the UART max. speed for this serial cam (officially 115200 bps):
-just for a rough estimation / overview:
another say if you have a 2000 bytes (2k) JPEG it takes 300 ms to get it, but if you have 20 000 bytes (20k) JPEG it takes 2000 msec (2 second !!!) to get it.

So if you want to play with 320x240 or even 640x480 sizes with normal jpeg quality: to will get a horrible FPS values !!

This is the reason why we ask GHI to send more info about real datasheets of this product.
(what about higher UART speeds like 921600 bps, what about SPI based interfaces , etc ??)

Just an idea: http://www.electronics123.com/s.nl/it.A/id.3011/.f

"Note on frame rate: The SPI Versions transfer rate is based on the masters SPI Clock rate which I think can go upto about 24M Bits /Sec or 3MBytes /Sec which means given a file size of 48KB the interface can transfer more than 60 FPS. The limiting factor will be the capture rate of the sensor since the compression chip is capable of real-time compression. I think realistically you should be able to achieve at least 2FPS with the UART and 15fps with the SPI. "

@ Gus - That’s correct ! (for future : it’s should be better to put such an info on the product page in advance, not after a month :frowning:
e.g. if we should know it in advance we should never bought it (and worked with it with a week)

Apologies this info was missing but we never promised anything FPS either. To some users 5FPS is good, to others it is not good, same goes to 0.5 or 50. If you still you feel you were mislead, please contact GHI fro a full refund.

Can you introduce SPI cam better ? 8))

Monkey want SPI 60FPS 640x480 module !!!

The goal from this camera was:

  1. cheap
  2. works with open source
  3. works with low memory devices

We are now looking into a newer camera:

  1. not cheap
  2. high res
  3. high speed
  4. works with open source

Ooohhh exciting!!

Very strange
http://www.linksprite.com/product/showproduct.php?id=50&lang=en
http://www.linksprite.com/article/shownews.php?lang=en&id=56

Serial rate: up to 115200
Image size: 640480 320240 160120
Frame rate: [b]640
480 30fps[/b]

Something like this?

Let me guess, $9.99 including Gadgeteer socket? :smiley:

He did mention the research was open source
 anybody up for making a fempto-second camera gadgeteer module?

@ ransomhall - Just for your kind information: the exposure time and the frames/second are two different properties ! :slight_smile:
(e.g. exposure time: femtosecond, but only 1 frame per hour :).

It means you should make a quasi femtosec camera even with Serial L1 with a very high engineered illuminant (flash)
:slight_smile:

Anyway: I hate Serial L1/L2 or any UART camera with low speed (e.g. 115200 bps) interface.
My recommendation: never buy a ‘camera’ -even with JPEG compression- below 921600bps UART or preferably with direct SPI interface.

Have nice day !

I found this one http://www.si-cube.com/Products/cctvequipment/24/
C339 SPI Jpeg Camera Module
Key Features:
[ul]Small size 22 x 22 x 9mm[/ul]
[ul]Adjustable resolution, Max VGA[/ul]
[ul]5V operation[/ul]
[ul]Low Power consumption 90mA[/ul]
[ul]UART interface up to 115.2Kbps[/ul]
[ul]SPI interface up to 13.5Mbps.[/ul]
[ul]TTL output level[/ul]
[ul]PAL/NTSC is optional[/ul]

2@ Gus
Maybe this can help find way to use SPI Camera - upgrade Serial camera for more faster?

Cost: 20-23$ per device

in quantities of how many? Thousands? Where from ?
$75 for a single camera module without lens in the first place I looked, http://www.electronics123.com/s.nl/it.A/id.3011/.f

http://ru.aliexpress.com/item/SPI-high-speed-Serial-JPEG-Camera-Module/547254704.html (http://www.aliexpress.com/item/SPI-high-speed-Serial-JPEG-Camera-Module/547254704.html)

[ul]Minimum Order Quantity: 50 Piece/Pieces[/ul]
[ul]Supply Ability: 10000 Piece/Pieces per Month[/ul]

50 pieces/lot ~ 1170$/lot