01 May 2018

Raspberry PiでI2C気温・気圧センサーBMP085を利用

Raspberry PiでI2C気温・気圧センサーBMP085を使う時の各言語でのソフトウエア。

C言語

サンプル・ソースコードが『 john.geek.nz : Reading data from a Bosch BMP085 with a Raspberry Pi 』に公開されている。

このソースコードのビルドに必要なsmbus.cは『Hackage :: [Package] hsI2C-0.1.3』 からダウンロードする。

ソースコードを次のように(赤字部分で示すように)少し改変して、コンパイルする。

gcc testBMP085.c smbus.c -lm -o bmp085

改変したソースコードをダウンロードする

testBMP085.c [download site]
/*
Raspberry Pi Bosch BMP085 communication code.
By:      John Burns (www.john.geek.nz)
Date:    01 August 2012
License: CC BY-SA v3.0 - http://creativecommons.org/licenses/by-sa/3.0/
*/
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include "smbus.h" 
 
#define BMP085_I2C_ADDRESS 0x77
 
const unsigned char BMP085_OVERSAMPLING_SETTING = 3;
 
// Calibration values - These are stored in the BMP085
short int ac1;
short int ac2; 
short int ac3; 
unsigned short int ac4;
unsigned short int ac5;
unsigned short int ac6;
short int b1; 
short int b2;
short int mb;
short int mc;
short int md;
 
int b5; 
 
unsigned int temperature, pressure;
 
// Open a connection to the bmp085
// Returns a file id
int bmp085_i2c_Begin()
{
    int fd;
    char *fileName = "/dev/i2c-1";
    
    // Open port for reading and writing
    if ((fd = open(fileName, O_RDWR)) < 0)
        exit(1);
    
    // Set the port options and set the address of the device
    if (ioctl(fd, I2C_SLAVE, BMP085_I2C_ADDRESS) < 0) {                  
        close(fd);
        exit(1);
    }
 
    return fd;
}
 
// Read two words from the BMP085 and supply it as a 16 bit integer
__s32 bmp085_i2c_Read_Int(int fd, __u8 address)
{
    __s32 res = i2c_smbus_read_word_data(fd, address);
    if (res < 0) {
        close(fd);
        exit(1);
    }
 
    // Convert result to 16 bits and swap bytes
    res = ((res<<8) & 0xFF00) | ((res>>8) & 0xFF);
 
    return res;
}
 
//Write a byte to the BMP085
void bmp085_i2c_Write_Byte(int fd, __u8 address, __u8 value)
{
    if (i2c_smbus_write_byte_data(fd, address, value) < 0) {
        close(fd);
        exit(1);
    }
}
 
// Read a block of data BMP085
void bmp085_i2c_Read_Block(int fd, __u8 address, __u8 length, __u8 *values)
{
    if(i2c_smbus_read_i2c_block_data(fd, address,length,values)<0) {
        close(fd);
        exit(1);
    }
}
 
void bmp085_Calibration()
{
    int fd = bmp085_i2c_Begin();
    ac1 = bmp085_i2c_Read_Int(fd,0xAA);
    ac2 = bmp085_i2c_Read_Int(fd,0xAC);
    ac3 = bmp085_i2c_Read_Int(fd,0xAE);
    ac4 = bmp085_i2c_Read_Int(fd,0xB0);
    ac5 = bmp085_i2c_Read_Int(fd,0xB2);
    ac6 = bmp085_i2c_Read_Int(fd,0xB4);
    b1 = bmp085_i2c_Read_Int(fd,0xB6);
    b2 = bmp085_i2c_Read_Int(fd,0xB8);
    mb = bmp085_i2c_Read_Int(fd,0xBA);
    mc = bmp085_i2c_Read_Int(fd,0xBC);
    md = bmp085_i2c_Read_Int(fd,0xBE);
    close(fd);
}
 
// Read the uncompensated temperature value
unsigned int bmp085_ReadUT()
{
    unsigned int ut = 0;
    int fd = bmp085_i2c_Begin();
 
    // Write 0x2E into Register 0xF4
    // This requests a temperature reading
    bmp085_i2c_Write_Byte(fd,0xF4,0x2E);
    
    // Wait at least 4.5ms
    usleep(5000);
 
    // Read the two byte result from address 0xF6
    ut = bmp085_i2c_Read_Int(fd,0xF6);
 
    // Close the i2c file
    close (fd);
    
    return ut;
}
 
// Read the uncompensated pressure value
unsigned int bmp085_ReadUP()
{
    unsigned int up = 0;
    int fd = bmp085_i2c_Begin();
 
    // Write 0x34+(BMP085_OVERSAMPLING_SETTING<<6) into register 0xF4
    // Request a pressure reading w/ oversampling setting
    bmp085_i2c_Write_Byte(fd,0xF4,0x34 + (BMP085_OVERSAMPLING_SETTING<<6));
 
    // Wait for conversion, delay time dependent on oversampling setting
    usleep((2 + (3<<BMP085_OVERSAMPLING_SETTING)) * 1000);
 
    // Read the three byte result from 0xF6
    // 0xF6 = MSB, 0xF7 = LSB and 0xF8 = XLSB
    __u8 values[3];
    bmp085_i2c_Read_Block(fd, 0xF6, 3, values);
 
    up = (((unsigned int) values[0] << 16) | ((unsigned int) values[1] << 8) | (unsigned int) values[2]) >> (8-BMP085_OVERSAMPLING_SETTING);
 
    return up;
}
 
// Calculate pressure given uncalibrated pressure
// Value returned will be in units of XXXXX
unsigned int bmp085_GetPressure(unsigned int up)
{
    int x1, x2, x3, b3, b6, p;
    unsigned int b4, b7;
  
    b6 = b5 - 4000;
    // Calculate B3
    x1 = (b2 * (b6 * b6)>>12)>>11;
    x2 = (ac2 * b6)>>11;
    x3 = x1 + x2;
    b3 = (((((int)ac1)*4 + x3)<<BMP085_OVERSAMPLING_SETTING) + 2)>>2;
  
    // Calculate B4
    x1 = (ac3 * b6)>>13;
    x2 = (b1 * ((b6 * b6)>>12))>>16;
    x3 = ((x1 + x2) + 2)>>2;
    b4 = (ac4 * (unsigned int)(x3 + 32768))>>15;
  
    b7 = ((unsigned int)(up - b3) * (50000>>BMP085_OVERSAMPLING_SETTING));
    if (b7 < 0x80000000)
        p = (b7<<1)/b4;
    else
        p = (b7/b4)<<1;
    
    x1 = (p>>8) * (p>>8);
    x1 = (x1 * 3038)>>16;
    x2 = (-7357 * p)>>16;
    p += (x1 + x2 + 3791)>>4;
  
    return p;
}
 
// Calculate temperature given uncalibrated temperature
// Value returned will be in units of 0.1 deg C
unsigned int bmp085_GetTemperature(unsigned int ut)
{
    int x1, x2;
  
    x1 = (((int)ut - (int)ac6)*(int)ac5) >> 15;
    x2 = ((int)mc << 11)/(x1 + md);
    b5 = x1 + x2;
 
    unsigned int result = ((b5 + 8)>>4);  
 
    return result;
}
 
int main(int argc, char **argv)
{
    bmp085_Calibration();
    temperature = bmp085_GetTemperature(bmp085_ReadUT());
    pressure = bmp085_GetPressure(bmp085_ReadUP());
    
    printf("Temperature\t%0.1f deg-C\n", ((double)temperature)/10);
    printf("Pressure\t%0.2f hPa\n", ((double)pressure)/100);
    return 0;
}

testBMP085.cで使われているsmbusライブラリのソースコードは次の通り。こちらも、赤字の部分は改変している。

smbus.c [download site]
/*
    smbus.c - SMBus level access helper functions
 
    Copyright (C) 1995-1997  Simon G. Vogl
    Copyright (C) 1998-1999  Frodo Looijaard <frodol@dds.nl>
    Copyright (C) 2012-2013  Jean Delvare <jdelvare@suse.de>
 */
#include <errno.h>
#include <stddef.h>
#include "smbus.h"
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
 
/* Compatibility defines */
#ifndef I2C_SMBUS_I2C_BLOCK_BROKEN
#define I2C_SMBUS_I2C_BLOCK_BROKEN I2C_SMBUS_I2C_BLOCK_DATA
#endif
#ifndef I2C_FUNC_SMBUS_PEC
#define I2C_FUNC_SMBUS_PEC I2C_FUNC_SMBUS_HWPEC_CALC
#endif
 
__s32 i2c_smbus_access(int file, char read_write, __u8 command,
               int size, union i2c_smbus_data *data)
{
    struct i2c_smbus_ioctl_data args;
    __s32 err;
 
    args.read_write = read_write;
    args.command = command;
    args.size = size;
    args.data = data;
 
    err = ioctl(file, I2C_SMBUS, &args);
    if (err == -1)
        err = -errno;
    return err;
}
 
__s32 i2c_smbus_write_quick(int file, __u8 value)
{
    return i2c_smbus_access(file, value, 0, I2C_SMBUS_QUICK, NULL);
}
 
__s32 i2c_smbus_read_byte(int file)
{
    union i2c_smbus_data data;
    int err;
 
    err = i2c_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
    if (err < 0)
        return err;
 
    return 0x0FF & data.byte;
}
 
__s32 i2c_smbus_write_byte(int file, __u8 value)
{
    return i2c_smbus_access(file, I2C_SMBUS_WRITE, value,
                I2C_SMBUS_BYTE, NULL);
}
 
__s32 i2c_smbus_read_byte_data(int file, __u8 command)
{
    union i2c_smbus_data data;
    int err;
 
    err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
                   I2C_SMBUS_BYTE_DATA, &data);
    if (err < 0)
        return err;
 
    return 0x0FF & data.byte;
}
 
__s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value)
{
    union i2c_smbus_data data;
    data.byte = value;
    return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
                I2C_SMBUS_BYTE_DATA, &data);
}
 
__s32 i2c_smbus_read_word_data(int file, __u8 command)
{
    union i2c_smbus_data data;
    int err;
 
    err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
                   I2C_SMBUS_WORD_DATA, &data);
    if (err < 0)
        return err;
 
    return 0x0FFFF & data.word;
}
 
__s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value)
{
    union i2c_smbus_data data;
    data.word = value;
    return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
                I2C_SMBUS_WORD_DATA, &data);
}
 
__s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
{
    union i2c_smbus_data data;
    data.word = value;
    if (i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
                 I2C_SMBUS_PROC_CALL, &data))
        return -1;
    else
        return 0x0FFFF & data.word;
}
 
/* Returns the number of read bytes */
__s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)
{
    union i2c_smbus_data data;
    int i, err;
 
    err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
                   I2C_SMBUS_BLOCK_DATA, &data);
    if (err < 0)
        return err;
 
    for (i = 1; i <= data.block[0]; i++)
        values[i-1] = data.block[i];
    return data.block[0];
}
 
__s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
                 const __u8 *values)
{
    union i2c_smbus_data data;
    int i;
    if (length > I2C_SMBUS_BLOCK_MAX)
        length = I2C_SMBUS_BLOCK_MAX;
    for (i = 1; i <= length; i++)
        data.block[i] = values[i-1];
    data.block[0] = length;
    return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
                I2C_SMBUS_BLOCK_DATA, &data);
}
 
/* Returns the number of read bytes */
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
   ask for less than 32 bytes, your code will only work with kernels
   2.6.23 and later. */
__s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
                    __u8 *values)
{
    union i2c_smbus_data data;
    int i, err;
 
    if (length > I2C_SMBUS_BLOCK_MAX)
        length = I2C_SMBUS_BLOCK_MAX;
    data.block[0] = length;
 
    err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
                   length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
                I2C_SMBUS_I2C_BLOCK_DATA, &data);
    if (err < 0)
        return err;
 
    for (i = 1; i <= data.block[0]; i++)
        values[i-1] = data.block[i];
    return data.block[0];
}
 
__s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
                     const __u8 *values)
{
    union i2c_smbus_data data;
    int i;
    if (length > I2C_SMBUS_BLOCK_MAX)
        length = I2C_SMBUS_BLOCK_MAX;
    for (i = 1; i <= length; i++)
        data.block[i] = values[i-1];
    data.block[0] = length;
    return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
                I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
}
 
/* Returns the number of read bytes */
__s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
                   __u8 *values)
{
    union i2c_smbus_data data;
    int i, err;
 
    if (length > I2C_SMBUS_BLOCK_MAX)
        length = I2C_SMBUS_BLOCK_MAX;
    for (i = 1; i <= length; i++)
        data.block[i] = values[i-1];
    data.block[0] = length;
 
    err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
                   I2C_SMBUS_BLOCK_PROC_CALL, &data);
    if (err < 0)
        return err;
 
    for (i = 1; i <= data.block[0]; i++)
        values[i-1] = data.block[i];
    return data.block[0];
}
smbus.h
#ifndef LIB_I2C_SMBUS_H
#define LIB_I2C_SMBUS_H
 
#include <linux/types.h>
#include <linux/i2c.h>
 
extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
			      int size, union i2c_smbus_data *data);
 
extern __s32 i2c_smbus_write_quick(int file, __u8 value);
extern __s32 i2c_smbus_read_byte(int file);
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
 
/* Returns the number of read bytes */
extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
					const __u8 *values);
 
/* Returns the number of read bytes */
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
   ask for less than 32 bytes, your code will only work with kernels
   2.6.23 and later. */
extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
					   __u8 *values);
extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
					    const __u8 *values);
 
/* Returns the number of read bytes */
extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
					  __u8 *values);
 
#endif /* LIB_I2C_SMBUS_H */