Step counter
What this example shows
Counting steps with the BMA400's built-in step counter, which also reports
whether you are walking, running or standing still. The chip does the step
detection and counting itself; it raises INT1 on a step, and your board reads
the count.
Besides the I2C wiring (GPIO 9 for SCL and GPIO 8 for SDA in the example),
wire the breakout's INT1 pin to GPIO 5, or change INT1_PIN to the pin you
use.
The example code
# FILE: bma400-stepCounter.py
# AUTHOR: Soldered Electronics
# BRIEF: Uses the BMA400's built-in step counter and activity type detection
# (running/walking/still)
# WORKS WITH: BMA400 Accelerometer breakout: www.solde.red/333419
# LAST UPDATED: 2026-09-17
from machine import I2C, Pin
from bma400 import (
BMA400,
BMA400_OK,
BMA400_INT_CHANNEL_1,
BMA400_INT_PUSH_PULL_ACTIVE_1,
BMA400_STEP_COUNTER_INT_EN,
BMA400_ASSERTED_STEP_INT,
BMA400_RUN_ACT,
BMA400_WALK_ACT,
BMA400_STILL_ACT,
)
# Hardware setup: connect the breakout board's INT1 pin to INT1_PIN below
INT1_PIN = 5
mems_event = False
def int1_callback(pin):
global mems_event
mems_event = True
# Change these to match how your board wires I2C
i2c = I2C(0, scl=Pin(9), sda=Pin(8))
sensor = BMA400(i2c)
if sensor.status != BMA400_OK:
raise Exception("Failed to initialize BMA400: " + sensor.status_string())
# Configure the step counter feature. Step detection and counting is handled
# entirely by the sensor; the count is a 24-bit value that can be read at any
# time
sensor.set_step_counter_interrupt({"int_chan": BMA400_INT_CHANNEL_1})
# Configure INT1 pin to push/pull mode, active high
sensor.set_interrupt_pin_mode(BMA400_INT_CHANNEL_1, BMA400_INT_PUSH_PULL_ACTIVE_1)
# Enable the step counter interrupt condition. This must be enabled for step
# counting to work at all, even without using the interrupt pin
sensor.enable_interrupt(BMA400_STEP_COUNTER_INT_EN, True)
int1 = Pin(INT1_PIN, Pin.IN)
int1.irq(trigger=Pin.IRQ_RISING, handler=int1_callback)
print("Waiting for steps...")
activity_names = {
BMA400_RUN_ACT: "Running",
BMA400_WALK_ACT: "Walking",
BMA400_STILL_ACT: "Standing still",
}
while True:
if mems_event:
mems_event = False
status = sensor.get_interrupt_status()
if status is not None and (status & BMA400_ASSERTED_STEP_INT):
count, activity = sensor.get_step_count()
if count is not None:
print(
"Step detected! Step count: {}, activity: {}".format(
count, activity_names.get(activity, "Unknown")
)
)
🔗Open the complete example
bma400-stepCounter.py at commit 484a5be
This example also uses machine.I2C
(MicroPython reference)
and machine.Pin with its irq() method
(MicroPython reference),
which come with the MicroPython firmware and are not part of the bma400
module.
Expected result:
Waiting for steps...
After that, each step interrupt prints one line: Step detected! Step count:
with the total count, then , activity: followed by Running, Walking,
Standing still or Unknown.
Functions used
BMA400(i2c=None, address=BMA400_I2C_ADDRESS_SDO_LOW)returns BMA400Constructor. Soft-resets the sensor, checks its chip ID (0x90) and puts it in normal power mode. Raises an exception when the sensor does not answer or another chip answers.
ReturnsThe sensor object.
Parameters
| Type | Name | Description |
|---|---|---|
I2C | i2c | An initialized machine.I2C object. Optional only on firmware that reports itself as esp32, esp8266 or one older Soldered board firmware. |
int | address | I2C address. BMA400_I2C_ADDRESS_SDO_LOW (0x14, the default) or BMA400_I2C_ADDRESS_SDO_HIGH (0x15). |
status_string()returns strDescribes the current sensor.status code, for example Communication failure or Sensor not found.
ReturnsThe description, or an empty string when the status is BMA400_OK.
set_step_counter_interrupt(config)returns NoneSets which interrupt pin the step counter signals on. The step counter itself needs no other configuration. The result is stored in sensor.status.
ReturnsNothing; check sensor.status.
Parameters
| Type | Name | Description |
|---|---|---|
dict | config | Key int_chan, here BMA400_INT_CHANNEL_1 for INT1. |
set_interrupt_pin_mode(channel, mode)returns NoneSets an interrupt pin as push-pull or open drain, active high or active low. The result is stored in sensor.status.
ReturnsNothing; check sensor.status.
Parameters
| Type | Name | Description |
|---|---|---|
int | channel | BMA400_INT_CHANNEL_1 or BMA400_INT_CHANNEL_2. |
int | mode | BMA400_INT_PUSH_PULL_ACTIVE_0, BMA400_INT_PUSH_PULL_ACTIVE_1 (default), BMA400_INT_OPEN_DRIVE_ACTIVE_0 or BMA400_INT_OPEN_DRIVE_ACTIVE_1. |
enable_interrupt(int_type, enable)returns NoneTurns one interrupt condition on or off. The step counter only counts while BMA400_STEP_COUNTER_INT_EN is enabled, even if you never use the interrupt pin.
ReturnsNothing; check sensor.status.
Parameters
| Type | Name | Description |
|---|---|---|
int | int_type | One of the BMA400_*_INT_EN constants, here BMA400_STEP_COUNTER_INT_EN. |
bool | enable | True to enable, False to disable. |
get_interrupt_status()returns int or NoneReads which interrupt conditions are asserted. Test the result against flags such as BMA400_ASSERTED_STEP_INT.
ReturnsA bitmask of BMA400_ASSERTED_* flags, or None on a bus error.
get_step_count()returns tupleReads the total step count and the activity the chip detected.
Returns(count, activity_type), where activity_type is BMA400_STILL_ACT, BMA400_WALK_ACT or BMA400_RUN_ACT; (None, None) on a bus error.
Putting it together
The step counter needs no tuning in this example: set_step_counter_interrupt()
only routes its interrupt to INT1, and enable_interrupt() switches it on.
INT1 is push-pull, active high, so the pin interrupt triggers on
Pin.IRQ_RISING and the callback only sets mems_event. The main loop then
checks the status for BMA400_ASSERTED_STEP_INT and reads the count and
activity with get_step_count().
Because the count lives in the chip, you can also call get_step_count() at
any time without waiting for the interrupt.