Arbeiten mit MicroSD-Karten
Verfasst: Mi 16. Feb 2022, 12:02
Um Daten zu speichern, kann man den Flash-Speicher des TTGO (ESP32) benutzen. Micropython stellt dazu eine Reihe von Befehlen zur Verfügung (s. u.). In manchen Fällen - z. B. beim Loggen von Daten - reicht aber der Speicherplatz des Flash-Speicherplatzes nicht aus; vielleicht will man seine Daten aber auch auf bequeme Weise weiterreichen. Hier kann es sinnvoll sein, die Daten nicht im Flash-Speicher, sondern auf einer SD-Karte zu speichern. Man benötigt dazu lediglich einen MicroSD-Karten-Adapter wie in Abb. 1.
Auf diesen Adapter bin ich durch den Artikel MicroPython und ESP32 / ESP8266 als Datenlogger von AZ-Delivery gestoßen. In diesem Beitrag stellt Jürgen Grzesina dar, wie man Klima-Daten über einen längeren Zeitraum loggen kann. Neben dem MicroSD-Karten-Adapter werden dabei auch eine Reihe weiterer Module behandelt.
In diesem Beitrag möchte ich mich auf den Einsatz dieses MikroSD-Karten-Adapter beschränken und dabei auch einige Aspekte beleuchten, welche in dem Artikel von J. Grzesina nur am Rande oder gar nicht erwähnt werden.
1. Anschließen des Adapters
Unser MikroSD-Karten-Adapter arbeitet mit dem Bus-System SPI. (Wie dieses System funktioniert habe ich hier ausführlich dargestellt.) Er nutzt aus, dass jede SD-Karte SPI von sich aus beherrscht (neben einer Reihe anderer Kommunikationssystemen). Deswegen ist es auch nicht erstaunlich, dass auf der Adapterplatine neben ein paar Widerständen und Kondensatoren nur zwei weitere Bausteine zu finden sind: ein Spannungsregulator (AMS1117) und ein "Quad Bus Buffer" (VHC125) als Leistungsanpasser.
Und so habe ich den MikroSD-Karten-Adapter an den TTGO angeschlossen:
2. Initialisierung
Mit folgenden Programmzeilen wird die Initialisierung vorgenommen:
Die benutzten Module machine, os und sys befinden sich standardmäßig im ESP32-Micropython-System. Das Modul sdcard muss man selbst in den Flash-Speicher des ESP32 laden. Die entsprechende Datei sdcard.py findet man z. B. hier oder auch im Anhang dieses Beitrags.
3. MicroSD-Karte im Betriebssystem anmelden
Bevor die SD-Karte vom Micropython-Betriebssystem (OS) benutzt werden kann, muss es dort angemeldet (mounted) werden:
Die SD-Karte ist nun unter der Bezeichnung '/sd' mit denselben Methoden anzusprechen, wie sie sonst auch für die Dateien im Flash-Speicher benutzt werden. Formal wird die SD-Karte dabei vom OS wie ein Unterverzeichnis im Flash angesehen. Im nächsten Abschnitt schauen wir uns das genauer an einem einfachen Beispiel an.
4. Text-Datei von der SD-Karte lesen und auf dem Terminal ausgeben
Wir bereiten unsere MicroSD-Karte vor, indem wir vom PC aus einen Text (z. B. "Hallo Welt!") im Stammverzeichnis der SD-Karte unter dem Namen "hallo1.txt" speichern. Diese MicroSD-Karte stecken wir in unseren Adapter und starten das folgende Programm:
Nur die letzten 5 Programmzeilen sind neu:
Zuerst wird die Datei mit open('/sd/hallo1.txt') geöffnet und der Variablen f zugewiesen. Formal gehört die Datei hallo1.txt für das OS zu einem Verzeichnis /sd. Dieses Unterverzeichnis finden wir tatsächlich auch im Bereich Micropython device wieder (s. Abb. 2), solange die SD-Karte im OS angemeldet ist. Um es noch einmal ganz deutlich zu sagen: Die Datei hallo1.txt befindet sich im Stammverzeichnis der SD-Karte, für das Micropython-OS liegt sie aber im Unterverzeichnis /sd des Flash-Speichers.
Mit dem nächsten Befehl wird der Text aus der Datei hallo1.txt gelesen und in der Variablen testString gespeichert. Anschließend wird dieser Text mit der print-Funktion ausgegeben. Zu guter Letzt wird die SD-Karte mit der umount-Methode wieder abgemeldet (unmounted).
Ist die in der open-Methode angegebene Datei nicht vorhanden, so bricht das Programm mit einer Fehlermeldung ab. Einen solchen Fehler kann man ähnlich wie bei der Ausführung der mount-Methode durch eine entsprechende try-except-Konstruktion abfangen.
Achtung: Angeblich soll der MicroSD-Karten-Adapter zusammen mit dem sdcard-Modul bei Standard-MicroSD-Karten bis 2 GB und bei MicroSD-HC-Karten bis zu 32 GB funktionieren. Leider konnte meine (intakte) 2-GB-SD-Karte zwar angemeldet werden, aber sowohl Lese- als auch Schreibzugriffe waren nicht möglich.
Für Neugierige: Wenn Sie (wie in Abb. 2) das Verzeichnis /sd im Bereich Micropython device sehen wollen, sollten Sie den umount-Befehl einmal auskommentieren; dann ist die MicroSD-Karte auch nach der Ausführung noch angemeldet (ggf. mit der Option refresh die device-Anzeige aktualisieren). Beachten Sie: Wenn ein Programm ohne Abmeldung der MicroSD-Karte beendet wird, erfolgt bei der nächsten Ausführung des Programms eine Fehlermeldung, in der man darauf hingewiesen wird, dass die MicroSD-Karte schon angemeldet ist; die vorherige Anmeldung bleibt aktiv - es sei denn, es wurde inzwischen ein RESET durchgeführt.
5. Ein kurzer Blick hinter die Kulissen
Wer einen Blick auf den Code von sdcard.py wirft, erkennt rasch, dass es im Wesentlichen um das Schreiben und Lesen von Blöcken geht (writeblocks, readblocks). Tatsächlich kann man auch ohne ein Betriebssystem direkt auf diese Blöcke zugreifen. In meinem Beitrag SD-Karten am Attiny habe ich dargestellt, wie man auf diese Weise mit dem knappen Speicher eines Attiny2313 Daten auf eine SD-Karte schreiben und auch von ihr lesen kann.
6. Grundgerüste für das Loggen von Daten
Zunächst kümmern wir uns um das Speichern der einzelnen Daten: Statt irgendwelcher Messwerte wollen wir einzelne Zeichenketten "Item (0)", "Item (1)", "Item (2)", ... auf der SD-Karte speichern. Dazu fügen wir an die Programmzeilen für das Initialisieren und das Anmelden der SD-Karte folgende Code an:
In einer Endlos-Schleife wird zunächst eine Zeichenkette mit dem Namen "teststring" gebildet; dabei wird an die Zeichenkette 'Item' in Klammern ein Zähler angefügt. Der Wert von zaehler ist zu Beginn 0 und wird bei jedem Schleifendurchlauf um 1 erhöht. An das Ende der Zeichenkette fügen wir noch das Steuerzeichen '\n' an; dies sorgt u. A. bei der Ausgabe der Datei in einem Texteditor für einen Zeilenvorschub (newline) nach jedem Item.
Mit dem Befehl f=open('/sd/log.txt', 'a') wird die Datei log.txt auf der SD-Karte geöffnet; der zweite Parameter gibt an, dass durch den
folgenden Schreibvorgang f.write(teststring) der aktuelle teststring an die bereits vorhandenen Daten angefügt wird. (Der Parameter 'a' steht für "append".) Wenn schließlich die Taste Pin0 (etwas) länger als 0,5 s gedrückt wird, dann wird die SD-Karte abgemeldet und das Programm beendet.
Um die "geloogten" Daten mit Micropython der Reihe nach zu lesen, fügen wir an die Programmzeilen für das Initialisieren und das Anmelden der SD-Karte nunmehr folgende Code an:
Neu ist hier die readline-Methode: Sie entnimmt der geöffneten Datei die Zeichen bis zum jeweils nächsten '\'-Zeichen und gibt sie als Zeichenkette (inklusive '\n'-Zeichen) an die Variable teststring zurück. Findet sie kein '\n'-Zeichen mehr (d. h. ist sie an das Ende der Datei gelangt), ist der Rückgabewert eine leere Zeichenkette.
Wenn die Zeichenkette teststring nicht leer ist, wird sie mit der print-Funktion auf dem Terminal ausgegeben; der end-Parameter sorgt dafür, dass die print-Funktion nicht von sich aus ein weiters newline-Kommando ausführt. Wenn die Zeichenkette leer ist, dann wird auf dem Terminal die Meldung "Ende der Datei..." ausgegeben und die Schleife abgebrochen.
Die try-except-Konstruktion kümmert sich um den Fall, dass die angegebene Datei nicht auf der SD-Karte vorhanden ist.
.
Auf diesen Adapter bin ich durch den Artikel MicroPython und ESP32 / ESP8266 als Datenlogger von AZ-Delivery gestoßen. In diesem Beitrag stellt Jürgen Grzesina dar, wie man Klima-Daten über einen längeren Zeitraum loggen kann. Neben dem MicroSD-Karten-Adapter werden dabei auch eine Reihe weiterer Module behandelt.
In diesem Beitrag möchte ich mich auf den Einsatz dieses MikroSD-Karten-Adapter beschränken und dabei auch einige Aspekte beleuchten, welche in dem Artikel von J. Grzesina nur am Rande oder gar nicht erwähnt werden.
1. Anschließen des Adapters
Unser MikroSD-Karten-Adapter arbeitet mit dem Bus-System SPI. (Wie dieses System funktioniert habe ich hier ausführlich dargestellt.) Er nutzt aus, dass jede SD-Karte SPI von sich aus beherrscht (neben einer Reihe anderer Kommunikationssystemen). Deswegen ist es auch nicht erstaunlich, dass auf der Adapterplatine neben ein paar Widerständen und Kondensatoren nur zwei weitere Bausteine zu finden sind: ein Spannungsregulator (AMS1117) und ein "Quad Bus Buffer" (VHC125) als Leistungsanpasser.
Und so habe ich den MikroSD-Karten-Adapter an den TTGO angeschlossen:
Code: Alles auswählen
TTGO | Adapter
-------+-------------
GND | GND
5V | VCC
GPIO15 | MISO
GPIO13 | MOSI
GPIO12 | SCK
GPIO2 | CS
2. Initialisierung
Mit folgenden Programmzeilen wird die Initialisierung vorgenommen:
Code: Alles auswählen
from machine import Pin, SPI
import os, sys, sdcard
spi = SPI(1, baudrate=100000, sck=Pin(12), mosi=Pin(13), miso=Pin(15), polarity=0, phase=0)
cs = Pin(2)
sd = sdcard.SDCard(spi, cs)
3. MicroSD-Karte im Betriebssystem anmelden
Bevor die SD-Karte vom Micropython-Betriebssystem (OS) benutzt werden kann, muss es dort angemeldet (mounted) werden:
Code: Alles auswählen
try:
os.mount(sd, '/sd')
print('SD-Card angemeldet als /sd')
except OSError as e:
print(e)
print('SD-Karte bereits angemeldet')
4. Text-Datei von der SD-Karte lesen und auf dem Terminal ausgeben
Wir bereiten unsere MicroSD-Karte vor, indem wir vom PC aus einen Text (z. B. "Hallo Welt!") im Stammverzeichnis der SD-Karte unter dem Namen "hallo1.txt" speichern. Diese MicroSD-Karte stecken wir in unseren Adapter und starten das folgende Programm:
Code: Alles auswählen
from machine import Pin, SPI
import os, sys, sdcard
spi = SPI(1, baudrate=100000, sck=Pin(12), mosi=Pin(13), miso=Pin(15), polarity=0, phase=0)
cs = Pin(2)
sd = sdcard.SDCard(spi, cs)
try:
os.mount(sd, '/sd')
print('SD-Card angemeldet als /sd')
except OSError as e:
print(e)
print('SD-Karte bereits angemeldet')
f = open('/sd/hallo1.txt')
testString = f.read()
print(testString)
f.close()
os.umount('/sd')
Zuerst wird die Datei mit open('/sd/hallo1.txt') geöffnet und der Variablen f zugewiesen. Formal gehört die Datei hallo1.txt für das OS zu einem Verzeichnis /sd. Dieses Unterverzeichnis finden wir tatsächlich auch im Bereich Micropython device wieder (s. Abb. 2), solange die SD-Karte im OS angemeldet ist. Um es noch einmal ganz deutlich zu sagen: Die Datei hallo1.txt befindet sich im Stammverzeichnis der SD-Karte, für das Micropython-OS liegt sie aber im Unterverzeichnis /sd des Flash-Speichers.
Mit dem nächsten Befehl wird der Text aus der Datei hallo1.txt gelesen und in der Variablen testString gespeichert. Anschließend wird dieser Text mit der print-Funktion ausgegeben. Zu guter Letzt wird die SD-Karte mit der umount-Methode wieder abgemeldet (unmounted).
Ist die in der open-Methode angegebene Datei nicht vorhanden, so bricht das Programm mit einer Fehlermeldung ab. Einen solchen Fehler kann man ähnlich wie bei der Ausführung der mount-Methode durch eine entsprechende try-except-Konstruktion abfangen.
Achtung: Angeblich soll der MicroSD-Karten-Adapter zusammen mit dem sdcard-Modul bei Standard-MicroSD-Karten bis 2 GB und bei MicroSD-HC-Karten bis zu 32 GB funktionieren. Leider konnte meine (intakte) 2-GB-SD-Karte zwar angemeldet werden, aber sowohl Lese- als auch Schreibzugriffe waren nicht möglich.
Für Neugierige: Wenn Sie (wie in Abb. 2) das Verzeichnis /sd im Bereich Micropython device sehen wollen, sollten Sie den umount-Befehl einmal auskommentieren; dann ist die MicroSD-Karte auch nach der Ausführung noch angemeldet (ggf. mit der Option refresh die device-Anzeige aktualisieren). Beachten Sie: Wenn ein Programm ohne Abmeldung der MicroSD-Karte beendet wird, erfolgt bei der nächsten Ausführung des Programms eine Fehlermeldung, in der man darauf hingewiesen wird, dass die MicroSD-Karte schon angemeldet ist; die vorherige Anmeldung bleibt aktiv - es sei denn, es wurde inzwischen ein RESET durchgeführt.
5. Ein kurzer Blick hinter die Kulissen
Wer einen Blick auf den Code von sdcard.py wirft, erkennt rasch, dass es im Wesentlichen um das Schreiben und Lesen von Blöcken geht (writeblocks, readblocks). Tatsächlich kann man auch ohne ein Betriebssystem direkt auf diese Blöcke zugreifen. In meinem Beitrag SD-Karten am Attiny habe ich dargestellt, wie man auf diese Weise mit dem knappen Speicher eines Attiny2313 Daten auf eine SD-Karte schreiben und auch von ihr lesen kann.
6. Grundgerüste für das Loggen von Daten
Zunächst kümmern wir uns um das Speichern der einzelnen Daten: Statt irgendwelcher Messwerte wollen wir einzelne Zeichenketten "Item (0)", "Item (1)", "Item (2)", ... auf der SD-Karte speichern. Dazu fügen wir an die Programmzeilen für das Initialisieren und das Anmelden der SD-Karte folgende Code an:
Code: Alles auswählen
from time import sleep
taste = Pin(0, Pin.IN, Pin.PULL_UP) # Abbruch des Loggens mit Taste Pin0
zaehler = 0 # Startwert für Item-Zähler
while True:
teststring = 'Item (' + str(zaehler) + ')\n'
f=open('/sd/log.txt', 'a')
f.write(teststring)
print(teststring)
f.close()
sleep(0.5)
zaehler += 1
if taste.value()==0: # länger als 0.5 Sekunden gedrückt halten!
os.umount('/sd')
print('\nMit Pin0 abgebrochen!\nSD-Karte wurde abgemeldet und kann entnommen werden.')
sys.exit()
In einer Endlos-Schleife wird zunächst eine Zeichenkette mit dem Namen "teststring" gebildet; dabei wird an die Zeichenkette 'Item' in Klammern ein Zähler angefügt. Der Wert von zaehler ist zu Beginn 0 und wird bei jedem Schleifendurchlauf um 1 erhöht. An das Ende der Zeichenkette fügen wir noch das Steuerzeichen '\n' an; dies sorgt u. A. bei der Ausgabe der Datei in einem Texteditor für einen Zeilenvorschub (newline) nach jedem Item.
Mit dem Befehl f=open('/sd/log.txt', 'a') wird die Datei log.txt auf der SD-Karte geöffnet; der zweite Parameter gibt an, dass durch den
folgenden Schreibvorgang f.write(teststring) der aktuelle teststring an die bereits vorhandenen Daten angefügt wird. (Der Parameter 'a' steht für "append".) Wenn schließlich die Taste Pin0 (etwas) länger als 0,5 s gedrückt wird, dann wird die SD-Karte abgemeldet und das Programm beendet.
Um die "geloogten" Daten mit Micropython der Reihe nach zu lesen, fügen wir an die Programmzeilen für das Initialisieren und das Anmelden der SD-Karte nunmehr folgende Code an:
Code: Alles auswählen
try:
f = open('/sd/log.txt')
while True:
teststring = f.readline()
if teststring != '':
print(teststring, end='')
else:
print('Ende der Datei...')
break
f.close()
except OSError as e:
print(e)
print('Datei nicht vorhanden')
os.umount('/sd')
print('\nSD-Karte wurde abgemeldet und kann entnommen werden.')
Wenn die Zeichenkette teststring nicht leer ist, wird sie mit der print-Funktion auf dem Terminal ausgegeben; der end-Parameter sorgt dafür, dass die print-Funktion nicht von sich aus ein weiters newline-Kommando ausführt. Wenn die Zeichenkette leer ist, dann wird auf dem Terminal die Meldung "Ende der Datei..." ausgegeben und die Schleife abgebrochen.
Die try-except-Konstruktion kümmert sich um den Fall, dass die angegebene Datei nicht auf der SD-Karte vorhanden ist.
.