Writing Apps
Here's a the example app that should show the calls for most of the things you might want to do
You can do literally anything the Jumperless can in an app, so if there's a specific thing, lmk and I'll write an example. Until I make this into a proper operating system, what you're doing when you write an App is just writing a function in the main firmware. There's really no guard rails, and the API is just any function in the firmware.
If you don't want to recompile anything, you don't have to. A .py script saved on the board's file system is an app too, you just pick it from the Files row on the clickwheel and it runs. See MicroPython and the File Manager. From Python you can also run any of the built-in apps by name with jumperless.run_app("Custom App").
First get it PlatformIO set up to flash code
So fork the firmware here: https://github.com/Architeuthis-Flux/JumperlOS
I'm using PlatformIO in VSCode. Open the cloned JumperlOS folder itself, platformio.ini is at the root so the whole repo is the PlatformIO project.
The build environment is jumperless_v5. There's also jumperless_v5_debug (flashes over SWD), jumperless_v5_erase (wipes the file system while flashing) and jumperless_og for the original RP2040 Jumperless. CodeDocs/CONTRIBUTING.md has the pio run / pio run -t upload steps.
If your board doesn't come up on the port PlatformIO expects, comment out (or change) upload_port = /dev/cu.usbmodemJLV5port1 and monitor_port = /dev/cu.usbmodemJLV5port1 in platformio.ini so it'll just automatically find it. On macOS with a stock V5 that's the name it enumerates as, so you may not need to touch them.
You should probably try to just load the firmware just to make sure everything works.
To write an App
Before you go writing your app, follow these steps to make it so it's listed in the App library and you can run it from the menus.
-
Go to
src/remembering/menuTree.hand add the name of your app under"Apps". Each menu row is its own quoted string in the array, and an Apps entry looks like"-Custom \31App". The leading-puts it one level underApps, and the\31is the line break between the two halves. It needs to fit in 7x2 chars to show on the breadboard. -
Go to
src/Apps.hand declare your function where you'll write your app, a plainvoid yourApp(void);next to the other prototypes. The body goes insrc/Apps.cpp, or any.cppundersrc/, every folder is on the include path. -
Go to
src/Apps.cppand add a row toapps[NUM_APPS]for your app:{"Name", index, works, function}. The name is up to 19 chars,indexis informational,workshas to be1or the app gets skipped withApp not found, andfunctionis the onerunAppactually calls. The table is exactly full, so bumpNUM_APPSinsrc/Apps.hby one for each row you add or it won't compile. -
The name in
menuTree.hand the name inapps[]have to match. Picking the row on the clickwheel passes the row's own text torunApp, so"-Custom \31App"and"Custom App"line up once the\31is dropped and runs of spaces are collapsed. Case doesn't matter. If they don't match, the row just printsApp not foundand does nothing. -
You don't need to edit
runApp()itself. It finds the app by name inapps[](or by array position when you give it a number) and calls that row's function pointer. -
Make a function that's the entirety of your app, I just pushed a demo function called
customApp(void)with some (non exhaustive) examples of things you can do from an app. -
Run your app with the clickwheel,
Apps > Custom App.
From the terminal you run it from MicroPython with jumperless.run_app("Custom App") (the > prefix works, so >run_app("Custom App")). There's no digit shortcut.
If you want your own single-character shortcut, find an unused one and register it in src/SingleCharCommands.cpp with registerCommand('<char>', "short description", "help text", callback, ...), where the callback calls runApp(-1, (char*)"Your App Name") and returns CMD_DONT_SHOW_MENU. Run it by name, not by number.
To actually write the app
The code for Custom App is customApp() in src/Apps.cpp (line 1358 in 5.7.11.0). It's mostly uncommented, but it shows the calls: addBridgeToState / removeBridgeFromState plus refreshConnections, setTopRail, setDacByNumber, readAdcVoltage, INA0 current and bus voltage, probing.justReadProbe and checkProbeButtonState, and b.print on the LEDs. Fair warning, it paints Fuck you! on the breadboard at one point. There are tons more, but what's shown there are the higher-level helper functions that should roughly do what they say they're doing. If you want to skip the router entirely, sendXYraw(chip, x, y, 1) sets a single crosspoint on one of the crossbar chips directly (and 0 instead of 1 clears it), it's the same thing as send_raw() in MicroPython.
If your app changes connections, call SlotManager::getInstance().enterTemporarySlot(8) at the start and leaveApp() on every way out, so the user's slot comes back the way they left it.