1 /**
2 * collectd - src/barometer.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; only version 2.1 of the License is
7 * applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Tomas Menzl
20 **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "utils_cache.h"
25 #include "plugin.h"
27 #include <stdint.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <linux/i2c-dev.h>
31 #include <math.h>
33 /* ------------ MPL115 defines ------------ */
34 /* I2C address of the MPL115 sensor */
35 #define MPL115_I2C_ADDRESS 0x60
37 /* register addresses */
38 #define MPL115_ADDR_CONV 0x00
39 #define MPL115_ADDR_COEFFS 0x04
41 /* register sizes */
42 #define MPL115_NUM_CONV 4
43 #define MPL115_NUM_COEFFS 12
45 /* commands / addresses */
46 #define MPL115_CMD_CONVERT_PRESS 0x10
47 #define MPL115_CMD_CONVERT_TEMP 0x11
48 #define MPL115_CMD_CONVERT_BOTH 0x12
50 #define MPL115_CONVERSION_RETRIES 5
53 /* ------------ MPL3115 defines ------------ */
54 /* MPL3115 I2C address */
55 #define MPL3115_I2C_ADDRESS 0x60
57 /* register addresses (only the interesting ones) */
58 #define MPL3115_REG_STATUS 0x00
59 #define MPL3115_REG_OUT_P_MSB 0x01
60 #define MPL3115_REG_OUT_P_CSB 0x02
61 #define MPL3115_REG_OUT_P_LSB 0x03
62 #define MPL3115_REG_OUT_T_MSB 0x04
63 #define MPL3115_REG_OUT_T_LSB 0x05
64 #define MPL3115_REG_DR_STATUS 0x06
65 #define MPL3115_REG_WHO_AM_I 0x0C
66 #define MPL3115_REG_SYSMOD 0x11
67 #define MPL3115_REG_PT_DATA_CFG 0x13
68 #define MPL3115_REG_BAR_IN_MSB 0x14
69 #define MPL3115_REG_BAR_IN_LSB 0x15
70 #define MPL3115_REG_CTRL_REG1 0x26
71 #define MPL3115_REG_CTRL_REG2 0x27
72 #define MPL3115_REG_CTRL_REG3 0x28
73 #define MPL3115_REG_CTRL_REG4 0x29
74 #define MPL3115_REG_CTRL_REG5 0x2A
75 #define MPL3115_REG_OFF_P 0x2B
76 #define MPL3115_REG_OFF_T 0x2C
77 #define MPL3115_REG_OFF_H 0x2D
79 /* Register values, masks */
80 #define MPL3115_WHO_AM_I_RESP 0xC4
82 #define MPL3115_PT_DATA_DREM 0x04
83 #define MPL3115_PT_DATA_PDEF 0x02
84 #define MPL3115_PT_DATA_TDEF 0x01
86 #define MPL3115_DR_STATUS_TDR 0x02
87 #define MPL3115_DR_STATUS_PDR 0x04
88 #define MPL3115_DR_STATUS_PTDR 0x08
89 #define MPL3115_DR_STATUS_DR (MPL3115_DR_STATUS_TDR | MPL3115_DR_STATUS_PDR | MPL3115_DR_STATUS_PTDR)
91 #define MPL3115_DR_STATUS_TOW 0x20
92 #define MPL3115_DR_STATUS_POW 0x40
93 #define MPL3115_DR_STATUS_PTOW 0x80
95 #define MPL3115_CTRL_REG1_ALT 0x80
96 #define MPL3115_CTRL_REG1_RAW 0x40
97 #define MPL3115_CTRL_REG1_OST_MASK 0x38
98 #define MPL3115_CTRL_REG1_OST_1 0x00
99 #define MPL3115_CTRL_REG1_OST_2 0x08
100 #define MPL3115_CTRL_REG1_OST_4 0x10
101 #define MPL3115_CTRL_REG1_OST_8 0x18
102 #define MPL3115_CTRL_REG1_OST_16 0x20
103 #define MPL3115_CTRL_REG1_OST_32 0x28
104 #define MPL3115_CTRL_REG1_OST_64 0x30
105 #define MPL3115_CTRL_REG1_OST_128 0x38
106 #define MPL3115_CTRL_REG1_RST 0x04
107 #define MPL3115_CTRL_REG1_OST 0x02
108 #define MPL3115_CTRL_REG1_SBYB 0x01
109 #define MPL3115_CTRL_REG1_SBYB_MASK 0xFE
111 #define MPL3115_NUM_CONV_VALS 5
114 /* ------------ Normalization ------------ */
115 /* Mean sea level pressure normalization methods */
116 #define MSLP_NONE 0
117 #define MSLP_INTERNATIONAL 1
118 #define MSLP_DEU_WETT 2
120 /** Temperature reference history depth for averaging. See #get_reference_temperature */
121 #define REF_TEMP_AVG_NUM 5
123 /* ------------------------------------------ */
124 static const char *config_keys[] =
125 {
126 "Device",
127 "Oversampling",
128 "PressureOffset", /**< only for MPL3115 */
129 "TemperatureOffset", /**< only for MPL3115 */
130 "Altitude",
131 "Normalization",
132 "TemperatureSensor"
133 };
135 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
137 static char * config_device = NULL; /**< I2C bus device */
138 static int config_oversample = 1; /**< averaging window */
140 static double config_press_offset = 0.0; /**< pressure offset */
141 static double config_temp_offset = 0.0; /**< temperature offset */
143 static double config_altitude = NAN; /**< altitude */
144 static int config_normalize = 0; /**< normalization method */
146 static _Bool configured = 0; /**< the whole plugin config status */
148 static int i2c_bus_fd = -1; /**< I2C bus device FD */
150 static _Bool is_MPL3115 = 0; /**< is this MPL3115? */
151 static __s32 oversample_MPL3115 = 0; /**< MPL3115 CTRL1 oversample setting */
154 /* MPL115 conversion coefficients */
155 static double mpl115_coeffA0;
156 static double mpl115_coeffB1;
157 static double mpl115_coeffB2;
158 static double mpl115_coeffC12;
159 static double mpl115_coeffC11;
160 static double mpl115_coeffC22;
162 /* ------------------------ averaging ring buffer ------------------------ */
163 /* Used only for MPL115. MPL3115 supports real oversampling in the device so */
164 /* no need for any postprocessing. */
166 static _Bool avg_initialized = 0; /**< already initialized by real values */
168 typedef struct averaging_s {
169 long int * ring_buffer;
170 int ring_buffer_size;
171 long int ring_buffer_sum;
172 int ring_buffer_head;
173 } averaging_t;
176 static averaging_t pressure_averaging = { NULL, 0, 0L, 0 };
177 static averaging_t temperature_averaging = { NULL, 0, 0L, 0 };
180 /**
181 * Create / allocate averaging buffer
182 *
183 * The buffer is initialized with zeros.
184 *
185 * @param avg pointer to ring buffer to be allocated
186 * @param size requested buffer size
187 *
188 * @return Zero when successful
189 */
190 static int averaging_create(averaging_t * avg, int size)
191 {
192 int a;
194 avg->ring_buffer = (long int *) malloc(size * sizeof(*avg));
195 if (avg->ring_buffer == NULL)
196 {
197 ERROR ("barometer: averaging_create - ring buffer allocation of size %d failed",
198 size);
199 return -1;
200 }
202 for (a=0; a<size; ++a)
203 {
204 avg->ring_buffer[a] = 0L;
205 }
207 avg->ring_buffer_size = size;
208 avg->ring_buffer_sum = 0L;
209 avg->ring_buffer_head = 0;
211 return 0;
212 }
215 /**
216 * Delete / free existing averaging buffer
217 *
218 * @param avg pointer to the ring buffer to be deleted
219 */
220 static void averaging_delete(averaging_t * avg)
221 {
222 if (avg->ring_buffer != NULL)
223 {
224 free(avg->ring_buffer);
225 avg->ring_buffer = NULL;
226 }
227 avg->ring_buffer_size = 0;
228 avg->ring_buffer_sum = 0L;
229 avg->ring_buffer_head = 0;
230 }
233 /*
234 * Add new sample to the averaging buffer
235 *
236 * A new averaged value is returned. Note that till the buffer is full
237 * returned value is inaccurate as it is an average of real values and initial
238 * zeros.
239 *
240 * @param avg pointer to the ring buffer
241 * @param sample new sample value
242 *
243 * @return Averaged sample value
244 */
245 static double averaging_add_sample(averaging_t * avg, long int sample)
246 {
247 double result;
249 avg->ring_buffer_sum += sample - avg->ring_buffer[avg->ring_buffer_head];
250 avg->ring_buffer[avg->ring_buffer_head] = sample;
251 avg->ring_buffer_head = (avg->ring_buffer_head+1) % avg->ring_buffer_size;
252 result = (double)(avg->ring_buffer_sum) / (double)(avg->ring_buffer_size);
254 DEBUG ("barometer: averaging_add_sample - added %ld, result = %lf",
255 sample,
256 result);
258 return result;
259 }
262 /* ------------------------ temperature refference ------------------------ */
264 /**
265 * Linked list type of temperature sensor references
266 */
267 typedef struct temperature_list_s {
268 char * sensor_name; /**< sensor name/reference */
269 size_t num_values; /**< number of values (usually one) */
270 _Bool initialized; /**< sensor already provides data */
271 struct temperature_list_s * next; /**< next in the list */
272 } temperature_list_t;
274 static temperature_list_t * temp_list = NULL;
277 /*
278 * Add new sensor to the temperature reference list
279 *
280 * @param list the list
281 * @param sensor reference name (as provided by the config file)
282 *
283 * @return Zero when successful
284 */
285 static int temp_list_add(temperature_list_t * list, const char * sensor)
286 {
287 temperature_list_t * new_temp;
289 new_temp = (temperature_list_t *) malloc(sizeof(*new_temp));
290 if(new_temp == NULL)
291 return -1;
293 new_temp->sensor_name = strdup(sensor);
294 new_temp->initialized = 0;
295 new_temp->num_values = 0;
296 if(new_temp->sensor_name == NULL)
297 {
298 free(new_temp);
299 return -1;
300 }
302 new_temp->next = temp_list;
303 temp_list = new_temp;
304 return 0;
305 }
308 /*
309 * Delete the whole temperature reference list
310 *
311 * @param list the list to be deleted
312 */
313 static void temp_list_delete(temperature_list_t ** list)
314 {
315 temperature_list_t * tmp;
317 while (*list != NULL)
318 {
319 tmp = (*list);
320 (*list) = (*list)->next;
321 free(tmp->sensor_name);
322 free(tmp);
323 tmp = NULL;
324 }
325 }
328 /*
329 * Get reference temperature value
330 *
331 * First initially uc_get_rate_by_name is tried. At the startup due to nondeterministic
332 * order the temperature may not be read yet (then it fails and first measurment gives
333 * only absolute air pressure reading which is acceptable). Once it succedes (should be
334 * second measurement at the latest) we use average of few last readings from
335 * uc_get_history_by_name. It may take few readings to start filling so again we use
336 * uc_get_rate_by_name as a fallback.
337 * The idea is to use basic "noise" filtering (history averaging) across all the values
338 * which given sensor provides (up to given depth). Then we get minimum among
339 * the sensors.
340 *
341 * @param result where the result is stored. When not available NAN is stored.
342 *
343 * @return Zero when successful
344 */
345 static int get_reference_temperature(double * result)
346 {
347 temperature_list_t * list = temp_list;
349 gauge_t * values = NULL; /**< rate values */
350 size_t values_num = 0; /**< number of rate values */
351 int i;
353 gauge_t values_history[REF_TEMP_AVG_NUM];
355 double avg_sum; /**< Value sum for computing average */
356 int avg_num; /**< Number of values for computing average */
357 double average; /**< Resulting value average */
359 *result = NAN;
361 while(list != NULL)
362 {
363 avg_sum = 0.0;
364 avg_num = 0;
366 /* First time need to read current rate to learn how many values are
367 there (typically for temperature it would be just one).
368 We do not expect dynamic changing of number of temperarure values
369 in runtime yet (are there any such cases?). */
370 if(!list->initialized)
371 {
372 if(uc_get_rate_by_name(list->sensor_name,
373 &values,
374 &values_num))
375 {
376 DEBUG ("barometer: get_reference_temperature - rate \"%s\" not found yet",
377 list->sensor_name);
378 list = list->next;
379 continue;
380 }
382 DEBUG ("barometer: get_reference_temperature - initialize \"%s\", %zu vals",
383 list->sensor_name,
384 values_num);
386 list->initialized = 1;
387 list->num_values = values_num;
389 for(i=0; i<values_num; ++i)
390 {
391 DEBUG ("barometer: get_reference_temperature - rate %d: %lf **",
392 i,
393 values[i]);
394 if(!isnan(values[i]))
395 {
396 avg_sum += values[i];
397 ++avg_num;
398 }
399 }
400 free(values);
401 values = NULL;
402 }
404 /* It is OK to get here the first time as well, in the worst case
405 the history will full of NANs. */
406 if(uc_get_history_by_name(list->sensor_name,
407 values_history,
408 REF_TEMP_AVG_NUM,
409 list->num_values))
410 {
411 ERROR ("barometer: get_reference_temperature - history \"%s\" lost",
412 list->sensor_name);
413 list->initialized = 0;
414 list->num_values = 0;
415 list = list->next;
416 continue;
417 }
419 for(i=0; i<REF_TEMP_AVG_NUM*list->num_values; ++i)
420 {
421 DEBUG ("barometer: get_reference_temperature - history %d: %lf",
422 i,
423 values_history[i]);
424 if(!isnan(values_history[i]))
425 {
426 avg_sum += values_history[i];
427 ++avg_num;
428 }
429 }
431 if(avg_num == 0) /* still no history? fallback to current */
432 {
433 if(uc_get_rate_by_name(list->sensor_name,
434 &values,
435 &values_num))
436 {
437 ERROR ("barometer: get_reference_temperature - rate \"%s\" lost",
438 list->sensor_name);
439 list->initialized = 0;
440 list->num_values = 0;
441 list = list->next;
442 continue;
443 }
445 for(i=0; i<values_num; ++i)
446 {
447 DEBUG ("barometer: get_reference_temperature - rate last %d: %lf **",
448 i,
449 values[i]);
450 if(!isnan(values[i]))
451 {
452 avg_sum += values[i];
453 ++avg_num;
454 }
455 }
456 free(values);
457 values = NULL;
458 }
460 if(avg_num == 0)
461 {
462 ERROR ("barometer: get_reference_temperature - could not read \"%s\"",
463 list->sensor_name);
464 list->initialized = 0;
465 list->num_values = 0;
466 }
467 else
468 {
469 average = avg_sum / (double) avg_num;
470 if(isnan(*result))
471 *result=average;
472 else if(*result>average)
473 *result=average;
474 }
475 list = list->next;
476 } /* while sensor list */
478 if(*result == NAN)
479 {
480 ERROR("barometer: get_reference_temperature - no sensor available (yet?)");
481 return -1;
482 }
483 DEBUG ("barometer: get_reference_temperature - temp is %lf", *result);
484 return 0;
485 }
487 /* ------------------------ MPL115 access ------------------------ */
489 /**
490 * Read the MPL115 sensor conversion coefficients.
491 *
492 * These are (device specific) constants so we can read them just once.
493 *
494 * @return Zero when successful
495 */
496 static int MPL115_read_coeffs(void)
497 {
498 uint8_t mpl115_coeffs[MPL115_NUM_COEFFS];
499 int32_t res;
501 int8_t sia0MSB, sia0LSB, sib1MSB, sib1LSB, sib2MSB, sib2LSB;
502 int8_t sic12MSB, sic12LSB, sic11MSB, sic11LSB, sic22MSB, sic22LSB;
503 int16_t sia0, sib1, sib2, sic12, sic11, sic22;
505 char errbuf[1024];
507 res = i2c_smbus_read_i2c_block_data(i2c_bus_fd,
508 MPL115_ADDR_COEFFS,
509 MPL115_NUM_COEFFS,
510 mpl115_coeffs);
511 if (res < 0)
512 {
513 ERROR ("barometer: read_mpl115_coeffs - problem reading data: %s",
514 sstrerror (errno, errbuf, sizeof (errbuf)));
515 return -1;
516 }
518 /* Using perhaps less elegant/efficient code, but more readable. */
519 /* a0: 16total 1sign 12int 4fract 0pad */
520 sia0MSB = mpl115_coeffs[0];
521 sia0LSB = mpl115_coeffs[1];
522 sia0 = (int16_t) sia0MSB <<8; /* s16 type, Shift to MSB */
523 sia0 += (int16_t) sia0LSB & 0x00FF; /* Add LSB to 16bit number */
524 mpl115_coeffA0 = (double) (sia0);
525 mpl115_coeffA0 /= 8.0; /* 3 fract bits */
527 /* b1: 16total 1sign 2int 13fract 0pad */
528 sib1MSB= mpl115_coeffs[2];
529 sib1LSB= mpl115_coeffs[3];
530 sib1 = sib1MSB <<8; /* Shift to MSB */
531 sib1 += sib1LSB & 0x00FF; /* Add LSB to 16bit number */
532 mpl115_coeffB1 = (double) (sib1);
533 mpl115_coeffB1 /= 8192.0; /* 13 fract */
535 /* b2: 16total 1sign 1int 14fract 0pad */
536 sib2MSB= mpl115_coeffs[4];
537 sib2LSB= mpl115_coeffs[5];
538 sib2 = sib2MSB <<8; /* Shift to MSB */
539 sib2 += sib2LSB & 0x00FF; /* Add LSB to 16bit number */
540 mpl115_coeffB2 = (double) (sib2);
541 mpl115_coeffB2 /= 16384.0; /* 14 fract */
543 /* c12: 14total 1sign 0int 13fract 9pad */
544 sic12MSB= mpl115_coeffs[6];
545 sic12LSB= mpl115_coeffs[7];
546 sic12 = sic12MSB <<8; /* Shift to MSB only by 8 for MSB */
547 sic12 += sic12LSB & 0x00FF;
548 mpl115_coeffC12 = (double) (sic12);
549 mpl115_coeffC12 /= 4.0; /* 16-14=2 */
550 mpl115_coeffC12 /= 4194304.0; /* 13+9=22 fract */
552 /* c11: 11total 1sign 0int 11fract 11pad */
553 sic11MSB= mpl115_coeffs[8];
554 sic11LSB= mpl115_coeffs[9];
555 sic11 = sic11MSB <<8; /* Shift to MSB only by 8 for MSB */
556 sic11 += sic11LSB & 0x00FF;
557 mpl115_coeffC11 = (double) (sic11);
558 mpl115_coeffC11 /= 32.0; /* 16-11=5 */
559 mpl115_coeffC11 /= 4194304.0; /* 11+11=22 fract */
561 /* c12: 11total 1sign 0int 10fract 15pad */
562 sic22MSB= mpl115_coeffs[10];
563 sic22LSB= mpl115_coeffs[11];
564 sic22 = sic22MSB <<8; /* Shift to MSB only by 8 for MSB */
565 sic22 += sic22LSB & 0x00FF;
566 mpl115_coeffC22 = (double) (sic22);
567 mpl115_coeffC22 /= 32.0; //16-11=5
568 mpl115_coeffC22 /= 33554432.0; /* 10+15=25 fract */
570 DEBUG("barometer: read_mpl115_coeffs: a0=%lf, b1=%lf, b2=%lf, c12=%lf, c11=%lf, c22=%lf",
571 mpl115_coeffA0,
572 mpl115_coeffB1,
573 mpl115_coeffB2,
574 mpl115_coeffC12,
575 mpl115_coeffC11,
576 mpl115_coeffC22);
577 return 0;
578 }
581 /*
582 * Convert raw adc values to real data using the sensor coefficients.
583 *
584 * @param adc_pressure adc pressure value to be converted
585 * @param adc_temp adc temperature value to be converted
586 * @param pressure computed real pressure
587 * @param temperature computed real temperature
588 */
589 static void MPL115_convert_adc_to_real(double adc_pressure,
590 double adc_temp,
591 double * pressure,
592 double * temperature)
593 {
594 double Pcomp;
595 Pcomp = mpl115_coeffA0 + \
596 (mpl115_coeffB1 + mpl115_coeffC11*adc_pressure + mpl115_coeffC12*adc_temp) * adc_pressure + \
597 (mpl115_coeffB2 + mpl115_coeffC22*adc_temp) * adc_temp;
599 *pressure = ((1150.0-500.0) * Pcomp / 1023.0) + 500.0;
600 *temperature = (472.0 - adc_temp) / 5.35 + 25.0;
601 DEBUG ("barometer: convert_adc_to_real - got %lf hPa, %lf C",
602 *pressure,
603 *temperature);
604 }
607 /**
608 * Read sensor averegaed measurements
609 *
610 * @param pressure averaged measured pressure
611 * @param temperature averaged measured temperature
612 *
613 * @return Zero when successful
614 */
615 static int MPL115_read_averaged(double * pressure, double * temperature)
616 {
617 uint8_t mpl115_conv[MPL115_NUM_CONV];
618 int8_t res;
619 int retries;
620 int conv_pressure;
621 int conv_temperature;
622 double adc_pressure;
623 double adc_temperature;
624 char errbuf[1024];
626 *pressure = 0.0;
627 *temperature = 0.0;
629 /* start conversion of both temp and presure */
630 retries = MPL115_CONVERSION_RETRIES;
631 while (retries>0)
632 {
633 /* write 1 to start conversion */
634 res = i2c_smbus_write_byte_data (i2c_bus_fd,
635 MPL115_CMD_CONVERT_BOTH,
636 0x01);
637 if (res >= 0)
638 break;
640 --retries;
641 if(retries>0)
642 {
643 ERROR ("barometer: MPL115_read_averaged - requesting conversion: %s, " \
644 "will retry at most %d more times",
645 sstrerror (errno, errbuf, sizeof (errbuf)),
646 retries);
647 }
648 else
649 {
650 ERROR ("barometer: MPL115_read_averaged - requesting conversion: %s, "\
651 "too many failed retries",
652 sstrerror (errno, errbuf, sizeof (errbuf)));
653 return -1;
654 }
655 }
657 usleep (10000); /* wait 10ms for the conversion */
659 retries=MPL115_CONVERSION_RETRIES;
660 while (retries>0)
661 {
662 res = i2c_smbus_read_i2c_block_data(i2c_bus_fd,
663 MPL115_ADDR_CONV,
664 MPL115_NUM_CONV,
665 mpl115_conv);
666 if (res >= 0)
667 break;
669 --retries;
670 if (retries>0)
671 {
672 ERROR ("barometer: MPL115_read_averaged - reading conversion: %s, " \
673 "will retry at most %d more times",
674 sstrerror (errno, errbuf, sizeof (errbuf)),
675 retries);
676 }
677 else
678 {
679 ERROR ("barometer: MPL115_read_averaged - reading conversion: %s, " \
680 "too many failed retries",
681 sstrerror (errno, errbuf, sizeof (errbuf)));
682 return -1;
683 }
684 }
686 conv_pressure = ((mpl115_conv[0] << 8) | mpl115_conv[1]) >> 6;
687 conv_temperature = ((mpl115_conv[2] << 8) | mpl115_conv[3]) >> 6;
688 DEBUG ("barometer: MPL115_read_averaged, raw pressure ADC value = %d, " \
689 "raw temperature ADC value = %d",
690 conv_pressure,
691 conv_temperature);
693 adc_pressure = averaging_add_sample (&pressure_averaging, conv_pressure);
694 adc_temperature = averaging_add_sample (&temperature_averaging, conv_temperature);
696 MPL115_convert_adc_to_real(adc_pressure, adc_temperature, pressure, temperature);
698 DEBUG ("barometer: MPL115_read_averaged - averaged ADC pressure = %lf / temperature = %lf, " \
699 "real pressure = %lf hPa / temperature = %lf C",
700 adc_pressure,
701 adc_temperature,
702 *pressure,
703 *temperature);
705 return 0;
706 }
708 /* ------------------------ MPL3115 access ------------------------ */
710 /**
711 * Detect presence of a MPL3115 pressure sensor by checking register "WHO AM I"
712 *
713 * @return 1 if MPL3115, 0 otherwise
714 */
715 static int MPL3115_detect(void)
716 {
717 __s32 res;
719 res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_WHO_AM_I);
720 if(res == MPL3115_WHO_AM_I_RESP)
721 {
722 DEBUG ("barometer: MPL3115_detect - positive detection");
723 return 1;
724 }
726 DEBUG ("barometer: MPL3115_detect - negative detection");
727 return 0;
728 }
730 /**
731 * Adjusts oversampling to values supported by MPL3115
732 *
733 * MPL3115 supports only power of 2 in the range 1 to 128.
734 */
735 static void MPL3115_adjust_oversampling(void)
736 {
737 int new_val = 0;
739 if(config_oversample > 100)
740 {
741 new_val = 128;
742 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_128;
743 }
744 else if(config_oversample > 48)
745 {
746 new_val = 64;
747 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_64;
748 }
749 else if(config_oversample > 24)
750 {
751 new_val = 32;
752 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_32;
753 }
754 else if(config_oversample > 12)
755 {
756 new_val = 16;
757 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_16;
758 }
759 else if(config_oversample > 6)
760 {
761 new_val = 8;
762 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_8;
763 }
764 else if(config_oversample > 3)
765 {
766 new_val = 4;
767 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_4;
768 }
769 else if(config_oversample > 1)
770 {
771 new_val = 2;
772 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_2;
773 }
774 else
775 {
776 new_val = 1;
777 oversample_MPL3115 = MPL3115_CTRL_REG1_OST_1;
778 }
780 DEBUG("barometer: correcting oversampling for MPL3115 from %d to %d",
781 config_oversample,
782 new_val);
783 config_oversample = new_val;
784 }
786 /**
787 * Read sensor averegaed measurements
788 *
789 * @param pressure averaged measured pressure
790 * @param temperature averaged measured temperature
791 *
792 * @return Zero when successful
793 */
794 static int MPL3115_read(double * pressure, double * temperature)
795 {
796 __s32 res;
797 __s32 ctrl ;
798 __u8 data[MPL3115_NUM_CONV_VALS];
799 long int tmp_value = 0;
800 char errbuf[1024];
802 /* Set Active - activate the device from standby */
803 res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1);
804 if (res < 0)
805 {
806 ERROR ("barometer: MPL3115_read - cannot read CTRL_REG1: %s",
807 sstrerror (errno, errbuf, sizeof (errbuf)));
808 return 1;
809 }
810 ctrl = res;
811 res = i2c_smbus_write_byte_data(i2c_bus_fd,
812 MPL3115_REG_CTRL_REG1,
813 ctrl | MPL3115_CTRL_REG1_SBYB);
814 if (res < 0)
815 {
816 ERROR ("barometer: MPL3115_read - problem activating: %s",
817 sstrerror (errno, errbuf, sizeof (errbuf)));
818 return 1;
819 }
821 /* base sleep is 5ms x OST */
822 usleep(5000 * config_oversample);
824 /* check the flags/status if ready */
825 res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS);
826 if (res < 0)
827 {
828 ERROR ("barometer: MPL3115_read - cannot read status register: %s",
829 sstrerror (errno, errbuf, sizeof (errbuf)));
830 return 1;
831 }
833 while ((res & MPL3115_DR_STATUS_DR) != MPL3115_DR_STATUS_DR)
834 {
835 /* try some extra sleep... */
836 usleep(10000);
838 /* ... and repeat the check. The conversion has to finish sooner or later. */
839 res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS);
840 if (res < 0)
841 {
842 ERROR ("barometer: MPL3115_read - cannot read status register: %s",
843 sstrerror (errno, errbuf, sizeof (errbuf)));
844 return 1;
845 }
846 }
848 /* Now read all the data in one block. There is address autoincrement. */
849 res = i2c_smbus_read_i2c_block_data(i2c_bus_fd,
850 MPL3115_REG_OUT_P_MSB,
851 MPL3115_NUM_CONV_VALS,
852 data);
853 if (res < 0)
854 {
855 ERROR ("barometer: MPL3115_read - cannot read data registers: %s",
856 sstrerror (errno, errbuf, sizeof (errbuf)));
857 return 1;
858 }
860 tmp_value = (data[0] << 16) | (data[1] << 8) | data[2];
861 *pressure = ((double) tmp_value) / 4.0 / 16.0 / 100.0;
862 DEBUG ("barometer: MPL3115_read, absolute pressure = %lf hPa", *pressure);
864 if(data[3] > 0x7F)
865 {
866 data[3] = ~data[3] + 1;
867 *temperature = data[3];
868 *temperature = - *temperature;
869 }
870 else
871 {
872 *temperature = data[3];
873 }
875 *temperature += (double)(data[4]) / 256.0;
876 DEBUG ("barometer: MPL3115_read, temperature = %lf C", *temperature);
878 return 0;
879 }
881 /**
882 * Initialize MPL3115 for barometeric measurements
883 *
884 * @return 0 if successful
885 */
886 static int MPL3115_init_sensor(void)
887 {
888 __s32 res;
889 __s8 offset;
890 char errbuf[1024];
892 /* Reset the sensor. It will reset immediately without ACKing */
893 /* the transaction, so no error handling here. */
894 i2c_smbus_write_byte_data(i2c_bus_fd,
895 MPL3115_REG_CTRL_REG1,
896 MPL3115_CTRL_REG1_RST);
898 /* wait some time for the reset to finish */
899 usleep(100000);
901 /* now it should be in standby already so we can go and configure it */
903 /* Set temperature offset. */
904 /* result = ADCtemp + offset [C] */
905 offset = (__s8) (config_temp_offset * 16.0);
906 res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_OFF_T, offset);
907 if (res < 0)
908 {
909 ERROR ("barometer: MPL3115_init_sensor - problem setting temp offset: %s",
910 sstrerror (errno, errbuf, sizeof (errbuf)));
911 return -1;
912 }
914 /* Set pressure offset. */
915 /* result = ADCpress + offset [hPa] */
916 offset = (__s8) (config_press_offset * 100.0 / 4.0);
917 res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_OFF_P, offset);
918 if (res < 0)
919 {
920 ERROR ("barometer: MPL3115_init_sensor - problem setting pressure offset: %s",
921 sstrerror (errno, errbuf, sizeof (errbuf)));
922 return -1;
923 }
925 /* Enable Data Flags in PT_DATA_CFG - flags on both pressure and temp */
926 res = i2c_smbus_write_byte_data(i2c_bus_fd,
927 MPL3115_REG_PT_DATA_CFG,
928 MPL3115_PT_DATA_DREM \
929 | MPL3115_PT_DATA_PDEF \
930 | MPL3115_PT_DATA_TDEF);
931 if (res < 0)
932 {
933 ERROR ("barometer: MPL3115_init_sensor - problem setting PT_DATA_CFG: %s",
934 sstrerror (errno, errbuf, sizeof (errbuf)));
935 return -1;
936 }
938 /* Set to barometer with an OSR */
939 res = i2c_smbus_write_byte_data(i2c_bus_fd,
940 MPL3115_REG_CTRL_REG1,
941 oversample_MPL3115);
942 if (res < 0)
943 {
944 ERROR ("barometer: MPL3115_init_sensor - problem configuring CTRL_REG1: %s",
945 sstrerror (errno, errbuf, sizeof (errbuf)));
946 return -1;
947 }
949 return 0;
950 }
953 /* ------------------------ Common functionality ------------------------ */
955 /**
956 * Convert absolute pressure (in hPa) to mean sea level pressure
957 *
958 * Implemented methods are:
959 * - MSLP_NONE - no converions, returns absolute pressure
960 *
961 * - MSLP_INTERNATIONAL - see http://en.wikipedia.org/wiki/Atmospheric_pressure#Altitude_atmospheric_pressure_variation
962 * Requires #config_altitude
963 *
964 * - MSLP_DEU_WETT - formula as recommended by the Deutsche Wetterdienst. See
965 * http://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Theorie
966 * Requires both #config_altitude and temperature reference(s).
967 *
968 * @param abs_pressure absloute pressure to be converted
969 *
970 * @return mean sea level pressure if successful, NAN otherwise
971 */
972 static double abs_to_mean_sea_level_pressure(double abs_pressure)
973 {
974 double mean = -1.0;
975 double temp = 0.0;
976 int result = 0;
978 DEBUG ("barometer: abs_to_mean_sea_level_pressure: absPressure = %lf, method = %d",
979 abs_pressure,
980 config_normalize);
982 if (config_normalize >= MSLP_DEU_WETT)
983 {
984 result = get_reference_temperature(&temp);
985 if(result)
986 {
987 return NAN;
988 }
989 }
991 switch(config_normalize)
992 {
993 case MSLP_NONE:
994 mean = abs_pressure;
995 break;
997 case MSLP_INTERNATIONAL:
998 mean = abs_pressure / \
999 pow(1.0 - 0.0065*config_altitude/288.15, 0.0065*0.0289644/(8.31447*0.0065));
1000 break;
1002 case MSLP_DEU_WETT:
1003 {
1004 double E; /* humidity */
1005 double x;
1006 if(temp<9.1)
1007 E = 5.6402 * (-0.0916 + exp(0.06*temp) );
1008 else
1009 E = 18.2194 * (1.0463 - exp(-0.0666*temp) );
1010 x = 9.80665 / (287.05 * (temp+273.15 + 0.12*E + 0.0065*config_altitude/2)) * config_altitude;
1011 mean = abs_pressure * exp(x);
1012 }
1013 break;
1015 default:
1016 ERROR ("barometer: abs_to_mean_sea_level_pressure: wrong conversion method %d",
1017 config_normalize);
1018 mean = abs_pressure;
1019 break;
1020 }
1022 return mean;
1023 }
1025 /* ------------------------ main plugin callbacks ------------------------ */
1027 /**
1028 * Main plugin configuration callback (using simple config)
1029 *
1030 * @param key configuration key we should process
1031 * @param value configuration value we should process
1032 *
1033 * @return Zero when successful.
1034 */
1035 static int collectd_barometer_config (const char *key, const char *value)
1036 {
1037 DEBUG("barometer: collectd_barometer_config");
1039 if (strcasecmp (key, "Device") == 0)
1040 {
1041 sfree (config_device);
1042 config_device = strdup (value);
1043 }
1044 else if (strcasecmp (key, "Oversampling") == 0)
1045 {
1046 int oversampling_tmp = atoi (value);
1047 if (oversampling_tmp < 1 || oversampling_tmp > 1024)
1048 {
1049 WARNING ("barometer: collectd_barometer_config: invalid oversampling: %d." \
1050 " Allowed values are 1 to 1024 (for MPL115) or 128 (for MPL3115).",
1051 oversampling_tmp);
1052 return 1;
1053 }
1054 config_oversample = oversampling_tmp;
1055 }
1056 else if (strcasecmp (key, "Altitude") == 0)
1057 {
1058 config_altitude = atof (value);
1059 }
1060 else if (strcasecmp (key, "Normalization") == 0)
1061 {
1062 int normalize_tmp = atoi (value);
1063 if (normalize_tmp < 0 || normalize_tmp > 2)
1064 {
1065 WARNING ("barometer: collectd_barometer_config: invalid normalization: %d",
1066 normalize_tmp);
1067 return 1;
1068 }
1069 config_normalize = normalize_tmp;
1070 }
1071 else if (strcasecmp (key, "TemperatureSensor") == 0)
1072 {
1073 if(temp_list_add(temp_list, value))
1074 {
1075 return -1;
1076 }
1077 }
1078 else if (strcasecmp (key, "PressureOffset") == 0)
1079 {
1080 config_press_offset = atof(value);
1081 }
1082 else if (strcasecmp (key, "TemperatureOffset") == 0)
1083 {
1084 config_temp_offset = atof(value);
1085 }
1086 else
1087 {
1088 return -1;
1089 }
1091 return 0;
1092 }
1095 /**
1096 * Shutdown callback.
1097 *
1098 * Close I2C and delete all the buffers.
1099 *
1100 * @return Zero when successful (at the moment the only possible outcome)
1101 */
1102 static int collectd_barometer_shutdown(void)
1103 {
1104 DEBUG ("barometer: collectd_barometer_shutdown");
1106 if(!is_MPL3115)
1107 {
1108 averaging_delete (&pressure_averaging);
1109 averaging_delete (&temperature_averaging);
1111 temp_list_delete(&temp_list);
1112 }
1114 if (i2c_bus_fd > 0)
1115 {
1116 close (i2c_bus_fd);
1117 i2c_bus_fd = -1;
1118 sfree (config_device);
1119 }
1121 return 0;
1122 }
1125 /**
1126 * Plugin read callback for MPL115.
1127 *
1128 * Dispatching will create values:
1129 * - <hostname>/barometer-mpl115/pressure-normalized
1130 * - <hostname>/barometer-mpl115/pressure-absolute
1131 * - <hostname>/barometer-mpl115/temperature
1132 *
1133 * @return Zero when successful.
1134 */
1135 static int MPL115_collectd_barometer_read (void)
1136 {
1137 int result = 0;
1139 double pressure = 0.0;
1140 double temperature = 0.0;
1141 double norm_pressure = 0.0;
1143 value_list_t vl = VALUE_LIST_INIT;
1144 value_t values[1];
1146 DEBUG("barometer: MPL115_collectd_barometer_read");
1148 if (!configured)
1149 {
1150 return -1;
1151 }
1153 /* Rather than delaying init, we will intitialize during first read. This
1154 way at least we have a better chance to have the reference temperature
1155 already available. */
1156 if(!avg_initialized)
1157 {
1158 int i;
1159 for(i=0; i<config_oversample-1; ++i)
1160 {
1161 result = MPL115_read_averaged(&pressure, &temperature);
1162 if(result)
1163 {
1164 ERROR ("barometer: MPL115_collectd_barometer_read - mpl115 read, ignored during init");
1165 }
1166 DEBUG("barometer: MPL115_collectd_barometer_read - init %d / %d", i+1, config_oversample-1);
1167 usleep(20000);
1168 }
1169 avg_initialized = 1;
1170 }
1172 result = MPL115_read_averaged(&pressure, &temperature);
1173 if(result)
1174 return result;
1176 norm_pressure = abs_to_mean_sea_level_pressure(pressure);
1178 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
1179 sstrncpy (vl.plugin, "barometer", sizeof (vl.plugin));
1180 sstrncpy (vl.plugin_instance, "mpl115", sizeof (vl.plugin_instance));
1182 vl.values_len = 1;
1183 vl.values = values;
1185 /* dispatch normalized air pressure */
1186 sstrncpy (vl.type, "pressure", sizeof (vl.type));
1187 sstrncpy (vl.type_instance, "normalized", sizeof (vl.type_instance));
1188 values[0].gauge = norm_pressure;
1189 plugin_dispatch_values (&vl);
1191 /* dispatch absolute air pressure */
1192 sstrncpy (vl.type, "pressure", sizeof (vl.type));
1193 sstrncpy (vl.type_instance, "absolute", sizeof (vl.type_instance));
1194 values[0].gauge = pressure;
1195 plugin_dispatch_values (&vl);
1197 /* dispatch sensor temperature */
1198 sstrncpy (vl.type, "temperature", sizeof (vl.type));
1199 sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
1200 values[0].gauge = temperature;
1201 plugin_dispatch_values (&vl);
1203 return 0;
1204 }
1207 /**
1208 * Plugin read callback for MPL3115.
1209 *
1210 * Dispatching will create values:
1211 * - <hostname>/barometer-mpl3115/pressure-normalized
1212 * - <hostname>/barometer-mpl3115/pressure-absolute
1213 * - <hostname>/barometer-mpl3115/temperature
1214 *
1215 * @return Zero when successful.
1216 */
1217 static int MPL3115_collectd_barometer_read (void)
1218 {
1219 int result = 0;
1221 double pressure = 0.0;
1222 double temperature = 0.0;
1223 double norm_pressure = 0.0;
1225 value_list_t vl = VALUE_LIST_INIT;
1226 value_t values[1];
1228 DEBUG("barometer: MPL3115_collectd_barometer_read");
1230 if (!configured)
1231 {
1232 return -1;
1233 }
1235 result = MPL3115_read(&pressure, &temperature);
1236 if(result)
1237 return result;
1239 norm_pressure = abs_to_mean_sea_level_pressure(pressure);
1241 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
1242 sstrncpy (vl.plugin, "barometer", sizeof (vl.plugin));
1243 sstrncpy (vl.plugin_instance, "mpl3115", sizeof (vl.plugin_instance));
1245 vl.values_len = 1;
1246 vl.values = values;
1248 /* dispatch normalized air pressure */
1249 sstrncpy (vl.type, "pressure", sizeof (vl.type));
1250 sstrncpy (vl.type_instance, "normalized", sizeof (vl.type_instance));
1251 values[0].gauge = norm_pressure;
1252 plugin_dispatch_values (&vl);
1254 /* dispatch absolute air pressure */
1255 sstrncpy (vl.type, "pressure", sizeof (vl.type));
1256 sstrncpy (vl.type_instance, "absolute", sizeof (vl.type_instance));
1257 values[0].gauge = pressure;
1258 plugin_dispatch_values (&vl);
1260 /* dispatch sensor temperature */
1261 sstrncpy (vl.type, "temperature", sizeof (vl.type));
1262 sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
1263 values[0].gauge = temperature;
1264 plugin_dispatch_values (&vl);
1266 return 0;
1267 }
1270 /**
1271 * Initialization callback
1272 *
1273 * Check config, initialize I2C bus access, conversion coefficients and averaging
1274 * ring buffers
1275 *
1276 * @return Zero when successful.
1277 */
1278 static int collectd_barometer_init (void)
1279 {
1280 char errbuf[1024];
1282 DEBUG ("barometer: collectd_barometer_init");
1284 if (config_device == NULL)
1285 {
1286 ERROR("barometer: collectd_barometer_init I2C bus device not configured");
1287 return -1;
1288 }
1290 if (config_normalize >= MSLP_INTERNATIONAL && isnan(config_altitude))
1291 {
1292 ERROR("barometer: collectd_barometer_init no altitude configured " \
1293 "for mean sea level pressure normalization.");
1294 return -1;
1295 }
1297 if (config_normalize == MSLP_DEU_WETT
1298 &&
1299 temp_list == NULL)
1300 {
1301 ERROR("barometer: collectd_barometer_init no temperature reference "\
1302 "configured for mean sea level pressure normalization.");
1303 return -1;
1304 }
1307 i2c_bus_fd = open(config_device, O_RDWR);
1308 if (i2c_bus_fd < 0)
1309 {
1310 ERROR ("barometer: collectd_barometer_init problem opening I2C bus device \"%s\": %s (is loaded mod i2c-dev?)",
1311 config_device,
1312 sstrerror (errno, errbuf, sizeof (errbuf)));
1313 return -1;
1314 }
1316 if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0)
1317 {
1318 ERROR("barometer: collectd_barometer_init problem setting i2c slave address to 0x%02X: %s",
1319 MPL115_I2C_ADDRESS,
1320 sstrerror (errno, errbuf, sizeof (errbuf)));
1321 return -1;
1322 }
1324 /* detect sensor type - MPL115 or MPL3115 */
1325 is_MPL3115 = MPL3115_detect();
1327 /* init correct sensor type */
1328 if(is_MPL3115) /* MPL3115 */
1329 {
1330 MPL3115_adjust_oversampling();
1332 if(MPL3115_init_sensor())
1333 return -1;
1335 plugin_register_read ("barometer", MPL3115_collectd_barometer_read);
1336 }
1337 else /* MPL115 */
1338 {
1339 if (averaging_create (&pressure_averaging, config_oversample))
1340 {
1341 ERROR("barometer: collectd_barometer_init pressure averaging init failed");
1342 return -1;
1343 }
1345 if (averaging_create (&temperature_averaging, config_oversample))
1346 {
1347 ERROR("barometer: collectd_barometer_init temperature averaging init failed");
1348 return -1;
1349 }
1351 if (MPL115_read_coeffs() < 0)
1352 return -1;
1354 plugin_register_read ("barometer", MPL115_collectd_barometer_read);
1355 }
1357 configured = 1;
1358 return 0;
1359 }
1361 /* ------------------------ plugin register / entry point ------------------------ */
1363 /**
1364 * Plugin "entry" - register all callback.
1365 *
1366 */
1367 void module_register (void)
1368 {
1369 plugin_register_config ("barometer",
1370 collectd_barometer_config,
1371 config_keys,
1372 config_keys_num);
1373 plugin_register_init ("barometer", collectd_barometer_init);
1374 plugin_register_shutdown ("barometer", collectd_barometer_shutdown);
1375 }