Arnov Sharma
Published © MIT

Pico VGA Board 1.0

Drive VGA/HDMI Monitors with this Raspberry Pi PICO-based Dev board.

BeginnerFull instructions provided2 hours122
Pico VGA Board 1.0

Things used in this project

Hardware components

Raspberry Pi Pico W
Raspberry Pi Pico W
×1
NextPCB  Custom PCB Board
NextPCB Custom PCB Board
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

SCH

Code

Demo 1

C/C++
Hello world
#include "vga_graphics.h"
#include <string.h>

/* ── Screen ───────────────── */
#define SW 640
#define SH 480

/* ── Text scale ──────────── */
#define SCALE 4

/* ── Centered print ───────── */
void printCentered(const char* text, int y, char color) {
  int len = strlen(text);
  int textWidth = len * 6 * SCALE;
  int x = (SW - textWidth) / 2;

  for (int i = 0; i < len; i++) {
    drawChar(x + i * 6 * SCALE, y, text[i], color, BLACK, SCALE);
  }
}

/* ── CRT Scanlines ───────── */
void drawScanlines() {
  for (int y = 0; y < SH; y += 2) {
    drawHLine(0, y, SW, BLACK);  // dark line every other row
  }
}

/* ── Setup ─────────────── */
void setup() {
  initVGA();
  clearScreen();

  int lineHeight = 8 * SCALE;

  int totalHeight = lineHeight * 3 + 20; // spacing included
  int startY = (SH - totalHeight) / 2;

  // Line 1 (RED)
  printCentered("HELLO WORLD 1", startY, RED);

  // Line 2 (GREEN)
  printCentered("HELLO WORLD 2", startY + lineHeight + 10, GREEN);

  // Line 3 (BLUE)
  printCentered("HELLO WORLD 3", startY + (lineHeight + 10) * 2, BLUE);

  // CRT overlay
  drawScanlines();
}

/* ── Loop ─────────────── */
void loop() {
}

Demo 2

C/C++
VGA SHELL FALLOUT
#include "vga_graphics.h"

// ---------------- CONFIG ----------------
#define BORDER_THICKNESS 4
#define BORDER_MARGIN    14

// Fallout (big text)
#define BIG_CHAR_W 12
#define BIG_CHAR_H 16

// Linux (small text)
#define SMALL_CHAR_W 6
#define SMALL_CHAR_H 8

// Cursor
bool cursor_visible = true;
unsigned long last_blink = 0;

// ---------------- CURSORS ----------------
int big_col = 0;
int big_row = 0;

int small_col = 0;
int small_row = 18;   // start lower on screen

// ---------------- BORDER ----------------
void drawBorder() {
  for (int t = 0; t < BORDER_THICKNESS; t++) {
    drawHLine(t, t, screenWidth - 2*t, GREEN);
    drawHLine(t, screenHeight - 1 - t, screenWidth - 2*t, GREEN);
    drawVLine(t, t, screenHeight - 2*t, GREEN);
    drawVLine(screenWidth - 1 - t, t, screenHeight - 2*t, GREEN);
  }
}

// ---------------- TEXT HELPERS ----------------
void big_print(const char* s) {
  while (*s) {
    if (*s == '\n') {
      big_col = 0;
      big_row++;
    } else {
      drawChar(
        BORDER_MARGIN + big_col * BIG_CHAR_W,
        BORDER_MARGIN + big_row * BIG_CHAR_H,
        *s,
        GREEN,
        BLACK,
        2
      );
      big_col++;
    }
    s++;
  }
}

void small_print(const char* s) {
  while (*s) {
    if (*s == '\n') {
      small_col = 0;
      small_row++;
    } else {
      drawChar(
        BORDER_MARGIN + small_col * SMALL_CHAR_W,
        BORDER_MARGIN + small_row * SMALL_CHAR_H,
        *s,
        GREEN,
        BLACK,
        1
      );
      small_col++;
    }
    s++;
  }
}

// ---------------- BLINKING CURSOR ----------------
void draw_cursor() {
  int x = BORDER_MARGIN + big_col * BIG_CHAR_W;
  int y = BORDER_MARGIN + big_row * BIG_CHAR_H;

  if (cursor_visible) {
    fillRect(x, y + BIG_CHAR_H - 2, BIG_CHAR_W, 2, GREEN);
  } else {
    fillRect(x, y + BIG_CHAR_H - 2, BIG_CHAR_W, 2, BLACK);
  }
}

// ---------------- SETUP ----------------
void setup() {
  initVGA();
  clearScreen();
  drawBorder();

  // Fallout header (BIG)
  big_print("ROBCO INDUSTRIES UNIFIED OS\n");
  big_print("COPYRIGHT 2075-2077 ROBCO\n");
  big_print("- VAULT TERMINAL -\n\n");

  big_print("> INITIALIZING SYSTEM...\n");
  big_print("> MEMORY BANKS: OK\n");
  big_print("> VGA INTERFACE: OK\n");
  big_print("> RP2040 STATUS: NOMINAL\n\n");
  big_print("> ");

  // Linux / RPi info (SMALL)
  small_print("pico@rp2040:~$ neofetch\n\n");
  small_print("OS: Pico Linux (demo)\n");
  small_print("Host: Raspberry Pi Pico W\n");
  small_print("Kernel: 0.1-retro\n");
  small_print("Uptime: just now\n");
  small_print("Resolution: 640x480 VGA\n");
  small_print("Shell: bash (fake)\n");
}

// ---------------- LOOP ----------------
void loop() {
  unsigned long now = millis();
  if (now - last_blink > 500) {
    last_blink = now;
    cursor_visible = !cursor_visible;
    draw_cursor();
  }
}

Demo 3

C/C++
Snake Game
#include "vga_graphics.h"

/* ── Screen ───────────────── */
#define SW 640
#define SH 480

/* ── Grid ───────────────── */
#define CELL 12
#define GRID_W (SW / CELL)
#define GRID_H (SH / CELL)

/* ── Snake ─────────────── */
struct Segment {
  int x;
  int y;
};

Segment snake[200];
int snakeLength = 5;

int dx = 1, dy = 0;

int foodX, foodY;
int prevTailX, prevTailY;

/* ── Random food ───────── */
void placeFood() {
  foodX = random(2, GRID_W - 2);
  foodY = random(2, GRID_H - 2);
}

/* ── Reset ─────────────── */
void resetGame() {
  clearScreen();

  snakeLength = 5;
  dx = 1; dy = 0;

  for (int i = 0; i < snakeLength; i++) {
    snake[i].x = GRID_W / 2 - i;
    snake[i].y = GRID_H / 2;
  }

  placeFood();
}

/* ── Random movement ───── */
void randomMove() {
  if (random(0, 10) < 3) {  // 30% chance

    int dir = random(0, 4);

    if (dir == 0 && dy == 0) { dx = 0; dy = -1; } // up
    if (dir == 1 && dy == 0) { dx = 0; dy = 1; }  // down
    if (dir == 2 && dx == 0) { dx = -1; dy = 0; } // left
    if (dir == 3 && dx == 0) { dx = 1; dy = 0; }  // right
  }
}

/* ── Collision ─────────── */
bool selfCollision() {
  for (int i = 1; i < snakeLength; i++) {
    if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
      return true;
    }
  }
  return false;
}

/* ── Draw one cell ─────── */
void drawCell(int gx, int gy, char color) {
  fillRect(gx * CELL, gy * CELL, CELL - 1, CELL - 1, color);
}

/* ── Setup ─────────────── */
void setup() {
  initVGA();
  clearScreen();

  randomSeed(analogRead(26)); // Pico randomness

  resetGame();
}

/* ── Loop ─────────────── */
void loop() {

  // random AI movement
  randomMove();

  // store tail
  prevTailX = snake[snakeLength - 1].x;
  prevTailY = snake[snakeLength - 1].y;

  // move body
  for (int i = snakeLength - 1; i > 0; i--) {
    snake[i] = snake[i - 1];
  }

  // move head
  snake[0].x += dx;
  snake[0].y += dy;

  // wrap screen
  if (snake[0].x >= GRID_W) snake[0].x = 0;
  if (snake[0].x < 0) snake[0].x = GRID_W - 1;
  if (snake[0].y >= GRID_H) snake[0].y = 0;
  if (snake[0].y < 0) snake[0].y = GRID_H - 1;

  // self collision
  if (selfCollision()) {
    delay(500);
    resetGame();
    return;
  }

  // food
  if (snake[0].x == foodX && snake[0].y == foodY) {
    snakeLength++;
    placeFood();
  }

  // erase tail
  drawCell(prevTailX, prevTailY, BLACK);

  // draw snake
  for (int i = 0; i < snakeLength; i++) {
    drawCell(snake[i].x, snake[i].y, GREEN);
  }

  // draw food
  drawCell(foodX, foodY, WHITE);

  delay(120);
}

Demo 4

C/C++
#include "vga_graphics.h"

/* ── Screen ───────────────── */
#define SW 640
#define SH 480

/* ── Grid ───────────────── */
#define CELL 8
#define GRID_W (SW / CELL)
#define GRID_H (SH / CELL)

/* ── Buffers ───────────── */
bool grid[GRID_W][GRID_H];
bool nextGrid[GRID_W][GRID_H];

/* ── Draw cell ─────────── */
void drawCell(int x, int y, bool alive) {
  fillRect(
    x * CELL,
    y * CELL,
    CELL - 1,
    CELL - 1,
    alive ? YELLOW : BLACK
  );
}

/* ── Random init ───────── */
void randomizeGrid() {
  for (int x = 0; x < GRID_W; x++) {
    for (int y = 0; y < GRID_H; y++) {
      grid[x][y] = random(0, 2);
      drawCell(x, y, grid[x][y]);
    }
  }
}

/* ── Count neighbors ───── */
int countNeighbors(int x, int y) {
  int count = 0;

  for (int dx = -1; dx <= 1; dx++) {
    for (int dy = -1; dy <= 1; dy++) {
      if (dx == 0 && dy == 0) continue;

      int nx = x + dx;
      int ny = y + dy;

      // wrap around edges
      if (nx < 0) nx = GRID_W - 1;
      if (nx >= GRID_W) nx = 0;
      if (ny < 0) ny = GRID_H - 1;
      if (ny >= GRID_H) ny = 0;

      if (grid[nx][ny]) count++;
    }
  }

  return count;
}

/* ── Update simulation ─── */
void updateGrid() {

  for (int x = 0; x < GRID_W; x++) {
    for (int y = 0; y < GRID_H; y++) {

      int neighbors = countNeighbors(x, y);

      if (grid[x][y]) {
        // alive
        nextGrid[x][y] = (neighbors == 2 || neighbors == 3);
      } else {
        // dead
        nextGrid[x][y] = (neighbors == 3);
      }
    }
  }

  // apply + redraw only changes
  for (int x = 0; x < GRID_W; x++) {
    for (int y = 0; y < GRID_H; y++) {

      if (grid[x][y] != nextGrid[x][y]) {
        drawCell(x, y, nextGrid[x][y]);
      }

      grid[x][y] = nextGrid[x][y];
    }
  }
}

/* ── Setup ─────────────── */
void setup() {
  initVGA();
  clearScreen();

  randomSeed(analogRead(26)); // Pico randomness

  randomizeGrid();
}

/* ── Loop ─────────────── */
void loop() {
  updateGrid();
  delay(80);  // speed control
}

Credits

Arnov Sharma
377 projects • 397 followers
I'm Arnov. I build, design, and experiment with tech—3D printing, PCB design, and retro consoles are my jam.

Comments