Skip to main content

apitor-ble

PyPI Python versions CI License: MIT Bluetooth LE Code style: black Ruff

A small, dependency-light Python library for driving Apitor toy robots over Bluetooth Low Energy — hardware tested using Robot J — plus a test/demo CLI and full protocol documentation.

The protocol was reverse-engineered from the official Apitor Kit Android app. See docs/PROTOCOL.md for the byte-level details.

⚠️ Unofficial. Not affiliated with or endorsed by Apitor. For use with hardware you own, for interoperability and educational purposes.

Why?

Apitor doesn't publish a public Bluetooth protocol for its robots, so there's no official way to control them from your own code. This library fills that gap: the protocol was independently reverse-engineered from the official app purely for interoperability — so people can experiment, learn, teach, and build their own robotics projects with hardware they already own.

Features

  • Async API built on bleak (Windows/macOS/Linux).
  • Scan + name-filter for Apitor devices, connect, and perform the required authorization handshake automatically.
  • Motor control, LED control, raw frame access, and notification callbacks.
  • Pure, hardware-free protocol layer (apitor_ble.protocol) with unit tests.

Demo

See a real robot driven straight from Python — no app required.

Install

pip install apitor-ble

From source

pip install -e .

Requirements

  • Python 3.10+
  • A Bluetooth Low Energy adapter
  • Supported on Windows, macOS, and Linux (via bleak)

New to programming? Start here 🤖

There's a beginner-friendly API — no async, no Bluetooth setup, just plain commands. See docs/EASY.md.

from apitor_ble.easy import Robot

robot = Robot()            # Robot J by default; Robot(product="s") for others
robot.connect()

robot.forward(2)       # drive forward for 2 seconds
robot.color("blue")    # make the lights blue
robot.wait(1)
robot.lights_off()

robot.disconnect()

Works with every Apitor BLE kit (Robot J, S, Q, R, X, and Wheels). Driving directions can be calibrated per build in a few lines — see Setting up a different robot. For the official motor tables of every buildable model, see docs/ROBOTS.md.

Teaching with this? There's a classroom quickstart with lesson ideas and tips in docs/TEACHERS.md.

Use it in your project

import asyncio
from apitor_ble import ApitorRobot, Motor, Direction, Color

async def main():
    robot = await ApitorRobot.discover(product="j")
    async with robot:
        await robot.run_motor(Motor.M1, Direction.D1, speed=8)
        await asyncio.sleep(2)
        await robot.stop_all_motors()
        await robot.all_leds(Color.BLUE)

asyncio.run(main())

A fuller example is in examples/use_in_another_project.py.

Try it from the command line

Installing the package provides an apitor command:

apitor scan        # find nearby Robot J devices
apitor demo        # connect and run a motor + LED routine
apitor listen      # print incoming notification frames
apitor drive       # keyboard-driven live control

apitor demo --address AA:BB:CC:DD:EE:FF   # skip scanning
apitor scan --product s                   # a different Apitor product
apitor demo --verbose                     # log raw TX/RX frames

(The older python main.py <command> invocation still works too.)

How it fits together

Each layer talks only to the one below it, so you can plug in at whatever level you need:

Your code
│
├── easy.py        Beginner synchronous API  (uses profiles.py)
│
├── robot.py       Async BLE driver          (decodes via sensor.py)
│
├── protocol.py    Packet encoding
│
├── bleak          Bluetooth library
│
├── Bluetooth LE   Your computer's radio
│
└── Apitor Robot   Robot J / S / Q / R / X / Wheels

Beginners start at easy.py. Advanced users can drop straight to robot.py for the full async API, or to protocol.py to build frames by hand.

Project layout

apitor-ble/
├── apitor_ble/
│   ├── __init__.py        # public API
│   ├── protocol.py        # pure byte-level protocol (no I/O)
│   ├── sensor.py          # notification / telemetry decoding
│   ├── robot.py           # async BLE driver (bleak)
│   ├── profiles.py        # per-robot driving profiles
│   └── easy.py            # beginner-friendly synchronous API
├── main.py                # test / demo CLI
├── examples/
│   ├── use_in_another_project.py
│   └── first_program.py
├── tests/
│   ├── test_protocol.py   # hardware-free frame tests
│   ├── test_sensor.py     # notification decoding tests
│   ├── test_easy.py       # easy-API helper tests
│   └── test_profiles.py   # driving-profile tests
├── docs/
│   ├── PROTOCOL.md        # reverse-engineered protocol reference
│   ├── USAGE.md           # library + CLI usage guide
│   ├── EASY.md            # beginner guide + calibration steps
│   ├── ROBOTS.md          # official motor tables per model
│   └── TEACHERS.md        # classroom quickstart
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── pyproject.toml
└── requirements.txt

Tests

The protocol layer needs no hardware or bleak:

pip install pytest
pytest

Robot J at a glance

Item Value Notes
Service 0000f0ff-0000-1000-8000-00805f9b34fb GATT service
Write 0000f001-... phone → robot, split into 20-byte chunks
Notify 0000f002-... robot → phone
Name ApitorTJ... advertised name, case-insensitive
Auth 55AA1120 436E354174675A4C4A7671723863447A send right after connect
Motor 55AA03 <port> <dir> <speed> e.g. M1 fwd spd8 = 55AA03 06 01 08
Stop 55AA03 10 00 00 stop all motors
LED 55AA04 <index> <color> 00 00 index 4 = all LEDs

Roadmap

Planned future improvements:

  • Publish to PyPI
  • Add GitHub Actions CI
  • Add coverage reporting
  • Support additional Apitor firmware revisions
  • More example programs
  • Additional classroom resources

Changelog

See CHANGELOG.md for release notes.

Contributing

Contributions are welcome — see CONTRIBUTING.md. Especially valuable:

  • Testing on additional robots (and calibrating them on real hardware)
  • Protocol verification
  • Documentation improvements
  • New example programs
  • Bug reports

Disclaimer

This project was independently reverse-engineered for interoperability.

It is not affiliated with, endorsed by, or supported by Apitor.

All trademarks belong to their respective owners.

License

MIT — see LICENSE.

Metadata

Release files for apitor-ble 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for apitor-ble 0.2.1
File Size Uploaded
apitor_ble-0.2.1.tar.gz 29.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for apitor-ble 0.2.1
File Interpreter ABI Platform
apitor_ble-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 56.8 kB

Release files / apitor_ble-0.2.1.tar.gz

Download URL apitor_ble-0.2.1.tar.gz
Size 29.7 kB
Tags Source
SHA-256 checksum
How to use checksums
a811e5dcd769c13b1230929e66c014feba9c56eb623796f4178abbc056b02a89
BLAKE2b-256 checksum
How to use checksums
004f6bfb90b81055fdc90c658cc68709dabdaa0442c00d131c9b88563fafa1f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / apitor_ble-0.2.1-py3-none-any.whl

Download URL apitor_ble-0.2.1-py3-none-any.whl
Size 27.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
285e7b5645a14e38076953e7b931977deda87c80fee55c5fc243eb39269f7d85
BLAKE2b-256 checksum
How to use checksums
e782d85c10422564a24c117f5dd1539738d4a2d3bbdc1e1328239f95ef5a6c22
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

This release

0.2.1 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page