Beetje Coderen

Les 3 · Module 1 · De ESP32 als sensorknooppunt

Sensoren via I2C

Sluit een SHT31-sensor aan op de I2C-bus van je ESP32, scan adressen, schrijf zelf een kleine driver die temperatuur en luchtvochtigheid uitleest, en controleer de CRC.

⏱ 75 minuten leestijd + oefenen

Illustratie: een printplaatje met een sensor verbonden door lijnen, gegevens die over een bus reizen

🎯 Na deze les kun je

  • een I2C-sensor correct aansluiten op de ESP32
  • de I2C-bus scannen en adressen herkennen
  • een eenvoudige driver schrijven uit het datasheet
  • meetwaarden controleren met een CRC en fouten afhandelen

Het meetstation begint bij de sensor. De SHT31 meet temperatuur en relatieve luchtvochtigheid nauwkeurig en praat via I2C: een bus met twee draden waarop meerdere apparaten kunnen zitten, elk met een eigen adres. In les 5 zet je het scherm op dezelfde bus.

Aansluiten

SHT31 ESP32
VIN 3V3
GND GND
SCL GPIO 22
SDA GPIO 21

Gebruik 3,3 V, niet 5 V: de ESP32-pinnen zijn niet 5V-tolerant. De meeste breakoutbordjes hebben al pull-up-weerstanden op SDA en SCL.

De bus scannen

from machine import I2C, Pin
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=100_000)
print([hex(a) for a in i2c.scan()])

Voorspel eerst: welk adres verwacht je, en wat zie je als een draad los zit?

Uitkomst

['0x44']: het standaardadres van de SHT31 (met de ADDR-pin aan VIN wordt het 0x45). Zit SDA of SCL los, dan krijg je []. Zijn SDA en SCL omgewisseld, ook []. Een lege lijst betekent dus bijna altijd: bedrading. Zie je tientallen adressen, dan ontbreken pull-ups of zit er een kortsluiting.

Het datasheet lezen

Voor één meting (single shot, high repeatability) zegt het datasheet van de SHT31:

  1. Stuur het commando 0x24 0x00.
  2. Wacht minstens 15 ms.
  3. Lees 6 bytes: temperatuur (2 bytes + CRC), vochtigheid (2 bytes + CRC).
  4. Reken om: T = -45 + 175 × raw / 65535 °C en RH = 100 × raw / 65535 %.

Een driver

lib/sensor.py:

import time
from micropython import const

_ADRES = const(0x44)

def _crc8(data):
    crc = 0xFF
    for b in data:
        crc ^= b
        for _ in range(8):
            crc = ((crc << 1) ^ 0x31) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF
    return crc

class SensorFout(Exception):
    pass

class SHT31:
    def __init__(self, i2c, adres=_ADRES):
        self.i2c = i2c
        self.adres = adres
        if adres not in i2c.scan():
            raise SensorFout("SHT31 niet gevonden op 0x%02x" % adres)

    def lees(self):
        self.i2c.writeto(self.adres, b"\x24\x00")
        time.sleep_ms(16)
        d = self.i2c.readfrom(self.adres, 6)
        if _crc8(d[0:2]) != d[2] or _crc8(d[3:5]) != d[5]:
            raise SensorFout("CRC klopt niet")
        t_raw = d[0] << 8 | d[1]
        rh_raw = d[3] << 8 | d[4]
        temp = -45 + 175 * t_raw / 65535
        rv = 100 * rh_raw / 65535
        return temp, rv

Gebruik:

from machine import I2C, Pin
from sensor import SHT31
s = SHT31(I2C(0, scl=Pin(22), sda=Pin(21)))
print("%.2f °C  %.1f %%" % s.lees())

De CRC (een controlegetal) vangt bitfouten op door storing of lange draden. Een driver zonder CRC-controle geeft bij storing stilletjes rare waarden, bijvoorbeeld 130 °C.

Fouten afhandelen

I2C kan OSError: [Errno 19] ENODEV of ETIMEDOUT geven als de sensor even niet antwoordt. In main.py:

try:
    temp, rv = s.lees()
except (OSError, SensorFout) as e:
    print("meting mislukt:", e)
    temp = rv = None

Een meetstation dat bij één mislukte meting crasht, is niet bruikbaar. In les 16 bouw je hier een volledig herstelpad omheen.

Typische fouten

  • OSError: [Errno 19] ENODEV: niets op dat adres; bedrading of verkeerd adres.
  • Temperatuur te hoog: de sensor zit vlak naast de ESP32, die warm wordt. Zet hem een paar centimeter verder, of aan een kabel.
  • Andere pinnen: niet elk bord gebruikt 21/22 voor I2C. Elke GPIO met invoer/uitvoer kan; geef ze op bij I2C(...).

Oefenen

  1. Sluit de SHT31 aan en scan de bus.
  2. Schrijf lib/sensor.py en lees elke 2 seconden temperatuur en vochtigheid.
  3. Adem op de sensor. Wat gebeurt er met beide waarden, en hoe snel?
  4. Test de CRC-controle door in de code tijdelijk één byte te wijzigen (d = bytearray(d); d[1] ^= 1). Wat gebeurt er?
Modelantwoord oefening 1

[0x44]. Zie je niets: controleer 3V3/GND en wissel SDA/SCL om.

Modelantwoord oefening 2
import time
from machine import I2C, Pin
from sensor import SHT31
s = SHT31(I2C(0, scl=Pin(22), sda=Pin(21)))
while True:
    t, rv = s.lees()
    print("%.2f °C  %.1f %%" % (t, rv))
    time.sleep(2)
Modelantwoord oefening 3

De luchtvochtigheid springt binnen een paar seconden naar 80–100%, de temperatuur stijgt een paar graden. Daarna zakken ze langzaam terug: de sensor reageert snel op stijgen, maar het opdrogen kost tientallen seconden. Daar heb je in les 4 iets aan.

Modelantwoord oefening 4

SensorFout: CRC klopt niet. Haal de wijziging daarna weer weg.

Lokaal oefenen

Geen live lab: je hebt de echte sensor nodig. Heb je nog geen sensor, schrijf dan een NepSensor-klasse met dezelfde lees()-methode die willekeurige waarden geeft, zodat je verder kunt.

Samenvatting

  • I2C: twee draden, apparaten met een adres; altijd 3,3 V.
  • scan() is je eerste diagnose: leeg = bedrading.
  • Een eigen driver uit het datasheet: commando, wachten, lezen, omrekenen, CRC.
  • Vang sensorfouten op; één mislukte meting mag niet crashen.
  • Volgende les: meten, filteren en kalibreren.

📝 Quiz: Sensoren via I2C

5 vragen · 70% nodig om te slagen

1. Op welke spanning sluit je de SHT31 aan op de ESP32?
2. Wat betekent een lege lijst bij i2c.scan()?
3. Waarvoor is de CRC in de SHT31-data?
4. Wat is het standaardadres van de SHT31?
5. Waarom is de temperatuur vaak te hoog?