Flipdot Matrix PCB Design Review

Refine this doc
Ask about this doc
Firmware Getting Started — ESP32-S3-MINI-1 Flipdot 6x8
Platform
  • MCU/module: ESP32-S3-MINI-1-N8 direct-solder module
  • Framework: Arduino / ESP32 board support 3.x
  • Build system: PlatformIO
  • Debug/command interface: native USB Serial/JTAG via board USB-C connector
Hardware Notes
  • U10 VDD is 3.3V only from VCC_3V3; never connect VM_5V to the module.
  • Manual reset: SW1 pulls EN low.
  • Manual boot: SW2 pulls IO0 low for download mode if needed.
  • Native USB: IO19 = USB_D_N, IO20 = USB_D_P.

GPIO Mapping


FunctionGPIOConnected ToDirectionNotes
ROW0_IN11U1 AIN1OutputROW0 rail high/low control input 1
ROW0_IN22U1 AIN2OutputROW0 rail high/low control input 2
ROW1_IN13U1 BIN1OutputROW1 rail input 1
ROW1_IN24U1 BIN2OutputROW1 rail input 2
ROW2_IN15U2 AIN1OutputROW2 rail input 1
ROW2_IN26U2 AIN2OutputROW2 rail input 2
ROW3_IN17U2 BIN1OutputROW3 rail input 1
ROW3_IN28U2 BIN2OutputROW3 rail input 2
ROW4_IN19U3 AIN1OutputROW4 rail input 1
ROW4_IN210U3 AIN2OutputROW4 rail input 2
ROW5_IN111U3 BIN1OutputROW5 rail input 1
ROW5_IN212U3 BIN2OutputROW5 rail input 2
ROW6_IN113U4 AIN1OutputROW6 rail input 1
ROW6_IN214U4 AIN2OutputROW6 rail input 2
ROW7_IN115U4 BIN1OutputROW7 rail input 1
ROW7_IN216U4 BIN2OutputROW7 rail input 2
COL0_IN117U5 AIN1OutputCOL0 rail input 1
COL0_IN218U5 AIN2OutputCOL0 rail input 2
USB_D_N19J1 D- through U11USBNative USB D-
USB_D_P20J1 D+ through U11USBNative USB D+
COL1_IN121U5 BIN1OutputCOL1 rail input 1
COL1_IN233U5 BIN2OutputCOL1 rail input 2
COL2_IN134U6 AIN1OutputCOL2 rail input 1
COL2_IN235U6 AIN2OutputCOL2 rail input 2
COL3_IN136U6 BIN1OutputCOL3 rail input 1
COL3_IN237U6 BIN2OutputCOL3 rail input 2
COL4_IN138U7 AIN1OutputCOL4 rail input 1
COL4_IN239U7 AIN2OutputCOL4 rail input 2
COL5_IN140U7 BIN1OutputCOL5 rail input 1
COL5_IN241U7 BIN2OutputCOL5 rail input 2
STATUS_LED48R_LED -> LED1OutputActive-high status LED
PlatformIO Project Setup

Ini


[env:esp32-s3-mini-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
board_build.flash_size = 8MB
board_build.arduino.memory_type = qio_opi
build_flags =
    -D ARDUINO_USB_MODE=1
    -D ARDUINO_USB_CDC_ON_BOOT=1
Complete Firmware Source

Cpp


#include <Arduino.h>

// Matrix geometry
constexpr uint8_t ROWS = 8;
constexpr uint8_t COLS = 6;

// Pulse timing defaults
uint16_t PULSE_MS = 15;
constexpr uint16_t SETTLE_MS = 3;
constexpr uint16_t MAX_PULSE_MS = 50;

// ROW rail control pins: each rail has IN1, IN2 to one DRV8833 half-bridge.
const uint8_t ROW_IN1[ROWS] = {1, 3, 5, 7, 9, 11, 13, 15};
const uint8_t ROW_IN2[ROWS] = {2, 4, 6, 8, 10, 12, 14, 16};

// COL rail control pins.
const uint8_t COL_IN1[COLS] = {17, 21, 34, 36, 38, 40};
const uint8_t COL_IN2[COLS] = {18, 33, 35, 37, 39, 41};

constexpr uint8_t STATUS_LED_PIN = 48;

// DRV8833 single-ended rail states using OUT1 only:
// IN1=0, IN2=0: high-Z / coast
// IN1=1, IN2=0: OUT1 high
// IN1=0, IN2=1: OUT1 low
// IN1=1, IN2=1: brake, avoided
void railHiZ(uint8_t in1, uint8_t in2) {
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}

void railHigh(uint8_t in1, uint8_t in2) {
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
}

void railLow(uint8_t in1, uint8_t in2) {
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
}

void allRailsHiZ() {
  for (uint8_t r = 0; r < ROWS; r++) railHiZ(ROW_IN1[r], ROW_IN2[r]);
  for (uint8_t c = 0; c < COLS; c++) railHiZ(COL_IN1[c], COL_IN2[c]);
}

void initMatrixPins() {
  for (uint8_t r = 0; r < ROWS; r++) {
    pinMode(ROW_IN1[r], OUTPUT);
    pinMode(ROW_IN2[r], OUTPUT);
  }
  for (uint8_t c = 0; c < COLS; c++) {
    pinMode(COL_IN1[c], OUTPUT);
    pinMode(COL_IN2[c], OUTPUT);
  }
  allRailsHiZ();
}

void flipCell(uint8_t row, uint8_t col, bool whiteFaceUp) {
  if (row >= ROWS || col >= COLS) return;

  allRailsHiZ();
  delay(SETTLE_MS);

  if (whiteFaceUp) {
    // Forward current: ROW high, COL low.
    railHigh(ROW_IN1[row], ROW_IN2[row]);
    railLow(COL_IN1[col], COL_IN2[col]);
  } else {
    // Reverse current: ROW low, COL high.
    railLow(ROW_IN1[row], ROW_IN2[row]);
    railHigh(COL_IN1[col], COL_IN2[col]);
  }

  delay(PULSE_MS);
  allRailsHiZ();
  delay(SETTLE_MS);
}

void setAll(bool whiteFaceUp) {
  for (uint8_t r = 0; r < ROWS; r++) {
    for (uint8_t c = 0; c < COLS; c++) {
      flipCell(r, c, whiteFaceUp);
    }
  }
}

void checkerboard(bool invert) {
  for (uint8_t r = 0; r < ROWS; r++) {
    for (uint8_t c = 0; c < COLS; c++) {
      flipCell(r, c, ((r + c) & 1) ^ invert);
    }
  }
}

void sweep() {
  for (uint8_t r = 0; r < ROWS; r++) {
    for (uint8_t c = 0; c < COLS; c++) {
      flipCell(r, c, true);
    }
  }
  for (uint8_t r = 0; r < ROWS; r++) {
    for (uint8_t c = 0; c < COLS; c++) {
      flipCell(r, c, false);
    }
  }
}

void halfSelectTest() {
  // Pulses one selected cell repeatedly; surrounding same-row/same-col cells should not move.
  for (uint8_t i = 0; i < 10; i++) {
    flipCell(0, 0, true);
    delay(100);
    flipCell(0, 0, false);
    delay(100);
  }
}

void printHelp() {
  Serial.println("Commands:");
  Serial.println("  c              clear all cells");
  Serial.println("  s              set all cells");
  Serial.println("  k              checkerboard");
  Serial.println("  K              inverse checkerboard");
  Serial.println("  w              sweep");
  Serial.println("  h              half-select test at R0 C0");
  Serial.println("  f R C T        flip row R col C to target T (0/1)");
  Serial.println("  p N            set pulse width in ms, clamped to MAX_PULSE_MS");
}

void handleCommand(String line) {
  line.trim();
  if (line.length() == 0) return;

  if (line == "c") setAll(false);
  else if (line == "s") setAll(true);
  else if (line == "k") checkerboard(false);
  else if (line == "K") checkerboard(true);
  else if (line == "w") sweep();
  else if (line == "h") halfSelectTest();
  else if (line[0] == 'p') {
    int n = line.substring(1).toInt();
    if (n > 0) PULSE_MS = min<uint16_t>(n, MAX_PULSE_MS);
    Serial.printf("PULSE_MS=%u\n", PULSE_MS);
  } else if (line[0] == 'f') {
    int r = -1, c = -1, t = -1;
    if (sscanf(line.c_str(), "f %d %d %d", &r, &c, &t) == 3) {
      flipCell((uint8_t)r, (uint8_t)c, t != 0);
    } else {
      Serial.println("Usage: f R C T");
    }
  } else {
    printHelp();
  }
}

void setup() {
  pinMode(STATUS_LED_PIN, OUTPUT);
  digitalWrite(STATUS_LED_PIN, LOW);

  initMatrixPins();

  Serial.begin(115200);
  unsigned long start = millis();
  while (!Serial && (millis() - start < 1500)) {
    delay(10);
  }

  digitalWrite(STATUS_LED_PIN, HIGH);
  Serial.println("ESP32-S3-MINI-1 Flipdot 6x8 firmware ready.");
  printHelp();
}

void loop() {
  if (Serial.available()) {
    String line = Serial.readStringUntil('\n');
    handleCommand(line);
  }
}
Build & Flash

Bash


pio run
pio run -t upload
pio device monitor -b 115200
If USB download mode is needed manually, hold BOOT (SW2), tap RESET (SW1), then release BOOT before uploading.

    Platform

    Hardware Notes

    GPIO Mapping

    PlatformIO Project Setup

    Complete Firmware Source

    Build & Flash

Documents

    Flipdot 6x8 Matrix Controller — Imported Specification

    Firmware Getting Started — ESP32-S3-MINI-1 Flipdot 6x8

    Flipdot 6x8 Matrix Controller — Design Review Notes

    Power Budget and Pulse Analysis — Flipdot 6x8 Matrix Controller

    ESP32-S3-MINI-1 Architecture Change

Assets

No assets added.

Flipdot 6x8 Matrix Controller

Flipdot 6x8 Matrix Controller thumbnail
ESP32-S3 based 6x8 flip-dot matrix controller using DRV8833 H-bridges, USB-C 5V input, AP2112K 3.3V logic rail, and a 48-coil row/column inductor matrix.

Properties

Properties describe core aspects of the project.

Pricing & Availability

Distributor

Qty 1

Arrow

$9.01–$11.23

Digi-Key

$50.59–$53.09

HQonline

$108.60–$108.82

LCSC

$17.78–$18.09

Mouser

$97.22–$97.28

TME

$1.32

Verical

$32.45–$37.11

Controls