BMP085 pressure and temperature readings via a 28X2

Simmicht

Senior Member
This is a working solution. I am using the BMP085 to provide pressure data in a remote weather station setup.
I have used the Q16.16 Arithmetic Module by Jeremy Leach and a paper Floating Point Calculations for the BMP085

The readings were a little variable so I used the oversampling to reduce the noise, the BMP085 takes 8 readings and returns the less noisy result.
The code size is 3233 bytes but on a 28X2 this is only one slot....:)

I have yet to really go into the accuracy of all the trickery to make a pressure reading pop out, and I did have to apply a -6.2 hPa fudge factor at the end, but it does seems to work.


Weather station 3G P1050182 50.JPG

View attachment BMP085 with FractionalMaths.bas
 

g6ejd

Senior Member
Out of interest, I use the pressure sensor mpx4115a and get very good results, exactly the same a my Davis 6150 weather station and it tracks all of the local METAR stations exactly. I don't bother with anything less than 0.1hPa resolution. What is your altitude (from Google Earth maybe), as that will affect the calibration hence an offset of 6.2 needing to be introduced.
 

Simmicht

Senior Member
yes I do realize that sitting on floor 5 at work at about 100m above sea level will affect the reading.
The thing that does worry me is that using the same module in a Arduino I used a +3.9 to get a reading same as met office.
When I feed the data straight into the formulas provided, I get 1015.6 to which I add 3.9 to get 1019.5, same a met office.
Whereas this code calculates 1026.4 to which i add -6.3 to get 1020.1
So I need to recheck all the calculations.
Terry
 

Simmicht

Senior Member
bug fix

I found one place to increase accuracy.
Replace the lines for calculating y0 with these.
I was doing a 2 step
Code:
C4 = BMP_AC4 / 2^15 / 1000  
y0 = C4 * s^15
Now
Code:
y0 = BMP_C4/1000
which makes more accurate value for y0


so replace the code for calculating y0 (around lines 615-640) with the lines below

Code:
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'Sertxd(cr,lf,"y0 = C4 * s^15")
'Sertxd(cr,lf,"y0 = BMP_C4/1000/2^15 * 2^15")
'Sertxd(cr,lf,"y0 = BMP_C4/1000 ")
'get stored result
'Address = FPC_C4:PEEK Address, Word AMSW,Word ALSW

AMSW = BMP_AC4 // 1000
ALSW = 0    
BMSW = 1000
BLSW = 0
Operator = Op_Div
Gosub Calculate        'calculate fraction part of C4/1000
    
'Sertxd (cr,lf," = "):GoSub DispResult

RMSW = BMP_AC4 /  1000    'integer part of C4/1000
RLSW = RLSW            'fractional part of C4/1000

'save result
Address = FPC_y0:POKE Address, Word RMSW,Word RLSW
#IFDEF ShowDebug 
Sertxd (cr,lf,"FPC_y0 = "):GoSub DispFullResult
#ENDIF
 

Simmicht

Senior Member
Another Bug Fix

I found that the calculations would occasionally lock up.

One additional check is needed in the division routine.
The second line needs to be added after the first line
Code:
        If FLSW >= EndThreshold Then Exit 'Exit when Fn has converged to 1.
        If FLSW = 0 and FMSW=1  Then Exit 'Exit when Fn has converged to 1.
 

g6ejd

Senior Member
I have now been reading pressure (and temperature) from a BMP085 and note a similar offset to Simmicht, I have to add 5.8hPA to get close to the average of all the local weather stations in Pa. I have found a good article on the sparkfun webpage that explains why this is happening, but I have yet to write a routine to calcualte true altitude from the BMP085 pressure reading, I'll do that in the next few days. It's not a sensor issue, it's a conversion problem and the need to normalise the results.

Another good source of explanations is: https://github.com/adafruit/Adafruit-BMP085-Library

Essentially use the following: altitude = 44330 * (1-(BMP085PressureResult/TodaysAirPressureAtYourLocation)^(1/5.255))

Using Average Sea level air pressure for 'TodaysAirPressure' does not give the correct result (real altitude), it gives it relative to sea level - confusing I know!

e.g. Currently the local air pressure is 100815pa, the BMP085 reads 100325pa the calculated altitude is therefore 41.08M. Google maps says 40M, my GPS says 39M, so an error of 4% relative to the average of those sources, not bad.
 
Last edited:

ThierryP

New Member
Simmicht,
thanks for this post on BMP085 calculus with fractional math.
I've used / adapted it for a Pressure and Temp reader with a serial OLED/LCD unit for readout (AXE133Y).
The following adaptations are made:
  • turned off 8x oversampling by hardware (OSS=0, or '1x' oversampling)
  • introduced 256x oversampling in software by a) accumulating 256 2-byte rawP and 256 rawT readings, b) then divide by 256, c) then do the Fractional Maths to calculate real P and T, d) then display
  • optimised P and T readings somewhat for speed by a) only doing FPC calculation at initialisation (as these don't change over time), b) clocking the 28X2 at 32MHz
  • red LED flashes when new results are displayed
  • me too, I needed a 'Fudge factor', I only need to add 0.6 hPa for my BMP085
  • rest is unchanged, some coding was visually compacted a bit
The P and T results are displayed on the serial LCD every 3-4 seconds in resolution of 0.01 hPa and 0.01 degrees Celcius.
I anticipated that these values would be too much and readings would go all over the place as they are quite below the speficied absolute and relative error of the BMP085 device.
I was pleasantly surprised.
I'm not claiming I can backup this by comparison with a laboratory test unit, but I do believe to see from the display readings an rms value of below <0.10 hPa for P and <0.05 C for T.
For the range of 980-1040 hPa and 18-27 C.

Attaching the .bas file for 28X2
View attachment 28X2_BMP085 with FractionalMaths 8.bas

Next step I plan to use a 20X to read humidity with a DHT11, P and T with a BMP085 and to drive a 4x20 LCD display directly.

Thierry
 

ThierryP

New Member
I've now ported the BPM085 program from Terry Simmich to a 20X2, which was relatively straightforward except for the smaller RAM size compared to the 28X2, so I had to squeeze for a little bit of room.
Plus added Humidity reading with a DHT11 unit using an external 555 timer IC, as described here:
The code is attached.
View attachment 20X2_BMP085_DHT11_1.bas

Next I'd like to try the same 20X2 to drive the LCD/OLED display directly rather than via SerialLCD unit, as is the case now.
Not sure if I can reuse pins B.5 and B.7 also to directly drive an LCD as these are now also in use for I2C signals to BMP085. Anyone tried this?

Thierry
 
Last edited:

nwright

New Member
Essentially use the following: altitude = 44330 * (1-(BMP085PressureResult/TodaysAirPressureAtYourLocation)^(1/5.255))


Hi

did you ever manage to write a routine using Q16.16 for the above equation? I am struggling breaking it down to base operators. Any help with this would be most appreciated

Thanks

Neil
 

ThierryP

New Member
Neil,

No, I've not tried that equation, as it has a fractional power.
I did convert a similar pressure to height equation to a Taylor series or linear, quadratic or cubic approximation, which can then be done in Q16.16. E.g.
H = a.P^3 + b.P^2 + c.P + d
If you can give me the pressure and heights range you require and the maximum error you'd accept due to calculus approximation error, I can give it a try.
Both should provide good results over a few km of height difference

Thierry
 

nwright

New Member
Hi Thierry

so far as accuracy: 5 to 10% is ok for this first cut project - it is more the principle that is of importance and getting the project completed. So far as height ranges: between 0 and 10Km is fine. For pressures: between 1500 and 300 Hpa should be more than enough for initial testing

Again- your help is much appreciated

Thanks

Neil
 

rq3

Senior Member
It's not a BMP085 or a 28X2, but follows some code (with ALOT of forum help) for a Freescale MPL3115a2 on a Picaxe 20M2. There's a lot of extra "stuff" like stepper motors and touch switches, but the actual barometer routines and Pascals to feet conversions may be of use. In the US, the local barometer reading is in inches of mercury, and is entered into an altimeter via the Kollsman window dial. Hence the Pascal to inHg conversion, and the Kolls variable. It's also interesting to note that these devices are capable of detecting "microbaroms", very small changes in atmospheric pressure caused by infrasonic ocean waves. The constant wandering of the data over a few Pascals is real. As you can see, I've tried to use ALL of the commands in Manual 2, so any code critique is more than welcome!

Code:
#PICAXE 20M2
#no_data
disablebod
disabletime
setfreq M32
dirsC=%11111101                                                                ;make C.1 input(C.1 is decay mode)

high A.0                                                                            ;prepare for T38400_32 serial
pause 40000                                                                        ;initialize display
serout A.0, T38400_32,(0x00,0x0C,0x00,0x00)        ;screen saver time off(no scroll)
serout A.0, T38400_32,(0xFF,0x66,0x00,0x10)        ;contrast
serout A.0,T38400_32,(0xFF,0xD7)                            ;clear screen
pause 60                                                                            ;7 msec. delay
serout A.0,T38400_32,(0xFF,0x76,0x00,0x01)        ;bold
serout A.0,T38400_32,(0xFF,0xE4,0x00,0x04,0x00,0x00);cursor mid-screen

hi2csetup i2cmaster,0xC0,i2cfast_32,i2cbyte        ;I2C setup
    
symbol Data_Reg=b0    
symbol Ref_Msb=w1
symbol Ref_Csb=w2
symbol Ref_Lsb=w3
symbol Ref_Alt=w4
symbol Cur_Alt=w5
symbol Delta=w6
symbol Steps=w7
symbol Touch_Flag=w8                                                    ;touch sensor flag
symbol Kolls=w9                                                                ;local Kollsman setting-inHg x 100
symbol Bar_Val=w10                                                        ;barometer setting
symbol Alt_Val=w11
symbol Limit=1000                                                            ;capstan step rotation limit
symbol Gain=100                                                                ;sets step multiplier
symbol CW_32=%10100100                                                ;step size 1x1xxxx1,direction xxxxx1xx
symbol CCW_32=%10100000

main:
pause 60
serout A.0,T38400_32,(0xFF,0x7F,0xF8,0x00,0x00,0x06,"     OFF    ","    ",0x00)

test:                                                                                    ;detect switch condition
    outpinsC=%00001000                                                    ;disable the stepper driver
    do
    gosub finger
    loop until Touch_Flag=4
    hi2cout 0x26,(0x02)                                                    ;reboot/reset
    hi2cout 0x26,(0xBA)                                                    ;enable altitude/128 OSR/Now
    hi2cout 0x13,(0x02)                                                    ;enable pressure data flag
    hi2cout 0x26,(0xBB)                                                    ;make device active

    Kolls=Kolls*20**55486                                                ;convert inHg to Pascals
    hi2cout 0x14,(b19,b18)                                            ;write Kollsman to sensor

ref:
gosub sensor
Ref_Alt=Ref_Msb+Ref_Csb*4**53756+Ref_Lsb        ;meters to feet

alt_1:
gosub sensor
Cur_Alt=Ref_Msb+Ref_Csb*4**53756+Ref_Lsb        ;meters to feet
    
serout A.0,T38400_32,(0xFF,0xE4,0x00,0x04,0x00,0x00)     ;cursor mid-screen
serout A.0,T38400_32,(0xFF,0x76,0x00,0x01)                        ;bold

compare:
    if Cur_Alt=Ref_Alt then
    goto alt_1                
    elseif Cur_Alt>Ref_Alt then
    Delta=Cur_Alt-Ref_Alt*Gain max Limit                ;steps=delta*Gain
    serout A.0,T38400_32,(0xFF,0x7F,0x00,0x1F,0x00,0x06,"ALT: ",#Cur_Alt," FT     ",0x00)
    goto ccw
    elseif Cur_Alt<Ref_Alt then
    Delta=Ref_Alt-Cur_Alt*Gain max Limit
    serout A.0,T38400_32,(0xFF,0x7F,0xF8,0x00,0x00,0x06,"ALT: ",#Cur_Alt," FT     ",0x00)
    goto cw
    endif
        
cw:
 outpinsC=CW_32                                                                ;stepper/direction low
 goto turn

ccw:
 outpinsC=CCW_32                                                            ;stepper/direction high
 goto turn
  
turn:
 for Steps=1 to Delta                                                 ;step pulses decelerating rate
 pauseus Steps
 pulsout C.4,2                                                                ;make one step
 gosub finger                                                                    ;check touch switch
 next Steps
 goto alt_1

finger:
touch16 [%11101000],B.0,s_w1
touch16 [%11101000],B.1,s_w2
touch16 [%11101000],B.2,s_w3
touch16 [%11101000],B.3,s_w4
if s_w1<10000 and s_w2<10000 and s_w3<10000 and s_w4<10000 then return
endif
if s_w1>10000 then inc Touch_Flag
if Touch_Flag=5 then
Touch_Flag=0
endif
endif

if Touch_Flag=2 and s_w2>10000 then  
Bar_Val=Bar_Val+1000 max 33000 min 25000
elseif Touch_Flag=2 and s_w3>10000 then Bar_Val=Bar_Val+100
elseif Touch_Flag=2 and s_w4>10000 then Bar_Val=Bar_Val+10
endif
if Bar_Val>=33000 then
Bar_Val=25000
endif

Kolls=Bar_Val/10                                                            ;massage for display
bintoascii Kolls,b0,b27,b26,b25,b24                        ;massage for decimal point
b27=b27-48
b26=b26-48
b25=b25-48
b24=b24-48

if Touch_Flag=3 and s_w2>10000 then 
Alt_Val=Alt_Val+1000 max 36000
elseif Touch_Flag=3 and s_w3>10000 then Alt_Val=Alt_Val+100
elseif Touch_Flag=3 and s_w4>10000 then Alt_Val=Alt_Val+10
endif
if Alt_Val>=36000 then
Alt_Val=0
endif

if Touch_Flag=4 and Kolls<=2500 then 
Touch_Flag=1
endif

if Touch_Flag=1 then serout A.0,T38400_32,(0xFF,0x7F,0xF8,0x00,0x00,0x06,"     OFF    ","    ",0x00)
elseif Touch_Flag=2 then serout A.0,T38400_32,(0xFF,0x7F,0x00,0x1F,0x00,0x06,"BAR ",#b27,#b26,".",#b25,#b24," inHg  ",0x00)
elseif Touch_Flag=3 then serout A.0,T38400_32,(0xFF,0x7F,0x04,0x00,0x00,0x06,"HOLD? ",#Alt_Val,"       ",0x00)
elseif Touch_Flag=4 then serout A.0,T38400_32,(0xFF,0x7F,0x04,0x00,0x00,0x06,"HOLD  ",#Alt_Val,"       ",0x00)
endif

serout A.0,T38400_32,(0xFF,0x76,0x00,0x01)                    ;bold
serout A.0,T38400_32,(0xFF,0xE4,0x00,0x04,0x00,0x00);cursor mid-screen

goto test

sensor:
do
    hi2cin 0x06,(Data_Reg)                                            ;check for pressure data status
    loop until bit2=1                                                        ;if data flag set,then
    hi2cin 0x01,(Ref_Msb,Ref_Csb,Ref_Lsb)                ;get Msb,Csb,Lsb data
    Ref_Msb=Ref_Msb*256                                                    ;convert Msb to decimal meters
    Ref_Lsb=Ref_Lsb/16**53756/4                                    ;convert LSB to fractional feet
return
 

ThierryP

New Member
Neil

If you use this formula:
  • H = 3626*X^3 + 3394*X^2 + 8371*X
  • where H=height in meters, X=1-x, and x=P/P0. P being the airpressure measured at height H, P0 the airpressure at sea level
as approximation for:
  • h = 44330*(1-(x^(1/5.255))
  • where h=height in meters, x=P/P0
it looks as this approximation is well within 1% up to 7km, and about 3% off at 10km.
Problem may be, the approximated formula is already an approximation in itself of the real formula, and at 10km is already ~10% off.

I have not tried this in Q16.16 on a Picaxe, but this should be straightforward. Maybe I'll give it a try later this weekend.

Thierry
 

nwright

New Member
Thank you for the detailed reply Thierry. Sorry for the delay in responding - few email problems and did not see your post. Have tried your coding and all works well - thank you again for the time & effort

Neil
 
Top