Topic: BMP280 C Python & Perl code

Just instaled a BMP280 with the following code:

// 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.
// BMP280
//
// Compile with gcc -o BMP280  BMP280.c 
//
// This code is designed to work with the BMP280_I2CS I2C Mini Module available from ControlEverything.com.
// https://www.controleverything.com/content/Barometer?sku=BMP280_I2CSs#tabs-0-product_tabset-2

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

void main() 
{
        // 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, BMP280 I2C address is 0x76(108)
        // But you need to ground SDO otherwise its is 0x77!!!
        ioctl(file, I2C_SLAVE, 0x76);

        // Read 24 bytes of data from address(0x88)
        char reg[1] = {0x88};
        write(file, reg, 1);
        char data[24] = {0};
        if(read(file, data, 24) != 24)
        {
                printf("Error : Input/output Error \n");
                exit(1);
        }
        // Convert the data
        // temp coefficents
        int dig_T1 = data[1] * 256 + data[0];
        int dig_T2 = data[3] * 256 + data[2];
        if(dig_T2 > 32767)
        {
                dig_T2 -= 65536;
        }
        int dig_T3 = data[5] * 256 + data[4];
        if(dig_T3 > 32767)
        {
                dig_T3 -= 65536;
        }
        // pressure coefficents
        int dig_P1 = data[7] * 256 + data[6];
        int dig_P2  = data[9] * 256 + data[8];
        if(dig_P2 > 32767)
        {
                dig_P2 -= 65536;
        }
        int dig_P3 = data[11]* 256 + data[10];
        if(dig_P3 > 32767)
        {
                dig_P3 -= 65536;
        }
        int dig_P4 = data[13]* 256 + data[12];
        if(dig_P4 > 32767)
        {
                dig_P4 -= 65536;
        }
        int dig_P5 = data[15]* 256 + data[14];
        if(dig_P5 > 32767)
        {
                dig_P5 -= 65536;
        }
        int dig_P6 = data[17]* 256 + data[16];
        if(dig_P6 > 32767)
        {
                dig_P6 -= 65536;
        }
        int dig_P7 = data[19]* 256 + data[18];
        if(dig_P7 > 32767)
        {
                dig_P7 -= 65536;
        }
        int dig_P8 = data[21]* 256 + data[20];
        if(dig_P8 > 32767)
        {
                dig_P8 -= 65536;
        }
        int dig_P9 = data[23]* 256 + data[22];
        if(dig_P9 > 32767)
        {
                dig_P9 -= 65536;
        }

        // Select control measurement register(0xF4)
        // Normal mode, temp and pressure over sampling rate = 1(0x27)
        char config[2] = {0};
        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);
        sleep(1);

        // 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);
        if(read(file, data, 8) != 8)
        {
                printf("Error : Input/output Error \n");
                exit(1);
        }

        // 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;

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

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

        // Output data to screen
        printf("Press : %.2f hPa \n", pressure);
        printf("Temp in C : %.2f C \n", cTemp);
        printf("Temp in F : %.2f F \n", fTemp);
}

Works like a champ.

For Python try this:

#!/usr/bin/python
#--------------------------------------
#    ___  ___  _ ____
#   / _ \/ _ \(_) __/__  __ __
#  / , _/ ___/ /\ \/ _ \/ // /
# /_/|_/_/  /_/___/ .__/\_, /
#                /_/   /___/
#
#           bme280.py
#  Read data from a digital pressure sensor.
#
#  Official datasheet available from :
#  https://www.bosch-sensortec.com/bst/products/all_products/bme280
#
# Author : Matt Hawkins
# Date   : 25/07/2016
#
# http://www.raspberrypi-spy.co.uk/
#
#--------------------------------------
import smbus
import time
from ctypes import c_short
from ctypes import c_byte
from ctypes import c_ubyte

DEVICE = 0x76 # Default device I2C address


bus = smbus.SMBus(1) # Rev 2 Pi, Pi 2 & Pi 3 uses bus 1
                     # Rev 1 Pi uses bus 0

def getShort(data, index):
  # return two bytes from data as a signed 16-bit value
  return c_short((data[index+1] << 8) + data[index]).value

def getUShort(data, index):
  # return two bytes from data as an unsigned 16-bit value
  return (data[index+1] << 8) + data[index]

def getChar(data,index):
  # return one byte from data as a signed char
  result = data[index]
  if result > 127:
    result -= 256
  return result

def getUChar(data,index):
  # return one byte from data as an unsigned char
  result =  data[index] & 0xFF
  return result

def readBME280ID(addr=DEVICE):
  # Chip ID Register Address
  REG_ID     = 0xD0
  (chip_id, chip_version) = bus.read_i2c_block_data(addr, REG_ID, 2)
  return (chip_id, chip_version)

def readBME280All(addr=DEVICE):
  # Register Addresses
  REG_DATA = 0xF7
  REG_CONTROL = 0xF4
  REG_CONFIG  = 0xF5

  REG_HUM_MSB = 0xFD
  REG_HUM_LSB = 0xFE

  # Oversample setting - page 27
  OVERSAMPLE_TEMP = 2
  OVERSAMPLE_PRES = 2
  MODE = 1

  control = OVERSAMPLE_TEMP<<5 | OVERSAMPLE_PRES<<2 | MODE
  bus.write_byte_data(addr, REG_CONTROL, control)

  # Read blocks of calibration data from EEPROM
  # See Page 22 data sheet
  cal1 = bus.read_i2c_block_data(addr, 0x88, 24)
  cal2 = bus.read_i2c_block_data(addr, 0xA1, 1)
  cal3 = bus.read_i2c_block_data(addr, 0xE1, 7)

  # Convert byte data to word values
  dig_T1 = getUShort(cal1, 0)
  dig_T2 = getShort(cal1, 2)
  dig_T3 = getShort(cal1, 4)

  dig_P1 = getUShort(cal1, 6)
  dig_P2 = getShort(cal1, 8)
  dig_P3 = getShort(cal1, 10)
  dig_P4 = getShort(cal1, 12)
  dig_P5 = getShort(cal1, 14)
  dig_P6 = getShort(cal1, 16)
  dig_P7 = getShort(cal1, 18)
  dig_P8 = getShort(cal1, 20)
  dig_P9 = getShort(cal1, 22)

  dig_H1 = getUChar(cal2, 0)
  dig_H2 = getShort(cal3, 0)
  dig_H3 = getUChar(cal3, 2)

  dig_H4 = getChar(cal3, 3)
  dig_H4 = (dig_H4 << 24) >> 20
  dig_H4 = dig_H4 | (getChar(cal3, 4) & 0x0F)

  dig_H5 = getChar(cal3, 5)
  dig_H5 = (dig_H5 << 24) >> 20
  dig_H5 = dig_H5 | (getUChar(cal3, 4) >> 4 & 0x0F)

  dig_H6 = getChar(cal3, 6)

  # Read temperature/pressure/humidity
  data = bus.read_i2c_block_data(addr, REG_DATA, 8)
  pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
  temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
  hum_raw = (data[6] << 8) | data[7]

  #Refine temperature
  var2 = (((((temp_raw>>4) - (dig_T1)) * ((temp_raw>>4) - (dig_T1))) >> 12) * (dig_T3)) >> 14
  t_fine = var1+var2
  temperature = float(((t_fine * 5) + 128) >> 8);

  # Refine pressure and adjust for temperature
  var1 = t_fine / 2.0 - 64000.0
  var2 = var1 * var1 * dig_P6 / 32768.0
  var2 = var2 + var1 * dig_P5 * 2.0
  var2 = var2 / 4.0 + dig_P4 * 65536.0
  var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0
  var1 = (1.0 + var1 / 32768.0) * dig_P1
  if var1 == 0:
    pressure=0
  else:
    pressure = 1048576.0 - pres_raw
    pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
    var1 = dig_P9 * pressure * pressure / 2147483648.0
    var2 = pressure * dig_P8 / 32768.0
    pressure = pressure + (var1 + var2 + dig_P7) / 16.0

  # Refine humidity
  humidity = t_fine - 76800.0
  humidity = (hum_raw - (dig_H4 * 64.0 + dig_H5 / 16384.8 * humidity)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * humidity * (1.0 + dig_H3 / 67108864.0 * humidity)))
  humidity = humidity * (1.0 - dig_H1 * humidity / 524288.0)
  if humidity > 100:
    humidity = 100
  elif humidity < 0:
    humidity = 0

  return temperature/100.0,pressure/100.0,humidity

def main():

  (chip_id, chip_version) = readBME280ID()
  print "Chip ID     :", chip_id
  print "Version     :", chip_version

  temperature,pressure,humidity = readBME280All()

  print "Temperature :", temperature, "C"
  print "Pressure    :", pressure, "hPa"
  print "Humidity    :", humidity, "%"

if __name__=="__main__":
   main()

However only with the BME280 you will get the humidity of course.

Also found a Perl script but that did not work for me it read out values (constant so thats ok) but wrong on my system.
Maybe thats because of a difference bewteen BMP18 and BMP280? Anyone?

#!/usr/bin/perl

# Copyright 2014 by Jason Seymour
# Rev 1.0
# Rev 1.1 by Jason Miller
# https://github.com/JasonSeymour/rpi-perl-bmp180/blob/master/readBMP180.pl
# https://www.linkedin.com/in/jason-seymour-10513b2
# This is a Perl script to read temperature and barometric
# pressure from an Adafruit BMP180 breakout board.

use Device::SMBus;
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval nanosleep
                    clock_gettime clock_getres clock_nanosleep clock
                    stat );


# Instantiate an SMBus object referring to the correct I2C bus
# and the device address of the sensor. The Adafruit BMP180
# sensor always has an address of 0x77. The rev2 RPi puts the
# primary I2C bus on i2c-1. The rev1 uses i2c-0.

my $bmp180 = Device::SMBus->new(
  I2CBusDevicePath => '/dev/i2c-1',
  I2CDeviceAddress => 0x76,
);

# Use this constant to enable or disable printing diagnostic data

use constant DIAG               => False;

# Define a standard list of operating modes for the sensor.
# These control the number of samples per second that the 
# sensor takes internally.

use constant BMP180_ULTRALOWPOWER       => 0;
use constant BMP180_STANDARD            => 1;
use constant BMP180_HIRES       => 2;
use constant BMP180_ULTRAHIRES          => 3;

# Define the sensor registers. Many of these store calibration data
# which is used to calculate temperature compensated pressure readings.

use constant BMP180_CAL_AC1     => 0xAA;     # Calibration data (16 bit)
use constant BMP180_CAL_AC2     => 0xAC;     # Calibration data (16 bit) 
use constant BMP180_CAL_AC3     => 0xAE;     # Calibration data (16 bit)
use constant BMP180_CAL_AC4     => 0xB0;     # Calibration data (16 bit)
use constant BMP180_CAL_AC5     => 0xB2;     # Calibration data (16 bit)
use constant BMP180_CAL_AC6     => 0xB4;     # Calibration data (16 bit)
use constant BMP180_CAL_B1      => 0xB6;     # Calibration data (16 bit)
use constant BMP180_CAL_B2      => 0xB8;     # Calibration data (16 bit)
use constant BMP180_CAL_MB      => 0xBA;     # Calibration data (16 bit)
use constant BMP180_CAL_MC      => 0xBC;     # Calibration data (16 bit)
use constant BMP180_CAL_MD      => 0xBE;     # Calibration data (16 bit)
use constant BMP180_CONTROL     => 0xF4;
use constant BMP180_TEMPDATA            => 0xF6;
use constant BMP180_PRESSUREDATA        => 0xF6;
use constant BMP180_READTEMPCMD         => 0x2E;
use constant BMP180_READPRESSURECMD     => 0x34;

# Define a list of variables to store the calibration data into.

my $cal_AC1     = 0;
my $cal_AC2     = 0;
my $cal_AC3     = 0;
my $cal_AC4     = 0;
my $cal_AC5     = 0;
my $cal_AC6     = 0;
my $cal_B1      = 0;
my $cal_B2      = 0;
my $cal_MB      = 0;
my $cal_MC      = 0;
my $cal_MD      = 0;

# The Device::SMBus module provides methods for reading 8 and 16 bit values
# from the sensor, but these methods don't differentiate between signed and
# unsigned values. We need to create our own functions to read signed values
# and handle them correctly.

sub readS8 {
        my ($bmp180,$register) = @_;
        my $readVal = $bmp180->readByteData($register);
        if($readVal > 127) {
                $readVal -= 256;
        }
        return $readVal;
}

sub readS16 {
        my ($bmp180,$register) = @_;
        my $readValHi = readS8($bmp180,$register);
        my $readValLo = $bmp180->readByteData($register+1);
        my $bufferHi = $readValHi << 8;
        my $retVal = $bufferHi + $readValLo;
        return $retVal;
}

sub readU16 {
        my ($bmp180,$register) = @_;
        my $readValHi = $bmp180->readByteData($register);
        my $readValLo = $bmp180->readByteData($register+1);
        my $bufferHi = $readValHi << 8;
        my $retVal = $bufferHi + $readValLo;
        return $retVal;
}

# Read the calibration data from the sensor's eeprom and store it locally

$cal_AC1 = readS16($bmp180,BMP180_CAL_AC1);
$cal_AC2 = readS16($bmp180,BMP180_CAL_AC2);
$cal_AC3 = readS16($bmp180,BMP180_CAL_AC3);
$cal_AC4 = readU16($bmp180,BMP180_CAL_AC4);
$cal_AC5 = readU16($bmp180,BMP180_CAL_AC5);
$cal_AC6 = readU16($bmp180,BMP180_CAL_AC6);
$cal_B1  = readS16($bmp180,BMP180_CAL_B1);
$cal_B2  = readS16($bmp180,BMP180_CAL_B2);
$cal_MB  = readS16($bmp180,BMP180_CAL_MB);
$cal_MC  = readS16($bmp180,BMP180_CAL_MC);
$cal_MD  = readS16($bmp180,BMP180_CAL_MD);

# DIAG: Print out the calibration data to validate that we're working up to this point
if(DIAG eq "True") {
        print "Calibration Data\n";
        print "----------------\n";
        printf "AC1 = %6d\n",$cal_AC1;
        printf "AC2 = %6d\n",$cal_AC2;
        printf "AC3 = %6d\n",$cal_AC3;
        printf "AC4 = %6d\n",$cal_AC4;
        printf "AC5 = %6d\n",$cal_AC5;
        printf "AC6 = %6d\n",$cal_AC6;
        printf "B1 = %6d\n",$cal_B1;
        printf "B2 = %6d\n",$cal_B2;
        printf "MB = %6d\n",$cal_MB;
        printf "MC = %6d\n",$cal_MC;
        printf "MD = %6d\n",$cal_MD;
}

# Read the raw (uncompensated) temperature from the sensor

sub readRawTemp {
        my ($bmp180) = @_;
        $bmp180->writeByteData(BMP180_CONTROL,BMP180_READTEMPCMD);
        # usleep takes microseconds, so this is 5 milliseconds
        usleep(5000);
        my $rawTemp = readU16($bmp180,BMP180_TEMPDATA);
        if(DIAG eq "True") {
                my $temp = $rawTemp & 0xFFFF;
                printf "Raw Temp: 0x%x (%d)\n", $temp, $rawTemp;
        }
        return $rawTemp;
}

# Read the raw (uncompensated) barometric pressure from the sensor

sub readRawPressure {
        my ($bmp180,$mode) = @_;
        my $writeVal = BMP180_READPRESSURECMD + ($mode << 6);
        $bmp180->writeByteData(BMP180_CONTROL,$writeVal);
        if ($mode == BMP180_ULTRALOWPOWER) {
                usleep(5000);
        }
        elsif ($mode == BMP180_HIRES) {
                usleep(14000);
        }
        elsif ($mode == BMP180_ULTRAHIRES) {
                usleep(26000);
        }
        else {
                usleep(8000);
        }
        my $msb = $bmp180->readByteData(BMP180_PRESSUREDATA);
        my $lsb = $bmp180->readByteData(BMP180_PRESSUREDATA+1);
        my $xlsb = $bmp180->readByteData(BMP180_PRESSUREDATA+2);
        my $rawPressure = (($msb << 16) + ($lsb << 8) + $xlsb) >> (8 - $mode);
        if(DIAG eq "True") {
                my $press =  $rawPressure & 0xFFFF;
                printf "Raw Pressure value: 0x%X (%d)\n", $press, $rawPressure;
        }
        return $rawPressure;
}

# Read the compensated temperature

sub readTemp {
        my ($bmp180) = @_;
        my $UT = 0;
        my $X1 = 0;
        my $X2 = 0;
        my $B5 = 0;
        my $temp = 0.0;

        $UT = readRawTemp($bmp180);
        use integer;
        $X1 = (($UT - $cal_AC6) * $cal_AC5) >> 15;
        $X2 = ($cal_MC << 11) / ($X1 + $cal_MD);
        $B5 = $X1 + $X2;
        no integer;
        $temp = (($B5 + 8) >> 4) / 10.0;
        return $temp;
}

# Read the compensated barometric pressure

sub readPressure {
        my ($bmp180,$mode) = @_;
        my $UT = readRawTemp($bmp180);
        my $UP = readRawPressure($bmp180,$mode);

        # Calculate true temperature, but don't convert to simple output format yet
        use integer;
        my $X1 = (($UT - $cal_AC6) * $cal_AC5) >> 15;
        my $X2 = ($cal_MC << 11) / ($X1 + $cal_MD);
        my $B5 = $X1 + $X2;
        no integer;
        my $temp = (($B5 + 8) >> 4) / 10.0;

        # Calculate compensated pressure
        use integer;
        my $B6 = $B5 - 4000;
        #printf "B6 = $B6\n";
        my $X1 = ($cal_B2 * ($B6 * $B6) >> 12) >> 11;
        #printf "X1 = $X1\n";
        my $X2 = ($cal_AC2 * $B6) >> 11;
        my $X3 = $X1 + $X2;
        my $B3 = ((($cal_AC1 * 4 + $X3) << $mode) + 2) /4;
        $X1 = ($cal_AC3 * $B6) >> 13;
        $X2 = ($cal_B1 * (($B6 * $B6)) >> 12 ) >> 16;
        $X3 = (($X1 + $X2) + 2) >> 2;
        my $B4 = ($cal_AC4 * ($X3 + 32768)) >> 15;
        my $B7 = ($UP - $B3) * (50000 >> $mode);
        my $p = 0;
        if ($B7 < 0x80000000) {
                $p = ($B7 * 2) / $B4;
        } else {
                $p = ($B7 / $B4) * 2;
        }
        $X1 = ($p >> 8) * ($p >> 8);
        $X1 = ($X1 * 3038) >> 16;
        $X2 = (-7357 * $p) >> 16;
        $p = $p + (($X1 + $X2 + 3791) >> 4);
        #printf "Calibration pressure is %d Pa\n", $p;
        return $p;
}

# Print the raw uncorrected temperature
#my $RawTemp = readRawTemp($bmp180);

# Call the compensated temp function to print temperature
#my $Temperature = readTemp($bmp180);



# Call the compensated pressure function to print barometric pressure
my $Temperature = readTemp($bmp180);
my $Pressure = readPressure($bmp180,BMP180_STANDARD) / 100.0;

my $TemperatureF = (($Temperature * 9) / 5) + 32;

printf "Temperature: %.2f C\n", $Temperature;
printf "Temperature: %.2f F\n", $TemperatureF;
printf "Pressure: %.2f hPa\n", $Pressure;

Temperature: 82.70 C
Temperature: 180.86 F
Pressure: -43852.45 hPa

Hmmmm....

But interesting using perl with SMBUS..

Chrs