Hallo zusammen,
dieser Thread ist ein kleiner Exkurs zu Luftentfeuchtung - theoretische Grundlagen und beschäftigt sich mit dem Auslesen des BME280 von Bosch. Als Programmiersprache kommt (wie meistens bei mir) Icon zum Einsatz.
1. Datenblatt BME280
Hiermit oute ich mich als jemand, der sich sehr intensiv mit Datenblättern beschäftigt, bevor er sich mit einem Stück Hardware über ein Stück (selbstgeschriebener) Software annähert.
Als erstes habe ich mich mit dem Datenblatt zum BME280 beschäftigt. Dabei sind mir eine Reihe von Dingen aufgefallen:
- die Konfiguration ist nicht so ganz einfach (wenn man mehr als nur Standard-Funkionen erreichen möchte)
- im BME280 sind in einer Reihe von Registern Kalibrierdaten gespeichert, die es auf den auslesbaren Analogwert anzuwenden gilt, um einen physikalischen plausiblen Wert erhalten zu können
- ein paar Dinge sind im ansonsten recht guten Datenblatt nur angedeutet - da bleibt also Spielraum zum Ausprobieren (bis es dann hoffentlich funktioniert)
2. Das Sensormodul
Zum Einsatz kam der BME280 in der Form Combosensor-Modul BME280, den ich während meines letzten Projektes in Berlin bei Segor erworben hatte.
Die Stiftleiste war schnell angelötet. Dann sind nur noch 4 Strippen zum RPi zu legen.
3. Anschluss des Sensor-Moduls an den RPi
Das Sensormodul habe ich mit dem RPi (Modell 4B) verbunden:
Pin Sensormodul | Pin RPi |
VIN | Pin1 (3V3) |
GND | Pin9 (GND) |
SCL | Pin5 (GPIO3 als I2C1_SCL) |
SDA | Pin3 (GPIO2 als I2C1_SDA) |
Auf dem RPi läuft Raspbian OS in aktueller Version.
4. Kommunikation Sensor-Modul mit RPi über I2C
4.1 Welches I2C-Device?
Über
habe ich herausgefunden, dass es sich um das Device I2C-1 handelt.
4.2 Welche I2C-Adresse?
Über (die 1 im Linux-Kommando rührt von der soeben ermittelten Device-Nummer 1)
habe ich dann die Adresse herausgefunden, unter der das Sensor-Modul erreichbar ist:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --
Es handelt sich somit um die Adresse 0x76.
4.3 Auslesen von Registern des BME280
Über
habe ich mal alle Register des Sensor-Moduls ausgelesen:
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
80: 95 70 89 3a 43 0d e7 06 3a 6e 38 66 32 00 2f 8f ?p?:C???:n8f2./?
90: cb d5 d0 0b 19 1c c7 ff f9 ff ac 26 0a d8 bd 10 ???????.?.?&????
a0: 00 4b 8b 00 00 00 00 00 00 00 00 00 33 00 00 c0 .K?.........3..?
b0: 00 54 00 00 00 00 60 02 00 01 ff ff 1f 60 03 00 .T....`?.?..?`?.
c0: 00 00 2c ff 00 00 00 00 01 00 00 00 00 00 00 00 ..,.....?.......
d0: 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 `...............
e0: 00 79 01 00 11 2c 03 1e 9d 41 ff ff ff ff ff ff .y?.?,???A......
f0: ff 00 01 04 2c 01 00 56 61 c0 82 3e 00 62 c3 80 ..??,?.Va??>.b??
Alles anzeigen
Über
habe ich mal das Register 0xD0 ausgelesen (in dem sich Informationen über das angeschlossene Modul befinden):
Das Datenblatt verrät, dass es sich somit um einen BME280 handelt. Das macht schon mal Sinn und ist auch plausibel, weil es die Erwartungshaltung erfüllt.
4.4 Schreiben in Register des BME280
Wenn man sich das Datenblatt zu Gemüte geführt hat, dann bleibt einem das Register F4 in Erinnerung. In dieses Register werden die Oversampling-Informationen bzgl. Temperatur- und Druck-Messungen gespeichert.
Als Versuch habe ich mal
ausprobiert.
Das Auslesen über i2cget betätigte, dass der Schreibvorgang erfolgreich verlief.
4.5 Verwendung mehrerer BME280-Sensormodule
Je mehr ich mit diesem Sensormodul ins Detail gehe, umso mehr Modelle gibt es:
- 4 Pin
- 5 Pin
- 6 Pin
- 7 Pin
An der Schalte zur Kommunikation über das I2C-Protokoll ändert sich gegenüber der Tabelle 3 nichts.
Je nach Hersteller ist eine der beiden I2C-Adressen 0x76 oder 0x77 voreingestellt. Da ich ja vorhabe, zwei Sensormodule auszulesen, benötige ich zwei Sensormodule mit verschiedenen I2C-Adressen.
Hierzu gibt es mehrere Möglichkeiten:
4.5.1 BME280-Sensormodul mit Lötbrücken
Das Sensormodul mit 4 Pins besitzt eine Lötbrücke von SDO zu GND. Dies führt zur I2C-Adresse 0x76.
Unterbricht man diese Lötbrücke durch Aufkratzen mit einem scharfen Messer und verbindet das mittlere Lötpad mit dem dritten Lötpad, führt dies zu einer Verbindung von SDO mit VIN. Dies führt zur I2C-Adresse 0x77
4.5.2 Sensormodul ohne Lötbrücken
Die BME280-Sensormodule mit mehr als 4 Pins haben keine Lötbrücke, die man einfach so trennen und neu zusammenlöten kann. Wenn man hier auf dem Steckbrett eine Verbindung von VIN zu SDO vornimmt, dann führt dies ebenso zur I2C-Adresse 0x77. Dies lässt sich über das bereits bekannte Kommando i2cdetect -y 1 zeigen:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
4.5.3 Schaltung bei zwei BME280-Sensormodulen
Die Schaltung wird geringfügig erweitert.
Pin Sensormodul 1 | Pin Sensormodul 2 | Pin Raspberry Pi |
VIN | VIN | Pin 1 (3V3) |
GND | GND | Pin 9 (GND) |
SCL | SCL | Pin 5 (GPIO3 als I2C1_SCL) |
SDA | SDA | Pin 3 (GPIO2 als I2C1_SDA) |
Neu: SDO mit VIN verbinden |
Abschließende Bestätigung der Erreichbarkeit beider Sensormodule über I2C:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 77
5 Beispiel-Programm in der Programmiersprache Icon
Danach habe ich mich daran gemacht, möglichst alles, was das Datenblatt offenbart, auch in einem Beispiel-Programm verwenden zu können.
5.1 Quellcode (ein Sensormodul)
Das Ergebnis ist folgendes Programm (gespeichert im Icon-Arbeitsverzeichnis /home/pi/icon9_51/bin) - hätte ich gern hochgeladen, aber irgendein Schlaumeier hat *.icn-Dateien nicht mehr vorgesehen.
link hexcvt, BME280_lib
global I2C_address
$define ALTITUDE 225 # Höhe über NN von Schmidhofen
procedure main()
bme280 := BME280()
if device := I2C_find_device() then BME280_interface("I2C", bme280); write("Device = ", device)
I2C_bus := BME280_find(device); write("Gefundenes I2C-Device unter der Adresse ", I2C_bus)
BME280_dump(device, I2C_bus)
BME280_evaluate_chip_ID(device, I2C_bus, "show")
BME280_read_calibration_regs(device, I2C_bus, bme280)
repeat
{
# Konfiguration
# system("i2cset -y 1 0x76 0xE0 0xB6") # Reset: KEIN RESET durchführen!!!
BME280_configure_standby(1000, bme280)
BME280_configure_humidity(1, bme280)
BME280_configure_temperature(1, bme280)
BME280_configure_pressure(4, bme280)
BME280_configure_mode("Normal", bme280)
BME280_configure_filter("OFF", bme280)
BME280_configuration(device, I2C_bus, bme280)
# Auf Verfügbarkeit des nächsten Messwertes warten
BME280_wait_for_readiness(device, I2C_bus)
# BME280 auslesen
temperature := BME280_get_Temperature(device, I2C_bus, bme280)
pressure := BME280_get_Pressure(device, I2C_bus, bme280)
humidity := BME280_get_Humidity(device, I2C_bus, bme280)
write("Höhe: ", ALTITUDE)
pressure_nn := BME280_get_pressure_nn(device, I2C_bus, bme280, ALTITUDE)
write("Temperatur : ", temperature, " °C")
write("relative Luftfeuchtigkeit: ", humidity, " %rF")
write("Druck : ", pressure, " hPa")
write("Druck NN : ", pressure_nn, " hPa")
values := BME280_get_all(device, I2C_bus, bme280)
write(values[1])
write(values[2])
write(values[3])
delay(2500)
write(" ... next ...")
BME280_wait_for_next(device, I2C_bus)
}
end
Alles anzeigen
5.2 Deutung des Quellcodes
Zeile 1: zwei Bibliotheken werden eingebunden (die eine beschäftigt sich mit Hex-Code-Konvertierungen, die andere kann "BME280" rauf und runter...
Zeile 3: Eine globale Variable (ließe sich vermeiden)
Zeile 5: Höhe meines Wohnortes über NN (damit dann auch plausible Druckermittlungen ermöglicht werden)
Zeile 8: Der sog. Haupteintrittspunkt für das Programm: main()
Zeile 9: legt eine Datenstruktur des Typs BME280() an - und wird sinnigerweise bme280 genannt. Da die Datenstruktur BME280() im bisherigen Quellcode nicht spezifiziert wurde, wird das wohl in der eingebundenen Bibliothek BME280_lib (die "BME280 kann") passieren.
Zeile 11: Sucht nach I2C-Devices und I2C-Schnittstellen/Adressen für das BME280-Sensormodul. Beide Funktionen werden in der Bibliothek definiert.
Zeile 12: Sucht die I2C-Adresse des Sensor-Moduls.
Zeile 15 bis 17: Liest das Sensor-Modul komplett aus und stellt den Inhalt dar. Liest den Sensor-Typ aus. Liest die Kalibrierdaten aus - und speichert diese in der Variablen bme280 des Datentyps BME280.
Zeile 19: startet eine Endlosschleife - das Ende naht in der vorletzten Programmzeile, die mit dem }.
Zeile 24 bis 29: Konfiguriert die Standby-Zeit, das Oversampling für die Messung der relativen Luftfeuchtigkeit, der Temperatur, des Luftdrucks, definiert die Betriebsart und setzt die Filter (auf OFF). Wer das Datenblatt ebenso intensiv studiert hat, findet die ganzen Funktionalitäten hier wieder.
Zeile 31: Führt die zuvor definierten Konfigurationen aus, indem entsprechendes auf dem BME280 geschrieben wird.
Zeile 34: Wartet auf Betriebsbereitschaft des Sensors (es macht keinen Sinn, Daten auszulesen, während diese erst ermittelt und auf den Chip geschrieben werden. Auf diese Weise verwurschtelt man Daten aus zwei Mess-Zyklen...
Zeile 37: Ermittlung der Temperatur [°C]
Zeile 38: Ermittlung des Luftdrucks [Pa]
Zeile 39: Ermittlung der relativen Luftfeuchtigkeit [%rF]
jeweils unter Berücksichtigung der Kalibrierdaten, die zuvor ausgelesen und in die Variable bme280 des Datentype BME280 gesetzt wurden. Die recht wilden Berechnungen werden in der Bibliothek durchgeführt.
Funktionen zur Umrechnung der drei Messgrößen in °F bzw. atm, bar, Torr sind in der Bibliothek enthalten bzw. werden noch aufgenommen.
Zeile 40: Ausgabe der Höhe über NN
Zeile 42: Berechnung des Drucks auf NN bezogen
Zeile 44 bis 47: Ausgabe der auf Basis der Kalibrierdaten korrigierten Messwerte
Zeile 49: Auslesen aller drei Messgrößen auf einmal
Zeile 50 bis 52: und deren Ausgabe
5.3 Quellcode (zwei Sensormodule)
link numbers, hexcvt, BME280_lib
global I2C_address
$define ALTITUDE 225 # Höhe über NN von Schmidhofen
procedure main()
I2C_bus := [] # 2 Einträge werden erwartet
bme280_inside := BME280(); BME280_location(bme280_inside, "Innen") # Sensor in der Wohnung
bme280_outside := BME280(); BME280_location(bme280_outside,"Außen") # Sensor außerhalb
if I2C_find_device([bme280_inside, bme280_outside]) then
{
BME280_interface("I2C", bme280_inside); write("Device = ", bme280_inside.device)
BME280_interface("I2C", bme280_outside); write("Device = ", bme280_outside.device)
BME280_find([bme280_inside, bme280_outside])
write("Gefundenes I2C-Device ", bme280_inside.location, " unter der Adresse ", bme280_inside.I2C_bus)
write("Gefundenes I2C-Device ", bme280_outside.location, " unter der Adresse ", bme280_outside.I2C_bus)
}
else
{
# TODO: SPI-Abfrage implementieren
}
BME280_dump(bme280_inside); BME280_dump(bme280_outside);
write("Das Sensormodul ", bme280_inside.location, " ist ein ", BME280_evaluate_chip_ID( bme280_inside, "show"))
write("Das Sensormodul ", bme280_outside.location, " ist ein ", BME280_evaluate_chip_ID(bme280_outside, "show"))
BME280_read_calibration_regs(bme280_inside); BME280_read_calibration_regs(bme280_outside)
# Konfiguration
# system("i2cset -y 1 0x76 0xE0 0xB6") # Reset: KEIN RESET durchführen!!!
BME280_configure_standby(1000, bme280_inside)
BME280_configure_humidity(1, bme280_inside)
BME280_configure_temperature(1, bme280_inside)
BME280_configure_pressure(4, bme280_inside)
BME280_configure_mode("Normal", bme280_inside)
BME280_configure_filter("OFF", bme280_inside)
BME280_configure_standby(1000, bme280_outside)
BME280_configure_humidity(1, bme280_outside)
BME280_configure_temperature(1, bme280_outside)
BME280_configure_pressure(4, bme280_outside)
BME280_configure_mode("Normal", bme280_outside)
BME280_configure_filter("OFF", bme280_outside)
repeat
{
BME280_configuration(bme280_inside)
BME280_configuration(bme280_outside)
delay(2500)
# Auf Verfügbarkeit des nächsten Messwertes warten
BME280_wait_for_readiness(bme280_inside)
# BME280 auslesen
temperature_in := BME280_get_Temperature(bme280_inside)
pressure_in := BME280_get_Pressure(bme280_inside)
humidity_in := BME280_get_Humidity(bme280_inside)
write("\n\nHöhe: ", ALTITUDE)
pressure_nn_in := BME280_get_pressure_nn(bme280_inside, ALTITUDE)
BME280_wait_for_readiness(bme280_outside)
# BME280 auslesen
temperature_out := BME280_get_Temperature(bme280_outside)
pressure_out := BME280_get_Pressure(bme280_outside)
humidity_out := BME280_get_Humidity(bme280_outside)
write("\n\nHöhe: ", ALTITUDE)
pressure_nn_out := BME280_get_pressure_nn(bme280_outside, ALTITUDE)
write("Temperatur : ", frn(temperature_in, 13, 8), " °C\t\t", frn(temperature_out, 13, 8), " °C")
write("relative Luftfeuchtigkeit: ", frn(humidity_in , 13, 8), " %rF\t\t", frn(humidity_out , 13, 8), " %rF")
write("Druck : ", frn(pressure_in , 13, 8), " hPa\t\t", frn(pressure_out , 13, 8), " hPa")
write("Druck NN : ", frn(pressure_nn_in, 13, 8), " hPa\t\t", frn(pressure_nn_out, 13, 8), " hPa\n")
values_in := BME280_get_all(bme280_inside)
values_out := BME280_get_all(bme280_outside)
write("Temperatur : ", frn(values_in[1], 13, 8), " °C\t\t", frn(values_out[1], 13, 8), " °C\t\t")
write("relative Luftfeuchtigkeit: ", frn(values_in[3], 13, 8), " %rF\t\t", frn(values_out[3], 13, 8), " %rF\t\t")
write("Druck : ", frn(values_in[2], 13, 8), " hPa\t\t", frn(values_out[2], 13, 8), " hPa\t\t")
#delay(2500)
BME280_wait_for_next(bme280_inside)
BME280_wait_for_next(bme280_outside)
}
end
Alles anzeigen
5.4 Deutung des Quellcodes
Wer der Deutung in 5.2 folgen konnte, wird einige wenige Neuerungen des Codes in 5.3 feststellen:
- Die Datenstruktur BME280 wurde erweitert. Neu sind die Felder location (Ortsbezeichnung zur Identifizierung), interface (zur Spezifizierung über welche Schnittstelle kommuniziert werden soll), SPI_device (nimmt die Information der Variablen device des aufrufenden Hauptprogramms auf und muss daher nicht jedes Mal mit übergeben werden - es reicht also ein Verweis auf die die Variable vom Datentyp BME280) und I2C_bus (dito bzgl. der Variablen bus). Dadurch wird das Jonglieren mit irgendwelchen Variablen vermieden, um die I2C-Schnittstelle sauber anzusteuern.
- Einigen BME280-Funktionen können mehrere Variablen vom Datentyp BME280 übergeben werden, Bsp:
kann im Hauptprogramm durch
angesprochen werden.
- Bei einigen BME280-Funktionen wurde die Argument-Liste vereinfacht. Statt device und bus steht jetzt nur noch chip, wodurch nur noch die alles enthaltende BME280-Datenstruktur übergeben wird.
Sonst wurde nichts Bedeutsames geändert.
6 Bibliothek in der Programmiersprache Icon
U-Dateien hätte ich auch gern gern hochgeladen - dito... (der Satz mit den Schlaumeiern)...
6.1 Hauptprogramm
link numbers, hexcvt, BME280_lib
global I2C_address
$define ALTITUDE 225 # Höhe über NN von Schmidhofen
procedure main()
bme280 := BME280()
if device := I2C_find_device() then BME280_interface("I2C", bme280); write("Device = ", device)
I2C_bus := BME280_find(device); write("Gefundenes I2C-Device unter der Adresse ", I2C_bus)
BME280_dump(device, I2C_bus)
BME280_evaluate_chip_ID(device, I2C_bus, "show")
BME280_read_calibration_regs(device, I2C_bus, bme280)
# Konfiguration
# system("i2cset -y 1 0x76 0xE0 0xB6") # Reset: KEIN RESET durchführen!!!
BME280_configure_standby(1000, bme280)
BME280_configure_humidity(1, bme280)
BME280_configure_temperature(1, bme280)
BME280_configure_pressure(4, bme280)
BME280_configure_mode("Normal", bme280)
BME280_configure_filter("OFF", bme280)
repeat
{
BME280_configuration(device, I2C_bus, bme280)
delay(2500)
# Auf Verfügbarkeit des nächsten Messwertes warten
BME280_wait_for_readiness(device, I2C_bus)
# BME280 auslesen
temperature := BME280_get_Temperature(device, I2C_bus, bme280)
pressure := BME280_get_Pressure(device, I2C_bus, bme280)
humidity := BME280_get_Humidity(device, I2C_bus, bme280)
write("\n\nHöhe: ", ALTITUDE)
pressure_nn := BME280_get_pressure_nn(device, I2C_bus, bme280, ALTITUDE)
write("Temperatur : ", frn(temperature, 13, 8), " °C")
write("relative Luftfeuchtigkeit: ", frn(humidity , 13, 8), " %rF")
write("Druck : ", frn(pressure , 13, 8), " hPa")
write("Druck NN : ", frn(pressure_nn, 13, 8), " hPa\n")
values := BME280_get_all(device, I2C_bus, bme280)
write("Temperatur : ", frn(values[1], 13, 8), " °C")
write("relative Luftfeuchtigkeit: ", frn(values[3], 13, 8), " %rF")
write("Druck : ", frn(values[2], 13, 8), " hPa")
#delay(2500)
BME280_wait_for_next(device, I2C_bus)
}
end
Alles anzeigen
6.2. Biobliothek für >= 2 Sensormodule
record BME280( location, # Ortsbezeichnung zur Identifizierung
interface, # Kommunikation über I2C oder SPI
device,
I2C_bus,
t_sb, # Konfiguration: Standby
osrs_t, osrs_p, osrs_h, # Oversampling
mode, # Mode
filter, # Filter
dig_T1, dig_T2, dig_T3, dig_P1, # im BME280 gespeicherte Kalibrierdaten für Temperatur
dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9, # dito für Druck
dig_H1, dig_H2, dig_H3, dig_H4, dig_H5, dig_H6, # dito für relative Luftfeuchtigkeit
T1, T2, T3, # Temperaturparameter, (MSB, LSB, XSB)
P1, P2, P3, # dito für Druck
H1, H2, # dito für relative Luftfeuchtigkeit
t_fine, # Zwischenwert aus der Berechnung der Temperatur, der bei der Korrektur des Druckes und der relativen Feuchtigkeit benötigt wird
temperature, # Korrigierte Temperatur [°C]
pressure, # Korrigierter Druck [hPa]
pressure_nn, # Druck auf Höhe = 0 NN, 101325 Pa
humidity # Relative Luftfeuchtogkeit [%rF]
)
# Info-Quelle:
# http://www.netzmafia.de/skripten/hardware/RasPi/Projekt-BMP280/index.html
# http://www.netzmafia.de/skripten/hardware/RasPi/RasPi_I2C.html
procedure convert(s, modus)
# Little Endian
# s := s[5:0] || s[3:5] # Vertauschung von Byte 1 und 2
s := s[3:0] # Keine Vertauschung
n := hex(s)
# Bei einigen Adressen wird Vorzeichen geprüft
if \modus == "sign" then
{
# n - 65536 führt zu einer Datenbreite > 16 Bit ohne negatives vorzeichen
if iand(n, 32768) > 0 then n := -(icom(n) + 65536 + 1)
}
return n
end
procedure piper(s)
answer := []
#write("Piper: ", s)
if fh := open(s, "p") then
{
while put(answer, read(fh))
}
close(\fh)
#every write("Piper: ", !answer)
case *answer of
{
0: fail
1: return answer[1]
default: return answer
}
end
procedure BME280_location(chip, loc)
chip.location := loc
end
procedure I2C_find_device(chip_liste)
#write("I2C_find_device: *chip_liste = ", *chip_liste)
#write(image(chip_liste[1]))
#write(image(chip_liste[2]))
# Herausfinden, welcher I2C verwendet wird
if fh := open("/dev/i2c-0", "r") then device0 := 0
close(\fh)
if fh := open("/dev/i2c-1", "r") then device1 := 1
close(\fh)
#write(image(device0))
#write(image(device1))
if /device0 & /device1 then fail # Keine I2C-Devices vorhanden!
if /device0 & \device1 then
{
every i := 1 to *chip_liste do chip_liste[i].device := device1
return device1
}
if \device0 & /device1 then
{
every i := 1 to *chip_liste do chip_liste[i].device := device0
return device1
}
if \device0 & \device1 then
{
return [device0, device1]
}
end
procedure BME280_find(chip_liste)
addresslist := [] # ermöglicht zwei BME280-Sensormodule
every address := !["0x76", "0x77"] do # Die beiden Adressen sind für BME280 vorgesehen
{
if piper("i2cdetect -y " || chip_liste[1].device || " 0x03 " || address || " | grep " || address[3:0]) then put(addresslist, address)
}
#write("BME280_list *addresslist = ", *addresslist)
if *addresslist > 0 then
{
every i := 1 to *addresslist do chip_liste[i].I2C_bus := addresslist[i]
}
fail
end
procedure BME280_interface(s, chip)
case s of
{
"I2C" | "i2c" | "IIC" | "iic": chip.interface := 2r1
"SPI" | "spi": chip.interface := 2r0
default: chip.interface := 2r1
}
end
procedure BME280_dump(chip)
system("i2cdump -y -f " || chip.device || " " || chip.I2C_bus)
end
procedure BME280_evaluate_chip_ID(chip, mode)
if chip_id := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " 0xD0 b") then
{
if \mode == "show" then
{
case chip_id of
{
"0x56": ret := "BMP280 samples"
"0x57": ret := "BMP280 samples"
"0x58": ret := "BMP280 mass production"
"0x60": ret := "BME280"
default:ret := "Unknown BME/BMP: " || chip_id
}
return ret
}
}
else write("Reading of CHIP-ID failed!")
end
procedure BME280_read_calibration_regs(chip)
every address := !["0x88", "0x8A", "0x8C", "0x8E", "0x90", "0x92", "0x94", "0x96", "0x98", "0x9A", "0x9C", "0x9E"] do
{
if ret := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " " || address || " w") then
{
case address of
{
"0x88": { chip.dig_T1 := convert(ret)}
"0x8A": { chip.dig_T2 := convert(ret, "sign")}
"0x8C": { chip.dig_T3 := convert(ret, "sign")}
"0x8E": { chip.dig_P1 := convert(ret)}
"0x90": { chip.dig_P2 := convert(ret, "sign")}
"0x92": { chip.dig_P3 := convert(ret, "sign")}
"0x94": { chip.dig_P4 := convert(ret, "sign")}
"0x96": { chip.dig_P5 := convert(ret, "sign")}
"0x98": { chip.dig_P6 := convert(ret, "sign")}
"0x9A": { chip.dig_P7 := convert(ret, "sign")}
"0x9C": { chip.dig_P8 := convert(ret, "sign")}
"0x9E": { chip.dig_P9 := convert(ret, "sign")}
}
}
}
# Kalibrierdaten HUM
every address := !["0xA1", "0xE1", "0xE2", "0xE3", "0xE4", "0xE5", "0xE6", "0xE7"] do
{
if ret := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " " || address || " b") then
{
case address of
{
"0xA1": { a1 := hex(ret[3:0])}
"0xE1": { e1 := hex(ret[3:0])}
"0xE2": { e2 := hex(ret[3:0])}
"0xE3": { e3 := hex(ret[3:0])}
"0xE4": { e4 := hex(ret[3:0])}
"0xE5": { e5 := hex(ret[3:0])}
"0xE6": { e6 := hex(ret[3:0])}
"0xE7": { e7 := hex(ret[3:0])}
}
}
}
# Kalibrierregister gemäß Datenblatt Bosch BME280 zusammenstellen
chip.dig_H1 := a1
chip.dig_H2 := ishift(e2, 8) + e1
chip.dig_H3 := e3
chip.dig_H4 := ishift(e4, 4) + iand(e5, 15)
chip.dig_H5 := ishift(e6, 4) + ishift(e5, -4)
chip.dig_H6 := e7
end
procedure BME280_configure_standby(cycle, chip)
case cycle of
{
0.5: chip.t_sb := 2r000
10: chip.t_sb := 2r110
20: chip.t_sb := 2r111
62.5: chip.t_sb := 2r001
125: chip.t_sb := 2r010
250: chip.t_sb := 2r011
500: chip.t_sb := 2r100
1000: chip.t_sb := 2r101
default:chip.t_sb := 2r101
}
return 0
end
procedure BME280_configure_temperature(oversampling, chip)
case oversampling of
{
0: chip.osrs_t := 2r000
1: chip.osrs_t := 2r001
2: chip.osrs_t := 2r010
4: chip.osrs_t := 2r011
8: chip.osrs_t := 2r100
16: chip.osrs_t := 2r101
default: chip.osrs_t := 2r101
}
return 0
end
procedure BME280_configure_pressure(oversampling, chip)
case oversampling of
{
0: chip.osrs_p := 2r000
1: chip.osrs_p := 2r001
2: chip.osrs_p := 2r010
4: chip.osrs_p := 2r011
8: chip.osrs_p := 2r100
16: chip.osrs_p := 2r101
default: chip.osrs_p := 2r101
}
return 0
end
procedure BME280_configure_humidity(oversampling, chip)
case oversampling of
{
0: chip.osrs_h := 2r000
1: chip.osrs_h := 2r001
2: chip.osrs_h := 2r010
4: chip.osrs_h := 2r011
8: chip.osrs_h := 2r100
16: chip.osrs_h := 2r101
default: chip.osrs_h := 2r101
}
return 0
end
procedure BME280_configure_mode(mode, chip)
case mode of
{
"Sleep": chip.mode := 2r00
"Forced": chip.mode := 2r01
"Forced2": chip.mode := 2r10
"Normal": chip.mode := 2r11
default: chip.mode := 2r11
}
return 0
end
procedure BME280_configure_filter(filter, chip)
case filter of
{ "OFF": chip.filter := 2r000
2: chip.filter := 2r001
4: chip.filter := 2r010
8: chip.filter := 2r011
16: chip.filter := 2r100
default:chip.filter := 2r100
}
return 0
end
procedure BME280_configuration(chip)
# 0xF2 auslesen - 0xF2 enthält 2 Bits zur Ansteuerung der Messung der relativen Luftfeuchtigkeit
if content := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " 0xF2 b") then
{
content := hex(content[3:0])
content := iand(content, 2r11111100) # Ausblenden von Bit 1 und 0
content := ior(content, chip.osrs_h) # Einblenden von Bit 1 und 0
content_hex := "0x" || "01234567890ABCDEF"[content / 16 + 1] || "01234567890ABCDEF"[content % 16 + 1]
if system("i2cset -y " || chip.device || " " || chip.I2C_bus || " 0xF2 " || content_hex) ~= 0 then config_error1 := 2r001 # Feuchtemessung EIN noch aktivieren
}
content := ishift(chip.osrs_t, 5) + ishift(chip.osrs_p, 2) + chip.mode
content_hex := "0x" || "01234567890ABCDEF"[content / 16 + 1] || "01234567890ABCDEF"[content % 16 + 1]
if system("i2cset -y " || chip.device || " " || chip.I2C_bus || " 0xF4 " || content_hex) ~= 0 then config_error2 := 2r010 # Temperaturmessung mit Oversampling, Druckmessung mit Oversampling, Mode
if content := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " 0xF2 b") then
{
content := hex(content[3:0])
dont_touch := iand(content, 2r00000010) # Bit nicht ändern
content := ishift(chip.t_sb, 5) + ishift(chip.filter, 2) + dont_touch + chip.interface
content_hex := "0x" || "01234567890ABCDEF"[content / 16 + 1] || "01234567890ABCDEF"[content % 16 + 1]
if system("i2cset -y " || chip.device || " " || chip.I2C_bus || " 0xF5 " || content_hex) ~= 0 then config_error3 := 2r100
# wird erst aktiv, wenn Register 0xF4 geschrieben wird (Konfiguration Oversampling für Temperatur und Druck)
}
/config_error1 := 0 # = if config_error1 ~=== &null then config_error1 := 0
/config_error2 := 0
/config_error3 := 0
return ior(ior(config_error1, config_error2), config_error3)
end
###################################################################################################################################################
procedure BME280_wait_for_readiness(chip)
# auf Betriebsbereitschaft warten
while iand(hex(piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " 0xF3 b")[3:0]), 9) > 0
return 0
end
procedure BME280_wait_for_next(chip)
#write("WFN 1")
weiter := 1
while weiter = 1 do
{
f3 := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " 0xF3 b")
#write(f3)
f3 := hex(f3[3:0])
#write(f3)
f3 := iand(f3, 9)
#write(f3)
if f3 = 0 then break
}
#write("WFN 2")
while iand(hex(piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " 0xF3 b")[3:0]), 9) > 0
#write("WFN 3")
end
procedure BME280_get_Temperature(chip)
# Auslesen der Messwerte in den Registern
every address := !["0xFA", "0xFB", "0xFC"] do
{
if ret := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " " || address || " b") then
{
case address of
{
"0xFA": { chip.T1 := hex(ret[3:0])}
"0xFB": { chip.T2 := hex(ret[3:0])}
"0xFC": { chip.T3 := hex(ret[3:0])}
}
}
else fail
}
# Zusammensetzen der max. 20-Bit Datenbreiten in den AD-Wandlern
adc_t := ishift(chip.T1, 12) + ishift(chip.T2, 4) + ishift(chip.T3, -4) # 20 Bit
# temperature offset calculations
temp1 := (adc_t/16384.0 - chip.dig_T1/1024.0) * chip.dig_T2
temp3 := adc_t/131072.0 - chip.dig_T1/8192.0
temp2 := temp3 * temp3 * chip.dig_T3
temperature := (temp1 + temp2)/5120.0
t_fine := temp1 + temp2 # Für die Berechnung der relativen Luftfeuchtigkeit
chip.t_fine := t_fine
chip.temperature := temperature
return temperature
end
procedure BME280_get_Pressure(chip)
# Auslesen der Messwerte in den Registern
every address := !["0xF7", "0xF8", "0xF9"] do
{ #write(image(address))
if ret := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " " || address || " b") then
{
case address of
{
"0xF7": { chip.P1 := hex(ret[3:0])}
"0xF8": { chip.P2 := hex(ret[3:0])}
"0xF9": { chip.P3 := hex(ret[3:0])}
}
}
else fail
}
# Zusammensetzen der max. 20-Bit Datenbreiten in den AD-Wandlern
adc_p := ishift(chip.P1, 12) + ishift(chip.P2, 4) + ishift(chip.P3, -4) # 20 Bit
# pressure offset calculations
press1 := chip.t_fine/2.0 - 64000.0
press2 := press1 * press1 * chip.dig_P6 /32768.0
press2 := press2 + press1 * chip.dig_P5 * 2.0
press2 := (press2 / 4.0) + chip.dig_P4 * 65536.0
press1 := (chip.dig_P3 * press1 * press1 / 524288.0 + chip.dig_P2 * press1) / 524288.0
press1 := (1.0 + press1 / 32768.0) * chip.dig_P1
press3 := 1048576.0 - adc_p;
if press1 ~= 0.0 then # avoid error: division by 0
{
press3 := (press3 - press2 / 4096.0) * 6250.0 / press1
press1 := chip.dig_P9 * press3 * press3 / 2147483648.0
press2 := press3 * chip.dig_P8 / 32768.0
pressure := (press3 + (press1 + press2 + chip.dig_P7) / 16.0) / 100.0
}
else pressure := 0.0
chip.pressure := pressure
return pressure
end
procedure BME280_get_TemperatureF(chip)
return chip.temperature * 1.8 + 32.0
end
procedure BME280_absolute_temperature(chip)
return chip.temperature + 273.15
end
procedure BME280_get_pressure_nn(chip, altitude)
pressure_nn := chip.pressure / ((1 - altitude / 44330.0) ^ 5.255)
chip.pressure_nn := pressure_nn
return chip.pressure_nn
end
procedure BME280_get_Humidity(chip)
# Auslesen der Messwerte in den Registern
every address := !["0xFD", "0xFE"] do
{
if ret := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " " || address || " b") then
{
case address of
{
"0xFD": { chip.H1 := hex(ret[3:0])}
"0xFE": { chip.H2 := hex(ret[3:0])}
}
}
else fail
}
# Zusammensetzen der max. 20-Bit Datenbreiten in den AD-Wandlern
adc_h := ishift(chip.H1, 8) + chip.H2 # 16 Bit
# Berechnung der relativen Luftfeuchtigkeit
var_h := chip.t_fine - 76800.0
var_h := (adc_h - (chip.dig_H4 * 64.0 + chip.dig_H5 / 16384.0 * var_h)) * (chip.dig_H2 / 65536.0 * (1.0 + chip.dig_H6 / 67108864.0 * var_h * (1.0 + chip.dig_H3 / 67108864.0 * var_h)))
var_h := var_h * (1.0 - chip.dig_H1 * var_h / 524288.0)
if var_h > 100.0 then var_h := 100.0
else if var_h < 0.0 then var_h := 0.0
chip.humidity := var_h
return var_h
end
procedure BME280_get_all(chip)
if chip.osrs_t > 0 then t := BME280_get_Temperature(chip)
if chip.osrs_p > 0 then p := BME280_get_Pressure(chip)
if chip.osrs_h > 0 then h := BME280_get_Humidity(chip)
return [t, p, h]
end
procedure BME280_read_from_register(chip, register, amount)
memory := []
every i := 1 to amount do
{ address := hex(register[3:0]) + i - 1
address_hex := "0x" || "01234567890ABCDEF"[address / 16 + 1] || "01234567890ABCDEF"[address % 16 + 1]
if ret := piper("i2cget -y -f " || chip.device || " " || chip.I2C_bus || " " || address || " b") then
{
put(memory, hex(ret[3:0]))
}
}
return memory
end
procedure BME280_write_to_register(chip, register, memory)
every i := 1 to *memory do
{
address := hex(register[3:0]) + i - 1
address_hex := "0x" || "01234567890ABCDEF"[address / 16 + 1] || "01234567890ABCDEF"[address % 16 + 1]
content := memory[i]
content_hex := "0x" || "01234567890ABCDEF"[content / 16 + 1] || "01234567890ABCDEF"[content % 16 + 1]
system("i2cset -y " || chip.device || " " || chip.I2C_bus || " " || address_hex || " " || content_hex)
}
end
Alles anzeigen
7 Compilieren & Starten
Wer mit Geany arbeitet (siehe Icon-Tutorial Teil 2) drückt die Taste F9, um aus dem Quellcode der Bibliothek die für Icon benötigten U1- und U2-Dateien zu erzeugen. Diese Dateien müssen dann in das Verzeichnis lib kopiert werden, um über die Anweisung link gefunden werden zu können. Das Hauptprogramm wird dann nach Drücken von F5 zum ausführbaren Programm compiliert und gestartet.
Beste Grüße
Andreas