1 /*
2  * Author: William Penner <william.penner@intel.com>
3  * Copyright (c) 2014 Intel Corporation.
4  *
5  * This application code supports the mpl3115a2 digital barometric pressure
6  * and temperature sensor from Freescale.  The datasheet is available
7  * from their website:
8  * http://cache.freescale.com/files/sensors/doc/data_sheet/MPL3115A2.pdf
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  */
28 
29 #include <iostream>
30 #include <string>
31 #include <stdexcept>
32 #include <unistd.h>
33 #include <stdlib.h>
34 
35 #include "mpl3115a2.h"
36 
37 using namespace upm;
38 
MPL3115A2(int bus,int devAddr,uint8_t mode)39 MPL3115A2::MPL3115A2 (int bus, int devAddr, uint8_t mode) : m_i2ControlCtx(bus)
40 {
41     int id;
42 
43     m_name = MPL3115A2_NAME;
44 
45     m_controlAddr = devAddr;
46     m_bus = bus;
47 
48     mraa::Result ret = m_i2ControlCtx.address(m_controlAddr);
49     if (ret != mraa::SUCCESS) {
50         throw std::runtime_error(std::string(__FUNCTION__) +
51                                  ": mraa_i2c_address() failed");
52         return;
53     }
54 
55     setOversampling(mode);
56 
57     id = i2cReadReg_8(MPL3115A2_WHO_AM_I);
58     if (id != MPL3115A2_DEVICE_ID)  {
59         throw std::runtime_error(std::string(__FUNCTION__) +
60                                  ": incorrect device id");
61         return;
62     }
63 }
64 
65 /*
66  * Function to test the device and verify that is appears operational
67  * Typically functioning sensors will return "noisy" values and would
68  * be expected to change a bit.  This fuction will check for this
69  * variation.
70  */
71 
72 int
testSensor(void)73 MPL3115A2::testSensor(void)
74 {
75     int i, iTries;
76     int iError = 0;
77     float pressure, temperature;
78     float fPMin, fPMax, fTMin, fTMax;
79 
80     fprintf(stdout, "Executing Sensor Test.\n" );
81 
82     pressure    = getPressure(true);
83     temperature = getTemperature(false);
84     fPMin = fPMax = pressure;
85     fTMin = fTMax = temperature;
86 
87     iTries = 20;
88     do {
89         sampleData();
90         pressure = getPressure(true);
91         temperature = getTemperature(false);
92         if (pressure < fPMin)    fPMin = pressure;
93         if (pressure > fPMax)    fPMax = pressure;
94         if (temperature < fTMin) fTMin = temperature;
95         if (temperature > fTMax) fTMax = temperature;
96     }
97     while(fPMin == fPMax && fTMin == fTMax && --iTries);
98 
99     if (fPMin == fPMax && fTMin == fTMax) {
100         fprintf(stdout, "  Warning - sensor values not changing.\n" );
101         return -1;
102     }
103 
104     fprintf(stdout, "  Test complete.\n");
105 
106     return 0;
107 }
108 
109 /*
110  * Function to dump out the i2c register block to the screen
111  */
112 
113 void
dumpSensor(void)114 MPL3115A2::dumpSensor(void)
115 {
116     int i, j, ival;
117 
118     fprintf(stdout, "Dumping i2c block from %s\n", MPL3115A2_NAME);
119     for (i=0; i < 256; i+=16) {
120         fprintf(stdout, "  %02x: ", i);
121         for (j=i; j < i+16; j++) {
122             fprintf(stdout, "%02x ", i2cReadReg_8(j));
123         }
124         fprintf(stdout, "\n");
125     }
126 }
127 
128 /*
129  * Function used to soft RESET the MPL3115A2 device to ensure
130  * it is in a known state.  This function can be used to reset
131  * the min/max temperature and pressure values.
132  */
133 
134 int
resetSensor(void)135 MPL3115A2::resetSensor(void)
136 {
137     fprintf(stdout, "Resetting MPL3115A2 device\n" );
138     i2cWriteReg(MPL3115A2_CTRL_REG1, MPL3115A2_CTRL_RESET);
139     usleep(50000);
140     i2cWriteReg(MPL3115A2_CTRL_REG1, MPL3115A2_CTRL_RESET |
141             MPL3115A2_SETOVERSAMPLE(m_oversampling));
142 
143     return 0;
144 }
145 
146 int
sampleData(void)147 MPL3115A2::sampleData(void)
148 {
149     int val;
150     mraa::Result ret;
151     int tries = 15;
152     uint32_t us_delay;
153 
154     // trigger measurement
155     ret = i2cWriteReg(MPL3115A2_CTRL_REG1,
156             MPL3115A2_CTRL_OST | MPL3115A2_SETOVERSAMPLE(m_oversampling));
157     if (mraa::SUCCESS != ret) {
158         fprintf(stdout, "Write to trigger measurement failed\n");
159         return -1;
160     }
161 
162     // Calculate and delay the appopriate time for the measurement
163     us_delay = ((1 << m_oversampling) * 4 + 2) * 1000;
164     usleep(us_delay);
165 
166     // Loop waiting for the ready bit to become active
167     while (tries-- > 0) {
168         val = i2cReadReg_8(MPL3115A2_CTRL_REG1);
169 
170         /* wait for data ready, i.e. OST cleared */
171         if (!(val & MPL3115A2_CTRL_OST))
172             break;
173         usleep(20000);
174     }
175     if (tries < 0) {
176         throw std::runtime_error(std::string(__FUNCTION__) +
177                                  ": timeout during measurement");
178         return -1;
179     }
180 
181     return 0;
182 }
183 
184 int32_t
getPressureReg(int reg)185 MPL3115A2::getPressureReg(int reg) {
186     return ((i2cReadReg_16(reg) << 8)|(uint32_t)i2cReadReg_8(reg+2))*100/64;
187 }
188 
189 int32_t
getTempReg(int reg)190 MPL3115A2::getTempReg(int reg) {
191     return (int32_t)((int16_t)i2cReadReg_16(reg)) * 1000 / 256;
192 }
193 
194 float
getPressure(int bSampleData)195 MPL3115A2::getPressure(int bSampleData) {
196     int ret;
197 
198     // Trigger request to make a measurement
199     if (bSampleData) {
200         ret = sampleData();
201         if (ret < 0) {
202             fprintf(stdout, "Error sampling pressure\n");
203             return -1;
204         }
205     }
206     m_iPressure = getPressureReg(MPL3115A2_OUT_PRESS);
207 
208     return (float)m_iPressure / 100;
209 }
210 
211 float
getTemperature(int bSampleData)212 MPL3115A2::getTemperature(int bSampleData) {
213     int ret;
214 
215     // Trigger request to make a measurement
216     if (bSampleData) {
217         ret = sampleData();
218         if (ret < 0) {
219             fprintf(stdout, "Error sampling temperature\n");
220             return -1;
221         }
222 	}
223     m_iTemperature = getTempReg(MPL3115A2_OUT_TEMP);
224 
225     return (float)m_iTemperature / 1000;
226 }
227 
228 float
getSealevelPressure(float altitudeMeters)229 MPL3115A2::getSealevelPressure(float altitudeMeters) {
230     float fPressure = (float)m_iPressure / 100.0;
231     return fPressure / pow(1.0-altitudeMeters/44330, 5.255);
232 }
233 
234 float
getAltitude(float sealevelPressure)235 MPL3115A2::getAltitude (float sealevelPressure) {
236     float fPressure = (float)m_iPressure / 100.0;
237     return 44330 * (1.0 - pow(fPressure /sealevelPressure,0.1903));
238 }
239 
240 void
setOversampling(uint8_t oversampling)241 MPL3115A2::setOversampling(uint8_t oversampling)
242 {
243     if (oversampling > MPL3115A2_MAXOVERSAMPLE)
244         oversampling = MPL3115A2_MAXOVERSAMPLE;
245     m_oversampling = oversampling;
246 }
247 
248 uint8_t
getOversampling(void)249 MPL3115A2::getOversampling(void)
250 {
251     return m_oversampling;
252 }
253 
254 float
getTemperatureMax(void)255 MPL3115A2::getTemperatureMax(void)
256 {
257     return (float)getTempReg(MPL3115A2_T_MAX) / 1000;
258 }
259 
260 float
getTemperatureMin(void)261 MPL3115A2::getTemperatureMin(void)
262 {
263     return (float)getTempReg(MPL3115A2_T_MIN) / 1000;
264 }
265 
266 float
getPressureMax(void)267 MPL3115A2::getPressureMax(void)
268 {
269     return (float)getPressureReg(MPL3115A2_P_MAX) / 1000;
270 }
271 
272 float
getPressureMin(void)273 MPL3115A2::getPressureMin(void)
274 {
275     return (float)getPressureReg(MPL3115A2_P_MIN) / 1000;
276 }
277 
278 float
convertTempCtoF(float fTemp)279 MPL3115A2::convertTempCtoF(float fTemp)
280 {
281     return(fTemp * 9 / 5 + 32);
282 }
283 
284 /*
285  * This is set for 15degC (Pa = 0.0002961 in Hg)
286  */
287 float
convertPaToinHg(float fPressure)288 MPL3115A2::convertPaToinHg(float fPressure)
289 {
290     return(fPressure * 0.0002961);
291 }
292 
293 /*
294  * Functions to read and write data to the i2c device
295  */
296 
297 mraa::Result
i2cWriteReg(uint8_t reg,uint8_t value)298 MPL3115A2::i2cWriteReg (uint8_t reg, uint8_t value) {
299     mraa::Result error = mraa::SUCCESS;
300 
301     uint8_t data[2] = { reg, value };
302     m_i2ControlCtx.address (m_controlAddr);
303     error = m_i2ControlCtx.write (data, 2);
304 
305     if (error != mraa::SUCCESS)
306       throw std::runtime_error(std::string(__FUNCTION__) +
307                                ":mraa_i2c_write() failed");
308     return error;
309 }
310 
311 uint16_t
i2cReadReg_16(int reg)312 MPL3115A2::i2cReadReg_16 (int reg) {
313     uint16_t data;
314 
315     m_i2ControlCtx.address(m_controlAddr);
316     data  = (uint16_t)m_i2ControlCtx.readReg(reg) << 8;
317     data |= (uint16_t)m_i2ControlCtx.readReg(reg+1);
318 
319     return data;
320 }
321 
322 uint8_t
i2cReadReg_8(int reg)323 MPL3115A2::i2cReadReg_8 (int reg) {
324     m_i2ControlCtx.address(m_controlAddr);
325     return m_i2ControlCtx.readReg(reg);
326 }
327 
328