Forum: Compiler & IDEs BME680 etwas ATMega-freundlicher auslesen


von Heiner (Gast)


Lesenswert?

Hallo,

ich würde die Routinen, die zum Auslesen des BME680 von Bosch nötig 
sind, gerne etwas vereinfachen/optimieren. Wie ihr vielleicht wisst, 
gibt der Sensor aus irgendeinem Grund nur Rohdaten und eine Tabelle mit 
Kalibrierungsdaten raus und der arme µC muss damit klar kommen.

Das hier ist aus dem offiziellen Code von
https://github.com/BoschSensortec/BME680_driver/blob/master/bme680.c
1
/*!
2
 * @brief This internal API is used to calculate the temperature value.
3
 */
4
static int16_t calc_temperature(uint32_t temp_adc, struct bme680_dev *dev)
5
{
6
  int64_t var1;
7
  int64_t var2;
8
  int64_t var3;
9
  int16_t calc_temp;
10
11
  var1 = ((int32_t) temp_adc >> 3) - ((int32_t) dev->calib.par_t1 << 1);
12
  var2 = (var1 * (int32_t) dev->calib.par_t2) >> 11;
13
  var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
14
  var3 = ((var3) * ((int32_t) dev->calib.par_t3 << 4)) >> 14;
15
  dev->calib.t_fine = (int32_t) (var2 + var3);
16
  calc_temp = (int16_t) (((dev->calib.t_fine * 5) + 128) >> 8);
17
18
  return calc_temp;
19
}
20
21
/*!
22
 * @brief This internal API is used to calculate the pressure value.
23
 */
24
static uint32_t calc_pressure(uint32_t pres_adc, const struct bme680_dev *dev)
25
{
26
  int32_t var1;
27
  int32_t var2;
28
  int32_t var3;
29
  int32_t pressure_comp;
30
31
  var1 = (((int32_t)dev->calib.t_fine) >> 1) - 64000;
32
  var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) *
33
    (int32_t)dev->calib.par_p6) >> 2;
34
  var2 = var2 + ((var1 * (int32_t)dev->calib.par_p5) << 1);
35
  var2 = (var2 >> 2) + ((int32_t)dev->calib.par_p4 << 16);
36
  var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
37
    ((int32_t)dev->calib.par_p3 << 5)) >> 3) +
38
    (((int32_t)dev->calib.par_p2 * var1) >> 1);
39
  var1 = var1 >> 18;
40
  var1 = ((32768 + var1) * (int32_t)dev->calib.par_p1) >> 15;
41
  pressure_comp = 1048576 - pres_adc;
42
  pressure_comp = (int32_t)((pressure_comp - (var2 >> 12)) * ((uint32_t)3125));
43
  if (pressure_comp >= BME680_MAX_OVERFLOW_VAL)
44
    pressure_comp = ((pressure_comp / var1) << 1);
45
  else
46
    pressure_comp = ((pressure_comp << 1) / var1);
47
  var1 = ((int32_t)dev->calib.par_p9 * (int32_t)(((pressure_comp >> 3) *
48
    (pressure_comp >> 3)) >> 13)) >> 12;
49
  var2 = ((int32_t)(pressure_comp >> 2) *
50
    (int32_t)dev->calib.par_p8) >> 13;
51
  var3 = ((int32_t)(pressure_comp >> 8) * (int32_t)(pressure_comp >> 8) *
52
    (int32_t)(pressure_comp >> 8) *
53
    (int32_t)dev->calib.par_p10) >> 17;
54
55
  pressure_comp = (int32_t)(pressure_comp) + ((var1 + var2 + var3 +
56
    ((int32_t)dev->calib.par_p7 << 7)) >> 4);
57
58
  return (uint32_t)pressure_comp;
59
60
}
61
62
/*!
63
 * @brief This internal API is used to calculate the humidity value.
64
 */
65
static uint32_t calc_humidity(uint16_t hum_adc, const struct bme680_dev *dev)
66
{
67
  int32_t var1;
68
  int32_t var2;
69
  int32_t var3;
70
  int32_t var4;
71
  int32_t var5;
72
  int32_t var6;
73
  int32_t temp_scaled;
74
  int32_t calc_hum;
75
76
  temp_scaled = (((int32_t) dev->calib.t_fine * 5) + 128) >> 8;
77
  var1 = (int32_t) (hum_adc - ((int32_t) ((int32_t) dev->calib.par_h1 * 16)))
78
    - (((temp_scaled * (int32_t) dev->calib.par_h3) / ((int32_t) 100)) >> 1);
79
  var2 = ((int32_t) dev->calib.par_h2
80
    * (((temp_scaled * (int32_t) dev->calib.par_h4) / ((int32_t) 100))
81
      + (((temp_scaled * ((temp_scaled * (int32_t) dev->calib.par_h5) / ((int32_t) 100))) >> 6)
82
        / ((int32_t) 100)) + (int32_t) (1 << 14))) >> 10;
83
  var3 = var1 * var2;
84
  var4 = (int32_t) dev->calib.par_h6 << 7;
85
  var4 = ((var4) + ((temp_scaled * (int32_t) dev->calib.par_h7) / ((int32_t) 100))) >> 4;
86
  var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
87
  var6 = (var4 * var5) >> 1;
88
  calc_hum = (((var3 + var6) >> 10) * ((int32_t) 1000)) >> 12;
89
90
  if (calc_hum > 100000) /* Cap at 100%rH */
91
    calc_hum = 100000;
92
  else if (calc_hum < 0)
93
    calc_hum = 0;
94
95
  return (uint32_t) calc_hum;
96
}
97
98
/*!
99
 * @brief This internal API is used to calculate the Gas Resistance value.
100
 */
101
static uint32_t calc_gas_resistance(uint16_t gas_res_adc, uint8_t gas_range, const struct bme680_dev *dev)
102
{
103
  int64_t var1;
104
  uint64_t var2;
105
  int64_t var3;
106
  uint32_t calc_gas_res;
107
  /**Look up table 1 for the possible gas range values */
108
  uint32_t lookupTable1[16] = { UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647),
109
    UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2130303777),
110
    UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2143188679), UINT32_C(2136746228),
111
    UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2147483647) };
112
  /**Look up table 2 for the possible gas range values */
113
  uint32_t lookupTable2[16] = { UINT32_C(4096000000), UINT32_C(2048000000), UINT32_C(1024000000), UINT32_C(512000000),
114
    UINT32_C(255744255), UINT32_C(127110228), UINT32_C(64000000), UINT32_C(32258064), UINT32_C(16016016),
115
    UINT32_C(8000000), UINT32_C(4000000), UINT32_C(2000000), UINT32_C(1000000), UINT32_C(500000),
116
    UINT32_C(250000), UINT32_C(125000) };
117
118
  var1 = (int64_t) ((1340 + (5 * (int64_t) dev->calib.range_sw_err)) *
119
    ((int64_t) lookupTable1[gas_range])) >> 16;
120
  var2 = (((int64_t) ((int64_t) gas_res_adc << 15) - (int64_t) (16777216)) + var1);
121
  var3 = (((int64_t) lookupTable2[gas_range] * (int64_t) var1) >> 9);
122
  calc_gas_res = (uint32_t) ((var3 + ((int64_t) var2 >> 1)) / (int64_t) var2);
123
124
  return calc_gas_res;
125
}

Der Gaswiderstand wird in sage und schreibe 64 Bit gerechnet...

Vielleicht hat ja von euch schon jemand die Routinen schon etwas 
abgespeckt?

von Heiner (Gast)


Lesenswert?

Temperatur auch...
Was auf jeden Fall schonmal geht, ist, diese Konstanten in der unteren 
Funktion ins Flash zu packen.

von Wolfgang (Gast)


Lesenswert?

Heiner schrieb:
> Wie ihr vielleicht wisst, gibt der Sensor aus irgendeinem Grund nur
> Rohdaten und eine Tabelle mit Kalibrierungsdaten raus und der arme µC
> muss damit klar kommen.

Der Grund wird sein, dass der Sensor aus Kostengründen keinen passenden 
Rechenkern enthält.

Lass den armen µC doch rechnen, oder was meinst du, wie das Verhältnis 
von Sensorzeitkonstanten zu Rechenzeit ist.

Es kann durchaus sein, dass die Kalibrierdaten genau auf diese von Bosch 
veröffentlichte Umrechnungsroutine angepasst sind. Bei Änderungen am 
Algorithmus und Abweichungen in der Rechengenauigkeit solltest du also 
genau gucken, wie empfindlich das Ergebnis darauf reagiert.

Vielleicht hatten aber auch die Betriebswirtschaftler bei der 
Entwicklung des Algorithmus die Oberhand, i.e. Auswerteroutine 
funktioniert, nicht mehr dran drehen, keine Zeit mehr reinstecken - 
fertig und raus damit ;-)

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Zumindest beim BME280 gabs dann ganz hinten im DB noch Rechenfunktionen 
in 32Bit.
Sind die beim 680er verschwunden?

von Heiner (Gast)


Lesenswert?

Ne, glaube nicht. Es gibt aber float-Versionen. Ist dann auch nur 32 
Bit, aber naja, ob das schneller geht oder weniger Flash braucht?

Was mich stutzig macht, ist das ständige Casten in Bitbreiten, die 
offensichtlich schon da sind. Und dann wird einfach mal nach signed 
gecastet. Und dann werden signed Variablen bitweise verschoben!? 
Programmiert Bosch alles so? Bauen die auch Komponenten für 
Flugzeuge.....?

Aber das nur am Rande.

von Wolfgang (Gast)


Lesenswert?

Heiner schrieb:
> Es gibt aber float-Versionen. Ist dann auch nur 32 Bit

Bei Float gehen von den 32 Bit bereits 8 für den Exponenten drauf.

von Max (Gast)


Lesenswert?

Warum bieten solche Sensoren ihre Messwerte nicht schon fix und fertig 
an???

von MaWin (Gast)


Lesenswert?

Heiner schrieb:
> ich würde die Routinen, die zum Auslesen des BME680 von Bosch nötig
> sind, gerne etwas vereinfachen/optimieren

Dann mach das doch (möglichst natürlich unter Beibehaltung der 
Genauigkeit).

Oder wolltest du schreiben: "Ich kann es nicht, und suche jemanden der 
es für mich macht".

Da stellt sich die Frage: Wozu ? Kann dein C Compiler es nicht 
übersetzen oder ist dein Restprogramm schon so umfangreich dass diese 
Routine nicht mehr in den uC passt ? Warum dann nicht das Restprogramm 
optimieren.

von Wolfgang (Gast)


Lesenswert?

Max schrieb:
> Warum bieten solche Sensoren ihre Messwerte nicht schon fix und fertig
> an???

Wolfgang schrieb:
> Der Grund wird sein, dass der Sensor aus Kostengründen keinen passenden
> Rechenkern enthält.

von Bert (Gast)


Lesenswert?

Wolfgang schrieb:
> Max schrieb:
> Warum bieten solche Sensoren ihre Messwerte nicht schon fix und fertig
> an???
>
> Wolfgang schrieb:
> Der Grund wird sein, dass der Sensor aus Kostengründen keinen passenden
> Rechenkern enthält.

Wieviel Leute würden wohl einen geringen Mehrpreis bezahlen um sich 
diesen aufwendigen Berechnungs-Hickhack zu sparen? Ich tippe mal das 
tendiert gegen 100%...

von Heiner (Gast)


Lesenswert?

Eigentlich ist das Ganze sowieso ein Witz. Der Sensor wird beworben mit 
"Messung der Luftqualität", liefert aber dafür nur einen elektrischen 
Luftwiderstandswert (neben Temperatur, Feuchte und Druck). Den 
beworbenen IAQ-Wert (indoor air quality), der sich aus diesen ableitet, 
kriegt man aber nicht, bzw. nur, wenn man die (closed source) Binaries 
verwendet. Zumindest habe ich das so verstanden. Die Binaries für ATMega 
(8 Bit) benötigen 25kB ROM und 1kB RAM...

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Bert schrieb:
> Wieviel Leute würden wohl einen geringen Mehrpreis bezahlen um sich
> diesen aufwendigen Berechnungs-Hickhack zu sparen? Ich tippe mal das
> tendiert gegen 100%..

Ich tippe eher mal gegen 0%.
Die paar Krümel die von Privatpersonen gekauft werden interessieren 
keinen Hersteller. Für die zählen die Kunden die die Dinger 
Millionenfach kaufen und für die geht es um jeden 1/10 Cent.

von E. Zellner (Gast)


Lesenswert?

Irgend W. schrieb:
> Für die zählen die Kunden die die Dinger
> Millionenfach kaufen und für die geht es um jeden 1/10 Zent.

Nö. Wenn es um jeden 1/10 Cent geht wählt man eher eine andere Lösung. 
Dafür sind die Teile zu teuer. Auch dafür, "millionenfach" verwendet zu 
werden. Ich finde, die Anwrndungs- "Simplicity" wird von den Herstellern 
chronisch unterbewertet!

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Ich versuche, in den Unterlagen irgendwo eine vernünftige Beschreibung 
zu finden, wie die Rohdaten in korrigierte Werte umgerechnet werden. Ein 
echter Fall von Code-Obfuscation. Anscheinend ist ein Programmteil 
namens "bsec_do_steps()" zuständig, den ich aber noch nicht gefunden 
habe.

Von Bosch gibt es die ZIP-Datei "BSEC_1.4.7.4_Generic_Release.zip" für 
mehrere Controller. ATMEGA2560 oder Arduino oder diverse andere, sind 
das die richtigen Quelltexte?
https://ae-bst.resource.bosch.com/media/_tech/media/bsec/BSEC_1.4.7.4_Generic_Release.zip

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

https://forum.fhem.de/index.php/topic,78619.msg706435.html#msg706435
"bsec_do_steps()  scheint nur in den precompiled libs zu existieren....
Die scheinen Angst vor den Chinesen zu haben "

von Heiner (Gast)


Lesenswert?

Die Werte erhält man durch o.g. Berechnungen. Bosch bietet zusätzlich 
(optional) die BSEC-Software an, welche noch etwas mehr Korrektur 
anbietet. Rausrechnen von Wärmequellen usw. Beispielsweise kriegt man so 
wohl Temperatur und Feuchte außerhalb des Gerätes und auch den ominösen 
IAQ-Wert etc.
BSEC ist closed-source! Man kann nur die API runterladen und 
verschiedene Binaries. Ich habe es nicht probiert, da es für mein 
Projekt auch Overkill ist. Außerdem passt es wohl kaum in meinen 
ATMega328. ;)

von Heiner (Gast)


Lesenswert?

Ein chinesischer Chip würde die Werte wahrscheinlich direkt ausspucken, 
wie jeder andere vernünftige Sensor, ohne aufwändige externe Rechnerei. 
Wenn ich die Wahl hätte, würde ich das auch bevorzugen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn ich lesen muss das Bosch möchte für die Arduino IDE den arduino 
builder auszutauschen, dann weiß ich nicht was Bosch falsch gemacht hat. 
Ich würde sagen probiere erstmal diese Lib 
https://github.com/adafruit/Adafruit_BME680

von Heiner (Gast)


Lesenswert?

Ist derselbe Code wie bei Bosch. Auch 64bit bzw. float.

von Veit D. (devil-elec)


Lesenswert?

Schade.

von Michael U. (amiga)


Lesenswert?

Hallo,

ich habe mit dem BME680 jetzt mal etwas rumgespielt und auch mit der 
BSEC-Software, allerdings auf einem ESP32.
Diese Version habe ich zumindest zu Laufen bekommen:
https://github.com/BoschSensortec/BSEC-Arduino-library

ArduionoIDE 1.8.10 und ESP32-Boardmanagerversion 1.04.
Allerdings hat es da ein anderes Problem gegeben: nach der Änderung der 
plattform.txt compiliert nichts anderes mehr für den ESP32.......

Die Version ist auch für 8Bit AVR dabei, aber:
1
Platform Type Compiler ROM size of
2
API in bytes                            7814            
3
ROM(.text+.data) in bytes Normal/lite   43291 / 26085
4
RAM(.data+.bss) in bytes Normal/lite    1064 / 1048
5
File Size* In bytes Normal/lite         141k / 85k
Daten aus BSEC Binary Size Information.pdf.
Es kämen also nur Mega644/1284/2560 in Frage.

Mein Problem ist aber eher der Sensor selbst: das Ding misst die 
relative Luftgüte. Er muß durchlaufen und liefert erst nach mehreren 
Tagen sinnvolle Werte, auch nur dann, wenn es ausreichend gro0e 
Änderungen der Luftgüte gab.

Wenn das Ding z.B. in einer Werkstatt hängt, wo Staub, Dämpfe o.ä. 
auftreten, hat mach der Einlaufzeit einen Wert für "beste Luft" skaliert 
auf 25 des IAQ und eine "schlechte Luft" skaliert auf 250.
Abweichungen nach untern bzw. nach oben geben dann an, daß sich die 
Luftqualität am Ort verbessert oder verschlechtert hat.
Ob man ohne die BSEC-Lib ausgegebenen Wert des "Gas-Widerstandes" 
überhaupt irgendwie nutzen kann, ist mir im Moment völlig unklar.

Ich habe also keine Ahnung, was ich mit dem Ding nun eigentlich mache, 
aber zum Glück habe ich ihn ja nur aus reinem Spaß und Interesse 
gakauft.

Gruß aus Berlin
Michael

: Bearbeitet durch User
von Heiner (Gast)


Lesenswert?

Dazu kommt, dass der zurück gegebene Widerstand davon abhängt, wie oft 
man misst. Wenn man seltener misst, ist der Wert kleiner. Ich nehme an, 
dass sich die Substanzen aus der Luft im Sensor anlagern und man das 
Messen gleichzeitig als "Freibrennen" betrachten kann. Wenn man öfter 
freibrennt, sind eben weniger flüchtige Stoffe im Sensor...

von Michael U. (amiga)


Lesenswert?

Hallo,

Michael U. schrieb:
> Diese Version habe ich zumindest zu Laufen bekommen:
> https://github.com/BoschSensortec/BSEC-Arduino-library
>
> ArduionoIDE 1.8.10 und ESP32-Boardmanagerversion 1.04.
> Allerdings hat es da ein anderes Problem gegeben: nach der Änderung der
> plattform.txt compiliert nichts anderes mehr für den ESP32.......

ich zitiere mich mal selbst, man sollte nicht eine Ergänzung in der 
platform.txt vergessen, dann klappt es alles ohne Probleme.

Ich werde das mal an einem Odroid Go dran lassen, die Ausgabe auf dem 
Display noch einbauen und den Kram mal längere Zeit beobachten. Bekommt 
dereine Odroid Go wenigstens mal eine Verwendung. ;)

Gruß aus Berlin
Michael

Beitrag #6593810 wurde von einem Moderator gelöscht.
Beitrag #6593819 wurde von einem Moderator gelöscht.
Beitrag #6594289 wurde von einem Moderator gelöscht.
Beitrag #6594471 wurde von einem Moderator gelöscht.
Beitrag #6594508 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.