Table of Contents

How to talk to Pixy2

We currently have software support for Arduino, and Raspberry Pi and similar controllers

You may want Pixy2 to talk to a different controller and that's not a problem – Pixy2 is easy to strike up a conversation with! It's easier than the original Pixy. Pixy2 has three separate methods of communication:

Picking the right interface

You first need to determine how you want to talk to Pixy2. Pixy2 sends information to Arduino at 2 Mbits/second over SPI because the Arduino SPI clock rate is set to 2 MHz. UART serial on the other hand, is typically only 19.2 to 115 kbits/second. The goal with the communication interface is to make sure that you can get the information out of Pixy2 at the full 60 frames-per-second.

If you plan on using USB for communication

If you plan on using USB for communication with Pixy2, we have a library called libpixyusb2. It should be relatively easy to port to your controller, especially if your controller runs Linux. If your controller runs Linux, refer to this guide, which should get you close (at the very least).
Whereas the previous version of libpixyusb required libboost and libusb, libpixyusb2 only requires libusb. Libusb is available on lots of platforms besides Linux. So if your platform doesn't run Linux, start by locating a port of libusb for your platform/OS and go from there by first getting libpixyusb2 to compile and then testing with a simple example like the get_blocks_cpp_demo that is referred to in this guide.

The rest of this guide is for other (non USB) interfaces.

Setting the interface (other than USB)

You can configure the output interface through PixyMon. The “Data out port” parameter in the “Interface” tab determines which interface you're using.

Note, the USB interface and protocol is always enabled, while each of the interfaces above can only be enabled one at a time.

image_248_2.jpg

Notes on each interface

SPI

The ICSP SPI interface operates as an SPI slave. It is designed around the Arduino's ICSP port, which doesn't have a slave select signal. The default data rate is 2 mbits/sec, but this can be increased by modifying the PIXY_SPI_CLOCKRATE value in Pixy2.h in the Pixy2 Arduino library. The protocol has checksums to deal with bit errors. The specific type of SPI that Pixy2 supports:

Pixy2 also supports SPI with slave select (SPI with SS).

Here's how to hook up your controller's SPI to Pixy2 (if you are not using an Arduino and the supplied cable):

I2C

You'll need to make a special cable.

Here's how to hook up your controller's I2C to Pixy:

Note, when talking to more than one Pixy over I2C, you will need to configure a different I2C address for each Pixy2 so they don't step on each other. You can make a “multi-crimp cable”, meaning you can take a 10-conductor ribbon cable and crimp N 10-pin IDC connectors to it and plug into to your N Pixy2s. That is, when selecting I2C as an interface, all signals on Pixy2's I/O connector go into a high-impedance state and won't interfere with each other, waste power, etc.

UART

Here's how to hook up your controller's UART to Pixy:

Analog and digital output

Here's how to hook up your controller's ADC and digital I/O to Pixy:

Note, all digital output signals on Pixy2 are 3.3V CMOS logic. All digital input signals on Pixy2 are 5V tolerant.

The serial protocol

Whether you're using SPI, I2C or UART serial, the protocol is exactly the same.

Pixy2 communicates with “packets” in both directions – request and response. Each packet has the following structure, if no checksums are used:

Communication with Pixy2 begins by sending a request packet. For example, here is a packet to request the hardware/firmware version data chunk. (Normally, request packets are sent without using checksums.):

0xae  // first byte of no_checksum_sync (little endian -> least-significant byte first)
0xc1  // second byte of no_checksum_sync
0x0e  // this is the version request type
0x00  // data_length is 0

Pixy2 will respond with the packet bytes below (or similar, depending on the hardware/firmware versions.) Response packets always have checksums so the receiver (your controller) can test data integrity:

0xaf // first byte of checksum_sync (little endian -> least-significant byte first)
0xc1 // second byte of checksum_sync
0x0f // this is the version response type
0x10 // data_length is 0x10 (16) bytes 
0x0d // first byte of data checksum (little endian -> least-significant byte first)
0x03 // second byte of data checksum
0x00 // first byte of hardware version (little endian -> least-significant byte first)
0x22 // second byte of hardware version
0x03 // firmware major version number
0x00 // firmware minor version number
0x0a // first byte of firmware build number (little endian -> least-significant byte first)
0x00 // second byte of firmware build number
0x67 // byte 0 of firmware type ASCII string
0x65 // byte 1 of firmware type ASCII string
0x6e // byte 2 of firmware type ASCII string
0x65 // byte 3 of firmware type ASCII string
0x72 // byte 4 of firmware type ASCII string
0x61 // byte 5 of firmware type ASCII string
0x6c // byte 6 of firmware type ASCII string
0x00 // byte 7 of firmware type ASCII string
0x00 // byte 8 of firmware type ASCII string
0x00 // byte 9 of firmware type ASCII string

The version request-response (above) is a simple way to test communication between your controller and Pixy2. Go ahead and code it up for your controller, paying special attention to how to initialize the communication interface on your platform, whether it's SPI, I2C or UART serial. Here is some code that might help get you started:

  uint8_t i, lenReceived, recvBuf[32];
  uint8_t versionRequest[] =
  {
    0xae,  // first byte of no_checksum_sync (little endian -> least-significant byte first)
    0xc1,  // second byte of no_checksum_sync
    0x0e,  // this is the version request type
    0x00   // data_length is 0
  };
  
  // You need to write send(), which takes a pointer to the data you want to send and the number of 
  // bytes to send via your serial port (SPI, I2C or UART).  It returns the number of bytes successfully sent.
  extern uint8_t send(uint8_t *data, uint8_t len);
  
  // You also need to write recv(), which takes a pointer to a data buffer where the received data
  // will be written/returned and the number of bytes to receive via your serial port (SPI, I2C or UART).  
  // It returns the number of bytes immediately available and written into the buffer (without needing 
  // to busy-wait.)
  extern uint8_t recv(uint8_t *data, uint8_t len);
  
  // clear out any stale data
  while(recv(recvBuf, 1));
  
  send(versionRequest, 4);   
  delayMillisecond(1); // delay a little so we don't receive too fast (may not be necessary.)  
  lenReceived = recv(recvBuf, 6 + 16); // 6 bytes of header and checksum and 16 bytes of version data
  
  // print result
  printf("Received %hhu bytes.\n", lenReceived);
  for (i=0; i<lenReceived; i++)
    printf("%hhu: 0x%hhx\n", i, recvBuf[i]); 
  

If you can't get this request-response to work, spend some time trying to determine what might be going wrong. Try using an oscilloscope to see if your controller is outputting data as expected (first), and Pixy2 is outputting data as expected in response (second). The challenge with getting any communication interface up and running is the strict requirement that the wiring, connections, and code (everything) need to be correct in order for it to work. If any single detail is not correct, e.g. missed/swapped connection or incorrect initialization code, communication will not work (at all). So we start with this simple exchange. The good news is that once you get this working, the hard part is over, and the rest is quick/easy.

TPixy2.h

TPixy2.h is a template class that can be easily reused, if your porting language is C/C++. If your porting language isn't C/C++, it's still a good reference for the port translation, as it contains request-response code for all possible requests and responses. Pixy2CCC.h and Pixy2Line.h are also needed to round out the Pixy2 API.

TPixy2.h uses the LinkType class to communicate with Pixy2. It assumes some member functions and an implementation of millis() and delayMicroseconds(), as shown in the header code below:

// YourLink example class declaration 
// 
// This is an example class for use in the template class TPixy2
// (https://github.com/charmedlabs/pixy2/blob/master/src/host/arduino/libraries/Pixy2/TPixy2.h)
//
// Refer to 
// https://github.com/charmedlabs/pixy2/blob/master/src/host/arduino/libraries/Pixy2/Pixy2UART.h 
// or 
// https://github.com/charmedlabs/pixy2/blob/master/src/host/arduino/libraries/Pixy2/Pixy2.h 
// for working examples of template classes that work with TPixy2. 

#include "TPixy2.h"

class YourLink
{

public:
  // open() is called upon initialization.  A 32-bit arg is passed as-is from the TPixy2 init() function.  
  // You can ignore the arg or use it to set up an address, data rate, whatever you want. 
  // Returns 0 if successful, or <0 if it fails
  int8_t open(uint32_t arg);
  
  // close is called when the TPixy2 instance is destroyed. 
  void close();
  
  // recv() takes a pointer to a data buffer where the received data will be written/returned and 
  // the number of bytes to receive via your serial port (SPI, I2C or UART).  It returns the number of 
  // bytes immediately available and written into the buffer (without needing to busy-wait.)  
  // It also takes a pointer to a 16-bit unsigned int that it will write the simple checksum of all 
  // bytes received, but only if the pointer is non-null.  Refer to the example classes in the links above.
  int16_t recv(uint8_t *data, uint8_t len, uint16_t *checksumCalculation=NULL);
  
  // send() takes a pointer to the data you want to send and the number of bytes to send via your serial 
  // port (SPI, I2C or UART).  It returns the number of bytes successfully sent.  
  int16_t send(uint8_t *data, uint8_t len);
  
  // ...
};


// These two functions are borrowed from the Arduino API and need to be implemented in some form 
// in order for the existing TPixy2 code to work as-is.  millis() may be difficult to implement, 
// but its use isn't hugely important.  Look in TPixy2.h for its references, and remove if necessary.  
// delayMicroseconds() can be easily implemented by a simple nested for-loop, and calibrated. 
uint32_t millis();
void delayMicroseconds(uint32_t us);


// Define an easy-to-use type for Pixy2 that uses your link as the class parameter to TPixy2
typedef TPixy2<YourLink> Pixy2WithYourLink;

millis() and delayMicroseconds() are borrowed from the Arduino API.

Debugging

When attempting to get your link class integrated and everything working, problems may arise and you may have to track down the problem. A good place to start is in the getSync() function in TPixy2.h of the code. Is it finding the sync codes from Pixy2? If not, why? Is it getting the correct bytes? If it is successfully getting the sync codes, there is a problem occuring after getting the sync. This will get you started. :-)

If you need help, post on our forum or send us an email ([email protected]). Happy coding!