You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
6.0 KiB
237 lines
6.0 KiB
/* |
|
* 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 ; |
|
}
|
|
|