PiicoDev Atmospheric Sensor BME280 Air Density Algorithm

I got my board working as intended. Very nice! What I’d really like is to be able to estimate Air Density (including Humidity). The Calculation is rather complex. Before I dive into it does anyone have a suitable Algorithm written in Micro Python?

2 Likes

Hi Ray,

No, but I am interested to learn about the Calculation.

Murray

2 Likes

Welcome Ray, and hey Murray,

I stumbled across this online calculator that has some steps attached to it and some light reading. Air Density Calculator | What is Air's Density?

All of the variables can be populated by the outputs of the BME280.

Also stumbled across this module though cant confirm it works in Micropython: Initialising a class and class method for calculating the density of humid air in Python - Stack Overflow

Thanks Liam,

I have it sorted and I’m coding it now.

All the references seem to come back to that eighth order algebraic algorithm by Herman Wobus in your second link. It does look far less frightening coded in Python :slight_smile:

More later.

Ray

2 Likes

Hi Ray et al,
I have coded it up on a Pi 3 with the the PiicoDev BME280 sensor working.
Needed to fix some typos in the original code.

( havent yet tried this on a Pico …)

class Density:

    def __init__(self, Pressure_hPa, Temp_C, RelativeHumidity):
        self.Pressure_hPa = Pressure_hPa
        self.Temp_C = Temp_C
        self.RelativeHumidity = RelativeHumidity

    def Pressure_Pa(self):
        return self.Pressure_hPa*100

    def Temp_K(self):
        return self.Temp_C+273.15

    def Es_Pa(self):
        Eso = 6.1078
        c0 = 0.99999683
        c1 = -0.90826951*10**-2
        c2 = 0.78736169*10**-4
        c3 = -0.61117958*10**-6
        c4 = 0.43884187*10**-8
        c5 = -0.29883885*10**-10
        c6 = 0.21874425*10**-12
        c7 = -0.17892321*10**-14
        c8 = 0.11112018*10**-16
        c9 = -0.30994571*10**-19
        p = c0+self.Temp_C*(c1+self.Temp_C*(c2+self.Temp_C*(c3+self.Temp_C*(c4+self.Temp_C*(c5+self.Temp_C*(c6+self.Temp_C*(c7+self.Temp_C*(c8+self.Temp_C*(c9)))))))))
        Es_hPa = Eso/(p**8)
        return Es_hPa*100

    def PartialPressureWaterVapour_Pa(self):
        return self.Es_Pa() * self.RelativeHumidity

    def PartialPressureDryAir_Pa(self):
        return self.Pressure_Pa() - self.PartialPressureWaterVapour_Pa()

    def DensityHumidAir(self):
        SpecificGasConstantDryAir = 287.0531
        SpecificGasConstantWaterVapour = 461.4964
        return self.PartialPressureDryAir_Pa() / (SpecificGasConstantDryAir*self.Temp_K())+self.PartialPressureWaterVapour_Pa() / (SpecificGasConstantWaterVapour*self.Temp_K())

and the main.py code

from Density import Density
from PiicoDev_BME280 import PiicoDev_BME280

sensor = PiicoDev_BME280() # initialise the sensor

tempC, presPa, humRH = sensor.values() # read all data from the sensor
pres_hPa = presPa / 100 # convert air pressure Pascals -> hPa (or mbar, if you prefer)

AmbientAir = Density(1029, 15, 0.5)
print("demo run")
print("15 °C  1029 hPa  50 %RH")
print("Air Density = %2.3f kg/m^3" % (AmbientAir.DensityHumidAir()))

print("")

AmbientAir = Density(pres_hPa, tempC, humRH/100)
print("run with BME280 data")
print("%3.2f °C  %4.2f hPa  %3.2f %%RH" % (tempC, pres_hPa, humRH))
print("Air Density = %2.3f kg/m^3" % (AmbientAir.DensityHumidAir()))

with the results today at 11:40 in Melbourne (under dirzzly rain)

>>> %Run main.py
demo run
15 °C  1029 hPa  50 %RH
Air Density = 1.240 kg/m^3

run with BME280 data
21.71 °C  1002.21 hPa  55.08 %RH
Air Density = 1.178 kg/m^3
>>> 

The demo run aligns wit the result from the calculator on the page Liam found.

cheers
Murray

2 Likes

Me too, I have it running on a pico. Just need to check it out with some real independent data.

Ray

1 Like

Hi Ray

A couple more runs about 10 minutes apart (test pass removed)

>>> %Run main.py

run with BME280 data
21.87 °C  1002.04 hPa  53.78 %RH
Air Density = 1.177 kg/m^3

>>> %Run main.py

run with BME280 data
21.23 °C  1001.78 hPa  59.04 %RH
Air Density = 1.179 kg/m^3

The values agree with the online calculator so I am giving the code a reasonably high degree of confidence.

Murray

2 Likes

I have added the Dewpoint function to the Density class also

 # math.log(x[, base]) With one argument, return the natural logarithm of x (to base e). 
from math import log 

    def Dewpoint(self):
        alpha = log(self.RelativeHumidity) + 17.62 * self.Temp_C / (243.12 + self.Temp_C)
        return 243.12 * alpha / (17.62 - alpha)

and a run gives this (checked with online calc)

run with BME280 data
19.70 °C  999.20 hPa  60.79 %RH
Air Density = 1.18235 kg/m^3
Dewpoint    = 11.91188 C

Murray

2 Likes

And me being a C programmer - I couldn’t resist … here it is in C. I am building this as a future module for a compiled library, hence the static declarations for functions that are used internally.

The callable functions are:

  • pressure_pa( p ) with the parameter in hPa - returns pressure in Pascals
  • temp_k( t ) with the parameter in Celsius - returns temperature in Kelvin
  • density( p, t, rh ) with pressure in hPa, temperature in Celsius, and Rh in range 0-1.0 - returns moist air density in kg/cu.m
  • dewpoint( t, rh ) with temperature in Celsius, and Rh in range 0-1.0 - returns dewpoint in Celsius
/*
Air Density calculator based on the code from here
https://stackoverflow.com/questions/42874528/initialising-a-class-and-class-method-for-calculating-the-density-of-humid-air-i
converted to C

A test run
int main()
{
        double x;

        x = densityhumidair(1029, 15, 0.5);
        printf("Air Density = %2.3f kg/m^3\n", x);

        x = dewpoint(15, 0.5);
        printf("Dewpoint = %2.3f C\n", x);

        return 0;
}

returns the same results as this calculator

Air Density = 1.240 kg/m^3
Dewpoint = 4.652 C

https://www.omnicalculator.com/physics/air-density

Murray T 20221014
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>       /* pow(3), log(3) */

#define test        /* if defined, include test main() fn */

double
pressure_pa(double p)
{
	return(p*100.0);
}

double
temp_k(double t)
{
        return(t + 273.15);
}

static double
es_pa(double t)
{
        double p;
        double es_hpa;
        double eso = 6.1078;
        double c0 =  0.99999683;
        double c1 = -0.90826951e-2;     /* -0.90826951*10**-2; */
        double c2 =  0.78736169e-4;     /*  0.78736169*10**-4; */
        double c3 = -0.61117958e-6;     /* -0.61117958*10**-6; */
        double c4 =  0.43884187e-8;     /*  0.43884187*10**-8; */
        double c5 = -0.29883885e-10;    /* -0.29883885*10**-10; */
        double c6 =  0.21874425e-12;    /*  0.21874425*10**-12; */
        double c7 = -0.17892321e-14;    /* -0.17892321*10**-14; */
        double c8 =  0.11112018e-16;    /*  0.11112018*10**-16; */
        double c9 = -0.30994571e-19;    /* -0.30994571*10**-19; */

        p = c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * (c6 + t * (c7 + t * (c8 + t * (c9)))))))));

       es_hpa = eso/(pow(p,8));
       return (es_hpa * 100);
}

static double
partialpressurewatervapour_pa(double t, double rh)
{
        return(es_pa(t) * rh);
}

static double
partialpressuredryair_pa(double p, double t, double rh)
{
        return(pressure_pa(p) - partialpressurewatervapour_pa(t, rh));
}

double
densityhumidair(double p, double t, double rh)
{
        double SpecificGasConstantDryAir = 287.0531;
        double SpecificGasConstantWaterVapour = 461.4964;

        return(partialpressuredryair_pa(p, t, rh) / (SpecificGasConstantDryAir * temp_k(t)) +
               partialpressurewatervapour_pa(t, rh) / (SpecificGasConstantWaterVapour * temp_k(t)));
}

double
dewpoint(double t, double rh)
{
        double alpha = log(rh) + 17.62 * t / (243.12 + t);
        return(243.12 * alpha / (17.62 - alpha));
}

#ifdef test
int main()
{
        double x;

        x = densityhumidair(1029, 15, 0.5);
        printf("Air Density = %2.3f kg/m^3\n", x);

        x = dewpoint(15, 0.5);
        printf("Dewpoint = %2.3f C\n", x);

        return 0;
}
#endif

Make sure that the math library is linked in the compilation

cc -o density density.c -lm

and a test run returns the correct answers as compared with the online calculator (NOTE: the test code is placed in the last part of the library file at this time. Normally the density library would be linked to a separate main program.)

pi@raspberrypi:~/weather2/weather-station/density $ ./density 
Air Density = 1.240 kg/m^3
Dewpoint = 4.652 C
pi@raspberrypi:~/weather2/weather-station/density $ 

Murray

1 Like

Added a small function to allow Relative Humidity to be entered as either in the range 0 - 1.0 OR as a value 1 < rh <= 100

added function

static double
scale_rh(double r)
{
        if ( r > 1.0 )
               r /= 100.0;
        return(r);
}

and the updates to the two affected functions

Checked in test run, results match with rh values of 0.5 OR 50. Still to do edge tests at 1.0 etc and might create versions that require the user to call the correct function if I allow RH percentage values < 1.0% i.e. Rh = 0.9% (that is REALLY low but might be needed).
So there would then be functions like these:

  • densityhumidair_1(double p, double t, double rh) for rh in range 0 - 1.0 i.e. already scaled
  • densityhumidair_100(double p, double t, double rh) for rh in the full percentage range of 0.0 - 100.0%

The real world can be a PITA at times…
(And of course, real error checking into a production library …)

Murray

1 Like

Murray, wow you’ve certainly aced my modest efforts. It may be quite awhile before I could ever match your fluency with both C++ and Micro Python. My rather crude and inelegant effort seems to give results the same as those from the online calculators to the third decimal place. That’s good enough for my purposes. My interest is to map the changes in Air Density, Pressure, Temperature and Humidity over a hot Summer day at Terrey Hills in Sydney. I plan on taking a reading every ten minutes on the Pico and read it into an opened/closed text file in RAM. I intend using the Pico and the sensor in stand alone mode with simply a power switch to initialize the device.

Thanks again for your enthusiasm.

Ray

1 Like

Thanks Ray,
It’s not a challenge, but this exchange of ideas is what we are here for. It was you who started this, then Liam found reference sites etc …

You are building a particular project, and your code is doing what you need to this point. Done deal.

I am aiming for a different concept and am happy to show my direction(s). Not done deal yet at all - this is step 3 (I think) of many…

Hack away :exploding_head:

Murray

2 Likes