Arbeiten mit MicroSD-Karten

Hier werden einzelne Projekte mit MicroPython vorgestellt
Antworten
Heinrichs
Beiträge: 171
Registriert: Do 21. Okt 2010, 18:31

Arbeiten mit MicroSD-Karten

Beitrag von Heinrichs » 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.

MicroSD_Adapter_klein.jpg
Abb. 1: MicroSD-Karten-Adapter
MicroSD_Adapter_klein.jpg (64.68 KiB) 2226 mal betrachtet

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)
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:

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')
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.

device.jpg
Abb. 2
device.jpg (26.45 KiB) 2164 mal betrachtet

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')
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:

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.')
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.

.
Dateianhänge
sd_card_files.zip
Alle vorgestellten Programme
(1.62 KiB) 313-mal heruntergeladen

Antworten