MicroPython Driver for MMA8452 Accelerometer
Contents
Introduction
This a MicroPython driver written specifically for the BBC micro:bit that will work with the MMA8452 accelerometer sensor.
The MMA8452 accelerometer is discussed in some detail here.
FIG 1 - MMA8452 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 MMA8452
This sensor's only serial interface is I2C. Hooking it up to the micro:bit is easy:
| micro:bit | Sensor board |
|---|---|
| 3.3V | VCC |
| GND | GND |
| Pin 20 | SDA |
| Pin 19 | SCL |
This utilises the standard I2C pins of the micro:bit.
Optionally, a jumper wire can be placed between the VCC pin and the SAO pin on the breakout board. This changes the default I2C address and is discussed below.
Driver Overview
Features of this driver include:
- Standby or Active mode is user selectable.
- Output data rate (ODR), measurement range (full scale), oversampling mode and noise mode are user selectable/configurable.
- Acceleration readings in all/any axis with properties available that signal when fresh values are available in all or any axis.
- A simple tool for measuring angles from the horizontal in the X and Y axis.
- The chip's ID value may be read from the sensor.
Interrupts related to activity and orientation 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_mma8452.py - OR -
- Download as a zip file using the link. Unzip the file and save it as fc_mma8452.py into the default directory where the MicroPython editor e.g. Mu Editor saves python code files.
After saving the fc_mma8452.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.
Class constructorThe driver is implemented as a class.The first thing to do is call the constructor of the MMA8452 class to obtain a sensor object.
Syntax:
MMA8452()
Example
from fc_mma8452 import *
# Declare a MMA8452 sensor object.
sensor = MMA8452()
This assumes that the file fc_mma8452.py has been successfully copied to the micro:bit's filesystem as described above.
Methods and PropertiesThe driver provides methods and properties:
-
Power Mode settings:
SetActiveMode(), IsActiveMode -
Setup sampling parameters:
SetODR(), GetODR
SetGRange(), GetGRange
SetLowNoise(), IsLowNoise
SetOS, GetOS -
Read acceleration:
Reading, X, Y, Z
IsDataReady, IsXDataReady, IsYDataReady, IsZDataReady -
Angle measurement:
Xangle, Yangle -
Chip ID:
GetID
Power Modes
There are two possible power modes that this driver implements:
- Standby : No sampling, very low power, I2C interface available.
- Active Mode : Normal operating mode.
Syntax:
SetActiveMode(Active=True)
Method that sets the sampling power mode;
Active = True; Active mode
Active = False; Standby
IsActiveMode
Method that returns the sampling power mode.
Returns True if in Active mode.
Returns False if in Standby.
Example
# Test power modes of the MMA8452.
from fc_mma8452 import *
from microbit import sleep
sensor = MMA8452()
print('\nChanging to Standby...')
sensor.SetActiveMode(False)
sleep(100)
print('Is mode Active?', sensor.IsActiveMode)
print('\nChanging to Active mode...')
sensor.SetActiveMode(True)
sleep(100)
print('Is mode Active?', sensor.IsActiveMode)
Output
Changing to Standby...
Is mode Active? False
Changing to Active mode...
Is mode Active? True
Sampling Options
The user has the option to set the output data rate (ODR), measurement range (full scale), oversampling mode and the low noise mode.
Syntax:
SetODR(ODR = 3)
Sets the output data rate (ODR).
One of 0 to 7.
0 = 800Hz, 1 = 400Hz, 2 = 200Hz, 3 = 100Hz
4 = 50Hz, 5 = 12.5Hz, 6 = 6.25Hz, 7 = 1.56Hz
Default (3) = 100Hz.
GetODR
Property that returns the ODR in use.
One of 0 to 7.
SetGRange(Range=2)
Sets the measurement range (+/-g).
One of 2, 4, 8.
GetGRange
Property that returns the measurement range in use.
One of 2, 4, 8 (+/-g).
SetOS(OS=0)
Sets the amount of oversampling performed.
One of 0, 1, 2, 3.
Highest data resolution is at OS = 2 and ODR = 7
See datasheet Table 66 and Table 67.
GetOS
Property that returns the oversampling value.
One of 0, 1, 2, 3.
SetLowNoise(LowNoise = True)
Turn low noise mode on or off.
LowNoise = True; turns on low noise mode.
LowNoise = False; turns off low noise mode.
IsLowNoise
Property returns True if in low noise mode.
Example
# Test ODR, measurement range
# oversampling and noise mode settings.
from fc_mma8452 import *
sensor = MMA8452()
print('\nDEFAULTS...')
print('Default ODR:', sensor.GetODR)
print('Default measurement range:', sensor.GetGRange)
print('Default oversampling:', sensor.GetOS)
print('Default low noise on?:', sensor.IsLowNoise)
# Change the above defaults.
sensor.SetODR(7) # 1.56Hz
sensor.SetGRange(8) # +/-8g
sensor.SetOS(2) # High resolution
sensor.SetLowNoise(True) # Turn on low noise mode
# Report new settings
print('\nNOW...')
print('ODR:', sensor.GetODR)
print('Measurement range:', sensor.GetGRange)
print('Oversampling setting:', sensor.GetOS)
print('Low noise on?:', sensor.IsLowNoise)
Output:
DEFAULTS...
Default ODR: 3
Default measurement range: 2
Default oversampling: 0
Default low noise on?: False
NOW...
ODR: 7
Measurement range: 8
Oversampling setting: 2
Low noise on?: True
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).
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)
IsDataReady
Property returns True if the sensor
has new data in all three axis ready for reading.
X
Property that returns the acceleration in the X-axis.
IsXDataReady
Property returns True if the sensor
has new data in the X-axis ready for reading.
Y
Property that returns the acceleration in the Y-axis.
IsYDataReady
Property returns True if the sensor
has new data in the Y-axis ready for reading.
Z
Property that returns the acceleration in the Z-axis.
IsZDataReady
Property returns True if the sensor
has new data in the Z-axis ready for reading.
Example 1:
from fc_mma8452 import *
sensor = MMA8452()
# Read acceleration of all three axis.
acc = sensor.Reading
print('\n(X-axis, Y-axis, Z-axis):')
print(acc)
# Read acceleration separately from each axis.
print('\nX-axis:', sensor.X)
print('Y-axis:', sensor.Y)
print('Z-axis:', sensor.Z)
Typical Output:
X-axis: 0.01660156
Y-axis: 0.01660156
Z-axis: 1.00293
Example 2
# Set the ODR to a slow setting.
# TEST 1: Read 5 acceleration values across
# all axis as quickly as the data
# becomes available.
# TEST 2: Read 5 acceleration values across
# the X-axis as quickly as the data
# becomes available.
from fc_mma8452 import *
sensor = MMA8452()
sensor.SetODR(6) # ODR = 6.25Hz
# TEST 1
print('\nTEST 1')
print('(X-axis, Y-axis, Z-axis):')
for __ in range(5):
while not sensor.IsDataReady:
sleep(1)
print(sensor.Reading)
# TEST 2
print('\nTEST 2')
print('X-axis:')
for __ in range(5):
while not sensor.IsXDataReady:
sleep(1)
print(sensor.X)
Typical Output:
TEST 1
(X-axis, Y-axis, Z-axis):
(0.01464844, 0.01953125, 1.000977)
(0.01464844, 0.02050781, 1.0)
(0.01464844, 0.02050781, 0.9980469)
(0.01367188, 0.02050781, 1.00293)
(0.01367188, 0.02050781, 1.000977)
TEST 2
X-axis:
0.01269531
0.01464844
0.01367188
0.015625
0.01464844
In the above example the sensor was lying on a flat horizontal surface (mounted in a breadboard). In this orientation the X and Y axis acceleration should be very close to 0g and the Z axis acceleration should be close to 1g (from the Earth's gravity).
Measuring Angles
Accelerometers measures two types of acceleration:
- 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_mma8452 import *
from microbit import sleep
sensor = MMA8452()
# 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.9546926
5.355175
36.05809
65.51743
29.48429
11.69763
0.925561
Traceback (most recent call last):
File "main.py", line 17, in <module>
In the above example the board was tilted at various angles between the two-second reads.
Product ID
The MMA8452 has a product ID value burnt into non-volatile memory at time of manufacture. This driver provides a simple property to read this value.
Syntax:
GetID
Returns the product ID
Example:
from fc_mma8452 import *
sensor = MMA8452()
print('\nMMA8452 ID:', sensor.GetID)
Output:
MMA8452 ID: 0x2a
Enjoy!
MMA8452 Driver Code for micro:bit
Download as zip file
'''
MMA8452 3-Axis Accelerometer
MicroPython driver for BBC micro:bit
AUTHOR: fredscave.com
DATE : 2025/010
VERSION : 1.00
'''
from microbit import *
from micropython import const
from math import atan, pi, sqrt
_REG_STATUS = const(0x00)
_REG_OUT_X = const(0x01)
_REG_OUT_Y = const(0x03)
_REG_OUT_Z = const(0x05)
_REG_WHO_AM_I = const(0x0D)
_REG_XYZ_DATA_CFG = const(0xE)
_REG_CTRL_REG1 = const(0x2A)
_REG_CTRL_REG2 = const(0x2B)
RANGES = (2, 4, 8)
SENSITIVITIES = (1024, 512, 256)
class MMA8451():
def __init__(self, ADDR=0x1C):
self.ADDR = ADDR
self.SetGRange() # +/-2g
self.SetODR() # 100Hz
self.SetOS() # Normal
self.SetLowNoise(False) # Low Noise mode is off.
self.SetActiveMode()
# Sets active mode or standby mode.
def SetActiveMode(self, Active=True):
buf = self._readReg(_REG_CTRL_REG1, 1)
b = buf[0]
if Active == True:
b = b | 0b00000001
else:
b = b & 0b11111110
self._writeReg([_REG_CTRL_REG1, b])
# Sets measurement range.
# One of 2, 4, 8 (+/-g)
def SetGRange(self, Range = 2):
self.Range = Range if (Range in RANGES) else 2
active = False
# Must be in Standby mode.
if self.IsActiveMode == True:
active = True
self.SetActiveMode(False)
buf = self._readReg(_REG_XYZ_DATA_CFG, 1)
b = buf[0]
b = b & 0b11111100
b = b | RANGES.index(self.Range)
self._writeReg([_REG_XYZ_DATA_CFG, b])
if active == True:
self.SetActiveMode()
# Sets output data rate (ODR).
# 0 = 800Hz, 1 = 400Hz, 2 = 200Hz, 3 = 100Hz
# 4 = 50Hz, 5 = 12.5Hz, 6 = 6.25Hz, 7 = 1.56Hz
def SetODR(self, ODR = 3):
odr = ODR if (ODR in range(8)) else 3
active = False
# Must be in Standby mode.
if self.IsActiveMode == True:
active = True
self.SetActiveMode(False)
buf = self._readReg(_REG_CTRL_REG1, 1)
b = buf[0]
b = b & 0b11000111
b = b | (odr << 3)
self._writeReg([_REG_CTRL_REG1, b])
if active == True:
self.SetActiveMode()
# Sets the oversampling mode.
# One of 0, 1, 2, 3
# Highest data resolution is
# at OS = 2 and ODR = 7.
# See datasheet Table 66 and Table 67.
def SetOS(self, OS=0):
os = OS if (OS in range(4)) else 0
active = False
# Must be in Standby mode.
if self.IsActiveMode == True:
active = True
self.SetActiveMode(False)
buf = self._readReg(_REG_CTRL_REG2, 1)
b = buf[0]
b = b & 0b11111100
b = b | os
self._writeReg([_REG_CTRL_REG2, b])
if active == True:
self.SetActiveMode()
# Sets Low Noise mode
def SetLowNoise(self, LowNoise = True):
active = False
# Must be in Standby mode.
if self.IsActiveMode == True:
active = True
self.SetActiveMode(False)
buf = self._readReg(_REG_CTRL_REG1, 1)
b = buf[0]
b = b & 0b11111011
if LowNoise == True:
b = b | 4
self._writeReg([_REG_CTRL_REG1, b])
if active == True:
self.SetActiveMode()
# *******************************************
# Properties
# *******************************************
# 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_OUT_X, 6)
X_unscaled = (buf[0] << 4) | (buf[1] >> 4)
Y_unscaled = (buf[2] << 4) | (buf[3] >> 4)
Z_unscaled = (buf[4] << 4) | (buf[5] >> 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_OUT_X, 2)
unscaled = (buf[0] << 4) | (buf[1] >> 4)
return self._Scale(unscaled)
# Returns Y-axis acceleration in g units.
@property
def Y(self):
buf = self._readReg(_REG_OUT_Y, 2)
unscaled = (buf[0] << 4) | (buf[1] >> 4)
return self._Scale(unscaled)
# Returns Z-axis acceleration in g units.
@property
def Z(self):
buf = self._readReg(_REG_OUT_Z, 2)
unscaled = (buf[0] << 4) | (buf[1] >> 4)
return self._Scale(unscaled)
# Returns True if a new acceleration dataset
# is available for all three axis (X, Y, Z)
@property
def IsDataReady(self):
buf = self._readReg(_REG_STATUS, 1)
b = buf[0]
return ((b & 0b00001000) >> 3) == 1
# Returns True if new X-axis acceleration
# data is available.
@property
def IsXDataReady(self):
buf = self._readReg(_REG_STATUS, 1)
b = buf[0]
return (b & 1) == 1
# Returns True if new Y-axis acceleration
# data is available.
@property
def IsYDataReady(self):
buf = self._readReg(_REG_STATUS, 1)
b = buf[0]
return (b & 0b00000010) == 2
# Returns True if new Z-axis acceleration
# data is available.
@property
def IsZDataReady(self):
buf = self._readReg(_REG_STATUS, 1)
b = buf[0]
return (b & 0b00000100) == 4
# Returns True if in Active mode.
# Returns False if in Standby mode.
@property
def IsActiveMode(self):
buf = self._readReg(_REG_CTRL_REG1, 1)
return (buf[0] & 1) == 1
# Returns measurement range.
# One of 2, 4, 8 (+/-g)
@property
def GetGRange(self):
buf = self._readReg(_REG_XYZ_DATA_CFG, 1)
b = buf[0]
return RANGES[(b & 0b00000011)]
# Returns output data rate (ODR).
# One of 0 to 7.
@property
def GetODR(self):
buf = self._readReg(_REG_CTRL_REG1, 1)
b = buf[0]
return (b & 0b00111000) >> 3
# Returns the oversampling mode.
# One of 0, 1, 2, 3
# See datasheet Table 66 and Table 67.
@property
def GetOS(self):
buf = self._readReg(_REG_CTRL_REG2, 1)
b = buf[0]
return b & 3
# Returns the chip's ID
@property
def GetID(self):
buf = self._readReg(_REG_WHO_AM_I, 1)
return hex(buf[0])
# Returns True if in Low Noise mode.
@property
def IsLowNoise(self):
buf = self._readReg(_REG_CTRL_REG1, 1)
b = buf[0]
return (b & 4) == 4
# 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
raw = raw if (raw < 2048) else raw - 4096
scale = SENSITIVITIES[RANGES.index(self.Range)]
return raw / scale
# Writes one or more bytes to register.
# Bytes is expected to be a list.
# First element is the register address.
# NOTE: A stop bit is not sent.
def _writeReg(self, Bytes):
i2c.write(self.ADDR, bytes(Bytes), repeat=True)
# 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
Exploring the Noise Modes
The MMA8452 offers two different noise reduction strategies to improve resolution and accuracy.
Oversampling is a widely used technique. The simplest approach is to internally take multiple samples then report the average through the sensor's digital interface. This approach is used by this accelerometer. The application of oversampling is not trivial though and the user should consult the datasheet.
Additional, the MMA8452 also provides a separate low noise setting. There's very little information in the datasheet other than how to turn the setting on or off.
The MMA8452 development board, mounted on a breadboard, was set at a random angle before running the code in the micro:bit.
Code:
# This program reads the X-axis acceleration
# multiple times for three separate scenarios.
# SCENERIO 1: Low noise off, Lowest level of oversampling used.
# SCENERIO 2: Low noise off, Highest level of oversampling used.
# SCENERIO 3: Low noise on, Lowest level of oversampling used.
# For each set of acceleration measurements per
# mode, the average and standard deviation
# is calculated.
from fc_mma8452 import *
from math import sqrt
from microbit import sleep
Samples = 20
sensor = MMA8452()
def Avg(L):
# Returns the average (mean) of
# the elements of a list.
n = len(L)
sum = 0.0
for i in range(n):
sum += L[i]
return sum/n
def SD(L, avg):
# Returns the sample standard deviation
# of the elements of a list.
n = len(L)
sum = 0
for i in range(n):
diff = (L[i] - avg) ** 2
sum += diff
return sqrt(sum / (n-1))
def GetSamples(Scenerio):
L = [' '] * Samples
for s in range(Samples):
while not sensor.IsXDataReady:
sleep(1)
L[s] = sensor.Xangle
avg = round(Avg(L), 4)
sd = round(SD(L, avg), 4)
print('SCENARIO', Scenerio,
'Angle =', avg, ' SD =', sd)
# main program
print()
# SCENERIO 1
sensor.SetODR(0) # 800Hz
sensor.SetLowNoise(False) # Low noise off
sensor.SetOS(0) # Minimum oversampling
sensor.Reading # Clear the data registers.
GetSamples(Scenerio=1)
# SCENERIO 2
sensor.SetODR(7) # 1.56Hz
sensor.SetLowNoise(False) # Low noise off
sensor.SetOS(2) # Maximum oversampling
GetSamples(Scenerio=2)
# SCENERIO 3
sensor.SetODR(0) # 800Hz
sensor.SetLowNoise(True) # Low noise on
sensor.SetOS(0) # Minimum oversampling
GetSamples(Scenerio=3)
Typical Output:
SCENARIO 1 Angle = 22.3118 SD = 0.241
SCENARIO 2 Angle = 22.1666 SD = 0.0258
SCENARIO 3 Angle = 22.3887 SD = 0.1348
This test program was run multiple times, with each run yielding similar results. The low noise setting does reduce spread and is a useful option to consider if resolution is important at high output data rates.
However, the use of oversampling (but needing lower data rate output) is the best option if maximum resolution and accuracy is required.