PiezoPiano com Raspberry Pi Pico W
Protótipo de piano electrónico com sensores piezoeléctricos, protecção por díodo zener e reprodução de sons através de DFPlayer Mini.
1Ideia do projecto
O projecto consiste na construção de um pequeno piano electrónico em que cada tecla é representada por um disco piezoeléctrico. Ao sofrer uma pancada ou vibração, o piezo gera um impulso eléctrico que é lido pelo Raspberry Pi Pico W.
Depois de detectado o impacto, o Pico W pode acender um LED, registar dados, ou enviar um comando ao DFPlayer Mini para reproduzir uma nota musical em formato MP3.
2Objectivos educativos
- Explorar o funcionamento de sensores piezoeléctricos.
- Medir sinais analógicos com o ADC do Raspberry Pi Pico W.
- Compreender a necessidade de protecção das entradas de 3,3 V.
- Relacionar electrónica, programação, som e aquisição de dados.
- Construir um protótipo musical interactivo com aplicação prática.
3Arquitectura geral
Piezo → circuito de protecção → Raspberry Pi Pico W → DFPlayer Mini → coluna
A solução inicial utiliza três entradas analógicas do Pico W: GP26, GP27 e GP28. Para mais teclas, podem ser usadas entradas digitais ou um multiplexador analógico.
4Ligação de um piezo
O piezo não deve ser ligado directamente ao GPIO. Como pode gerar picos de tensão superiores a 3,3 V, é usado um circuito simples de limitação e descarga.
Piezo (+)
|
[10 kΩ]
|
●------[1 kΩ]------ GPIO/ADC do Pico
|
[1 MΩ]
|
GND
● ---- cátodo do zener 3,3 V
GND -- ânodo do zener 3,3 V
Piezo (-) ---- GND
5Função dos componentes
| Componente | Função |
|---|---|
| 10 kΩ | Limita a corrente que vem do piezo. |
| 1 MΩ | Funciona como pulldown e descarrega o piezo, evitando leituras flutuantes. |
| 1 kΩ | Protecção extra antes da entrada do Pico. |
| Zener 3,3 V | Limita a tensão aplicada ao GPIO/ADC. |
6Orientação do zener
O díodo zener deve ser ligado inversamente polarizado: a faixa preta, que identifica o cátodo, fica do lado do sinal; o outro terminal fica ligado ao GND.
SINAL / ponto ● ----|<|---- GND
zener 3,3 V
7Entradas analógicas usadas
| Entrada | GPIO | Pino físico | Uso |
|---|---|---|---|
| A0 / ADC0 | GP26 | 31 | Piezo 1 |
| A1 / ADC1 | GP27 | 32 | Piezo 2 |
| A2 / ADC2 | GP28 | 34 | Piezo 3 |
8Importância do GND comum
Todos os GND do circuito devem estar ligados ao GND do Pico W. Isto inclui o negativo dos piezos, a resistência de 1 MΩ, o ânodo do zener e o GND do DFPlayer Mini.
GND dos piezos
GND do zener
GND do DFPlayer
GND do Pico W
→ todos ligados em comum
9Leitura digital ou analógica?
Com leitura analógica é possível medir a intensidade do impacto. Com leitura digital apenas se detecta se houve ou não toque. Para o estudo de tensão, deve ser usada a entrada analógica GP26.
10Ligações UART
| DFPlayer Mini | Raspberry Pi Pico W |
|---|---|
| VCC | VBUS / 5 V |
| GND | GND |
| RX | GP0 / UART0 TX |
| TX | GP1 / UART0 RX |
| SPK1 | Coluna |
| SPK2 | Coluna |
11Ficheiros MP3
No cartão microSD, criar a pasta mp3 e colocar ficheiros numerados:
/mp3/0001.mp3
/mp3/0002.mp3
/mp3/0003.mp3
Cada ficheiro pode corresponder a uma nota musical ou efeito sonoro.
| Piezo | Entrada Pico W | Ficheiro reproduzido |
|---|---|---|
| Piezo 1 | GP26 / A0 | 0001.mp3 |
| Piezo 2 | GP27 / A1 | 0002.mp3 |
| Piezo 3 | GP28 / A2 | 0003.mp3 |
12Objectivo do ensaio
Para caracterizar o comportamento do piezo, foi proposta uma aquisição de dados através da entrada analógica GP26. Durante alguns segundos, são aplicadas pancadas no piezo e o Pico W grava os valores do ADC e a tensão calculada num ficheiro CSV.
13Conversão ADC para tensão
tensao = valor_adc × 3.3 / 65535
O ADC do Pico devolve valores entre 0 e 65535, correspondentes aproximadamente ao intervalo entre 0 V e 3,3 V.
14Ressalva sobre o zener
15Dados recolhidos e gráfico do ensaio
O ensaio realizado no piezo ligado ao GP26 / A0 registou 1553 amostras durante aproximadamente 5 segundos. A tensão máxima medida foi de cerca de 2,158 V, correspondente ao valor ADC 42858, no instante aproximado de 4257 ms.
| Parâmetro | Valor |
|---|---|
| Amostras | 1553 |
| Duração | ~5 s |
| Pico máximo | 2,158 V |
| ADC máximo | 42858 |
| Instante do pico | 4257 ms |
16Interpretação esperada
O gráfico mostra picos rápidos nos momentos de impacto, seguidos de uma descida acentuada e recuperação gradual para a tensão de repouso. Neste ensaio, os picos ficaram abaixo de 3,3 V, mas a leitura continua condicionada pelo circuito de protecção. Caso sejam aplicados impactos mais fortes, o zener pode limitar os valores superiores, protegendo o ADC do Pico W.
17Registo CSV no Pico W
from machine import ADC, Pin
import time
piezo = ADC(Pin(26))
led = Pin("LED", Pin.OUT)
FICHEIRO = "piezo_gp26.csv"
INTERVALO_MS = 2
DURACAO_MS = 5000
def adc_para_tensao(valor_adc):
return valor_adc * 3.3 / 65535
time.sleep(2)
led.value(1)
inicio = time.ticks_ms()
with open(FICHEIRO, "w") as ficheiro:
ficheiro.write("tempo_ms,adc,tensao_v\n")
while time.ticks_diff(time.ticks_ms(), inicio) < DURACAO_MS:
tempo_ms = time.ticks_diff(time.ticks_ms(), inicio)
valor_adc = piezo.read_u16()
tensao = adc_para_tensao(valor_adc)
ficheiro.write("{{}},{{}},{{:.4f}}\n".format(tempo_ms, valor_adc, tensao))
time.sleep_ms(INTERVALO_MS)
led.value(0)
print("Ficheiro gravado:", FICHEIRO)
18Gráfico em Python
import csv
import matplotlib.pyplot as plt
FICHEIRO = "piezo_gp26.csv"
tempos = []
tensoes = []
adcs = []
with open(FICHEIRO, "r") as ficheiro:
leitor = csv.DictReader(ficheiro)
for linha in leitor:
tempos.append(float(linha["tempo_ms"]))
adcs.append(int(linha["adc"]))
tensoes.append(float(linha["tensao_v"]))
tensao_maxima = max(tensoes)
indice_pico = tensoes.index(tensao_maxima)
tempo_pico = tempos[indice_pico]
adc_maximo = adcs[indice_pico]
print("Resumo do ensaio")
print("----------------")
print("Tensão máxima medida:", round(tensao_maxima, 3), "V")
print("ADC máximo:", adc_maximo)
print("Tempo do pico máximo:", tempo_pico, "ms")
print("Número de amostras:", len(tensoes))
plt.figure(figsize=(12, 6))
plt.plot(tempos, tensoes, label="Tensão medida")
plt.scatter(tempo_pico, tensao_maxima, label="Pico máximo")
plt.title("Resposta do piezo ligado ao GP26")
plt.xlabel("Tempo (ms)")
plt.ylabel("Tensão no ADC (V)")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.savefig("grafico_piezo_gp26.png", dpi=300)
plt.show()
19Reprodução MP3 com três piezos
Este código permite associar cada piezo a uma música diferente no DFPlayer Mini. Assim, ao tocar no piezo ligado ao GP26, é reproduzido o ficheiro 0001.mp3; ao tocar no piezo ligado ao GP27, é reproduzido o ficheiro 0002.mp3; e ao tocar no piezo ligado ao GP28, é reproduzido o ficheiro 0003.mp3.
/mp3/0001.mp3 → Piezo 1 / GP26
/mp3/0002.mp3 → Piezo 2 / GP27
/mp3/0003.mp3 → Piezo 3 / GP28
from machine import Pin, ADC, UART
import time
# -------------------------------------------------
# Piezos ligados às entradas analógicas do Pico W
# -------------------------------------------------
piezo1 = ADC(Pin(26)) # A0 / GP26
piezo2 = ADC(Pin(27)) # A1 / GP27
piezo3 = ADC(Pin(28)) # A2 / GP28
# LED interno do Pico W
led = Pin("LED", Pin.OUT)
# -------------------------------------------------
# DFPlayer Mini ligado à UART0
# GP0 = TX do Pico -> RX do DFPlayer
# GP1 = RX do Pico <- TX do DFPlayer
# -------------------------------------------------
uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
def dfplayer_cmd(cmd, param=0):
start = 0x7E
version = 0xFF
length = 0x06
feedback = 0x00
end = 0xEF
param_high = (param >> 8) & 0xFF
param_low = param & 0xFF
checksum = 0 - (version + length + cmd + feedback + param_high + param_low)
checksum &= 0xFFFF
packet = bytes([
start,
version,
length,
cmd,
feedback,
param_high,
param_low,
(checksum >> 8) & 0xFF,
checksum & 0xFF,
end
])
uart.write(packet)
def set_volume(volume):
# Volume entre 0 e 30
dfplayer_cmd(0x06, volume)
def play_track(track_number):
# Toca /mp3/0001.mp3, /mp3/0002.mp3, etc.
dfplayer_cmd(0x12, track_number)
# -------------------------------------------------
# Configuração dos toques
# -------------------------------------------------
LIMIAR = 8000 # baixa se não detectar; aumenta se disparar sozinho
BLOQUEIO_MS = 350 # evita várias leituras do mesmo toque
ultimo_toque_1 = 0
ultimo_toque_2 = 0
ultimo_toque_3 = 0
# Espera pelo arranque do DFPlayer Mini
time.sleep(2)
set_volume(24)
print("Sistema pronto.")
print("Piezo 1 / GP26 -> 0001.mp3")
print("Piezo 2 / GP27 -> 0002.mp3")
print("Piezo 3 / GP28 -> 0003.mp3")
# -------------------------------------------------
# Ciclo principal
# -------------------------------------------------
while True:
agora = time.ticks_ms()
valor1 = piezo1.read_u16()
valor2 = piezo2.read_u16()
valor3 = piezo3.read_u16()
# Valores no terminal para afinar o limiar
print("P1:", valor1, " P2:", valor2, " P3:", valor3)
if valor1 > LIMIAR and time.ticks_diff(agora, ultimo_toque_1) > BLOQUEIO_MS:
print("Piezo 1 tocado -> Música 1")
led.value(1)
play_track(1)
time.sleep_ms(80)
led.value(0)
ultimo_toque_1 = agora
if valor2 > LIMIAR and time.ticks_diff(agora, ultimo_toque_2) > BLOQUEIO_MS:
print("Piezo 2 tocado -> Música 2")
led.value(1)
play_track(2)
time.sleep_ms(80)
led.value(0)
ultimo_toque_2 = agora
if valor3 > LIMIAR and time.ticks_diff(agora, ultimo_toque_3) > BLOQUEIO_MS:
print("Piezo 3 tocado -> Música 3")
led.value(1)
play_track(3)
time.sleep_ms(80)
led.value(0)
ultimo_toque_3 = agora
time.sleep_ms(20)
LIMIAR. Se não detectar os impactos, baixa esse valor.20Comandos úteis
cd /home/alvaro/Documentos/Escola/piezo
nano grafico_piezo.py
python3 grafico_piezo.py
xdg-open grafico_piezo_gp26.png