Microcycler Python SDK
Python SDK for the Microcycler battery testing platform.
Status: Pre-alpha — API subject to change.
Installation
pip install microcycler
The package is a thin Python layer over a Rust core (core/ in the repository: the same code the Microcycler dashboard and the headless agent run), shipped as one prebuilt wheel per platform (Windows, macOS, Linux; Python 3.9+). pip install microcycler needs no compiler. Installing from a source checkout (pip install -e sdk) builds the core and needs a Rust toolchain (rustup.rs); without the compiled core the package falls back to a pure-Python implementation, which MICROCYCLER_BACKEND=python forces.
Uplink agent
microcycler-agent import <credential> then microcycler-agent run uploads this network's licensed cyclers to your organization (see the remote-link documentation). It needs only the installed package.
Quickstart
Connect and read telemetry
import asyncio
from microcycler import Cycler
async def main():
async with Cycler("192.168.1.100") as cycler:
print(await cycler.identify())
t = await cycler.get_telemetry()
print(f"State: {t.state}")
print(f"Voltage: {t.voltage:.3f} V")
print(f"Current: {t.current:.3f} A")
print(f"Temp: {t.temperature:.1f} °C")
print(f"Capacity: {t.capacity_mah:.1f} mAh")
asyncio.run(main())
Discover cyclers on the network
import asyncio
from microcycler import Cycler, discover
async def main():
# Listens for the discovery beacon on UDP 5002. The beacon is periodic
# and unsolicited, so this is a collection window, not a deadline —
# allow at least one beacon period (10 s by default).
for dev in await discover():
print(dev) # HADES-042 @ 169.254.10.5:5000 (standby)
async with Cycler(dev.host, port=dev.port) as cycler:
print(await cycler.get_life())
asyncio.run(main())
Stream live telemetry via UDP
from microcycler.streaming import CyclerStream
async def monitor():
async with CyclerStream() as stream:
async for telemetry in stream:
print(telemetry.voltage, telemetry.current)
Build and run a recipe
from microcycler import Cycler
from microcycler.recipes import Recipe, CC, CV, Rest, Loop
recipe = (
Recipe(name="Standard Formation")
.add(CC(current=0.1, cutoff_voltage_high=4.2))
.add(CV(voltage=4.2, limit_current_high=0.1, cutoff_current_low=0.02))
.add(Rest(duration=1800))
.add(CC(current=-0.2, cutoff_voltage_low=2.8))
.add(Rest(duration=1800))
.add(Loop(cycles=3))
)
async def run():
async with Cycler("192.168.1.100") as cycler:
await cycler.load_recipe(recipe)
await cycler.start()
Recipe steps
| Step | Description |
|---|---|
CC(current, cutoff_voltage_high, cutoff_voltage_low, cutoff_time, cutoff_capacity) |
Constant current. current is signed: positive = charge, negative = discharge. |
CV(voltage, limit_current_high, cutoff_current_low, cutoff_time) |
Constant voltage |
CP(power, cutoff_voltage, cutoff_time) |
Constant power |
CR(resistance, cutoff_voltage, cutoff_time) |
Constant resistance |
Rest(duration) |
Open-circuit rest |
EIS(freq_start, freq_end, amplitude, points_per_decade) |
Impedance sweep |
Loop(cycles) |
Repeat preceding steps N times |
ACRipple(duration, dc_current, ac_amplitude, ac_freq, cutoff_temp) |
AC ripple excitation for calorimetry / thermal characterisation |
License
MIT
Lifetime wear counters
Every board keeps an odometer in its EEPROM. Three independent axes, because a unit can be worn out along any one of them while the others look fine: mechanical (relay actuations), calendar (powered time) and electrical (charge through the output).
from microcycler import Cycler
async with Cycler("192.168.1.200") as cycler:
life = await cycler.get_life()
print(f"boots {life.boot_count}")
print(f"powered {life.powered_hours:,.0f} h")
print(f"throughput {life.throughput_ah:,.1f} Ah")
print(f"MAIN relay {life.relay_main:,} closings")
print(f"FAN relay {life.relay_fan:,} closings")
if not life.durable:
print("WARNING: counters are not reaching the EEPROM")
These belong to the physical board, not to your database, so they survive a reinstall, an MCU swap, or the unit moving to another lab. There is deliberately no way to reset them — an odometer that can be wound back is not evidence of anything.
life.durable (i.e. write_errors == 0) is the field worth alerting on. A
unit with a dead EEPROM still counts perfectly well in RAM and reports entirely
plausible numbers; nothing else distinguishes a kept odometer from one that is
lost at every power cut.
Counters are flushed at most once every few minutes to spare the part, so up to that much accrued wear is in RAM only. Before deliberately powering a unit down:
await cycler.flush_life()
Metadata
Release files for microcycler 0.1.14
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| microcycler-0.1.14.tar.gz | 162.0 kB | Without provenance | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| microcycler-0.1.14-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| microcycler-0.1.14-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 7.3 MB
Release files / microcycler-0.1.14.tar.gz
| Download URL | microcycler-0.1.14.tar.gz |
|---|---|
| Size | 162.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
57771cf3feb3b797a9b23fea729dac8f01e3786fe536a848613d6b3366febd98
|
|
BLAKE2b-256 checksum How to use checksums |
1729404a4d748e377a3065de9e03e9afb8751e4b258b7188993daa1de889e4c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / microcycler-0.1.14-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | microcycler-0.1.14-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 3.5 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
37656d76fe5d9459ece159f69b7ef2e8c14ef21df3d1158eb534701973b670ee
|
|
BLAKE2b-256 checksum How to use checksums |
5b2dc0adc41a2b29e5eae863adb34b295b401eb3864ad876ae7ee873396ae306
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / microcycler-0.1.14-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | microcycler-0.1.14-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 3.6 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
7d4a10d4466b10220c7e1ac78ced0e9ea8dd207d1a72a02864e75fdb5910afff
|
|
BLAKE2b-256 checksum How to use checksums |
9fb904c041e2f20252634bc8e78cab77bd8e94ce059669fb58ee833783b0b403
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Security for microcycler 0.1.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.