RP2040-GEEK mit BME 280


Datenlogging: Temperatur, Luftdruck und Höhe



Anleitung getestet mit CircuitPython 10.0.3






Zunächst ein paar Bemerkungen zum Anschluss des Temperatursensors BME 280. Der Sensor wird am I2C-Bus angeschlossen. Da ich nicht die CircuitPython Firmware von Waveshare verwende, kann die vierpolige I2C-Buchse (für GP29, GP28, Plus und Masse - siehe Gehäuseaufdruck) nicht benutzt werden. Ich verwende stattdessen zwei dreipolige Stecker und schließe die wie im Foto gezeigt an. Dabei achten Sie darauf, dass bei der i2c-Buchse nur Plus und Masse genutzt werden und der Stecker in den linken drei Anschlüsse steckt. SCL und SDA werden an GP3 und GP2 angeschlossen. Ob Sie richtig verkabelt haben stellen Sie beim Scannen des I2C-Bus fest. Das geht im Thonny mit dem folgenden kurzen Programm:

  
  
1  import board
2  import busio
3
4  i2c = busio.I2C(board.GP3, board.GP2)
5  count = 0
6
7  # Wait for I2C lock
8  while not i2c.try_lock():
9      pass
10
11 # Scan for devices on the I2C bus
12 print("Scanning I2C bus")
13 for x in i2c.scan():
14     print(hex(x))
15     count += 1
16
17 print("%d device(s) found on I2C bus" % count)
18
19 # Release the I2C bus
20 i2c.unlock()
  

Starten Sie das Programm um bei richtiger Verkabelung folgende Meldung zu bekommen:

Scanning I2C bus
0x38
0x77
2 device(s) found on I2C bus

oder
Scanning I2C bus
0x38
0x76
2 device(s) found on I2C bus

Im zweiten Fall haben Sie einen Sensor der auf Adresse 76 sendet. Dazu werde ich an entsprechender Stelle eingehen. Bis hierher ist erst mal alles gut.



Los gehts

Es folgt der Programmcode für die Inbetriebnahme des Displays, das Mounten der SD-Karte und die Definition des BME Sensors.

  
  
1   import time
2   import board
3   import busio
4   import sdcardio
5   import digitalio
6   import storage
7   import terminalio
8   import displayio
9   from fourwire import FourWire
10  from adafruit_display_text import label
11  from adafruit_st7789 import ST7789
12  from adafruit_bme280 import basic as adafruit_bme280
13
14  abbruch = False
15
16  # Display initialisieren
17  lcd_cs=board.GP9
18  lcd_dc=board.GP8
19  lcd_reset=board.GP12
20  # Release any resources currently in use for the displays
21  displayio.release_displays()
22  spi = busio.SPI(board.GP10, board.GP11)
23  display_bus = FourWire(spi, command=lcd_dc, chip_select=lcd_cs, reset=lcd_reset)
24  display = ST7789(display_bus, rotation=90, width=240, height=135, rowstart=40, colstart=53)
25  # Create the display group and append objects to it
26  splash = displayio.Group()
27  display.root_group = splash
28
29  # SD Karte mounten
30  SD_SCK = board.GP18
31  SD_MOSI = board.GP19
32  SD_MISO = board.GP20
33  cs = board.GP23
34
35 try:
36      card = busio.SPI(SD_SCK, SD_MOSI, SD_MISO)
37      sdcard = sdcardio.SDCard(card, cs)
38      vfs = storage.VfsFat(sdcard)
39      storage.mount(vfs, "/sd")
40      try:
41          filename = "/sd/log_anzahl.txt"
42          f = open(filename, "r")
43          zahl = f.readline()
44          f.close()
45          anzahl = int(zahl)
46          anzahl += 1
47          with open(filename, "w") as f:
48              f.write(str(anzahl))
49          f.close()
50      except:
51          abbruch = True
52      # Logdatei anlegen
53      filename = "/sd/log_daten_" + str(anzahl) + ".txt"
54      with open(filename, "w") as f:
55          f.write("Temperatur, Feuchte und Luftdruck loggen:\r\n\n")
56      f.close()
57  except OSError as exc:
58      abbruch = True
59
60  # Create sensor object, using the board's default I2C bus.
61  i2c = busio.I2C(board.GP3, board.GP2)  # SCL, SDA
62  bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x77)
63
64  # change this to match the location's pressure (hPa) at sea level
65  bme280.sea_level_pressure = 1004
66
67  # Make a background color fill
68  color_bitmap = displayio.Bitmap(display.width, display.height, 3)
69  color_palette = displayio.Palette(3)
70  color_palette[0] = 0x660088
71  color_palette[1] = 0x000000
72  color_palette[2] = 0xffffff
73  bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
74  splash.append(bg_sprite)
75
76  # Draw a label
77  text_area = label.Label(font=terminalio.FONT, text="\n   RP2040 GEEK\n\n Messung startet\n     in 60 s", color=0xFFFF00, scale=2, line_spacing=1 )
78  text_group = displayio.Group(x=15, y=10)
79  text_group.append(text_area)  # Subgroup for text scaling
80  splash.append(text_group)
81
  

Nach dem Import der Bibliotheken folgt in Zeile 14 eine Variable, die beim Fehlen der SD-Karte oder der Datei 'log_anzahl.txt' zum definierten Programmabbruch mit entsprechender Ausgabe auf dem Display führt. Die Fehlerabfrage selbst wird in den Zeilen 35 bis 58 durchgeführt. In den Zeilen 60 bis 65 wird der BME 280 Sensor definiert. Tragen Sie in Zeile 62 die Adresse Ihres Sensors (vom Scan) ein.

Wenn Sie beim Programmstart die Meldung: 'Failed to find BME280! Chip ID 0x58' bekommen, haben Sie bei der 'adafruit_bme280 Bibliothek eine Datei 'basic.py', welche immer noch einen Fehler enthält. Um das zu vermeiden, nehmen Sie aus dem Bibliotheksbundle mit den unkompilierten Dateien ebenfalls 'adafruit_bme280' und bearbeiten die Datei 'basic.py'. In Zeile 54 ändern Sie : _BME280_CHIPID = const(0x60) in _BME280_CHIPID = const(0x58). Es sollte jetzt kein Fehler mehr angezeigt werden, wenn Sie die Thonny-IDE starten. Danach wird in den Zeilen 67 bis 80 ein Hintergrund und eine Textausgabe vorbereitet.

Es folgt die Anzeige bzw. die Aufzeichnung der gemessenen Werte:

  
  
82  start = time.monotonic()
83  lastsave = time.monotonic()
84  wait = 15
85
86  while True:
87      if abbruch :
88          text_area.text = "\n   RP2040 GEEK\n\n  keine SD-Karte\n oder Datei fehlt"
89          break
90      current_time = time.localtime()
91      uhrzeit = "{:02}:{:02} Uhr".format(current_time.tm_hour, current_time.tm_min)
92      # erste Anzeige nach einer Minute und dann einmal pro Minute
93      if (time.monotonic() - start >= 60):
94          start = time.monotonic()
95          wait -= 1
96          text_area.text = (uhrzeit + "  ({:1} min)".format(wait) +
97                          "\nTemper.: %0.1f C" % bme280.temperature +
98                          "\nFeuchte: %0.1f %%" % bme280.relative_humidity +
99                          "\nLuftdr.: %0.1f hPa" % bme280.pressure +
100                         "\nHoehe  : %0.2f m" % bme280.altitude)
101     # alle 15 Minuten speichern
102     if (time.monotonic() - lastsave >= 900):
103         print("gespeichert")
104         lastsave = time.monotonic()
105         wait = 16
106         filename = "/sd/log_daten_" + str(anzahl) + ".txt"
107         with open(filename, "a") as f:
108             f.write("Logtime: " + uhrzeit + "\n")
109             f.write("Temperatur:  " + str("%0.1f C" % bme280.temperature) + " \n")
110             f.write("Feuchte   :  " + str("%0.1f %%" % bme280.relative_humidity) + " \n")
111             f.write("Luftdruck :  " + str("%0.1f hPa" % bme280.pressure) + " \n")
112             f.write("Hoehe     :  " + str("%0.2f m" % bme280.altitude) + " \n\n")
113             f.close()
114     time.sleep(1)
  

Die Variable 'start' und 'lastsave' sind für die Erneuerung der Anzeige pro Minute bzw. das Speichern der Werte pro 15 Minuten zuständig, was durch die Variable 'wait' festgelegt wird. In den Zeilen 87 bis 89 wird bei 'abbruch == True' das Programm, wie bereits erwähnt, beendet. Die Zeilen 90 und 91 holen die aktuelle Uhrzeit, wenn der Geek am PC angeschlossen ist. Beim Start der 'code.py' ohne PC beginnt der Timer bei 00:00 Uhr zu zählen. Mit Hilfe der Zeilen 92 bis 113 erfolgt die formatierte Werteanzeige auf dem Display bzw. Speicherung in einer Datei 'log_daten_XX.txt'. Bei jedem weiteren Start wird der Wert 'XX' um eins erhöht. Dadurch werden die Dateien nicht automatisch überschrieben und können im Nachgang ausgewertet werden.

Es ist dann allerdings von Zeit zu Zeit ein manuelles Löschen notwendig. Dazu starten Sie im Thonny das Programm 'sd_mounten.py', gehen mit 'Datei öffnen' in den Ordner '/sd' und löschen die entsprechenden Dateien.



Viel Spass und Erfolg beim Ausprobieren.