Gordons Projects

--> Projects Top-Level GIT

Updating for various stuff and bugs. CM3+ and PiA3+
[wiringPi] / wiringPi / bmp180.c
1 /*
2  * bmp180.c:
3  *      Extend wiringPi with the BMP180 I2C Pressure and Temperature
4  *      sensor. This is used in the Pi Weather Station
5  *      Copyright (c) 2016 Gordon Henderson
6  *
7  *      Information from the document held at:
8  *              http://wmrx00.sourceforge.net/Arduino/BMP085-Calcs.pdf
9  *      was very useful when building this code.
10  *
11  ***********************************************************************
12  * This file is part of wiringPi:
13  *      https://projects.drogon.net/raspberry-pi/wiringpi/
14  *
15  *    wiringPi is free software: you can redistribute it and/or modify
16  *    it under the terms of the GNU Lesser General Public License as
17  *    published by the Free Software Foundation, either version 3 of the
18  *    License, or (at your option) any later version.
19  *
20  *    wiringPi is distributed in the hope that it will be useful,
21  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *    GNU Lesser General Public License for more details.
24  *
25  *    You should have received a copy of the GNU Lesser General Public
26  *    License along with wiringPi.
27  *    If not, see <http://www.gnu.org/licenses/>.
28  ***********************************************************************
29  */
30
31 #include <unistd.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <math.h>
35
36 #include "wiringPi.h"
37 #include "wiringPiI2C.h"
38
39 #include "bmp180.h"
40
41 #undef  DEBUG
42
43 #define I2C_ADDRESS     0x77
44 #define BMP180_OSS         0
45
46
47 // Static calibration data
48 //      The down-side of this is that there can only be one BMP180 in
49 //      a system - which is practice isn't an issue as it's I2C
50 //      address is fixed.
51
52 static  int16_t AC1, AC2, AC3 ;
53 static uint16_t AC4, AC5, AC6 ;
54 static  int16_t VB1, VB2 ;
55 static  int16_t  MB,  MC, MD ;
56
57 static double c5, c6, mc, md, x0, x1, x2, yy0, yy1, yy2, p0, p1, p2 ;
58
59 // Pressure & Temp variables
60
61 uint32_t cPress, cTemp ;
62
63 static int altitude ;
64
65 /*
66  * read16:
67  *      Quick hack to read the 16-bit data with the correct endian
68  *********************************************************************************
69  */
70
71 uint16_t read16 (int fd, int reg)
72 {
73   return (wiringPiI2CReadReg8 (fd, reg) <<  8) | wiringPiI2CReadReg8 (fd, reg + 1) ;
74
75 }
76
77
78 /*
79  * bmp180ReadTempPress:
80  *      Does the hard work of reading the sensor
81  *********************************************************************************
82  */
83
84 static void bmp180ReadTempPress (int fd)
85 {
86   double fTemp, fPress ;
87   double tu, a ;
88   double pu, s, x, y, z ;
89
90   uint8_t data [4] ;
91
92 // Start a temperature sensor reading
93
94   wiringPiI2CWriteReg8 (fd, 0xF4, 0x2E) ;
95   delay (5) ;
96
97 // Read the raw data
98
99   data [0] = wiringPiI2CReadReg8 (fd, 0xF6) ;
100   data [1] = wiringPiI2CReadReg8 (fd, 0xF7) ;
101
102 // And calculate...
103
104   tu = (data [0] * 256.0) + data [1] ;
105
106   a = c5 * (tu - c6) ;
107   fTemp = a + (mc / (a + md)) ;
108   cTemp = (int)rint (((100.0 * fTemp) + 0.5) / 10.0) ;
109
110 #ifdef  DEBUG
111   printf ("fTemp: %f, cTemp: %6d\n", fTemp, cTemp) ;
112 #endif
113
114 // Start a pressure snsor reading
115
116   wiringPiI2CWriteReg8 (fd, 0xF4, 0x34 | (BMP180_OSS << 6)) ;
117   delay (5) ;
118
119 // Read the raw data
120
121   data [0] = wiringPiI2CReadReg8 (fd, 0xF6) ;
122   data [1] = wiringPiI2CReadReg8 (fd, 0xF7) ;
123   data [2] = wiringPiI2CReadReg8 (fd, 0xF8) ;
124
125 // And calculate...
126
127   pu = ((double)data [0] * 256.0) + (double)data [1] + ((double)data [2] / 256.0) ;
128   s = fTemp - 25.0 ;
129   x = (x2 * pow (s, 2.0)) + (x1 * s) + x0 ;
130   y = (yy2 * pow (s, 2.0)) + (yy1 * s) + yy0 ;
131   z = (pu - x) / y ;
132   fPress = (p2 * pow (z, 2.0)) + (p1 * z) + p0 ;
133   cPress = (int)rint (((100.0 * fPress) + 0.5) / 10.0) ;
134
135 #ifdef  DEBUG
136   printf ("fPress: %f, cPress: %6d\n", fPress, cPress) ;
137 #endif
138 }
139
140
141 /*
142  * myAnalogWrite:
143  *      Write to a fake register to represent the height above sea level
144  *      so that the peudo millibar register can read the pressure in mB
145  *********************************************************************************
146  */
147
148 static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value)
149 {
150   int chan = pin - node->pinBase ;
151
152   if (chan == 0)
153     altitude = value ;
154 }
155
156 /*
157  * myAnalogRead:
158  *********************************************************************************
159  */
160
161 static int myAnalogRead (struct wiringPiNodeStruct *node, int pin)
162 {
163   int chan = pin - node->pinBase ;
164
165   bmp180ReadTempPress (node->fd) ;
166
167   /**/ if (chan == 0)   // Read Temperature
168     return cTemp ;
169   else if (chan == 1)   // Pressure
170     return cPress ;
171   else if (chan == 2)   // Pressure in mB
172     return cPress / pow (1 - ((double)altitude / 44330.0), 5.255) ;
173   else
174     return -9999 ;
175
176 }
177
178
179 /*
180  * bmp180Setup:
181  *      Create a new instance of a PCF8591 I2C GPIO interface. We know it
182  *      has 4 pins, (4 analog inputs and 1 analog output which we'll shadow
183  *      input 0) so all we need to know here is the I2C address and the
184  *      user-defined pin base.
185  *********************************************************************************
186  */
187
188 int bmp180Setup (const int pinBase)
189 {
190   double c3, c4, b1 ;
191   int fd ;
192   struct wiringPiNodeStruct *node ;
193
194   if ((fd = wiringPiI2CSetup (I2C_ADDRESS)) < 0)
195     return FALSE ;
196
197   node = wiringPiNewNode (pinBase, 4) ;
198
199   node->fd          = fd ;
200   node->analogRead  = myAnalogRead ;
201   node->analogWrite = myAnalogWrite ;
202
203 // Read calibration data
204
205   AC1 = read16 (fd, 0xAA) ;
206   AC2 = read16 (fd, 0xAC) ;
207   AC3 = read16 (fd, 0xAE) ;
208   AC4 = read16 (fd, 0xB0) ;
209   AC5 = read16 (fd, 0xB2) ;
210   AC6 = read16 (fd, 0xB4) ;
211   VB1 = read16 (fd, 0xB6) ;
212   VB2 = read16 (fd, 0xB8) ;
213    MB = read16 (fd, 0xBA) ;
214    MC = read16 (fd, 0xBC) ;
215    MD = read16 (fd, 0xBE) ;
216
217 // Calculate coefficients
218
219   c3 = 160.0 * pow (2.0, -15.0) * AC3 ;
220   c4 = pow (10.0, -3.0) * pow(2.0,-15.0) * AC4 ;
221   b1 = pow (160.0, 2.0) * pow(2.0,-30.0) * VB1 ;
222   c5 = (pow (2.0, -15.0) / 160.0) * AC5 ;
223   c6 = AC6 ;
224   mc = (pow (2.0, 11.0) / pow(160.0,2.0)) * MC ;
225   md = MD / 160.0 ;
226   x0 = AC1 ;
227   x1 = 160.0 * pow (2.0, -13.0) * AC2 ;
228   x2 = pow (160.0, 2.0) * pow(2.0,-25.0) * VB2 ;
229   yy0 = c4 * pow (2.0, 15.0) ;
230   yy1 = c4 * c3 ;
231   yy2 = c4 * b1 ;
232   p0 = (3791.0 - 8.0) / 1600.0 ;
233   p1 = 1.0 - 7357.0 * pow (2.0, -20.0) ;
234   p2 = 3038.0 * 100.0 * pow (2.0,  -36.0) ;
235
236   return TRUE ;
237 }