Quantcast
Channel: Pimoroni Buccaneers - Latest posts
Viewing all articles
Browse latest Browse all 53895

Strange Barometer behaviour

$
0
0

This is my least favourite module in terms of getting the convoluted calculations right. I made some changes to try and fix the temperature data, which appear to have broken basically everything.

I have a hunch what the problem might be, so I've applied a "fix" and humbly submit Firmware 1.13 for testing: http://get.pimoroni.com/flotilla-dock-113.hex

For anyone interested, or good at bug hunting, this is basically the code as it stands now. It's full of type traps, where things could be cast to the wrong signedness.

#define WEATHER_ADDRESS 0x77
#define WEATHER_TEMP_THRESHOLD_DELTA 5
#define WEATHER_PRESSURE_THRESHOLD_DELTA 50

#define BMP280_REGISTER_CHIPID              0xD0
#define BMP280_REGISTER_VERSION             0xD1
#define BMP280_REGISTER_SOFTRESET           0xE0
#define BMP280_REGISTER_STATUS              0xF3 // bit 3 set to 1 when conversion is running
#define BMP280_REGISTER_CONTROL             0xF4
#define BMP280_REGISTER_CONFIG              0xF5
#define BMP280_REGISTER_TEMPDATA            0xFA // Temp MSB ( 3 bytes ) 0xFA, 0xFB, 0xFC
#define BMP280_REGISTER_PRESSUREDATA        0xF7 // Pressure MSB ( 3 bytes ) 0xF7, 0xF8, oxF9
#define BMP280_REGISTER_READTEMPCMD         0x2E
#define BMP280_REGISTER_READPRESSURECMD     0x34
#define BMP280_REGISTER_DIG_T1		    0x88 // Unsigned Short
#define BMP280_REGISTER_DIG_T2		    0x8A // Signed Short
#define BMP280_REGISTER_DIG_T3		    0x8C // Signed Short
#define BMP280_REGISTER_DIG_P1		    0x8E // Unsigned Short
#define BMP280_REGISTER_DIG_P2		    0x90 // Signed Short
#define BMP280_REGISTER_DIG_P3		    0x92 // Signed Short
#define BMP280_REGISTER_DIG_P4              0x94 // Signed Short
#define BMP280_REGISTER_DIG_P5              0x96 // Signed Short
#define BMP280_REGISTER_DIG_P6              0x98 // Signed Short
#define BMP280_REGISTER_DIG_P7              0x9A // Signed Short
#define BMP280_REGISTER_DIG_P8              0x9C // Signed Short
#define BMP280_REGISTER_DIG_P9              0x9E // Signed Short

static int32_t bmp280_read_config(unsigned char reg){
	if( reg == BMP280_REGISTER_DIG_T1 || reg == BMP280_REGISTER_DIG_P1 ){
		return (uint16_t)i2c_read_word_lsb_first(WEATHER_ADDRESS,reg);
	}
	else
	{
		return (int16_t)i2c_read_word_lsb_first(WEATHER_ADDRESS,reg);
	}
}

// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
static int32_t bmp280_compensate_T_int32(long signed int adc_T)
{
	int32_t var1, var2;
	
	int32_t t1 = bmp280_read_config(BMP280_REGISTER_DIG_T1);
	
	var1  = ((((adc_T>>3) - (t1 <<1))) *
			(bmp280_read_config(BMP280_REGISTER_DIG_T2))) >> 11;

	var2  = (((((adc_T>>4) - (t1)) *
			((adc_T>>4) - (t1))) >> 12) *
			(bmp280_read_config(BMP280_REGISTER_DIG_T3))) >> 14;

	return var1 + var2;
}

// Returns pressure in Pa as unsigned 32 bit integer. Output value of “96386” equals 96386 Pa = 963.86 hPa
static uint32_t bmp280_compensate_P_int32(long signed int adc_P, long signed int t_fine)
{
	int64_t var1, var2, p;

	var1 = ((int64_t)t_fine) - 128000;
	var2 = var1 * var1 * (int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P6);
	var2 = var2 + ((var1*(int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P5))<<17);
	var2 = var2 + (((int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P4))<<35);
	var1 = ((var1 * var1 * (int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P3))>>8) +
	((var1 * (int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P2))<<12);
	var1 = (((((int64_t)1)<<47)+var1))*((int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P1))>>33;
	  
	if (var1 == 0) {
		return 0;  // avoid exception caused by division by zero
	}
	p = 1048576 - adc_P;
	p = (((p<<31) - var2)*3125) / var1;
	var1 = (((int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P9)) * (p>>13) * (p>>13)) >> 25;
	var2 = (((int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P8)) * p) >> 19;

	p = ((p + var1 + var2) >> 8) + (((int64_t)bmp280_read_config(BMP280_REGISTER_DIG_P7))<<4);
	return p/256;
}

static int32_t read_int32(unsigned char reg)
{
	int32_t v = i2c_read_word(WEATHER_ADDRESS, reg);
	return (v<<4) + (i2c_read_byte(WEATHER_ADDRESS, reg+2) >> 4);
}

static bool is_connected(void) {
	return i2c_slave_present(WEATHER_ADDRESS);
}

static void init(channel_t *channel) {
	i2c_write_byte(WEATHER_ADDRESS, BMP280_REGISTER_CONTROL, 0b01001001);
}

static bool tick(channel_t *channel) {

	if((i2c_read_byte(WEATHER_ADDRESS, BMP280_REGISTER_STATUS) & 0b00001000) == 0){
		
		int32_t temp = bmp280_compensate_T_int32(read_int32(BMP280_REGISTER_TEMPDATA));
		uint32_t pressure = bmp280_compensate_P_int32(read_int32(BMP280_REGISTER_PRESSUREDATA),temp);
		
		int32_t last_temp = *(int32_t *)(&channel->data[0]);
		uint32_t last_pressure = *(uint32_t *)(&channel->data[2]);

		temp = (temp * 5 + 128) >> 8;

		if( abs(temp - last_temp) > WEATHER_TEMP_THRESHOLD_DELTA || abs(pressure - last_pressure) > WEATHER_PRESSURE_THRESHOLD_DELTA){
			serial_printf_P(PSTR("u %d/%s %ld,%lu\r\n"), channel->id, channel->module_type->name, (int32_t)temp, (uint32_t)pressure); 
		
			*(int32_t *)(&channel->data[0]) = temp;
			*(uint32_t *)(&channel->data[2]) = pressure;

			return true;
		}

		// Force next conversion
		i2c_write_byte(WEATHER_ADDRESS, BMP280_REGISTER_CONTROL, 0b01001001);
	}
	
	return false;
}

Viewing all articles
Browse latest Browse all 53895

Trending Articles