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