Homepage
🏡   Articoli
 
 Ospite
Un progettino per creare l'effetto fuoco con Raspberry Pi usando due LED

Un progettino per creare l'effetto fuoco con Raspberry Pi usando due LED

Raspberry Pi
🇮🇹 Italiano
Scritto da Rocco | 11/07/2026

Introduzione

Come abbiamo visto nel precedente articolo, il Raspberry Pi può trovare diversi ambiti di utilizzo, ma quello che lo fa emergere rispetto ad altri mini computer è la presenza dell'interfaccia GPIO accessibile dai suoi 40 pin.

Pinout del Raspberry Pi 3B
Pinout del Raspberry Pi

In questo articolo vedremo come utilizzare il canale PWM (Pulse-Width Modulation, o modulazione di larghezza di impulso) per variare in maniera randomica il duty cycle dell'alimentazione di due LED di colore diversi in modo da simulare un effetto fuoco da usare in ambito modellismo o presepe, difficilmente ottenibile solo con lo stato ON/OFF.

Materiali necessari

Per questo progetto avrai bisogno di:

  • 1 Raspberry Pi con installato Raspberry Pi OS
  • 1 pin GND, 1 pin 3,3 V e 2 pin PWM liberi sulla GPIO, preferibilmente 7, 10, 12 e 14
  • 2 LED, uno rosso e uno giallo
  • 2 resistenze da 330 ohm
  • 1 pulsante normalmente aperto
  • cavi vari
  • saldatore e stagno o, in alternativa, una breadboard su cui lavorare
  • auspicabile, ma non vincolante, guaina termorestringente o nastro isolante (🤮) per non mandare in corto i pin a lavoro concluso

Pulse-Width Modulation (PWM), o modulazione di larghezza di impulso

Il PWM è un meccanismo che consente di modulare la larghezza degli impulsi di un segnale digitale, emulando di fatto un output analogico. Architetturalmente, questo approccio permette di variare in modo granulare la potenza erogata ai LED, garantendo un controllo fluido della luminosità e un eccellente disaccoppiamento (decoupling) tra la logica del demone software e la componente puramente elettronica.

A livello elettrico il Raspberry Pi non eroga una tensione analogica variabile, ma lavora unicamente con stati digitali fornendo una tensione fissa di 3.3V. La modulazione agisce sul dominio del tempo alterando il duty cycle, ovvero la percentuale di tempo in cui il segnale rimane alto rispetto al totale della durata del ciclo.
Cosa significa? Che un segnale a 3.3V tenuto alto per la metà del tempo fornirà al componente ricevente una tensione media equivalente a 1.65V, diminuendo di fatto la luminosità erogata.

Il SoC (System-on-a-Chip) della scheda offre canali PWM implementati a livello hardware nativo, accessibili tipicamente dai pin GPIO 12, 13, 18 e 19, i quali delegano l'oscillazione a un controller dedicato garantendo precisione temporale assoluta senza impattare sui cicli di clock del processore principale.

L'astrazione garantita dalle librerie odierne ci consente inoltre di abilitare un PWM via software su qualsiasi altro pin GPIO standard, delegando il calcolo alla CPU. Questa soluzione introduce un carico del tutto trascurabile per applicazioni a bassa frequenza come il tremolio di un LED, offrendo massima flessibilità architetturale e liberando i canali hardware per periferiche stringenti come i servomotori, ma in questo articolo andremo a utilizzare i canali PWM nativi.

La nostra implementazione

Per gestire oculatamente e senza sprechi di risorse il nostro progettino, la struttura logica del demone Python abbraccia un paradigma puramente guidato dagli eventi.
Il cuore del controllo è un pulsante di innesco fisico, collegato al pin GPIO 4, che permetterà di attivare o disattivare l'effetto tremolio senza incidere considerevolmente sulla CPU con un polling continuo.

Per garantire stabilità e resilienza alle interferenze elettromagnetiche, abilitiamo la resistenza di pull up interna sul pin GPIO 4 (a cui il pulsante è collegato), definendo uno stato logico di riposo ancorato in alto ed evitando letture casuali.
Sul versante dell'output, destiniamo i pin GPIO 15 e 18 alla gestione PWM dei due LED, chiudendo il circuito di massa su uno dei pin GND adiacenti, come il pin fisico 6 o 14.

Attenzione!

I pin fisici non corrispondono al numero GPIO!

  • GPIO 4 = pin 7
  • GPIO 15 = pin 10
  • GPIO 18 = pin 12
  • per il GND useremo i vicini pin 6 o 14

A livello elettronico è tassativo non collegare i diodi luminosi direttamente al Raspberry Pi, poiché la tensione erogata è superiore a quella nominale del LED. L'erogazione richiede dunque l'interposizione di una resistenza limitatrice sul lato dell'anodo di ciascun LED (polo positivo, generalmente con il pin più lungo) per prevenire assorbimenti fuori scala, in grado di danneggiare irreversibilmente i pin logici e bruciare il nostro LED.

Anatomia di un LED. Source: Elettronica Semplice
Elettronica Semplice

Un resistore da 220 o 330 ohm rappresenta il punto di equilibrio perfetto per garantire un'emissione luminosa pulita operando in totale sicurezza operativa e termica.

Resistenza da 330 Ω con tolleranza 5%.
Resistenza da 330 Ω con tolleranza 5%.

Il codice per il funzionamento

Dritti al sodo, prima il codice e poi la spiegazione.
Chiameremo il file gestore_fuoco.py e lo posizioneremo in /home/pi/ con il comando

sh
touch /home/pi/gestore_fuoco.py
nano /home/pi/gestore_fuoco.py

per poi inserire il seguente codice Python:

python
#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import time
import random
from gpiozero import PWMLED, Button

PIN_LED_1 = 15
PIN_LED_2 = 18
PIN_PULSANTE_FUOCO = 4
PAUSA_TREMOLIO = 0.02

led_fuoco_1 = PWMLED(PIN_LED_1)
led_fuoco_2 = PWMLED(PIN_LED_2)
pulsante_innesco = Button(PIN_PULSANTE_FUOCO, pull_up=True)

stato_fuoco_attivo = False

def toggle_stato_innesco():
    global stato_fuoco_attivo
    stato_fuoco_attivo = not stato_fuoco_attivo
    
    if not stato_fuoco_attivo:
        led_fuoco_1.value = 0
        led_fuoco_2.value = 0
        print("Infrastruttura PWM: Effetto fuoco disattivato.")
    else:
        print("Infrastruttura PWM: Effetto fuoco attivato.")

pulsante_innesco.when_pressed = toggle_stato_innesco

def demone_pwm_core():
    print("Demone PWM in esecuzione. In attesa di innesco sul pin 4.")
    try:
        while True:
            if stato_fuoco_attivo:
                led_fuoco_1.value = random.uniform(0, 1)
                led_fuoco_2.value = random.uniform(0, 1)
                time.sleep(PAUSA_TREMOLIO)
            else:
                time.sleep(0.1)
    except KeyboardInterrupt:
        print("Terminazione richiesta. Rilascio risorse in corso...")
        led_fuoco_1.value = 0
        led_fuoco_2.value = 0

if __name__ == '__main__':
    demone_pwm_core()

L'architettura del codice si fonda sul principio della separazione delle responsabilità e aderisce alla metodologia DRY (Don't Repeat Yourself), definendo le mappature hardware in testa al file per agevolare repentine modifiche di pinout senza toccare la logica core.
Dopo l'importazione delle librerie base per il tempo e l'entropia matematica, avviamo le istanze degli oggetti PWMLED e Button da gpiozero, demandando alla libreria le complessità del basso livello. La configurazione degli endpoint fisici avviene in modo puramente dichiarativo, attivando contestualmente il pull up logico sul pulsante.

Il cambio di stato è confinato nella funzione di callback che inverte la variabile booleana ogni volta che il pin 4 registra la variazione di tensione scatenata dalla pressione fisica. Il lavoro "sporco" del demone è isolato nel loop principale. Quando l'interruttore software è armato, il sistema inietta un valore casuale tra 0 e 1 nel duty cycle dei canali PWM e attende una frazione di secondo calcolata per replicare il movimento naturale di una fiamma.
Nei cicli di inattività lo script entra in idle, abbattendo radicalmente l'overhead sul processore del Raspberry Pi e preservando la resilienza del sistema operativo.

Dalla prova al consolidamento

Prima di trasformare il processo in un demone da avviare al boot, è prassi architetturale validare la stabilità del codice direttamente da riga di comando (CLI) per assicurarci che non vi siano errori di sintassi o conflitti sui permessi di accesso all'infrastruttura GPIO.

Per prima cosa assegniamo dunque i permessi di esecuzione allo script e lanciamolo manualmente. Questo ci permette di verificare a video i log generati dalla funzione print e testare il comportamento del pulsante fisico in tempo reale.

Per provare il nostro codice salviamo premendo Ctrl-O, Y e Ctrl-X per confermare il salvataggio e chiudere nano, dopo di che modifichiamo i permessi per abilitare l'esecuzione dello script e avviamolo:

sh
chmod +x /home/pi/gestore_fuoco.py
/usr/bin/python3 /home/pi/gestore_fuoco.py

Una volta superato il collaudo, possiamo avviare il nostro demone senza impiegare soluzioni legate all'utente, come crontab o sessioni in background, sfruttando lo standard infrastrutturale per l'orchestrazione dei servizi di Raspberry Pi OS, lo standard systemd.
Questo strumento garantisce il riavvio automatico in caso di crash (assicurando resilienza) e il totale disaccoppiamento dalla sessione utente loggata.

Procediamo creando il file di configurazione dell'unità di servizio.

sh
sudo nano /etc/systemd/system/gestore_fuoco.service

All'interno del file, definiamo le regole di ingaggio del nostro demone. Indichiamo a systemd di avviare il processo solo dopo che l'infrastruttura multi-utente è caricata, specificando l'utente proprietario e imponendo un riavvio automatico in caso di fallimento.

toml
[Unit]
Description=Demone PWM per effetto tremolio
After=multi-user.target

[Service]
Type=simple
User=pi
ExecStart=/usr/bin/python3 /home/pi/gestore_fuoco.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Dopo dobbiamo istruire il sistema operativo affinché ricarichi l'albero dei demoni, integri il nostro servizio nella sequenza di boot e lo avvii immediatamente.

sh
sudo systemctl daemon-reload
sudo systemctl enable gestore_fuoco.service
sudo systemctl start gestore_fuoco.service

Così facendo avremo ottenuto un'infrastruttura autonoma, solida e in grado di funzionare anche a seguito di possibili riavvii della macchina.

Troubleshooting

Per ispezionare il comportamento del demone in tempo reale e diagnosticare eventuali anomalie senza fermare il servizio, è sufficiente interrogare il journal di sistema.

sh
journalctl -u gestore_fuoco.service -f

Rocco, i miei LED non si accendono o se ne accende solo uno!

I consigli dello chef:

  • Controlla i punti di saldatura
  • Controlla la polarità del LED, pin lungo = polo positivo = 3.3 V (ricordati la resistenza su questo lato), lato scalpellato piatto del LED = polo negativo = GND
  • Verifica se hai collegato tutto ai pin corretti o modificali (indifferente se fisicamente o via codice)
  • Sei sicuro che i LED funzionino? 🫨

Conclusioni

La nostra ricetta è pronta per essere utilizzata ogni volta che vorrete dare fuoco alla vostra casa simulare un piccolo fuoco!
Con un cavo sufficientemente lungo vi sarà possibile nasconderlo all'interno di una capanna nel vostro presepe (vi avevo detto che usavo RPi per gestirne uno, no?) o in un diorama.
L'implementazione proposta rispetta i principi DRY e cerca di tenere meno codice hardcoded possibile così da poter modificare in maniera relativamente facile i pin impiegati - prestando comunque attenzione al fatto che, purtroppo, la numerazione logica è diversa da quella fisica! - e propone già una modalità di avvio automatico al boot del sistema.

Buon divertimento!


Torna in cima