238 lines
6.0 KiB
C
238 lines
6.0 KiB
C
|
/*
|
||
|
* bmp180.c:
|
||
|
* Extend wiringPi with the BMP180 I2C Pressure and Temperature
|
||
|
* sensor. This is used in the Pi Weather Station
|
||
|
* Copyright (c) 2016 Gordon Henderson
|
||
|
*
|
||
|
* Information from the document held at:
|
||
|
* http://wmrx00.sourceforge.net/Arduino/BMP085-Calcs.pdf
|
||
|
* was very useful when building this code.
|
||
|
*
|
||
|
***********************************************************************
|
||
|
* This file is part of wiringPi:
|
||
|
* https://projects.drogon.net/raspberry-pi/wiringpi/
|
||
|
*
|
||
|
* wiringPi is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as
|
||
|
* published by the Free Software Foundation, either version 3 of the
|
||
|
* License, or (at your option) any later version.
|
||
|
*
|
||
|
* wiringPi is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with wiringPi.
|
||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||
|
***********************************************************************
|
||
|
*/
|
||
|
|
||
|
#include <unistd.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <math.h>
|
||
|
|
||
|
#include "wiringPi.h"
|
||
|
#include "wiringPiI2C.h"
|
||
|
|
||
|
#include "bmp180.h"
|
||
|
|
||
|
#undef DEBUG
|
||
|
|
||
|
#define I2C_ADDRESS 0x77
|
||
|
#define BMP180_OSS 0
|
||
|
|
||
|
|
||
|
// Static calibration data
|
||
|
// The down-side of this is that there can only be one BMP180 in
|
||
|
// a system - which is practice isn't an issue as it's I2C
|
||
|
// address is fixed.
|
||
|
|
||
|
static int16_t AC1, AC2, AC3 ;
|
||
|
static uint16_t AC4, AC5, AC6 ;
|
||
|
static int16_t VB1, VB2 ;
|
||
|
static int16_t MB, MC, MD ;
|
||
|
|
||
|
static double c5, c6, mc, md, x0, x1, x2, yy0, yy1, yy2, p0, p1, p2 ;
|
||
|
|
||
|
// Pressure & Temp variables
|
||
|
|
||
|
uint32_t cPress, cTemp ;
|
||
|
|
||
|
static int altitude ;
|
||
|
|
||
|
/*
|
||
|
* read16:
|
||
|
* Quick hack to read the 16-bit data with the correct endian
|
||
|
*********************************************************************************
|
||
|
*/
|
||
|
|
||
|
uint16_t read16 (int fd, int reg)
|
||
|
{
|
||
|
return (wiringPiI2CReadReg8 (fd, reg) << 8) | wiringPiI2CReadReg8 (fd, reg + 1) ;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* bmp180ReadTempPress:
|
||
|
* Does the hard work of reading the sensor
|
||
|
*********************************************************************************
|
||
|
*/
|
||
|
|
||
|
static void bmp180ReadTempPress (int fd)
|
||
|
{
|
||
|
double fTemp, fPress ;
|
||
|
double tu, a ;
|
||
|
double pu, s, x, y, z ;
|
||
|
|
||
|
uint8_t data [4] ;
|
||
|
|
||
|
// Start a temperature sensor reading
|
||
|
|
||
|
wiringPiI2CWriteReg8 (fd, 0xF4, 0x2E) ;
|
||
|
delay (5) ;
|
||
|
|
||
|
// Read the raw data
|
||
|
|
||
|
data [0] = wiringPiI2CReadReg8 (fd, 0xF6) ;
|
||
|
data [1] = wiringPiI2CReadReg8 (fd, 0xF7) ;
|
||
|
|
||
|
// And calculate...
|
||
|
|
||
|
tu = (data [0] * 256.0) + data [1] ;
|
||
|
|
||
|
a = c5 * (tu - c6) ;
|
||
|
fTemp = a + (mc / (a + md)) ;
|
||
|
cTemp = (int)rint (((100.0 * fTemp) + 0.5) / 10.0) ;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
printf ("fTemp: %f, cTemp: %6d\n", fTemp, cTemp) ;
|
||
|
#endif
|
||
|
|
||
|
// Start a pressure snsor reading
|
||
|
|
||
|
wiringPiI2CWriteReg8 (fd, 0xF4, 0x34 | (BMP180_OSS << 6)) ;
|
||
|
delay (5) ;
|
||
|
|
||
|
// Read the raw data
|
||
|
|
||
|
data [0] = wiringPiI2CReadReg8 (fd, 0xF6) ;
|
||
|
data [1] = wiringPiI2CReadReg8 (fd, 0xF7) ;
|
||
|
data [2] = wiringPiI2CReadReg8 (fd, 0xF8) ;
|
||
|
|
||
|
// And calculate...
|
||
|
|
||
|
pu = ((double)data [0] * 256.0) + (double)data [1] + ((double)data [2] / 256.0) ;
|
||
|
s = fTemp - 25.0 ;
|
||
|
x = (x2 * pow (s, 2.0)) + (x1 * s) + x0 ;
|
||
|
y = (yy2 * pow (s, 2.0)) + (yy1 * s) + yy0 ;
|
||
|
z = (pu - x) / y ;
|
||
|
fPress = (p2 * pow (z, 2.0)) + (p1 * z) + p0 ;
|
||
|
cPress = (int)rint (((100.0 * fPress) + 0.5) / 10.0) ;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
printf ("fPress: %f, cPress: %6d\n", fPress, cPress) ;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* myAnalogWrite:
|
||
|
* Write to a fake register to represent the height above sea level
|
||
|
* so that the peudo millibar register can read the pressure in mB
|
||
|
*********************************************************************************
|
||
|
*/
|
||
|
|
||
|
static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value)
|
||
|
{
|
||
|
int chan = pin - node->pinBase ;
|
||
|
|
||
|
if (chan == 0)
|
||
|
altitude = value ;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* myAnalogRead:
|
||
|
*********************************************************************************
|
||
|
*/
|
||
|
|
||
|
static int myAnalogRead (struct wiringPiNodeStruct *node, int pin)
|
||
|
{
|
||
|
int chan = pin - node->pinBase ;
|
||
|
|
||
|
bmp180ReadTempPress (node->fd) ;
|
||
|
|
||
|
/**/ if (chan == 0) // Read Temperature
|
||
|
return cTemp ;
|
||
|
else if (chan == 1) // Pressure
|
||
|
return cPress ;
|
||
|
else if (chan == 2) // Pressure in mB
|
||
|
return cPress / pow (1 - ((double)altitude / 44330.0), 5.255) ;
|
||
|
else
|
||
|
return -9999 ;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* bmp180Setup:
|
||
|
* Create a new instance of a PCF8591 I2C GPIO interface. We know it
|
||
|
* has 4 pins, (4 analog inputs and 1 analog output which we'll shadow
|
||
|
* input 0) so all we need to know here is the I2C address and the
|
||
|
* user-defined pin base.
|
||
|
*********************************************************************************
|
||
|
*/
|
||
|
|
||
|
int bmp180Setup (const int pinBase)
|
||
|
{
|
||
|
double c3, c4, b1 ;
|
||
|
int fd ;
|
||
|
struct wiringPiNodeStruct *node ;
|
||
|
|
||
|
if ((fd = wiringPiI2CSetup (I2C_ADDRESS)) < 0)
|
||
|
return FALSE ;
|
||
|
|
||
|
node = wiringPiNewNode (pinBase, 4) ;
|
||
|
|
||
|
node->fd = fd ;
|
||
|
node->analogRead = myAnalogRead ;
|
||
|
node->analogWrite = myAnalogWrite ;
|
||
|
|
||
|
// Read calibration data
|
||
|
|
||
|
AC1 = read16 (fd, 0xAA) ;
|
||
|
AC2 = read16 (fd, 0xAC) ;
|
||
|
AC3 = read16 (fd, 0xAE) ;
|
||
|
AC4 = read16 (fd, 0xB0) ;
|
||
|
AC5 = read16 (fd, 0xB2) ;
|
||
|
AC6 = read16 (fd, 0xB4) ;
|
||
|
VB1 = read16 (fd, 0xB6) ;
|
||
|
VB2 = read16 (fd, 0xB8) ;
|
||
|
MB = read16 (fd, 0xBA) ;
|
||
|
MC = read16 (fd, 0xBC) ;
|
||
|
MD = read16 (fd, 0xBE) ;
|
||
|
|
||
|
// Calculate coefficients
|
||
|
|
||
|
c3 = 160.0 * pow (2.0, -15.0) * AC3 ;
|
||
|
c4 = pow (10.0, -3.0) * pow(2.0,-15.0) * AC4 ;
|
||
|
b1 = pow (160.0, 2.0) * pow(2.0,-30.0) * VB1 ;
|
||
|
c5 = (pow (2.0, -15.0) / 160.0) * AC5 ;
|
||
|
c6 = AC6 ;
|
||
|
mc = (pow (2.0, 11.0) / pow(160.0,2.0)) * MC ;
|
||
|
md = MD / 160.0 ;
|
||
|
x0 = AC1 ;
|
||
|
x1 = 160.0 * pow (2.0, -13.0) * AC2 ;
|
||
|
x2 = pow (160.0, 2.0) * pow(2.0,-25.0) * VB2 ;
|
||
|
yy0 = c4 * pow (2.0, 15.0) ;
|
||
|
yy1 = c4 * c3 ;
|
||
|
yy2 = c4 * b1 ;
|
||
|
p0 = (3791.0 - 8.0) / 1600.0 ;
|
||
|
p1 = 1.0 - 7357.0 * pow (2.0, -20.0) ;
|
||
|
p2 = 3038.0 * 100.0 * pow (2.0, -36.0) ;
|
||
|
|
||
|
return TRUE ;
|
||
|
}
|