ESP32: Barometrické čidlo BMP180 v MicroPythonu
Máme po čase další díl našeho „volného“ seriálu článků věnovaných MicroPythonu na modulu ESP32. V několika předchozích článcích jsme se již dotkli toho, jak připojovat různé periferie k modulu ESP32, a v jednom případě už dokonce i přes I2C sběrnici. Tentokrát se na sběrnici I²C však zaměříme více, protože bude hrát klíčovou roli při komunikaci s barometrickým senzorem BMP180. Pokusíme se nejen připojit toto čidlo k modulu ESP32, ale hlavně si ukážeme, jak v MicroPythonu vyřešit problém načítání měřených veličin.
Pochopitelně na závěr zkusíme vytvořit i nějaké komplexnější „praktické“ řešení. Tento rozsáhlejší projekt, který by měl využít mnoho z toho, co všechno jsme se během celého našeho seriálu naučili. A vězte, že jsme se toho během těch dosavadních pětadvaceti díl už o MicroPythonu na modulu ESP32 docela dost naučili!
Barometrické a teplotní čidlo BMP180
Čidlo BMP180 je malý a levný barometrický senzor, který kromě atmosférického tlaku dokáže měřit i teplotu. Jeho přesnost asi nedosahuje úrovně profesionálních meteorologických stanic, ale pro běžné amatérské projekty – například domácí meteostanici, měření nadmořské výšky nebo řízení větrání – je svou přesností více jak dostačující! Hlavní výhodou pro naše hrátky je především jeho dostupnost, malé rozměry, nízká cena, a díky I²C rozhraní i snadná komunikace.

Sběrnice I²C
O sběrnici I²C jsme se již v jednom z předešlých článků zmínili, ale protože to dnes bude především jen o čidlu pracujícím na sběrnici I²C, bude lepší si některé základní informace zopakovat.
I²C (Inter-Integrated Circuit) je dvoudrátová sériová komunikační sběrnice navržená pro jednoduché propojení mikrokontrolérů s periferními zařízeními, jako jsou senzory, displeje nebo paměti EEPROM. Pro přenos dat využívá dvě linky:
- SDA (Serial Data) – datová linka
- SCL (Serial Clock) – hodinová linka
Obě linky jsou typu open-drain a vyžadují tedy externí pull-up rezistory (typicky 4,7–10 kΩ) připojené na napájecí napětí sběrnice (obvykle 3,3 V nebo 5 V).
I²C využívá sběrnicovou topologii, kde jedno zařízení vystupuje jako master (řídící jednotka, např. modul ESP32), a jedno nebo více zařízení jako slave (podřízené periferie, zpravidla nějaké senzory). Každý slave má unikátní 7bitovou nebo 10bitovou adresu, kterou určuje výrobce nebo ji lze částečně nastavit hardwarově (např. pomocí propojek nebo pájecích plošek). Výhodou I²C je jednoduchá topologie (sběrnice), která poskytuje možnost připojení více zařízení současně na stejné vodiče. Další výhodou je široká podpora této sběrnice u různých čidel a mikrokontrolérů. Pro kratší vzdálenosti a nízké až střední rychlosti je sběrnice I²C velmi efektivní řešení.
Modul ESP32 podporuje více I²C rozhraní současně (I2C(0)
a I2C(1)
), přičemž defaultní piny jsou:
Modul ESP32 | ||
---|---|---|
I2C(0) | I2C(1) | |
SDA | GPIO21 | GPIO33 |
SCL | GPIO22 | GPIO32 |
MicroPython umožňuje použít jak hardwarové I²C (modul I2C
), tak i softwarovou emulaci (modul SoftI2C
), která dovoluje provoz sběrnice na libovolných digitálních pinech.
- Standard Mode – až 100 kbps
- Fast Mode – až 400 kbps
- Fast Mode Plus – až 1 Mbps (ESP32 může podporovat)
- High-Speed Mode – až 3,4 Mbps (méně běžné, ne všechny čipy podporují)
- Výhody:
- Jednoduché vedení sběrnice (2 vodiče pro všechna zařízení)
- Podpora více zařízení na jedné sběrnici
- Snadná implementace a široká dostupnost čidel
- Omezení:
- Omezená délka sběrnice (typicky do 1 metru, závisí na rychlosti a kapacitě vedení)
- Omezená přenosová rychlost (v porovnání s např. SPI)
- Náchylnost na rušení při delším vedení nebo při chybějících pull-up rezistorech
Připojení čidla BMP180 k modulu ESP32
Senzor BMP180, jak bylo naznačeno, komunikuje přes sběrnici I²C. Modul ESP32 má naštěstí několik hardwarově podporovaných I²C pinů, pro I2C(0)
to jsou obvykle GPIO21
(SDA) a GPIO22
(SCL). Pokud tedy nemáme zvláštní důvod, doporučujeme začít právě s těmito piny.
Schéma zapojení bude tedy jednoduché (viz obrázek a tabulka propojení)

BMP180 | ESP32 |
---|---|
VCC | 3V3 |
GND | GND |
SCL | GPIO22 |
SDA | GPIO21 |
- Poznámka:
- Některé moduly čidla BMP180 mají na desce i pull-up rezistory mezi Ucc a piny SCL a SDA, pokud bychom měli modul, který tyto rezistory nemá, můžeme je doplnit externě (např. 4.7 kΩ mezi SDA/SCL a 3.3V). Pro první pokusy to ale často funguje i bez nich. 😊
Modul ESP32 i jeho MicroPython nám dává i možnost – použít tzv. softwarovou I²C sběrnici pomocí třídy SoftI2C
. Budeme si pamatovat, že, pokud bychom potřebovali použít i piny bez hardwarové podpory I²C, můžeme použít modul I2CSoft
. Pomocí SoftI2C
pak lze libovolně zvolit, které piny použijeme pro SDA a SCL – stačí to nastavit při inicializaci softwarové I²C sběrnice.
To se hodí v případě, že potřebujeme klasické piny uvolnit nebo už na nich máme připojeného něco jiného. I když, jak už o sběrnici I²C víme, senzory se na ni dají připojovat „neomezeně“, protože komunikace se řídí pomocí adresace. Problém by však nastal při připojení dvou čidel BMP180 na stejnou I²C sběrnici, neboť adresa obou čidel BMP180 je pevně stanovena výrobcem (hodnota 119).
Načítání dat z BMP180 v MicroPythonu
Máme-li po elektrické stránce vše připojeno, můžeme začít programovat. Nejdříve se pustíme do jednoduchého programu, který nám ukáže, jak ze senzoru BMP180 načíst hodnoty teploty a tlaku. Senzory komunikující na jakékoliv sběrnici, a ani I²C není výjimkou, je dobré řídit pomocí již existující knihovny. Zkusíme se tedy podívat po internetových komunitách a najít pro čidlo BMP180 již hotovou knihovnu. Knihovnu pojmenovanou bmp180.py
najdeme hned v několika článcích různých autorů. Řada z citovaných knihoven je běžně dostupná na GitHubu nebo v komunitních zdrojích MicroPythonu. Ale jako už jsme viděli kupříkladu u knihovny věnované I²C ovladači serva, ne vždy je knihovna přímo použitelná pro modul ESP32 a ne vždy je použitelná pro aktuální verzi firmwaru
MicroPythonu na modulu ESP32.
Bohužel tato situace nastává i v případě knihovny pro modul BMP180. Knihovna bmp180.py
, kterou jsme našli hned v několika různých verzích (a z několika různých zdrojů), vždy vyvolaly chybu programu. A to, i když jsme ji použili přímo s jejím ukázkovým programem daného autora! Nakonec se ukázalo, že ve všech případech jde o problém s inicializací I²C sběrnice. Skoro se nám zdá, že asi v některém z posledních firmwarů MicroPythonu pro ESP32 došlo k nějaké změně v modulu I2C
, čímž se tyto knihovny staly s dnešním MicroPythonem modulu ESP32 nekompatibilní. Ale je to jen naše spekulace!
Řešením vzniklé situace, podobně jako u knihovny pro ovládání serva pomocí I²C, byla naše úprava nefungující knihovny. Dále tedy uvádíme upravenou (opravenou?) knihovnu, která nyní (zatím?) funguje. Doufejme, že za nějaký čas se díky nějaké podobně nemilé události, naše knihovna nestane jen dalším nefunkčním kódem, jako tomu je u knihoven bmp180.py
, na které jsme při psaní tohoto článku narazili. 😒
Upravený modul bmp180_ext.py
Když už jsme museli zasáhnout do kódu původní knihovny bmp180.py
, abychom ji vůbec zprovoznili, zkusili jsme ji upravit trochu více. Do původní knihovny jsme přidali funkci, které se jistě hodí všem, kteří chtějí měřit tlak v pevně umístěné meteorologické stanici s jasně známou nadmořskou výškou. V takové situaci pak dává smysl přepočet tlaku na nadmořskou hladinu, co ale původní knihovna neumožňovala. V původní knihovně bylo možné jen ze zadané výchozí hodnoty tlaku na hladině moře (konstanta baseline
) naopak dopočítat nadmořskou výšku.
Druhou změnou byla úprava výpočtu oné nadmořské výšky. Ne nadarmo bychom byli Fyzikální kabinet, abychom někde „nevyškrábli“ přesnější vzoreček! 😊
Funkce sea_level_pressure()
Kromě změny metody určení nadmořské výšky z atmosférického tlaku jsme do knihovny přidali funkci sea_
, která přepočítává tlak na hladinu moře. Když měříme atmosférický tlak, měříme ho ve výšce, kde se právě nacházíme. Ale pokud chceme tuto hodnotu porovnávat s jinými měřeními hodnotami (například s údaji z jiné meteostanice v našem městě), musíme se shodnout na společné referenci – a tou je tlak přepočtený na hladinu moře. Proto když máme pouze „surovou“ hodnotu tlaku v místě měření, moc nám toho sama o sobě neřekne. Abychom zjistili, jaká je skutečná meteorologická situace (například blíží-li se tlaková níže), musíme tuto hodnotu přepočítat tak, jako bychom ji měřili u moře. K přepočtu na tuto hodnotu nám slouží funkce sea_
.
Funkce využívá naměřenou hodnotu tlaku, teploty a zadanou nadmořskou výšku. Je-li funkce volána s parametrem nadmořské výšky (v metech) vrátí přepočet na hladinu moře dle této zadané nadmořské výšky. Pokud žádný parametr do funkce nezadáme, funkce si za nadmořskou výšku dosadí hodnotu, kterou si sama spočítá pomocí vlastnosti altitude z výchozí zadané referenční hodnoty tlaku. (viz dále popis metod a atributů upravené knihovny).
Metody a vlastnosti knihovny BMP180_ext.py
Tím, že jsme původní knihovnu nejen upravili, ale i rozšířili, nechceme ji dále prezentovat pod stejným názvem jako předešlé knihovny bmp180.py
. Aby se zbytečně nepletla s těmi v tuto chvíli nefungujícími verzemi, přejmenovali jsme ji na bmp180_
.
Ještě než uvedeme zdrojový kód knihovny bmp180_ext.py
, představíme si její metody a vlastnosti.
Knihovna bmp180_
obsahuje základní třídu BMP180
. Tato třída umožňuje:
- číst teplotu (°C)
- číst tlak (Pa)
- spočítat nadmořskou výšku (m)
- vypočítat z nadmořské výšky tlak na hladině moře
- nastavovat režim přesnosti (oversampling) načítání čidla BMP180
- nastavit výchozí tlak na hladině moře (využito pro výpočet nadmořské výšky)
Knihovna se naimportuje do programu a pro přístup k čidlo BMP180 je třeba vytvořit objekt třídy BMP180
. Zadaným parametrem je u tohoto objektu platný objekt I²C sběrnice (buď hardwarové, nebo softwarové). Přestože při volbě hardwarového řadiče sběrnice I²C by měly být v MicroPythonu již nastaveny výchozí piny linek SDA a SCL, je potřeba je v definici objektu sběrnice I²C uvést – viz následující ukázka kódu.
from machine import I2C, Pin
from bmp180_ext import BMP180
i2c_bus = I2C(0, scl=Pin(22), sda=Pin(21))
sensor = BMP180(i2c_bus)
Objekt sensor
, pak získá pro nás využitelné následující metodu a atributy:
- .altitude
- Vypočítá a vrátí nadmořskou výšku podle barometrické rovnice, hodnota je uvedena v metrech. Využívá metody pro aktuální teplotu (
temperature
), tlak (pressure
) a nastavenou výchozí hodnotu tlaku na hladině mořebaseline
. - .baseline
- Nastavení atmosférického tlaku na hladině moře (v Pa) – využíváno např. pro výpočet nadmořské výšky.
- Výchozí hodnota:
101325
(standardní tlak na hladině moře) - .blocking_
read() - Spustí měření a počká, až budou data k dispozici.
- Data je třeba načítat v rychlých smyčkách, pokud funkci
pressure()
spustíme jednou a pak znovu za 10 sekund, vrátí 10 sekund starou hodnotu. V takovém případě je třeba před měřením použít metodublocking_read()
. - .oversample_
sett - Nastavuje přesnost měření. Vyšší číslo = vyšší přesnost měření, ale delší odezva (a vyšší spotřeba) senzoru.
- Možné hodnoty: 0, 1, 2, 3 (výchozí hodnota 3)
- Hodnota 0 – načtení: 1 vzorek – čas měření: 4,5 ms
- Hodnota 1 – načtení: 2 vzorky – čas měření: 7,5 ms
- Hodnota 2 – načtení: 4 vzorky – čas měření: 13,5 ms
- Hodnota 3 – načtení: 4 vzorky – čas měření: 25,5 ms
- .pressure
- Načte a vrátí atmosférický tlak, hodnota je uvedena v Pa (hodnota
float)
, využívá aktuální teplotu a interní kalibraci. - .sea_
level_ pressure( altitude) - Načte aktuální atmosférický tlak a přepočítá jej pomocí barometrické rovnice na tlak na hladině moře. Využívá metody pro aktuální teplotu (
temperature
), tlak (pressure
) a vstupní hodnotu nadmořské výšky (v metrech) zadanou ve vstupním parametrualtitude
. Není-li vstupní parametr zadán, dosadí se do něj výsledek zaltitude
. - .temperature
- Načte čidlo a vrátí aktuální teplotu, hodnota je ve °C (hodnota
float
).
Kromě výše uvedených vlastností jsou k dispozici i některé další, ale ty pro běžné hrátky s čidlem již nejsou potřeba, např.:
- .compvaldump()
- Vrací seznam všech kalibračních konstant ze senzoru. Senzor BMP180 má ve své vnitřní EEPROM paměti uloženy kalibrační konstanty, které jsou jedinečné pro každý kus senzoru. Výrobce je tam uloží při kalibraci během výroby. Funkce vrací pole kalibračních hodnot. (Používá se pro ladění)
Následující programový kód je zdrojový výpis knihovny bmp180_ext.py
. Kód je potřeba překopírovat do prostředí Thonny IDE a uložit do složky lib
na modulu ESP32. Pro uložení knihovny do modulu ESP32 lze využít v menu Soubor volbu Uložit jako… nebo Uložit kopii… a zvolit volbu Micropython zařízení (viz obrázek).

BMP180_ext.py
(s ohledem na rozsah kódu jej uvádíme bez obarvení syntaxe)
# https://github.com/micropython-IMU/micropython-bmp180 (UPRAVENO!!!)
# Modifications: FyzKAB, 2025
# Modified to work on the ESP32 module (MicroPython firmware v1.25.0),
# adjusted altitude calculation,
# and added a method for sea-level pressure estimation.
'''
bmp180 is a micropython module for the Bosch BMP180 sensor. It measures
temperature as well as pressure, with a high enough resolution to calculate
altitude.
Breakoutboard: http://www.adafruit.com/products/1603
data-sheet: http://ae-bst.resource.bosch.com/media/products/dokumente/
bmp180/BST-BMP180-DS000-09.pdf
The MIT License (MIT)
Copyright (c) 2014 Sebastian Plamauer, oeplse@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
'''
from ustruct import unpack as unp
from machine import I2C, Pin
import math
import time
# BMP180 class
class BMP180():
'''
Module for the BMP180 pressure sensor.
'''
_bmp_addr = 119 # adress of BMP180 is hardcoded on the sensor
# init
def __init__(self, i2c_bus):
# create i2c obect
_bmp_addr = self._bmp_addr
self._bmp_i2c = i2c_bus
self.chip_id = self._bmp_i2c.readfrom_mem(_bmp_addr, 0xD0, 2)
# read calibration data from EEPROM
self._AC1 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xAA, 2))[0]
self._AC2 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xAC, 2))[0]
self._AC3 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xAE, 2))[0]
self._AC4 = unp('>H', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB0, 2))[0]
self._AC5 = unp('>H', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB2, 2))[0]
self._AC6 = unp('>H', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB4, 2))[0]
self._B1 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB6, 2))[0]
self._B2 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB8, 2))[0]
self._MB = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xBA, 2))[0]
self._MC = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xBC, 2))[0]
self._MD = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xBE, 2))[0]
# settings to be adjusted by user
self.oversample_setting = 3
self.baseline = 101325.0
# output raw
self.UT_raw = None
self.B5_raw = None
self.MSB_raw = None
self.LSB_raw = None
self.XLSB_raw = None
self.gauge = self.makegauge() # Generator instance
for _ in range(128):
next(self.gauge)
time.sleep_ms(1)
def compvaldump(self):
'''
Returns a list of all compensation values
'''
return [self._AC1, self._AC2, self._AC3, self._AC4, self._AC5, self._AC6,
self._B1, self._B2, self._MB, self._MC, self._MD, self.oversample_setting]
# gauge raw
def makegauge(self):
'''
Generator refreshing the raw measurments.
'''
delays = (5, 8, 14, 25)
while True:
self._bmp_i2c.writeto_mem(self._bmp_addr, 0xF4, bytearray([0x2E]))
t_start = time.ticks_ms()
while (time.ticks_ms() - t_start) <= 5: # 5mS delay
yield None
try:
self.UT_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF6, 2)
except:
yield None
self._bmp_i2c.writeto_mem(self._bmp_addr, 0xF4, bytearray([0x34+(self.oversample_setting << 6)]))
t_pressure_ready = delays[self.oversample_setting]
t_start = time.ticks_ms()
while (time.ticks_ms() - t_start) <= t_pressure_ready:
yield None
try:
self.MSB_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF6, 1)
self.LSB_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF7, 1)
self.XLSB_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF8, 1)
except:
yield None
yield True
def blocking_read(self):
if next(self.gauge) is not None: # Discard old data
pass
while next(self.gauge) is None:
pass
@property
def oversample_sett(self):
return self.oversample_setting
@oversample_sett.setter
def oversample_sett(self, value):
if value in range(4):
self.oversample_setting = value
else:
print('oversample_sett can only be 0, 1, 2 or 3, using 3 instead')
self.oversample_setting = 3
@property
def temperature(self):
'''
Temperature in degree C.
'''
next(self.gauge)
try:
UT = unp('>H', self.UT_raw)[0]
except:
return 0.0
X1 = (UT-self._AC6)*self._AC5/2**15
X2 = self._MC*2**11/(X1+self._MD)
self.B5_raw = X1+X2
return (((X1+X2)+8)/2**4)/10
@property
def pressure(self):
'''
Pressure in mbar.
'''
next(self.gauge)
self.temperature # Populate self.B5_raw
try:
MSB = unp('B', self.MSB_raw)[0]
LSB = unp('B', self.LSB_raw)[0]
XLSB = unp('B', self.XLSB_raw)[0]
except:
return 0.0
UP = ((MSB << 16)+(LSB << 8)+XLSB) >> (8-self.oversample_setting)
B6 = self.B5_raw-4000
X1 = (self._B2*(B6**2/2**12))/2**11
X2 = self._AC2*B6/2**11
X3 = X1+X2
B3 = ((int((self._AC1*4+X3)) << self.oversample_setting)+2)/4
X1 = self._AC3*B6/2**13
X2 = (self._B1*(B6**2/2**12))/2**16
X3 = ((X1+X2)+2)/2**2
B4 = abs(self._AC4)*(X3+32768)/2**15
B7 = (abs(UP)-B3) * (50000 >> self.oversample_setting)
if B7 < 0x80000000:
pressure = (B7*2)/B4
else:
pressure = (B7/B4)*2
X1 = (pressure/2**8)**2
X1 = (X1*3038)/2**16
X2 = (-7357*pressure)/2**16
return pressure+(X1+X2+3791)/2**4
# Fyzikální konstanty – interní, sdílené všemi instancemi
_G = 9.80665 # tíhové zrychlení (m/s2)
_M = 0.0289644 # molární hmotnost vzduchu (kg/mol)
_R = 8.31447 # univerzální plynová konstanta (J/(mol·K))
@property
def altitude(self):
'''
Výpočet nadmořské výšky (v metrech) podle přesné barometrické rovnice.
Používá aktuální teplotu ze senzoru.
'''
try:
# Získání aktuální teploty a převod na Kelviny
temp_c = self.temperature
T = temp_c + 273.15
# Získání tlaku
p = self.pressure
p0 = self.baseline
# Výpočet výšky
h = (self._R * T) / (self._G * self._M) * math.log(p0 / p)
except Exception as e:
print(f"Chyba ve výpočtu výšky: {e}")
h = 0.0
return h
def sea_level_pressure(self, altitude=None):
'''
Vypočítá tlak přepočtený na hladinu moře (QNH) v Pa,
na základě aktuálního tlaku, teploty a nadmořské výšky.
'''
try:
L = 0.0065 # teplotní gradient (K/m)
temp_c = self.temperature
pressure = self.pressure
if altitude is None:
altitude = self.altitude
T = temp_c + 273.15
p0 = pressure * (1 - (L * altitude) / T) ** (-self._G * self._M / (self._R * L))
except Exception as e:
print("Chyba při výpočtu sea-level tlaku:", e)
p0 = 0.0
return p0
Testovací program čidla BMP180
Pokud jsme uložili knihovnu bmp180_ext.py
do našeho modulu ESP32, vytvoříme následující program, který nejen otestuje funkčnost knihovny, ale zároveň nám ukáže, jak načíst hodnoty z modulu BMP180.
Tradičně nejdříve uvedeme celý program, pak si vysvětlíme jeho jednotlivé části.
from bmp180_ext import BMP180
from machine import Pin, lightsleep, I2C # SoftI2C
import time
#i2c_bus = SoftI2C(scl=Pin(22), sda=Pin(21), freq=100000)
i2c_bus = I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)
bmp180 = BMP180(i2c_bus)
bmp180.oversample_sett = 2
bmp180.baseline = 101325
while True:
bmp180.blocking_read() # zajistí čerstvá data
temp = round(bmp180.temperature, 1)
p = round(bmp180.pressure)/100
phm = round(bmp180.sea_level_pressure())/100
altitude = round(bmp180.altitude)
t = time.localtime(time.time())
print(f"Aktualni cas: {t[3]:02d}:{t[4]:02d}:{t[5]:02d}, {t[2]:02d}.{t[1]:02d}.{t[0]:04d}")
print(f"Teplota: {temp:.1f} °C")
print(f"Tlak (STP): {p:.2f} hPa")
print(f"Tlak (MSLP): {phm:.2f} hPa")
print(f"Vyska: {altitude:.0f} m")
time.sleep(0.01)
lightsleep(60000)
print()
Program pochopitelně začíná importem potřebných obslužných modulů.
from bmp180_ext import BMP180
from machine import Pin, lightsleep, I2C # SoftI2C
import time
Pro činnost čidla BMP180 potřebujeme použít I²C sběrnici, tu můžeme buď ovládat pomocí modulu I2C
(hardwarový řadič) nebo modulem SoftI2C
(softwarový řadič). Podle potřeby je nutné odkomentovat, či zakomentovat příslušné části kódu.
Z modulu bmp180_ext
importujeme třídu BMP180
, kterou použijeme pro deklaraci objektu sensor
, který nám umožňuje přístup k čidlu BMP180. Zároveň hned čidlu nastavíme přesnost měření a stanovíme výchozí hladinu atmosférického tlaku na hladině moře pomocí vlastnosti baseline
.
#i2c_bus = SoftI2C(scl=Pin(22), sda=Pin(21), freq=100000)
i2c_bus = I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)
bmp180 = BMP180(i2c_bus)
bmp180.oversample_sett = 2
bmp180.baseline = 101325
V nekonečné hlavní smyčce pravidelně načítáme hodnoty z čidla, které dle potřeby zaokrouhlujeme na potřebný počet desetinných míst, tlak převádíme na hektopascaly.
bmp180.blocking_read() # zajistí čerstvá data
temp = round(bmp180.temperature, 1)
p = round(bmp180.pressure)/100
Hodnoty atmosférického tlaku na úrovni mořské hladiny a odvislou hodnoty nadmořské výšky čidla dopočítáme pomocí k tomu určených funkcí.
phm = round(bmp180.sea_level_pressure())/100
altitude = round(bmp180.altitude)
Jelikož načítáme hodnoty každých 10 minut, vypisujeme k naměřeným hodnotám i aktuální časovou značku. Deklarujeme objekt pro práci s aktuálním časem. Objekt má strukturu pole, kde jednotlivé položky pole mají význam časových parametrů (hodina, minuta… den, měsíc, rok…).
t = time.localtime(time.time())
print(f"Aktualni cas: {t[3]:02d}:{t[4]:02d}:{t[5]:02d}, {t[2]:02d}.{t[1]:02d}.{t[0]:04d}")
Doba čekání na další měření (1 minuta) není kdovíjak dlouhá, ale i tak je asi škoda, aby se modul ESP32 v plném provozním režimu (a s odpovídající spotřebou) po tuto dobu jen „točil“ v nějaké čekací smyčce. Využijeme tedy znalosti z předchozího článku a uvedeme modul ESP32 do režimu spánku. Jelikož v článku věnovanému režimům spánku jsme spíše preferovali režim hlubokého spánku (deep sleep), zde tedy pro změnu použijeme režim lehkého spánku (light sleep). Nespornou výhodu je v tomto případě chování modulu ESP32 po probuzení z lehkého spánku, kdy normálně pokračuje v běhu programu. Celý program se tedy chová, jako bychom použili klasický příkaz sleep(), ale můžeme mít dobrý pocit, že jsme použili zase nějakou tu mazáckou vychytávku. 😊
Modul ESP32 uvedeme do lehkého spánku na 1 minutu (tj. 60000 milisekund).
lightsleep(60000)

Následující obrázek nám ukazuje získaný výstup po spuštění programu:

Komplexnější příklad použití čidla BMP180
V předchozí části dnešního článku jsme si ukázali, jak pracovat s čidlem BMP180 – jak ho připojit, jak načíst teplotu a tlak… ale pojďme to posunout dál. Nechceme zůstat u stylu: „importuj knihovnu, zavolej metodu a hotovo“. Teď si ukážeme, jak ze stejného čidla vytěžit víc – pustíme se do mini aplikace, která se už blíží reálnému nasazení.
Pochopitelně půjde jen o ukázkový „cvičný“ příklad, tedy o nic přehnaně funkčně složitého. Tento projekt má především ukázat, že i relativně jednoduchý senzor, jako je BMP180, může posloužit jako výchozí bod pro poměrně komplexní aplikaci. V rámci jednoho programu propojíme práci se senzorem, připojení k internetu, zpracování dat z externího API, synchronizaci času, výpočet nadmořské výšky a nastavení úsporného režimu provozu mikrokontroléru ESP32. I když, jak bylo naznačeno, výsledná aplikace není určena k přímému nasazení jako plnohodnotná meteostanice, demonstruje řadu technik, které se dají dále rozvíjet. Důležitější než samotný výstup je ale pro nás zkušenost, kterou si při tvorbě takového programu osvojíme – a právě v tom spočívá skutečná hodnota tohoto typu projektů.
Věříme, že tento příklad ukáže, jak zde dříve prezentované části mohou do sebe zapadat a jak z nich lze postupně stavět složitější a užitečnější systémy.
Naše požadavky na program
Cílem našeho snažení je postavit jednoduchý systém, který:
- měří teplotu a tlak pomocí čidla BMP180,
- mezi měřeními šetří energii hlubokým spánkem (deep sleep),
- pravidelně synchronizuje čas přes internet z NTP serveru,
- zohledňuje letní/zimní čas (ano, i tohle může být šikovné),
- načte si referenční hodnotu aktuálního tlaku na hladině moře z API meteowebu,
- vypočítá nadmořskou výšku podle místního a „standardního“ tlaku,
- a jako bonus: může být kdykoli ručně probuzen tlačítkem, které zároveň „ukončí“ běh aplikace.

Řešení jednotlivých částí
Postupně si projdeme jednotlivé úkoly našeho programu a ukážeme si jejich řešení. Pokud někdo bude mít zájem o hlubší seznámení s danou problematikou, které se to bude aktuálně týkat, uvedeme mu odkaz na dřívější článek, ve kterém jsme se tím zabývali.
Načtení teploty a tlaku z čidla BMP180
Základním krokem je samozřejmě získání dat z čidla BMP180. Zde se v kontentu tohoto článku neodehraje nic dramatického. Stačí připojit čidlo přes I²C a použít příslušnou knihovnu. Knihovnu bmp180_ext.py
jsme si představili dříve, teď ji prostě použijeme.
from bmp180_ext import BMP180
from machine import I2C, Pin
#I2C pro BMP180
I2C_SCL_PIN = 22
I2C_SDA_PIN = 21
print("\n--- Mereni ---")
i2c_bus = I2C(1, scl=I2C_SCL_PIN, sda=I2C_SDA_PIN, freq=100000)
bmp180 = BMP180(i2c_bus)
bmp180.oversample_sett = 2
bmp180.baseline = 101325 # vychozi, bude prepsano internetem
tepl = round(bmp180.temperature, 1)
tlak = round(bmp180.pressure)/100
print(f"Teplota: {tepl:.1f} °C",)
print(f"Tlak (STP) : {tlak:.2f} hPa")
Zjištění tlaku na hladině moře
Abychom získali aktuální nadmořskou výšku, potřebuje hodnotu atmosférického tlaku korigovanou na hladinu moře. Běžně bychom asi použili těch všude zmiňovaných 101325 Pa, ale tato hodnota se ve skutečnosti mění v čase, neboť kupříkladu závisí na počasí.
Řešení se nabízí elegantní: Připojíme modul ESP32 k internetu a zjistíme si hodnotu atmosférického tlaku přepočtenou na hladinu moře z některé z profesionálních stanic v našem okolí.
Tuto hodnotu můžeme použít buď jako referenci pro výpočet nadmořské výšky, nebo jako určitou korekci při přepočtu lokálního tlaku na standardizovaný údaj.
Připojení k internetu
Abychom mohli stáhnout aktuální atmosférický tlak (nebo přesný čas), musí se náš modul ESP32 připojit k internetu.
- Připojení modulu ESP32 k internetu jsme řešili v článku: ESP32 – jak na Wi-Fi v MicroPython?
Předpokládáme, že náš modul ESP32 je v dosahu Wi-Fi sítě, která umožňuje přístup k internetu. Modul ESP32 připojíme k této síti jako tzv. stanici (STA). Na modulu ESP32 vytvoříme jednoduchý webový klient, který odešle dotaz na API některého z meteorologických webů a vyžádá si relevantní data.
import network
# pripojeni k Wi-Fi
WIFI_SSID = "Jmeno-WIFI-site"
WIFI_PASS = "heslo-k-WIFI"
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
wlan.connect(WIFI_SSID, WIFI_PASS)
print("Pripojuji k Wi-Fi")
for _ in range(30):
if wlan.isconnected():
break
print(".", end="")
time.sleep(1)
print()
return wlan.isconnected()
if connect_wifi():
print("Wi-Fi pripojeno.")
atd.
else:
print("Nepodarilo se pripojit k Wi-Fi.")
Načtení dat a API meteorologického webu
Jakmile máme modul ESP32 připojený k internetu, můžeme odeslat dotaz na některý z meteoserverů. Data se z API takových webů se (zpravidla) vracejí v podobě JSON dat, musíme je tedy správně načíst a zpracovat.
- Jak načíst data z API meteowebu jsme si ukazovali v článku: ESP32: Načtení JSON dat z internetu).
Jelikož chceme získat data co nejrychleji a pokud možno „bez zbytečné práce“, použijeme tedy pro vytvoření webového klienta modul urequests
. Výhoda tohoto modulu je, že má vyřešené problémy s načítáním dat, které jsme si ve výše uvedeném
článku ukazovali.
Pro zpracování dat ve formátu JSON použijeme modul ujson
(vysvětlení, co je JSON také najdete rovněž v dříve uvedeném článku).
Následující ukázka ilustruje část kódu, která odesílá dotaz na meteoweb. Pro tento dotaz potřebujeme GPS souřadnice místa, ke kterému požadujeme hodnotu tlaku, a URL pro dotaz na API (obsahuje GPS souřadnice a někdy i API klíč). My využíváme web open-meteo.com, který nevyžaduje žádnou registraci, takže nepotřebujeme zadávat do URL parametr API klíče.
Z načtené odezvy webu, která má podobu strukturovaných JSON dat, vyseparujeme parametr pressure_
,
konkrétně jeho aktuální (tj. current
) hodnotu.
Jelikož se během připojení k serveru a i při zpracování odezvy může cokoliv přihodit (server je nedostupný, změní se
struktura JSON dat) je celý tento úsek programu vsazen do struktury try
/except
, jejímž účelem je zachycení a zpracování případné chyby.
import urequests
try:
import ujson as json
except:
import json
# GPS souřadnice
LOCATION_LAT = 49.3951948 # Např. kasna na namesti v KT
LOCATION_LON = 13.2934678
# Požadavek na API Open-Meteo
OPEN_METEO_URL = (
f"https://api.open-meteo.com/v1/forecast?latitude={LOCATION_LAT}&longitude={LOCATION_LON}¤t=pressure_msl"
)
def get_sea_level_pressure():
try:
response = urequests.get(OPEN_METEO_URL)
data = response.json()
response.close()
return float(data["current"]["pressure_msl"])
except:
print("Chyba pri nacteni dat z open-meteo.com")
return None
sea_level_pressure = get_sea_level_pressure() # nacteni tlaku z meteowebu
if sea_level_pressure is not None:
bmp180.baseline = sea_level_pressure * 100
else:
print("Chyba pri ziskavani kalibrace tlaku.")
print(f"Tlak (internet): {sea_level_pressure:.2f} hPa")
Synchronizace času
Když už jsme úspěšně připojeni k Wi-Fi s přístupem k internetu, využijeme možnosti získání přesného času z některého z NTP serverů. NTP server je specializovaný server poskytující údaje o přesném času.
- Podobnější informace o NTP i úskalí, která nás při načítání času mohou potkat, jsme si ukazovali v článku: Čas z NTP serveru v MicroPythonu
Dále uvedená část kódu programu ukazuje načtení času do modulu ESP32. Čas je pak dostupný v objektu ntp.time()
, který jako parametr předáme do příkazu localtime()
. Z předchozího programu již víme, že objekt (v našem kódu označen t
) má strukturu pole, kde jednotlivé položky pole mají význam časových parametrů (hodina, minuta… den, měsíc, rok…).
Za zmínku v následující ukázce ještě stojí přičtení hodnoty 3600 k hodnotě získané z metody time()
. Tato metoda vrací čas v podobě UTC a my potřebujeme přičíst 1 hodinu (tj. 3600 vteřin), abychom získali čas středoevropský.
import ntptime
import time
def sync_time(ntp_server='ntp.nic.cz'):
ntptime.host = ntp_server
try:
ntptime.settime()
print("Cas synchronizovan.")
except:
print("Chyba pri synchronizaci casu.")
t = time.localtime(time.time() + 3600 * 1)
print(f"\nAktualni cas: {t[3]:02d}:{t[4]:02d}:{t[5]:02d}, {t[2]:02d}.{t[1]:02d}.{t[0]:04d}")
Jelikož modul ESP32 má svůj RTC obvod, který se stará o běh hodin, je asi zbytečné neustále načítat přesný čas z NTP serveru. Čas se tedy synchronizuje jen při prvním spuštění obvodu (je třeba nastavit čas v modulu ESP32), pak při každém desátém probuzení (konstanta NTP_SYNC_INTERVAL
).
NTP_SYNC_INTERVAL = 10
if boot_count == 1 or boot_count % NTP_SYNC_INTERVAL == 0:
print("Synchronizuji cas z NTP... ", end="")
sync_time()
Čítač probuzení
V předešlé ukázce se používá proměnná boot_count, která obsahuje počet probuzení obvodu ESP32 z režimu hlubokého spánku, ve kterém modul většinu času setrvává. Jelikož používáme režim hlubokého spánku, ze kterého se probuzení podobá resetu, je potřeba počet probuzení uchovávat v paměti modulu RTC (součást procesoru modulu ESP32). /p>
- Využití paměti RTC modulu jsme též využívali v článku: Režimy spánku ESP32 v MicroPythonu, ve kterém jsme se věnovali režimům spánku modulu ESP32.
Následující ukázka ukazuje, jak získávat počty probuzení, tedy nastavovat proměnnou boot_count
.
import machine
# ==== ČÍTAČ BOOTŮ V RTC ====
def load_wake_count():
rtc = machine.RTC()
try:
# Zkus načíst počet z RTC paměti
wake_count = rtc.memory()
return int(wake_count.decode()) if wake_count else 0
except Exception as e:
return 0
def save_wake_count(count):
rtc = machine.RTC()
rtc.memory(str(count).encode()) # Uložení počtu do RTC paměti
if machine.wake_reason() != machine.EXT0_WAKE:
boot_count = load_wake_count()
print(f"\n>>> Probuzeni č. {boot_count} (Timer)")
boot_count += 1 # Inkrementace počtu probuzení
save_wake_count(boot_count)
Ošetření letního času
Načteme-li čas z NTP serveru a upravíme jej na středoevropský čas, bylo by dobré ještě zohlednit i možnost nastavení
letního a zimního času. O hledem na předešlé řešení načtení času z NTP serveru si vytvoříme funkci summer()
, která bude vracet hodnotu 0 při zimním času a 1 při letním času. Tak jako jsme posunuli čas o 1 hodinu při korekci na středoevropský čas, tak stejně budeme, či nebudeme, přidávat funkcí summer další hodinu kvůli letnímu času.
Následující část kódu ukazuje kód funkce summer. Letní čas je stanoven od poslední neděle v březnu daného roku (2 hodiny ráno) a končí poslední nedělí v říjnu (3 hodiny ráno). Hlavní částí funkce je tedy určení poslední neděle v daném měsíci. 😊
def summer():
# Získání aktuálního UTC času
t = time.localtime()
year, month, day, hour, minute, second, weekday, yearday = t
# Najdi poslední neděli v březnu
if month == 3:
for d in range(31, 24, -1):
if time.localtime(time.mktime((year, 3, d, 0, 0, 0, 0, 0)))[6] == 6:
last_sunday_march = d
break
if day > last_sunday_march or (day == last_sunday_march and hour >= 2):
return 1
else:
return 0
# Duben až září = letní čas
elif 4 <= month <= 9:
return 1
# Najdi poslední neděli v říjnu
elif month == 10:
for d in range(31, 24, -1):
if time.localtime(time.mktime((year, 10, d, 0, 0, 0, 0, 0)))[6] == 6:
last_sunday_october = d
break
if day < last_sunday_october or (day == last_sunday_october and hour < 3):
return 1
else:
return 0
# Leden, únor, listopad, prosinec = zimní čas
return 0
Použití funkce summer
pak bude následující:
t = time.localtime(time.time() + 3600 * (1+summer()))
print(f"\nAktualni cas: {t[3]:02d}:{t[4]:02d}:{t[5]:02d}, {t[2]:02d}.{t[1]:02d}.{t[0]:04d}")
Správa režimů spánku
Máme-li všechny důležití části programu připravené, začneme je postupně skládat do hlavní části programu. Poslední částí, která se však již týká hlavního fungování celého programu, jsou nastavení okolo režimu spánku – respektive probuzení z něj.
Probuzení pomocí tlačítka
První částí, na kterou se zaměříme, je probuzení pomocí tlačítka. Poté, co modul ESP32 usne je možnost jeho odezvy omezená. Chceme-li v tokovém případě program zastavit, máme jen omezené možnosti. Využijeme tedy toho, že lze modul z režimu spánku probudit událostí na některém z RTC pinů.
Pro probuzení modulu ESP32 a určité „ukončení“ programu využijeme vestavěného tlačítka, které je zapojeno na pinu GPIO0
(který je naštěstí jedním z pinů rodiny RTC, které mohou modul ESP32 probouzet z režimu spánku.)
Proč „ukončení a ne ukončení?
Když uložíme program do modulu ESP32 jako hlavní program (soubor main.py
), zjistíme, že pokud program skončí nebo se resetuje, spustí se soubor main.py
znova. Program tedy vlastně nelze ukončit. Představa našeho „ukončení“ programu tedy bude taková, že program necháme běžet v nekonečné smyčce, ve které nic nedělá. Tento stav maximálně budeme signalizovat blikající vestavěnou LED (GPIO2
), abychom věděli, že program již nevykonává svou hlavní část.
Nejdříve se podíváme, jak nastavit probuzení modulu ESP32 pomocí stisknutí vestavěného tlačítka, pak si ukážeme výše popsané ukončení programu.
- Jednotlivé možnosti buzení modulu ESP32 jsme poznali v článku: Režimy spánku ESP32 v MicroPythonu
Nastavení probuzení jedním pinem (ext0
), který má být stisknutý (tj. míst hodnotu LOW
) zařídí následující řádky:
# ==== NASTAVENI BUZENI PINEM ====
button = Pin(BUTTON_PIN, mode=Pin.IN, pull=Pin.PULL_UP)
esp32.wake_on_ext0(pin=button, level=esp32.WAKEUP_ALL_LOW)
Jakmile se modul ESP32 probudí z hlubokého článku (což se podobá resetování), měli bychom obecně podchytit dva důležité stavy:
- Vyhodnotit, že došlo k resetu z důvodu probuzení modulu z režimu spánku.
- Vyhodnotit, že důvodem probuzení bylo stisknuté tlačítko a nikoliv časovač.
Naštěstí po startovním úvodním resetu potřebujeme, aby program běžel stejně jako po probuzení, nemusíme řešit rozdělení programu dle typu resetu, postačí nám tedy jen následující podmínka, která reaguje pouze na důvod probuzení, tedy zda nastalo probuzení pinem (ext0
) nebo časovačem:
if machine.wake_reason() != machine.EXT0_WAKE:
#tady je kod pro probuzeni casovacem
else:
print("Probuzeni pomoci tlacitka – program konci.")
while True: # bezi navzdy, coz ma byt konec programu
led.value(0)
time.sleep(0.2)
led.value(1)
time.sleep(0.2)
Uspání na určenou dobu
Na samý závěr programu už nezbývá než uspat modul ESP32 na předem stanovenou dobu. V tomto ukázkovém případě se jedná jen o dobu deseti minut (600 s). V reálném nasazení by to spíše byla hodina, neboť měření 1× za hodinu by asi mohlo u meteorologické stanice.
SLEEP_TIME_SEC = 600
# ==== USPANI ====
print(f"\nUspani na {SLEEP_TIME_SEC} sekund...")
time.sleep(0.01)
machine.deepsleep(SLEEP_TIME_SEC * 1000)
Hotový program
Celý kód jsme si postupně vysvětlili, nezbývá nám než jej spojit v jeden celek. Následující výpis programu je jednou z možných variant. Jen opět připomeňme, že je nutné program spustit v podobě souboru main.py
přímo v modulu ESP32 a nikoliv jen zeleným tlačítkem v prostředí Thonny IDE. To by program proběhl právě jednou, pak usnul na deset minut a skončil. Rozhodně též nesmíme zapomenout do modulu ESP32 (do složky lib
) uložit všechny externí knihovny – v tomto případě jen knihovnu bmp180_ext.py
, neboť ostatní knihovny jsou součástí aktuálního firmwaru MicroPythonu.
from bmp180_ext import BMP180
from machine import I2C, Pin, deepsleep
import machine
import network
import ntptime
import urequests
try:
import ujson as json
except:
import json
import esp32
import time
# ==== KONSTANTY ====
# pripojeni k Wi-Fi
WIFI_SSID = "Nazev-WiFi-site"
WIFI_PASS = "heslo-k-WiFi-siti"
# casovani buzeni a NTP synchronizace
SLEEP_TIME_SEC = 600
NTP_SYNC_INTERVAL = 10
# GPS souřadnice
LOCATION_LAT = 49.3951948 # Např. kasna na namesti v KT
LOCATION_LON = 13.2934678
# Požadavek na API Open-Meteo
OPEN_METEO_URL = (
f"https://api.open-meteo.com/v1/forecast?latitude={LOCATION_LAT}&longitude={LOCATION_LON}¤t=pressure_msl"
)
#I2C pro BMP180
I2C_SCL_PIN = 22
I2C_SDA_PIN = 21
# interni LED a stop tlacitko
LED_PIN = 2
BUTTON_PIN = 0
led = Pin(LED_PIN, Pin.OUT)
led.value(1) # LED rozsvítit po probuzení
# ==== FUNKCE ====
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
wlan.connect(WIFI_SSID, WIFI_PASS)
print("Pripojuji k Wi-Fi")
for _ in range(30):
if wlan.isconnected():
break
print(".", end="")
time.sleep(1)
print()
return wlan.isconnected()
def sync_time(ntp_server='ntp.nic.cz'):
ntptime.host = ntp_server
try:
ntptime.settime()
print("Cas synchronizovan.")
except:
print("Chyba pri synchronizaci casu.")
def get_sea_level_pressure():
try:
response = urequests.get(OPEN_METEO_URL)
data = response.json()
response.close()
return float(data["current"]["pressure_msl"])
except:
print("Chyba pri nacteni dat z open-meteo.com")
return None
def summer():
# Získání aktuálního UTC času
t = time.localtime()
year, month, day, hour, minute, second, weekday, yearday = t
# Najdi poslední neděli v březnu
if month == 3:
for d in range(31, 24, -1):
if time.localtime(time.mktime((year, 3, d, 0, 0, 0, 0, 0)))[6] == 6:
last_sunday_march = d
break
if day > last_sunday_march or (day == last_sunday_march and hour >= 2):
return 1
else:
return 0
# Duben až září = letní čas
elif 4 <= month <= 9:
return 1
# Najdi poslední neděli v říjnu
elif month == 10:
for d in range(31, 24, -1):
if time.localtime(time.mktime((year, 10, d, 0, 0, 0, 0, 0)))[6] == 6:
last_sunday_october = d
break
if day < last_sunday_october or (day == last_sunday_october and hour < 3):
return 1
else:
return 0
# Leden, únor, listopad, prosinec = zimní čas
return 0
# ==== ČÍTAČ BOOTŮ V RTC ====
def load_wake_count():
rtc = machine.RTC()
try:
# Zkuste načíst počet z RTC paměti
wake_count = rtc.memory()
return int(wake_count.decode()) if wake_count else 0
except Exception as e:
return 0
def save_wake_count(count):
rtc = machine.RTC()
rtc.memory(str(count).encode()) # Uložení počtu do RTC paměti
# ==== HLAVNÍ ČÁST ====
# ==== ZJIŠTĚNÍ DŮVODU PROBUZENÍ ====
if machine.wake_reason() != machine.EXT0_WAKE:
boot_count = load_wake_count()
print(f"\n>>> Probuzeni č. {boot_count} (Timer)")
boot_count += 1 # Inkrementace počtu probuzení
save_wake_count(boot_count)
print("\n--- Mereni ---")
bus = I2C(1, scl=I2C_SCL_PIN, sda=I2C_SDA_PIN, freq=100000) # on esp8266
bmp180 = BMP180(bus)
bmp180.oversample_sett = 2
bmp180.baseline = 101325
if connect_wifi():
print("Wi-Fi pripojeno.")
if boot_count == 1 or boot_count % NTP_SYNC_INTERVAL == 0:
print("Synchronizuji cas z NTP... ", end="")
sync_time()
sea_level_pressure = get_sea_level_pressure() # nacteni tlaku z meteowebu
if sea_level_pressure is not None:
bmp180.baseline = sea_level_pressure * 100
else:
print("Chyba pri ziskavani kalibrace tlaku.")
else:
print("Nepodarilo se pripojit k Wi-Fi.")
tepl = round(bmp180.temperature, 1)
tlak = round(bmp180.pressure)/100
vyska = round(bmp180.altitude)
t = time.localtime(time.time() + 3600 * (1+summer()))
print(f"\nAktualni cas: {t[3]:02d}:{t[4]:02d}:{t[5]:02d}, {t[2]:02d}.{t[1]:02d}.{t[0]:04d}")
print(f"Teplota: {tepl:.1f} °C",)
print(f"Tlak: {tlak:.2f} hPa")
print(f"Tlak (hladina moře): {sea_level_pressure:.2f} hPa")
print(f"Vyska: {vyska:.0f} m n. m.")
# ==== NASTAVENI BUZENI PINEM ====
button = Pin(BUTTON_PIN, mode=Pin.IN, pull=Pin.PULL_UP)
esp32.wake_on_ext0(pin=button, level=esp32.WAKEUP_ALL_LOW)
# ==== USPANI ====
print(f"\nUspani na {SLEEP_TIME_SEC} sekund...")
time.sleep(0.01)
machine.deepsleep(SLEEP_TIME_SEC * 1000)
else:
print("Probuzeni pomoci tlacitka - program konci.")
while True: # bezi navzdy, coz ma byt konec programu
led.value(0)
time.sleep(0.2)
led.value(1)
time.sleep(0.2)
Na následujícím obrázku vidíme výstupní okno prostředí Thonny IDE, které nyní slouží jako určitý sériový monitor výstupu modulu ESP32. Takto vypadá výstup po spuštění programu. Všimněme si, že při prvním spuštění došlo pro připojení k Wi-Fi k synchronizaci hodit z NTP, zatímco při první probuzení je tato část vynechána (hodiny se budou synchronizovat zase až při desátém probuzení).

Program běží, máme tedy hotovo!
Je program přesně podle našich představ? Modul ESP32 většinu času spí a šetří baterii. Jednou za čas se probudí, načte aktuální hodnoty tlaku a teploty. Pochopitelně se i připojí k Wi-Fi a (v zadaných okamžicích) sesynchronizuje čas s internetem a stáhne referenční atmosférický tlak z profesionální meteorologické stanice. Na závěr vypočítá nadmořskou výšku a vše potřebné vytiskne na výstup. Nesmíme zapomenout na to, že ho lze ručně probudit z hlubokého spánku – pak se rozbliká a tím do klidového režimu.
A teď upřímně… není takový ve své podstatě docela „nelehký“ program vlastně brnkačka? 😊
Závěr (Závěr?)
Přestože se zprvu asi zdálo, že článek o čidle BMP180 bude naprosto nudným článkem, který nám akorát představí jen další „zvířátko ze zoologické zahrady“ čidel připojitelných k modulu ESP32, ukázalo se, že ve světě bastlení nikdy nic není nic nudného!
Kdyby nic jiného, tak snad nám tento článek poskytne ke stažení knihovnu bmp180_ext.py
, kterou (dokud bude fungovat!) můžete používat do svých projektů. A pokud jste k těmto projektům našli inspiraci v našich článcích, nebyl záchvat naší grafomanie zbytečný!
Dnešním článkem se pomalu blížíme k závěru celého našeho seriálu věnovanému MicroPythonu na modulu ESP32. Během těch pětadvaceti článků jsme prošli především to hlavní (kromě Bluetooth, to nesnášíme!), co bychom od modulu ESP32 vlastně mohli požadovat a co by naopak on od nás potřeboval. Jistě, ještě nám tu pár věcí zbývá, takže to (možná) na dalších pár článků vydá… Ale upřímně, skoro se zdá, že již se vlastně nemáme co nového učit. Hlavní vlastnosti modulu ESP32 jsme poznali:
- Víme jak načítat piny, tak pomocí nich řídit různá čidla.
- Zvládli jsme zachytávat události a to jak na úrovni přerušení, tak třeba jako události pro probuzení z režimu spánku.
- Poznali jsme pár zajímavých čidel (někdy i s jejich vrtochami)
- A co legrace jsme si užili s připojením modulu ESP32 k internetu!
Zkrátka, ať si kdokoliv říká o MicroPythonu na modulu ESP32, co chce. My víme, že už víme (a teď už i umíme) své!