Two PicoDVI Eyes Synced
What's better than one eye via DVI? Two! You can sync two PicoDVI boards together with a STEMMA QT cable to show a set of two coordinated eyeballs on two different DVI displays.
- One board acts as the primary, showing the left eye, and sending commands to sync gaze, pupil and blink actions.
- The secondary receives these commands and outputs them to the matching right eye.
There is a primary sketch and a secondary sketch that is uploaded to each board. Both are embedded below.
Wiring
You can use any two PicoDVI boards. They do not need to be identical hardware. You can see in the demo GIF at the top of the page, that the code is running with a Feather RP2040 DVI and a Pico RP2040 paired with a DVI PiCowbell.
The I2C wiring in Monster Eyes assumes that you are using the default defined Wire port. This port aligns with STEMMA QT ports on PiCowbells and the Feather RP2040 DVI.
Otherwise, you'll want to wire up the default Wire I2C connections with shared ground. Here's what that wiring would look like with two Pico RP2040's:
- Primary Pico GP4/SDA to Secondary Pico GP4/SDA (blue wire)
- Primary Pico GP5/SCL to Secondary Pico GP5/SCL (yellow wire)
- Primary Pico GND to Secondary Pico GND (black wire)
Pin Defines
The PicoDVI library has predefined pin definitions for these hardware combinations. You can use these pin definitions at the top of your code, just like you would with other sketches that use the PicoDVI library.
#if defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_DVI)
#define DVI_PIN_CONFIG adafruit_feather_dvi_cfg
#else
#define DVI_PIN_CONFIG adafruit_dvibell_cfg // PiCowbell DVI;
// pico_sock_cfg // Pico DVI Sock
// pimoroni_demo_hdmi_cfg // Pimoroni Pico DV
#endif
DVIGFX16 dvi(DVI_RES_320x240p60, DVI_PIN_CONFIG);
Example Code - Primary Board
After installing the Monster Eyes library, upload this sketch to the primary PicoDVI board.
// Adafruit Monster Eyes -- primary for PicoDVI sync
//
// Upload PicoDVI_Sync_Primary to one board and PicoDVI_Sync_Secondary to the
// other, with the same config.eye and assets on both.
//
// WIRING: one STEMMA QT cable between the two boards or connect SDA to SDA, SCL to SCL
#include <Adafruit_Monster_Eyes.h>
#include <Eyes_Sync.h>
#include <PicoDVI.h>
#if defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_DVI)
#define DVI_CFG adafruit_feather_dvi_cfg
#else
#define DVI_CFG adafruit_dvibell_cfg // PiCowbell DVI;
// pico_sock_cfg // Pico DVI Sock
// pimoroni_demo_hdmi_cfg // Pimoroni Pico DV
#endif
DVIGFX16 dvi(DVI_RES_320x240p60, DVI_CFG);
Adafruit_Monster_Eyes eyes(&dvi, 1);
Eyes_SyncPrimary sync(eyes); // Defaults: Wire, address 0x42, right eye
void setup() {
Serial.begin(115200);
eyes.setVerbose(Serial);
if (eyes.driveModeRequested()) {
eyes.runDriveMode();
}
eyes.setDriveModeEnabled(false);
if (!eyes.begin()) {
Serial.print("Monster Eyes failed: ");
Serial.println(eyes.errorString());
while (1)
delay(1000);
}
sync.begin();
}
void loop() {
eyes.animate();
sync.send();
static uint32_t last = 0;
if (millis() - last >= 1000) {
last = millis();
Serial.printf("%.0f fps\n", eyes.frameRate());
Serial.printf("sync: sent %lu, failed %lu\n", sync.sent(), sync.failed());
sync.resetCounts();
}
}
Example Code - Secondary Board
After installing the Monster Eyes library, upload this sketch to the secondary PicoDVI board.
// Adafruit Monster Eyes -- secondary of a two-board pair, one eye each.
//
// Upload PicoDVI_Sync_Primary to one board and PicoDVI_Sync_Secondary to the
// other, with the same config.eye and assets on both.
//
// WIRING: one STEMMA QT cable between the two boards or SDA to SDA, SCL to SCL
#include <Adafruit_Monster_Eyes.h>
#include <Eyes_Sync.h>
#include <PicoDVI.h>
#if defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_DVI)
#define DVI_CFG adafruit_feather_dvi_cfg
#else
#define DVI_CFG adafruit_dvibell_cfg // PiCowbell DVI;
// pico_sock_cfg // Pico DVI Sock
// pimoroni_demo_hdmi_cfg // Pimoroni Pico DV
#endif
DVIGFX16 dvi(DVI_RES_320x240p60, DVI_CFG);
Adafruit_Monster_Eyes eyes(&dvi, 1);
Eyes_SyncSecondary sync(eyes); // Defaults: Wire, address 0x42, left eye
void setup() {
Serial.begin(115200);
eyes.setVerbose(Serial);
if (eyes.driveModeRequested()) {
eyes.runDriveMode();
}
eyes.setDriveModeEnabled(false);
if (!eyes.begin()) {
Serial.print("Monster Eyes failed: ");
Serial.println(eyes.errorString());
while (1)
delay(1000);
}
sync.begin();
}
void loop() {
sync.poll(); // Takes over gaze, pupil and lids while packets arrive
eyes.animate();
static uint32_t last = 0;
if (millis() - last >= 1000) {
last = millis();
Serial.printf("%.0f fps\n", eyes.frameRate());
sync.report(Serial);
sync.resetCounts();
}
}
Page last edited September 23, 2026
Text editor powered by tinymce.