Bonjour, j’ai un RPi Zero v1.3, celui sans le Wifi, et depuis pas mal de temps je cherche à lui donner un projet.
Après pas mal de réflexion, et vu le prix des visualisateur audio (VU Meter), j’en suis venu à voir si c’est possible de le faire avec le RPi Zero, j’avais déjà sous la main un microphone « adafruit » et un petit OLED 0.91".
Requirement
- RPi Zero (v1.3 no-wifi)
- Écran Waveshare OLED 0.91" 4pins I2C/SPI
- Microphone I2C Adafruit (SPH0645) ( Raspberry Pi Wiring & Test | Adafruit I2S MEMS Microphone Breakout | Adafruit Learning System )
- Raspbian OS Lite (Buster)
- Cava (compilé manuellement)
- Python3 avec Pip, Pillow, Luma Oled et autre smodules si vous rencontrez des erreurs
SI vous désirez utiliser un écran « standard », comme un 3.5" sur le GPIO ou en HDMI, seul « cava » serait requis et donne un résultat intéressant « out-of-the-box », mais ici ont utilise un écran OLED, qui ne passe pas par le canal régulier d’affichage.
Alors avec l’aide de ChatGPT, j’ai réussi a créer de quoi d’intéressant. L’idée est d’utiliser Cava pour fournir un résultat lisible par le Python pour l’envoyer sur l’affichage. Mais vu les faibles performances de RPi Zero, faut trouver un ajustement entre fluidité, précision et rapidité. Ce n’est pas une chose simple.
Mise en route
Je vous laisse le plaisir de préparer le Cava et d’installer votre écran OLED, ont saute directement à l’installation et ajustement pour le projet.
- Alors préparons le système , dans le
/boot/config.txt
, il faut faire quelques ajustement;
#Active l'I2C et SPI
dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on
#Désactive l'audio embarqué
dtparam=audio=off
#Booster le port I2C pour un rendu plus rapide
dtparam=i2c_arm_baudrate=400000
#Le microphone
dtoverlay=googlevoicehat-soundcard
- Ajuster les valeurs de Cava pour l’utilisation par défaut, dans
home/pi/.config/cava/config
;
## Configuration file for CAVA.
# Remove the ; to change parameters.
[general]
# Accepts only non-negative values.
framerate = 25
autosens = 0
sensitivity = 5000
bars = 64
lower_cutoff_freq = 60
higher_cutoff_freq = 20000
[input]
method = alsa
source = hw:0,0
framerate = 25
buffer = 256
sample_bits = 8
[output]
method = raw
raw_target = /tmp/cava.fifo
data_format = ascii
channels = mono
[smoothing]
;integral = 0
;gravity = 100
;noise_reduction = 50
25FPS, pas d’ajustement de la sensibilité, capture de 100Hz à 12KHz, capture de Alsa, appareil 0, prise 0, avec un buffer limité à 256 (au lieu de 4096). Sort le résultat dans un fichier FIFO en ASCII. Le Smoothing est désactivé, avec une gravité forcé de 100 (peut ^tre ajuster sinon via noise_reduction)
- Ajouter le script dans
/home/pi/oledlive.py
;
import os
import select
from PIL import Image, ImageDraw
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
WIDTH, HEIGHT = 128, 32
EXPECTED_BARS = 64
FIFO_PATH = "/tmp/cava.fifo"
serial = i2c(port=1, address=0x3C)
device = ssd1306(serial, width=WIDTH, height=HEIGHT)
try:
fifo_fd = os.open(FIFO_PATH, os.O_RDONLY | os.O_NONBLOCK)
except FileNotFoundError:
exit(1)
buffer = ""
bar_width = WIDTH // EXPECTED_BARS
img = Image.new('1', (WIDTH, HEIGHT))
draw = ImageDraw.Draw(img)
while True:
rlist, _, _ = select.select([fifo_fd], [], [])
if fifo_fd in rlist:
try:
data = os.read(fifo_fd, 2048)
if not data:
continue
buffer += data.decode(errors='ignore')
lines = buffer.split('\n')
buffer = lines[-1] if buffer[-1] != '\n' else ""
for line in lines[:-1]:
vals = line.strip().split(';')
if len(vals) < EXPECTED_BARS:
continue
# Parsing optimisé
values = []
append = values.append
for v in vals[:EXPECTED_BARS]:
try:
append(int(v))
except:
append(0)
# Dessin rapide
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=0)
for i, val in enumerate(values):
bar_height = max(1, val * HEIGHT // 200)
x0 = i * bar_width
x1 = x0 + bar_width - 1
draw.rectangle((x0, HEIGHT - bar_height, x1, HEIGHT), fill=255)
device.display(img)
except OSError:
continue
Exécution
Avant d’implanter en démarrage auto, il faut tester, vous pouvez ajuster selon vos résultats pour tenter d’améliorer le rendu.
- Lancer Cava :
cava &
- Lancer le script :
python3 oledlive.py &
Observez votre écran OLED, si tout marche correctement, en son « nul », une ligne sera présent en bas d’écran, à la présence de son, le visualisateur à bande s’activera. Le rendu est quand même un peu lent (latence de quelques ms, avec un effet de fondu des fréquences), à date, c’est le mieux que j’arrive a tirer du projet.
Sans le boost du I2C, le FPS max varie de 10 à 12 sur le OLED, ont peut toucher le 25 FPS avec le boost.
Alléger le RPi
sudo systemctl disable --now \
avahi-daemon \
bluetooth \
triggerhappy \
rsyslog \
systemd-timesyncd \
fake-hwclock
sudo systemctl disable getty@tty2.service getty@tty3.service \
getty@tty4.service getty@tty5.service getty@tty6.service
sudo dphys-swapfile swapoff
sudo systemctl disable dphys-swapfile
sudo apt purge --auto-remove \
triggerhappy \
logrotate \
dphys-swapfile \
rsyslog \
fake-hwclock \
manpages \
man-db \
avahi-daemon \
bluez \
sudo journalctl --vacuum-size=10M
sudo sed -i 's/#Storage=auto/Storage=volatile/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald
Dans /etc/rc.local
, pour désactiver le HDMI;
#!/bin/bash
/opt/vc/bin/tvservice -o
exit 0
Alors l’idée est là, merci d’apporter vos commentaires, et suggestion pour le rendre meilleur.