Main Site Documentation

Linksprite driver code


#1

Below is a class I made that reads the linksprite camera. Couldn’t find any .NET MF code so created this using linksprite docs and looking at arduino libraries.

http://www.fezzer.com/project/220/linksprite-camera-library/

Sample usage:

        public static void Main()
        {
            Debug.EnableGCMessages(false); 

            using (var camera = new LinkspriteCamera(new SerialPort("COM1", 38400, Parity.None, 8, StopBits.One)))
            {

                if(camera.Reset())
                    camera.GetPicture(ProcessChunk);

                camera.Stop();
            }
        }

        private static void ProcessChunk(byte[] bytes)
        {
            foreach (var byter in bytes)
                Debug.Print(byter.ToString());
        }

This just returns the bytes over the debug cable, you can do something with the bytes if you have an SD card or are going to transmit, otherwise just copy the bytes to the clippboard from the output window and use the below snippet to create the jpg file and look at it on your computer:

            var byteString = @ "<put bytes here>";
            var lines = byteString.Split(Environment.NewLine.ToCharArray());
            var bytes = new List<byte>();
            foreach (var line in lines.Where(x => (x ?? "").Trim() != ""))
            {
                byte num = Convert.ToByte(line);
                bytes.Add(num);
            }

            string file = @ "C:\temp.jpg";
            File.WriteAllBytes(file, bytes.ToArray());
            System.Diagnostics.Process.Start(file);


#2

Thanks for the code. I was just thinking of that after seeing cam on Sparkfun. Please put your code in “Code” block. Edit your post, hightlight the code, and hit the button with the 1’s and 0’s on it to surrowned it with code block markers. Should also post on Fezzer.com when your happy with final.


#3

This should go on FEZZer, for points and will help others see it, please.

Please “code tag” your code so it is redable…here is your code but with tags this time

        public static void Main()
        {
            Debug.EnableGCMessages(false); 

            using (var camera = new LinkspriteCamera(new SerialPort("COM1", 38400, Parity.None, 8, StopBits.One)))
            {

                if(camera.Reset())
                    camera.GetPicture(ProcessChunk);

                camera.Stop();
            }
        }

        private static void ProcessChunk(byte[] bytes)
        {
            foreach (var byter in bytes)
                Debug.Print(byter.ToString());
        }


This just returns the bytes over the debug cable, you can do something with the bytes if you have an SD card or are going to transmit, otherwise just copy the bytes to the clippboard from the output window and use the below snippet to create the jpg file and look at it on your computer:

            var byteString = @ "<put bytes here>";
            var lines = byteString.Split(Environment.NewLine.ToCharArray());
            var bytes = new List<byte>();
            foreach (var line in lines.Where(x => (x ?? "").Trim() != ""))
            {
                byte num = Convert.ToByte(line);
                bytes.Add(num);
            }

            string file = @ "C:\temp.jpg";
            File.WriteAllBytes(file, bytes.ToArray());
            System.Diagnostics.Process.Start(file);


using System;
using Microsoft.SPOT;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Collections;

namespace CameraController
{
    public class LinkspriteCamera : IDisposable
    {
        public LinkspriteCamera(SerialPort port)
        {
            this.port = port;

            port.ReadTimeout = 250; //so read call doesn't block forever
            port.Open();
        }

        public bool Reset()
        {
            bool sendAndLookFor = SendAndLookFor(RESET_COMMAND, RESET_OK_RESPONSE);

            //camera needs time after reset
            if (sendAndLookFor)
            {
                ReadAllRemaining();
                Thread.Sleep(3000);
            }

            return sendAndLookFor;
        }

        public bool SetPictureSize(byte[] sizeBytes)
        {
            bool sendAndLookFor = SendAndLookFor(sizeBytes, SET_SIZE_OK_RESPONSE);
            if(sendAndLookFor)
                ReadAllRemaining();

            return sendAndLookFor;
        }

        public bool Stop()
        {
            ReadAllRemaining();
            return SendAndLookFor(STOP_COMMAND, STOP_OK_RESPONSE);
        }

        public delegate void ActionBytes(byte[] chunk);
        public delegate void Complete();

        const int IN_BUFFER_SIZE = 512;
        byte[] InBuffer = new byte[IN_BUFFER_SIZE];

        public void GetPicture(ActionBytes bytesAction)
        {
            Send(SNAP_COMMAND);
            if (LookFor(SNAP_OK_RESPONSE))
            {
                Send(SIZE_COMMAND);
                if (LookFor(SIZE_OK_RESPONSE))
                {
                    //MSB, LSB
                    var sizeBytesLength = Read(2);
                    int fileSize = (InBuffer[0] << 8) | InBuffer[1];

                    int startAddress = 0;
                    int bytesRead = 0;

                    GET_CHUNK_COMMAND[12] = MSB(IN_BUFFER_SIZE);
                    GET_CHUNK_COMMAND[13] = LSB(IN_BUFFER_SIZE);

                    bool endReached = false;
                    while (!endReached)
                    {
                        GET_CHUNK_COMMAND[8] = MSB(startAddress);
                        GET_CHUNK_COMMAND[9] = LSB(startAddress);

                        Send(GET_CHUNK_COMMAND);
                        if (LookFor(GET_CHUNK_OK_RESPONSE))
                        {
                            int chunkLength = 0;
                            do
                            {
                                chunkLength = Read();

                                //ditch footer
                                Read(junkBuffer, GET_CHUNK_OK_RESPONSE.Length);

                                //publish byte data
                                if (chunkLength > 0)
                                {
                                    bytesRead += chunkLength;
                                    if (bytesRead >= fileSize)
                                    {
                                        endReached = true;

                                        chunkLength = FindEnd(chunkLength);
                                    }

                                    bytesAction(NewArray(chunkLength));
                                }

                                startAddress += chunkLength;

                            } while (!endReached && chunkLength > 0);
                        }
                    }
                }
            }
        }

        private byte[] NewArray(int chunkLength)
        {
            //make new array for bytes event so receiver can consume
            //in sep thread without it changing during processing
            var chunk = new byte[chunkLength];
            Array.Copy(InBuffer, chunk, chunkLength);
            return chunk;
        }

        private int FindEnd(int chunkLength)
        {
            if (chunkLength >= 2)
            {
                bool foundEnd = false;

                for (int i = chunkLength - 1; i >= 2; i--)
                {
                    if (InBuffer[i - 1] == 0xFF &&
                        InBuffer[i - 0] == 0xD9
                    )
                    {
                        chunkLength = i + 1; //include end marker in output
                        foundEnd = true;
                        break;
                    }
                }

                if (!foundEnd)
                    Debug.Print("Invalid JPG data");
            }
            return chunkLength;
        }

        private static byte LSB(int num)
        {
            return (byte)(num & 0xFF);
        }

        private static byte MSB(int num)
        {
            return (byte)(num >> 8);
        }

        private bool SendAndLookFor(byte[] command, byte[] lookFor)
        {
            Send(command);

            return LookFor(lookFor);
        }

        byte[] junkBuffer = new byte[IN_BUFFER_SIZE];
        private void ReadAllRemaining()
        {
            int readCount = 0;

            do
            {
                readCount = Read(junkBuffer, IN_BUFFER_SIZE);
            } while (readCount != 0);
        }

        private bool LookFor(byte[] expectedResponse)
        {
            var inSize = Read(expectedResponse.Length);
            if (AreEqual(expectedResponse, inSize))
                return true;

            return false;
        }

        private int Read()
        {
            return Read(IN_BUFFER_SIZE);
        }

        private int Read(int bytes)
        {
            return Read(InBuffer, bytes);
        }

        private int Read(byte[] buffer, int bytes)
        {
            return port.Read(buffer, 0, bytes);
        }

        private void Send(byte[] command)
        {
            port.Write(command, 0, command.Length);
        }

        static readonly byte[] RESET_OK_RESPONSE = new byte[] { 0x76, 0x00, 0x26, 0x00 };
        static readonly byte[] RESET_COMMAND = new byte[] { 0x56, 0x00, 0x26, 0x00 };

        static readonly byte[] STOP_OK_RESPONSE = new byte[] { 0x76, 0x00, 0x36, 0x00, 0x00 };
        static readonly byte[] STOP_COMMAND = new byte[] { 0x56, 0x00, 0x36, 0x01, 0x03 };

        static readonly byte[] SNAP_OK_RESPONSE = new byte[] { 0x76, 0x00, 0x36, 0x00, 0x00 };
        static readonly byte[] SNAP_COMMAND = new byte[] { 0x56, 0x00, 0x36, 0x01, 0x00 };

        static readonly byte[] SIZE_OK_RESPONSE = new byte[] { 0x76, 0x00, 0x34, 0x00, 0x04, 0x00, 0x00 };
        static readonly byte[] SIZE_COMMAND = new byte[] { 0x56, 0x00, 0x34, 0x01, 0x00 };

        static readonly byte[] GET_CHUNK_OK_RESPONSE = new byte[] { 0x76, 0x00, 0x32, 0x00, 0x00 };
        static readonly byte[] GET_CHUNK_COMMAND = new byte[] { 0x56, 0x00, 0x32, 0x0C, 0x00, 0x0A, 0x00, 0x00, 255, 255, 0x00, 0x00, 255, 255, 0x00, 0x0A };

        public static readonly byte[] SET_SIZE_160x120 = new byte[] { 0x56, 0x00, 0x31, 0x05, 0x04, 0x01, 0x00, 0x19, 0x22 };
        public static readonly byte[] SET_SIZE_320x240 = new byte[] { 0x56, 0x00, 0x31, 0x05, 0x04, 0x01, 0x00, 0x19, 0x11 };
        public static readonly byte[] SET_SIZE_640x480 = new byte[] { 0x56, 0x00, 0x31, 0x05, 0x04, 0x01, 0x00, 0x19, 0x00 };
        public static readonly byte[] SET_SIZE_OK_RESPONSE = new byte[] { 0x76, 0, 0x31, 0 };

        SerialPort port;

        private bool AreEqual(byte[] left, int inSize)
        {
            if (left == null || left.Length != inSize)
                return false;

            for (int i = 0; i < left.Length; i++)
                if (left[i] != InBuffer[i])
                    return false;

            return true;
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (port != null)
                port.Dispose();
        }

        #endregion
    }
}


#4

Edited original post, thanks for the tips.


#5

Fantastic, thanks :slight_smile:


#6

I wolud like to change the bitrate in 115200, but when I send the command to change in 115200 the camera doesn’t return the code "76 00 24 00 00"
I tried to set 38400 and it send me the right code “76 00 24 00 00”.

I want to improve the speed and change to 115200.


#7

Max, are you changing your receiving baud rate straight after changing the modules baud rate? I would imagine if you change the module’s rate, unless you change your own you will get corrupted data.


#8

Max,

Did you only change the baudrate in the code here?

using (var camera = new LinkspriteCamera(new SerialPort("COM1", 38400, Parity.None, 8, StopBits.One)))
{...

If so, That’s not going to work. :frowning:
To change the Camera baud rate to 115200
you need to set the command byte array like this

static readonly byte[] Baud115200_COMMAND = new byte[] { 0x56, 0x00, 0x34, 0x0D, 0xA6 }; 

and then use the Send() method to send the command to the camera.

Send(Baud115200_COMMAND); 

here is the list of the baudrates used

static readonly byte[] Baud9600_COMMAND = new byte[] { 0x56, 0x00, 0x34, 0xAE, 0xC8 }; 
static readonly byte[] Baud19200_COMMAND = new byte[] { 0x56, 0x00, 0x34, 0x56, 0xE4 }; 
static readonly byte[] Baud38400_COMMAND = new byte[] { 0x56, 0x00, 0x34, 0x2A, 0xF2 }; // default baudrate
static readonly byte[] Baud57600_COMMAND = new byte[] { 0x56, 0x00, 0x34, 0x1C, 0x4C }; 
static readonly byte[] Baud115200_COMMAND = new byte[] { 0x56, 0x00, 0x34, 0x0D, 0xA6 }; 

hope this help.

sam


#9

How do we connect the linksprite camera to Fez Cobra?Cable is a JST to 5 wires (2 are vcc and gnd)

The other 3 are TXD, RXD and TV anyone knows where to connect them? Thanks


#10

It’s a serial device so you connect the TX/RX to a COM port unless you are processing a raw video stream in which case use the TV line.


#11

I wired the camera to a Cobra with two JST cables. One had TX, VCC and Ground. The ohter had ground and Rx.

You don’t need the TV signal.

I plugged the cables into the JST connectors on the Cobra for COM1 RX and COM1 RX.


#12

Thanks for reply. I put it on IO28=RX and IO29=TX but i get

ERROR resetting
ERROR changing size
ERROR resetting
ERROR stopping


#13

What assembly reference does it need for this? it shows errors

            var byteString = @ "<put bytes here>";
            var lines = byteString.Split(Environment.NewLine.ToCharArray());
            var bytes = new List<byte>();
            foreach (var line in lines.Where(x => (x ?? "").Trim() != ""))
            {
                byte num = Convert.ToByte(line);
                bytes.Add(num);
            }
 
            string file = @ "C:\temp.jpg";
            File.WriteAllBytes(file, bytes.ToArray());
            System.Diagnostics.Process.Start(file);

#14

That is just a bit of sample code that you can put in a console or app or unit test project to take the debug data output and write it to a file on your computer. Alternatively, if you have an SD card you can write the data directly to it and bypass the outputting of the byte info and this step. I was using a Panda at the time without and SD card so I had to do this to see the picture.


#15

Ok thanks, but still I cant get the camera working. I even tested it on com3 IO28 and IO29 , also on COM1 pins etc. But keeps getting the error not resetting.

Any ideas?


#16

yes you probably have your RX and TX pins reversed.

RX on Fez == TX on camera
TX on Fez == RX on camera


#17

ah correct thanks. LoL why are they opposite? :smiley:

How can I show the image directly on my pc? (i dont have an SD Card atm and I don’t want manually get the bytes like on the example you said)


#18

TX = Transmit, RX = Receive.

When you [italic]transmit[/italic] (TX) from the Fez, the Camera [italic]receives[/italic] (RX) it, so Fez TX connects to Camera RX.
And when the Fez [italic]receives[/italic] (RX), it’s receiving what the Camera [italic]Transmits[/italic], so Fez RX connects to Camera TX.

Hopefully that makes sense. ;D


#19

Ok got it thanks :slight_smile: