Drüben bei Strom sparen beim Einsatz der MicroPython-Firmware im Batteriebetrieb ging es auch um die optimal stromsparende Ansteuerung der BME280 Sensoren.
Einen Teil der Diskussion haben wir hierher verlagert.
Drüben bei Strom sparen beim Einsatz der MicroPython-Firmware im Batteriebetrieb ging es auch um die optimal stromsparende Ansteuerung der BME280 Sensoren.
Einen Teil der Diskussion haben wir hierher verlagert.
Use BME280 forced mode from Strom sparen beim Einsatz der MicroPython-Firmware im Batteriebetrieb.
Für die erste Implementierung sind wir damals bei dieser Bibliothek gelandet, die ursprünglich für den ESP8266 vorgesehen ist. Die lag wohl damals zufällig im Regal.
Driver for the Bosch BME280 for use with MicroPython on ESP8266 boards - GitHub - catdog2/mpy_bme280_esp8266: Driver for the Bosch BME280 for use with MicroPython on ESP8266 boards
- # Operating Modes
- BME280_OSAMPLE_1 = 1
- BME280_OSAMPLE_2 = 2
- BME280_OSAMPLE_4 = 3
- BME280_OSAMPLE_8 = 4
- BME280_OSAMPLE_16 = 5
- # Check that mode is valid.
- if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
- BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
- raise ValueError(
- 'Unexpected mode value {0}. Set mode to one of '
- 'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or '
- 'BME280_ULTRAHIGHRES'.format(mode))
- self._mode = mode
- self._l1_barray[0] = self._mode
- self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL_HUM,
- self._l1_barray)
- self._l1_barray[0] = self._mode << 5 | self._mode << 2 | 1
- self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
- self._l1_barray)
- sleep_time = 1250 + 2300 * (1 << self._mode)
- sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
- sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
- time.sleep_us(sleep_time) # Wait the required time
Die Referenz auf BME280_ULTRALOWPOWER
ist nett, wird aber scheinbar nirgendwo direkt implementiert oder ich sehs nicht. Werdet Ihr daraus schlau, @weef oder @roh?
Es wird auf jeden Fall noch einmal nach alternativen MicroPython-Bibliotheken für den BME280 Ausschau gehalten.
Es wird auf jeden Fall noch einmal nach alternativen MicroPython-Bibliotheken für den BME280 Ausschau gehalten.
… hätten wir ja auch aus erster Hand haben können. Meiomei.
Micropython driver for the BME280 sensor, target platform Pycom devices - GitHub - robert-hh/BME280: Micropython driver for the BME280 sensor, target platform Pycom devices
Es wird auf jeden Fall noch einmal nach alternativen MicroPython-Bibliotheken für den BME280 Ausschau gehalten.
Micropython driver for the BME280 sensor, target platform Pycom devices - GitHub - robert-hh/BME280: Micropython driver for the BME280 sensor, target platform Pycom devices
Die Bibliotheken sind vom gleichen Ursprung, die von robert-hh (Robert Hammelrath) · GitHub ist jedoch aktueller und implementiert folgende Features.
BME280_REGISTER_STATUS
. Und zwar wird statt
- sleep_time = 1250 + 2300 * (1 << self._mode)
- sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
- sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
- time.sleep_us(sleep_time) # Wait the required time
nun jenes verwendet
- # Wait for conversion to complete
- while self.i2c.readfrom_mem(self.address, BME280_REGISTER_STATUS, 1)[0] & 0x08:
- time.sleep_ms(5)
Neu hinzugekommen sind sealevel()
, altitude()
und dew_point()
, siehe
- @property
- def sealevel(self):
- return self.__sealevel
- @sealevel.setter
- def sealevel(self, value):
- if 30000 < value < 120000: # just ensure some reasonable value
- self.__sealevel = value
- @property
- def altitude(self):
- '''
- Altitude in m.
- '''
- from math import pow
- try:
- p = 44330 * (1.0 - pow(self.read_compensated_data()[1] /
- self.__sealevel, 0.1903))
- except:
- p = 0.0
This file has been truncated. show original
– Seitenblicke.
Bosch has stepped up their game with their new BME280 sensor, an environmental sensor with temperature, barometric pressure and humidity! This sensor is great for all sorts of weather/environmental sensing and can even be used in both I2C and SPI!
CircuitPython driver for the BME280. Contribute to adafruit/Adafruit_CircuitPython_BME280 development by creating an account on GitHub.
- """mode values"""
- MODE_SLEEP = const(0x00)
- MODE_FORCE = const(0x01)
- MODE_NORMAL = const(0x03)
Re. forced mode.
adafruit/Adafruit_CircuitPython_BME280/blob/2.3.1/adafruit_bme280.py#L138-L154
Daneben gibt es noch STANDBY Geschichten.
- """
- standby timeconstant values
- TC_X[_Y] where X=milliseconds and Y=tenths of a millisecond
- """
- STANDBY_TC_0_5 = const(0x00) #0.5ms
- STANDBY_TC_10 = const(0x06) #10ms
- STANDBY_TC_20 = const(0x07) #20ms
- STANDBY_TC_62_5 = const(0x01) #62.5ms
- STANDBY_TC_125 = const(0x02) #125ms
- STANDBY_TC_250 = const(0x03) #250ms
- STANDBY_TC_500 = const(0x04) #500ms
- STANDBY_TC_1000 = const(0x05) #1000ms
-
- _BME280_STANDBY_TCS = (STANDBY_TC_0_5, STANDBY_TC_10, STANDBY_TC_20,
- STANDBY_TC_62_5, STANDBY_TC_125, STANDBY_TC_250,
- STANDBY_TC_500, STANDBY_TC_1000)
- @property
- def standby_period(self):
- """
- Control the inactive period when in Normal mode
- Allowed standby periods are the constants STANDBY_TC_*
- """
- return self._t_standby
-
- @standby_period.setter
- def standby_period(self, value):
- if not value in _BME280_STANDBY_TCS:
- raise ValueError('Standby Period \'%s\' not supported' % (value))
- if self._t_standby == value:
- return
- self._t_standby = value
- self._write_config()
Den BME280 will man im ‘forced mode’ betreiben (Punkt 3.3.3. im Datenblatt), da mißt er nur auf Anforderung und geht selbsttätig in den sleep danach - da braucht er nur noch 0,1 µA.
Ohne im code selbst geschaut zu haben: fliegt Ihr den BME280 im forced mode?
BMP280 und BME280: die untersten beiden bits im Register 0xF4 “ctrl_meas” setzen diese modi: wenn Du da eine 1 oder eine 2 reinschreibst, startest Du einen cycle per forced mode.
Auf den Seiten 28 und 29 des BME280 – Data sheet finden sich:
5.4.5 Register 0xF4 “ctrl_meas
”The “ctrl_meas”register sets the pressure and temperature data acquisition options of the device. The register needs to be written after changing “ctrl_hum”for the changes to become effective.
Die untersten beiden bits im Register 0xF4 “ctrl_meas” setzen diese modi: wenn Du da eine 1 oder ein 2 reinschreibst, startest Du einen cycle per forced mode
Danke. Die untersten beiden – also die niederwertigsten (LSB) Bits – ja?
By robert-hh (Robert Hammelrath) · GitHub.
- _, self.dig_H1 = unpack("<HhhHhhhhhhhhBB", dig_88_a1)
- self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
- self._l1_barray)
- self.t_fine = 0
- def read_raw_data(self, result):
- """ Reads the raw (uncompensated) data from the sensor.
By jraber (Jeff Raber) · GitHub.
https://github.com/adafruit/Adafruit_CircuitPython_BME280/blob/master/adafruit_bme280.py#L291-L297
https://github.com/adafruit/Adafruit_CircuitPython_BME280/blob/master/adafruit_bme280.py#L161-L168
Bei der aktuellen, für Pycom ausgelegten Bibliothek haben wird als Standardmodus BME280_OSAMPLE_1 = 1
verwendet. Das ergibt folgendes Bitfeld für das Register 0xF4 “ctrl_meas”.
>>> mode = 1
>>> bin(mode << 5 | mode << 2 | 1)
'0b100101'
Formatted differently:
>>> format(mode << 5 | mode << 2 | 1, '#010b')
'0b00100101'
- self._l1_barray[0] = self._mode << 5 | self._mode << 2 | 1
- self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
- self._l1_barray)
Da stelle ich mir immer die Frage: Ist hier jetzt das LSB auf der linken Seite? Deshalb gibt es hier Strauchelpotenzial.
BMP280 und BME280: die untersten beiden bits im Register 0xF4 “ctrl_meas” setzen diese modi: wenn Du da eine 1 oder eine 2 reinschreibst, startest Du einen cycle per forced mode.
Gut, das ist meiner Meinung nach in dieser Bibliothek einfach nicht konfigurierbar sondern steht fix auf 1
– hier wird also immer im forced mode gearbeitet.
- def read_raw_data(self, result):
- """ Reads the raw (uncompensated) data from the sensor.
Dass self.mode
auch zur Berechnung der anderen beiden Bereiche im Bitfeld herangezogen wird, ist aus meinen Augen Quatsch und hatte mich bei der Durchsicht entsprechend verwirrt. Diese beiden regeln den/die/das Overscan [1] – und zwar individuell pro Temperature/Pressure.
Siehe die Adafruit-Implementierung im direkten Vergleich:
https://github.com/adafruit/Adafruit_CircuitPython_BME280/blob/master/adafruit_bme280.py#L80-L86
https://github.com/adafruit/Adafruit_CircuitPython_BME280/blob/master/adafruit_bme280.py#L91-L94
https://github.com/adafruit/Adafruit_CircuitPython_BME280/blob/master/adafruit_bme280.py#L126-L127
https://github.com/adafruit/Adafruit_CircuitPython_BME280/blob/master/adafruit_bme280.py#L291-L297
Not a hardware… ↩︎
Aber…
Bewertung
Die Bibliotheken sind vom gleichen Ursprung, die von robert-hh · GitHub ist jedoch aktueller und implementiert folgende Features.
Verbesserungen bei “Wait for conversion to complete”
- Per
BME280_REGISTER_STATUS
. Und zwar wird stattnun jenes verwendet
BME280/bme280_float.py at a7074fd2d5a140a14957dbb7c6f247f975a6dcfa · robert-hh/BME280 · GitHub
… auch hier könnte das Timing in die Suppe spucken, siehe Untersuchung und Verbesserung des Timings bei der Ansteuerung der DS18B20 Sensoren unter MicroPython. Daher verwenden wir auf jeden Fall besser die aktualisierte Variante der Bibliothek.
Daher verwenden wir auf jeden Fall besser die aktualisierte Variante der Bibliothek.
Die wirft erst einmal:
1397.5486 [terkin.datalogger ] INFO : Reading sensor port "BME280Sensor"
1397.6333 [terkin.datalogger ] ERROR : Reading sensor "BME280Sensor" failed
Traceback (most recent call last):
File "datalogger.py", line 310, in read_sensors
File "util.py", line 193, in __exit__
File "datalogger.py", line 310, in read_sensors
File "sensor_bme280.py", line 59, in read
ValueError: unknown format code 'd' for object of type 'float'
unknown format code ‘d’ for object of type ‘float’
war das die lib, bei der es float- sowie int-Versionen gab?
Die untersten beiden – also die niederwertigsten (LSB) Bits – ja?
ja! 8)
war das die lib, bei der es float - sowie int -Versionen gab?
Ja. Ich sehe auch grade: Der Fehler liegt in unserer Routine beim Postprocessing:
t, p, h = self.driver.read_compensated_data()
# Prepare readings.
values = {}
if t:
values["temperature"] = t / 100
if p:
p = p // 256
pi = p // 100
pd = p - pi * 100
values["pressure"] = float("{}.{:02d}".format(pi, pd))
if h:
hi = h // 1024
hd = h * 100 // 1024 - hi * 100
values["humidity"] = float("{}.{:02d}".format(hi, hd))
So sehen die Werte aus der Routine read_compensated_data()
aus:
>>> datalogger.sensor_manager.sensors
[<SystemMemoryFree object at 3f9a4ae0>, <SystemTemperature object at 3f9a4af0>, <SystemBatteryLevel object at 3f9a4b00>, <SystemUptime object at 3f9a5360>, <SystemWiFiMetrics object at 3f9a5370>, <HX711Sensor object at 3f9a6400>, <DS18X20Sensor object at 3f9a90e0>, <BME280Sensor object at 3f9a9db0>]
>>> bme280 = datalogger.sensor_manager.sensors[-1]
>>> bme280.driver.read_compensated_data()
array('f', [25.95522, 100929.1, 61.13297])
So sehen die Werte aus read_raw_data()
aus.
>>> from array import array
>>> result = array("i", [0, 0, 0])
>>> bme280.driver.read_raw_data(result)
>>> result
array('i', [532502, 343152, 35142])
Der Fehler liegt in unserer Routine beim Postprocessing
Float formats
temperature
: the temperature in degree Celsius.pressure
: the atmospheric pressure in Pascal.humidity
: the relative humidity in percent.
Wir sollten die Werte wohl nun einfach “as is” übernehmen und nicht mehr gegen das Scaling arbeiten, wie es die int
-Variante lieferte.
pressure
: the atmospheric pressure in Pascal.
Ist pascal in Ordnung oder sollen wir nach hectopascal umrechnen?
nach hectopascal umrechnen
War vorher auch so, nicht?
28.4585 [terkin.datalogger ] INFO : Sensor data:
humidity.0x77.i2c:0 60.7448 Temperatur und Feuchte außen
pressure.0x77.i2c:0 1009.36 Temperatur und Feuchte außen
temperature.0x77.i2c:0 25.7686 Temperatur und Feuchte außen
Ich halte die Ausgabe von Feuchte und Temperatur mit 4 Stellen nach dem Komma ( z.B.60.7448 % ) für falsch, denn es gaukelt eine Genauigkeit vor, die es nicht gibt. Schon die Stelle vor dem Komma muss nicht stimmen, so genau ist der BME280 auch nicht.
Für die Darstellung in der BOB-App ist die Ausgabe von 1 Nachkommastelle sinnvoll, damit die Kurven glatter werden.
Bei den üblichen Wetterstationen wird die Temperatur mit 1 und Feuchte und Luftdruck ohne Nachkommastelle ausgegeben.
Hi Didi,
wir wollen generell immer möglichst viel roh direkt vom Sensor schicken und Umrechnungen erst später machen. Das Auslesen des HX711 ist hier die Ausnahme, die die Regel bestätigt: Hier rechnen wir ja schon am Knoten auf Kilogramm um, selbst wenn auch hier eine spätere Umrechnung denkbar wäre.
Viele Grüße,
Andreas.
Finde es auch überflüssig die unnötigen Nachkommastellen mitzuschleppen, wenn wir an LoRa oder Zwischenspeichern oder irgendwas mit limitations denken müssen wir eh optimieren. Wenn es eine allgemeine Rountine für Temperatur wäre könnte man ja noch sagen, da kommt mal ein Wissenschaftler der mit anderem Gerät im 1/100 oder 1/1000-Bereich messen möchte, die Routinen sind aber an den BME oder den DS18B20 gekoppelt und der wird nicht mehr genauer.
Ich finde es sinnvoll, die Messwerte sofort auf Plausibilität zu prüfen. Damit wird Ballast bei jeder weiteren Verarbeitung oder Transfer vermieden.
Dazu gehören unnötige Nachkommastellen oder Messwertaussreisser bei DS18B20. Wenn z.B. alle 10 sec 23.6°C gemessen werden und dann taucht einmal der Wert 85.7°C auf, dann ist das ein Fehler, vermutlich ein Treiber-Fehler. Ihn zur BOB-App zu senden und dann wegzumitteln ist einfach falsch.