Cmucam5 (pixy)

Was anyone here lucky enough to communicate with pixy using spi on any gadgeteer board?

Nope, I am still waiting for an update to the firmware so it can detect more complex stuff.

Will definately be interested! ;D

I have one of these nut I haven’t taken it out of the box yet. Looking forward to hearing you guys get it working.

I received my pixy and tested the Arduino code, I tried to port it to .netmf but nothing is working. There are differences in the SPI libraries between Arduino and .Net that I don’t know about it. If someone interested please inspect the following code. It is almost 1 to 1 translation from arduino to .net

using System;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.SocketInterfaces;
using Microsoft.SPOT.Hardware;
using Gadgeteer.SocketInterfaces;

namespace Gadgeteer.Modules.AM
{
    /// <summary>
    /// A PixySPI module for Microsoft .NET Gadgeteer
    /// </summary>
    public class PixySPI : GTM.Module
    {
        private const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000;

        private static void DelayMicroseconds(int microSeconds)
        {
            long stopTicks = DateTime.Now.Ticks +
                (microSeconds * TicksPerMicrosecond);

            while (DateTime.Now.Ticks < stopTicks) { }
        }

        Socket _socket;
        GTI.Spi _spi;
        GTI.SpiConfiguration _config;
        LinkSPI spidriver;

        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        /// <param name="socketNumberTwo">The second socket that this module is plugged in to.</param>
        public PixySPI(int socketNumber)
        {
            _socket = Socket.GetSocket(socketNumber, true, this, null);
            _socket.EnsureTypeIsSupported('S', this);
           
            _spi = SocketInterfaces.SpiFactory.Create(_socket, _config, SpiSharing.Shared, _socket, Gadgeteer.Socket.Pin.Six, this);
            spidriver = new LinkSPI(_spi);

            skipStart = false;
            blockCount = 0;
            blockArraySize = PIXY_INITIAL_ARRAYSIZE;
            blocks = new Block[blockArraySize];
        }

        private class LinkSPI 
        {
            static byte PIXY_SYNC_BYTE = 0x5a;
            static byte PIXY_SYNC_BYTE_DATA = 0x5b;
            static byte PIXY_OUTBUF_SIZE = 6;

            byte[] outBuf = new byte[PIXY_OUTBUF_SIZE];
            byte outLen;
            byte outIndex;

            GTI.Spi _spi;

            public LinkSPI(GTI.Spi spi)
            {
                outLen = 0;
                _spi = spi;
            }

            public UInt16 GetWord()
            {
                // ordering is different because Pixy is sending 16 bits through SPI 
                // instead of 2 bytes in a 16-bit word as with I2C
                UInt16 w;
                byte c, cout = 0;
                byte[] recieveBuffer = new byte[2];

                if (outLen > 0)
                {
                    cout = outBuf[outIndex++];
                    if (outIndex == outLen)
                        outLen = 0;

                    _spi.WriteRead(new byte[] { PIXY_SYNC_BYTE_DATA, cout }, recieveBuffer);
                    //w = _spi.transfer(PIXY_SYNC_BYTE_DATA);

                }
                else
                {
                    _spi.WriteRead(new byte[] { PIXY_SYNC_BYTE, cout }, recieveBuffer);
                    //w = _spi.transfer(PIXY_SYNC_BYTE);
                }

                w = (UInt16)(((UInt16)recieveBuffer[0]) << 8);
                w |= recieveBuffer[1];
                //w <<= 8;
                //c = _spi.transfer(cout);
                //w |= c;

                return w;
            }

            public byte GetByte()
            {
                byte[] buffer = new byte[1];
                _spi.WriteRead(new byte[] { 0x00 }, buffer);
                return buffer[0];
            }

            public int send(byte[] data, byte len)
            {
                if (len > PIXY_OUTBUF_SIZE || outLen != 0)
                    return -1;
                //memcpy(outBuf, data, len);
                data.CopyTo(outBuf, 0);

                outLen = len;
                outIndex = 0;
                return len;
            }

        }

        public Block[] blocks
        {
            get;
            private set;
        }
        bool skipStart;
        ushort blockCount;
        ushort blockArraySize;

        static byte PIXY_INITIAL_ARRAYSIZE = 30;
        static byte PIXY_MAXIMUM_ARRAYSIZE = 130;
        static ushort PIXY_START_WORD = 0xaa55;
        static ushort PIXY_START_WORDX = 0x55aa;
        static byte PIXY_DEFAULT_ADDR = 0x54;  // I2C

        public struct Block
        {
            public void Print()
            {
                //char[] buf = new char[64];
                //sprintf(buf, "sig: %d x: %d y: %d width: %d height: %d\n", signature, x, y, width, height);
                string buf = "sig: " + signature + " x: " + x + " y: " + y + " width: " + width + " height: " + height;

                //Serial.print(buf);
                Debug.Print(buf);
            }
            public ushort signature;
            public ushort x;
            public ushort y;
            public ushort width;
            public ushort height;
        }

        bool GetStart()
        {
            ushort w, lastw;

            lastw = 0xffff;

            while (true)
            {
                w = spidriver.GetWord();
                if (w == 0 && lastw == 0)
                {
                    DelayMicroseconds(10);
                    return false;
                }
                else if (w == PIXY_START_WORD && lastw == PIXY_START_WORD)
                    return true;
                else if (w == PIXY_START_WORDX)
                {
                    //Serial.println("reorder");
                    DebugPrint("Reorder");
                    spidriver.GetByte(); // resync
                }
                lastw = w;
            }
        }

        void Resize()
        {
            Block[] newBlocks;
            blockArraySize += PIXY_INITIAL_ARRAYSIZE;
            newBlocks = new Block[blockArraySize];
            //memcpy(newBlocks, blocks, sizeof(Block)*blockCount);
            blocks.CopyTo(newBlocks, 0);
            blocks = newBlocks;
        }

        //public int SetServos(ushort s0, ushort s1)
        //{
        //    byte[] outBuf = new byte[6];

        //    outBuf[0] = 0x00;
        //    outBuf[1] = 0xff;
        //    var temp = GetBytes(s0);
        //    outBuf[2] = temp[0];
        //    outBuf[3] = temp[1];
        //    //*(uint16_t *)(outBuf + 2) = s0;
        //    temp = GetBytes(s1);
        //    outBuf[4] = temp[0];
        //    outBuf[5] = temp[1];
        //    //*(uint16_t *)(outBuf + 4) = s1;

        //    return spidriver.send(outBuf, 6);
        //}

        public static byte[] GetBytes(ushort value)
        {
            return new byte[2] { (byte)(value & 0xFF), (byte)((value >> 8) & 0xFF) };
        }

        public ushort GetBlocks(ushort maxBlocks)
        {
            byte i;
            ushort w, checksum, sum;
            Block block = new Block();

            if (!skipStart)
            {
                if (GetStart() == false)
                {
                    return 0;
                }
            }
            else
            {
                skipStart = false;
            }

            for (blockCount = 0; blockCount < maxBlocks && blockCount < PIXY_MAXIMUM_ARRAYSIZE; )
            {
                checksum = spidriver.GetWord();
                if (checksum == PIXY_START_WORD) // we've reached the beginning of the next frame
                {
                    skipStart = true;
                    //Serial.println("skip");
                    return blockCount;
                }
                else if (checksum == 0)
                {
                    return blockCount;
                }

                if (blockCount > blockArraySize)
                {
                    Resize();
                }

                blocks[blockCount] = block;

                //for (i=0, sum=0; i<sizeof(Block)/sizeof(uint16_t); i++)
                for (i = 0, sum = 0; i < 5; i++)
                {
                    w = spidriver.GetWord();
                    sum += w;
                    block.signature = w;

                    w = spidriver.GetWord();
                    sum += w;
                    block.x = w;

                    w = spidriver.GetWord();
                    sum += w;
                    block.y = w;

                    w = spidriver.GetWord();
                    sum += w;
                    block.width = w;

                    w = spidriver.GetWord();
                    sum += w;
                    block.height = w;

                    //*((uint16_t *)block + i) = w;
                }

                if (checksum == sum)
                {
                    blockCount++;
                }
                else
                {
                    //Serial.println("cs error");
                    DebugPrint("checksum error");
                }

                w = spidriver.GetWord();
                if (w != PIXY_START_WORD)
                {
                    return blockCount;
                }
            }
            return blockCount;
        }
    }
}

I am thinking of buying one of these cams. Did anyone get it working yet? I am happy to try and debug the c# code if required. Looks like the board supports i2c, spi, and uart. I wonder if any of these other protocols have been tried?

Also, did the wiring connection work with a standard gadgeteer cable, or was some adjustment required to the pins?

Thank you!

I bought one and I finally got PixyCam working and debugged. I have posted full code solution on codeshare:

https://www.ghielectronics.com/community/codeshare/entry/1017

Welcome to 50fps robot vision and object detection using .NET MF 4.3!

Enjoy!

4 Likes