Placar Inteligente de Basquete Avançado com Bluetooth, Sensores e Atualizações em Tempo Real
Componentes e Materiais
- 1x Arduino Nano R3
- 1x Módulo Bluetooth HC-05
- 1x Sensor de proximidade infravermelho E18-D80NK
- 1x Sensor de vibração Seeed Grove SW-420
- 1x Sensor de temperatura e umidade DHT22
- 1x LED RGB 8 mm
- Resistores: 3x 10k Ω, 3x 100 Ω, 1x 47 Ω, 1x 1k Ω, 1x 2.21k Ω
- 1x Bateria 9 V
Software e Aplicativos
- Arduino IDE
- MIT App Inventor (para app Android)
Sobre o Projeto
Faço treinos de basquete regularmente e sempre acompanho as estatísticas (arremessos, acertos, erros). Registrar esses números manualmente é entediante e difícil. Bolas "inteligentes" comerciais existem, mas têm desvantagens:
- Se quiser treinar com várias bolas, precisa comprar várias (caras).
- A precisão não é tão boa.
- Durabilidade da bola comprometida.
Em um projeto anterior (protótipo), usei o dispositivo Avnet SmartEdge para testar um método de rastreamento: acelerômetro para detectar arremessos e sensor de proximidade para detectar acertos. Agora, o objetivo é implementar uma solução definitiva para placar de basquete.
Versões
Comecei com componentes que tinha em casa e desenvolvi o software (Arduino e Android). A versão 1.1.0 funcionou bem, então fiz melhorias para usar componentes mais baratos e aprimorar o software. A versão 2.0.0 é ainda melhor.
Passo 1: Arduino
Hardware
Principais mudanças da v1.1.0 para v2.0.0:
- Arduino Mega 2560 → Arduino Nano R3
- Bluetooth Shield → Módulo HC-05
- Bateria Li-ion recarregável → Bateria 9 V
- Adicionado sensor DHT22 (temperatura e umidade)
Considerações sobre sensores:
- E18-D80NK: Sensor de proximidade IR que não sofre interferência de luz solar. Outros sensores podem ser usados, mas evite os sensíveis à luz ambiente.
- SW-420: Sensor de vibração que funciona muito bem. Outros também servem.
Os esquemáticos das duas versões estão na seção Esquemáticos.
Software (Arduino)
Estratégia de programação:
- Após inicialização (variáveis, LED, Bluetooth), monitora continuamente os sensores.
- Se o sensor de proximidade detecta a bola → arremesso = acerto.
- Se o sensor de vibração detecta movimento → arremesso ocorreu, mas aguarda até 2 s para decidir.
- Durante esses 2 s, se a proximidade detectar a bola → acerto imediato.
- Após 2 s sem detecção de proximidade → erro.
- O Arduino envia via Bluetooth ao Android o resultado (acerto/erro).
- Processo reinicia.
Mudanças v1.1.0 → v2.0.0:
- Suporte alterado de Bluetooth Shield para HC-05.
- Adicionado suporte ao DHT22.
- Lógica de detecção de arremessos, acertos e erros melhorada, com melhor sinalização via LED.
Os códigos das duas versões estão na seção Código.
Passo 2: Android (MIT App Inventor)
Estratégia do app:
- Após inicialização (contadores, Bluetooth), aguarda botão "Iniciar".
- Ao iniciar, monitora continuamente a conexão Bluetooth.
- Ao receber dados, atualiza o placar e toca som de notificação correto.
- Repete até botão "Pausar".
- Botões para selecionar dispositivo Bluetooth, zerar contadores e ajustes finos.
Mudanças v1.1.0 → v2.0.0:
- Layout alterado de retrato para paisagem.
- Adicionados: Luminosidade, Umidade, Temperatura e Índice de Calor.
- Botões de ajuste melhorados.
- Função "manter tela sempre ligada".
- Sons em todos os botões.
- Aviso a cada 50 arremessos (50, 100, 150…).


Passo 3: Montagem no Aro de Basquete
Utilizei o aro que uso regularmente nos treinos.

- Removi a tampa plástica sob o aro e fiz um furo para o sensor de proximidade.
- Fiz um pequeno furo para fixar o sensor de vibração.
- Como a tampa é curva, construí um suporte em MDF para o sensor de vibração.
- Fixei os sensores com parafusos e porcas.
- Conectei os demais componentes.
- Teste de fumaça (power-up).
- Instalação final no aro.














Passo 4: Teste no Treino
Hora de testar tudo na prática. O resultado final... é showtime!
Passo 5: Considerações Finais
- O sistema mostrou-se muito preciso, com poucos falsos positivos e raros falsos negativos.
- É fantástico ter as estatísticas do treino em tempo real e após o treino.
Divirta-se!
Códigos
Arduino v1.1.0
//----------------------------------------------------------------------------//
// Filename : Scoreboard.ino
// Description : Smart Basketball Scoreboard
// Version : 1.1.0
// Author : Marcelo Avila de Oliveira <marceloavilaoliveira@gmail.com>
//----------------------------------------------------------------------------//
// DEFINIÇÕES
// #define DEBUG
// #define DEBUG_PROX
// #define DEBUG_VIBR
//----------------------------------------------------------------------------//
// CONSTANTES
// PINOS
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;
// TEMPO
const unsigned long wait_interval = 3000;
// MATEMÁTICA
const float percent_to_bright_factor = 100 * log10(2) / log10(255);
//----------------------------------------------------------------------------//
// VARIÁVEIS
unsigned long wait_time;
boolean prox = false;
boolean vibr = false;
boolean wait = false;
//----------------------------------------------------------------------------//
// FUNÇÕES (CONFIGURAÇÃO)
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); // Magenta
Serial.begin(9600);
setup_bluetooth();
set_led(4, 100); // Azul
#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"); // Modo slave
Serial1.print("\r\n+STNA=Arduino\r\n"); // Nome
Serial1.print("\r\n+STOAUT=1\r\n"); // Permitir pareamento
Serial1.print("\r\n+STAUTO=0\r\n"); // Auto-conexão desligada
delay(2000);
Serial1.print("\r\n+INQ=1\r\n"); // Tornar inquirível
delay(2000);
while (Serial1.available()) {
delay(50);
Serial1.read();
}
}
//----------------------------------------------------------------------------//
// FUNÇÕES (LED)
int percent_to_bright(int percent) {
return 256 - pow(2, percent / percent_to_bright_factor);
}
void set_led(int color, int bright) {
// CORES: 0=Verde, 1=Amarelo, 2=Vermelho, 3=Ciano, 4=Azul, 5=Magenta, 6=Branco
// BRILHO: 0=Desligado .. 100=Máximo
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; // Verde
case 1: led_r_bright = bright_aux; led_g_bright = bright_aux; break; // Amarelo
case 2: led_r_bright = bright_aux; break; // Vermelho
case 3: led_g_bright = bright_aux; led_b_bright = bright_aux; break; // Ciano
case 4: led_b_bright = bright_aux; break; // Azul
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; // Branco
}
analogWrite(led_r_pin, led_r_bright);
analogWrite(led_g_pin, led_g_bright);
analogWrite(led_b_pin, led_b_bright);
}
//----------------------------------------------------------------------------//
// FUNÇÕES (VERIFICAÇÃO)
void check_prox() {
if (!prox && digitalRead(prox_pin) == LOW) {
#ifdef DEBUG_PROX
Serial.println("Proximity detected");
#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");
#endif
vibr = true;
wait = true;
wait_time = millis() + wait_interval;
set_led(1, 100); // Amarelo
}
}
void check_wait() {
if (wait && millis() > wait_time) {
if (!prox) set_shot(0);
reset();
}
}
//----------------------------------------------------------------------------//
// FUNÇÕES (DIVERSAS)
void set_shot(int mode) {
// 0 = Erro, 1 = Acerto
if (mode == 0) set_led(2, 100); // Vermelho
else set_led(0, 100); // Verde
Serial1.print(mode);
delay(1000);
}
void reset() {
vibr = false; prox = false; wait = false;
set_led(4, 100); // Azul
}
//----------------------------------------------------------------------------//
// MAIN
void loop() {
check_prox();
check_vibr();
check_wait();
}
Android v1.1.0
Desenvolvido no MIT App Inventor (https://ai2.appinventor.mit.edu/). Código disponível apenas para download.
Arduino v2.0.0
//----------------------------------------------------------------------------//
// Filename : Scoreboard.ino
// Description : Smart Basketball Scoreboard
// Version : 2.0.0
// Author : Marcelo Avila de Oliveira <marceloavilaoliveira@gmail.com>
//----------------------------------------------------------------------------//
// BIBLIOTECAS
#include "DHT.h"
#include "SoftwareSerial.h"
//----------------------------------------------------------------------------//
// DEFINIÇÕES
// #define DEBUG
// #define DEBUG_BLUE
// #define DEBUG_PROX
// #define DEBUG_VIBR
// #define DEBUG_DHT
// DHT SENSOR
#define DHTPIN 7
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// BLUETOOTH (RX, TX)
SoftwareSerial Bluetooth(10, 11);
//----------------------------------------------------------------------------//
// CONSTANTES
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);
//----------------------------------------------------------------------------//
// VARIÁVEIS
unsigned long wait_time, dht_time;
float humi, temp, heat;
boolean prox = false, vibr = false, wait = false;
//----------------------------------------------------------------------------//
// SETUP
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); // Magenta
Serial.begin(9600);
setup_bluetooth();
dht.begin();
set_led(4, 100); // Azul
#ifdef DEBUG
Serial.println("Board is alive");
#endif
}
void setup_bluetooth() {
#ifdef DEBUG_BLUE
Serial.println("Setting Bluetooth");
#endif
Bluetooth.begin(9600);
while (Bluetooth.available()) { delay(50); Bluetooth.read(); }
}
//----------------------------------------------------------------------------//
// LED
int percent_to_bright(int percent) {
return 256 - pow(2, percent / percent_to_bright_factor);
}
void set_led(int color, int bright) {
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);
}
//----------------------------------------------------------------------------//
// VERIFICAÇÕES
void check_prox() {
if (!prox && digitalRead(prox_pin) == LOW) {
#ifdef DEBUG_PROX
Serial.println("Proximity detected");
#endif
set_led(0, 100); // Verde
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");
#endif
set_led(1, 100); // Amarelo
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); } // Vermelho = erro
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("Heat Index: "); Serial.print(heat); Serial.println("C");
#endif
send_data(0);
dht_time = millis() + dht_interval;
}
}
//----------------------------------------------------------------------------//
// COMUNICAÇÃO
void send_data(int shot) {
// 0 = sem dado, 1 = erro, 2 = acerto
Bluetooth.print(humi);
Bluetooth.print(temp);
Bluetooth.print(heat);
Bluetooth.print(shot);
#ifdef DEBUG_BLUE
Serial.println("Bluetooth sent");
#endif
}
void reset() {
vibr = false; prox = false; wait = false;
delay(1000);
set_led(4, 100); // Azul
}
//----------------------------------------------------------------------------//
// LOOP
void loop() {
check_prox();
check_vibr();
check_wait();
check_dht();
}
Android v2.0.0
Desenvolvido no MIT App Inventor. Código disponível apenas para download.
Esquemáticos


Processo de manufatura
- Basquete: Da Invenção de Naismith em 1891 às Técnicas Modernas de Produção
- Construindo Sistemas de Smart Farming Open-Source: Capacitando Agricultores com IoT Acessível e Interoperável
- ECM3532: Primeira Placa de Sensor IA Integrada Ultra-Baixo Consumo da Eta Compute Acelera Desenvolvimento Edge
- Construa Monitor de Temperatura Doméstico com Raspberry Pi, MCP9808, InfluxDB e Grafana
- Lixeira Inteligente: Solução IoT Celular para Gestão Automatizada de Resíduos
- Bartender Inteligente: Construa sua Própria Estação de Coquetéis com Raspberry Pi
- Sensor de Emoção & EEG: Hardware de Baixo Custo para Detecção de Emoções em Tempo Real
- Sensor de Deformação Ultra-Sensível e Durável Revoluciona Têxteis Inteligentes
- Sensor Ultra-Fino Revolucionário Melhora Funcionalidade de Lentes de Contato Inteligentes
- Revolucionando Produção de Sensores: Sensata Usa Mecatrônica Inteligente