+++ title = "Drive the UI from your desk" description = "Press keys, take screenshots, fake inputs and test the awkward paths (updates, crashes, fixed IPs, crowded folders) without touching the device." weight = 4 [extra] tag = "Console" +++ Everything the keyboard can do, a command can do, and everything on the screen can be looked at remotely. That makes the Cardputer testable like a web page: **act, look, repeat**. ## Keys ``` key up|down|left|right|select|back|home|del|tab|space key a # any single character: it is typed ``` Two things to know before you use them: 1. **A name `key` does not know is `select`.** `key sleect` presses Enter. A single character is typed as that character; anything else that is not a known name is treated as Enter. Check what you type. 2. **A key that wakes a dark screen only wakes it.** The device's power policy swallows the key press that turns the screen back on, as it does for the real keyboard: the first `key` after the screen went off does nothing else. Send `key back` (harmless) first, or keep the screen on with the `normal` and `short` commands below. `Fn` combinations, modifiers and the compose key have no command: the arrows are `key up|down|left|right`, and `key back` is the back key (`` ` `` on the device). Text is typed one character at a time. ## Look before you press **Take a screenshot before any key that deletes, renames or installs.** A blind sequence of `key` commands goes wrong the moment the screen is not where you think it is, and the screen is often not where you think it is: a Toast, a dialog that has not closed, a different App. A sequence that was meant to open a note once renamed real data instead. ```sh scripts/rdbg.py key home # to the Launcher scripts/rdbg.py screenshot a.png # look: is it the Launcher? scripts/rdbg.py key down scripts/rdbg.py key select scripts/rdbg.py screenshot b.png # look again before the next destructive step ``` - Prefer **reading a state** to assuming it: `info`, `ls `, `cat `, `irc dump`, `gnss status`, `lora status`, `wifi status`, `update status`. - Test on a **scratch folder** on the card, not on your real files. - For anything that deletes (`rm`, a delete dialog), `ls` first and `ls` after. ## Keep the screen on, and make timeouts short ``` short # screen dims after 5 s, turns off after 10 s: to test the screen policy normal # back to 30 s and 60 s burst # five Toasts at once: to test notifications sound on | sound off ``` ## Fake the inputs Each of these puts something in, **without** the outside world: | Command | What it does | |---|---| | `lora inject [rssi] [snr]` | A packet into the LoRa Scanner as if the radio had received it. Nothing is sent | | `irc say ` | Types into an IRC buffer, commands included: `irc say 0 /join #test` | | `log ` | Adds a line to a test IRC log | | `gnss send ` | Sends an NMEA sentence **to** the GNSS receiver (the checksum is added), to configure it | | `gemini get ` | Fetches a page and reports header, size, certificate and heap use, without the App | | `gemini trust ` | Pins a certificate by hand | | `sd fill ` | **Debug Build.** Makes that many small files in a folder, to test a crowded one (the Storage App shows the first 256) | | `wifi add ` | Adds a Saved Network, so credentials stay out of the repository | ## Test the update path An update that goes wrong is the case you most want to rehearse, and a Debug Build can make it go wrong **on purpose**: ``` update status # what the device runs, what failed here before, the daily check, heap update check | update list # look at the server: the latest release, or the last ten update pretend v0.9.0 # take the running version to be v0.9.0: the latest release now counts as "new" update damage cut 50000 # the next download is cut after 50000 bytes update damage flip 100000 # ...or has the byte at offset 100000 damaged update install v0.11.0 force # try the install update probe git.twis.la # is that server's certificate accepted? (the two ISRG roots only) update daily # forget today's daily check: it runs again at the next tick update pretend off # back to the real version ``` - **`force` is needed on a Debug Build.** A plain `update install ` answers `Debug Build: update from the PC`, because installing a release would replace the console with a build that has none. `force` is accepted only on a Debug Build, and only from the console. - **A damaged download must be refused cleanly:** the Update Service checks the signature after the first 160 bytes and the image hash at the end, so a cut or a flipped byte must end in a refusal with **nothing switched**. Check `info` afterwards: both slots, and `update: confirmed`. - **An undamaged `force` install really installs** the release, into the other slot. The Debug Build stays where it was until the next update overwrites it, and a Rollback returns to it, but think before you do it. - The server is read by the device itself, with Wi-Fi up; the download is one TLS connection, about 52 KB of heap at its peak, so IRC steps aside (see [the memory limit](/howto/not-enough-memory/)). `status: heap` in the stream shows it happen. For crashes during Probation, see [Crashes and Safe Mode](/dev/debug/crashes/). ## Test a network change without losing the console The Debug Console runs over the Wi-Fi you are about to change, which is the usual way to lock yourself out. A **trial IP setting** takes care of it: ``` wifi ip MyNet 10.39.39.50/24 10.39.39.1 try 60 # use this address for 60 s... wifi ip keep # ...and keep it, if you could still reach the device ``` If you do not send `wifi ip keep` in time, the device goes back to the **previous** setting by itself, and the console comes back with it. (A Debug Build command: `try` is not in release builds.) ## Measure ``` info # firmware, uptime, last start reason, heap now/lowest/largest block, chip temperature, Wi-Fi, SD faults, both app slots tasks # each task over the next second: state, priority, least free stack, CPU share; each core's load; main-loop passes net # bytes each network service has read and written since start ``` `tasks` takes a second to answer. A low number in the `stack` column is a risk (the System App shows it in the warning colour under 512 bytes). `loop spin on|off` (Debug Build) makes the main loop spin without resting, to compare load and radio noise. And the `status: heap` line every 10 seconds in the stream is the cheapest memory trace there is: watch it while you do the thing you suspect. ``` task st pri stack cpu% core loopTask R 1 1828 1.3 1 wifi B 23 4072 0.7 0 debug B 1 3088 0.5 -1 ... load: core 0 2 %, core 1 1 % loop: 50 passes in the last second, chip 35.3 C ``` The [System App](/guide/system/) shows the same, live, on the device. ## Radio experiments `lora preset ` and `lora custom [preamble]` change what the receiver listens to (receive only: the radio never transmits), `lora sweep on [from] [to] [step]` takes a survey, and `lora noise test [gnss|quiet]` (Debug Build) runs a Sweep under one changed condition at a time, with Wi-Fi off for a moment, to find what raises the noise floor. `lora probe` finds the radio and reports its chip, oscillator, antenna switch, interrupt line and noise floor. Details in the [command reference](/dev/debug/commands/).