Mentre lavoravo alla roulette con strisce LED, mi è venuta l'idea di un altro utilizzo per questi dispositivi di illuminazione universali. In un attimo, da una striscia LED lunga 1 m con 60 LED Neopixel è nato un termometro. La struttura è così semplice che il progetto è particolarmente adatto ai principianti nel campo dei microcontrollori: nel caso più semplice sono necessari solo quattro componenti. Vi mostrerò la procedura in questo nuovo episodio della serie.
MicroPython su ESP8266, ESP32 e Raspberry Pi Pico
oggi
Il termometro a striscia LED
Il programma per il termometro è naturalmente scritto in MicroPython in modo tale da poter essere eseguito senza modifiche su tutti i controller citati nel titolo. Con due pulsanti aggiuntivi, necessari solo per la scrittura del firmware, anche il più piccolo rappresentante della famiglia ESP8266, l'ESP8266-01S, può fungere da unità di controllo per la striscia LED. Il programma operativo del termometro è orientato a questo controller con i suoi due pin GPIO esterni: GPIO0 controlla i LED della striscia, mentre GPIO2 realizza la connessione bus one-wire al DS18B20. Poiché la scheda dell'ESP8266-01S non contiene un convertitore di tensione da 5 V a 3,3 V, è necessario un circuito esterno. Anche il collegamento all'USB deve essere fornito da un adattatore USB-TTL esterno. Questo completa l'elenco dell'hardware necessario.
Hardware
| 1 | ESP32 Dev Kit C V4 senza saldature o Modulo ESP32 NodeMCU Scheda di sviluppo WLAN WiFi con CP2102 o ESP8266 01S esp-01S Modulo WiFi con adattatore breadboard o D1 Mini V3 NodeMCU con ESP8266-12F o NodeMCU Lua Lolin V3 Moduli ESP8266 ESP-12F WIFI Scheda di sviluppo Wifi con CH340 o NodeMCU Lua Amica Modulo V2 ESP8266 ESP-12F WIFI Scheda di sviluppo Wifi con CP2102 o D1 Mini V4 NodeMCU ESP8266EX Modulo WLAN con connessione USB-C compatibile con Arduino o |
| 1 | |
| 1 | Sensore di temperatura digitale DS18B20 TO92 o Cavo da 1 m Sensore di temperatura digitale DS18B20 in acciaio inossidabile |
| 1 | Resistenza 10kΩ |
| 2 | Opzionale per ESP8266-01S Pulsanti, ad esempio modulo pulsante KY-004 |
| 1 | se si utilizza un ESP8266-01S |
| 1 | se si utilizza un ESP8266-01S |
| 1 |
Come mostrano gli schemi elettrici seguenti, la configurazione è più semplice da realizzare con un ESP32 o ESP8266. Con l'ESP8266-01S è necessario un convertitore da 5 V a 3,3 V e inoltre per Per la scrittura del firmware sono necessari due pulsanti, poiché la scheda piccola non dispone né di un sistema di scrittura automatico come le versioni più grandi, né di un regolatore di tensione integrato. In compenso è possibile risparmiare la resistenza pull-up sulla linea di segnale del DS18B20. Per il collegamento all'USB è inoltre necessario un adattatore da USB a TTL, che però serve solo durante la fase di sviluppo del programma e successivamente per la scrittura del programma operativo ledstrip_thermo.py.
L'alimentazione della striscia LED dovrebbe essere fornita da un alimentatore a spina da 5 V per un funzionamento a lungo termine; il consumo massimo di corrente del controller e dei LED è di circa 80-120 mA. Alcune delle schede ESP8266 accettano tensioni esterne tramite la connessione Vin, ma non forniscono i 5 V dalla porta USB. Nel caso delle schede ESP32 e del Raspberry Pi Pico funziona già. Con questi controller, la striscia LED può quindi essere alimentata direttamente dalla porta USB. Chi desidera utilizzare il dispositivo finito indipendentemente dal PC, non può fare a meno di utilizzare un alimentatore, a meno che non si utilizzi una batteria Li in combinazione con un apposito portabatterie. Questo offre anche 5 V e 3,3 V conuna capacità di carico di 1 A ciascuno. Con una carica, il circuito funziona per circa 24 ore.
Ecco ora gli schemi elettrici.

Figura 1: Termometro a LED ESP8266 D1 mini

Figura 2: Termometro LED ESP32

Figura 3: Termometro a LED ESP8266-01S

Figura 4: Termometro a LED - Raspi Pi Pico
Gli schemi elettrici riflettono anche il tipo di struttura. La struttura con l'ESP8266-01S è un po' caotica a causa dei componenti aggiuntivi. L'uso di un portabatteria con batteria Li evita l'uso del regolatore di tensione da 5V a 3,3V. Tutti gli ESP hanno il firmware 19.1.

Figura 5: Struttura con ESP8266-01S
Il tutto risulta molto chiaro con un ESP8266 NodeMcu V3.

Figura 6: Struttura con ESP8266-NodeMcu V3
Il software
Per il flashing e la programmazione dell'ESP32:
Thonny o
Per la rappresentazione dei segnali bus
SALEAE – Software di analisi logica (64 bit) per Windows 8, 10, 11
Firmware utilizzato per l'ESP32:
Firmware utilizzato per l'ESP8266:
ESP8266_GENERIC-FLASH_1M-20220618-v1.19.1.bin
Firmware utilizzato per il Raspberry Pi Pico (W):
RPI_PICO_W-20240602-v1.23.0.uf2
I programmi MicroPython relativi al progetto:
ledstrip_thermo.py: programma operativo del termometro
MicroPython - Linguaggio - Moduli e programmi
Per l'installazione di Thonny, qui trovate istruzioni dettagliate (versione inglese). Qui è riportata anche una descrizione di come installare il firmware Micropython (aggiornato al 25/01/2024) sul chip ESP bruciato. Per sapere come rendere operativo il Raspberry Pi Pico, consultate qui.
MicroPython è un linguaggio interpretato. La differenza principale rispetto all'IDE Arduino, dove si eseguono sempre e solo programmi completi, è che il firmware MicroPython deve essere eseguito solo una volta all'inizio sull'ESP32 affinché il controller comprenda le istruzioni MicroPython. A tal fine è possibile utilizzare Thonny, µPyCraft o esptool.py. Per Thonny ho descritto la procedura qui.
Una volta flashato il firmware, è possibile comunicare liberamente con il controller, testare singoli comandi e vedere immediatamente la risposta, senza dover prima compilare e trasferire un intero programma. Questo è proprio ciò che mi disturba dell'IDE Arduino. Si risparmia un sacco di tempo se si possono verificare in anticipo semplici test della sintassi e dell'hardware, fino a provare e perfezionare funzioni e intere parti del programma tramite la riga di comando, prima di creare un programma. A questo scopo, mi piace creare piccoli programmi di test. Come una sorta di macro, raggruppano i comandi ricorrenti. Da questi frammenti di programma si sviluppano poi intere applicazioni.
Avvio automatico
Se si desidera che il programma si avvii automaticamente all'accensione del controller, copiare il testo del programma in un nuovo file vuoto. Salvare questo file con il nome main.py nell'area di lavoro e caricarlo sul chip ESP. Al successivo reset o all'accensione, il programma si avvierà automaticamente.
Testare i programmi
I programmi vengono avviati manualmente dalla finestra dell'editor corrente nell'IDE Thonny premendo il tasto F5. È più veloce che cliccare con il mouse sul pulsante di avvio o utilizzare il menu Esegui. Solo i moduli utilizzati nel programma devono trovarsi nella memoria flash dell'ESP32.
Nel frattempo, ancora una volta Arduino IDE?
Se in seguito desideri utilizzare nuovamente il controller insieme all'IDE Arduino, basta eseguire il flashing del programma come di consueto. Tuttavia, l'ESP32/ESP8266 avrà dimenticato di aver mai parlato MicroPython. Al contrario, ogni chip Espressif che contiene un programma compilato dall'IDE Arduino o il firmware AT o LUA o ... può essere facilmente dotato del firmware MicroPython. La procedura è sempre quella descritta qui.
Il programma operativo
Il programma è strutturato in modo chiaro, proprio come le sovrastrutture. Attraverso Scegliendo GPIO2 come connessione bus one-wire e GPIO0 come linea bus di controllo per la striscia Neopixel, il programma funziona su tutti i controller sopra citati senza modifiche. Le singole unità funzionali sono definite sotto forma di funzioni.
Per la famiglia ESP8266 ho notato più volte che il controller si blocca poco dopo l'avvio. Ciò sembra essere dovuto al fatto che cerca di stabilire un contatto con un punto di accesso tramite l'unità WLAN. Come soluzione alternativa, subito dopo aver flashato un nuovo firmware, disattivo web-REPL. Ciò ha notevolmente stabilizzato il comportamento all'avvio.
I GPIO degli 8266 hanno anche le denominazioni di LUA. In questo contesto, una tabella di conversione è molto utile durante la programmazione, soprattutto per quanto riguarda i diversi livelli di tensione che devono essere mantenuti durante l'avvio affinché il processo si svolga senza errori. Passiamo ora a discutere le parti del programma e la loro funzione.
# ledstrip_thermo.py
# Dopo aver eseguito il flashing del firmware sull'ESP8266:
# import webrepl_setup
# > d per disabilitare
# Quindi RST; riavvio!
# Pintranslator per schede ESP8266
#Pin LUA D0 D1 D2 D3 D4 D5 D6 D7 D8 RX TX
#ESP8266 Pin 16 5 4 0 2 14 12 13 15 3 1
#Attenzione hi sc sd hihi lo hi hi
# utilizzato DSLE
da macchina import Pin
da time import sleep
da onewire import OneWire
da ds18x20 import DS18X20
da neopixel import NeoPixel
da sys import exit, piattaforma
Per le operazioni di importazione utilizziamo solo moduli integrati nel kernel MicroPython. Vogliamo indirizzare i pin GPIO, lasciare il controller un po' inattivo nel frattempo, abbiamo bisogno del bus One-Wire per il sensore di temperatura DS18B20 e NeoPixel ci fornisce un array per le informazioni sul colore e la funzione write(), con la quale inviamo il contenuto del buffer tramite il bus Neopixel.
dsPin=Pin(2) # ESP8266 (D4)
ds = DS18X20(OneWire(dsPin))
device = ds.scan()[0]
ds.convert_temp()
sleep(0,750)
stampa(device,ds.read_temp(device))
GPIO2 diventa la nostra connessione bus One-Wire. OneWire(dsPin) crea l'oggetto bus, che trasferiamo immediatamente al costruttore della classe DS18X20. La funzione scan() dell'oggetto DS18X20 cerca i componenti sul bus e, si spera, troverà quello giusto, ovvero il nostro DS18B20. L'identificativo ROM del componente è un oggetto bytearray con otto valori, che viene convertito in un elenco insieme ad eventuali altri identificativi.
>>> ds.scan()
[bytearray(b'(\xffd\x0ei\x0f\x01>')]
Poiché abbiamo solo un DS18B20 sul bus, selezioniamo il primo elemento dall'elenco.
>>> ds.scan()[0]
bytearray(b'(\xffd\x0ei\x0f\x01>')
L'identificativo serve per indirizzare separatamente i singoli chip sul bus. convert_temp() ordina a tutti i chip di avviare una misurazione. Dopo almeno 750 ms possiamo recuperare il risultato con l'aiuto della funzione read_temp(). È necessario trasmettere il rispettivo identificativo del componente. Lo abbiamo memorizzato in device.
led=Pin(0,Pin.OUT,value =0) # ESP8266 (D3)
numOfPix=60
np=NeoPixel(led,numOfPix)
pos0 = 20
maxTemp=39
minTemp=-20
temp=[0 for i in intervallo(60)]
Definiamo il collegamento bus Neopixel GPIO0 come uscita e impostiamo il livello su 0. Il bus deve servire numOfPix = 60 LED. Il costruttore della classe NeoPixel genera l'oggetto np. Il LED con il numero 20 indica il punto zero della scala del termometro. La nostra scala va quindi da -20 °C (LED numero 0) a 39 °C (LED numero 59). Nell'elenco temp creiamo 60 elementi con l'aiuto di una comprensione di elenco, che inizialmente hanno semplicemente il valore 0. L'elenco riprenderà in seguito la rappresentazione cromatica della scala.
da=(0,0,0)
verde=(0,64,0)
blu scuro=(0,0,4)
azzurro=(0,0,128)
rosso scuro=(4,0,0)
rosso chiaro=(128,0,0)
magenta =(2,0,2)
statered=0
statered=0
stategreen=0
I valori cromatici dei LED Neopixel sono indicati daterne. I tre valori rappresentano la percentuale di rosso, verde e blu.
I LED sopra l'indicatore della temperatura attuale vengono cancellati (off=(0,0,0)), il punto zero è rappresentato da un verde chiaro. Le decine nella zona positiva si illuminano di rosso chiaro, nella zona negativa di blu chiaro. I valori intermedi sono rappresentati in modo più scuro. Per facilitare l'orientamento, lascio che le unità si illuminino di magenta scuro. A temperature superiori a 40 °C, il LED superiore deve lampeggiare, a temperature inferiori a -20 °C lampeggia il LED inferiore. Il punto zero è sempre illuminato. Per poter distinguere -1 °C da 0 °C, in quest'ultimo caso lampeggia il LED dello zero gradi. Per il controllo di questi stati utilizzo le tre variabili di stato.
def setScale():
for i in range(20):
temp[i]=blu scuro
for i in range(21,60):
temp[i]=rosso scuro
for i in [0,10]:
temp[i]=azzurro
for i in [30,40,50]:
temp[i]=rosso chiaro
for i in range(5,60,10):
temp[i]=magenta
temp[20]=verde
La funzione setScale() riempie la lista temp con i valori dei colori. L'area negativa viene inizialmente colorata in blu scuro, quella positiva in rosso scuro. I limiti dell'area e i valori della lista nelle istruzioni for sono i numeri di posizione dei LED nella striscia. I livelli da 10 vengono quindi sovrascritti in azzurro e rosso chiaro. I livelli da 5, a partire dal LED numero 5, vengono sovrascritti in incrementi di 10 fino al numero 55 in magenta scuro. Infine, imposto il numero 20 su verde.
def fillAndShow(temperatura):
t=min(temperatura,maxTemp)
t=max(minTemp,t)
maxpix = int(t) + pos0
for i in range(maxpix + 1):
np[i]=temp [i]
for i in range(maxpix + 1,numOfPix):
np[i]=da
if t < 0:
np[pos0]=verde
np.write()
La funzione fillAndShow() è praticamente il punto centrale del programma, ovvero la visualizzazione della temperatura. In questo caso, i valori cromatici vengono trasferiti dall'elenco temp alla memoria buffer np dell'oggetto NeoPixel.
Informazioni di base:
La memoria buffer dell'oggetto NeoPixel è in realtà un bytearray con l'identificatore buf. Nel nostro caso può essere referenziato con np.buf. Il contenuto è costituito da gruppi di tre elementi consecutivi per ogni LED. Se viene utilizzato np[2]=(1,2,3) il valore cromatico per il LED numero 2 viene trasferito come tupel, quindi la funzione nascosta __setitem__() converte il tupel in una sequenza di oggetti byte e scrive i valori nella bytearray np.buf. Se osserviamo il contenuto di np.buf, notiamo che l'ordine dei byte è stato modificato.
>>> np[2]=(1,2,3)
>>> np.buf
bytearray(b'\x00\x00\x80\x00\x00\x04\x02\x01\x03\x00\x00\x04\x00\x00\x04 …
I valori RGB vengono convertiti come specificato dall'attributo np.ORDER.
>>> np.ORDER
(1, 0, 2, 3)
Se passiamo R=1, G=2 e B=3, i valori finiscono nel buffer come G, R, B. Con 3 verrebbe passato un valore bianco, che però non è supportato dal tipo di LED utilizzato.
Ma ora diamo un'occhiata più da vicino alla funzione fillAndShow(). Quando viene richiamata, al parametro temperatura viene passato il valore della temperatura attuale, che deve rientrare nell'intervallo consentito affinché il programma non venga interrotto con un errore. t=min(temperatura,maxTemp) garantisce che t non superi la temperatura massima visualizzabile. min() restituisce il valore minimo tra quelli passati. t=max(minTemp,t) è responsabile del rispetto del limite inferiore.
Ora occorre determinare il numero del LED per la temperatura in t. Prendiamo la parte intera del valore della temperatura e livelliamo il punto zero della scala Celsius sulla posizione 20 della catena di LED. < span lang="DE" style="font-size: 10.0pt; font-family: 'Courier New'; mso-fareast-font-family: 'Times New Roman'; color: black;">maxpix = int(t) + pos0
Quindi riempiamo il buffer fino al limite calcolato con il contenuto della scala.
for i in range(maxpix + 1):
[i]=temp[i]
Il resto dei LED sovrastanti li impostiamo su off=0,0,0.
for i in range(maxpix + 1,numOfPix):
np[i]=da
Se la temperatura è negativa, impostiamo anche il punto zero. Quindi inviamo il contenuto del buffer al display.
if t < 0:
np[pos0]=verde
np.write()
Possiamo spegnere l'intera striscia LED con la funzione clearStrip(). Descriviamo il buffer con tuple (0,0,0).
def clearStrip():
for i in range(numOfPix):
np[i]=da
np.write()
I tre eventi di lampeggiamento per temperatura superiore e inferiore e 0 °C vengono gestiti con l'aiuto delle tre funzioni binkRed(), blinkBlue() e blinkGreen(). Tutte hanno la stessa struttura. Esaminiamo più da vicino blinkRed() a titolo esemplificativo.
def blinkRed():
globale statered
if statered == 1:
np[numOfPix-1]=da
np.write()
statered = 0
else:
np[numOfPix-1]=rosso scuro
np.write()
statered = 1
Il valore della variabile statered viene modificato nella procedura e deve essere nuovamente disponibile alla successiva chiamata. Un modo per ottenere questo risultato è utilizzare variabili globali. Senza la dichiarazione global, la variabile scomparirebbe nel nulla all'uscita dalla funzione. Un altro modo per rendere statica la variabile sarebbe l'uso di una chiusura. Seguite il link se volete saperne di più. Per i principianti, ci atteniamo alla prima soluzione, più semplice.
Se statered è uguale a 1, il LED superiore deve essere spento. numOfPix ha il valore 60. Il LED superiore ha quindi il numero 59, perché la numerazione inizia da 0. Inviamo lo stato (di tutti!) i LED con np.write() alla striscia e impostiamo statered su 0.
Alla chiamata successiva viene eseguito il ramo else. Impostiamo il contenuto del LED superiore su rosso scuro, inviamo il buffer e impostiamo statered su 1.
Analogamente trattiamo il LED più in basso (numero 0) e il LED del punto zero nella posizione pos0 = 20.
def blinkBlue():
globale stateblue
if stateblue == 1:
np[0]=da
np.write()
stateblue = 0
altro:
np[0]=azzurro
np.write()
stateblue = 1
def blinkGreen():
globale stategreen
if stategreen == 1:
np[pos0]=da
np.write()
stategreen = 0
altro:
np[pos0]=verde
np.write()
stategreen = 1
Grazie all'utilizzo di funzioni, creiamo una panoramica nel programma principale, che comprende quindi solo 15 righe.
Generiamo la scala del termometro ed eliminiamo i LED, quindi entriamo nel ciclo principale.
setScale()
clearStrip()
Viene avviata la misurazione del valore attuale della temperatura. Il controller può contare le pecore per un secondo e poi determina il valore intero della temperatura letta arrotondando per eccesso.
while 1:
ds.convert_temp()
sleep(1)
=int(ds.read_temp(device) + 0,5)
stampa(t,"C")
fillAndShow(t)
if t < minTemp:
blinkBlue()
if t > maxTemp:
blinkRed()
if t == 0:
blinkGreen()
Possiamo eliminare il comando di stampa nella produzione. fillAndShow() mostra la temperatura sulla striscia, come descritto sopra. Con i costrutti if trattiamo i casi speciali.
Ecco il listing completo del programma ledstrip_thermo.py:
# ledstrip_thermo.py
# Dopo aver eseguito il flashing del firmware sull'ESP8266:
# import webrepl_setup
# > d per disabilitare
# Quindi RST; riavvio!
# Pintranslator per schede ESP8266
#Pin LUA D0 D1 D2 D3 D4 D5 D6 D7 D8 RX TX
#ESP8266 Pin 16 5 4 0 2 14 12 13 15 3 1
#Attenzione hi sc sd hihi lo hi hi
# utilizzato OLED DS LE
da macchina import Pin
da time import sleep
da onewire import OneWire
da ds18x20 import DS18X20
da neopixel import NeoPixel
da sys import exit, piattaforma
tasto=Pin(0,Pin.IN,Pin.PULL_UP)
dsPin=Pin(2) # ESP8266 (D4)
ds = DS18X20(OneWire(dsPin))
device = ds.scan()[0]ds.convert_temp()
sleep(0,750)
stampa(device,ds.read_temp(device))
led=Pin (0,Pin.OUT,valore=0) # ESP8266 (D3)
numOfPix=60
np=NeoPixel(led,numOfPix)
pos0 = 20
maxTemp=39
minTemp=-20
temp=[0 for i in intervallo(60)]
da=(0,0,0)
verde=(0,64,0)
blu scuro=(0,0,4)
azzurro=(0,0,128)
rosso scuro=(4,0,0)
rosso chiaro=(128,0,0 )
magenta=(2,0,2)
statered=0
statered=0
stategreen=0
def setScale():
for i in range(20):
temp[i]=blu scuro
for i in range(21,60):
temp[i]=rosso scuro
for i in [0,10]:
temp[i]=azzurro
for i in [30,40,50]:
temp[i]=rosso chiaro
for i in range(5 ,60,10):
temp[i]=magenta
temp[20]=verde
def fillAndShow(temperatura):
t=min(temperatura,maxTemp)
t=max(minTemp,t)
maxpix = int(t) + pos0
for i in range(maxpix + 1):
np[i]=temp[i]
for i in range(maxpix + 1,numOfPix):
np[i]=da
if t < 0:
np [pos0]=verde
np.write()
def clearStrip():
for i in range(numOfPix):
np[i]=da
np.write()
def blinkRed():
globale statered
if statered == 1:
np[numOfPix-1]=da
np.write()
statered = 0
else:
np [numOfPix-1]=rosso scuro
np.write()
statered = 1
def blinkBlue():
global stateblue
if stateblue == 1:
np[0]=da
np.write()
stateblue = 0
altro:
np[0]=azzurro
np.write()
stateblue = 1
def blinkGreen():
globale stategreen
if stategreen == 1:
np[pos0]=da
np.write()
stategreen = 0
else:
np[pos0]=verde
np.write()
stategreen = 1
setScale ()
clearStrip()
while 1:
ds.convert_temp()
sleep(1)
t=int(ds.read_temp(device) + 0,5)
stampa(t,"C")
fillAndShow(t)
if t < minTemp:
blinkBlue()
if t > maxTemp:
blinkRed()
if t == 0:
blinkGreen()
Per essere indipendenti dal PC e dall'USB, possiamo caricare il programma con il nome main.py nella memoria flash del controller. Ora è sufficiente un'alimentazione a 5 V affinché il programma si avvii automaticamente al prossimo riavvio o dopo un reset.
Ecco come appare la parte intorno allo zero durante il funzionamento.

Figura 7: indicazione del termometro da -6 °C a 2 °C
Prospettive:
La scelta dei colori per i LED ci lascia alcune possibilità aperte. Ad esempio, si potrebbe collegare al bus One-Wire un secondo sensore per la temperatura esterna e cambiare la visualizzazione ogni cinque secondi. Utilizzando colori diversi per le due aree è possibile distinguere facilmente la temperatura esterna da quella interna.
Utilizzando un lettore di schede SD e un RTC, è possibile registrare le misurazioni con precisione temporale per una successiva valutazione.
Se utilizziamo la funzionalità WLAN dei nostri controller, i dati di misurazione possono essere facilmente trasferiti su un cellulare, un tablet o un server.
Buon divertimento con le vostre ricerche, i vostri progetti e la programmazione.






