Michael Caplan

This project was completed for a class, and our final project was delivered in the form of a GitHub repository tagged for release.

I’ve linked that here: github.com/ENGN2912B-2018/EE-A. It has a complete readme detailing the scope of the project and all of the work that we did in different areas.

In this context, it makes sense to talk about what I did on the project, which revolved around communication between an ADXL345 accelerometer and a Raspberry Pi computer.

Up and Running

The beginning stages of my work revolved around the ADXL345 datasheet, a 40 page masterpiece covering all of the minute details involved in using this full-featured MEMS accelerometer from Analog Devices. We decided to use SPI over I2C for maximum transfer speed.

Spi Timing

I tried to use the WiringPi library, but kept getting garbage data back from the device. A little digging revealed that there are multiple SPI modes, and WiringPi only has Mode 0 baked in. So, I switched to the BCM2835 library, which proved to be better.

After I confirmed I could read/write, I had to peruse the register map and figure out which bits I needed to set in order that the accelerometer would operate the way I wanted.

Register Map

Refining Communications

I read in the top of the datasheet that the ADXL345 would return 10 bit integers split over two bytes, so I naively thought I could just bitshift the chars I received from the SPI read, cast to an integer, and then remap the int to a meaningful acceleration figure. If the accelerometer had a +-2g range, and 210 = 1024, then if I received 512 back on the x axis, I had 0g acceleration.

This wasn’t working and I was getting very nervous that I had a deep implementation error, until a team member pointed me to one of the last pages of the datasheet, which showed exactly how the data was coming back.

Register Map

Wrapping SPI

When communication was working well, I needed to wrap everything in an easy-to-use class interface. My teammate had done the truly admirable work of getting a Kalman Filter implementation up and running, and we wanted to be able to filter the data as close to real-time as possible. In order to achieve this, I decided it would make sense if a separate thread would spin up, read SPI, and put everything in a queue for the Kalman filter to pull samples from.

Because we were in a single producer-single consumer situation, there were minimal race conditions, so I was able to use a lockless queue. I borrowed a wonderful circular FIFO instantiation from Kjell Hedstrom. The source is here and a great explanation of how it uses std::atomic variables is here.

The std::thread library let me easily create a new thread (perks of being in C++), and I got the queue working.

Conclusions

Our device worked much better than I thought it would, but it is not a surprise to anyone familiar with dead-reckoning techniques that our success was limited. The problem is gravity. The accelerometer works within the inertial frame of the earth, and thus there is a constant gravity vector pointing towards the core of the planet. Of course, with a perfectly aligned accelerometer, this offset could be calibrated. However, as soon as the device rotates the gravity vector projects onto our accelerometer x, y, and z vectors. We tried to solve this with a simple moving average, but ultimately we would have to implement some sort of rotation sensing and utilize euler angles to track and calibrate for the gravity vector. A great resource is here.

The full conclusions and all the code are in the github repository linked at the top. If you are interested, I suggest you read that.