Skip to main content

Python library and CLI for Kelly motor controllers

Project description

pyKellyMotion

A Python library for interfacing with Kelly motor controllers via serial/UART communication. Provides comprehensive monitoring, configuration read/write, and control capabilities for electric motor systems.

Protocol reverse engineered from Kelly ACAduserEnglish Android app.

Features

  • Real-time Monitoring - Motor speed, temperature, current, voltage, switch states
  • Configuration Read/Write - Read and modify controller parameters over serial
  • Firmware Version Query - Get controller firmware information
  • Motor Identification - Enter/exit motor auto-tuning mode
  • Checksum Validation - All packets validated for data integrity
  • CLI Tool - Command-line interface for quick testing and monitoring
  • Clean Pythonic API - Simple interface with type hints and dataclasses

Installation

From PyPI

pip install pykellymotion

From Source

git clone https://github.com/ril3y/pyKellyMotion.git
cd pyKellyMotion
pip install .

Development Install

pip install -e ".[dev]"

Quick Start

Command Line (pykelly)

After installation, the pykelly command is available:

# Real-time monitoring (default)
pykelly COM3

# Monitor with custom interval
pykelly COM3 monitor --interval 0.25

# Get firmware version
pykelly /dev/ttyUSB0 version

# Read controller configuration
pykelly COM3 config

# Read config as raw hex
pykelly COM3 config --raw

# Single monitor read (JSON output)
pykelly COM3 single --json

# Read phase current ADC values
pykelly COM3 phase

# Motor identification mode
pykelly COM3 identify

# Enable debug output
pykelly COM3 --debug monitor

Python Library

from pykellymotion import KellyController

# Connect
controller = KellyController("COM3", debug=False)
controller.connect()

# Monitor
controller.read_monitor()
print(f"RPM: {controller.rpm}")
print(f"Throttle: {controller.throttle}%")
print(f"Battery: {controller.battery_voltage}V")
print(f"Errors: {controller.errors}")

# Read configuration
controller.read_config()
controller.print_config()
max_current = controller.get_config('current_percent')

# Cleanup
controller.disconnect()

Hardware Setup

Wiring

Kelly Controller          Computer
   TX  ────────────────>  RX
   RX  <────────────────  TX
   GND ─────────────────  GND

Supported Controllers

  • KBLS - Kelly BLDC Sensorless/Sensored
  • KACI - Kelly AC Induction
  • Other Kelly controllers with serial interface

API Reference

KellyController

Connection

from pykellymotion import KellyController

controller = KellyController(comport: str, debug: bool = False)
controller.connect() -> bool
controller.disconnect()
controller.is_connected -> bool

Monitoring

controller.read_monitor() -> bool                              # Read all monitor packets
controller.start_monitor_loop(callback=None, interval=0.5)     # Continuous monitoring
controller.print_monitor()                                     # Print formatted data

Monitor Properties

Property Type Description
rpm int Motor speed in RPM
motor_mph float Speed in MPH (uses tire_diameter)
throttle int Throttle position 0-100%
phase_current int Phase current in amps
battery_voltage int Battery voltage
motor_temp int Motor temperature (C)
controller_temp int Controller temperature (C)
is_forward bool Forward direction selected
is_reverse bool Reverse direction selected
errors list Current error strings

Configuration

controller.read_config() -> bool                    # Read config from controller
controller.get_config(param_name: str) -> Any       # Get specific parameter
controller.get_all_config() -> dict                 # Get all parameters
controller.print_config()                           # Print formatted config
controller.write_config(data: bytes) -> bool        # Write raw config (13 bytes) - UNTESTED!

WARNING: write_config() has NOT been tested on real hardware. Read operations work correctly, but writing could potentially damage your controller. Use at your own risk!

Other Commands

controller.get_version() -> str                     # Firmware version (hex)
controller.get_phase_current_adc() -> tuple         # Raw ADC (a, b, c)
controller.enter_identify_mode() -> bool            # Enter motor auto-tune
controller.exit_identify_mode() -> bool             # Exit motor auto-tune
controller.is_identify_active() -> bool             # Check if tuning active

Low-Level Access

from pykellymotion import Communications, Commands

comm = Communications("COM3", debug=True)
comm.open()

success, data = comm.send_command(Commands.GET_VERSION)
print(f"Version: {data.hex()}")

comm.close()

Configuration Parameters

Parameter Description Range
module_name Module identifier 8 chars ASCII
serial_number Serial number Hex
software_version Firmware version Hex
current_percent Max current limit 20-100%
battery_current_limit Battery current limit 20-100%
low_voltage Undervoltage cutoff 0-1000V
over_voltage Overvoltage cutoff 0-1000V
max_speed Maximum RPM 0-60000
max_forward_speed Forward speed limit 30-100%
max_reverse_speed Reverse speed limit 20-100%
tps_type Throttle type 0=None, 1=0-5V, 2=1-4V, 3=0-5K
tps_dead_low Throttle dead zone low 0-80%
tps_dead_high Throttle dead zone high 120-200%
accel_time Acceleration ramp (x0.1s) 0-250
brake_time Brake ramp (x0.1s) 0-250
regen_brake_percent Regen on throttle release 0-50%
motor_poles Motor pole pairs x2 2-32
speed_sensor_type 0=None, 1=Encoder, 2=Hall, 3=Resolver 0-4
high_temp_cutoff Motor overtemp cutoff 60-170C

Communication Protocol

Serial Settings

Parameter Value
Baud Rate 19200
Data Bits 8
Parity None
Stop Bits 1

Packet Structure

[CMD] [LENGTH] [DATA...] [CHECKSUM]
Field Size Description
CMD 1 byte Command ID
LENGTH 1 byte Data byte count (0-16)
DATA 0-16 bytes Command-specific data
CHECKSUM 1 byte sum(preceding_bytes) & 0xFF

Special case: When LENGTH = 0, CHECKSUM equals CMD.

Commands

Command Hex Description
MONITOR_ONE 0x3A Real-time data page 1 (throttle, switches, temps)
MONITOR_TWO 0x3B Real-time data page 2 (RPM, current)
MONITOR_THREE 0x3C Real-time data page 3 (errors)
GET_VERSION 0x11 Firmware version
READ_CONFIG 0x4B Read configuration/calibration
WRITE_CONFIG 0x4C Write configuration (13 bytes)
GET_PHASE_I_AD 0x35 Phase current ADC values
ENTRY_IDENTIFY 0x43 Enter motor identification mode
QUIT_IDENTIFY 0x42 Exit motor identification mode
CHECK_IDENTIFY_STATUS 0x44 Check identification status

Protocol Examples

Read Monitor Data:

TX: 3A 00 3A
RX: 3A 10 [16 bytes data] [checksum]

Read Configuration:

TX: 4B 00 4B
RX: 4B [len] [config data] [checksum]

Get Version:

TX: 11 00 11
RX: 11 [len] [version data] [checksum]

Project Structure

pyKellyMotion/
├── pykellymotion/
│   ├── __init__.py          # Public API exports
│   ├── cli.py               # CLI entry point (pykelly command)
│   ├── protocol.py          # Protocol constants, commands, checksum
│   ├── communications.py    # Serial communication layer
│   ├── parser.py            # Packet parsing and data extraction
│   └── kelly_controller.py  # High-level controller interface
├── tests/
│   ├── test_protocol.py     # Protocol unit tests
│   ├── test_parser.py       # Parser unit tests
│   └── test_cli.py          # CLI argument tests
└── pyproject.toml           # Package configuration

Advanced Usage

Custom Monitor Callback

from pykellymotion import KellyController

def my_callback(monitor_data):
    print(f"Speed: {monitor_data.motor_speed} RPM")
    if monitor_data.motor_temp > 80:
        print("WARNING: Motor hot!")

controller = KellyController("COM3")
controller.connect()
controller.start_monitor_loop(callback=my_callback, interval=0.25)

Debug Mode

controller = KellyController("COM3", debug=True)
# Shows TX/RX packets in hex

Or via CLI:

pykelly COM3 --debug monitor

Error Codes

16-bit bitmask decoded by controller.errors:

Bit Error
0 Identification Error
1 Over Voltage
2 Low Voltage
4 Stall
5 Internal Voltage Fault
6 Controller Over Temp
7 Throttle Error (Startup)
9 Internal Reset
10 Hall Sensor Error
15 Motor Over Temp

Testing

# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=pykellymotion

# Lint and format
ruff check .
ruff format .

Troubleshooting

No Response

  • Check TX/RX wiring (may need swap)
  • Verify 19200 baud
  • Ensure controller powered
  • Enable --debug flag or debug=True

Permission Denied (Linux)

sudo usermod -a -G dialout $USER
# Log out and back in

Checksum Errors

  • Check cable quality
  • Ensure clean ground connection

Contributing

  1. Fork repository
  2. Create feature branch
  3. Make changes with tests
  4. Run ruff check and ruff format
  5. Submit pull request

License

MIT License - see LICENSE

Acknowledgments

  • Protocol reverse engineered from Kelly ACAduserEnglish Android app
  • Kelly Controller motor controller products
  • Electric vehicle hobbyist community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pykellymotion-0.1.1.tar.gz (22.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pykellymotion-0.1.1-py3-none-any.whl (18.8 kB view details)

Uploaded Python 3

File details

Details for the file pykellymotion-0.1.1.tar.gz.

File metadata

  • Download URL: pykellymotion-0.1.1.tar.gz
  • Upload date:
  • Size: 22.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pykellymotion-0.1.1.tar.gz
Algorithm Hash digest
SHA256 ffc20f45b5566fe4577f5d8cae4023d6bc1122aa92d03bf521c153c141e6fbe5
MD5 29d1a8453471a672755c9055bcf218e6
BLAKE2b-256 ea03f20102a444c5cc77add288f7773b28fae59fe9be35a1e335afbd05277be3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pykellymotion-0.1.1.tar.gz:

Publisher: publish.yml on ril3y/pyKellyMotion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pykellymotion-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pykellymotion-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 18.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pykellymotion-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3c4c13129a775be67192241a4b5415170e626eb8d0b0237ce6065068862f5230
MD5 76aa49549c53fabe6a01ef9a2fabb807
BLAKE2b-256 2b9cba12e7dea1c6df0795989cd4aec7d98900eaa3e73c67f39e2288ae6c9b85

See more details on using hashes here.

Provenance

The following attestation bundles were made for pykellymotion-0.1.1-py3-none-any.whl:

Publisher: publish.yml on ril3y/pyKellyMotion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page