News:

Let's find out together what makes a PIC Tick!

Main Menu

BME280 Pressure, Temperature and Humitity Sensor

Started by Bob (G8GFA), Feb 01, 2021, 11:32 AM

Previous topic - Next topic

Bob (G8GFA)

Example code for the BME280 sensor.

'****************************************************************
'*  Name    : BME280.BAS                                        *
'*  Author  : Bob Marshall                                      *
'*  Date    : 26/07/2016                                        *
'*  Version : 1.0                                               *
'*  Notes   : This program uses a BME280 sensor to measure      *
'*          : current pressure, temperature, humidity and       *
'*          : altitude.                                         *
'****************************************************************


  Include "Amicus18_mk2.inc"

  Declare Float_Display_Type = Fast
  Declare Hbus_Bitrate 400

  Symbol BME280 = $EC  'BME280 I2C address
  Symbol CalDatStAd = $88 'Start address for cal. data.
  Symbol HumCalDatAd = $E1 'Start address for MAIN humidity cal. data.
  Symbol BME280HumCtrlReg = $F2 'BME280 humidity control register
  Symbol BME280ContReg = $F4 'BME280 control Register
  Symbol BME280ConfigReg = $F5 'BME280 configuration register
  Symbol BME280DataReg = $F7 'Uncompensated Data start register

'Values of 0-3 acceptable but see data sheet. 3 = Normal Mode
  Symbol PowerMode = 3

'Values of 0-5 acceptable but see data sheet. 5 = 20 bit res.
  Symbol TempRes =  5

'Values of 0-5 acceptable but see data sheet. 5 = 20 bit res.
  Symbol PresRes = 5

'Values of 0-7 acceptable. See data sheet before changing.
  Symbol Filter = 4

'Values of 0-7 acceptable but see data sheet. 4 = 500ms.
  Symbol Standby =  4

'Values of 0-5 acceptable but see data sheet.
  Symbol BME280CtrlHum = 5
 
'Temperature related variables.
  Dim T1 As Word
  Dim T2 As SWord
  Dim T3 As SWord

  Dim UT As Word  'Uncompensated temperature
  Dim XLSB_t As Byte
  Dim UTLong As SDword
  Dim Temperature As Float
  Dim T_fine As Float

'Pressure related variables
  Dim P1 As Word
  Dim P2 As SWord
  Dim P3 As SWord
  Dim P4 As SWord
  Dim P5 As SWord
  Dim P6 As SWord
  Dim P7 As SWord
  Dim P8 As SWord
  Dim P9 As SWord

  Dim UP As Word 'Uncompensated pressure.
  Dim UPLong As SDword
  Dim XLSB_p As Byte
  Dim P As Float

'Humidity related variables.
  Dim H1 As Byte
  Dim H2 As SWord
  Dim H3 As Byte
  Dim H4 As SWord
  Dim H5 As SWord
  Dim H6 As Byte
  Dim H4p1 As Byte      'H4 high bits
  Dim H4p2H5p2 As Byte  'H4 & H5 low bits
  Dim H5p1 As Byte      'H5 high bits

  Dim UH As Word   'Uncompensated Humidity
  Dim H As Float

'BME280 related variables.
  Dim BME280Config As Byte
  Dim BME280Control As Byte

'General variables
  Dim var1 As Float
  Dim var2 As Float
  Dim Altitude As Float
  Dim SeaLevel As Dword
  Main:

  DelayMS 500 'Let things settle down.
  Clear

  'Setup BME280 Control and configuration registers.

 
  HBusOut BME280,BME280HumCtrlReg,[BME280CtrlHum]

  BME280Config = (Standby <<5) + (Filter <<2) ' Combine configuration bits
  HBusOut BME280,BME280ConfigReg,[BME280Config]

  BME280Control = (TempRes <<5) + (PresRes <<2) + PowerMode ' Combine BME280Control bits.
  HBusOut BME280,BME280ContReg,[BME280Control]


' Read calibration data
  HBusIn BME280,CalDatStAd,[T1,T2,T3,P1,P2,P3,P4,P5,P6,P7,P8,P9]
  HBusIn BME280, $A1,[H1]'First byte of humdity comp. data held in $A1
  HBusIn BME280,HumCalDatAd,[H2,H3,H4p1,H4p2H5p2,H5p1,H6]

  H4 = (H4p1 << 4)|(H4p2H5p2 & $0F)
  H5 = (H5p1 << 4)|(H4p2H5p2 >> 4)

  Swap T1.Byte0,T1.Byte1 : Swap T2.Byte0,T2.Byte1 : Swap T3.Byte0,T3.Byte1
  Swap P1.Byte0,P1.Byte1 : Swap P2.Byte0,P2.Byte1 : Swap P3.Byte0,P3.Byte1
  Swap P4.Byte0,P4.Byte1 : Swap P5.Byte0,P5.Byte1 : Swap P6.Byte0,P6.Byte1
  Swap P7.Byte0,P7.Byte1 : Swap P8.Byte0,P8.Byte1 : Swap P9.Byte0,P9.Byte1
  Swap H2.Byte0,H2.Byte1

  While
'Read the uncompensated temperature and pressure values.
      HBusIn BME280,BME280DataReg,[UP,XLSB_p,UT,XLSB_t,UH]
      UTLong = UT * 16 + (XLSB_t >> 4)
      UPLong = UP * 16 + (XLSB_p >> 4)

' Test data from the Bosch datasheet. Used to check calculations.
'         T1=27504  : T2=26435  : T3=-1000
'         P1=36477  : P2=-10685 : P3=3024
'         P4=2855   : P5=140    : P6=-7
'         P7=15500  : P8=-14600 : P9=6000
'         UTlong=519888 : UPlong=415148
' Expected results: Temperature = 25.08, Pressure =  100656.26

      var1=(UTLong/16384-T1/1024)*T2
      var2=(UTLong/131072-T1/8192)*(UTLong/131072-T1/8192)*T3
      Temperature=(var1+var2)/5120
      T_fine=(var1+var2)


' $B0 is the hex value for the degree symbol. Not correctly displayed on some terminals.
      HSerOut ["Current temperature is: ",Dec2 Temperature,$B0,"C",13]

' Pressure calculations start here.

      var1=T_fine/2-64000
      var2=var1*var1*P6/32768
      var2=var2+var1*P5 * 2
      var2=(var2/4)+ P4 * 65536
      var1=(P3*var1*var1/524288+P2*var1)/524288
      var1=(1+var1/32768)*P1
      P=1048576-UPLong
      P=(P-var2/4096)*6250/var1

' The *-1 is needed here to negate the value of var1
' See p23 of the Bosch data sheet (example calculation).
      var1=(P9*P*P/2147483648)*-1

      var2=P*P8/32768
      P=P+(var1+var2+P7)/16

      HSerOut ["Current pressure: ",Dec2 P,"Pa",13]

' Note: The altitude calculation takes about 1.5k of program memory.
'     : Remove if not needed!

' Altitude = 44330*((1-(p/101325)) ^ (1/5.255))
' 101325 represents the 'standard' pressure at sea level. Adjust to suit.

      SeaLevel = 101200 ' Air pressure in pascals at sea level
      var1 = Pow((P/SeaLevel),0.190295)
      Altitude =  44330 *(1-var1)
      HSerOut ["Current altitude: ",Dec2 Altitude ,"m",13]

'Humidity Calculations.
      H = T_fine - 76800
      H  = (UH - (H4 * 64 + H5 / 16384 * H)) * (H2 / 65536 * (1 + H6 / 67108864 * H * (1.0 + H3 / 67108864 * H)))
      H = H * (1 - H1 * H / 524288)
      If H > 100 Then
          H = 100
      Else If H < 0 Then
          H = 0
      EndIf

      HSerOut ["Current humidity: ",Dec2 H ,"%",13,10,13]

      DelayMS 2000

  Wend

' End of program

jpeeters19

Hi, i copy pasted this code yesterday on a 18f4685, but the altitude gives a negative result or is way off and humidity shows always zero. Temperature and pressure seems to be ok.

Gods

Quote from: jpeeters19 on Mar 15, 2021, 04:17 PMand humidity shows always zero. Temperature and pressure seems to be ok.

You are using a BMP280 sensor and not a BME280 sensor. The BMP does not have a humidity measurement.

John Drew

Your altitude formula will use less memory and work faster if you break the calculation into smaller parts with temporary variables if necessary.
It's counter intuitive but something I've found when doing complex calculations.
John

jpeeters19

#4
Quote from: Gods on Mar 15, 2021, 11:23 PMYou are using a BMP280 sensor and not a BME280 sensor. The BMP does not have a humidity measurement.

Hmm i did check the sensor under a microscope and it has the UP characters on it so it should be a real bme280 chip. But i ordered another genuine bme280 from mouser, so i can verify if it was a fake from ali :-)

towlerg

I may have misunderstood this but for altitude don't you need to change this constant. Bob?
SeaLevel = 100000

top204

#6
Excellent code Bob.

QuoteIt's counter intuitive but something I've found when doing complex calculations.

A large expression requires the compiler to create temporary system (expression stack) variables to hold the seperate parts of it, in order to maintain the expression's hierarchal order, and must use "worse case" schenarios when mixed variable types are used in it. i.e. If using floats and integers, it must convert all integers to floats, which takes more code and time and RAM. Or if an expression has some Dword (32-bit) variables in it and the assignement is a Dword, all the expression's underlying code must use Dword, expression stack, variables to make sure there are no value overloads. All compilers use temporary variables for large expressions, as a stack system, and these need loading and unloading etc, so it takes longer to perform, and uses more code memory.

It has always been better to break an expression into smaller chunks, so the user has control of the hierarchal management and the variable types, and the use of, re-usable, temp variables. I even do this in Delphi and C and C++ as well because it actually creates faster underlying code.

In some C and C++ programs, you see huge expressions on a single line, or huge comparisons or booleans etc, because the user thinks that will produce smaller code because it all fitted on a single, high level, line. However, that is "never" the case with any compiler, and even with castings, it produces large underlying asm code because the compiler must use worse case scenarios, and can not always see the full line of code and must do each section of the line in turn, even with its lexical scanner seperating its list tree, it will never see everything in a single event and has to store bits and pieces it may need further along the tree. Thus wasting space and time. However, a user can see "all" the seperate, hierarchal, parts of an expression and speperate them into appropriate variable types, then join them as needed because we can see as much of the expression as we require and change things in any order, instead of sequentially.


jpeeters19

Today i received the original bosch sensor and tested it. Now the code works fine. So it was a fake BME280 from Ali

Bob (G8GFA)


towlerg


Bob (G8GFA)

#10
Sorry George, I missed post 5. Yes, you are correct. The value represents the pressure in pascals and must be adjusted to suit local conditions if accurate results are to be obtained. The temperature at sea level being a major factor.

I hope all is well with you George.

Bob

towlerg

Hi Bob all good here, hope same with you.

Dumb question but why not feed actual pressure into the equation?

George

Bob (G8GFA)

The reason, I think George, is that altitude is calculated by looking at the difference between the pressure at sea level and that which is currently being measured. The higher you go the lower the air pressure. The real problem here is that the air pressure at sea level constantly changes so your calculation is going to be approximate at best.


Bob

AllanD

Thank you very much Bob for posting the BME280 code. I've been playing with it for a bit, and I've noticed the humidity will only display to 75% max, and then it goes to 0%. I've tried chasing the math through, but I can't seem to see an error.

trastikata

- The question is do you need the absolute altitude or the relative altitude above some starting point?

- Another thing using left and right shifts for multiplication/division by 2^n should be faster. For example UTLong/131072 is the same as UTLong >> 17

- For the BME's ADC to pressure conversion - you have multiplication and division by constants in the same expression. For example var2=(var2/4)+ P4 * 65536  --> here P4 * 65536 you don't have to recalculate it every time, calculate it one time and place it in a variable.

John Drew

AlanD, your jump of value might be an instance of variable overflow. Check your variable declarations.
It's a bit hard to diagnose further while I'm using my phone.
John

towlerg

Quote from: trastikata on Mar 25, 2021, 09:49 PM- Another thing using left and right shifts for multiplication/division by 2^n should be faster. For example UTLong/131072 is the same as UTLong >> 17

- For the BME's ADC to pressure conversion - you have multiplication and division by constants in the same expression. For example var2=(var2/4)+ P4 * 65536  --> here P4 * 65536 you don't have to recalculate it every time, calculate it one time and place it in a variable.

I wonder if you'd put together a modified version of Bob's code reflecting the changes you suggest. BME280 is such a nice device seems a pity not to run it optimally. I'd do it myself but I have a doctors note which excuses me from hard sums.

trastikata

#17
Quote from: towlerg on Mar 26, 2021, 11:28 AMI wonder if you'd put together a modified version of Bob's code...

Here is the code I used some time ago to test the BME280 - all calculations are broken down to simple expressions. I think it worked then, could you run it to see if it still works  :). The only difference from Bob's code is that I temperature compensated the altitude above sea level at each iteration using the current temperature.

The code might look longer, but actually takes about 330 program bytes and 11 variables less. The processing time is about 500 instruction cycles faster.

'Calculated results
Dim pressure As Float
Dim temperature As Float
Dim humidity As Float
Dim altitude As Float

'BME's ADC data
Dim BME280_temperature As SDword
Dim BME280_pressure As SDword
Dim BME280_humidity As Word

'Coefficients
Dim BME280_T1 As Word
Dim BME280_T2 As SWord
Dim BME280_T3 As SWord
Dim BME280_P1 As Word
Dim BME280_P2 As SWord
Dim BME280_P3 As SWord
Dim BME280_P4 As SWord
Dim BME280_P5 As SWord
Dim BME280_P6 As SWord
Dim BME280_P7 As SWord
Dim BME280_P8 As SWord
Dim BME280_P9 As SWord
Dim BME280_H1 As Byte
Dim BME280_H2 As SWord
Dim BME280_H3 As Byte
Dim BME280_H4 As SWord
Dim BME280_H5 As SWord
Dim BME280_H6 As Byte

'Temporary variables
Dim BME280_X1 As Float
Dim BME280_X2 As Float
Dim BME280_X3 As Float
Dim BME280_T As Float

'One time calculation constants
Dim BME280_C1 As Dword
Dim BME280_C2 As SDword
Dim BME280_C3 As SDword
Dim BME280_C4 As Float
Dim BME280_C5 As SDword
Dim BME280_C6 As Float
Dim BME280_C7 As 0.1902632

CALCULATE_CONSTANTS:
    BME280_C1 = BME280_T1 << 1
    BME280_C2 = BME280_P5 * 2
    BME280_C3 = BME280_P4 * 65536
    BME280_C4 = BME280_P8 / 32768
    BME280_C5 = BME280_H4 * 64
    BME280_C6 = BME280_H1 / 524288

CALCULATE_BME280:
    'Temperature in C*
    BME280_X1 = (BME280_temperature >> 3) - BME280_C1
    BME280_X1 = BME280_X1 / 2048
    BME280_X1 = BME280_X1 * BME280_T2
    BME280_X2 = (BME280_temperature >> 4) - BME280_T1
    BME280_X2 = BME280_X2 / 64
    BME280_X2 = BME280_X2 * BME280_X2
    BME280_X2 = BME280_X2 / 16384
    BME280_X2 = BME280_X2 * BME280_T3
    BME280_T = BME280_X1 + BME280_X2
    BME280_X1 = BME280_T * 0.0001953125
    temperature = BME280_X1 + 0.005   
   
    'Pressure in Pa
    BME280_X1 = (BME280_T / 2) - 64000
    BME280_X2 = (BME280_X1 * BME280_X1) / 32768
    BME280_X2 = BME280_X2 * BME280_P6
    BME280_X2 = BME280_X2 + (BME280_X1 * BME280_C2)
    BME280_X2 = (BME280_X2 / 4) + BME280_C3
    BME280_X3 = BME280_X1 / 524288               
    BME280_X3 = BME280_X3 * BME280_P3 * BME280_X1
    BME280_X1 = BME280_X3 + (BME280_P2 * BME280_X1)
    BME280_X1 = BME280_X1 / 524288
    BME280_X1 = (BME280_X1 / 32768) + 1
    BME280_X1 = BME280_X1 * BME280_P1
    pressure = 1048576 - BME280_pressure
    pressure = pressure - (BME280_X2 / 4096)
    pressure = pressure * (6250 / BME280_X1)   
    BME280_X1 = BME280_P9 * pressure
    BME280_X1 = BME280_X1 / 2147483646
    BME280_X1 = BME280_X1 * pressure
    BME280_X2 = pressure * BME280_C4
    BME280_X3 = (BME280_X1 + BME280_X2 + BME280_P7)
    BME280_X3 = BME280_X3 / 16
    pressure = pressure + BME280_X3
   
    'Humidity
    BME280_X1 = BME280_T - 76800
    BME280_X2 = BME280_X1 * BME280_H3
    BME280_X2 = BME280_X2 / 67108864
    BME280_X3 = BME280_H6 * BME280_X1
    BME280_X3 = BME280_X3 / 67108864
    BME280_X2 = BME280_X2 * BME280_X3
    BME280_X2 = BME280_X2 + 1
    BME280_X2 = BME280_X2 * BME280_H2
    BME280_X2 = BME280_X2 / 65536
    BME280_X3 = BME280_X1 / 16384
    BME280_X3 = BME280_X3 * BME280_H5
    BME280_X3 = BME280_X3 + BME280_C5
    BME280_X3 = BME280_humidity - BME280_X3
    BME280_X2 = BME280_X2 * BME280_X3
    BME280_X3 = BME280_X2 * BME280_C6
    BME280_X3 = 1 - BME280_X3
    humidity = BME280_X2 * BME280_X3
    If humidity > 100 Then humidity = 100
   
    'Altitude (temperature compensated) above sea level in meters
    BME280_X2 = temperature + 273.15
    BME280_X1 = BME280_X2 / (-0.0065)
    BME280_X2 = pressure / 101325
    BME280_X3 = Pow(BME280_X2, BME280_C7)
    BME280_X2 = BME280_X3 - 1
    altitude = BME280_X2 * BME280_X1
   
END_OF_PROGRAM:
    End


AllanD

Quote from: John Drew on Mar 26, 2021, 10:41 AMAlanD, your jump of value might be an instance of variable overflow. Check your variable declarations.
It's a bit hard to diagnose further while I'm using my phone.
John

Yes, that's probably it. When the displayed humidity reaches about 72.74%, it rolls over to ~-310.89%.

Craig

Hi I am running some tests on the BME280 Sensor using some sample code that bob has provided above thank you Bob. These are Adafruit sensors so I presume they are all original Bosch sensors. The problem that I have encountered is that the Temperature is reading 1.5°C too High and the Humidity is way out it reads 17% too High. The real reading in my enclosure is say 23% humidity but the sensor indicates that the humidity is 40%. Has anyone encountered a similar problem with these sensors? The problem is that I am using them in a commercial application and I need 0.1°C accuracy on the Temperature and 3% on the Humidity. I can look at the SHT-40 Sensors but, the Bosch sensor should be accurate according to its data sheet.

Can anyone please point me in the right direction how to deal with this as I am sitting between a rock and a hard place with this?
Regards
Caraig