Les 5 · Module 1 · De ESP32 als sensorknooppunt
Een display aansturen
Zet een SSD1306-OLED op dezelfde I2C-bus als de sensor en toon de metingen van het meetstation: framebuffer, tekst en lijnen, een eenvoudige grafiek en scherm-uit om inbranden en stroom te besparen.
🎯 Na deze les kun je
- een SSD1306-OLED aansluiten en de driver installeren met mip
- tekst, lijnen en rechthoeken tekenen in de framebuffer
- een klein grafiekje van de laatste metingen tekenen
- het scherm dimmen of uitzetten om stroom te sparen
Het meetstation moet ook zonder dashboard laten zien wat het meet. Een SSD1306 is een klein OLED-scherm (128×64 pixels) dat ook via I2C praat. Het past op dezelfde twee draden als de sensor.
Aansluiten en installeren
| OLED | ESP32 |
|---|---|
| VCC | 3V3 |
| GND | GND |
| SCL | GPIO 22 (zelfde als sensor) |
| SDA | GPIO 21 (zelfde als sensor) |
mpremote mip install ssd1306
mip downloadt de driver uit micropython-lib en zet hem in /lib op het bordje. (Zonder wifi op het bordje gaat dat via je computer.)
from machine import I2C, Pin
i2c = I2C(0, scl=Pin(22), sda=Pin(21))
print([hex(a) for a in i2c.scan()]) # ['0x3c', '0x44']
Eerste tekst
import ssd1306
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
oled.fill(0)
oled.text("Meetstation", 0, 0)
oled.text("21.4 C 48 %", 0, 16)
oled.show()
Je tekent eerst in een framebuffer (een stuk geheugen van 128×64 bits = 1 kB), en show() stuurt die in één keer naar het scherm. De
ingebouwde letters zijn 8×8 pixels: 16 tekens per regel, 8 regels.
Voorspel eerst: je roept na elke oled.text(...) meteen oled.show() aan, voor vier regels per meting. Wat merk je?
Uitkomst
Het scherm flikkert en het bijwerken is merkbaar trager: elke show() stuurt de volledige 1 kB over I2C (bij 400 kHz ruim 20 ms). Vier keer
per meting is vier keer zoveel verkeer, en tussendoor zie je halve schermen. Teken alles in de framebuffer en roep show() één keer aan.
Een schermmodule
lib/scherm.py:
import ssd1306
class Scherm:
def __init__(self, i2c, breedte=128, hoogte=64):
self.oled = ssd1306.SSD1306_I2C(breedte, hoogte, i2c)
self.b, self.h = breedte, hoogte
self.geschiedenis = []
def toon(self, naam, t, rv, status=""):
o = self.oled
o.fill(0)
o.text(naam[:16], 0, 0)
if t is None:
o.text("geen meting", 0, 16)
else:
o.text("%5.1f C" % t, 0, 16)
o.text("%5.0f %%" % rv, 72, 16)
self._grafiek(t)
o.text(status[:16], 0, 56)
o.show()
def _grafiek(self, t, top=28, hoogte=24):
self.geschiedenis.append(t)
self.geschiedenis = self.geschiedenis[-self.b:]
lo, hi = min(self.geschiedenis), max(self.geschiedenis)
bereik = max(hi - lo, 0.5)
self.oled.rect(0, top, self.b, hoogte, 1)
vorige = None
for x, w in enumerate(self.geschiedenis):
y = top + hoogte - 2 - int((w - lo) / bereik * (hoogte - 4))
if vorige is not None:
self.oled.line(x - 1, vorige, x, y, 1)
vorige = y
def uit(self): self.oled.poweroff()
def aan(self): self.oled.poweron()
def dim(self, niveau): self.oled.contrast(niveau) # 0..255
bereik = max(…, 0.5) voorkomt delen door nul als alle waarden gelijk zijn, en houdt kleine ruis klein in beeld.
In main.py:
from scherm import Scherm
scherm = Scherm(i2c)
...
scherm.toon(config.NAAM, t, rv, status="wifi: nee")
Stroom en inbranden
Een OLED verbruikt stroom per brandende pixel, en pixels die maandenlang hetzelfde tonen, slijten (inbranden). Opties:
dim(30)'s nachts;- scherm uit na 30 seconden, weer aan met een knop (les 6);
- de inhoud elke paar minuten een paar pixels verschuiven.
Typische fouten
OSError: [Errno 19] ENODEVbij het maken van het scherm: adres is niet0x3C(sommige zijn0x3D); geefaddr=0x3dmee.ImportError: no module named 'ssd1306':mip installniet gedaan of het bestand staat niet in/lib.- Scherm blijft zwart:
show()vergeten. MemoryErrorbij een groter scherm (128×128): de framebuffer past niet; ruim op metgc.collect()vóór het maken.
Oefenen
- Sluit het scherm aan, installeer de driver en toon "Meetstation".
- Schrijf
lib/scherm.pyen toon elke 10 seconden temperatuur en vochtigheid uit de gefilterde meting. - Voeg de grafiek toe. Adem op de sensor en kijk hoe de lijn reageert.
- Zet het scherm na 30 seconden uit en toon de status "slaap" op de laatste regel daarvoor.
Modelantwoord oefening 1
Zie Eerste tekst. Toont i2c.scan() zowel 0x3c als 0x44, dan werken beide apparaten op dezelfde bus.
Modelantwoord oefening 2
from filter import meet_mediaan, EMA
ema_t, ema_rv = EMA(0.3), EMA(0.3)
while True:
try:
t, rv = meet_mediaan(sensor)
t, rv = ema_t.update(t + config.KAL_T), ema_rv.update(rv + config.KAL_RV)
except ValueError:
t = rv = None
scherm.toon(config.NAAM, t, rv)
time.sleep(config.INTERVAL_S)
Modelantwoord oefening 3
De lijn schiet omhoog en zakt langzaam terug. Omdat het bereik zich aanpast aan min en max, ziet een kleine schommeling er groot uit: zet desnoods een minimaal bereik van 2 °C.
Modelantwoord oefening 4
gestart = time.ticks_ms()
...
if time.ticks_diff(time.ticks_ms(), gestart) > 30_000:
scherm.toon(config.NAAM, t, rv, status="slaap")
scherm.uit()
Gebruik ticks_ms en ticks_diff: die gaan goed als de teller overloopt.
Lokaal oefenen
Geen live lab. Heb je (nog) geen scherm, laat Scherm dan een variant TekstScherm hebben die alleen print; de rest van de code blijft gelijk.
Samenvatting
- De SSD1306 deelt de I2C-bus; installeer de driver met
mip. - Teken in de framebuffer en roep
show()één keer aan. - Een kleine grafiek van de geschiedenis maakt trends zichtbaar.
- Dim of zet het scherm uit tegen stroomverbruik en inbranden.
- Volgende les: timers en interrupts.
📝 Quiz: Een display aansturen
Dit was de laatste gratis les
Je hebt les 1–5 van IoT met de ESP32 en MicroPython gratis gevolgd. Wil je verder? Met één aankoop (€ 39,00, eenmalig) krijg je de overige 15 lessen, de live labs en het certificaat. Geen abonnement.