Saturday 22 August 2015

Getting the BMP085/BMP180 pressure sensor working with EMFCamp's Tilda MKe badge

So I've been looking at making something useful out of my Tilda MKe badge from last years EMF Camp for some time now. With its nice (backlit) graphical LCD, I decided it'd be excellent to use as a temperature and atmospheric pressure display for my workshop at work. When you're calibrating these or these or these, you need to compensate for both to get an accurate distance measurement.

I bought a cheap as BMP180 sensor (it's a pin & software compatible updated version of the BMP085) from eBay for £1.19 and hooked it up to the first working Arduino I could find (which was about the third one I tried - really need to fix the others) and got out glorious pressure and temperature data over the serial port with the help of the Adafruit library.

Great! Getting this going on the Tilda should be easy as. I knew from a bit of tinkering with the badge I did on site over the EMF Camp weekend that they were using an RTOS and they'd helpfully added a 'Hello World' app in the firmware for hacking purposes. I quickly modified the Hello World app to display the temperature.

It didn't work.

Huh. Somewhere in the back of my mind, I recalled that the Arduino Due (which the Tilda is based on) has two I2C interfaces. My first thought, confirmed by a quick look at the schematic, was that perhaps the Tilda's SDA and SCL pins that were brought out on the Arduino header weren't connected to the first I2C interface, which is what would be referred to when you normally use the wire library.

No worries. I just had to modify the Adafruit library to use wire1 instead of wire. A bit of find/replace and I was getting temperature data displayed on the LCD. Yay.

From here, it should just be a case of adding in the call to read and display the pressure.

It didn't work.

But not in the way I had expected. I was geting some pressure data, but it was way, way too low. Like it thought I was up at 40,000m kind of atmospheric pressure.

I spent a bit of time futzing about with various things. I knew it had to be related to the Tilda somehow so I looked into the RTOS and delved deep into the BMP085 datasheet. 

To measure the pressure with the BMP085 takes three steps:
  • Firstly, each unit is individually calibrated at manufacture. The various calibration registers need to be read and their values saved for later calculations.
  • The pressure value is temperature sensitive as well, so the (uncompensated) temperature needs to be read from the chip and the true temperature calculated with the calibration data.
  • Lastly, read the uncompensated pressure value and, with the aid of the calibration data and the compensated temperature, you can go through a 15-step calculation to arrive at the correct pressure.
I read in the datasheet that after requesting the pressure the chip can take up to 25ms for the ADC to convert the reading. I had a look in the Adafruit library:
  if (oversampling == BMP085_ULTRALOWPOWER)
    delay(5);
  else if (oversampling == BMP085_STANDARD)
    delay(8);
  else if (oversampling == BMP085_HIGHRES)
    delay(14);
  else
    delay(26);
I knew that doing this kind of blocking delay would stop background tasks in the RTOS from working so I changed them all to Tilda::delay() as suggested, and crossed my fingers.

It didn't work. I was getting a little bit annoyed at this point, so decided to take a little several week break.

When I came back to it I continued looking at the way the Tilda was reading in the pressure data from the BMP085. Since the temperature was being calculated correctly it didn't point to an error in the calibration data or temperature reading.

To confirm that it wasn't a hardware problem with my Tilda, I decided to try programming it as an Arduino (ie, without the RTOS). Unfortunately, if you try this in the latest Arduino software (currently 1.6.4) you'll get a heap of errors like this:
/home/peter/Downloads/Mk2-Firmware-master/hardware/emfcamp/sam/variants/tilda_mke_v0.333/variant.cpp:342:55: note: candidates are:
In file included from /home/peter/Downloads/Mk2-Firmware-master/hardware/emfcamp/sam/variants/tilda_mke_v0.333/variant.h:43:0,
                 from /home/peter/Downloads/Mk2-Firmware-master/hardware/emfcamp/sam/variants/tilda_mke_v0.333/variant.cpp:24:
/home/peter/.arduino15/packages/arduino/hardware/sam/1.6.4/cores/arduino/UARTClass.h:45:5: note: UARTClass::UARTClass(Uart*, IRQn_Type, uint32_t, RingBuffer*, RingBuffer*)
The easiest solution was to use an older version of Arduino. I had a guess at which one they'd used to write the Tilda software in the first place and picked version 1.5.7, which worked fine.

I used the test sketch from the Adafruit BMP085 library (after modifying it to use wire1 again) and I was getting the correct temperature and pressure data out of the serial port!

Now I knew it wasn't a hardware problem, I needed to see the communication between the Tilda and the BMP180, and I had exactly the tool for the job, having recently acquired a Rigol DS1054Z Oscilloscope.

I fired it up and was quickly able to probe the SCL (clock, top in yellow) and SDA (data, bottom in blue)


Zooming in on the last burst, we can use the DS1054's decode function to see what all these blips actually correspond to:


This is with the Tilda in Arduino mode - the data being read is 0xA26C20 (decimal 10644512). 


Here's with the Tilda back running my HelloWorld app in the RTOS. The data read here is 0xA277C0 (decimal 10647488). The difference is only about 0.03%. Surely not enough to account for my massive error.

Obviously I was now going to have to delve into the calculations. Here's the page in the datasheet that describes the procedure:


Err... that looks complicated. I ended up making a spreadsheet to help. I plugged both uncorrected pressure values into the spreadsheet and got consistent pressure values out. Well fuck. I was pretty sure now that the bug was somewhere deep in the RTOS and I really didn't think I was going to be able to find it. Unless.... 

I'd been working on the assumption that the calibration data was being read ok because the temperature was being calculated correctly. However, after working through the calculation in my spreadsheet I knew that most of the calibration data doesn't get used until the pressure calculation.

I got the Tilda to spit out the calibration data in both Arduino mode and the RTOS and lo and behold, the values for AC1 and AC5 differed markedly. I guessed that some background task in the RTOS was interfering with the read. I had a stab at fixing it by adding in a delay into the library:
  Wire1.begin();

  if (read8(0xD0) != 0x55) return false;
 
  Tilda::delay(5);

  /* read calibration data */
  ac1 = read16(BMP085_CAL_AC1);
  ac2 = read16(BMP085_CAL_AC2);
  ac3 = read16(BMP085_CAL_AC3);
  ac4 = read16(BMP085_CAL_AC4);
  ac5 = read16(BMP085_CAL_AC5);
  ac6 = read16(BMP085_CAL_AC6);
Which worked perfectly! All of that time and frustration was fixed by a 5ms delay in the right place, apparently. 


Niiiiiiiice! I'm not exactly convinced the temperature is 100% correct. It seems to be a couple of degrees warmer than I think it should be. The pressure looks to be correct though. 

Next time: Making it into a usable barometer.