ORANGE PI and BME280 SENSOR

The BME280 is an integrated environmental sensor for pressure, humidity and temperature, combining high linearity, high accuracy and low power consumption. The humidity sensor features an extremely fast response time which supports performance requirements for emerging applications such as context awareness, and high accuracy over a wide temperature range. The pressure sensor is an absolute barometric pressure sensor with features exceptionally high accuracy and resolution at very low noise. The integrated temperature sensor has been optimized for very low noise and high resolution. It is primarily used for temperature compensation of the pressure and humidity sensors, and can also be used for estimating ambient temperature.

Wiring the Sensor

The BME280 supports the I²C and SPI digital interfaces; it acts as a slave for both protocols.
The I²C interface supports the Standard, Fast and High Speed modes. The SPI interface supports both SPI mode ‘00’ (CPOL = CPHA = ‘0’) and mode ‘11’ (CPOL = CPHA = ‘1’) in 4-wire and 3-wire configuration.

  • The I²C interface uses the following pins:
  • SCK: serial clock (SCL)
  • SDI: data (SDA)
  • SDO: Slave address LSB (GND = ‘0’, VDDIO = ‘1’)

Connecting SDO to GND results in slave address 1110110 (0x76); connecting it to VDDIO results in slave address 1110111 (0x77), which is the same as BMP280’s I²C address. The SDO pin cannot be left floating; if left floating, the I²C address will be undefined.

BME280_P Program

// Distributed with a free-will license.
// Use it any way you want, profit or free, provided it fits in the licenses of its associated works.
// BME280
// This code is designed to work with the BME280 sensor with I2C Interface

#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <getopt.h>
#include <unistd.h>

//void main()
//{

void print_usage() {
    printf("Usage: bme280_p -[ahCFH] -P[factor]\n");
    printf("Usage: bme280_p -a all parms verbose\n");
    printf("Usage: bme280_p -C Temp value in Celsius\n");
    printf("Usage: bme280_p -F Temp value in Fahrenheit\n");
    printf("Usage: bme280_p -H Humidity value in %%RH\n");
    printf("Usage: bme280_p -P[factor] Pressure value in hPa * factor\n");
}

int main(int argc, char *argv[]) {
    int option = 0;
      float fact = 1.0;
      float fpressure;

        // Create I2C bus
        int file;
        char *bus = "/dev/i2c-1";
        if((file = open(bus, O_RDWR)) < 0) 
        {
                printf("Failed to open the bus. \n");
                exit(1);
        }
        // Get I2C device, BME280 I2C address is 0x76(136)
        ioctl(file, I2C_SLAVE, 0x76);

        // Read 24 bytes of data from register(0x88)
        char reg[1] = {0x88};
        write(file, reg, 1);
        char b1[24] = {0};
        if(read(file, b1, 24) != 24)
        {
                printf("Error : Input/Output error \n");
                exit(1);
        }

        // Convert the data
        // temp coefficents
        int dig_T1 = (b1[0] + b1[1] * 256);
        int dig_T2 = (b1[2] + b1[3] * 256);
        if(dig_T2 > 32767)
        {
                dig_T2 -= 65536;
        }
        int dig_T3 = (b1[4] + b1[5] * 256);
        if(dig_T3 > 32767)
        {
                dig_T3 -= 65536;
        }

        // pressure coefficents
        int dig_P1 = (b1[6] + b1[7] * 256);
        int dig_P2 = (b1[8] + b1[9] * 256);
        if(dig_P2 > 32767)
        {
                dig_P2 -= 65536;
        }
        int dig_P3 = (b1[10] + b1[11] * 256);
        if(dig_P3 > 32767)
        {
                dig_P3 -= 65536;
        }
        int dig_P4 = (b1[12] + b1[13] * 256);
        if(dig_P4 > 32767)
        {
                dig_P4 -= 65536;
        }
        int dig_P5 = (b1[14] + b1[15] * 256);
        if(dig_P5 > 32767)
        {
        dig_P5 -= 65536;
        }
        int dig_P6 = (b1[16] + b1[17] * 256);
        if(dig_P6 > 32767)
        {
                dig_P6 -= 65536;
        }
        int dig_P7 = (b1[18] + b1[19] * 256);
        if(dig_P7 > 32767)
        {
                dig_P7 -= 65536;
        }
        int dig_P8 = (b1[20] + b1[21] * 256);
        if(dig_P8 > 32767)
        {
        dig_P8 -= 65536;
        }
        int dig_P9 = (b1[22] + b1[23] * 256);
        if(dig_P9 > 32767)
        {
                dig_P9 -= 65536;
        }

        // Read 1 byte of data from register(0xA1)
        reg[0] = 0xA1;
        write(file, reg, 1);
        char data[8] = {0};
        read(file, data, 1);
        int dig_H1 = data[0];

        // Read 7 bytes of data from register(0xE1)
        reg[0] = 0xE1;
        write(file, reg, 1);
        read(file, b1, 7);

        // Convert the data
        // humidity coefficents
        int dig_H2 = (b1[0] + b1[1] * 256);
        if(dig_H2 > 32767)
        {
                dig_H2 -= 65536;
        }
        int dig_H3 = b1[2] & 0xFF ;
        int dig_H4 = (b1[3] * 16 + (b1[4] & 0xF));
        if(dig_H4 > 32767)
        {
                dig_H4 -= 65536;
        }
        int dig_H5 = (b1[4] / 16) + (b1[5] * 16);
        if(dig_H5 > 32767)
        {
                dig_H5 -= 65536;
        }
        int dig_H6 = b1[6];
        if(dig_H6 > 127)
        {
                dig_H6 -= 256;
        }

        // Select control humidity register(0xF2)
        // Humidity over sampling rate = 1(0x01)
        char config[2] = {0};
        config[0] = 0xF2;
        config[1] = 0x01;
        write(file, config, 2);
        // Select control measurement register(0xF4)
        // Normal mode, temp and pressure over sampling rate = 1(0x27)
        config[0] = 0xF4;
        config[1] = 0x27;
        write(file, config, 2);
        // Select config register(0xF5)
        // Stand_by time = 1000 ms(0xA0)
        config[0] = 0xF5;
        config[1] = 0xA0;
        write(file, config, 2);

        // Read 8 bytes of data from register(0xF7)
        // pressure msb1, pressure msb, pressure lsb, temp msb1, temp msb, temp lsb, humidity lsb, humidity msb
        reg[0] = 0xF7;
        write(file, reg, 1);
        read(file, data, 8);

        // Convert pressure and temperature data to 19-bits
        long adc_p = ((long)(data[0] * 65536 + ((long)(data[1] * 256) + (long)(data[2] & 0xF0)))) / 16;
        long adc_t = ((long)(data[3] * 65536 + ((long)(data[4] * 256) + (long)(data[5] & 0xF0)))) / 16;
        // Convert the humidity data
        long adc_h = (data[6] * 256 + data[7]);

        // Temperature offset calculations
        float var1 = (((float)adc_t) / 16384.0 - ((float)dig_T1) / 1024.0) * ((float)dig_T2);
        float var2 = ((((float)adc_t) / 131072.0 - ((float)dig_T1) / 8192.0) *
                                        (((float)adc_t)/131072.0 - ((float)dig_T1)/8192.0)) * ((float)dig_T3);
        float t_fine = (long)(var1 + var2);
        float cTemp = (var1 + var2) / 5120.0;
        float fTemp = cTemp * 1.8 + 32;

        // Pressure offset calculations
        var1 = ((float)t_fine / 2.0) - 64000.0;
        var2 = var1 * var1 * ((float)dig_P6) / 32768.0;
        var2 = var2 + var1 * ((float)dig_P5) * 2.0;
        var2 = (var2 / 4.0) + (((float)dig_P4) * 65536.0);
        var1 = (((float) dig_P3) * var1 * var1 / 524288.0 + ((float) dig_P2) * var1) / 524288.0;
        var1 = (1.0 + var1 / 32768.0) * ((float)dig_P1);
        float p = 1048576.0 - (float)adc_p;
        p = (p - (var2 / 4096.0)) * 6250.0 / var1;
        var1 = ((float) dig_P9) * p * p / 2147483648.0;
        var2 = p * ((float) dig_P8) / 32768.0;
        float pressure = (p + (var1 + var2 + ((float)dig_P7)) / 16.0) / 100;

        // Humidity offset calculations
        float var_H = (((float)t_fine) - 76800.0);
        var_H = (adc_h - (dig_H4 * 64.0 + dig_H5 / 16384.0 * var_H)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * var_H * (1.0 + dig_H3 / 67108864.0 * var_H)));
        float humidity = var_H * (1.0 -  dig_H1 * var_H / 524288.0);
        if(humidity > 100.0)
        {
                humidity = 100.0;
        }else
        if(humidity < 0.0) 
        {
                humidity = 0.0;
        }

    while ((option = getopt(argc, argv,"ahCFHP::")) != -1) {
        switch (option) {
             case 'a' : 
                 printf("Temperature in Celsius : %.2f C \n", cTemp);
                 printf("Temperature in Fahrenheit : %.2f F \n", fTemp);
                 printf("Pressure : %.2f hPa \n", pressure);
                 printf("Relative Humidity : %.2f %%RH \n", humidity);
                 close(file);
                 exit(0);

             case 'C' : 
                 printf("%.2f \n", cTemp);
                 close(file);
                 exit(0);

             case 'P' :
                 if (optarg != NULL )
                 fact = atof(optarg);
                 else
                 fact = 1;
                 fpressure = fact * pressure;
                 if (fact < 1)
                   printf("%.4f \n", fpressure);
                  else
                   printf("%.2f \n", fpressure);
                 close(file);
                 exit(0);

             case 'H' : 
                 printf("%.2f \n", humidity);
                 close(file);
                 exit(0);

             case 'F' : 
                 printf("%.2f \n", fTemp);
                 close(file);
                 exit(0);

             case 'h' : 
                 print_usage();
                 close(file);
                 exit(0);

             case '?' :
             case ':' :
             default: print_usage(); 
                 exit(EXIT_FAILURE);
        }
    }
        print_usage();
        close(file);
}

Compiling the Program

gcc  -g -Wall -Wextra -pedantic -std=c11 -o bme280_p bme280_p.c

Running the Program

./bme280_p 
Usage: bme280_p -[ahCFH] -P[factor]
Usage: bme280_p -a all parms verbose
Usage: bme280_p -C Temp value in Celsius
Usage: bme280_p -F Temp value in Fahrenheit
Usage: bme280_p -H Humidity value in %RH
Usage: bme280_p -P[factor] Pressure value in hPa * factor

./bme280_p -a
Temperature in Celsius : 22.29 C 
Temperature in Fahrenheit : 72.13 F 
Pressure : 1009.53 hPa 
Relative Humidity : 43.52 %RH 

./bme280_p -C
22.30 

 ./bme280_p -F
72.14 

./bme280_p -H
43.53

./bme280_p -P
1009.52

./bme280_p -P1
1009.55

./bme280_p -P10
10095.85

./bme280_p -P0.01
10.0960

8 thoughts on “ORANGE PI and BME280 SENSOR”

  1. Another article that directly applies to my interest area! I have already successfully dabbled with BME280 on ESP8266/NodeMCU, and would like to try it on a ‘Pi’ device of some sort (I also have a C.H.I.P., but future supplies of that great chip are vague). Here, you wrote a ‘C’ program to access the BME280, but what about the various Python libraries that are available? Thanks!

    1. Python is ok for quick & easy works but it brings along the interpreter and many other libraries.
      I prefer to use C to have a single executable.

  2. Thank you for this post. Works like a charm for OPIzero almost from the box. All I needed to do was to change :
    char *bus = “/dev/i2c-1”;
    to
    char *bus = “/dev/i2c-0”;
    because I use different pins.

  3. Also works on OPI zero 2+ H5.

    Do not forget to enable the related overlay.

    But how can I use it directly from a regular user ? I need to be root to run the program.
    Thank you for the good work.

    1. Probably the best option to use the sensor from an unprivileged user is to create a deamon, which I’m thinking of developing also for other purposes.
      The “sudo” option shall work as well as a containerized option. In any case I do suggest to create a shell script (running privileged) that logs the results in a log file. Then an unprivileged user can gather data from the log file.

      1. Actually I installed “i2c-tools” and then I added “myuser” to i2c group.
        Quite easy.

        Your code is pure gold.

        Thank you.

        1. Oups, after a few days my BME280 stopped working. I firstly suspected BME burned, so I swap with another but I still have this error : Error : Input/Output error .

          Do you think it could be a hardware problem on the Orange Pi ?
          Thank you

          1. Check with “i2cdetect” the address of the bme280.
            If it doesn’t show nor 0x76 neither 0x77, suspect a loose cabling or hardware problem.
            If the address changes unexpectedly or has changed then make sure the (SPI) SDO line on the bme280 board is tied to ground for I2C address 0x76 and 3V3 for address 0x77.
            It should never been left floating.

Comments are closed.