Community project

20cm Soccer Drone

Photo of 20cm Soccer Drone
Generated with AI

Richard Wanet

Published November 14, 2026

This soccer scoreboard project turns an ESP32 microcontroller into a dedicated scoring display for 20cm drone soccer matches. The setup uses a small SSD1306 OLED screen to show real-time scores for two teams, with push buttons wired to increment points and reset the board.

The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for connecting the OLED display and control buttons to the ESP32. Builders will also receive the Arduino firmware that handles debounced button inputs, score tracking with a two-second reset hold, and formatted score display on the screen.

Wiring diagram

Wiring diagram for 20cm Soccer Drone
Loading diagram…

Gather all the parts

QtyComponent
1

SSD1306 OLED

0.96-inch 128 x 64 white monochrome OLED display module driven by an SSD1306 controller over I2C (address 0x3C). It runs from 3 V to 5 V and connects through a 4-pin 2.54 mm header (GND, VCC, SCL, SDA); the self-emitting pixels need no backlight.

1

Push Button

Momentary push button switch

1

Push Button

Momentary push button switch

1

Push Button

Momentary push button switch

1

Push Button

Momentary push button switch

1

Push Button

Momentary push button switch

Assemble it in 5 steps

1. Préparer le montage hors tension

Préparez un ESP32-DevKitC-32E, un écran OLED SSD1306 I2C 128 × 64 à quatre broches et cinq boutons poussoirs momentanés. Utilisez des fils de connexion et une plaque de montage sans soudure. Débranchez le câble USB avant le câblage.

2. Brancher le petit écran

VCC → 3V3 (alimentation), GND → GND (retour du courant), SDA → GPIO21 (données), SCL → GPIO22 (horloge de communication).

  • Vérifiez les inscriptions VCC et GND sur votre écran : leur position varie. Une inversion peut endommager le module.

3. Brancher les commandes des équipes

Chaque bouton relie son contact SIGNAL à une entrée et son autre contact à GND. Plus A → GPIO25, plus B → GPIO26, moins A → GPIO27, moins B → GPIO32. Les résistances internes de l’ESP32 sont activées par le programme.

  • Sur un bouton à quatre pattes, deux pattes sont déjà reliées entre elles. Choisissez deux contacts qui ne se rejoignent que lorsque vous appuyez.
  • Étiquetez les boutons A +, B +, A − et B −.

4. Ajouter la remise à zéro

Branchez SIGNAL du dernier bouton à GPIO33 et GND à GND. Étiquetez-le RAZ. Un maintien de deux secondes remet les deux scores à zéro.

5. Vérifier et protéger le tableau

Contrôlez tous les fils avant d’alimenter par USB. Fixez ensuite le montage dans un boîtier, hors de la zone de vol. L’écran de 0,96 pouce se lit de près : cette version sert à la table de marque, pas à l’affichage pour le public.

  • Les scores vont de 0 à 99. Une coupure d’alimentation remet les scores à zéro.

Review all connections

1. Connections between "oled" and "ESP32"

FunctionoledESP32
powerVCC3V3
groundGNDGND
i2cSDAGPIO 21
i2cSCLGPIO 22

2. Connections between "plusA" and "ESP32"

FunctionplusAESP32
digitalSIGNALGPIO 25
groundGNDGND

3. Connections between "plusB" and "ESP32"

FunctionplusBESP32
digitalSIGNALGPIO 26
groundGNDGND

4. Connections between "moinsA" and "ESP32"

FunctionmoinsAESP32
digitalSIGNALGPIO 27
groundGNDGND

5. Connections between "moinsB" and "ESP32"

FunctionmoinsBESP32
digitalSIGNALGPIO 32
groundGNDGND

6. Connections between "reset" and "ESP32"

FunctionresetESP32
digitalSIGNALGPIO 33
groundGNDGND

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 screen(128,64,&Wire,-1);
const uint8_t pins[] = {25,26,27,32,33};
bool raw[5]={}, stable[5]={};
unsigned long changed[5]={}, resetSince=0;
bool resetDone=false;
uint8_t score[2]={0,0};
void drawScore(){
 screen.clearDisplay(); screen.setTextColor(SSD1306_WHITE);
 screen.setTextSize(1); screen.setCursor(0,0); screen.print(F("EQUIPE A   EQUIPE B"));
 screen.setTextSize(3); screen.setCursor(3,20);
 if(score[0]<10) screen.print('0'); screen.print(score[0]);
 screen.setCursor(57,20); screen.print('-'); screen.setCursor(87,20);
 if(score[1]<10) screen.print('0'); screen.print(score[1]);
 screen.setTextSize(1); screen.setCursor(0,55); screen.print(F("RAZ : maintenir 2 s")); screen.display();
}
void setup(){
 Serial.begin(9600);
 for(uint8_t i=0;i<5;i++) pinMode(pins[i],INPUT_PULLUP);
 Wire.begin(21,22);
 delay(500);
 if(!screen.begin(SSD1306_SWITCHCAPVCC,0x3C)){Serial.println(F("Erreur ecran"));while(true) delay(100);}
 drawScore();
}
void loop(){
 unsigned long now=millis(); bool dirty=false;
 for(uint8_t i=0;i<5;i++){
  bool down=digitalRead(pins[i])==LOW;
  if(down!=raw[i]){raw[i]=down; changed[i]=now;}
  if(now-changed[i]>=35 && stable[i]!=raw[i]){
   stable[i]=raw[i];
   if(i==4){if(stable[i]){resetSince=now;resetDone=false;}}
   else if(stable[i] && !stable[4]){
    uint8_t team=i%2, before=score[team];
    if(i<2){if(score[team]<99)score[team]++;}
    else if(score[team]>0)score[team]--;
    dirty |= before!=score[team];
   }
  }
 }
 if(stable[4] && !resetDone && now-resetSince>=2000){
  dirty |= score[0]!=0 || score[1]!=0;
  score[0]=score[1]=0; resetDone=true;
 }
 if(dirty)drawScore();
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik