atMETEO
An ATmega based weather station
bmp180.h
Go to the documentation of this file.
1 /*
2  * atMETEO - An ATmega based weather station
3  * Copyright (C) 2014-2015 Christian Fetzer
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #pragma once
21 
35 #include "lib/utils.h"
36 
37 #include "i2c.h"
38 
39 namespace Avr
40 {
41 
53 class Bmp180
54 {
55 public:
59  enum class Mode
60  {
66 
71 
75  Standard,
76 
82  };
83 
90  : m_mode(mode)
91  {
92  }
93 
101  bool isValid() const { return m_valid; }
102 
110  float temperature() const
111  {
112  return m_temperature;
113  }
114 
122  float pressure() const
123  {
124  return m_pressure;
125  }
126 
135  float pressureAtSeaLevel(float altitude) const {
136  return (static_cast<float>(m_pressure) /
137  pow(1.0f - (static_cast<float>(altitude) / 44330.0f), 5.255f));
138  }
139 
146  bool read()
147  {
148  m_valid = false;
149  if (readUInt8(c_chipIdRegister) != c_chipId)
150  return false;
151 
152  if (!m_initialized) {
153  readCalibration();
154  calculatePolynomials();
155  m_initialized = true;
156  }
157 
158  // Calculate temperature
159  float a = m_fc5 * (readTemperature() - m_fc6);
160  m_temperature = a + (m_fmc / (a + m_fmd));
161 
162  // Calculate pressure
163  uint32_t pr = readPressure();
164  uint8_t pr0 = pr & 0xFF;
165  uint8_t pr1 = (pr >> 8) & 0xFF;
166  uint8_t pr2 = (pr >> 16) & 0xFF;
167  float pu = (pr2 * 256.00) + pr1 + (pr0 / 256.00);
168  float s = m_temperature - 25.0;
169  float x = (m_fx2 * pow(s, 2)) + (m_fx1 * s) + m_fx0;
170  float y = (m_fy2 * pow(s, 2)) + (m_fy1 * s) + m_fy0;
171  float z = (pu - x) / y;
172  m_pressure = ((m_fp2 * pow(z, 2)) + (m_fp1 * z) + m_fp0);
173 
174  m_valid = true;
175  return true;
176  }
177 
178 private:
179  static const uint8_t c_address = 0x77;
180  static const uint8_t c_chipId = 0x55;
181  static const uint8_t c_chipIdRegister = 0xD0;
182  static const uint8_t c_versionRegister = 0xD1;
183  static const uint8_t c_softResetRegister = 0xE0;
184  static const uint8_t c_ac1Register = 0xAA;
185  static const uint8_t c_ac2Register = 0xAC;
186  static const uint8_t c_ac3Register = 0xAE;
187  static const uint8_t c_ac4Register = 0xB0;
188  static const uint8_t c_ac5Register = 0xB2;
189  static const uint8_t c_ac6Register = 0xB4;
190  static const uint8_t c_b1Register = 0xB6;
191  static const uint8_t c_b2Register = 0xB8;
192  static const uint8_t c_mbRegister = 0xBA;
193  static const uint8_t c_mcRegister = 0xBC;
194  static const uint8_t c_mdRegister = 0xBE;
195  static const uint8_t c_controlRegister = 0xF4;
196  static const uint8_t c_dataRegister = 0xF6;
197 
198  static const uint8_t c_measureTempCmd = 0x2E; // 4.5 ms
199  static const uint8_t c_measurePressure0 = 0x34; // 4.5 ms
200  static const uint8_t c_measurePressure1 = 0x74; // 7.5 ms
201  static const uint8_t c_measurePressure2 = 0xB4; // 13.5 ms
202  static const uint8_t c_measurePressure3 = 0xF4; // 25.5 ms
203 
204  Mode m_mode;
205  bool m_valid = false;
206  bool m_initialized = false;
207  float m_temperature = 0.0;
208  float m_pressure = 0.0;
209 
210  // Calibration
211  int16_t m_ac1 = 0, m_ac2 = 0, m_ac3 = 0;
212  uint16_t m_ac4 = 0, m_ac5 = 0, m_ac6 = 0;
213  int16_t m_b1 = 0, m_b2 = 0, m_mb = 0, m_mc = 0, m_md = 0;
214 
215  // Polynomials
216  float m_fc5 = 0.0, m_fc6 = 0.0, m_fmc = 0.0, m_fmd = 0.0;
217  float m_fx0 = 0.0, m_fx1 = 0.0, m_fx2 = 0.0;
218  float m_fy0 = 0.0, m_fy1 = 0.0, m_fy2 = 0.0;
219  float m_fp0 = 0.0, m_fp1 = 0.0, m_fp2 = 0.0;
220 
221  void readCalibration()
222  {
223  m_ac1 = readUInt16(c_ac1Register);
224  m_ac2 = readUInt16(c_ac2Register);
225  m_ac3 = readUInt16(c_ac3Register);
226  m_ac4 = readUInt16(c_ac4Register);
227  m_ac5 = readUInt16(c_ac5Register);
228  m_ac6 = readUInt16(c_ac6Register);
229  m_b1 = readUInt16(c_b1Register);
230  m_b2 = readUInt16(c_b2Register);
231  m_mb = readUInt16(c_mbRegister);
232  m_mc = readUInt16(c_mcRegister);
233  m_md = readUInt16(c_mdRegister);
234  }
235 
236  void calculatePolynomials()
237  {
238  float fc3, fc4, fb1;
239  fc3 = 160.0 * pow(2, -15) * m_ac3;
240  fc4 = pow(10, -3) * pow(2, -15) * m_ac4;
241  fb1 = pow(160, 2) * pow(2, -30) * m_b1;
242 
243  // For temperature
244  m_fc5 = (pow(2, -15) / 160) * m_ac5;
245  m_fc6 = m_ac6;
246  m_fmc = (pow(2, 11) / pow(160, 2)) * m_mc;
247  m_fmd = m_md / 160.0;
248 
249  // For pressure
250  m_fx0 = m_ac1;
251  m_fx1 = 160.0 * pow(2, -13) * m_ac2;
252  m_fx2 = pow(160, 2) * pow(2, -25) * m_b2;
253  m_fy0 = fc4 * pow(2, 15);
254  m_fy1 = fc4 * fc3;
255  m_fy2 = fc4 * fb1;
256  m_fp0 = (3791.0 - 8.0) / 1600.0;
257  m_fp1 = 1.0 - 7357.0 * pow(2, -20);
258  m_fp2 = 3038.0 * 100.0 * pow(2, -36);
259  }
260 
261  uint16_t readTemperature()
262  {
263  writeUInt8(c_controlRegister, c_measureTempCmd);
264  _delay_ms(5);
265  return readUInt16(c_dataRegister);
266  }
267 
268  uint32_t readPressure()
269  {
270  switch (m_mode) {
271  case Mode::UltraLowPower:
272  writeUInt8(c_controlRegister, c_measurePressure0);
273  _delay_ms(5);
274  break;
275  case Mode::Standard:
276  writeUInt8(c_controlRegister, c_measurePressure1);
277  _delay_ms(8);
278  break;
280  writeUInt8(c_controlRegister, c_measurePressure2);
281  _delay_ms(14);
282  break;
284  writeUInt8(c_controlRegister, c_measurePressure3);
285  _delay_ms(26);
286  break;
287  }
288 
289  uint16_t msb = readUInt16(c_dataRegister);
290  uint8_t lsb = readUInt8(c_dataRegister + 2);
291  return (static_cast<uint32_t>(msb) << 8) | lsb;
292  }
293 
294  void writeUInt8(uint8_t reg, uint8_t value)
295  {
296  auto i2c = Avr::I2c::instance();
297  i2c.beginTransmission(c_address);
298  i2c.write(reg);
299  i2c.write(value);
300  i2c.endTransmission();
301  }
302 
303  uint8_t readUInt8(uint8_t reg)
304  {
305  auto i2c = Avr::I2c::instance();
306  i2c.beginTransmission(c_address);
307  i2c.write(reg);
308  i2c.endTransmission();
309 
310  uint8_t result = 0;
311  if (i2c.requestFrom(c_address, 1) == 1) {
312  result = i2c.read();
313  }
314  return result;
315  }
316 
317  uint16_t readUInt16(uint8_t reg)
318  {
319  auto i2c = Avr::I2c::instance();
320  i2c.beginTransmission(c_address);
321  i2c.write(reg);
322  i2c.endTransmission();
323 
324  uint16_t result = 0;
325  if (i2c.requestFrom(c_address, 2) == 2) {
326  uint8_t msb = i2c.read();
327  uint8_t lsb = i2c.read();
328  result = Sensors::word(msb, lsb);
329  }
330  return result;
331  }
332 };
333  // \addtogroup libtarget_bmp180
335 
336 } // namespace Avr
float temperature() const
Retrieves the cached temperature value.
Definition: bmp180.h:110
Mode
Sensor operation mode for reading the barometric pressure.
Definition: bmp180.h:59
float pressure() const
Retrieves the cached absolute pressure value.
Definition: bmp180.h:122
static I2c & instance()
Returns the I2c::I2c instance.
Definition: i2c.h:86
bool read()
Reads the temperature and pressure from the sensor and updates the cached values. ...
Definition: bmp180.h:146
bool isValid() const
Determines if the last sensor access was valid.
Definition: bmp180.h:101
float pressureAtSeaLevel(float altitude) const
Retrieves the cached pressure value relative to sea level.
Definition: bmp180.h:135
Bit manipulation utilities.
Namespace containing all symbols of the AVR C++ utilities library.
Definition: adc.h:48
Decodes data from Bosch BMP180 Digital pressure sensors.
Definition: bmp180.h:53
Bmp180(Mode mode=Mode::Standard)
Initializes the BMP180 sensor decoder.
Definition: bmp180.h:89
uint16_t word(uint8_t highByte, uint8_t lowByte)
Converts two bytes to a word.
Definition: utils.h:162
Wrapper for accessing built-in I2C (TWI) communication interfaces.