Built-in project · Arduino Uno R3

Scoreboard on an Arduino Uno

A MAX7219 eight-digit display driven with LedControl: home score, a ten-minute game clock and the guest score, with a button for each team and one for the clock. The whole build, an Arduino Uno and 4 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

IntermediateRuns in your browser. Free, and no account needed.

Scoreboard · Arduino Uno R3live0.000 s 0.00x
Hold the button
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Scoreboard: a MAX7219 eight-digit display as a game scoreboard, the home
// score on the left, a ten-minute game clock in the middle and the guest
// score on the right:
//
//     1 2  1 0.0 0  0 7
//     home  clock   guest
//
//   MAX7219 module   DIN -> 12   CLK -> 11   CS -> 10   VCC -> 5 V   GND -> GND
//   buttons          2: home point   3: guest point   4: start and stop the clock
//                    (each button to GND; the pins use their pull-ups)
//
// LedControl talks to the module: shutdown(0, false) wakes it, setDigit()
// writes one digit, and digit 0 is the rightmost. The point between minutes
// and seconds is the decimal point of the minutes digit. When the clock runs
// out the whole display flashes, by switching the chip's shutdown on and off.
//
// Hold the home or guest button for a second to take that team's point back.

#include "LedControl.h"

LedControl lc = LedControl(12, 11, 10, 1);  // DIN, CLK, CS, one module

const int HOME_BUTTON = 2;
const int GUEST_BUTTON = 3;
const int CLOCK_BUTTON = 4;
const uint16_t GAME_SECONDS = 10 * 60;

uint8_t home = 0;
uint8_t guest = 0;
uint16_t secondsLeft = GAME_SECONDS;
bool running = false;
unsigned long lastTick = 0;
uint32_t shown = 0xffffffff;

struct Button {
  int pin;
  bool down;
  unsigned long since;
  bool held;
};

Button buttons[3] = {
    {HOME_BUTTON, false, 0, false},
    {GUEST_BUTTON, false, 0, false},
    {CLOCK_BUTTON, false, 0, false},
};

// Two digits of a score at positions `right` and `right + 1`, no leading zero.
void showScore(uint8_t score, int right) {
  lc.setDigit(0, right, score % 10, false);
  if (score >= 10) {
    lc.setDigit(0, right + 1, score / 10, false);
  } else {
    lc.setChar(0, right + 1, ' ', false);
  }
}

void showClock() {
  uint8_t minutes = secondsLeft / 60;
  uint8_t seconds = secondsLeft % 60;
  lc.setDigit(0, 5, minutes / 10, false);
  lc.setDigit(0, 4, minutes % 10, true);
  lc.setDigit(0, 3, seconds / 10, false);
  lc.setDigit(0, 2, seconds % 10, false);
}

void show() {
  showScore(home, 6);
  showClock();
  showScore(guest, 0);
}

// What a button did since last time: 1 for a press, 2 for a hold of a second.
uint8_t check(Button &b) {
  bool down = digitalRead(b.pin) == LOW;
  unsigned long now = millis();
  uint8_t event = 0;
  if (down && !b.down) {
    b.since = now;
    b.held = false;
  } else if (down && !b.held && now - b.since >= 1000) {
    b.held = true;
    event = 2;
  } else if (!down && b.down && !b.held && now - b.since >= 20) {
    event = 1;
  }
  b.down = down;
  return event;
}

void setup() {
  for (int i = 0; i < 3; i++) pinMode(buttons[i].pin, INPUT_PULLUP);
  lc.shutdown(0, false);
  lc.setIntensity(0, 8);
  lc.clearDisplay(0);
  show();
}

void loop() {
  uint8_t e = check(buttons[0]);
  if (e == 1 && home < 99) home++;
  if (e == 2 && home > 0) home--;
  e = check(buttons[1]);
  if (e == 1 && guest < 99) guest++;
  if (e == 2 && guest > 0) guest--;
  if (check(buttons[2]) == 1) {
    if (secondsLeft == 0) {
      secondsLeft = GAME_SECONDS;  // a new period
      lc.shutdown(0, false);
    } else {
      running = !running;
    }
    lastTick = millis();
  }

  unsigned long now = millis();
  if (running && now - lastTick >= 1000) {
    lastTick += 1000;
    if (secondsLeft > 0) secondsLeft--;
    if (secondsLeft == 0) running = false;
  }

  // Only what changed goes out: eight digits are 128 clocks each time.
  uint32_t state = ((uint32_t)home << 24) | ((uint32_t)guest << 16) | secondsLeft;
  if (state != shown) {
    shown = state;
    show();
  }
  if (secondsLeft == 0) {
    // Time: flash the whole board, twice a second.
    lc.shutdown(0, (now / 250) % 2 == 0);
  }
  delay(10);
}

Parts list

6 parts, plus the jumper wires. Every one is in the editor's parts bin.

How it is wired

9 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.

  • Ground: Arduino Uno R3 pin GND; Eight-digit display, MAX7219 pin GND
  • Arduino Uno R3 pin 12; Eight-digit display, MAX7219 pin DIN
  • Arduino Uno R3 pin 11; Eight-digit display, MAX7219 pin CLK
  • Arduino Uno R3 pin 10; Eight-digit display, MAX7219 pin CS
  • Arduino Uno R3 pin 4; Pushbutton (3) pin 1a
  • Arduino Uno R3 pin 3; Pushbutton (2) pin 1a
  • Arduino Uno R3 pin 2; Pushbutton (1) pin 1a
  • Arduino Uno R3 pin 5V; Eight-digit display, MAX7219 pin VCC
  • Ground: Arduino Uno R3 pin GND; Pushbutton (1) pin 2a; Pushbutton (2) pin 2a; Pushbutton (3) pin 2a

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.