Jeu vidéo / Embarqué

LED Arcade

Un Pong maison sur matrice LED, joué sur enceinte de voiture

ArduinoC/C++LED MatrixEmbarquéAudioÉquipe

L'idée

Faire un Pong sans PC et sans écran classique. Juste un Arduino, une bande LED adressable repliée en matrice pour servir d’écran, et une vieille enceinte de voiture branchée sur une broche pour le son. Tout le code embarqué sur la carte, qui démarre toute seule une fois alimentée.

Pong se prête bien à l’exercice : deux raquettes, une balle, des collisions simples, une logique qui tient dans la mémoire d’un Arduino sans forcer. Le low-tech n’est pas un compromis sur le gameplay, c’est ce qui le rend possible.

On était trois sur le projet. J’ai pris toute la partie code Arduino, en particulier le morceau le plus tendu côté timing : faire cohabiter le rendu de l’image et la sortie audio sur le même cerveau.

Le matériel

Le côté low-tech était assumé dès le départ. Sans Raspberry Pi, sans carte son ni écran, on n’utilise que ce qu’on peut brancher directement sur un Arduino.

Le montage tient en trois éléments.

  • un Arduino comme cerveau, avec tout le code en C/C++ embarqué
  • une bande LED adressable repliée pour faire une matrice, c’est l’écran sur lequel on rend la balle, les raquettes et le score
  • une vieille enceinte de voiture pour la sortie audio, attaquée par un signal carré sur une broche

Mono-thread et interruptions

C’est le morceau qui m’a pris le plus de temps, à cause du conflit de timing entre la bande LED et l’audio. L’Arduino est mono-thread : tout ce qui doit tourner en parallèle de la boucle principale doit passer par une interruption matérielle.

Pour le rafraîchissement de l’image, j’ai fait simple. Dans la boucle principale, j’accumule le delta time dans une variable, et un if déclenche le refresh quand on atteint le seuil, avant de remettre la variable à zéro. Pas besoin d’interruption pour ça.

Pour l’audio, c’est différent. Chaque note est un signal carré à une fréquence précise sur la broche enceinte, et garder cette fréquence stable demande une interruption matérielle qui toggle le pin à intervalles réguliers, sans dépendre de ce que fait la boucle.

Le problème vient du protocole d’envoi des données aux bandes LED adressables, très sensible au timing. Si une interruption tombe au milieu de la trame, le signal est corrompu et les LEDs partent en sucette. Je désactive donc les interruptions pendant le refresh, je pousse les pixels, puis je réarme l’interruption audio sur la note suivante de la séquence.

Ça a trois conséquences.

  • la “musique” du jeu est découpée en notes, chacune jouant entre deux refresh de frame
  • le tempo est implicitement lié au framerate du jeu
  • pas de désynchro, le son redémarre juste après chaque refresh

Le rendu sonore n’a pas la richesse d’une vraie carte son, mais c’est exactement ce qu’on voulait : un grain de signal carré qui colle à l’esthétique LED, et un beep à chaque rebond de la balle qui passe juste.

Video game / Embedded

LED Arcade

A homemade Pong on an LED matrix, played through a car speaker

ArduinoC/C++LED MatrixEmbarquéAudioÉquipe

The idea

Build a Pong without a PC and without a regular screen. Just an Arduino, an addressable LED strip folded into a matrix as the screen, and an old car speaker wired to a pin for the sound. All the code lives on the board, which boots itself when powered on.

Pong suits this well: two paddles, a ball, simple collisions, logic that fits in an Arduino’s memory with room to spare. The low-tech setup isn’t a compromise on gameplay, it’s what makes it possible at all.

We were three on the project. I took on all the Arduino code, especially the part with the tightest timing: making image rendering and audio output coexist on the same chip.

The hardware

We went low-tech on purpose from the start. With no Raspberry Pi, sound card or display, we only use what plugs straight into an Arduino.

The setup has three parts.

  • an Arduino as the brain, with all the code in embedded C/C++
  • an addressable LED strip folded into a matrix as the screen, where the ball, paddles and score are rendered
  • an old car speaker for audio output, driven by a square wave on a single pin

Single-thread and interrupts

This is the piece that took me the most time, because of the timing conflict between the LED strip and the audio. The Arduino is single-threaded: anything that has to run in parallel with the main loop has to go through a hardware interrupt.

For the frame refresh, I kept it simple. In the main loop, I accumulate the delta time in a variable, and a single if triggers the refresh when the threshold is reached, then resets the variable. No interrupt needed for that.

Audio is a different story. Each note is a square wave at a precise frequency on the speaker pin, and keeping that frequency stable requires a hardware interrupt that toggles the pin at regular intervals, independently of what the main loop is doing.

The trouble comes from the LED data protocol, which is very timing-sensitive. If an interrupt fires in the middle of the data stream, the frame gets corrupted and the LEDs go haywire. So I disable interrupts during the refresh, push the pixels, then re-arm the audio interrupt on the next note of the sequence.

This has three consequences.

  • the game’s “music” is sliced into notes, each one playing between two frame refreshes
  • the tempo is implicitly tied to the framerate
  • no desync, the sound restarts right after each refresh

The audio output doesn’t have the richness of a real sound card, but that’s exactly what we wanted: a square-wave grit that matches the LED look, and a clean beep on every ball bounce.