Skip to main content

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:

ShellMicroPython
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 BMA400

Constructor. 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

TypeNameDescription
I2Ci2cAn initialized machine.I2C object. Optional only on firmware that reports itself as esp32, esp8266 or one older Soldered board firmware.
intaddressI2C address. BMA400_I2C_ADDRESS_SDO_LOW (0x14, the default) or BMA400_I2C_ADDRESS_SDO_HIGH (0x15).
status_string()returns str

Describes 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 None

Sets 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

TypeNameDescription
dictconfigKey int_chan, here BMA400_INT_CHANNEL_1 for INT1.
set_interrupt_pin_mode(channel, mode)returns None

Sets 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

TypeNameDescription
intchannelBMA400_INT_CHANNEL_1 or BMA400_INT_CHANNEL_2.
intmodeBMA400_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 None

Turns 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

TypeNameDescription
intint_typeOne of the BMA400_*_INT_EN constants, here BMA400_STEP_COUNTER_INT_EN.
boolenableTrue to enable, False to disable.
get_interrupt_status()returns int or None

Reads 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 tuple

Reads 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.