ESP32 mit MicroPython programmieren

Über den Beitrag

Im letzten Beitrag hatte ich die Grundlagen von MicroPython behandelt. Außerdem hatte ich gezeigt, wie ihr uPyCraft und Thonny installiert und wie ihr Programme auf den ESP32 ladet. Darauf, wie ihr die vielen Funktionen des ESP32 mit MicroPython beherrscht, bin ich hingegen nur sehr kurz eingegangen. Das möchte ich nun nachholen. Dabei werde ich insbesondere die Unterschiede zu Arduino / C++ hervorheben.

Im Einzelnen geht es um:

Wi-Fi und Bluetooth werde ich nur ganz kurz ansprechen.

Pinout des ESP32

Die Pins des ESP32 und ihre Funktionen habe ich hier im Detail erklärt. Hier aber noch einmal die Übersicht:

Pinout des ESP32 mit MicroPython
Pinout des ESP32 mit MicroPython

Der einzige Unterschied gegenüber der Pinbelegung bei der Arduino Implementierung liegt in der Position der I2C Pins.

Kurze Wiederholung: GPIOs digital schalten und Auslesen

Da ich das Schalten von GPIOs im letzten Beitrag ausführlich behandelt habe, gibt es hier nur eine kompakte Wiederholung. Ein einfaches Blinkprogramm sieht auf dem ESP32 mit MicroPython folgendermaßen aus:

from machine import Pin
from time import sleep
led = Pin(18, Pin.OUT)
while True:
    led.value(not led.value())
    sleep(0.5)

Der große Unterschied zu C++ ist, dass die Pins als Objekte definiert werden. Ob ein Pin als Ein- oder Ausgang fungiert, bestimmt ihr mit Pin.OUT bzw. Pin.IN. Den Level des Pins lest ihr mit pinname.value() aus. Schalten könnt ihr den Pin mit pinname.value(0/1). Außerdem könnt ihr Pull-Up oder Pull-Down Widerstände zuschalten, sofern sie am jeweiligen Pin verfügbar sind. Einen Pin zum Auslesen eines Tasters würdet ihr so definieren:

buttonPin = Pin(18, Pin.IN, Pin.PULL_DOWN) # button Pin = GPIO18 

AD-Wandler des ESP32 mit MicroPython auslesen

Vorab: so leistungsfähig der ESP32 ist, so schlecht sind seine A/D-Wandler. Sie haben eine hohe Schwankung und sind zu allem Unglück auch nicht linear. Ich habe das hier detailliert beschrieben.

Nun zum Code:

from machine import ADC, Pin
from time import sleep

adc = ADC(Pin(32))          

adc.atten(ADC.ATTN_11DB)    # ATTN_11DB, ATTN_6DB, ATTN_2_5DB, ATTN_0DB (default)
adc.width(ADC.WIDTH_12BIT)  # WIDTH_12BIT (default), WITDTH_11BIT, WIDTH_10BIT, WIDTH_9BIT

while True:
    val = adc.read()
    print("Raw ADC value:", val)
    sleep(1)

Um einen Pin als A/D-Wandler Eingang zu nutzen, erzeugt ihr also zunächst ein Objekt. Die Standardauflösung ist 12 Bit. Alternativ könnt ihr mit adc.width() 11, 10 oder 9 Bit einstellen. Mit adc.atten(ADC.ATTN_XDB) stellt ihr die Dämpfung (attenuation) ein und legt damit den Eingangsspannungsbereich fest:

Mögliche Parameter für die Dämpfung (Attenuation)
Mögliche Parameter für die Dämpfung (Attenuation)

Die Angaben habe ich der MicroPython Quick Reference entnommen. Bei meinen eigenen Messungen ist der A/D-Wandler in der maximalen Range schon bei ca. 3.15 Volt übergelaufen.

Pulsweitenmodulation

Um eine Pulsweitenmodulation (PWM) zu realisieren, kreiert ihr ein PWM-Objekt für einen bestimmten Pin und übergebt die Frequenz und den Duty Cycle. Bei der Arduino Implementierung des ESP32 könnt ihr die Auflösung einstellen (siehe hier). Programmiert ihr den ESP32 mit MicroPython, seid ihr auf 10 Bit (0-1023) festgelegt. Hier ein Beispiel:

from machine import Pin, PWM

pwm16 = PWM(Pin(16))     # create PWM object from GPIO 16
pwm16.freq(1000)         # 1 kHz
pwm16.duty(256)          # duty cycle = 256/1024 * 100 = 25%

# pwm16 = PWM(Pin(16), freq=1000, duty=256) # short Version

Weitere Funktionen sind:

pwm16.freq()             # get current frequency
pwm16.duty()             # get current duty cycle
pwm16.deinit()           # turn off PWM on the pin

Die maximale PWM Frequenz beträgt 40 MHz. Allerdings bekommt ihr die volle 10 Bit Auflösung aufgrund der Timerfrequenz des ESP32 (80 MHz) nur bis zu der folgenden PWM-Frequenz: 

f_{\text{max, full res}}=\frac{80000000}{2^{10}}=78125\;\text{[Hz]}

Für größere Frequenzen könnt ihr die tatsächliche Auflösung folgendermaßen ausrechnen:

\text{res} = \frac{80000000}{f}

Wenn ihr beispielsweise 20 MHz als Frequenz wählt, habt ihr nur noch eine Auflösung von 4. D.h. ihr könnt die Duty Cycles 0, 25, 50 und 75 % einstellen, indem ihr pwm.duty() Werte in den Bereichen 0-255, 256-511, 512-767 bzw. 768-1023 übergebt.

Analoge Pins

An den Pins 25 und 26 könnt ihr ein echtes analoges Signal zwischen 0 und 3.3 V abgreifen. Die Auflösung beträgt 8 Bit. Und so geht’s:

from machine import Pin, DAC
dac = DAC(Pin(25)) 
dac.write(128)       # voltage = 3.3 * 128/256

Der Wermutstropfen: Die ausgegebene Spannung verhält sich zwar linear zu dem Wert, den ihr dac.write() übergebt, aber die Gerade ist leicht gekippt. Bei meinen Messungen ging sie nicht von 0 bis 3.3 Volt, sondern von 0.086 bis 3.18 Volt (siehe hier).

Zeitfunktionen

Die Entsprechungen zu delay() und delayMicroseconds() sind beim ESP32 mit MicroPython sleep_ms() bzw. sleep_us(). Dazu gibt es noch die Funktion sleep(), der ihr Sekunden übergebt.

millis() und micros() heißen in Micropython ticks_ms() und ticks_us(). Mit ticks_diff() könnt ihr Zeitspannen ermitteln. Hier ein kleines Beispiel:

import time

start_ms = time.ticks_ms() 
start_us = time.ticks_us()
print("Millisecs: ", start_ms)
print("Microsecs: ", start_us)
time.sleep(3)
delta = time.ticks_diff(time.ticks_ms(), start_ms)
print("Delta =", delta)

Das Modul time

Mithilfe des time Moduls könnt ihr die aktuelle Zeit und die seit dem 01.01.2000, 00:00 Uhr vergangenen Sekunden abfragen. Die oft verwendete Unixzeit könnt ihr daraus berechnen.

import time

now = time.localtime()           # query local time as tuple
print(now)
print(now[0])                    # print element 0
now_secs = time.time()           # secs since 01/01/2000, 00:00
now_unix = now_secs + 946681200  # unixtime: secs since 01/01/1970, 00:00 
print(now_unix)

Die „localtime“ wird als Tupel im Format:

(Jahr, Monat, Tag des Monats, Stunden, Minuten, Sekunden, Wochentag, Tag des Jahres)

zurückgegeben. So sieht dann die Ausgabe aus:

Ausgabe von local_and_unixtime.py

Die RTC Klasse

Wenn ihr die Uhrzeit des ESP32 stellen wollt, dann müsst ihr die RTC Klasse nutzen. Der Zeittupel ist hier etwas anders aufgebaut:

(Jahr, Monat, Tag des Monats, Wochentag, Stunden, Minuten, Sekunden, Mikrosekunden).

Beim Wochentag ist zu beachten, dass Montag 0 ist und Sonntag 6. Beim Stellen der Zeit ist der Wochentag unerheblich. Eine falsche Angabe zieht MicroPython gerade.

from machine import RTC

rtc = RTC()
a = rtc.datetime()
print(a)
new_time= (2030, 12, 24, 0, 20, 35, 0, 0)
rtc.init(new_time)
a = rtc.datetime()
print(a)

Wir lernen: der 24. Dezember 2030 ist ein Dienstag:

Darüber hinaus lernen wir: die Zeit wird beim Booten des ESP32 vom Computer übernommen.

Eigentlich kann die RTC Klasse noch viel mehr, z.B. Alarminterrupts. Allerdings scheint das (noch) nicht für den ESP32 implementiert worden zu sein.

Externe Interrupts

Externe (Pin Change) Interrupts könnt ihr für jeden GPIO einrichten. Das funktioniert ähnlich wie beim Arduino. Das folgende Programm löst einen Interrupt aus, wenn ein Taster am GPIO 23 gedrückt wird. Daraufhin leuchtet eine LED am GPIO 18:

from machine import Pin
from time import sleep

led = Pin(18, Pin.OUT)
btn = Pin(23, Pin.IN, Pin.PULL_DOWN)

btn_pressed = False

def btn_handler(btn):
    global btn_pressed
    btn_pressed = True
    
btn.irq(trigger=Pin.IRQ_RISING, handler=btn_handler)
    
while True:
    if btn_pressed:
        led.value(1)
        sleep(1)
        led.value(0)
        btn_pressed = False

Ein paar Erklärungen dazu:

  • Mit btn.irq() bekommt der Pin „btn“ eine Interruptfunktion.
    • Durch trigger=Pin.IRQ_RISING wird der Interrupt mit der steigenden Flanke ausgelöst.
      • Daneben gibt es – Überraschung! –  noch IRQ_FALLING.
    • handler=btn_handler definiert den Interrupthandler, also die Funktion, die bei Auslösen des Interrupts aufgerufen wird (ohne Klammer).
  • Dem Interrupthandler muss das Pin Objekt übergeben werden.
  • In der Handlerfunktion muss btn_pressed als global gekennzeichnet werden, sonst hält Python die Variable für lokal.

Timer des ESP32 mit MicroPython programmieren

Timer lösen interne Interrupts aus. Der ESP32 besitzt 4 Hardware Timer mit der ID 0 bis 3, die sich sehr einfach programmieren lassen. Kein Vergleich zu den Timern des ATmega328P! Hier zunächst ein Beispielprogramm, das 2 LEDs in asynchron blinken lässt:

from machine import Pin, Timer

led0 = Pin(18, Pin.OUT)
led1 = Pin(19, Pin.OUT)

def handler_0(tim0):
    led0.value(not led0.value())
    
def handler_1(tim1):
    led1.value(not led1.value())

tim0 = Timer(0)
tim0.init(period=973, mode=Timer.PERIODIC, callback=handler_0)

tim1 = Timer(1)
tim1.init(period=359, mode=Timer.PERIODIC, callback=handler_1)

Ich denke, der Code ist fast selbsterklärend:

  • Timer ist eine Klasse aus dem Modul machine.
  • Zunächst erzeugt ihr ein Timer Objekt, wobei ihr die Timer ID (0 bis 3) übergebt.
  • Der init() Funktion übergebt ihr drei Argumente:
    • period ist die Zeit in Millisekunden, bis der Timerinterrupt auslöst.
    • mode ist entweder PERIODIC oder ONE_SHOT.
    • Mit callback definiert ihr die Funktion (Interrupthandler), der aufgerufen wird, wenn der Timerinterrupt auslöst.
  • Wie bei den externen Interrupts müsst ihr darauf achten, dass ihr den Handlern das verursachende Objekt übergebt (hier tim0 und tim1).

Ist das nicht herrlich einfach?

Alternativ zu Perioden könnt ihr auch die Frequenz in Hertz übergeben, z.B.: freq = 20000.

Watchdog Timer

Der Watchdog Timer des ESP32 ist nicht besonders komfortabel, aber dafür sehr einfach zu handhaben. Ihr kreiert ein WDT Objekt und übergebt den Timeout in Millisekunden. Einmal gestartet, kann er nicht mehr gestoppt werden. Gewöhnungsbedürftig ist, dass ihr den Reset ausschließlich durch ein wdt.feed() verhindern könnt. Von anderen MCUs ist man gewohnt, dass eine beliebige Anweisung den Timeout verlängert.

Hier ein Beispiel:

from machine import WDT
from time import sleep

wdt = WDT(timeout=5000)

print("Hi, I have just booted")
    
while True:
    sleep(1)
    print("Still there...")
    #wdt.feed()

Ihr werdet sehen, dass der ESP32 nach 5 Sekunden resettet. Wenn ihr das wdt.feed() entkommentiert, dann passiert das nicht. Der Mindest-Timeout ist übrigens 1 Sekunde.

I2C

Der ESP32 besitzt zwei I2C Schnittstellen. Ich bin darauf sehr intensiv hier eingegangen. In MicroPython werden die Schnittstellen durch ihren Index (0 oder 1) unterschieden. Die Schnittstelle 0 ist den GPIOs 18 (SCL) und 19 (SDA) zugeordnet. Die Schnittstelle 1 ist den GPIOs 25 (SCL) und 26 (SDA) zugeordnet. Ihr könnt die Zuordnung ändern und optional die Frequenz spezifizieren.  Das macht ihr bei der Erzeugung eures I2C Objekts.

from machine import I2C, Pin # I2C is a class in machine

i2c = I2C(0)   # simplest definition, SDA=GPIO19, SCL=GPIO18
i2c = I2C(1)   # SDA=GPIO26, SCL=GPIO25
i2c = I2C(0, scl=Pin(16), sda=Pin(17))  # new pin assignment
i2c = I2C(1, scl=Pin(12), sda=Pin(14), freq=400000) # new frequency and pin assignment

Das Scannen auf I2C Adressen ist mit scan() ausgesprochen einfach. Die Funktion gibt eine Liste mit den I2C Adressen im Dezimalsystem zurück:

ESP32 mit MicroPython - i2c.scan()
i2c.scan() im Einsatz

Register beschreibt ihr per I2C mit der Funktion writeto_mem() und lest sie mit readfrom_mem() oder readfrom_mem_into(). Allen Funktionen übergebt ihr die I2C Adresse und das Register. Die Daten, die ihr übergebt oder abfragt, müssen vom Typ Bytearray sein, selbst wenn es sich um einzelne Bytes handelt. So werden die Funktionen angewendet:

# writing to registers:
data = bytearray([128, 255]) # just an example
i2c.writeto_mem(I2C_ADDR, REG_ADDR, data)

# reading from registers:
data = i2c.readfrom_mem(I2C_ADDR, REG_ADDR, 2) # read two bytes
# or, alternatively:
data = bytearray(2)
i2c.readfrom_mem_into(I2C_ADDR, REG_ADDR, data)

Oft muss man 16-Bit Werte aus zwei 8-Bit Registern auslesen und die Einzelwerte dann zu einem Integer zusammensetzen. Das kann man wie bei C++ mit Shiftoperatoren machen (MSB<<8 | LSB). Weniger kryptisch ist die Funktion int.from_bytes(data, order). Dabei ist data ein Bytearray, das die zusammenzusetzenden Bytes enthält, order ist entweder „big“ oder „little“. „big“ bedeutet, dass das MSB (Most Significant Byte) vorne steht, bei „little“ steht es am Ende.

Dann gibt es noch ein kleines Problemchen: wie kann MicroPython wissen, ob die gelesenen Daten signed oder unsigned sind? Bei C++ könnt ihr das ja vorgeben. Die Antwort lautet: gar nicht, da müsst ihr euch anders behelfen. Wenn ihr beispielsweise ein 16-Bit signed integer ((-215) bis (+215-1) lest, dann ist der größte positive Wert 32767. Größere Werte sind falsch interpretiert und eigentlich negativ. Wegen der Zweierkomplement-Darstellung müsst ihr aber einfach nur 65536 (also 216) abziehen. Das gilt jedenfalls für die aktuelle Implementierung von MicroPython auf dem ESP32. Es gibt auch Python Versionen, in denen man int.from_bytes() ein signed=true übergeben kann.

I2C-Beispiel: MPU6050 am ESP32 mit MicroPython auslesen

Das folgende Programm liefert die Beschleunigungswerte, die Temperatur und die Gyroskopdaten eines MPU6050. Das Beispiel zeigt, wie kompakt MicroPython Code sein kann.

from machine import I2C
from time import sleep

MPU_ADDR = 0x68
i2c = I2C(0)
i2c.writeto_mem(MPU_ADDR, 0x6B, bytearray([0])) # "wake-up call"

def byteToInt(bytePair):
    intVal = int.from_bytes(bytePair, 'big') # "big" = MSB at beginning
    if intVal > 32767:                       # intVal is negative => 2^15
        intVal -= 65536
    return intVal
    

while True:
    regVal = i2c.readfrom_mem(MPU_ADDR, 0x3B, 14)  # Read 14 bytes   
    print("AccX =", byteToInt(bytearray([regVal[0],regVal[1]]))) 
    print("AccY =", byteToInt(bytearray([regVal[2],regVal[3]])))
    print("AccZ =", byteToInt(bytearray([regVal[4],regVal[5]])))
    print("Temp =", (byteToInt(bytearray([regVal[6],regVal[7]])))/340.00+36.53)
    print("GyrX =", byteToInt(bytearray([regVal[8],regVal[9]])))
    print("GyrY =", byteToInt(bytearray([regVal[10],regVal[11]])))
    print("GyrZ =", byteToInt(bytearray([regVal[12],regVal[13]])))
    print("***************")
    sleep(2)

Zunächst wird der MPU6050 aufgeweckt, indem eine Null in sein Register 0x6B (Powermanagement 1) geschrieben wird. Die Messdaten liegen in vierzehn Registern, beginnend ab 0x3B, und sie werden in einem Rutsch ausgelesen. Aus dem Bytearray regVal werden die 7 Messwerte aus jeweils 2 Bytes zusammengesetzt.

Meistens werdet ihr euch aber wohl nicht mit Registern herumschlagen wollen und greift deshalb auf externe Module zurück. Dazu komme ich jetzt.

Externe (I2C) Module hinzufügen

Zunächst einmal gibt es hier ein gewisses Namens-Wirrwarr. Ihr trefft auf die Begriffe Modul, Bibliothek und Paket. In erster Näherung bedeuten die Begriffe dasselbe.

Für viele Bauteile wie Sensoren, Displays, A/D-Wandler, usw. haben fleißige Leute Module geschrieben. Um diese nutzen zu können, müsst ihr sie in den meisten Fällen erst installieren.

Wenn ihr mit Thonny arbeitet, dann verwendet ihr dazu am besten die Paketverwaltung. Geht zu Extras → Verwalte Pakete (bzw. in Englisch: Tools → Manage Packages). Gebt den Namen ein und sucht auf PyPI. Wählt das Paket, installiert es und schon könnt ihr es einsetzen. Alles vergleichbar mit der Bibliotheksverwaltung in der Arduino IDE. Nur der Umgang mit Beispielprogrammen ist weniger gut gelöst. Um an diese zu kommen, müsst ihr auf die Plattformen gehen, wo die Pakete zur Verfügung gestellt werden.

Es mag aber auch Module geben, die ihr nicht über die Paketverwaltung findet. Vielleicht habt ihr sie auch selbst programmiert. In dem Fall nehmt die einzubindende Datei, macht in Thonny einen Rechtsklick darauf und wählt „Upload nach \“. Danach könnt ihr die Klassen und Funktionen benutzen.

uPyCraft hat keine Bibliotheksverwaltung. Da öffnet ihr die einzubindende Datei und wählt „Download“.  

SPI

Der ESP32 hat vier SPI Schnittstellen, von denen ihr zwei nutzen könnt. Sie heißen HSPI und VSPI und sie werden über ihre IDs 1 bzw. 2 angesprochen. Den Schnittstellen sind standardmäßig folgende Pins zugeordnet:

SPI Pins des ESP32
SPI Pins des ESP32

Ihr könnt die Zuordnung der Pins aber auch problemlos ändern. Die Erzeugung der SPI Objekte ähnelt I2C. Ihr könnt verschiedene Parameter übergeben:

from machine import Pin, SPI

hspi = SPI(1) # use default Pins
hspi = SPI(1, 20000000)
hspi = SPI(1, baudrate=20000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
vspi = SPI(2, baudrate=40000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(15), mosi=Pin(16), miso=Pin(17))

Bei Verwendung der Standard GPIOs kann die Baudrate bis zu 80 MHz betragen. Wählt ihr andere GPIOs, dann ist die Baudrate auf 40 MHz begrenzt.

Auch das Lesen von und das Schreiben in Register ist ähnlich. Die Daten werden als Bytearrays übergeben und empfangen. Hier Beispiel für die Verwendung der wichtigsten Funktionen:

spi.read(5)             # read 5 bytes 
spi.read(5, 0xFF)       # read 5 bytes and write 0xFF

buf = bytearray(20)    
spi.readinto(buf)       # read into the given buffer 
spi.readinto(buf, 0xFF) # read into the buffer and write 0xFF

spi.write(buf)          # write buffer
spi.write_readinto(buf_1, buf_2) # write buf_1 and read into the buf_2

 

UART (Serial)

Der ESP32 hat die drei UART Schnittstellen UART0, UART1 und UART2. Diese sind standardmäßig den folgenden GPIOs zugeordnet:

Standard GPIOs der UART Schnittstellen
Standard GPIOs der UART Schnittstellen

Bei der Auswahl der Pins müsst ihr etwas aufpassen. Die Standard GPIOS für UART0 und UART1 solltet ihr nicht benutzen (siehe hier).

Die Dinge wiederholen sich. Auch hier erzeugt ihr zunächst ein UART Objekt, wobei ihr mehrere Parameter übergeben könnt. Mindestens müsst ihr die ID der UART Schnittstelle übergeben. Hier ein Beispiel:

from machine import UART

uart1 = UART(1, baudrate=115200, tx=18, rx=19)

Und hier ein paar – ich denke selbsterklärende – Anweisungen:

uart1.init(9600, bits=8, parity=None, stop=1) # standard settings
uart1.read(7)        # read 7 characters, returns a bytes object, not bytearray
uart1.read()         # read all available characters
uart1.readline()     # read a line
uart1.readinto(buf)  # read and store into buf
uart1.write('xyz')   # write the 3 characters
uart1.any()          # returns number of bytes available 

Deep und Light Sleep

Zum Stromsparen hat der ESP32 eine Light Sleep und eine Deep Sleep Funktion. Nach einem Light Sleep setzt das Programm dort fort, wo es aufgehört hat. Ein Deep Sleep führt zu einem Reset. Die Einstellung ist auf dem ESP32 mit MicroPython kinderleicht. Ihr könnt folgende Programm dazu ausprobieren:

import machine, time 

if machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print("woke up from a deep sleep")

while True:
    #machine.deepsleep(3000)
    machine.lightsleep(3000)
    print("woke up from a light sleep")
    time.sleep(0.1)

Ohne die kleine time.sleep() Verzögerung funktionierte bei mir der print() Befehl nicht richtig. Anscheinend geht der ESP32 schon schlafen, bevor die print() Anweisung vollständig abgearbeitet ist.

Touch Pins

Die Touch Pins registrieren Kapazitäten. Da auch der menschliche Körper eine Kapazität hat, dienen die Touch Pins als Berührungssensoren. Und so geht’s:

from machine import TouchPad, Pin
from time import sleep

t = TouchPad(Pin(32))

while True:
    t_val=t.read()
    print("Touch value:", t_val)
    sleep(0.5)

Die Ausgangswerte (Verwendung auf dem Breadboard) lagen bei mir um 800. Wenn ich den Pin über ein Steckbrückenkabel berührt habe, sanken die Werte auf unter 100.

Ein Touch Event kann den ESP32 sowohl aus dem Light, wie auch aus dem Deep Sleep wecken. Das geht so:

import machine
from machine import TouchPad, Pin
import esp32
from time import sleep

t = TouchPad(Pin(32))
t.config(300)               # configure the threshold at which the pin is considered touched
esp32.wake_on_touch(True)

while True:
    machine.lightsleep()    # put the MCU to sleep until a touchpad is touched
    print("Woke up from light sleep")
    sleep(1)
    print("Falling asleep again")
    sleep(0.1)              # you can try without this

Hall Sensor

Der ESP32 besitzt einen Hall-Sensor, welcher magnetische Felder detektiert. Er ist sehr einfach auszulesen:

import esp32, time

while True:
    m = esp32.hall_sensor()
    print(m)
    time.sleep(2)

Wi-Fi

Einer der großen Vorzüge des ESP32 ist das integrierte WLAN. Allerdings ist das Thema ziemlich umfangreich und würde den Rahmen dieses Beitrages sprengen. Vielleicht behandele ich es in einem späteren Artikel.

Wenn ihr euch mit dem Thema näher beschäftigen wollt, dann empfehle ich euch als Einstieg dieses Tutorial. Dort wird gezeigt, wie ihr mit dem ESP32 einen Webserver einrichtet und dann eine LED per Browser schaltet. Die Anleitung klappte bei mir wunderbar auf Anhieb.

Bluetooth

Bei Bluetooth unterscheidet man – unter vielem anderen – zwischen Bluetooth Classic und BLE (Bluetooth Low Energy). Die Classic-Variante kennt ihr vielleicht von den HC-05 und HC-06 Modulen oder aus meinem Beitrag über die Programmierung des ESP32 mit Arduino Code. Der Vorteil ist die recht simple Programmierung.

Die BLE Variante ist, wie ihr Name verrät, energiesparender. Mehr über die Unterschiede zwischen Classic und BLE gibt es hier. Leider ist Classic Bluetooth in MicroPython (noch?) nicht implementiert. Hingegen ist BLE ein etwas komplexeres Thema, das hier den Rahmen sprengen würde und bei dem ich selbst auch noch etwas Lernbedarf habe.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.