We currently have software support for Arduino, LEGO Mindstorms EV3, roboRIO, 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:
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 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.
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.
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):
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.
Here's how to hook up your controller's UART to Pixy:
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.
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:
or the following structure, if 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 is a template class that can be easily reused, if your porting language is C++. If your porting language isn't 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.
We have a complete protocol reference below. It will be very helpful if you are porting to a platform in a language other than C++. But if you are using C/C++, TPixy2.h is a good starting point. It 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.
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!
In case you need to implement your own client to talk to Pixy2 in a language or microcontroller we don't support, here is a handy reference of the Pixy2 serial protocol request and response packets.
For reference, our Arduino Library implements all of what's below in the files TPixy2.h, Pixy2Line.h, Pixy2CCC.h, and Pixy2Video.h. For consistency, the function names listed here are the same as in that implementation.
All values exchanged through the protocol are integer quantities (no floating point values). All quantities are either 1-byte (8-bit), 2-byte (16-bit), or 4-byte (32-bit). Multi-byte values are sent using little-endian byte ordering – that is, the least-significant-byte is sent first, followed by the next-least-significant-byte. The most-significant-byte is sent last. This page does a good job explaining endianness.
Furthermore, all signed integer quantities are represented using two's complement representation. This page describes two's complement representation.
It's not hugely important that you understand little-endian and two's complement representations – 95% of all processors use little-endian and two's complement. So you basically read the bytes into memory and tell the processor there's an integer there, and it will happily interpret it correctly. But it's good to know, and required information for the protocol to work correctly.
Original Pixy would constantly broadcast data about objects it was (or wasn’t) seeing. With Pixy2, we’ve moved to a request-response system. That means Pixy2 is silent unless you send it a properly formatted request packet, in which case it will respond in kind.
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | (varies) |
3 | Length of payload in bytes (len) | (varies) |
4 - len | Variable length payload | (varies) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | (varies) |
3 | Length of payload in bytes (len) | (varies) |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - len | Variable length payload | (varies) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 14 |
3 | Length of payload | 0 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 15 |
3 | Length of payload | 16 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 7 | 16-bit hardware version | (varies) |
8 | Firmware version (major) | (varies) |
9 | Firmware version (minor) | (varies) |
10 - 11 | 16-bit firmware build | (varies) |
12 | Firmware type (human readable string) | (varies) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 12 |
3 | Length of payload | 1 |
4 | Type (unused - reserved for future versions) | 0 - 255 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 13 |
3 | Length of payload | 2 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 7 | 16-bit frame width | 0 - 511 |
8 - 9 | 16-bit frame height | 0 - 511 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | Sync | 174, 193 (0xc1ae) |
2 | Type of packet | 16 |
3 | Length of payload | 1 |
4 | Brightness | 0 - 255 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | 32-bit result | result value |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 18 |
3 | Length of payload | 4 |
4 - 5 | 16-bit s0 - pan servo value | 0 - 511 |
6 - 7 | 16-bit s1 - tilt servo value | 0 - 511 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | 32-bit result/acknowledge | result value |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 20 |
3 | Length of payload | 3 |
4 | r - Red value | 0 - 255 |
5 | g - Green value | 0 - 255 |
6 | b - Blue value | 0 - 255 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | 32-bit result/acknowledge | result value |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 22 |
3 | Length of payload | 2 |
4 | Upper - turn on the two white LEDs along Pixy2 top edge | 0 (off) or 1 (on) |
5 | Lower - turn on all channels of lower RGB LED | 0 (off) or 1 (on) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | 32-bit result/acknowledge | result value |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 24 |
3 | Length of payload | 0 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | 32-bit result | frames-per-second |
See Color Connected Components API for more details on sigmap, maxblocks, tracking index, etc.
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 32 |
3 | Length of payload | 2 |
4 | Sigmap - indicate which signatures to receive data from | 0 (none) - 255 (all) |
5 | Maximum blocks to return | 0 (none) - 255 (all blocks) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 33 |
3 | Length of payload | 14 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 7 | 16-bit signature / Color code number | 0 - 255 |
8 - 9 | 16-bit X (center) of block in pixels | 0 - 315 |
10 - 11 | 16-bit Y (center) of block in pixels | 0 - 207 |
12 - 13 | 16-bit Width of block in pixels | 0 - 316 |
14 - 15 | 16-bit Height of block in pixels | 0 - 208 |
16 - 17 | 16-bit Angle of color-code in degrees | -180 - 180 (0 if not a color code) |
18 | Tracking index of block (see API for more info) | 0 - 255 |
19 | Age - number of frames this block has been tracked | 0 - 255 (stops incrementing at 255) |
Check out the Line tracking API for more details.
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 48 |
3 | Length of payload | 2 |
4 | Request type | 0=main features, 1=all features |
5 | Features (bitmap) | 7=vectors, intersections, barcodes |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 49 |
3 | Length of payload | (varies) |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 | Feature-type | 1=Vector, 2=Intersection, 4=Barcode |
7 | Feature-length | (varies) |
8 - 8+feature-length | Feature-data | (varies) |
Next feature-type (optional) | (varies) | |
Next feature-length (optional) | (varies) | |
Next feature-data (optinal) | (varies) | |
Next feature-type (optional) | (varies) | |
Next feature-length (optional) | (varies) | |
Next feature-data (optinal) | (varies) |
Note, this response can have up to three different feature sections, one for vectors (if vector(s) exist), one for intersections (if intersection(s) exist) and one for barcodes (if barcode(s) exist) in the image. Each feature section begins with a feature-type. The feature-length is next, which indicates how many bytes are in the feature-data. The feature-data is last, which has the data associated with the feature. Sometimes the feature-data section will have more than one of the given feature type, for example, there may be three barcodes, in which case the three barcodes will be concatenated in the feature-data without any bytes between them.
For the specific feature data types and composition, please consult the Pixy2Line.h file.
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 54 |
3 | Length of payload | 1 |
4 | Mode | (varies) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | Response/acknowledge | (varies) |
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 58 |
3 | Length of payload | 2 |
4 - 5 | 16-bit angle | (varies) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | Response/acknowledge | (varies) |
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 60 |
3 | Length of payload | 2 |
4 - 5 | 16-bit angle | (varies) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | Response/acknowledge | (varies) |
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 56 |
3 | Length of payload | 1 |
4 | vector index | (varies) |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | Response/acknowledge | (varies) |
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 62 |
3 | Length of payload | 0 |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 - 9 | Response/acknowledge | (varies) |
Check out the Video API for more details.
Bit | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 174, 193 (0xc1ae) |
2 | Type of packet | 112 |
3 | Length of payload | 5 |
4 - 5 | 16-bit x value | 0 - 315 |
6 - 7 | 16-bit y value | 0 - 207 |
8 | Saturate flag | 0=don't saturate, 1=saturate |
Byte | Description | Value(s) |
---|---|---|
0 - 1 | 16-bit sync | 175, 193 (0xc1af) |
2 | Type of packet | 1 |
3 | Length of payload | 4 |
4 - 5 | 16-bit checksum | sum of payload bytes |
6 | blue value | (varies) |
7 | green value | (varies |
8 | red value | (varies) |