X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fbarometer.c;h=aaedd70afd075a51293b354c7a792a5f3e81d7ec;hb=5818b2e6cb1fe66746375885419e9c75134f8cb6;hp=95b05f4e2bda9af43b9e9a600fa43b62ccd44330;hpb=b49d4e33978d4c9508b68e931a7a27773f0348e1;p=collectd.git diff --git a/src/barometer.c b/src/barometer.c index 95b05f4e..aaedd70a 100644 --- a/src/barometer.c +++ b/src/barometer.c @@ -33,20 +33,20 @@ /* ------------ MPL115 defines ------------ */ /* I2C address of the MPL115 sensor */ #define MPL115_I2C_ADDRESS 0x60 - -/* register addresses */ + +/* register addresses */ #define MPL115_ADDR_CONV 0x00 #define MPL115_ADDR_COEFFS 0x04 - -/* register sizes */ + +/* register sizes */ #define MPL115_NUM_CONV 4 #define MPL115_NUM_COEFFS 12 - -/* commands / addresses */ + +/* commands / addresses */ #define MPL115_CMD_CONVERT_PRESS 0x10 #define MPL115_CMD_CONVERT_TEMP 0x11 #define MPL115_CMD_CONVERT_BOTH 0x12 - + #define MPL115_CONVERSION_RETRIES 5 @@ -82,12 +82,12 @@ #define MPL3115_PT_DATA_DREM 0x04 #define MPL3115_PT_DATA_PDEF 0x02 #define MPL3115_PT_DATA_TDEF 0x01 - + #define MPL3115_DR_STATUS_TDR 0x02 #define MPL3115_DR_STATUS_PDR 0x04 #define MPL3115_DR_STATUS_PTDR 0x08 #define MPL3115_DR_STATUS_DR (MPL3115_DR_STATUS_TDR | MPL3115_DR_STATUS_PDR | MPL3115_DR_STATUS_PTDR) - + #define MPL3115_DR_STATUS_TOW 0x20 #define MPL3115_DR_STATUS_POW 0x40 #define MPL3115_DR_STATUS_PTOW 0x80 @@ -111,6 +111,41 @@ #define MPL3115_NUM_CONV_VALS 5 +/* ------------ BMP085 defines ------------ */ +/* I2C address of the BMP085 sensor */ +#define BMP085_I2C_ADDRESS 0x77 + +/* register addresses */ +#define BMP085_ADDR_ID_REG 0xD0 +#define BMP085_ADDR_VERSION 0xD1 + +#define BMP085_ADDR_CONV 0xF6 + +#define BMP085_ADDR_CTRL_REG 0xF4 +#define BMP085_ADDR_COEFFS 0xAA + +/* register sizes */ +#define BMP085_NUM_COEFFS 22 + +/* commands, values */ +#define BMP085_CHIP_ID 0x55 + +#define BMP085_CMD_CONVERT_TEMP 0x2E + +#define BMP085_CMD_CONVERT_PRESS_0 0x34 +#define BMP085_CMD_CONVERT_PRESS_1 0x74 +#define BMP085_CMD_CONVERT_PRESS_2 0xB4 +#define BMP085_CMD_CONVERT_PRESS_3 0xF4 + +/* in us */ +#define BMP085_TIME_CNV_TEMP 4500 + +#define BMP085_TIME_CNV_PRESS_0 4500 +#define BMP085_TIME_CNV_PRESS_1 7500 +#define BMP085_TIME_CNV_PRESS_2 13500 +#define BMP085_TIME_CNV_PRESS_3 25500 + + /* ------------ Normalization ------------ */ /* Mean sea level pressure normalization methods */ #define MSLP_NONE 0 @@ -120,7 +155,17 @@ /** Temperature reference history depth for averaging. See #get_reference_temperature */ #define REF_TEMP_AVG_NUM 5 + /* ------------------------------------------ */ + +/** Supported sensor types */ +enum Sensor_type { + Sensor_none = 0, + Sensor_MPL115, + Sensor_MPL3115, + Sensor_BMP085 +}; + static const char *config_keys[] = { "Device", @@ -133,7 +178,7 @@ static const char *config_keys[] = }; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); - + static char * config_device = NULL; /**< I2C bus device */ static int config_oversample = 1; /**< averaging window */ @@ -142,13 +187,19 @@ static double config_temp_offset = 0.0; /**< temperature offset */ static double config_altitude = NAN; /**< altitude */ static int config_normalize = 0; /**< normalization method */ - + static _Bool configured = 0; /**< the whole plugin config status */ - + static int i2c_bus_fd = -1; /**< I2C bus device FD */ - -static _Bool is_MPL3115 = 0; /**< is this MPL3115? */ -static __s32 oversample_MPL3115 = 0; /**< MPL3115 CTRL1 oversample setting */ + +static enum Sensor_type sensor_type = Sensor_none; /**< detected/used sensor type */ + +static __s32 mpl3115_oversample = 0; /**< MPL3115 CTRL1 oversample setting */ + +// BMP085 configuration +static unsigned bmp085_oversampling; /**< BMP085 oversampling (0-3) */ +static unsigned long bmp085_timeCnvPress; /**< BMP085 conversion time for pressure in us */ +static __u8 bmp085_cmdCnvPress; /**< BMP085 pressure conversion command */ /* MPL115 conversion coefficients */ @@ -159,6 +210,21 @@ static double mpl115_coeffC12; static double mpl115_coeffC11; static double mpl115_coeffC22; +/* BMP085 conversion coefficients */ +static short bmp085_AC1; +static short bmp085_AC2; +static short bmp085_AC3; +static unsigned short bmp085_AC4; +static unsigned short bmp085_AC5; +static unsigned short bmp085_AC6; +static short bmp085_B1; +static short bmp085_B2; +static short bmp085_MB; +static short bmp085_MC; +static short bmp085_MD; + + + /* ------------------------ averaging ring buffer ------------------------ */ /* Used only for MPL115. MPL3115 supports real oversampling in the device so */ /* no need for any postprocessing. */ @@ -177,7 +243,7 @@ static averaging_t pressure_averaging = { NULL, 0, 0L, 0 }; static averaging_t temperature_averaging = { NULL, 0, 0L, 0 }; -/** +/** * Create / allocate averaging buffer * * The buffer is initialized with zeros. @@ -187,11 +253,9 @@ static averaging_t temperature_averaging = { NULL, 0, 0L, 0 }; * * @return Zero when successful */ -static int averaging_create(averaging_t * avg, int size) +static int averaging_create(averaging_t *avg, int size) { - int a; - - avg->ring_buffer = (long int *) malloc(size * sizeof(*avg)); + avg->ring_buffer = calloc ((size_t) size, sizeof (*avg->ring_buffer)); if (avg->ring_buffer == NULL) { ERROR ("barometer: averaging_create - ring buffer allocation of size %d failed", @@ -199,11 +263,6 @@ static int averaging_create(averaging_t * avg, int size) return -1; } - for (a=0; aring_buffer[a] = 0L; - } - avg->ring_buffer_size = size; avg->ring_buffer_sum = 0L; avg->ring_buffer_head = 0; @@ -250,9 +309,9 @@ static double averaging_add_sample(averaging_t * avg, long int sample) avg->ring_buffer[avg->ring_buffer_head] = sample; avg->ring_buffer_head = (avg->ring_buffer_head+1) % avg->ring_buffer_size; result = (double)(avg->ring_buffer_sum) / (double)(avg->ring_buffer_size); - - DEBUG ("barometer: averaging_add_sample - added %ld, result = %lf", - sample, + + DEBUG ("barometer: averaging_add_sample - added %ld, result = %lf", + sample, result); return result; @@ -415,7 +474,7 @@ static int get_reference_temperature(double * result) list = list->next; continue; } - + for(i=0; inum_values; ++i) { DEBUG ("barometer: get_reference_temperature - history %d: %lf", @@ -474,7 +533,7 @@ static int get_reference_temperature(double * result) } list = list->next; } /* while sensor list */ - + if(*result == NAN) { ERROR("barometer: get_reference_temperature - no sensor available (yet?)"); @@ -484,9 +543,45 @@ static int get_reference_temperature(double * result) return 0; } + /* ------------------------ MPL115 access ------------------------ */ -/** +/** + * Detect presence of a MPL115 pressure sensor. + * + * Unfortunately there seems to be no ID register so we just try to read first + * conversion coefficient from device at MPL115 address and hope it is really + * MPL115. We should use this check as the last resort (which would be the typical + * case anyway since MPL115 is the least accurate sensor). + * As a sideeffect will leave set I2C slave address. + * + * @return 1 if MPL115, 0 otherwise + */ +static int MPL115_detect(void) +{ + __s32 res; + char errbuf[1024]; + + if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0) + { + ERROR("barometer: MPL115_detect problem setting i2c slave address to 0x%02X: %s", + MPL115_I2C_ADDRESS, + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } + + res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL115_ADDR_COEFFS); + if(res >= 0) + { + DEBUG ("barometer: MPL115_detect - positive detection"); + return 1; + } + + DEBUG ("barometer: MPL115_detect - negative detection"); + return 0; +} + +/** * Read the MPL115 sensor conversion coefficients. * * These are (device specific) constants so we can read them just once. @@ -495,26 +590,26 @@ static int get_reference_temperature(double * result) */ static int MPL115_read_coeffs(void) { - uint8_t mpl115_coeffs[MPL115_NUM_COEFFS]; + uint8_t mpl115_coeffs[MPL115_NUM_COEFFS] = { 0 }; int32_t res; int8_t sia0MSB, sia0LSB, sib1MSB, sib1LSB, sib2MSB, sib2LSB; int8_t sic12MSB, sic12LSB, sic11MSB, sic11LSB, sic22MSB, sic22LSB; int16_t sia0, sib1, sib2, sic12, sic11, sic22; - + char errbuf[1024]; - res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, - MPL115_ADDR_COEFFS, - MPL115_NUM_COEFFS, + res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, + MPL115_ADDR_COEFFS, + STATIC_ARRAY_SIZE (mpl115_coeffs), mpl115_coeffs); if (res < 0) { - ERROR ("barometer: read_mpl115_coeffs - problem reading data: %s", + ERROR ("barometer: MPL115_read_coeffs - problem reading data: %s", sstrerror (errno, errbuf, sizeof (errbuf))); return -1; } - + /* Using perhaps less elegant/efficient code, but more readable. */ /* a0: 16total 1sign 12int 4fract 0pad */ sia0MSB = mpl115_coeffs[0]; @@ -523,7 +618,7 @@ static int MPL115_read_coeffs(void) sia0 += (int16_t) sia0LSB & 0x00FF; /* Add LSB to 16bit number */ mpl115_coeffA0 = (double) (sia0); mpl115_coeffA0 /= 8.0; /* 3 fract bits */ - + /* b1: 16total 1sign 2int 13fract 0pad */ sib1MSB= mpl115_coeffs[2]; sib1LSB= mpl115_coeffs[3]; @@ -531,7 +626,7 @@ static int MPL115_read_coeffs(void) sib1 += sib1LSB & 0x00FF; /* Add LSB to 16bit number */ mpl115_coeffB1 = (double) (sib1); mpl115_coeffB1 /= 8192.0; /* 13 fract */ - + /* b2: 16total 1sign 1int 14fract 0pad */ sib2MSB= mpl115_coeffs[4]; sib2LSB= mpl115_coeffs[5]; @@ -567,18 +662,18 @@ static int MPL115_read_coeffs(void) mpl115_coeffC22 /= 32.0; //16-11=5 mpl115_coeffC22 /= 33554432.0; /* 10+15=25 fract */ - DEBUG("barometer: read_mpl115_coeffs: a0=%lf, b1=%lf, b2=%lf, c12=%lf, c11=%lf, c22=%lf", - mpl115_coeffA0, - mpl115_coeffB1, - mpl115_coeffB2, - mpl115_coeffC12, - mpl115_coeffC11, + DEBUG("barometer: MPL115_read_coeffs: a0=%lf, b1=%lf, b2=%lf, c12=%lf, c11=%lf, c22=%lf", + mpl115_coeffA0, + mpl115_coeffB1, + mpl115_coeffB2, + mpl115_coeffC12, + mpl115_coeffC11, mpl115_coeffC22); return 0; } -/* +/** * Convert raw adc values to real data using the sensor coefficients. * * @param adc_pressure adc pressure value to be converted @@ -595,16 +690,16 @@ static void MPL115_convert_adc_to_real(double adc_pressure, Pcomp = mpl115_coeffA0 + \ (mpl115_coeffB1 + mpl115_coeffC11*adc_pressure + mpl115_coeffC12*adc_temp) * adc_pressure + \ (mpl115_coeffB2 + mpl115_coeffC22*adc_temp) * adc_temp; - + *pressure = ((1150.0-500.0) * Pcomp / 1023.0) + 500.0; *temperature = (472.0 - adc_temp) / 5.35 + 25.0; - DEBUG ("barometer: convert_adc_to_real - got %lf hPa, %lf C", + DEBUG ("barometer: MPL115_convert_adc_to_real - got %lf hPa, %lf C", *pressure, *temperature); } -/** +/** * Read sensor averegaed measurements * * @param pressure averaged measured pressure @@ -614,7 +709,7 @@ static void MPL115_convert_adc_to_real(double adc_pressure, */ static int MPL115_read_averaged(double * pressure, double * temperature) { - uint8_t mpl115_conv[MPL115_NUM_CONV]; + uint8_t mpl115_conv[MPL115_NUM_CONV] = { 0 }; int8_t res; int retries; int conv_pressure; @@ -625,7 +720,7 @@ static int MPL115_read_averaged(double * pressure, double * temperature) *pressure = 0.0; *temperature = 0.0; - + /* start conversion of both temp and presure */ retries = MPL115_CONVERSION_RETRIES; while (retries>0) @@ -661,8 +756,8 @@ static int MPL115_read_averaged(double * pressure, double * temperature) { res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, MPL115_ADDR_CONV, - MPL115_NUM_CONV, - mpl115_conv); + STATIC_ARRAY_SIZE (mpl115_conv), + mpl115_conv); if (res >= 0) break; @@ -682,7 +777,7 @@ static int MPL115_read_averaged(double * pressure, double * temperature) return -1; } } - + conv_pressure = ((mpl115_conv[0] << 8) | mpl115_conv[1]) >> 6; conv_temperature = ((mpl115_conv[2] << 8) | mpl115_conv[3]) >> 6; DEBUG ("barometer: MPL115_read_averaged, raw pressure ADC value = %d, " \ @@ -701,20 +796,31 @@ static int MPL115_read_averaged(double * pressure, double * temperature) adc_temperature, *pressure, *temperature); - + return 0; } /* ------------------------ MPL3115 access ------------------------ */ -/** +/** * Detect presence of a MPL3115 pressure sensor by checking register "WHO AM I" - * + * + * As a sideeffect will leave set I2C slave address. + * * @return 1 if MPL3115, 0 otherwise */ static int MPL3115_detect(void) { __s32 res; + char errbuf[1024]; + + if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL3115_I2C_ADDRESS) < 0) + { + ERROR("barometer: MPL3115_detect problem setting i2c slave address to 0x%02X: %s", + MPL3115_I2C_ADDRESS, + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_WHO_AM_I); if(res == MPL3115_WHO_AM_I_RESP) @@ -727,10 +833,10 @@ static int MPL3115_detect(void) return 0; } -/** +/** * Adjusts oversampling to values supported by MPL3115 * - * MPL3115 supports only power of 2 in the range 1 to 128. + * MPL3115 supports only power of 2 in the range 1 to 128. */ static void MPL3115_adjust_oversampling(void) { @@ -739,52 +845,52 @@ static void MPL3115_adjust_oversampling(void) if(config_oversample > 100) { new_val = 128; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_128; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_128; } else if(config_oversample > 48) { new_val = 64; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_64; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_64; } else if(config_oversample > 24) { new_val = 32; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_32; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_32; } else if(config_oversample > 12) { new_val = 16; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_16; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_16; } else if(config_oversample > 6) { new_val = 8; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_8; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_8; } else if(config_oversample > 3) { new_val = 4; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_4; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_4; } else if(config_oversample > 1) { new_val = 2; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_2; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_2; } else { new_val = 1; - oversample_MPL3115 = MPL3115_CTRL_REG1_OST_1; + mpl3115_oversample = MPL3115_CTRL_REG1_OST_1; } - DEBUG("barometer: correcting oversampling for MPL3115 from %d to %d", - config_oversample, + DEBUG("barometer: MPL3115_adjust_oversampling - correcting oversampling from %d to %d", + config_oversample, new_val); config_oversample = new_val; } -/** - * Read sensor averegaed measurements +/** + * Read sensor averaged measurements * * @param pressure averaged measured pressure * @param temperature averaged measured temperature @@ -798,7 +904,7 @@ static int MPL3115_read(double * pressure, double * temperature) __u8 data[MPL3115_NUM_CONV_VALS]; long int tmp_value = 0; char errbuf[1024]; - + /* Set Active - activate the device from standby */ res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1); if (res < 0) @@ -808,8 +914,8 @@ static int MPL3115_read(double * pressure, double * temperature) return 1; } ctrl = res; - res = i2c_smbus_write_byte_data(i2c_bus_fd, - MPL3115_REG_CTRL_REG1, + res = i2c_smbus_write_byte_data(i2c_bus_fd, + MPL3115_REG_CTRL_REG1, ctrl | MPL3115_CTRL_REG1_SBYB); if (res < 0) { @@ -817,10 +923,10 @@ static int MPL3115_read(double * pressure, double * temperature) sstrerror (errno, errbuf, sizeof (errbuf))); return 1; } - + /* base sleep is 5ms x OST */ usleep(5000 * config_oversample); - + /* check the flags/status if ready */ res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS); if (res < 0) @@ -829,12 +935,12 @@ static int MPL3115_read(double * pressure, double * temperature) sstrerror (errno, errbuf, sizeof (errbuf))); return 1; } - + while ((res & MPL3115_DR_STATUS_DR) != MPL3115_DR_STATUS_DR) { /* try some extra sleep... */ usleep(10000); - + /* ... and repeat the check. The conversion has to finish sooner or later. */ res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS); if (res < 0) @@ -844,10 +950,10 @@ static int MPL3115_read(double * pressure, double * temperature) return 1; } } - + /* Now read all the data in one block. There is address autoincrement. */ - res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, - MPL3115_REG_OUT_P_MSB, + res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, + MPL3115_REG_OUT_P_MSB, MPL3115_NUM_CONV_VALS, data); if (res < 0) @@ -856,11 +962,11 @@ static int MPL3115_read(double * pressure, double * temperature) sstrerror (errno, errbuf, sizeof (errbuf))); return 1; } - + tmp_value = (data[0] << 16) | (data[1] << 8) | data[2]; *pressure = ((double) tmp_value) / 4.0 / 16.0 / 100.0; - DEBUG ("barometer: MPL3115_read, absolute pressure = %lf hPa", *pressure); - + DEBUG ("barometer: MPL3115_read - absolute pressure = %lf hPa", *pressure); + if(data[3] > 0x7F) { data[3] = ~data[3] + 1; @@ -871,16 +977,16 @@ static int MPL3115_read(double * pressure, double * temperature) { *temperature = data[3]; } - + *temperature += (double)(data[4]) / 256.0; - DEBUG ("barometer: MPL3115_read, temperature = %lf C", *temperature); - + DEBUG ("barometer: MPL3115_read - temperature = %lf C", *temperature); + return 0; } -/** +/** * Initialize MPL3115 for barometeric measurements - * + * * @return 0 if successful */ static int MPL3115_init_sensor(void) @@ -888,18 +994,18 @@ static int MPL3115_init_sensor(void) __s32 res; __s8 offset; char errbuf[1024]; - + /* Reset the sensor. It will reset immediately without ACKing */ /* the transaction, so no error handling here. */ - i2c_smbus_write_byte_data(i2c_bus_fd, - MPL3115_REG_CTRL_REG1, + i2c_smbus_write_byte_data(i2c_bus_fd, + MPL3115_REG_CTRL_REG1, MPL3115_CTRL_REG1_RST); - + /* wait some time for the reset to finish */ usleep(100000); /* now it should be in standby already so we can go and configure it */ - + /* Set temperature offset. */ /* result = ADCtemp + offset [C] */ offset = (__s8) (config_temp_offset * 16.0); @@ -910,7 +1016,7 @@ static int MPL3115_init_sensor(void) sstrerror (errno, errbuf, sizeof (errbuf))); return -1; } - + /* Set pressure offset. */ /* result = ADCpress + offset [hPa] */ offset = (__s8) (config_press_offset * 100.0 / 4.0); @@ -923,7 +1029,7 @@ static int MPL3115_init_sensor(void) } /* Enable Data Flags in PT_DATA_CFG - flags on both pressure and temp */ - res = i2c_smbus_write_byte_data(i2c_bus_fd, + res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_PT_DATA_CFG, MPL3115_PT_DATA_DREM \ | MPL3115_PT_DATA_PDEF \ @@ -935,10 +1041,10 @@ static int MPL3115_init_sensor(void) return -1; } - /* Set to barometer with an OSR */ - res = i2c_smbus_write_byte_data(i2c_bus_fd, - MPL3115_REG_CTRL_REG1, - oversample_MPL3115); + /* Set to barometer with an OSR */ + res = i2c_smbus_write_byte_data(i2c_bus_fd, + MPL3115_REG_CTRL_REG1, + mpl3115_oversample); if (res < 0) { ERROR ("barometer: MPL3115_init_sensor - problem configuring CTRL_REG1: %s", @@ -949,6 +1055,327 @@ static int MPL3115_init_sensor(void) return 0; } +/* ------------------------ BMP085 access ------------------------ */ + +/** + * Detect presence of a BMP085 pressure sensor by checking its ID register + * + * As a sideeffect will leave set I2C slave address. + * + * @return 1 if BMP085, 0 otherwise + */ +static int BMP085_detect(void) +{ + __s32 res; + char errbuf[1024]; + + if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, BMP085_I2C_ADDRESS) < 0) + { + ERROR("barometer: BMP085_detect - problem setting i2c slave address to 0x%02X: %s", + BMP085_I2C_ADDRESS, + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } + + res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_ID_REG); + if(res == BMP085_CHIP_ID) + { + DEBUG ("barometer: BMP085_detect - positive detection"); + + /* get version */ + res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_VERSION ); + if (res < 0) + { + ERROR("barometer: BMP085_detect - problem checking chip version: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 0 ; + } + DEBUG ("barometer: BMP085_detect - chip version ML:0x%02X AL:0x%02X", + res & 0x0f, + (res & 0xf0) >> 4); + return 1; + } + + DEBUG ("barometer: BMP085_detect - negative detection"); + return 0; +} + + +/** + * Adjusts oversampling settings to values supported by BMP085 + * + * BMP085 supports only 1,2,4 or 8 samples. + */ +static void BMP085_adjust_oversampling(void) +{ + int new_val = 0; + + if( config_oversample > 6 ) /* 8 */ + { + new_val = 8; + bmp085_oversampling = 3; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_3; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_3; + } + else if( config_oversample > 3 ) /* 4 */ + { + new_val = 4; + bmp085_oversampling = 2; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_2; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_2; + } + else if( config_oversample > 1 ) /* 2 */ + { + new_val = 2; + bmp085_oversampling = 1; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_1; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_1; + } + else /* 1 */ + { + new_val = 1; + bmp085_oversampling = 0; + bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_0; + bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_0; + } + + DEBUG("barometer: BMP085_adjust_oversampling - correcting oversampling from %d to %d", + config_oversample, + new_val); + config_oversample = new_val; +} + + +/** + * Read the BMP085 sensor conversion coefficients. + * + * These are (device specific) constants so we can read them just once. + * + * @return Zero when successful + */ +static int BMP085_read_coeffs(void) +{ + __s32 res; + __u8 coeffs[BMP085_NUM_COEFFS]; + char errbuf[1024]; + + res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, + BMP085_ADDR_COEFFS, + BMP085_NUM_COEFFS, + coeffs); + if (res < 0) + { + ERROR ("barometer: BMP085_read_coeffs - problem reading data: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + bmp085_AC1 = ((int16_t) coeffs[0] <<8) | (int16_t) coeffs[1]; + bmp085_AC2 = ((int16_t) coeffs[2] <<8) | (int16_t) coeffs[3]; + bmp085_AC3 = ((int16_t) coeffs[4] <<8) | (int16_t) coeffs[5]; + bmp085_AC4 = ((uint16_t) coeffs[6] <<8) | (uint16_t) coeffs[7]; + bmp085_AC5 = ((uint16_t) coeffs[8] <<8) | (uint16_t) coeffs[9]; + bmp085_AC6 = ((uint16_t) coeffs[10] <<8) | (uint16_t) coeffs[11]; + bmp085_B1 = ((int16_t) coeffs[12] <<8) | (int16_t) coeffs[13]; + bmp085_B2 = ((int16_t) coeffs[14] <<8) | (int16_t) coeffs[15]; + bmp085_MB = ((int16_t) coeffs[16] <<8) | (int16_t) coeffs[17]; + bmp085_MC = ((int16_t) coeffs[18] <<8) | (int16_t) coeffs[19]; + bmp085_MD = ((int16_t) coeffs[20] <<8) | (int16_t) coeffs[21]; + + DEBUG("barometer: BMP085_read_coeffs - AC1=%d, AC2=%d, AC3=%d, AC4=%u,"\ + " AC5=%u, AC6=%u, B1=%d, B2=%d, MB=%d, MC=%d, MD=%d", + bmp085_AC1, + bmp085_AC2, + bmp085_AC3, + bmp085_AC4, + bmp085_AC5, + bmp085_AC6, + bmp085_B1, + bmp085_B2, + bmp085_MB, + bmp085_MC, + bmp085_MD); + + return 0; +} + + +/** + * Convert raw BMP085 adc values to real data using the sensor coefficients. + * + * @param adc_pressure adc pressure value to be converted + * @param adc_temp adc temperature value to be converted + * @param pressure computed real pressure + * @param temperature computed real temperature + */ +static void BMP085_convert_adc_to_real(long adc_pressure, + long adc_temperature, + double * pressure, + double * temperature) + +{ + long X1, X2, X3; + long B3, B5, B6; + unsigned long B4, B7; + + long T; + long P; + + + /* calculate real temperature */ + X1 = ( (adc_temperature - bmp085_AC6) * bmp085_AC5) >> 15; + X2 = (bmp085_MC << 11) / (X1 + bmp085_MD); + + /* B5, T */ + B5 = X1 + X2; + T = (B5 + 8) >> 4; + *temperature = (double)T * 0.1; + + /* calculate real pressure */ + /* in general X1, X2, X3 are recycled while values of B3, B4, B5, B6 are kept */ + + /* B6, B3 */ + B6 = B5 - 4000; + X1 = ((bmp085_B2 * ((B6 * B6)>>12)) >> 11 ); + X2 = (((long)bmp085_AC2 * B6) >> 11); + X3 = X1 + X2; + B3 = (((((long)bmp085_AC1 * 4) + X3) << bmp085_oversampling) + 2) >> 2; + + /* B4 */ + X1 = (((long)bmp085_AC3*B6) >> 13); + X2 = (bmp085_B1*((B6*B6) >> 12) ) >> 16; + X3 = ((X1 + X2) + 2 ) >> 2; + B4 = ((long)bmp085_AC4* (unsigned long)(X3 + 32768)) >> 15; + + /* B7, P */ + B7 = (unsigned long)(adc_pressure - B3)*(50000>>bmp085_oversampling); + if( B7 < 0x80000000 ) + { + P = (B7 << 1) / B4; + } + else + { + P = (B7/B4) << 1; + } + X1 = (P >> 8) * (P >> 8); + X1 = (X1 * 3038) >> 16; + X2 = ((-7357) * P) >> 16; + P = P + ( ( X1 + X2 + 3791 ) >> 4); + + *pressure = P / 100.0; // in [hPa] + DEBUG ("barometer: BMP085_convert_adc_to_real - got %lf hPa, %lf C", + *pressure, + *temperature); +} + + +/** + * Read compensated sensor measurements + * + * @param pressure averaged measured pressure + * @param temperature averaged measured temperature + * + * @return Zero when successful + */ +static int BMP085_read(double * pressure, double * temperature) +{ + __s32 res; + __u8 measBuff[3]; + + long adc_pressure; + long adc_temperature; + + char errbuf[1024]; + + /* start conversion of temperature */ + res = i2c_smbus_write_byte_data( i2c_bus_fd, + BMP085_ADDR_CTRL_REG, + BMP085_CMD_CONVERT_TEMP ); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem requesting temperature conversion: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + usleep(BMP085_TIME_CNV_TEMP); /* wait for the conversion */ + + res = i2c_smbus_read_i2c_block_data( i2c_bus_fd, + BMP085_ADDR_CONV, + 2, + measBuff); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem reading temperature data: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + adc_temperature = ( (unsigned short)measBuff[0] << 8 ) + measBuff[1]; + + + /* get presure */ + res = i2c_smbus_write_byte_data( i2c_bus_fd, + BMP085_ADDR_CTRL_REG, + bmp085_cmdCnvPress ); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem requesting pressure conversion: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + usleep(bmp085_timeCnvPress); /* wait for the conversion */ + + res = i2c_smbus_read_i2c_block_data( i2c_bus_fd, + BMP085_ADDR_CONV, + 3, + measBuff ); + if (res < 0) + { + ERROR ("barometer: BMP085_read - problem reading pressure data: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return 1; + } + + adc_pressure = (long)((((ulong)measBuff[0]<<16) | ((ulong)measBuff[1]<<8) | (ulong)measBuff[2] ) >> (8 - bmp085_oversampling)); + + + DEBUG ("barometer: BMP085_read - raw pressure ADC value = %ld, " \ + "raw temperature ADC value = %ld", + adc_pressure, + adc_temperature); + + BMP085_convert_adc_to_real(adc_pressure, adc_temperature, pressure, temperature); + + return 0; +} + + + +/* ------------------------ Sensor detection ------------------------ */ +/** + * Detect presence of a supported sensor. + * + * As a sideeffect will leave set I2C slave address. + * The detection is done in the order BMP085, MPL3115, MPL115 and stops after + * first sensor beeing found. + * + * @return detected sensor type + */ +static enum Sensor_type detect_sensor_type(void) +{ + if(BMP085_detect()) + return Sensor_BMP085; + + else if(MPL3115_detect()) + return Sensor_MPL3115; + + else if(MPL115_detect()) + return Sensor_MPL115; + + return Sensor_none; +} /* ------------------------ Common functionality ------------------------ */ @@ -961,7 +1388,7 @@ static int MPL3115_init_sensor(void) * - MSLP_INTERNATIONAL - see http://en.wikipedia.org/wiki/Atmospheric_pressure#Altitude_atmospheric_pressure_variation * Requires #config_altitude * - * - MSLP_DEU_WETT - formula as recommended by the Deutsche Wetterdienst. See + * - MSLP_DEU_WETT - formula as recommended by the Deutsche Wetterdienst. See * http://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Theorie * Requires both #config_altitude and temperature reference(s). * @@ -975,10 +1402,6 @@ static double abs_to_mean_sea_level_pressure(double abs_pressure) double temp = 0.0; int result = 0; - DEBUG ("barometer: abs_to_mean_sea_level_pressure: absPressure = %lf, method = %d", - abs_pressure, - config_normalize); - if (config_normalize >= MSLP_DEU_WETT) { result = get_reference_temperature(&temp); @@ -993,12 +1416,12 @@ static double abs_to_mean_sea_level_pressure(double abs_pressure) case MSLP_NONE: mean = abs_pressure; break; - + case MSLP_INTERNATIONAL: mean = abs_pressure / \ - pow(1.0 - 0.0065*config_altitude/288.15, 0.0065*0.0289644/(8.31447*0.0065)); + pow(1.0 - 0.0065*config_altitude/288.15, 9.80665*0.0289644/(8.31447*0.0065)); break; - + case MSLP_DEU_WETT: { double E; /* humidity */ @@ -1013,23 +1436,28 @@ static double abs_to_mean_sea_level_pressure(double abs_pressure) break; default: - ERROR ("barometer: abs_to_mean_sea_level_pressure: wrong conversion method %d", + ERROR ("barometer: abs_to_mean_sea_level_pressure: wrong conversion method %d", config_normalize); mean = abs_pressure; break; } - return mean; + DEBUG ("barometer: abs_to_mean_sea_level_pressure: absPressure = %lf hPa, method = %d, meanPressure = %lf hPa", + abs_pressure, + config_normalize, + mean); + + return mean; } /* ------------------------ main plugin callbacks ------------------------ */ -/** +/** * Main plugin configuration callback (using simple config) - * + * * @param key configuration key we should process * @param value configuration value we should process - * + * * @return Zero when successful. */ static int collectd_barometer_config (const char *key, const char *value) @@ -1047,7 +1475,7 @@ static int collectd_barometer_config (const char *key, const char *value) if (oversampling_tmp < 1 || oversampling_tmp > 1024) { WARNING ("barometer: collectd_barometer_config: invalid oversampling: %d." \ - " Allowed values are 1 to 1024 (for MPL115) or 128 (for MPL3115).", + " Allowed values are 1 to 1024 (for MPL115) or 1 to 128 (for MPL3115) or 1 to 8 (for BMP085).", oversampling_tmp); return 1; } @@ -1083,7 +1511,7 @@ static int collectd_barometer_config (const char *key, const char *value) { config_temp_offset = atof(value); } - else + else { return -1; } @@ -1092,18 +1520,18 @@ static int collectd_barometer_config (const char *key, const char *value) } -/** +/** * Shutdown callback. - * + * * Close I2C and delete all the buffers. - * + * * @return Zero when successful (at the moment the only possible outcome) */ static int collectd_barometer_shutdown(void) { DEBUG ("barometer: collectd_barometer_shutdown"); - if(!is_MPL3115) + if(sensor_type == Sensor_MPL115) { averaging_delete (&pressure_averaging); averaging_delete (&temperature_averaging); @@ -1122,9 +1550,9 @@ static int collectd_barometer_shutdown(void) } -/** +/** * Plugin read callback for MPL115. - * + * * Dispatching will create values: * - /barometer-mpl115/pressure-normalized * - /barometer-mpl115/pressure-absolute @@ -1142,7 +1570,7 @@ static int MPL115_collectd_barometer_read (void) value_list_t vl = VALUE_LIST_INIT; value_t values[1]; - + DEBUG("barometer: MPL115_collectd_barometer_read"); if (!configured) @@ -1204,9 +1632,9 @@ static int MPL115_collectd_barometer_read (void) } -/** +/** * Plugin read callback for MPL3115. - * + * * Dispatching will create values: * - /barometer-mpl3115/pressure-normalized * - /barometer-mpl3115/pressure-absolute @@ -1217,21 +1645,21 @@ static int MPL115_collectd_barometer_read (void) static int MPL3115_collectd_barometer_read (void) { int result = 0; - + double pressure = 0.0; double temperature = 0.0; double norm_pressure = 0.0; - + value_list_t vl = VALUE_LIST_INIT; value_t values[1]; - + DEBUG("barometer: MPL3115_collectd_barometer_read"); - + if (!configured) { return -1; } - + result = MPL3115_read(&pressure, &temperature); if(result) return result; @@ -1267,12 +1695,75 @@ static int MPL3115_collectd_barometer_read (void) } -/** +/** + * Plugin read callback for BMP085. + * + * Dispatching will create values: + * - /barometer-bmp085/pressure-normalized + * - /barometer-bmp085/pressure-absolute + * - /barometer-bmp085/temperature + * + * @return Zero when successful. + */ +static int BMP085_collectd_barometer_read (void) +{ + int result = 0; + + double pressure = 0.0; + double temperature = 0.0; + double norm_pressure = 0.0; + + value_list_t vl = VALUE_LIST_INIT; + value_t values[1]; + + DEBUG("barometer: BMP085_collectd_barometer_read"); + + if (!configured) + { + return -1; + } + + result = BMP085_read(&pressure, &temperature); + if(result) + return result; + + norm_pressure = abs_to_mean_sea_level_pressure(pressure); + + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "barometer", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, "bmp085", sizeof (vl.plugin_instance)); + + vl.values_len = 1; + vl.values = values; + + /* dispatch normalized air pressure */ + sstrncpy (vl.type, "pressure", sizeof (vl.type)); + sstrncpy (vl.type_instance, "normalized", sizeof (vl.type_instance)); + values[0].gauge = norm_pressure; + plugin_dispatch_values (&vl); + + /* dispatch absolute air pressure */ + sstrncpy (vl.type, "pressure", sizeof (vl.type)); + sstrncpy (vl.type_instance, "absolute", sizeof (vl.type_instance)); + values[0].gauge = pressure; + plugin_dispatch_values (&vl); + + /* dispatch sensor temperature */ + sstrncpy (vl.type, "temperature", sizeof (vl.type)); + sstrncpy (vl.type_instance, "", sizeof (vl.type_instance)); + values[0].gauge = temperature; + plugin_dispatch_values (&vl); + + return 0; +} + + +/** * Initialization callback - * + * * Check config, initialize I2C bus access, conversion coefficients and averaging * ring buffers - * + * * @return Zero when successful. */ static int collectd_barometer_init (void) @@ -1313,19 +1804,14 @@ static int collectd_barometer_init (void) return -1; } - if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0) - { - ERROR("barometer: collectd_barometer_init problem setting i2c slave address to 0x%02X: %s", - MPL115_I2C_ADDRESS, - sstrerror (errno, errbuf, sizeof (errbuf))); - return -1; - } - - /* detect sensor type - MPL115 or MPL3115 */ - is_MPL3115 = MPL3115_detect(); + /* detect sensor type - this will also set slave address */ + sensor_type = detect_sensor_type(); /* init correct sensor type */ - if(is_MPL3115) /* MPL3115 */ + switch(sensor_type) + { +/* MPL3115 */ + case Sensor_MPL3115: { MPL3115_adjust_oversampling(); @@ -1334,25 +1820,48 @@ static int collectd_barometer_init (void) plugin_register_read ("barometer", MPL3115_collectd_barometer_read); } - else /* MPL115 */ + break; + +/* MPL115 */ + case Sensor_MPL115: { if (averaging_create (&pressure_averaging, config_oversample)) { ERROR("barometer: collectd_barometer_init pressure averaging init failed"); return -1; } - + if (averaging_create (&temperature_averaging, config_oversample)) { ERROR("barometer: collectd_barometer_init temperature averaging init failed"); return -1; } - + if (MPL115_read_coeffs() < 0) return -1; plugin_register_read ("barometer", MPL115_collectd_barometer_read); } + break; + +/* BMP085 */ + case Sensor_BMP085: + { + BMP085_adjust_oversampling(); + + if (BMP085_read_coeffs() < 0) + return -1; + + plugin_register_read ("barometer", BMP085_collectd_barometer_read); + } + break; + +/* anything else -> error */ + default: + ERROR("barometer: collectd_barometer_init - no supported sensor found"); + return -1; + } + configured = 1; return 0; @@ -1360,15 +1869,15 @@ static int collectd_barometer_init (void) /* ------------------------ plugin register / entry point ------------------------ */ -/** +/** * Plugin "entry" - register all callback. - * + * */ void module_register (void) { - plugin_register_config ("barometer", - collectd_barometer_config, - config_keys, + plugin_register_config ("barometer", + collectd_barometer_config, + config_keys, config_keys_num); plugin_register_init ("barometer", collectd_barometer_init); plugin_register_shutdown ("barometer", collectd_barometer_shutdown);