Getting Started¶
Install¶
Choose the release path before installing:
- Using ScrollKit in a desktop project: install the stable PyPI release.
A version tag such as
v0.8.4corresponds to a released package. Pin the version in production/reproducible builds. - Contributing or trying unreleased work: clone
masterand install it editable.masteris a development branch, not the normal consumer path. - Deploying to CircuitPython: copy a tested source or matching
.mpybuild into the app'slib/payload. Devices should not followmasterdirectly.
For a stable desktop install, use the simulator extras (pygame + numpy + Pillow):
pip install "scrollkit[simulator]"
# Or pin a known release: pip install "scrollkit[simulator]==X.Y.Z"
To modify ScrollKit itself or run the bundled demos (what the rest of this page
assumes), clone the repo and install it editable — your edits to src/scrollkit/
take effect without reinstalling:
git clone https://github.com/czei/scrollkit.git
cd scrollkit
pip install -e ".[simulator]"
The legacy-looking release-* branches and an optional live branch described
in OTA Updates are application deployment channels. They are not
alternative package-install branches.
Run anything from the repo root with src on the path:
PYTHONPATH=src python demos/easy/hello_world.py
Running the tests
Use PYTHONSAFEPATH=1 PYTHONPATH=src python -m pytest test/unit/....
PYTHONSAFEPATH=1 keeps the repo root off sys.path (see make test-unit).
Your first app¶
import asyncio
import sys
from scrollkit.app.base import ScrollKitApp
from scrollkit.display.content import ScrollingText
class HelloWorldApp(ScrollKitApp):
async def create_display(self):
if sys.implementation.name == "circuitpython":
return await super().create_display()
from scrollkit.display.simulator import SimulatorDisplay
return SimulatorDisplay(width=64, height=32)
async def setup(self):
self.content_queue.add(
ScrollingText("Hello, World!", y=12, color=(0, 255, 128)))
asyncio.run(HelloWorldApp().run())
On desktop this opens a window showing the simulated 64×32 matrix. On a supported
CircuitPython board such as the MatrixPortal S3, the platform branch delegates to
the base app and UnifiedDisplay drives the physical panel. Keeping the
desktop-only simulator import inside that branch is important: importing it at
module scope would fail on CircuitPython. If you omit the entire
create_display() override, the app still runs headless on desktop.
Writing an app¶
Subclass ScrollKitApp and override the hooks you need; the framework runs
them as cooperative async tasks.
import asyncio
from scrollkit.app.base import ScrollKitApp
from scrollkit.display.content import ScrollingText
class MyApp(ScrollKitApp):
def __init__(self):
super().__init__(enable_web=False, update_interval=60)
async def setup(self):
self.content_queue.add(ScrollingText("Hello from ScrollKit"))
async def update_data(self):
... # fetch fresh data every update_interval seconds
asyncio.run(MyApp().run())
Deploying to hardware¶
Before installing software, connect the MatrixPortal S3, HUB75 ribbon, power leads, and LED panel by following the photographed hardware assembly guide. Then deploy the software:
- Connect a supported board such as the MatrixPortal S3 over USB (it mounts as
CIRCUITPY). - Copy the app's own
code.py, optionalboot.py, and package tree according to that app's imports. - Install ScrollKit at
CIRCUITPY/lib/scrollkit/. From this repository,make copy-to-circuitpyperforms only this library copy; it does not deploy an app's root files orsrc/tree. - Install the matching Adafruit bundle dependencies described below.
- Configure WiFi — two ways:
- On the device itself, no file editing (the end-user path): wire the
onboarding portal into your app's
setup()and the panel walks the user through joining the device's own access point and picking a network from a phone — see WiFi onboarding portal. secrets.py(the developer shortcut): addsecrets = {"ssid": "your-network", "password": "your-password"}on the device (the standard CircuitPython convention — seescrollkit.utils.url_utils.load_credentials). Portal-saved settings take precedence oversecrets.py.
- On the device itself, no file editing (the end-user path): wire the
onboarding portal into your app's
The same app code runs unchanged: UnifiedDisplay auto-selects the hardware
backend on CircuitPython and auto-detects which board it's on (pass board="..."
to force one). See Adding New Hardware for board ports.
CircuitPython dependencies (circup)¶
The device also needs the Adafruit libraries ScrollKit uses (e.g.
adafruit_requests, adafruit_httpserver, adafruit_display_text, and
adafruit_bitmap_font). The MatrixPortal S3 additionally needs
adafruit_matrixportal. Manage the bundle libraries with
circup:
pip install circup
circup install adafruit_requests adafruit_httpserver adafruit_display_text adafruit_bitmap_font adafruit_matrixportal
Saving RAM with .mpy (optional)¶
The MatrixPortal S3 is memory-constrained. Cross-compiling the library to
.mpy loads faster and uses less RAM than shipping raw .py.
Do not pip install mpy-cross — that PyPI package is MicroPython's
compiler, and CircuitPython rejects its bytecode with
ValueError: incompatible .mpy file. Use the binary Adafruit builds from
CircuitPython itself: download the one matching your board's CircuitPython
version from the
mpy-cross index,
chmod +x it, and put it on your PATH as mpy-cross (or pass
MPY_CROSS=/path/to/it to make). Then:
make mpy # -> build/scrollkit/*.mpy
Then copy build/scrollkit/ to the device (e.g. CIRCUITPY/lib/scrollkit/)
instead of the raw src/scrollkit/. The .mpy format is stable within a
CircuitPython major family (9.x/10.x share one); recompile with the matching
mpy-cross when the board moves to a new major. Shipping .mpy over OTA
has more rules — see OTA Updates.
Next: the Easy tutorial.