Beetje Coderen

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.

⏱ 70 minuten leestijd + oefenen

Illustratie: een klein scherm met een golvende lijn naast een printplaatje

🎯 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] ENODEV bij het maken van het scherm: adres is niet 0x3C (sommige zijn 0x3D); geef addr=0x3d mee.
  • ImportError: no module named 'ssd1306': mip install niet gedaan of het bestand staat niet in /lib.
  • Scherm blijft zwart: show() vergeten.
  • MemoryError bij een groter scherm (128×128): de framebuffer past niet; ruim op met gc.collect() vóór het maken.

Oefenen

  1. Sluit het scherm aan, installeer de driver en toon "Meetstation".
  2. Schrijf lib/scherm.py en toon elke 10 seconden temperatuur en vochtigheid uit de gefilterde meting.
  3. Voeg de grafiek toe. Adem op de sensor en kijk hoe de lijn reageert.
  4. 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

5 vragen · 70% nodig om te slagen

1. Wat is het I2C-adres van een gangbare SSD1306?
2. Wat doet oled.show()?
3. Waarom niet na elke tekstregel show()?
4. Waarom gebruik je ticks_diff in plaats van aftrekken?
5. Hoe beperk je inbranden?

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.

Cursus kopen en verdergaan