Měříme analogové napětí pomocí ESP32 v MicroPythonu
U modulu ESP32 jsou některé piny připojeny k integrovanému analogově-digitálnímu převodníku (ADC), který převádí úroveň vstupního napětí na číselnou hodnotu úměrnou tomuto vstupnímu napětí. Maximální rozlišení AD převodu je 12 bitů, takže získané hodnoty jsou pak v rozmezí hodnot 0 až 4095.
- UPOZORNĚNÍ:
- Na rozdíl od modulu Arduino nesmí analogové vstupy na modulu ESP32 překročit napětí 3,3 V. Překročení této hodnoty by mohlo poškodit nebo dokonce zničit ADC vstupy.
Možnost načítání analogové hodnoty připojeného napětí pomocí ADC vstupu lze použít ve své aplikaci pro různé účely. Namátkově lze uvést kupříkladu možnost sledování napájecího napětí modulu ESP32 v některé z bateriových aplikací.
Docela zajímavým způsobem využití analogového vstupu je i možnost načítání několika tlačítek připojených pouze k jedinému vstupu. To někdy může docela efektně vyřešit problém chybějících vstupních digitálních pinů. Tento způsob načítání si ukážeme na případu hned osmi tlačítek, ale z principu je jasné, že jej lze použít i pro jiný počet.
Tlačítka T1, T2, … T8 připojíme ke zvolenému ADC pinu dle následjícího zapojení:
Zvolený ADC pin je přes rezistor R0 připojen k napájecímu napětí (v případě modulu ESP32 je UCC = 3,3 V). Při nestisknutém žádném tlačítku je vstupní napětí na ADC pinu 3,3 V, což při načtení vstupní hodnoty odpovídá hodnotě 4095 (v ideálním případě!). Jakmile stiskneme některé z tlačítek, připojíme jeho rezistor k zemi a tím vytvoříme tzv. dělič napětí. Na ADC pinu tedy již nebude napájecí napětí UCC, ale bude zde napětí odpovídající poměru zapojených rezistorů. Pokud rezistor příslušný stisknutému tlačítku obecně označíme jako RT (kde T může být 1, 2, … 8), vznikne dělič napětí složený ze sériově zapojených rezistorů R0 a RT. Přes oba rezistory poteče proud dán jejich sériovým zapojením, na vstupu ADC bude napětí dané dle Ohmova zákona:
Následující tabulka ukazuje vstupní napětí na ADC pinu při stisknutí jednotlivých tlačítek:
Stisknuté tlačítko |
Vstupní napětí UADC |
Načtená hodnota ADC |
---|---|---|
T1 | 2,7 V | 3357 |
T2 | 2,3 V | 2861 |
T3 | 1,98 V | 2457 |
T4 | 1,72 V | 2137 |
T5 | 1,34 V | 1660 |
T6 | 0,73 V | 900 |
T7 | 0,43 V | 534 |
T8 | 0 V | 0 |
Dokonce, pokud bychom zmáčkli třeba dvě tlačítka vznikne na dolní větvi děliče napětí na paralelním zapojení rezistorů, tedy i tomuto stavu lze přisoudit vstupní napětí. Výjimkou by zde byla jakékoliv kombinace s tlačítkem T8, které připojuje ADC vstup přímo „natvrdo“ proti úrovni 0 V (GND).
Pokud bychom chtěli takto jedním ADC vstupem načítat osm tlačítek, je třeba ještě upozornit, že není dobré testovat přímo vypočítané vstupní hodnoty z tabulky. Zaprvé nelze vždy předpokládat napájecí napětí přesně 3,3 V, zadruhé ADC vstup modulu ESP32 není ideálně lineární. Každopádně, pokud budeme testovat hodnoty z určitého intervalu „okolo“ odpovídající hodnoty, můžeme tuto techniku úspěšně použít.
Ale vraťme se k obecnému načítání ADC pinů. Ještě než, se pustíme do samotné ukázky, jak načítat ADC vstupy v MicroPythonu, je třeba upozornit na jeden technický detail. Modul ESP32 má několik pinů, které lze nakonfigurovat jako analogově-digitální převodníky (ADC). Bohužel jsou o něco složitější na použití než u některých jiných mikrokontrolérů. Vstupní piny modulu ESP32 jsou rozděleny do několika ADC skupin. Piny GPIO32–36 a 39 jsou skupina ADC1, piny GPIO 0, 2, 4, 12–15, 25–27 jsou ze skupiny ADC2. MicroPython umožňuje „rozumně“ pracovat s ADC jen ze skupiny ADC1 (viz poznámka). Toto je důležité vědět, neboť to ovlivňuje jejich výběr při možném použití.
- UPOZORNĚNÍ:
- Pro použití ADC2 existují určitá omezení:
- Aplikace může ADC2 používat pouze tehdy, když není spuštěn ovladač WI-FI, protože ADC používá také ovladač WI-FI, který má vyšší prioritu.
- Některé z pinů ADC2 se na vývojových kitech modulu ESP32 používají pro jiné speciální účely (GPIO 0, 2, (4), 15), takže je nelze volně používat.
Další důležitou informací, kterou je třeba vědět, je fakt, že GPIO piny jsou při použití pro analogový vstup osazeny předzesilovači. Rozsah načtení vstupního napětí modulem ESP32 tedy závisí na nastavení útlumu těchto vstupních předzesilovačů.
Hodnoty útlumů, odpovídajících maximálních vstupních napětí a odpovídajících konstant pro nastavení uvádí následující tabulka:
Nastavený útlum | Napěťový rozsah | Konstanta objektu ADC |
---|---|---|
0 dB | 1,2 V | ATTN_0DB |
2,5 dB | 1,5 V | ATTN_2_5DB |
6 dB | 2,0 V | ATTN_6DB |
11 dB | 3,3 V | ATTN_11DB |
- POZNÁMKA:
- Vzhledem k vlastnostem ADC pinů (problém linearity ADC pinů modulu ESP32) se nejpřesnějších výsledků dosahuje v následujících přibližných napěťových rozsazích:
- 0 dB … 100–950 mV
- 2,5 dB … 100–1250 mV
- 6 dB … 150–1750 mV
- 11 dB … 150–2450 mV
Čtěme analogový vstup na ESP32 pomocí MicroPythonu
Napětí analogového vstupu se měří pomocí objektu ADC
z modulu machine
. Je to vlastně obdobné tomu, co jsme viděli při práci s výstupy PWM nebo DAC. Takže pokud budeme chtít načít analogovou hodnotu napětí kupříkladu na pin 32, bude kód vypadat následujícím způsobem:
from machine import Pin, ADC
adc_pin = Pin(32, mode=Pin.IN)
adc = ADC(adc_pin) # prirazeni ADC pinu
adc.atten(ADC.ATTN_11DB) # nastaveni utlumu
Pokud chceme měřit napětí v rozsahu 0 až 3,3 V, nastavili jsme pomocí příkazu adc.atten(ADC.ATTN_11DB)
na předzesilovači asociovaného vstupu útlum 11 dB (viz předcházející tabulka).
Je též možné objekt ADC
vytvořit zadáním čísla analogového kanálu, pod kterými jsou GPIO piny na modulu ESP32 dostupné.
from machine import Pin, ADC
adc = ADC(4) # zvolen ADC kanal ADC_4 (pin 32)
- POZNÁMKA:
- Na ESP32 jsou analogové kanály přiřazeny pinům neuspořádaně. Je proto lepší použít definici podle čísla PIN.
Jakmile v MicroPythonu nakonfigurujete analogový vstup, stačí pouze ke čtení analogové hodnoty použít funkci read()
.
from machine import Pin, ADC
adc = ADC(Pin(32, mode=Pin.IN))
adc.atten(ADC.ATTN_11DB)
print(adc.read())
- DŮLEŽITÉ:
- Všimněme si možného výrazně zkráceného (kompaktního) zápisu pro deklaraci proměnné
abc
: -
adc = ADC(Pin(32, mode=Pin.IN))
- To se jistě občas hodí. 😉
Ovládání intenzity LED pomocí potenciometru
Pro ukázku využití analogového vstupu v microPythonu se pokusíme o následující projekt. Pomocí potenciometru (nebo trimru) budeme měnit vstupní napětí na ADC vstupu. Dle naměřeného vstupního napětí pak budeme pomocí PWM měnit svit připojené LED.
Začneme sestavením elektrického schéma. LED přes ochranný rezistor (220 Ω) zapojíme mezi piny GND a GPIO27. Potenciometr zapojíme svými krajními vývody mezi napájecí napětí 3,3 V a GND. Jezdec přivedeme na pin GPIO32.
Chceme-li odečítat polohu samotného potenciometru a vypisovat naměřené napětí, použijeme následující kód:
from machine import Pin, ADC
import time
adc = ADC(Pin(32, mode=Pin.IN))
adc.atten(ADC.ATTN_11DB)
while True:
val = adc.read()
val = val * (3.3 / 4095) # prevod na napeti
print(round(val, 2), "V") # uved na 2 des. mista a pripoj V
time.sleep_ms(100)
Nyní kód upravíme pro PWM řízení připojneé LED. Abychom si kromě elektronické části ukázali i nějakou další vlastnost jazyka Python, využijeme pro nastavení PWM výstupu vlastní funkci (nazvanou
map
):
from machine import Pin, ADC, PWM
import time
adc = ADC(Pin(32, mode=Pin.IN))
adc.atten(ADC.ATTN_11DB)
pwm_led = PWM(Pin(27, mode=Pin.OUT))
pwm_led.freq(1_000)
def map(x, in_min, in_max, out_min, out_max):
return (x-in_min) * (out_max-out_min) // (in_max-in_min) + out_min
while True:
val = adc.read()
pwm_value = map(x=val, in_min=0, in_max=4095, out_min=0, out_max=1023)
pwm_led.duty(pwm_value)
time.sleep_ms(10)
V rámci našeho průběžného pythonovského okénka si opět představíme něco z Pythonu. Dnes je to uživatelem definovaná funkce. V tomto případě se jedná o funkci map
, kterou možná někdo zná z jazyka Wire prostředí Arduino IDE. Účel funkce je následující: Zadanou hodnotu x
, která je z intervalu mezi hodnotami in_min
až in_max
, převést („přemapovat“) na výstupní hodnotu z intervalu mezi hodnotami out_min
až out_max
.
Definici této uživatelem definované funkce najdeme v první části kódu, ještě před nekonečnou smyčkou hlavního běhu. Definice funkce je dána klíčovým slovem def, pak následuje název funkce a v následující závorce jsou deklarované vstupní parametry. V těle funkce (odsazená část) je pak kód funkce. Pokud má funkce návratovou hodnotu, vrací se tato výstupní hodnota pomocí příkazu return, za kterým následuje hodnota, proměnná s hodnotou nebo výraz určující hodnotu, která má být navrácena.
V případě naší funkce map funkce žádné tělo nemá, neboť je celý převod proveden přímo v příkazu return. Po úvodní definici je funkce map
použita běžným způsobem v další části kódu. To je však běžná věc, kterou známe i z jiných programovacích jazyků.
Za zmínku snad ještě stojí použitý operátor //
, který znamená celočíselné dělení.
Kód máme hotový, můžeme jej uložit do modulu ESP32. Nyní bychom měli pomocí otáčení potenciometru regulovat jas připojené LED. A to vše díky načítání analogového napětí na vstupním ADC pinu.
Při otočení potenciometrem, získáváme standardně hodnoty od 0 do 4095, protože že piny ADC mají ve výchozím nastavení 12bitové rozlišení. Občas se nám může hodit získat hodnotu v jiném rozlišení. Rozlišení ADC vstupů můžeme změnit pomocí metody width()
.
Kupříkladu u naší proměnné adc
(objekt ADC
) by to bylo takto:
adc.width(bit)
kde místo argumentu bit
je zadán jeden z následujících parametrů (viz tabulka):
Rozlišení ADC | Konstanta modulu ADC |
---|---|
0–511 | ADC.WIDTH_9BIT |
0–1023 | ADC.WIDTH_10BIT |
0–2047 | ADC.WIDTH_11BIT |
0–4095 | ADC.WIDTH_12BIT |
Takže, například při nastavení rozlišení vstupu adc.width
jsme vůbec nemuseli řešit přemapování standardně 12bitového ADC vstupu na 10bitový PWM výstup pomocí funkce map()
, neboť by byl vstup rovnou načítán jako 10bitový.
Ale to bychom si zase nemohli ukázat, jak lze v Pythonu zadávat uživatelské funkce s návratovou hodnotou, že? 😉
Shrnutí:
- Chceme-li přečíst analogovou hodnotu ADC pinem modulu ESP32, musíme do kódu importovat třídu
ADC
z modulumachine
. - Pro výtvoření objektu
ADC
použijeme kostrukciADC(Pin(GPIO))
, kdeGPIO
je číslo GPIO pinu, ze kterého chcete číst analogové hodnoty. - Analogovou hodnotu načteme metodou
.read()
. - Dle napěťového rozsahu vstupního napětí je třeba nastavit útlum předzesilovače na analogovém pinu. K tomu slouží metoda
.atten(attenuation)
, kdeattenuation
je konstanta požadovaného útlumu (viz tabulka výše). - Rozlišení načítané analogové hodnoty na zadaném pinu lze změnit metodou
.width(bit)
, kdebit
je konstanta odpovídající rozlišení (viz tabulka výše).
Dnes jsme poznali další z možností práce s GPIO piny modulu ESP32. Všechny dosud dříve popsané způsoby práce s vstupně/výstupními piny jsou asi obdobné práci s piny jiných mikrokontrolérů. Modul ESP32 má však ještě jednu možnost načítání některých vstupních pinů. Je to reakce na dotyk, kdy některé piny mohou fungovat jako kapacitní spínače.
To tedy bude téma našeho dalšího příspěvku.