MicroPython Driver for ADXL345 Accelerometer
Contents
Introduction
This a MicroPython driver written specifically for the BBC micro:bit that will work with the ADXL345 accelerometer sensor.
The ADXL345 sensor is discussed in some detail here.
FIG 1 - ADXL345 breakout board: [Left] front view, [Right] rear view
The sensor is quite small and for projects using a breadboard its much easier to use a breakout board. The breakout boards are not particularly expensive.
Connecting the ADXL345
This sensor has both I2C and SPI serial interfaces available. This driver will use I2C. Connecting the breakout board to the micro:bit is simple.
| micro:bit | Sensor board |
|---|---|
| 3.3V | VCC |
| GND | GND |
| Pin 19 | SCL |
| Pin 20 | SDA |
This utilises the standard I2C pins of the micro:bit.
Optionally, a jumper wire can be placed between the VCC pin and the SDO pin on the breakout board. This changes the default I2C address and is discussed below.
Driver Overview
The driver implements the base functionality (i.e. acceleration measurements) offered by this sensor. Also included is a simple tool for measuring angles from the horizontal.
The FIFO and all interrupts (with the exception of Data Ready) are not available in this driver.
Driver codeThe driver code can be:
- Copied from this webpage onto the clipboard then pasted into the MicroPython editor e.g. the Mu Editor. It should be saved as fc_adxl345.py - OR -
- Download as a zip file using the link. Unzip the file and save it as fc_adxl345.py into the default directory where the MicroPython editor e.g. Mu Editor saves python code files.
After saving the fc_adxl345.py file to the computer it should be copied to the small filesystem on the micro:bit. The examples on this page will not work if this step is omitted. Any MicroPython editor that recognises the micro:bit will have an option to do this.
The Mu Editor is recommended for its simplicity. It provides a Files button on the toolbar for just this purpose.
I2C addressADXL345 breakout boards have a default I2C address of 0x53. This can be changed to 0x1D if a jumper wire is connected between the VCC pin and SDO pin.
Class constructorThe driver is implemented as a class.The first thing to do is call the constructor of the ADXL345 class to obtain a sensor object.
Syntax:
ADXL345(ADDR=0x53)
Where:
ADDR is the I2C address.
Example
from fc_adxl345 import *
# Declare a ADXL345 sensor object.
# The default I2C address is used.
sensor = ADXL345()
This assumes that the file fc_adxl345.py has been successfully copied to the micro:bit's filesystem as described above.
Methods and PropertiesThe driver provides methods and properties:
-
Setup sampling parameters:
SetODR(), GetODR
SetGRange(), GetGRange -
Read acceleration:
Reading, X, Y, Z, IsDataReady -
Angle measurement:
Xangle, Yangle -
Chip ID:
ID
Sampling Options
The user has the option to set the output data rate (ODR) and measurement range.
Syntax:
SetODR(ODR = 10)
Sets the output data rate (ODR).
One of 0 to 11.
This provides a range from 0.1Hz to 200Hz.
See Table 7 in the datasheet.
GetODR
Returns the ODR in use.
One of 0 to 11.
SetGRange(Range=2)
Sets the measurement range.
One of 2, 4, 8, 16.
GetGRange
Returns the measurement range in use.
One of 2, 4, 8, 16.
Example:
from fc_adxl345 import *
# Declare a ADXL345 sensor object
# The I2C address is 0x53 (default).
sensor = ADXL345()
# Report the default sampling options.
odr_freq = (0.10, 0.20, 0.39, 0.78, 1.56, 3.13,
6.25, 12.5, 25, 50, 100, 200)
odr = sensor.GetODR
print('ODR:', odr_freq[odr], 'Hz')
print('Range: +/-', sensor.GetGRange, 'g')
# Change ODR and measurement range.
sensor.SetODR(11) # 200Hz
sensor.SetGRange(4) # +/-4g
print('\nNew settings...')
odr = sensor.GetODR
print('ODR:', odr_freq[odr], 'Hz')
print('Range: +/-', sensor.GetGRange, 'g')
Output:
ODR: 100 Hz
Range: +/- 2 g
New settings...
ODR: 200 Hz
Range: +/- 4 g
Measuring Acceleration
It's a very simple process to read acceleration values from the sensor. The acceleration values have units of g (1g = 9.8 m/sec2).
The driver uses Full Resolution mode.
Syntax:
Reading
Property that returns acceleration in all three axis.
Values are returned in a tuple in the follow order:
(X-axis, Y-axis, Z-axis)
X
Property that returns the acceleration in the X-axis.
Y
Property that returns the acceleration in the Y-axis.
Z
Property that returns the acceleration in the Z-axis.
IsDataReady
Property returns True if the sensor
has new data ready for reading.
Example:
from fc_adxl345 import *
# Declare a ADXL345 sensor object
sensor = ADXL345()
# Read acceleration of all three axis.
acc = sensor.Reading
print('(X-axis, Y-axis, Z-axis):', acc)
# Read acceleration separately from each axis.
# Wait till data is ready.
while not sensor.IsDataReady:
sleep(1)
print('\nX-axis:', sensor.X)
print('Y-axis:', sensor.Y)
print('Z-axis:', sensor.Z)
Typical Output:
(X-axis, Y-axis, Z-axis): (0.0117, 0.0546, 0.936)
X-axis: 0.0039
Y-axis: 0.0507
Z-axis: 0.8814
Product ID
The ADXL345 has a product ID burnt into non-volatile memory at time of manufacture. This driver provides a simple property to read this ID.
Syntax:
GetID
Returns the product ID
Example:
from fc_adxl345 import *
sensor = ADXL345()
print('ADXL345 product ID:', sensor.GetID)
Output:
ADXL345 product ID: 0xe5
Measuring Angles
Accelerometers measures:
- static : The sensor is stationary but is under the influence of the Earth's gravitational field
- dynamic : The component of acceleration resulting from the motion or changes in velocity experienced by the sensor over time.
When the accelerometer is stationary and lying perfectly perpendicular to the direction of the Earths gravity, the X-axis and Y-axis acceleration will both be 0g. The Z-axis acceleration will be 1g.
This fact along with some simple trigonometry can be used to measure angles in the X-Axis or Y-axis.
Syntax:
Xangle
Property returns inclination of X-axis from the
horizontal in degrees.
Yangle
Property returns inclination of Y-axis from the
horizontal in degrees.
Example:
# Demonstrates the calculation of the
# X-axis angle from the horizontal.
# While the program is running change
# the X-axis tilt angle to output different
# angle values.
from fc_adxl345 import *
from microbit import sleep
# Declare an ADXL345 sensor object
# The default analog pins are used.
sensor = ADXL345()
# Calculate the X-axis angle every two seconds.
# Terminate the program in the REPL with
# Ctrl/C on Windows or Command/C on Mac.
while True:
sleep(2000)
print(sensor.Xangle)
Typical Output:
0.2372182
15.53749
26.33024
30.09161
46.2832
46.45235
82.62279
0.4816699
Traceback (most recent call last):
File "main.py", line 19, in <module>
KeyboardInterrupt:
In the above example the board was tilted at various angles between the two second reads.
Enjoy!
ADXL345 Driver Code for micro:bit
Download as zip file
'''
ADXL345 3-Axis Accelerometer
MicroPython driver for BBC micro:bit
AUTHOR: fredscave.com
DATE : 2025/08
VERSION : 1.00
'''
from microbit import *
from micropython import const
from math import atan, sqrt, pi
_REG_ID = const(0x00)
_REG_DATA_X = const(0x32)
_REG_DATA_Y = const(0x34)
_REG_DATA_Z = const(0x36)
_REG_BW_RATE = const(0x2c)
_REG_POWER_CTL = const(0x2D)
_REG_DATA_FORMAT = const(0x31)
_REG_INT_SOURCE = const(0x30)
# Measure bit is On.
_POWER_CTL_BYTE = const(0x08)
# Ranges (+/-g)
_RANGES = {2:0, 4:1, 8:2, 16:3}
# Sensitivity (LSB/g) for all ranges.
_SENSITIVITY = 0.0039
# Mask out unwanted bits on data MSB.
_MSB_MASK = (0b11, 0b111, 0b1111, 0b11111)
class ADXL345():
def __init__(self, ADDR=0x53):
self.ADDR = ADDR
# Set full resolution mode
self._writeReg([_REG_DATA_FORMAT, 0b1000])
# Default range (+/-2g)
self.SetGRange()
# Turn acceleration sampling on.
self._writeReg([_REG_POWER_CTL, _POWER_CTL_BYTE])
# Set Normal power mode.
# Default Output Data Rate (ODR=10) is 100Hz
self.SetODR(10)
# Sets Output Data Rate (ODR).
# Must be a value: 0 to 11
# See Table 7 in Datasheet for frequency equivalents.
def SetODR(self, ODR=10):
self.ODR = ODR if ODR in range(12) else 10
self._writeReg([_REG_BW_RATE, self.ODR])
dummy = self.Reading
# Sets the range for acceleration sampling (+/-g).
# Must be one of: 2, 4, 8, 16
def SetGRange(self, Range=2):
if Range not in (2, 4, 8, 16):
self.Range = 2
else:
self.Range = Range
buf = self._readReg(_REG_DATA_FORMAT, 1)
r = buf[0]
r = r & 0b11111100
r = r | _RANGES[self.Range]
self._writeReg([_REG_DATA_FORMAT, r])
sleep(10)
# *******************************************
# Properties
# *******************************************
# Returns product ID.
@property
def GetID(self):
buf = self._readReg(_REG_ID, 1)
return hex(buf[0])
# Returns X-axis, Y-axis, Z-axis acceleration
# in that order in a tuple in g units.
@property
def Reading(self):
buf = self._readReg(_REG_DATA_X, 6)
X_unscaled = (buf[1] & _MSB_MASK[_RANGES[self.Range]]) << 8 | buf[0]
Y_unscaled = (buf[3] & _MSB_MASK[_RANGES[self.Range]]) << 8 | buf[2]
Z_unscaled = (buf[5] & _MSB_MASK[_RANGES[self.Range]]) << 8 | buf[4]
X = self._Scale(X_unscaled)
Y = self._Scale(Y_unscaled)
Z = self._Scale(Z_unscaled)
return (X, Y, Z)
# Returns X-axis acceleration in g units.
@property
def X(self):
buf = self._readReg(_REG_DATA_X, 2)
unscaled = (buf[1] & _MSB_MASK[_RANGES[self.Range]]) << 8 | buf[0]
return self._Scale(unscaled)
# Returns Y-axis acceleration in g units.
@property
def Y(self):
buf = self._readReg(_REG_DATA_Y, 2)
unscaled = (buf[1] & _MSB_MASK[_RANGES[self.Range]]) << 8 | buf[0]
return self._Scale(unscaled)
# Returns Z-axis acceleration in g units.
@property
def Z(self):
buf = self._readReg(_REG_DATA_Z, 2)
unscaled = (buf[1] & _MSB_MASK[_RANGES[self.Range]]) << 8 | buf[0]
return self._Scale(unscaled)
@property
def IsDataReady(self):
buf = self._readReg(_REG_INT_SOURCE, 1)
b = buf[0]
return (b & 0b10000000) != 0
# Returns Output Data Rate (ODR).
# Value in range of 0 to 11
@property
def GetODR(self):
return self.ODR
# Returns the acceleration sampling range (+/-g).
# One of 2, 4, 8, 16
@property
def GetGRange(self):
return self.Range
# Returns inclination of X-axis from the
# horizontal in degrees.
@property
def Xangle(self):
t = self.Reading
X, Y, Z = t[0], t[1], t[2]
p = atan(X / sqrt((Y*Y + Z*Z))) * 180 / pi
return p
# Returns inclination of Y-axis from the
# horizontal in degrees.
@property
def Yangle(self):
t = self.Reading
X, Y, Z = t[0], t[1], t[2]
p = atan(Y / sqrt((X*X + Z*Z))) * 180 / pi
return p
# *******************************************
# Private Methods
# *******************************************
# Scales raw acceleration.
# Scaling is dependent on
# current G range set.
def _Scale(self, Raw):
raw = Raw
res = _RANGES[self.Range]
max = 2 ** (res + 10)
if raw > max /2 - 1:
raw -= max
return raw * _SENSITIVITY
# Writes one or more bytes to register.
# Bytes is expected to be a list.
# First element is the register address.
def _writeReg(self, Bytes):
i2c.write(self.ADDR, bytes(Bytes))
# Read a given number of bytes from
# a register.
def _readReg(self, Reg, Num):
self._writeReg([Reg])
buf = i2c.read(self.ADDR, Num)
return buf
Comparison of Angle Measurements
This project will measure a tilt angle in the X-axis and compare the results with that obtained from a commercial inclinometer. The inclinometer has a magnetic base which easily mounts on a breadboard.
The image below shows the setup used.
Code:
# This program measures a tilt angle (X-axis)
# using an ADXL345 accelerometer mounted on
# a breadboard.
# A commercial inclinometer can be mounted on
# the breadboard inline with the sensor as a
# check on the accuracy of the ADXL345's
# measurement.
from fc_adxl345 import *
sensor = ADXL345()
# Endless loop:
# Pressing Button A on the micro:bit
# returns the X-axis angle.
while True:
print('\nPress Button A to calculate the angle')
while not button_a.was_pressed():
sleep(100)
print('Tilt in X-axis:', sensor.Xangle, 'degrees')
Typical Output:
Press Button A to calculate the angle
Tilt in X-axis: 11.24113 degrees
Press Button A to calculate the angle
Traceback (most recent call last):
File "main.py", line 20, in <module>
KeyboardInterrupt:
The ADXL345 read the angle as 11.24 degrees which agrees quite well with the inclinometer's reading of 11.75 degrees.
It needs to be pointed out that the setup for this test is somewhat crude. However it does indicate that even a quite simple accelerometer as the ADXL345 is most likely able to determine tilt angles with a reasonable degree of accuracy.