Robótica com C — Bloco 3: Projetos Práticos
Uma campainha que, quando alguém pressiona o botão na porta, toca um buzzer e envia uma notificação instantânea para o celular via Bluetooth — mesmo que você esteja em outro cômodo sem ouvir o som. O celular pode ainda responder comandos de volta: silenciar o buzzer, verificar quantas vezes tocaram, ou resetar o contador.
O padrão novo deste projeto: evento físico → notificação remota. Nos projetos anteriores o celular enviava comandos para o Arduino. Aqui a direção principal se inverte — o Arduino detecta um evento (botão pressionado) e empurra uma notificação para o celular sem que o usuário pedisse. É o padrão base de sistemas de alarme, monitoramento de presença e IoT em geral.
O buzzer (piezoelétrico) converte sinais elétricos em som. Existem dois tipos:
| Tipo | Funcionamento | Como controlar | Uso neste projeto |
|---|---|---|---|
| Buzzer ativo | Tem oscilador interno — emite som fixo quando recebe 5V | digitalWrite(pino, HIGH) |
✅ Mais simples — liga e desliga |
| Buzzer passivo | Sem oscilador — precisa de sinal PWM com frequência | tone(pino, freq) / noTone(pino) |
✅ Permite melodias e diferentes tons |
Este projeto usa o buzzer ativo. Para adicionar uma melodia de campainha, basta trocar por buzzer passivo e usar tone() — a lógica do projeto não muda.
Como identificar: buzzers ativos geralmente têm uma etiqueta adesiva cobrindo a abertura e são polarizados (perna longa = positivo). Buzzers passivos costumam ter a abertura visível. Na dúvida, aplique 5V direto — o ativo toca imediatamente, o passivo fica silencioso.
Corrente do buzzer: buzzers ativos consomem tipicamente 30–40 mA — próximo do limite de 40 mA por pino do Arduino. Para uso prolongado em hardware físico, intercale um transistor NPN (BC547) entre o pino e o buzzer. Para testes e simulações no Wokwi, a ligação direta funciona.
Tudo simulável exceto o Bluetooth. O Wokwi tem buzzer e botão como componentes nativos — você vai ouvir o som do buzzer durante a simulação (ou ver o indicador visual). Use o Monitor Serial no lugar do celular para enviar comandos e ver as notificações.
Roteiro de simulação:
RING:#1 aparecer no Monitor Serial e o buzzer soar"S" para silenciar; "M" para ativar o modo mudo| Componente | Conexão | Observação |
|---|---|---|
| Botão da campainha | Pino 2 → botão → GND | INPUT_PULLUP — LOW quando pressionado |
| Buzzer ativo (+) | Pino 9 | HIGH = toca, LOW = silencia |
| LED notificação + 220Ω | Pino 8 → R → LED → GND | Pisca ao detectar toque |
| HC-05 TX → pino 10 | Direto | Recebe comandos do celular |
| HC-05 RX ← pino 11 | Via divisor R1=1kΩ, R2=2kΩ | Envia notificações ao celular |
| Evento | Mensagem enviada ao celular |
|---|---|
| Botão pressionado | RING:#n — n = número do toque |
| Timer do buzzer expirou | BUZZER:OFF |
| Sistema iniciado | ONLINE:campainha |
| Comando | Ação | Resposta |
|---|---|---|
S |
Silencia o buzzer imediatamente | BUZZER:OFF |
M |
Toggle modo mudo | MUDO:ON ou MUDO:OFF |
C |
Consulta o contador de toques | TOQUES:n |
Z |
Zera o contador | TOQUES:0 |
T5 |
Define duração do buzzer (1–60s) | DURACAO:5 |
? |
Ajuda | Lista de comandos |
/* * ===================================================== * Projeto 05 — Campainha inteligente * Botão + Buzzer + Bluetooth HC-05 * ===================================================== * Evento: botão → buzzer toca + notificação ao celular * Comandos: S M C Z T1-60 ? * ===================================================== */ #include <SoftwareSerial.h> // ── Bluetooth ────────────────────────────────────── SoftwareSerial bluetooth(10, 11); // ── Pinos ────────────────────────────────────────── const int PINO_BOTAO = 2; const int PINO_LED = 8; const int PINO_BUZ = 9; // ── Estado do buzzer ─────────────────────────────── bool buzzerAtivo = false; bool modoMudo = false; unsigned long buzzerFim = 0; unsigned long duracaoBuzzer = 5000; // 5s padrão // ── Contador e debounce ──────────────────────────── unsigned int contadorToques = 0; int estadoAnterior = HIGH; unsigned long ultimoBounce = 0; const unsigned long DEBOUNCE = 50; // ── LED piscante não bloqueante ──────────────────── unsigned long ultimoPiscada = 0; int piscarRestante = 0; // ════════════════════════════════════════════════════ // Buzzer // ════════════════════════════════════════════════════ void tocarBuzzer() { if (modoMudo) return; buzzerAtivo = true; buzzerFim = millis() + duracaoBuzzer; digitalWrite(PINO_BUZ, HIGH); } void silenciarBuzzer() { buzzerAtivo = false; digitalWrite(PINO_BUZ, LOW); bluetooth.println("BUZZER:OFF"); Serial.println("BUZZER:OFF"); } void verificarBuzzer() { if (buzzerAtivo && millis() >= buzzerFim) silenciarBuzzer(); } // ════════════════════════════════════════════════════ // LED pisca n vezes sem bloquear o loop() // ════════════════════════════════════════════════════ void iniciarPiscar(int n) { piscarRestante = n * 2; // cada piscada = ligar + apagar ultimoPiscada = millis(); } void processarPiscar() { if (piscarRestante <= 0) { digitalWrite(PINO_LED, LOW); return; } if (millis() - ultimoPiscada >= 150) { ultimoPiscada = millis(); digitalWrite(PINO_LED, piscarRestante % 2 == 0 ? HIGH : LOW); piscarRestante--; } } // ════════════════════════════════════════════════════ // Botão com debounce — detecta borda de descida // ════════════════════════════════════════════════════ void verificarBotao() { int leitura = digitalRead(PINO_BOTAO); if (leitura != estadoAnterior) ultimoBounce = millis(); if ((millis() - ultimoBounce) > DEBOUNCE) { // Borda de descida: botão acabou de ser pressionado if (leitura == LOW && estadoAnterior == HIGH) { contadorToques++; bluetooth.print("RING:#"); bluetooth.println(contadorToques); Serial.print("RING:#"); Serial.println(contadorToques); tocarBuzzer(); iniciarPiscar(3); } estadoAnterior = leitura; } estadoAnterior = leitura; } // ════════════════════════════════════════════════════ // Comandos do celular // ════════════════════════════════════════════════════ void processarMensagem(String msg) { if (msg.length() == 0) return; char cmd = msg[0]; switch (cmd) { case 'S': case 's': silenciarBuzzer(); break; case 'M': case 'm': modoMudo = !modoMudo; if (modoMudo) silenciarBuzzer(); bluetooth.print("MUDO:"); bluetooth.println(modoMudo ? "ON" : "OFF"); Serial.print("MUDO:"); Serial.println(modoMudo ? "ON" : "OFF"); break; case 'C': case 'c': bluetooth.print("TOQUES:"); bluetooth.println(contadorToques); Serial.print("TOQUES:"); Serial.println(contadorToques); break; case 'Z': case 'z': contadorToques = 0; bluetooth.println("TOQUES:0"); Serial.println("TOQUES:0"); break; case 'T': case 't': if (msg.length() > 1) { int seg = constrain(msg.substring(1).toInt(), 1, 60); duracaoBuzzer = (unsigned long)seg * 1000UL; bluetooth.print("DURACAO:"); bluetooth.println(seg); Serial.print("DURACAO:"); Serial.println(seg); } break; case '?': bluetooth.println("Cmds: S M C Z T1-60 ?"); break; case '\n': case '\r': return; default: bluetooth.print("ERR:"); bluetooth.println(cmd); } } // ════════════════════════════════════════════════════ // SETUP e LOOP // ════════════════════════════════════════════════════ void setup() { pinMode(PINO_BOTAO, INPUT_PULLUP); pinMode(PINO_LED, OUTPUT); pinMode(PINO_BUZ, OUTPUT); digitalWrite(PINO_BUZ, LOW); Serial.begin(9600); bluetooth.begin(9600); Serial.println("=== Campainha Inteligente ==="); bluetooth.println("ONLINE:campainha"); // handshake ao conectar } void loop() { verificarBotao(); verificarBuzzer(); processarPiscar(); if (bluetooth.available() > 0) { String msg = bluetooth.readStringUntil('\n'); msg.trim(); processarMensagem(msg); } if (Serial.available() > 0) bluetooth.write(Serial.read()); }
=== Campainha Inteligente ===
[botão pressionado] RING:#1
[5s depois] BUZZER:OFF
[botão pressionado] RING:#2
[celular envia "S"] BUZZER:OFF
[celular envia "M"] MUDO:ON
[botão pressionado] RING:#3 (buzzer silencioso)
[celular envia "C"] TOQUES:3
[celular envia "Z"] TOQUES:0
// ── Versão WOKWI ───────────────────────────────────
// Remove SoftwareSerial — usa Serial direto
// Pressione o botão no diagrama; envie comandos pelo Monitor Serial
const int PINO_BOTAO = 2;
const int PINO_LED = 8;
const int PINO_BUZ = 9;
bool buzzerAtivo = false;
bool modoMudo = false;
unsigned long buzzerFim = 0;
unsigned long duracaoBuzzer = 5000;
unsigned int contadorToques = 0;
int estadoAnterior = HIGH;
unsigned long ultimoBounce = 0;
unsigned long ultimoPiscada = 0;
int piscarRestante = 0;
const unsigned long DEBOUNCE = 50;
void tocarBuzzer() {
if (modoMudo) return;
buzzerAtivo = true;
buzzerFim = millis() + duracaoBuzzer;
digitalWrite(PINO_BUZ, HIGH);
}
void silenciarBuzzer() {
buzzerAtivo = false;
digitalWrite(PINO_BUZ, LOW);
Serial.println("BUZZER:OFF");
}
void verificarBuzzer() {
if (buzzerAtivo && millis() >= buzzerFim) silenciarBuzzer();
}
void iniciarPiscar(int n) {
piscarRestante = n * 2;
ultimoPiscada = millis();
}
void processarPiscar() {
if (piscarRestante <= 0) { digitalWrite(PINO_LED, LOW); return; }
if (millis() - ultimoPiscada >= 150) {
ultimoPiscada = millis();
digitalWrite(PINO_LED, piscarRestante % 2 == 0 ? HIGH : LOW);
piscarRestante--;
}
}
void verificarBotao() {
int leitura = digitalRead(PINO_BOTAO);
if (leitura != estadoAnterior) ultimoBounce = millis();
if ((millis() - ultimoBounce) > DEBOUNCE) {
if (leitura == LOW && estadoAnterior == HIGH) {
contadorToques++;
Serial.print("RING:#"); Serial.println(contadorToques);
tocarBuzzer();
iniciarPiscar(3);
}
estadoAnterior = leitura;
}
estadoAnterior = leitura;
}
void processarMensagem(String msg) {
if (msg.length() == 0) return;
char cmd = msg[0];
switch (cmd) {
case 'S': case 's': silenciarBuzzer(); break;
case 'M': case 'm':
modoMudo = !modoMudo;
if (modoMudo) silenciarBuzzer();
Serial.print("MUDO:"); Serial.println(modoMudo ? "ON" : "OFF"); break;
case 'C': case 'c': Serial.print("TOQUES:"); Serial.println(contadorToques); break;
case 'Z': case 'z': contadorToques = 0; Serial.println("TOQUES:0"); break;
case 'T': case 't':
if (msg.length() > 1) {
int seg = constrain(msg.substring(1).toInt(), 1, 60);
duracaoBuzzer = (unsigned long)seg * 1000UL;
Serial.print("DURACAO:"); Serial.println(seg);
}
break;
case '?': Serial.println("Cmds: S M C Z T1-60 ?"); break;
case '\n': case '\r': return;
default: Serial.print("ERR:"); Serial.println(cmd);
}
}
void setup() {
pinMode(PINO_BOTAO, INPUT_PULLUP);
pinMode(PINO_LED, OUTPUT);
pinMode(PINO_BUZ, OUTPUT);
digitalWrite(PINO_BUZ, LOW);
Serial.begin(9600);
Serial.println("=== Campainha Wokwi — pressione o botão ===");
}
void loop() {
verificarBotao();
verificarBuzzer();
processarPiscar();
if (Serial.available() > 0) {
String msg = Serial.readStringUntil('\n');
msg.trim();
processarMensagem(msg);
}
}
Testando o modo mudo no Wokwi: pressione o botão e confirme que RING:#1 aparece e o buzzer soa. Envie "M" e pressione o botão novamente — RING:#2 deve aparecer, mas o buzzer deve ficar silencioso. Esse teste valida a separação entre notificação (sempre ativa) e som (controlável).
A função processarPiscar() usa um contador piscarRestante e millis() para gerenciar as piscadas sem congelar o loop(). Se usasse delay(), o botão seria ignorado durante as piscadas — um segundo toque simultâneo se perderia. Com o padrão não bloqueante, o Arduino pisca o LED, checa o botão e lê o Bluetooth ao mesmo tempo.
estadoAnterior = leitura em verificarBotao()?A segunda atribuição (fora do if de debounce) garante que o estado anterior é sempre atualizado, mesmo quando o debounce ainda está bloqueando. Sem ela, se o botão for pressionado e solto rapidamente dentro da janela de debounce, estadoAnterior ficaria desatualizado e a próxima detecção falharia.
bluetooth.println("ONLINE:campainha") no setup?Quando o celular se reconectar ao Arduino (após desligamento ou perda de sinal), receberá imediatamente a mensagem "ONLINE:campainha". O app usa isso para confirmar que está conectado ao dispositivo correto e atualizar sua interface. É uma boa prática em sistemas que ficam ligados continuamente.
Padrão evento → notificação: verificarBotao() detecta o evento, tocarBuzzer() age localmente, e bluetooth.print("RING:#n") notifica remotamente — tudo no mesmo bloco. Esse padrão se repete em todos os sistemas de alerta: PIR → alerta, fumaça → alerta, porta aberta → alerta. A estrutura é sempre a mesma.
| Botão | Rótulo | Macro |
|---|---|---|
| M1 | 🔕 Silenciar | S |
| M2 | 🔇 Modo mudo | M |
| M3 | 🔢 Ver toques | C |
| M4 | 🔄 Zerar | Z |
| M5 | ⏱️ 3s | T3 |
| M6 | ⏱️ 10s | T10 |
Notificação sonora no celular: configure o Serial Bluetooth Terminal para emitir um som ou vibrar quando receber mensagem contendo "RING:". Nas configurações do app, procure por Receive Sound ou Notification — assim você é alertado mesmo com o celular na bolsa.
| Conceito | Aula de origem | Onde aparece neste projeto |
|---|---|---|
Debounce com millis() |
Aula 42 | verificarBotao() — janela de 50ms |
| Detecção de borda de descida | Aula 42 | leitura == LOW && estadoAnterior == HIGH |
Temporizador com millis() |
Projeto 01 | buzzerFim + verificarBuzzer() |
| Padrão não bloqueante | Aula 41b | processarPiscar() — LED pisca sem travar o loop |
| Evento → notificação remota | Conceito novo (padrão IoT) | bluetooth.print("RING:#n") ao detectar toque |
INPUT_PULLUP |
Aula 42 | Botão sem resistor externo — LOW = pressionado |
| Bluetooth HC-05 + protocolo | Projetos 02–04 | Canal bidirecional — notificações push + comandos pull |
Teste sua compreensão da campainha inteligente: