So a while back I was designing my carputer.  Now the MX-5 is a sweet handling car, it is not particuarly fast, but it is so much fun on all those twisty country roads found all across England!  Here is an account of a complete electronics novice experience with getting to grips with the basics of integrating an accelerometer into a car pc!

I wanted to get an idea of cornering forces, and although completely pointless, I thought it would be fun to add an accelerometer to my carputer.  I quickly realised that this was not going to be a case of simply plug and play.  Most accelerometers are designed for embedded systems, this means that they don’t work with USB.  A common theme was I2C, pronounced I squared C.  This is a protocol proposed by Phillips a while ago.  It is a two wire interface, a clock line going low and high, and a data line carrying data bits in sync with the clock.

So of course I set about finding an I2C to USB convertor, these don’t really exist and are generally quite expensive.  I continued researching and stumbled across ‘Arduino’, it can do Input/Output (I am also hoping to be able to switch things on/off like my headlights etc. using my computer).  The Arduino could also talk I2C, so I bought one.  I then bought a soldering iron, solder and multimeter!

I was really quite fortunate and acquired an accelerometer from work, but something like a butchered wii remote is adequate, I suppose it depends on your required accuracy of data.


The first hurdle was powering the damn thing, my Arduino runs at slightly less than 3.3V or an equally ‘not quite’ 5v.  I needed a nice stable 1.8V.  I am new to electronics and had just learnt about potential dividers.  Incidentally I put that knowledge to good use, creating a VERY simple circuit + code to drive pin 13’s LED high when ambient light in the room drops low.

Anyway, I initially planned to create a potential divider to step down my Arduino’s voltage.  This is not the way to go, I tried and it just does not provide a suitable solution.  A loaded circuit behaves differently to one with no current draw.

I then learnt about zener diodes, they allow current to flow once a certain voltage is achieved, ‘knee point’ or something similar.  This could work but at such low voltage it is best to use a linear regulator. I got hold of a 1.8v LDO from Farnell.  A three pin affair; input voltage, ground and regualted output.  Get a nice chunky one, then when you wire it up wrong it will shut down before it dies!

An LDO needs a capacitor for stability.  The data sheet often specifies what you need as a minimum capacitance, this is for high volume low cost circuit designers.  However, I am lead to believe that if you have a bigger one lying around, there is no harm in using that.  I have a big cap (100 microfarad) on the input side aswell as a little one (10 micro farad) and then I have another big (100 micro farad) cap on the output.  The big caps are there to keep a constant flow of current as and when the LDO requires it.  The smaller cap is not strictly neccessary, but it should ensure that supply remains ripple free, (its small size facilitates good transient response).

Now I have my 1.8v I am ready to wire up the accelerometer.  I am using a freescale MMA8450Q.  The data sheet found on freescale’s site shows you where each of it’s 16 pins need to go, following this sheet can be tiresome but I bought some stripboard and a few 8 row dual inline (DIL) plugs and set to work.

Wiring it up

Something I have failed to mention so far, the I2C bus supports multiple slaves. That means you can hook loads of devices up and read from each one seperately using only two wires connected to the slave (the Arduino of course!).  So I bought a temperature sensor , now I can pretend I drive one of those cars that has a sensor for the outside temperature.

The final piece of hardware clearly visible in my completed circuit pictured below, is a logic level convertor.  It consists of a mosfet and enables me to run logic at two different voltages.  Basically my Arduino will be running at 5v supply and hence it sees a logic level of ‘1/on’ as anything in excess of 3v for example.  My 1.8v digital devices on the other hand, need only 1.5v for example, to denote a logical 1.  The logic level convertor is the elegant solution to this nasty issue!

Before producing the circuit you see before you,  I sketched a rough layout to ensure I could fit everything I wanted in the available space –  I needed it to fit neatly inside a disguarded Ferrero Rocher case of course!  I didn’t keep to the plan it kind of evolved as I was making it.

A tip that may or may not be common knowledge.  I found that a good way to mount a flat, off the shelf  integrated circuit (IC ) breakout board like the one I am using (pictured).

Chop the legs off a resistor and then run it through the breakout board’s hole, then solder the leg to that and then onto the strip board.  Look closely and you will see what I mean, I have left the odd leg poking out!

My stripboard circuit was designed in such a way that the I2C lines were available as an expandable bus.  By this I mean that the two wires (clock and serial) ran the full width of the board.  You can make out two resistors in the finished picture, these are the pullups for each line and the wires are colour coded as white= SCL and red = SDA.  Now in theory I can add further sensors to the bus as and when.  The pullup resistors are present because the I2C lines are designed to remain in a logic high state, these ensure that the line is pulled high.  The sensors in my circuit are only able to pull things to ground, they cannot pull the lines high.

Following the data sheet I cut and soldered each of the 16 pins to their appropriate destinations and tried it out.

Firing it up

First off it didn’t work, this was a result of sloppy workmanship.  Using a multimeter it was apparent that something wasn’t quite right.  Further digging revealed the first error- I had failed to snip the excess wire off one of my components after soldering, this meant that the data line was shorted out to ground.  Having solved that issue I tried again.  Voltages were now as expected.  I read a few tutorials online and used the wire library to perform some read/writes to my accelerometer.  This failed.  After many frustrated hours I READ THE DATASHEET very carefully.  Basically the wire library was unable to support my hardware.  The accelerometer uses ‘repeated start’ this is something unsupported by the wire library so after some searching I discovered another I2C library which quite frankly is far far better than wire.  It is provided free by Peter Fleury available on his website here.  Using the data sheet and this excellent library I was able to interface with the accelerometer and the temperature sensor.

The code

And finally, the last piece in the puzzle- the exciting bit.  We write some code, compile and upload onto the Arduino’s Atmega 328 chip.  So first off I downloaded my new I2C library and added it to the libraries folder in my Arduino directory.  I think I might have changed the c file extension to a .cpp in order to be compatible with the arduino’s compiler.

Be sure to include the relevant c++ library.  Read your datasheet to establish the address of the device you wish to talk to.  The accel chip is addressed as 0x0D and temp chip addressed as 0x48.  This is #defined at the top of my sketch, a method of letting the compiler know to assign a constant value to an identifier.  Here is a code snippet with most of the variables I #defined…

#include <i2cmaster.h>

#define ACCEL_ADDRS (0x1D << 1) //This is the MMA8450Q device address when pin 7 is high.
#define ACCEL_CTRL_REG1 (0x38) //This is the system control register
#define standby (0x00) //stops measuring, used to configure device.
#define RESOLUTION (0x01) //Sets to 2g mode 0 1
#define ODR (0x03 << 2) //Set the output data rate 3 = 50Hz
#define WHO_AM_I (0x0F)
#define XYZ_DATA_CFG (0x16) //Configure the ‘data ready’ register
#define REG_STATUS (0x04) //This gets set when data is available and reset when data is read

//temperature sensor
#define TEMP_ADDRS (0x48 << 1) //This is the temp device address
#define TEMP_REG (0x00) //This is the temperature register

Now avoid blindly copying my code, you will be better reading how to use the library and applying that to your application.  Check your data sheet and try to understand what is going on.  That being said I will proceed with what I got up to in this project…

The Standby and Active modes are controlled by the last two bits of the System
Control 1 Register (at 0x38), FS1 and FS0.
[0 0 = Standby], [0,1 = Active 2g mode], [1,0 = Active 4g], [1,1 = 8g]
#define OUT_X_LSB (0x05) //X Register
#define OUT_X_MSB (0x06) //Y Register
#define OUT_Y_LSB (0x07) //Z Register
#define OUT_Y_MSB (0x08)
#define OUT_Z_LSB (0x09)
#define OUT_Z_MSB (0x0A)

int MSB_x = 0;
int MSB_y = 0;
int MSB_z = 0;
int LSB_x = 0;
int LSB_y = 0;
int LSB_z = 0;
int X_data = 0;
int Y_data = 0;
int Z_data = 0;
int xx_dat = 0;
int yy_dat = 0;
int zz_dat = 0;
int wire_debug = 1;
int running2g = 0;
int data_ready = 0;
int looopcount = 0;
int xyz_event = 80;
bool temp_sensor_read = 0;
double ambient_temp = 0;
unsigned long tot_accel = 0;
unsigned long avg_accel = 0;
int array_pos = 0;
const int avg_size = 10;
unsigned int accel_now[avg_size]; //set size of array for storing readings
bool movement_detected = 0; //false means vehicle is stationary
unsigned int roll_reset = 0;
unsigned short time_stamp;

void setup()

//  Serial.println(“Serial running”);
i2c_init(); //Sets up HW pins for I2C

/*Initialise the sensor, put all configs here before we activate */

i2c_start_wait(ACCEL_ADDRS + I2C_WRITE); //Accelerometer device address

//Setup for polling mode
i2c_start_wait(ACCEL_ADDRS + I2C_WRITE);

/* All configs should be above, as we are going online now. */
i2c_start_wait(ACCEL_ADDRS + I2C_WRITE); //Accelerometer device address
wire_debug = i2c_write(ACCEL_CTRL_REG1); //This addresses the appropriate register
if (wire_debug == 0) //Success
i2c_write(ODR + RESOLUTION);
if (RESOLUTION == 1)
running2g = 1;
Serial.println(“failed to wake the ol’ girl up”); //Error trap

for (int thisReading = 0; thisReading < avg_size; thisReading++)
accel_now[thisReading] = 0; //ensures the array is full of 0s when we start
} // end setup()

I chose to go for a fairly quick serial baud rate, anything quicker and my frontend gui was overwhelmed.  The HW pins 4&5 on the Arduino are lost as an I2C interface when we send i2c_init(); Next I address the sensor and tell it to expect a write.  Then I send the register address.  Then I send the value intended to be written to the defined register on the defined slave address.  Various configurations are then set – the data rate for example.  Now onto the main programme which runs indefinately.

So we head into the loop.  I am still developing bits and pieces, this code has a timestamp which is to be passed across the serial for my GUI, in future I expect that will go and I will keep time on the pc side.  My data ready flag is initialised as 0 and is set high by the accelerometer everytime the accel reports it has data on the XYZ registers.  A while loop is perfect for polling.  It will continually keep checking if data is ready until this flag is not 0.  The commented section was used during debugging for establishing how many times I could run the while loop before data was ready.  A significant number of iterations of the while loop was possible, so I decided to go and read from my temperature sensor.  To avoid doing this too often I set a boolean high once temperature has been obtained, this flag gets set once outside of the main while loop.

void loop()
time_stamp = millis();
while (data_ready == 0)
i2c_start_wait(ACCEL_ADDRS + I2C_WRITE); //Accelerometer device address
i2c_write(REG_STATUS); //Register with data ready flag
i2c_rep_start(ACCEL_ADDRS + I2C_READ);
data_ready = i2c_readNak();
//looopcount = looopcount + 1;
// Expect a min of 1422 iterations in 2g mode at 1.5 Hz
// N.B. 1422 even after reading from temp sensor!
// This gives us a great window of opportunity for temp readings!!
if (temp_sensor_read == 0)
ambient_temp = get_temperature();
temp_sensor_read = 1;
// Serial.println(looopcount); //for debug
//looopcount = 0;
data_ready = 0; //Resets flag for next loop iteration.
temp_sensor_read = 0;  //Set this to 0 to tell function to read temp sensor

i2c_start_wait(ACCEL_ADDRS + I2C_WRITE);
i2c_rep_start(ACCEL_ADDRS + I2C_READ);

Binary hex and dec

Onto some bit shifting.  Computers can’t strictly count, they can recognise if voltage levels are high or not, (on or off).  This is why for a computer all numbers must be made up of only ones and zeroes, for example, 5 = 101 in binary.  Basically we are working with a number system to base 2.

An unsigned 8 bit number has the possibility of representing 255 integers (as well as 0), that is, 2^8.  So for 5 we have 1.(2^2) + 0.(2^1) + 1.( 2^0), giving 101.  Unsigned means the number can’t go negative.  If you want negative you have to use a signed integer.  An 8 bit signed number ranges between 127 to -127, we have 7 bits available for the value with the final left most (most significant bit) available for the sign.  Check out two’s complement for a more comprehensive description.

Bit shifting is very handy.  Hopefully you understand that numbers are represented as binary at the very basic level on the Arduino chip.  Programmers often use hexadecimal, hex for short, this is purely for simplicity when writing code.  It has ended up, for whatever reason that bits are often packaged as bytes, 8 bits are in a byte.  One byte occupies only 2 characters in hex.  Theoretically we could write 1111 1111 (this is what it will look like on the chip) or we could say 255 in decimal (base 10 is generally used in the real world), but FF in hex is the least cumbersome of the three, despite all three having the same value.

In my code I receive 12 bits or 1.5 bytes for each direction (1 byte & 1 nibble).  In order to arrange it meaningfully into one number of the correct magnitude I must do some bit shifting.  It’s like if you shift the decimal number 10 left, we add a zero to the end and it becomes 100.  The same happens in binary.  So if we shift b101 left: 101<<1 it becomes 1010, so we now have decimal 10.

That being said, the data is read using a multibyte read, this reduces the number of physical writes required as the accelerometer chip will auto increment through its registers, (rather than wasting time and resource using the Arduino to request data sequentially for each register).

//Let’s start a multi byte read… (use the datasheet for the order!)
LSB_x = i2c_readAck() << 4;
MSB_x = i2c_readAck() << 8;
LSB_y = i2c_readAck() << 4;
MSB_y = i2c_readAck() << 8;
LSB_z = i2c_readAck() << 4;
MSB_z = i2c_readNak() << 8;

X_data = get_accel_data(MSB_x,LSB_x);
xx_dat = X_data / 10;
Y_data = get_accel_data(MSB_y,LSB_y);
yy_dat = Y_data / 10;
Z_data = get_accel_data(MSB_z,LSB_z);
zz_dat = Z_data / 10;

/*I want to keep a rolling average of sum of squares for say 10 ticks at 200 Hz */

tot_accel = tot_accel – accel_now[array_pos]; //allows us to keep rolling!
accel_now[array_pos] = (xx_dat*xx_dat + yy_dat*yy_dat + zz_dat*zz_dat);
tot_accel = tot_accel + accel_now[array_pos];
if (array_pos >= avg_size)
array_pos = 0;
avg_accel = tot_accel/avg_size;

/*Now we have an average, lets check if we think we are stationary! */

if(  ((accel_now[array_pos] + avg_accel) / 2) > (avg_accel)* 1.05 || ((accel_now[array_pos] + avg_accel) / 2) < (avg_accel)* 0.95  )
movement_detected = 1;
movement_detected = 0;


This pretty much completes it.  I am developing a quick and easy movement detected algorithm using the sum of squares to identify if acceleration is abnormal compared with the stationary average.

I push each value out on the serial port.  It is seperated using a ‘|’ this makes it easy to ID where each piece of data resides when using it in the GUI.  The raw data is gathered and arranged using a function, you can see it being called in the code above, ‘get_accel_data’ for example.  This and the temperature data is obtained using the functions written after the void loop() as follows:

double get_temperature()
double TEMP_DATA = 0; // Contains the temperature intended to be displayed as a decimal
int TEMP_DATA_L = 0; //Low byte of the form xxxx 0000 where 0s are not populated
int TEMP_DATA_H = 0; //High byte containing complete data set MSB contains sign info 0 = +ve

i2c_start(TEMP_ADDRS + I2C_WRITE); //Accelerometer device address
i2c_write(TEMP_REG); //Setup for a read from device’s appropriate register
i2c_start(TEMP_ADDRS + I2C_READ);
TEMP_DATA_H = i2c_readAck() << 4;
TEMP_DATA_L = i2c_readNak() >> 4;
return TEMP_DATA;

int get_accel_data(int MSB, int LSB)
int raw_data = MSB | LSB; //This is a 16 bit number with last 4 bits redundant

// The arduino is already performing 2’s complement maths on a 2 byte word.
// So once the data is arranged as a word, we can shift if back to 12 bits.

//Get rid of the first 4 bits which are always zero.
raw_data = raw_data / (B1000);
return raw_data; //2024 counts for 1 g.

There we have it.  Hopefully I will post up how I get on with the GUI and how I get on with it in my car very soon.  Questions and comments welcomed, have fun if you try something similar!

  1. […] I still have not re-partioned my HDD, however, I have just published to my blog, the first steps towards integrating an accelerometer with my car PC… I wanted to get an idea of […]

    • tim says:

      I’m concerned with your shifting 4 bits instead of eight to complete a 16 bit number… It is Least Significant BYTE… not NIBBLE (4-bits)… Aren’t you trying to complete at least a 10 through 13 bit number?… It seems that the MSByte should be shifted eight bits to the upper half of a 16 bit word and the LSB OR’ed in to the lower half… or did you use the left justification command and I am just missing it?… Anyway… thanks for posting this and I hope it works out for you…

      • thoughtdraw says:

        The MMA8450Q accelerometer has 12 bit precision. The arduino grabs data from device in this bit of code:

        LSB_x = i2c_readAck() << 4;
        MSB_x = i2c_readAck() << 8;

        I now have two int variables LSB_x and MSB_x containing data.
        Actually in retrospect I think it would have been more appropriate to left shift MSB_x <<4 rather than fiddling with it back and forth, hence the /B1000 and probably fixing it elsewhere in the code):
        The function, get_accel_data:
        int get_accel_data(int MSB, int LSB)
        int raw_data = MSB | LSB; //This is a 16 bit number with last 4 bits redundant

        // The arduino is already performing 2′s complement maths on a 2 byte word.
        // So once the data is arranged as a word, we can shift if back to 12 bits.

        //Get rid of the first 4 bits which are always zero.
        raw_data = raw_data / (B1000);
        return raw_data; //2024 counts for 1 g.

        Coming back to this, I am a bit confused at what is going on – it appears to work though! thanks for the comment

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s