Tableau de score basketball intelligent avancé avec Bluetooth, capteurs et mises à jour temps réel
Composants et fournitures
- Arduino Nano R3
- Module Bluetooth HC-05
- Capteur de proximité infrarouge E18-D80NK
- Capteur de vibration Seeed Grove (SW-420)
- Capteur de température et humidité DHT22
- LED RGB 8 mm
- Résistances : 3× 10 kΩ, 3× 100 Ω, 1× 47 Ω, 1× 1 kΩ, 1× 2,21 kΩ
- Pile 9 V
Applications et services en ligne
- Arduino IDE
- MIT App Inventor (Android IDE)
À propos du projet
Je m'entraîne régulièrement au basketball et je suivais toujours les statistiques de mes séances (nombre de tirs, paniers, ratés, etc.). Suivre ces chiffres est fastidieux et difficile. Les ballons « intelligents » existent mais présentent des inconvénients :
- Il faut acheter plusieurs ballons coûteux pour s'entraîner avec plusieurs balles.
- La précision n'est pas optimale.
- La durabilité du ballon laisse à désirer.
Dans mon projet précédent (prototype de tableau de score intelligent), j'utilisais l'appareil Avnet SmartEdge pour tester une méthode de suivi. J'utilisais le capteur d'accélération pour détecter les tirs et le capteur de proximité pour détecter les paniers. L'idée de ce projet est d'implémenter une solution définitive pour le tableau de score basketball.
Versions
Au début, j'ai utilisé les composants matériels dont je disposais et développé les logiciels (Arduino et Android) nécessaires. Cette première version (1.1.0) fonctionnait bien, alors j'ai décidé d'apporter des modifications pour utiliser des composants moins chers et améliorer le logiciel. La deuxième version (2.0.0) est encore meilleure.
Étape 1 : Arduino
Matériel
La liste des composants pour la version 2.0.0 se trouve dans la section « Composants ».
Principales modifications de la v1.1.0 vers la v2.0.0 :
- Arduino Mega 2560 → Arduino Nano R3
- Bluetooth Shield → Module Bluetooth HC-05
- Alimentation Li-ion rechargeable → Pile 9 V
- Ajout du capteur DHT22 (température et humidité)
Considérations sur d'autres composants :
- Capteur de proximité E18-D80NK : D'autres capteurs de proximité peuvent convenir, mais privilégiez-en un qui ne souffre pas des interférences de la lumière du soleil, comme celui-ci.
- Capteur de vibration SW420 : D'autres capteurs de vibration peuvent fonctionner, celui-ci donne d'excellents résultats.
Les schémas Arduino pour les deux versions sont disponibles dans la section « Schémas ».
Logiciel
J'ai utilisé l'Arduino IDE pour développer le code, avec la stratégie suivante :
- Après l'initialisation (variables, LED, Bluetooth, etc.), surveillance continue de l'état des capteurs.
- Si le capteur de proximité détecte la balle → un tir vient d'avoir lieu et c'est un panier.
- Si le capteur de vibration détecte un mouvement → un tir vient d'avoir lieu, mais on attend 2 secondes (maximum) pour décider.
- Pendant ce temps, si le capteur de proximité détecte la balle → c'est immédiatement un panier.
- À la fin des 2 secondes, si le capteur de proximité n'a rien détecté → c'est un raté.
- L'Arduino informe l'application Android via Bluetooth qu'un tir (panier ou raté) vient d'avoir lieu.
- Le processus recommence.
Principales modifications logicielles v1.1.0 → v2.0.0 :
- Support du module HC-05 au lieu du shield Bluetooth
- Ajout du support du capteur DHT22
- Amélioration de la logique de détection des tirs, paniers, ratés et de la signalisation par LED.
Les codes Arduino pour les deux versions sont dans la section « Code ».
Étape 2 : Android
J'ai utilisé MIT App Inventor pour développer l'application Android, avec cette stratégie :
- Après l'initialisation (tirs, paniers, ratés, Bluetooth, etc.), attente de l'appui sur le bouton « Start ».
- Une fois « Start » pressé, surveillance continue de la connexion Bluetooth.
- À chaque réception de données, mise à jour du tableau et lecture du son de notification approprié.
- Le processus se répète jusqu'à l'appui sur « Pause ».
- Boutons pour sélectionner le périphérique Bluetooth, réinitialiser les compteurs, et boutons supplémentaires pour ajuster l'affichage si nécessaire.
Principales modifications v1.1.0 → v2.0.0 :
- Passage du mode portrait au mode paysage
- Ajout de la luminosité, humidité, température et indice de chaleur
- Amélioration des boutons d'ajustement
- Fonction maintien de l'écran allumé
- Sons sur tous les boutons
- Fonctionnalité d'alerte à chaque multiple de 50 tirs (50, 100, 150…)


Les codes Android pour les deux versions sont dans la section « Code ».
Étape 3 : Installation sur le panneau de basketball
Voici le panneau de basketball original que j'utilise pour mes entraînements.

J'ai d'abord retiré le cache plastique sous le cercle et percé un trou pour le capteur de proximité.

Puis j'ai fait un petit trou pour fixer le capteur de vibration.

Je n'ai pas pu fixer le capteur directement sur le cache plastique à cause de sa courbure, j'ai donc construit un support en MDF.



J'ai fixé les capteurs de proximité et de vibration avec des boulons et écrous.



Puis j'ai connecté les autres composants.



Test de fonctionnement (smoke test).

Installation finale sur le panneau de basketball.


Étape 4 : Test d'entraînement basketball
Maintenant, place au test complet.
Et le grand final… c'est l'heure du spectacle !
Étape 5 : Considérations finales
- Le système s'est avéré très précis, avec très peu de faux positifs et de rares faux négatifs.
- C'est fantastique de jouer avec le système pour connaître les statistiques d'entraînement en temps réel et après la séance.
Amusez-vous bien !
Code
Code Arduino (v1.1.0)
//----------------------------------------------------------------------------//
// Filename : Scoreboard.ino
// Description : Smart Basketball Scoreboard
// Version : 1.1.0
// Author : Marcelo Avila de Oliveira
//----------------------------------------------------------------------------//
// DEFINITIONS
//----------------------------------------------------------------------------//
// #define DEBUG
// #define DEBUG_PROX
// #define DEBUG_VIBR
//----------------------------------------------------------------------------//
// CONSTANTS
//----------------------------------------------------------------------------//
// PINS
const int prox_pin = 2;
const int vibr_pin = 3;
const int led_r_pin = 4;
const int led_g_pin = 5;
const int led_b_pin = 6;
// TIME
const unsigned long wait_interval = 3000;
// MATH
const float percent_to_bright_factor = 100 * log10(2) / log10(255);
//----------------------------------------------------------------------------//
// VARIABLES
//----------------------------------------------------------------------------//
// TIME
unsigned long wait_time;
// STATUS
boolean prox = false;
boolean vibr = false;
boolean wait = false;
//----------------------------------------------------------------------------//
// FUNCTIONS (SETTINGS)
//----------------------------------------------------------------------------//
void setup() {
pinMode(prox_pin, INPUT);
pinMode(vibr_pin, INPUT);
pinMode(led_r_pin, OUTPUT);
pinMode(led_g_pin, OUTPUT);
pinMode(led_b_pin, OUTPUT);
set_led(5, 100);
Serial.begin(9600);
setup_bluetooth();
set_led(4, 100);
#ifdef DEBUG
Serial.println("Board is alive");
Serial.println();
#endif
}
void setup_bluetooth() {
#ifdef DEBUG
Serial.println("Setting Bluetooth");
Serial.println();
#endif
Serial1.begin(38400);
Serial1.print("\r\n+STWMOD=0\r\n");
Serial1.print("\r\n+STNA=Arduino\r\n");
Serial1.print("\r\n+STOAUT=1\r\n");
Serial1.print("\r\n+STAUTO=0\r\n");
delay(2000);
Serial1.print("\r\n+INQ=1\r\n");
delay(2000);
while (Serial1.available()) {
delay(50);
Serial1.read();
}
}
//----------------------------------------------------------------------------//
// FUNCTIONS (LIGHT)
//----------------------------------------------------------------------------//
int percent_to_bright(int percent) {
return 256 - pow(2, percent / percent_to_bright_factor);
}
void set_led(int color, int bright) {
#ifdef DEBUG
Serial.println("Setting LED");
Serial.println();
#endif
if (color < 0 || color > 6 || bright < 0 || bright > 100) return;
int led_r_bright = 255, led_g_bright = 255, led_b_bright = 255;
int bright_aux = percent_to_bright(bright);
switch (color) {
case 0: led_g_bright = bright_aux; break; // GREEN
case 1: led_r_bright = bright_aux; led_g_bright = bright_aux; break; // YELLOW
case 2: led_r_bright = bright_aux; break; // RED
case 3: led_g_bright = bright_aux; led_b_bright = bright_aux; break; // CYAN
case 4: led_b_bright = bright_aux; break; // BLUE
case 5: led_r_bright = bright_aux; led_b_bright = bright_aux; break; // MAGENTA
case 6: led_r_bright = bright_aux; led_g_bright = bright_aux; led_b_bright = bright_aux; break; // WHITE
}
analogWrite(led_r_pin, led_r_bright);
analogWrite(led_g_pin, led_g_bright);
analogWrite(led_b_pin, led_b_bright);
}
//----------------------------------------------------------------------------//
// FUNCTIONS (CHECK)
//----------------------------------------------------------------------------//
void check_prox() {
if (!prox && digitalRead(prox_pin) == LOW) {
#ifdef DEBUG_PROX
Serial.println("Proximity detected");
Serial.println();
#endif
prox = true;
if (!vibr) { wait = true; wait_time = millis() + wait_interval; }
set_shot(1);
}
}
void check_vibr() {
if (!prox && !vibr && digitalRead(vibr_pin) == HIGH) {
#ifdef DEBUG_VIBR
Serial.println("Vibration detected");
Serial.println();
#endif
vibr = true;
wait = true;
wait_time = millis() + wait_interval;
set_led(1, 100);
}
}
void check_wait() {
if (wait && millis() > wait_time) {
if (!prox) set_shot(0);
reset();
}
}
//----------------------------------------------------------------------------//
// FUNCTIONS (MIS)
//----------------------------------------------------------------------------//
void set_shot(int mode) {
if (mode == 0) set_led(2, 100); else set_led(0, 100);
Serial1.print(mode);
delay(1000);
}
void reset() {
vibr = false; prox = false; wait = false;
set_led(4, 100);
}
//----------------------------------------------------------------------------//
// MAIN
//----------------------------------------------------------------------------//
void loop() {
check_prox(); check_vibr(); check_wait();
}
Code Android (v1.1.0)
Android MIT App Inventor (https://ai2.appinventor.mit.edu/)
No preview (download only).
Code Arduino (v2.0.0)
//----------------------------------------------------------------------------//
// Filename : Scoreboard.ino
// Description : Smart Basketball Scoreboard
// Version : 2.0.0
// Author : Marcelo Avila de Oliveira
//----------------------------------------------------------------------------//
// LIBRARIES
//----------------------------------------------------------------------------//
#include "DHT.h"
#include "SoftwareSerial.h"
//----------------------------------------------------------------------------//
// DEFINITIONS
//----------------------------------------------------------------------------//
#define DHTPIN 7
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
SoftwareSerial Bluetooth(10, 11); // RX, TX
//----------------------------------------------------------------------------//
// CONSTANTS
//----------------------------------------------------------------------------//
const int prox_pin = 2;
const int vibr_pin = 3;
const int led_r_pin = 4;
const int led_g_pin = 5;
const int led_b_pin = 6;
const unsigned long wait_interval = 2000;
const unsigned long dht_interval = 10000;
const float percent_to_bright_factor = 100 * log10(2) / log10(255);
//----------------------------------------------------------------------------//
// VARIABLES
//----------------------------------------------------------------------------//
unsigned long wait_time, dht_time;
float humi, temp, heat;
boolean prox = false, vibr = false, wait = false;
//----------------------------------------------------------------------------//
// FUNCTIONS (SETTINGS)
//----------------------------------------------------------------------------//
void setup() {
pinMode(prox_pin, INPUT);
pinMode(vibr_pin, INPUT);
pinMode(led_r_pin, OUTPUT);
pinMode(led_g_pin, OUTPUT);
pinMode(led_b_pin, OUTPUT);
set_led(5, 100);
Serial.begin(9600);
setup_bluetooth();
dht.begin();
set_led(4, 100);
#ifdef DEBUG
Serial.println("Board is alive");
Serial.println();
#endif
}
void setup_bluetooth() {
#ifdef DEBUG_BLUE
Serial.println("Setting Bluetooth");
Serial.println();
#endif
Bluetooth.begin(9600);
while (Bluetooth.available()) { delay(50); Bluetooth.read(); }
}
//----------------------------------------------------------------------------//
// FUNCTIONS (LIGHT)
//----------------------------------------------------------------------------//
int percent_to_bright(int percent) {
return 256 - pow(2, percent / percent_to_bright_factor);
}
void set_led(int color, int bright) {
#ifdef DEBUG
Serial.println("Setting LED");
Serial.println();
#endif
if (color < 0 || color > 6 || bright < 0 || bright > 100) return;
int led_r_bright = 255, led_g_bright = 255, led_b_bright = 255;
int bright_aux = percent_to_bright(bright);
switch (color) {
case 0: led_g_bright = bright_aux; break;
case 1: led_r_bright = bright_aux; led_g_bright = bright_aux; break;
case 2: led_r_bright = bright_aux; break;
case 3: led_g_bright = bright_aux; led_b_bright = bright_aux; break;
case 4: led_b_bright = bright_aux; break;
case 5: led_r_bright = bright_aux; led_b_bright = bright_aux; break;
case 6: led_r_bright = bright_aux; led_g_bright = bright_aux; led_b_bright = bright_aux; break;
}
analogWrite(led_r_pin, led_r_bright);
analogWrite(led_g_pin, led_g_bright);
analogWrite(led_b_pin, led_b_bright);
}
//----------------------------------------------------------------------------//
// FUNCTIONS (CHECK)
//----------------------------------------------------------------------------//
void check_prox() {
if (!prox && digitalRead(prox_pin) == LOW) {
#ifdef DEBUG_PROX
Serial.println("Proximity detected");
Serial.println();
#endif
set_led(0, 100);
send_data(2);
prox = true;
if (!vibr) { wait = true; wait_time = millis() + wait_interval; }
}
}
void check_vibr() {
if (!prox && !vibr && digitalRead(vibr_pin) == HIGH) {
#ifdef DEBUG_VIBR
Serial.println("Vibration detected");
Serial.println();
#endif
set_led(1, 100);
vibr = true;
wait = true;
wait_time = millis() + wait_interval;
}
}
void check_wait() {
if (wait && millis() > wait_time) {
if (!prox) { set_led(2, 100); send_data(1); }
reset();
}
}
void check_dht() {
if (!prox && !vibr && millis() > dht_time) {
humi = dht.readHumidity();
temp = dht.readTemperature();
heat = dht.computeHeatIndex(temp, humi, false);
#ifdef DEBUG_DHT
Serial.print("Humidity : "); Serial.print(humi); Serial.println("%");
Serial.print("Temperature: "); Serial.print(temp); Serial.println("C");
Serial.print("Head Index : "); Serial.print(heat); Serial.println("C");
Serial.println("");
#endif
send_data(0);
dht_time = millis() + dht_interval;
}
}
//----------------------------------------------------------------------------//
// FUNCTIONS (MIS)
//----------------------------------------------------------------------------//
void send_data(int shot) {
Bluetooth.print(humi);
Bluetooth.print(temp);
Bluetooth.print(heat);
Bluetooth.print(shot);
#ifdef DEBUG_BLUE
Serial.println("Bluetooth sent");
Serial.println();
#endif
}
void reset() {
vibr = false; prox = false; wait = false;
delay(1000);
set_led(4, 100);
}
//----------------------------------------------------------------------------//
// MAIN
//----------------------------------------------------------------------------//
void loop() {
check_prox(); check_vibr(); check_wait(); check_dht();
}
Code Android (v2.0.0)
Android MIT App Inventor (https://ai2.appinventor.mit.edu/)
No preview (download only).
Schémas


Processus de fabrication
- Basket-ball : de l'invention de Naismith en 1891 aux techniques de production modernes
- Construire des systèmes d'agriculture intelligente open-source : donner aux agriculteurs les moyens d'une IoT abordable et interopérable
- ECM3532 : La première carte capteur IA intégrée ultra-basse consommation d'Eta Compute accélère le développement edge
- Construire un moniteur de température domestique avec Raspberry Pi, MCP9808, InfluxDB et Grafana
- Poubelle intelligente : Une solution IoT cellulaire pour la gestion automatisée des déchets
- Barman intelligent : Construisez votre propre station de cocktails alimentée par Raspberry Pi
- Capteur d'émotion & EEG : Matériel low-cost pour détection d'émotion en temps réel
- Capteur de déformation ultra-sensible et durable pour textiles intelligents
- Capteur ultra-fin révolutionnaire améliore la fonctionnalité des lentilles de contact intelligentes