SolarOS Embedded Apps
Run apps to list the applications available in your firmware. Start an app by its command name, with any arguments shown below.
Common controls
| Action | Display | Port terminal |
| Close the current app | Ctrl+Alt+Del | Ctrl+] |
| Switch to a retained session | Alt+Tab or Alt+Right; previous with Alt+Left | Ctrl+Z to suspend, then fg or fg ID to resume |
| Move local input between displays | Ctrl+Alt+Arrow | session focus TARGET |
Either Alt key works, including AltGr. Apps may also provide Esc or Q to exit. In shared text menus, Alt+Enter toggles full screen; on Pi0cket CL-32 use File+OK. Small text menus start in full screen automatically.
Closing returns to the shell. Python/Lua stdout, errors, and tracebacks remain in shell scrollback; app screens are discarded. Errors return to the shell with a diagnostic. Run status to check the most recent app exit code: 0 means success. See Sessions for switching and display focus.
messages
Manage messaging instances, connections, and local message history on the display or a VT100 terminal. Run messages without arguments to open the TUI. Existing messages subcommands remain available for shell and scripting use.
Press Tab to switch between Instances, Connections, and Messages. Use the arrow keys to select a row, Enter to open it, a for actions, and ? for help. f selects an instance or All instances. The selected instance is shared between tabs. Esc returns to the previous view; q closes the TUI.
- Instances:
n adds an instance using a provider form, e edits its settings, and d removes it after confirmation. Select a field and press Enter to edit it in place with a visible insertion cursor; s saves the form. The other fields remain visible. Unchanged secret fields keep their saved values. Submitting an empty secret field clears it. Removal deletes local history, queued sends, settings, and credentials for that instance. A Deleting... popup stays visible while removal runs. Choose SC2 to enter a relay address such as chat.solar-os.eu:1337 (TLS), a full chats:// or chat:// URL, or a bare domain for signed HTTPS discovery. Domain discovery fills in the endpoint, TLS pin, relay signing key, and fallback relays. An admission code remains optional and must be supplied by the operator when required. The other optional fields are the TLS pin and relay signing key (base64). New SC2 instances start disabled. Changing the relay URL resets its TLS and signing-key pins unless you also edit those fields.
- Connections:
c enables the selected instance and x disables it. s starts its background worker; the same action is in the a menu and connection details. Workers that are already running or waiting for memory are left active. A shared worker serves the enabled instances of its provider. Enter shows connection state, worker state, and errors. Enabling an instance and starting its worker are separate actions. Link transports prompt for the worker name and settings, as used after job start, for example radio-link mylink radio0 PROFILE chat=on or espnow-link mylink chat=on. Use the transport name saved in the Link instance.
- Messages:
v cycles through conversations, unread messages, and the pending outbox. Enter opens conversation history or a message's full text. Page Up loads older records and Page Down loads newer records. c opens Chat for the selected conversation; closing Chat returns to this browser. r marks the selected conversation read. d deletes one selected local message after confirmation. A pending send must first be cancelled with x before its history can be deleted. Local deletion does not retract a remote message.
Message browsing uses the persistent Messaging archive. The universal Inbox also contains notifications from other applications; see inbox for those. See Messaging for provider settings, trust, and storage behavior.
dlp
Use a named SolarOS display as a Display Link Protocol receiver. The dlp package requires graphics support. One receiver may run at a time.
dlp serve display0 stream=cdc0
dlp status
dlp stop display0
Run these commands from the local display shell or a separate port/network shell. The selected stream must provide exclusive, ordered, duplex bytes. Release any shell, log, or bridge owner on that stream first; DLP needs both directions for commands and replies. DLP does not configure baud rates, establish network connections, or add a BLE/radio transport.
The receiver runs as a foreground session on the named display, suspending its previous session. It advertises the current display orientation and MONO1 pixels (black=1, least-significant pixel first). Targets with direct RGB565 presentation also advertise RGB565 with big-endian pixel bytes. The host must use an advertised format. Updates are staged; the display changes on PRESENT. Changed rectangles and RLE are supported. A new connection/session must send a full image before partial updates. The receiver also supports negotiated upload windows of up to 16 packets. Uploads receive cumulative progress acknowledgements, and the final reply waits for presentation completion. Lost or corrupt packets are retransmitted within the bounded window; retries do not repeat accepted writes or PRESENT.
MONO1 uses direct presentation where supported. Displays with an INDEX2 raster backend use batched MONO1 conversion and send changed regions, without rebuilding a graphics canvas. This path needs an extra two-bit-per-pixel conversion buffer (38,400 bytes at 480×320). Initial frames and resume redraws repaint the full display. Other targets use the regular graphics canvas.
Ctrl+], Ctrl+Alt+Delete, Escape, or dlp stop <display> closes the receiver, releases the stream, and restores the previous session, including while waiting for a host. Suspension pauses protocol processing; resuming redraws the last completed image. Provider disconnects and I/O errors leave the app waiting for a new connection, retaining the last completed image. The receiver retries opening an unavailable stream. Each reopened transport starts a fresh protocol session and requires a full image. Serial links with no disconnect indication remain listening until stopped.
The desktop test sender requires Python, pyserial, and Pillow:
python3 -m pip install pyserial Pillow
python3 scripts/dlp_image.py --port /dev/ttyACM0 image.png
python3 scripts/dlp_image.py --port /dev/ttyACM0 --demo --frames 20 --stats
python3 scripts/dlp_image.py --port /dev/ttyACM0 --demo --fps 30 --frames 100 --stats
python3 scripts/dlp_image.py --port /dev/ttyACM0 --demo --frames 6 --reconnect-every 2
Start the receiver before running the sender. The script discovers geometry, fits images on a white background, and sends MONO1 frames. --legacy-cells tests fixed-cell framing; compact DLP framing is the default. This is an image test tool; desktop virtual-monitor integration belongs to a host project. Windowed uploads are negotiated by default, with automatic fallback for older receivers. --no-window forces the original stop-and-wait behavior for comparison; it is independent of --legacy-cells. After display suspension invalidates retained content, the sender starts a new session and repaints fully.
For Wi-Fi, create a raw TCP stream from a separate device console:
stream tcp listen tcp0 7777
dlp serve display0 stream=tcp0
Then run the desktop sender, replacing the example address with the device IP:
python3 scripts/dlp_image.py --tcp 192.0.2.10:7777 --demo --fps 30 --frames 100 --stats
TCP needs Pillow but does not need pyserial. Use the raw TCP listener, rather than a Telnet shell port. Closing the TCP sender leaves DLP waiting on the same listener. Connect a new sender without running dlp serve again. To remove the endpoint, stop DLP first if it is running, then run stream tcp remove tcp0.
For a BLE connection between two SolarOS devices, configure a CoC listener on the receiving device and start DLP:
stream ble listen ble0
dlp serve display0 stream=ble0
On the sending device, use ble scan to find the advertised SolarOS ble0 peripheral. Copy its address and address type into the connect command:
stream ble connect ble0 aa:bb:cc:dd:ee:ff public
expansion attach stream-dlp remote stream=ble0
display test remote
The central/peripheral roles belong to the BLE connection; either side can serve a display or send frames. Release the consumer to close the connection. DLP serving waits for a new connection after a disconnect. An attached stream-dlp display reconnects on a subsequent presentation attempt. To remove an endpoint, stop DLP or detach the display, then stream ble remove ble0. See BLE stream commands.
--stats prints actual completion rate, byte counts, and host wall times per input frame: prep generates/loads and converts the image; upload plans and sends the update, including intermediate window checkpoints; present_wait runs from PRESENT through the completion reply. With windows it also includes sending the final buffered upload batch. total includes preparation and any requested reconnect, and excludes the intentional frame-rate sleep. The final summary counts presented frames and unchanged images separately; unchanged images do not increase completed FPS. The default rate limit is 2 FPS; --fps 30 sets a 30 FPS ceiling and includes image preparation in that budget.
Run dlp status from a separate shell for receiver-side last/average/maximum timings in microseconds. render measures MONO1 canvas preparation or changed-area detection and batched INDEX2 conversion; display measures the display-service call, including driver conversion, rotation, tile checks and I/O. Unconverted direct rasters have zero separate render time. All driver work is included under display. total is their sum. These measurements exclude wire upload, startup and resume/overlay redraws; they measure service completion, not panel scanout. Completion rate averages the intervals between the first and last DLP frame, including host pacing. The statistics remain available after stopping, until the next receiver start. Host present_wait includes stream transfer, round trips, scheduling and legacy status polling, so it is not a device-only or SPI-only measurement. Diagnostics never enter the DLP byte stream.
rtsp
Play a trusted-LAN RTSP stream using UDP RTP/JPEG video and RTP/L16 audio.
rtsp rtsp://192.168.1.238/media
rtsp 192.168.1.238/media
rtsp user:secret@192.168.1.192:8554/youtube
rtsp --audio-only rtsp://192.168.1.113/media
rtsp --stats rtsp://192.168.1.192:8554/youtube
rtsp --audio-only --stats rtsp://192.168.1.192:8554/youtube
rtsp-auth set 192.168.1.50 admin camera-password
rtsp rtsp://192.168.1.50/media
The rtsp:// prefix is optional. The RTSP control connection uses TCP port 554 unless a port is supplied; RTP media uses negotiated UDP ports. Addresses without a path are accepted; include the publisher's path when required.
RTSP playback keeps Wi-Fi awake while connecting, playing, and reconnecting to avoid delayed or dropped live media. Normal power saving resumes after playback stops, unless another active service still needs low-latency Wi-Fi.
Inline credentials use user:password@host; percent-encode spaces and reserved characters in the username or password. rtsp-auth set <host[:port]> <user> <password> saves up to four accounts for URLs without inline credentials. Run rtsp-auth to list accounts, or rtsp-auth clear [host[:port]] to remove one account or all accounts when the host is omitted. The player hides passwords.
Graphical sessions show aspect-fit JPEG video when a video track is selected. Audio-only sessions use the common audio GUI oscilloscope, fed by playback PCM, filling the available area between the header and player controls. Port shells select audio only and print playback status. --audio-only skips video negotiation and video buffers even when the publisher offers both tracks. Audio uses the selected default output and global volume; Up/Down adjust volume (+/- remain aliases). The graphical player uses the common header, volume bar, and Stop/Play button. Enter, Space, or a tap on the button stops playback without closing the app; Play reconnects to the same URL. Stopping releases the network, decoder, audio output, and media buffers once their workers finish. RTSP URLs are single sources, so there are no Previous/Next buttons. F toggles fullscreen, hiding the header and controls while retaining aspect-fit video or expanding the audio-only oscilloscope to the whole screen. D toggles the separate frame diagnostic overlay: received, displayed, network/app drops, and displayed frames per second. Q, Escape, or the normal application-exit key closes playback. Suspending keeps audio playing and pauses video decoding until you return. Closing releases the audio output and network connection.
Temporary connection failures retry up to six times. Unsupported codecs, failed or unsupported authentication, and local memory or audio failures stop immediately. The shell diagnostic identifies the failing operation. Use --stats and log show for frame counts, drops, timings, audio buffering, and retry details. The D overlay works without logging; --stats also enables it initially.
Requirements and limits:
- IPv4 RTSP 1.0 with unicast UDP media. Client authentication supports Basic or Digest MD5 (
qop=auth or no qop); the rtspd publisher remains unauthenticated.
- One JPEG video track and/or one L16 audio track (8–48 kHz, mono or stereo).
- JPEG types 0/1 with explicit 8-bit quantization tables (Q=255); restart markers are supported.
- TCP interleaving, multicast, encrypted RTSP, compressed audio, and redirects are unsupported.
- Five seconds without media triggers reconnection. Retry exhaustion returns an error to the shell.
rtspd allows one receiver at a time; stop another receiver before connecting.
- Microphone capture cannot be opened while playback is active.
If playback reports no memory, use mem to check internal memory and its largest free block, then stop unused jobs. PSRAM alone does not meet audio and DMA memory requirements.
agent
Native Responses/Chat-Completions LLM client and SolarOS agent control plane. It streams model text directly to the active shell and exposes typed system-status, storage-listing, job-listing, display-discovery, and optional Python/Lua execution tools. agent tools shows risk, policy, and runtime availability.
Configure the full endpoint and model, then ask a question:
agent config endpoint https://api.openai.com/v1/responses
agent config model gpt-model
agent config key api-key
agent config reasoning medium
agent config tools confirm
agent config max-tools 16
agent
job start speechd /voices/en-US
agent --tts
agent list
agent resume SLOT
agent ask How much memory is free on this device?
agent script python -c "print(6 * 7)"
agent script lua /script.lua argument
Bare agent and agent new open a new foreground prompt loop. Completed turns are saved, and agent list plus agent resume SLOT restore a selected transcript after leaving the app or rebooting. agent delete SLOT removes one conversation without changing provider credentials. Responses uses its saved provider continuation ID; Chat Completions uses bounded local history. Slots are 1 to 3 on internal flash or 1 to 8 on SD; the oldest is reused when full. agent ask performs one unsaved request but likewise waits for Esc or the app-exit key after completion, so display-shell output is not immediately replaced by the shell screen.
agent --tts, agent --tts new, agent --tts resume SLOT, and agent --tts ask PROMPT keep the written answer and also queue the successful final model response through the running local PicoTTS speechd. Start speechd with a voice directory first. Status and retry messages, tool calls, tool results, confirmations, and intermediate text before a tool call are not spoken.
Use agent config key clear for an endpoint that does not require bearer authentication. agent status shows configuration, request counters, HTTP status, reasoning effort, duration, traffic, and internal-RAM/PSRAM measurements from the last request. The API key itself is never printed. A Chat-Completions-compatible endpoint can still be configured explicitly.
The agent allows 16 tool calls per request by default. Set a limit from 1 to 32 with agent config max-tools COUNT. When the budget is used, it gives a final answer from the results already collected. Answers are limited to 16 KiB.
agent script supports Python or Lua with a 30-second deadline and up to 4095 output bytes. Model-generated scripts are limited to 640 source bytes and 383 output bytes. Esc or the app-exit key cancels execution.
The storage registry includes bounded listing, sensitive text-file reads, and text-file replacement. Reads and writes are capped at 3072 bytes and paths below .ssh are unavailable to protect SSH identity files.
The default confirm policy runs read-only tools automatically and shows the exact arguments of sensitive, mutating, disruptive, and script calls before waiting for a local y/N decision. off disables tools, readonly excludes all protected risk classes, and explicit all allows every available tool without prompting. See Native Agent Service for the provider contract and current limits.
Controls:
Esc or the app-exit key cancels an active request and exits.
Page Up/Page Down scroll terminal output while a request is active.
aplay
Play audio files through the default registered playback endpoint. WAV and MP3 are supported when an output device is present. MP3 decoding is provided by the shared, device-independent audio codec service. aplay prints the source details, plays the file once through the shared background audio player, and then returns to the prompt without clearing existing terminal output. It can be launched from display, UART, USB CDC, Telnet, and other port shells.
Usage:
aplay [-v volume] file.wav|file.mp3
Controls:
Esc or app-exit stops playback early and returns to the prompt.
arecord
Record a registered capture endpoint to a WAV file. The default is the first compatible input; -i selects a specific stream such as adc0.capture. This requires a registered input device, but not built-in board audio. With -d, recording stops after the specified number of seconds. Without -d, recording continues until app-exit, the storage fills, or the WAV size limit is reached. It can be launched from display, UART, USB CDC, Telnet, and other port shells.
Usage:
arecord [-d seconds] [-i capture-stream] file.wav
Controls:
- App-exit stops recording, finalizes the WAV header, and returns to the prompt.
recorder
Interactive GUI/TUI counterpart to arecord. Recorder writes PCM WAV files so the channel count, sample rate, and resolution travel with the recording and the result can be played immediately. It accepts any registered signed-16-bit PCM capture stream. On the Waveshare board it initially selects audio0.capture. Mono/stereo output, 8/16-bit file resolution, and sample rates from 8 kHz through 48 kHz are converted from the selected stream as necessary.
Usage:
recorder [--tui] [file.wav]
The optional path supplies the initial recording folder and filename. The Setup view can edit the filename or select another destination directory with the shared file browser. When the filename is empty, each recording gets a unique local-time name such as rec-260810-113045.wav; a numeric suffix avoids overwriting a file created in the same second. Recorder remembers its selected input, folder, format, hardware input gain, output volume, and visualizer in .recorder/settings.bin on the current storage root. An explicit path argument overrides the remembered folder. The filename is not remembered, so reopening Recorder cannot accidentally reuse a one-off name.
Recorder selects its graphical interface on a graphical shell and its text interface on a port shell. --tui forces the text interface even when the launching shell has graphics.
On displays narrower than 240 pixels or shorter than 180 pixels, the graphical media controls use an 8-pixel bitmap font and smaller transport buttons. Short displays show volume as a percentage beside the filename instead of a volume bar. Lists scroll to keep the selected entry visible, and edit dialogs fit the display. When the cassette cannot fit, the visualizer uses a scope.
Tab switches between Record and Setup. The Record view uses the shared cassette widget by default; V cycles through Cassette, Oscilloscope, and Spectrum. R starts recording, Space pauses or resumes, S stops and finalizes the WAV header, and P plays the most recent or selected recording. M starts or stops monitoring: it continuously streams the selected input to the default output and visualizer. Monitoring can remain enabled when recording starts and can be toggled while the WAV writer continues. Up/Down changes monitor and playback volume. The cassette reels move during recording and playback, but remain still during monitoring. Oscilloscope and Spectrum remain live during monitoring. Active visualizers are presented on the 40 ms app tick (nominally 25 frames per second), independently of the audio block rate.
Setup selects the capture stream, recording folder, mono/stereo conversion, sample rate, 8/16-bit WAV resolution, input gain, and output volume. Enter on Folder opens the WAV-filtered browser. D selects its current directory as the recording folder; Enter on a WAV file plays it and returns to Record. Input gain is not a generic stream multiplier: Recorder enables it only when the selected capture stream belongs to an audio device that advertises hardware input-gain control. Thus audio0.capture on the Waveshare controls the ES7210 microphone gain, while an unrelated generic stream shows Input gain: n/a. The hardware gain is system-wide; Recorder does not add hidden digital amplification to the saved PCM.
The text interface exposes the same setup values, browser, transport keys, monitoring, pause behavior, and playback path. Recorder is resumable: capture, monitoring, or playback continues while its UI session is in the background, and closing the app stops the worker and finalizes an active recording.
funcgen
Audio-only function generator built on the shared real-time Synth service. It emits signed 16-bit stereo PCM and can use the default playback device or an explicitly selected runtime playback stream, including an attached LEDC PWM audio expansion.
Usage:
funcgen [--tui]
The graphical interface shows the exact generated PCM in the shared oscilloscope widget. The remaining controls select sine, square, triangle, saw, pulse, or noise output; frequency from 20 through 8000 Hz; amplitude; pulse width; a repeating linear sweep; sweep end frequency; sweep time; and playback stream. The TUI exposes the same controls. --tui forces it on a graphical shell.
Small graphical displays show one selected control at a time, with its position in the eight-control sequence. The waveform remains visible when space allows. Short terminals scroll the controls to keep the selection visible. Long output names are clipped to the available width.
Controls:
Left/Right selects a control.
Up/Down, +/-, or Enter adjusts the selected control.
Space starts or stops output.
Esc, Q, or the app-exit key closes the application.
The output is off initially. Frequency is capped below the selected stream's Nyquist limit during rendering. A sweep moves linearly from Frequency to Sweep end during Sweep time, then repeats without resetting oscillator phase. Suspending the UI leaves an active generator running; closing it stops the Synth worker and releases the stream.
All controls can be targeted by the shared control-binding system while Funcgen is active:
funcgen.waveform: 0 sine, 1 square, 2 triangle, 3 saw, 4 pulse, 5 noise.
funcgen.frequency: 20 through 8000 Hz, logarithmic.
funcgen.amplitude: 0 through 100 percent.
funcgen.pulse.width: 1 through 99 percent.
funcgen.sweep.enabled: 0 off or 1 on.
funcgen.sweep.end: 20 through 8000 Hz, logarithmic.
funcgen.sweep.time: 100 through 60000 ms, logarithmic.
funcgen.output: runtime output index; 0 follows the default output.
funcgen.enabled: 0 off or 1 on.
player
Interactive WAV/MP3 player and the user-facing counterpart to aplay. player keeps a persistent playlist under .player on the current storage root. Opening an audio file from Files adds it to that playlist, selects it, and starts playback. Missing files remain listed so removable media can be reattached.
Usage:
player [--tui] [file.wav|file.mp3]
Player selects its graphical interface on a graphical shell and its text interface on a port shell. --tui forces the text interface even when the launching shell has graphics. The graphical controls and lists adapt to small displays, including 240×64 and 128×64 targets.
On a graphical session, Tab switches between Play and Playlist. The Play tab uses the space above the shared bottom controls for a cassette visualizer by default; V cycles through Cassette, Oscilloscope, and Spectrum. The cassette reels turn only while audio plays and show track progress when duration is known. Left/Right plays the previous or next track in the playlist ring, Enter plays or stops, Space pauses or resumes, and Up/Down adjusts volume. </> seeks backward/forward by ten seconds without changing tracks; rewind/forward buttons provide the same actions. Seeking preserves pause state and discards queued audio. The controls show the track name, playback status and time above the volume bar. The middle button stops or starts playback and indicates pause while paused. On the Playlist tab, Up/Down selects, Enter starts the track and returns to Play, A opens the WAV/MP3 file browser, and Delete removes the selected playlist entry.
The text interface is one playlist screen: Up/Down selects, Enter plays or stops, Space pauses or resumes, </> seeks backward/forward ten seconds, A opens the filtered file browser, Delete removes an entry, and Esc exits. Its bottom status line shows the playing, paused, or stopped state with elapsed and total time. Playback follows the resumable app while another foreground session is selected and stops when Player closes. End of file advances to the next playlist entry.
Player shows SEEKING while preparing a new position. MP3 seeking can take longer on slow storage, including with variable-bitrate files. Stop playback to cancel a seek.
WAV playback converts the file's mono/stereo channel count and sample rate to the selected output stream. A mono recording therefore plays through a fixed stereo device without changing the recording stored on disk.
calc
Scientific calculator and function plotter. On a graphical display, calc opens an expression list beside a Cartesian plot. From UART, USB CDC, Telnet, or any other text-only shell, the same command opens a scientific REPL without the plot pane. calc --tui forces that REPL even when graphics are available.
The expression engine supports +, -, *, /, %, powers with ^, parentheses, scientific notation, and implicit multiplication such as 2pi or 2(x + 1). Built-ins include sin, cos, tan, their inverse functions, sqrt, abs, exp, ln, log, floor, ceil, round, min, max, pow, and atan2. Trigonometric input is in radians; rad(degrees) and deg(radians) convert explicitly. Constants are pi and e.
Rows can hold scalar calculations, variables, one-argument functions, or plots:
a = 2
f(x) = sin(x) / x
f(pi / a)
y = f(x)
For a one-off shell calculation, use:
calc -e "sqrt(2)^2"
Text REPL commands:
:list or :vars shows worksheet rows and scalar results.
:del N removes a row; :clear empties the worksheet.
:save [file] and :load [file] write or restore one expression per line. The default file is calc.txt in the current shell directory.
:help shows the compact reference; :quit returns to the shell.
- Up/Down recalls input history; Left/Right, Home, End, Backspace, and Delete edit the current line.
Graphical controls:
- The expression editor keeps a white background for legible small text and outlines the active row with a thin dark border.
- Type to edit the selected row; Up/Down changes rows and Enter adds a row.
- Left/Right, Home, End, Backspace, and Delete edit within a row.
- Page Up toggles the selected graph row.
- Tab moves between the expression list and graph.
- In the graph, arrows pan,
+/- zoom, and Home or 0 resets the view. Press t for a numeric trace cursor; Left/Right moves it and Up/Down selects another plotted row. Press t again to resume panning.
- Ctrl+S saves and Ctrl+O loads the default
calc.txt worksheet.
- App-exit closes the calculator. The worksheet is kept only when explicitly saved.
chat
Tabbed provider-neutral conversation client. The Channels tab groups conversations by named instance, including IRC, Gateway, MeshCore, and Link. Enter selects a conversation and opens its bounded shared history on the Chat tab, which also contains the message/command input. The app opens and remains useful offline; network or radio transport jobs connect independently.
/leave removes the current channel or room from Chat's Channels tab and returns to that tab. Its local history is retained. Use /join instance channel to show the room and its previous messages again. Explicit departures survive restart; disconnecting a provider keeps its channels visible. Messages still provides access to retained history while a room is left.
MeshCore channel labels distinguish [radio] and [companion]. Entries marked (history) retain readable messages but cannot accept sends. After submitting a message, the footer follows its delivery state through queued, sending, sent, delivered, or failed.
Usage:
chat [instance|class|conversation-id]
With no selector, Chat opens a unified view and initially selects the newest unread conversation. An instance name or provider class filters the list. A decimal conversation ID opens exactly that conversation.
The background gateway-sync job owns the gateway transport connection, retries, and joined-channel replay. Each join sends the greatest stable message ID retained for that room as its cursor; the gateway replays only newer messages. Requested membership and server-confirmed membership are separate, connection-scoped states. The shared messaging service owns queued outbound messages and gateway-sync consumes only gateway requests. Start it explicitly with job start gateway-sync, just like email-sync. Closing or suspending chat does not disconnect an already-running synchronizer. Incoming messages remain in the shared bounded messaging store and publish bounded notifications to the universal inbox; reopening the app replays retained conversations from every provider. With SD storage, full messages are retained under /.messages/messages.bin. On internal flash, Chat restores the compact message copy already retained in /.inbox/messages.bin, so it consumes no second flash ring. Both backends deduplicate transport replays by stable message identity and keep linked Inbox read state aligned.
Unlike email-sync, gateway-sync takes no interval argument: it waits for Wi-Fi and reconnects with exponential backoff while remaining in the running state.
Gateway setup and room lifecycle use gateway status, gateway configure, gateway connect, gateway disconnect, gateway rooms, gateway join, gateway leave, and gateway delete. Gateway synchronization runs only under the gateway-sync job name. Selecting a known gateway room in the Channels tab requests a join automatically and opens it after the server confirms joined.
Additional Gateway connections use messages add gateway NAME URL [TOKEN], messages connect NAME, and messages join NAME ROOM. The same gateway-sync job maintains every enabled instance independently. In Chat, use /add gateway NAME URL [TOKEN], /connect NAME, and /join NAME ROOM. /remove NAME closes the instance and deletes its settings, pending sends, and local history.
Conversation rows show provider, unread, and security state. Outbound rows show queued/sending/sent/delivered/failed state. Use /new CONTACT_ID to open a direct conversation with the contact's preferred endpoint. Sending to a discovered endpoint asks for a second Enter confirmation; blocked endpoints cannot be messaged.
The conversation header reports the selected instance's state. Connection providers use disconnected, connecting, and connected; connectionless MeshCore and Link providers use stopped, starting, ready, and error.
In-app commands:
/help
/new contact-id
/join instance channel
/query instance nickname
/leave
/connect instance
/disconnect instance
/remove instance
/server instance host port nickname
/sasl instance account password
/sasl instance off
/status
/quit
Controls:
Tab switches between the Channels and Chat tabs.
- In Channels,
Up/Down select a conversation and Enter joins a known gateway room if necessary, then opens the Chat tab.
- In Chat,
Up/Down navigate input history and Enter sends input.
Page Up/Page Down scroll messages.
Esc or app-exit key exits.
clock
Full-screen graphical seven-segment clock, alarm countdown, and stopwatch. The countdown is serviced in the background, so it continues when the Clock session is suspended. It works without RTC hardware while SolarOS remains powered. With a wired RTC interrupt, the RTC alarm or countdown is also armed and explicit light sleep can wake for it.
Usage:
clock
clock -a mm:ss
clock -s
Controls:
- In stopwatch mode,
Space starts/stops.
- In stopwatch mode, any other ordinary key resets to zero.
- Exiting a countdown removes that transient alarm and stops it if ringing.
Esc or app-exit key exits.
com
Serial terminal for a bidirectional byte-stream port. Display-keyboard or port-shell input is forwarded to the selected port, and received bytes are drawn in the active terminal. The port may be a UART or a virtual port such as a peer-bound SolarOS Link stream.
Usage:
com [--autobaud] [--hex] [port]
The port defaults to uart0. For example, com gps connects to an existing runtime UART bus named gps, while com vser0 opens a Link stream previously created with link stream create. The selected port remains claimed by the app until the session exits. com works from both display and port shells; when launched from a port shell, its terminal output is returned through that same port. The input/output shell port and selected COM port must be different.
--autobaud is available only for UART buses. It samples the RX signal for three seconds before opening the terminal. Send a repeating 0x55 or 0xaa pattern during that interval. A reliable measurement is matched to a standard UART rate and applied to the current bus connection without overwriting the saved baud setting. UART input forwarding begins when sampling completes. If measurement fails, the configured rate is kept.
--hex displays received bytes as eight-byte offset, hexadecimal, and ASCII rows instead of interpreting them as terminal text. Both options can be used together.
Examples:
com --hex gps
com --autobaud --hex uart0
com vser0
Controls:
curl
HTTP client for quick text downloads and diagnostics. It can print response data to the terminal or save it to a file.
Usage:
curl [-L] [-o file] URL
Controls:
- App-exit key cancels an active transfer.
webradio
Stream a direct MP3 URL through the default registered audio output. On a graphical display shell, WebRadio opens a two-tab media-player GUI. The GUI uses compact controls, scrolling channels and fitted edit dialogs on small display targets. On UART, USB CDC, Telnet, SSH, and other text shells, it opens a station-list TUI.
The catalog is stored as .solar/webradio/catalog.bin on the current storage root and starts with the Nightride, Chillsynth, Datawave, Spacesynth, Darksynth, Horrorsynth, EBSM, and Rekt streams. Catalog changes survive reboot. On first use after an upgrade, WebRadio moves an older NVS catalog to this file and removes the large NVS blob. reset restores the initial list.
Usage:
webradio
webradio --tui
webradio https://stream.nightride.fm/nightride.mp3
webradio list
webradio add MyStation https://example.net/live.mp3
webradio remove MyStation
webradio reset
--tui forces the station-list TUI even on a graphical shell and can be combined with a direct stream URL. Catalog-management commands also accept it as a harmless interface override.
URLs are literal HTTP or HTTPS MP3 stream URLs. WebRadio does not translate station names or website addresses and does not discover streams from HTML pages. Playlists, HLS, and AAC are unsupported.
Controls:
Tab switches between the Player and Channels tabs in the GUI.
- The Player tab gives the space above the shared bottom controls to a live PCM oscilloscope or spectrum analyzer.
V switches visualizers. The spectrum analyzer shows the frequencies in the playing audio.
- On the Player tab,
Left and Right play the previous or next catalog channel. The catalog wraps as a ring. Enter stops playback or reconnects the channel; Space pauses/resumes, and Up/Down changes global volume in five-percent steps.
- The bottom controls show the channel name, playback state and elapsed output time above the volume bar, followed by Previous, Stop/Play, and Next buttons. The buttons support clicking or tapping; the middle icon indicates pause while paused. Live-channel pause uses bounded audio buffers; a server may disconnect during a long pause, requiring reconnection.
- On the Channels tab,
Up/Down selects a channel, A adds one, E edits one, and Delete removes one. Enter starts the selected channel and returns to the Player tab. Add and edit dialogs accept a name followed by a literal stream URL.
- The TUI is a single catalog screen.
Up/Down or J/K selects a channel, +/- changes output volume, A adds a channel, E edits it, and Delete removes it. Enter plays the selected channel, Space stops playback, and R reconnects it. Add and edit use inline name and URL fields on the catalog screen; Enter advances or saves and Esc cancels.
Q, Esc, or the app-exit key exits when no catalog dialog is open.
The app is available on Wi-Fi builds even when the board has no built-in audio hardware. Playback starts when a default output device has been registered, including an output supplied by a runtime-attached expansion.
Playback continues when you switch sessions. Closing WebRadio stops reception and playback and releases the output device. The visualizer follows played audio, so it does not run ahead of sound while the stream buffers.
help
Foreground browser for the package-aware SolarOS manual. The foldable tree groups the topics available in the current firmware and shows whether it is using the setup and recovery guide or a verified downloaded revision. Full guides and scripting API references require the downloaded manual; use help update to install it on devices with Wi-Fi, PSRAM, and SD storage. All groups start folded. The selection, scroll position, and fold state remain unchanged after a topic closes.
Usage:
help
help agent
Controls:
Up/Down, Page Up/Page Down, Home/End: move through the tree.
Left/Right, Enter, or Space on a group: fold or unfold it.
Enter or Right on a topic: open it in reader on graphic display shells or less on text shells.
q, Esc, or the app-exit key: return to the shell.
The maintenance forms help status, help update, and help reset remain shell operations. SD-capable builds show terminal-width-aware progress while downloading and extracting one exact-version signed manual archive. Use help command.status to open the shell status command page.
edit
Text editor for files on mounted storage. It supports cursor navigation, selection, clipboard operations, text-size changes, and syntax highlighting for known source files. The editor supports files up to 256 KiB on boards with PSRAM and 32 KiB on boards without PSRAM. Use hexedit for binary files.
Usage:
edit <file>
Controls:
- Arrows move the cursor.
Ctrl+Left/Ctrl+Right move by words.
Shift+Arrows extend selection.
Ctrl+A, Ctrl+C, Ctrl+X, Ctrl+V select all, copy, cut, and paste.
Ctrl+F or F3 opens Find. Matches are case-insensitive and wrap at the end of the file.
Ctrl+S or F2 saves in place. Ctrl+Q, Esc, F10, or the app-exit key exits. When the file has pending changes, answer Y to save and exit or N to discard the changes and exit.
Ctrl++ and Ctrl+- adjust editor text size for the active session.
hexedit
Two-pane binary editor for files on mounted storage. Each row shows a file offset, hexadecimal bytes, and their synchronized printable ASCII view. The number of bytes per row adapts to the terminal width. It uses the same 256 KiB PSRAM and 32 KiB internal-memory limits as edit.
Usage:
hexedit <file>
Controls:
Tab switches input focus between the HEX and ASCII panes. The corresponding byte remains highlighted in both panes.
- Hexadecimal digits replace the active high or low nibble in the HEX pane. Printable characters replace the active byte in the ASCII pane.
- Arrows, Page Up/Page Down, Home/End, and Ctrl+Home/Ctrl+End navigate by byte, row, page, or file.
Shift with navigation extends a byte selection. Ctrl+A, Ctrl+C, Ctrl+X, and Ctrl+V select all, copy, cut, and paste binary data.
- Backspace and Delete remove bytes. Typing at end of file appends data.
Ctrl+S saves in place. Ctrl+Q, Esc, or the app-exit key exits without saving pending changes.
Ctrl++ and Ctrl+- adjust editor text size for the active session.
launcher
Configurable native graphical launcher for display shells. It draws the configured grid, centers one icon in each occupied cell, and shows the selected icon larger with its title centered underneath. Arrow keys move between occupied cells, Enter opens the selection, and a pointer selects and opens items by point and click. Esc or the app-exit key returns to the shell.
Usage:
launcher [config.json]
Without an argument, Launcher reads launcher.json from the active storage root. The first run creates a usable default file there. A supplied path selects another configuration. Launcher reloads the file whenever a child application returns, so editing the configuration through a launcher item takes effect immediately.
The file is JSON. layout.columns and layout.rows define a grid from 1 by 1 through 8 by 8. Each item has a displayed name, an Open Iconic icon name, a SolarOS shell command, and a zero-based column and row. A cell can contain at most one item, and a configuration can contain up to 32 items:
{
"layout": {"columns": 3, "rows": 2},
"items": [
{"name": "Files", "icon": "folder", "command": "files", "column": 0, "row": 0},
{"name": "Manual", "icon": "book", "command": "help", "column": 1, "row": 0},
{"name": "Wi-Fi", "icon": "wifi", "command": "wifi", "column": 2, "row": 0},
{"name": "Sketch", "icon": "brush", "command": "sketch", "column": 0, "row": 1}
]
}
Icon names are the lowercase, hyphenated Open Iconic names, such as book, browser, brush, calculator, clock, cog, document, folder, home, musical-note, pencil, tablet, terminal, and wifi. Legacy numeric IDs from 0 through 222 remain accepted for existing configurations.
Commands use the normal SolarOS shell parser, so arguments, aliases, scripts, and application availability checks behave exactly as at the prompt. A launched foreground application returns to Launcher when it closes. A command that does not open an application returns to the shell so its output remains visible.
files
File manager inspired by Midnight Commander. Its normal mode provides two panes for copy, move, delete, and launch workflows on mounted storage. Launcher mode provides a minimal single-pane application menu suitable for a startup script.
Usage:
files [--launcher] [path]
files --launcher hides dot-prefixed entries, removes the message and function key bars, and expands one pane to the bottom of the terminal. Launcher mode is read-only: navigate with the arrows, open with Enter, refresh with r, move to the parent with Backspace, and exit with q or the app-exit key. To make a directory the startup menu, add a line such as this to /.shell/startup:
files --launcher /apps
File associations come from the app registry, including installed external foreground apps. Associations include images to view, WAV/MP3 to player, MPEG-1 .mpg/.mpeg files to vplay, CSV to sheet, Python and Lua scripts to their runtimes, documents to reader (or writer when Reader is unavailable), and .gb ROMs to the installed Game Boy module. NIB disk images open with the installed Apple II Plus module. Unknown files fall back to less or edit. A .sh file runs through the built-in SolarOS shell. In launcher mode, documents associated with Reader open as reader --pager for row-aware page navigation.
Controls:
- Arrows navigate panes.
Tab switches active pane.
Enter opens directories or launches known files.
- Returning to a parent directory restores the cursor to the directory that was just exited.
F3/V, F4/E, F5/C, F6/M, F7/k/K, F8/D, and F9/Z view, edit, copy, move, create a directory, delete, and create a zip archive. The function keys remain the primary map; the help bar shows the letter mnemonics within the action names.
Space toggles a mark on the current file or directory and advances to the next item.
- Copy, move, and delete operate on the current or marked entries. Copy and recursive delete scan their source trees first, then show measured progress with the current entry. Move shows progress across the selected top-level entries.
Esc cancels an active copy, move, or delete operation and keeps Files open. A partial current-file copy is removed; top-level items completed before the cancellation remain copied, moved, or deleted.
- File operations refresh both panes after completion.
- App-exit key exits.
ftp
Two-pane FTP file manager. The left pane is local mounted storage. The right pane is a remote FTP server. The app uses unencrypted IPv4 FTP and passive data connections.
Usage:
ftp
ftp HOST [PORT] [--user USER --password PASSWORD] [--remote PATH] [--local PATH]
Press Tab after a host prefix to complete aliases from .ssh/hosts on the default storage.
Anonymous login is the default (anonymous with solaros@ as its password). Specify both --user and --password for an authenticated server. Passwords are plaintext on the network and remain in shell history when entered on the command line. Use FTP only on a trusted network. Starting without arguments opens the disconnected file manager; F2/n opens the connection form. The command-line form remains available as a direct connection shortcut. If login is rejected, FTP reopens the form on the password field with the entered host, port, user, remote path, and masked password preserved. Connection failures are reported as refused, timed out, host unreachable, or network unreachable when the socket supplies that distinction, and as server unavailable otherwise.
Controls:
- Up/Down selects an item.
PgUp/PgDn moves by one visible page, and Home/End selects the first/last item. Tab, Left, and Right switch panes.
Space toggles a mark on the current file or directory and advances to the next item, as in Files. Marked items show *; each pane shows its mark count. Copy, move, and delete use the active pane's marked items, or the current item when none are marked. The parent entry cannot be marked.
F2/n opens the connection form. Tab or Up/Down selects a field, Enter connects, and Esc cancels the form.
Enter opens a directory; Backspace opens its parent. r refreshes the remote and local listings. h toggles hidden files and directories in both panes. Hidden items are shown initially; the title shows hidden:on or hidden:off.
F3/V views a local file. For a remote file, FTP downloads a temporary hidden copy into the current local folder, opens its registered viewer, and removes the copy when control returns to FTP.
F5/C copies the current or marked files and directories to the other pane. Directory transfers are recursive. Copy and move show the same preparing/transfer progress popup used by Files.
F6/M moves the current or marked files and directories to the other pane by completing each item's copy before deleting its source.
F7/k/K creates a directory in the active pane.
F8/D recursively deletes the current or marked items after confirmation. The help bar emphasizes the same operation mnemonics used by Files and SFTP.
q, F10, or the app-exit key closes the connection and exits.
- After an operation refreshes the current directories, both panes retain their cursor and scroll positions. Opening another directory starts at its top. Marks survive refreshes in the same directory and pane switching. Opening another directory clears that pane's marks; hiding hidden items clears their marks. Completed items are unmarked. A failure stops the batch and leaves remaining items marked.
FTP is a resumable TUI on the display shell and VT100-compatible port shells. One operation runs at a time in its foreground worker. Existing destination files are replaced. A failed download removes its staging file and preserves an existing destination; a move does not delete its source unless the complete copy succeeds.
sftp
Two-pane SFTP file manager with the same panes, file operations, progress, and key bindings as ftp. The left pane is local mounted storage and the right pane is a remote SSH server's SFTP subsystem. It uses the shared SSH transport, known-host policy, host aliases, and password or public-key authentication.
Usage:
sftp
sftp [user@]HOST[:PATH] [PORT] [--user USER] [--password PASSWORD] [--remote PATH] [--local PATH]
Press Tab after a host prefix to complete aliases from .ssh/hosts on the default storage. Completion preserves a supplied user@ prefix.
Starting without arguments opens the disconnected file manager. Press F2/n to enter the host, port, user, optional password, and initial remote path. Leave the password empty to use the configured SSH key. When user@ and --user are omitted from the command-line shortcut, SFTP uses the SolarOS identity user. A remote path can be appended to the host, for example sftp nils@remote:/directory; this form connects immediately and opens that directory. --remote PATH overrides an appended path. A command-line password remains in shell history; the connection form masks it. The active form field keeps a visible text cursor. If passwordless key authentication fails, SFTP reopens the form on the password field instead of showing a low-level error.
Controls are identical to FTP: F2/n opens connection setup; F3/V views, F5/C copies, F6/M moves, F7/k/K creates a directory, and F8/D deletes after confirmation. PgUp/PgDn moves by one visible page, Backspace opens the parent directory, and h toggles hidden files and directories in both panes. Space marks or unmarks files and directories and advances the cursor, as in Files. Copy, move, and delete operate on marked items in the active pane, or the current item if none are marked. Transfers are recursive, show progress, retain both pane positions after refresh, and stage downloaded and uploaded files before replacement. q, F10, or the app-exit key closes the SSH connection and exits.
flash
Download verified SolarOS factory artifacts to SD and program another supported ESP board over UART. The browser refreshes the signed catalog on request and shows which board, flavor, and version artifacts are already cached. Its tree starts folded and retains its selection and fold state after operations. Delete removes a selected cached artifact after confirmation. The shell form accepts a named UART plus optional boot and reset GPIO pins.
flash
flash refresh
flash list
flash download BOARD FLAVOR [VERSION]
flash BOARD FLAVOR [version=VERSION] [port=uart0] [boot=PIN] [reset=PIN] [baud=RATE]
See Flash another ESP board for wiring, target-selection, security, storage, and verification details.
contacts
Provider-neutral address book for gateway and MeshCore identities. Contacts can carry multiple provider endpoints while retaining trust independently for each endpoint. A signed MeshCore advert creates a discovered endpoint: the signature proves possession of the advertised key, not the human identity behind it.
Open the searchable TUI:
contacts
The list is grouped by trust and provider. Press / to search, Enter to view the selected endpoint addresses, capabilities, last-seen times, and bounded provider metadata summary, and Esc or q to leave. The footer shows a Actions and ? Help, including on compact screens.
When Chat is included, select a contact and press c to open their direct conversation. Closing Chat returns to Contacts. Chat uses the contact's preferred direct endpoint, favoring trusted endpoints; sending to a discovered endpoint still requires confirmation. If its saved provider instance is unavailable, Contacts names that instance in the error. Check it in Messages before opening a chat. A running worker does not by itself mean its radio or companion binding is ready.
Press a in the list or details to manage the selected contact:
c: open Chat for the selected contact, when Chat is included.
t: trust all endpoints or choose one endpoint.
b: block all endpoints or choose one endpoint.
r: edit the contact name, then Enter to save or Esc to cancel.
l: choose another contact to link into the selected contact. Confirm with y to move its endpoints and remove its source record.
d: remove the contact and its endpoints after confirming with y.
These shortcuts also work directly from the list and details. Use arrows and Enter in menus, and Esc to cancel. Confirmation pages require y; Enter does not confirm removal or linking. Press f in the list to filter by endpoint trust (all, discovered, trusted, or blocked), and s for store status. ? opens a scrollable key guide. Filters match contacts with any endpoint in that trust state; linking can select any other contact regardless of the current filter or name search.
The same operations are available from the shell:
contacts status
contacts list [all|discovered|trusted|blocked]
contacts show CONTACT_ID
contacts rename CONTACT_ID NAME
contacts trust CONTACT_ID [ENDPOINT_ID]
contacts block CONTACT_ID [ENDPOINT_ID]
contacts remove CONTACT_ID
contacts link TARGET_CONTACT_ID SOURCE_CONTACT_ID
Contact and endpoint identifiers autocomplete from live service snapshots. Linking moves the source endpoints to the target contact and removes the source record. When the 512-contact store is full, radio discovery may evict the oldest unpinned contact whose endpoints are all still discovered; trusted and blocked records are never automatically evicted. BLE companion imports preserve existing records and report overflow instead of evicting contacts. The shared store supports up to 576 endpoints.
The versioned store is CRC checked, uses two alternating headers and data copies, remains below 192 KiB, and normally lives at /.contacts/contacts.bin. If storage is unavailable, Contacts remains usable in volatile mode and contacts status reports the storage error.
inbox
Universal incoming-message browser for pages, chat notifications, mail, and other background producers. It reads the same shared inbox that supplies the status-bar unread count. Messages and read state survive reboot in the bounded /.inbox/messages.bin store; the service retains at most 64 entries and keeps the file below 32 KB even when internal flash is the only storage.
Each list item occupies exactly one terminal row: unread/priority markers, local reception date and time, a compact source (chat/general, email, or pocsag), and as much of the message body preview as fits the screen.
Notification sound is enabled by default on boards with audio. Press s in the Inbox or use inbox notify on, inbox notify off, or inbox notify test; the setting is persistent. Notification tones remain active while SolarOS is suspended.
Usage:
inbox
Controls:
Up/Down, Page Up/Page Down, Home, and End navigate.
Enter or Right opens the selected message and marks it read.
Left, Backspace, or Esc returns from a message to the list.
u toggles the unread-only filter.
m toggles the selected message between read and unread.
s toggles the notification sound on boards with audio output.
r refreshes and q or the app-exit key exits.
email
Receive-only IMAPS client for the configured mailbox. The app shows the provider-specific message list while every newly synchronized message is also published to the universal inbox and its shared status-bar unread counter.
Configure and synchronize the account before opening the app:
wifi on
email configure imaps://imap.example.com user@example.com app-password INBOX
email sync
email
Controls:
Up/Down, Page Up/Page Down, Home, and End navigate.
Enter or Right opens the selected message and marks its universal inbox notification read.
Left, Backspace, or Esc returns from a message to the list.
u toggles the unread-only filter.
m toggles the selected message between read and unread.
r refreshes and q or the app-exit key exits.
The account configuration persists in NVS. The local message list is volatile and keeps the newest 32 synchronized messages. This first version displays a best-effort text preview; MIME attachments, encoded headers, sending, and server-side read flags are not implemented yet.
io
Interactive expansion I/O manager. Its default Layout view presents the board's connectors in their physical arrangement, followed by the existing pin, named-bus, and resource-claim views. It uses the same ownership and validation services as the gpio, i2c, spi, uart, midi, onewire, and expansion commands.
Usage:
io
Controls:
Tab switches between Layout, Pins, Buses, and Claims. Outside Layout, Left and Right also switch views.
- Arrows move through the physical connector grid in Layout and through rows in the other views. Page Up/Page Down, Home, and End make larger moves.
- Layout adapts to the terminal: compact headers show several pins across, while long headers such as the DevKitC J1/J3 pair scroll vertically with one physical pin pair per row. Narrow screens show fewer columns and keep the selected pin visible.
- Pins, Buses, and Claims fit their columns to the screen width. Bus state stays ahead of routing details; trailing columns are omitted when space is limited.
- Layout markers are
* free, ~ releasable, @ claimed, ! fixed/control, + power, - ground, and x not connected.
Enter opens context-sensitive actions for a pin or bus.
n creates a board-approved named I2C, SPI, UART, MIDI, 1-Wire, PS/2, or CAN bus, depending on the installed drivers.
- Bus creation uses arrows to select fields and values; the generated bus name can be edited directly.
- Runtime buses can be attached, detached, or removed when their lease state permits it. Their
Autostart action idempotently appends the matching expansion bus create ... command to /.shell/startup. Direct GPIO and PWM assignments can be created and released from the Pins or Layout view.
- CAN creation selects TX/RX GPIOs, bitrate in kbit/s, listen-only or normal mode, and an optional transceiver-select GPIO with its active level. The default is 500 kbit/s in listen-only mode. CAN controllers appear in Buses; runtime controllers support removal and
Autostart, which saves an expansion attach twai-can ... command. A compatible CAN transceiver is required; see manual hardware.can.
- A selected I2C bus has a
Set I2C speed action with 100 kHz, 400 kHz, and 1 MHz choices. The change applies at runtime even while devices lease the bus.
r refreshes; q, Esc, or the app-exit key exits.
invaders
Graphical arcade shooter. The playfield scales to the active display; ship movement, shields and collision detection use the same game coordinates. Small displays use bitmap text for the score and lives.
Usage:
invaders
Controls:
Left/Right move the ship.
Space or f fires.
Esc or app-exit key exits.
less
Terminal pager for text files. It preserves original text layout and is useful for quick file inspection.
Usage:
less <file>
Controls:
Up/Down or j/k scroll one line.
Page Up/Page Down, b, or Space page.
Home/End or g/G jump to start/end.
Ctrl+F or / opens Find. F3 or n jumps to the next case-insensitive match; N jumps to the previous match. Search wraps.
q, Esc, or app-exit key exits.
ltop
Live text task monitor for display and port terminals. Unlike the top command's cumulative snapshot, ltop samples the FreeRTOS runtime counters once per second and reports the interval deltas. One progress bar shows the busy share of each CPU core, derived from that core's idle-task delta. Each task percentage is its share of one core, so the task total can approach 200% on a dual-core target. The title shows the current aggregate CPU load instead of the fixed maximum capacity.
The IRAM and ERAM progress bars show the currently used share of the internal and external heaps. ERAM is shown as unavailable on boards without external RAM.
The task table is sorted by current CPU load and shows task state, priority, interval CPU percentage, and peak stack consumption since the task started. Peak stack consumption is the allocated stack size minus the minimum remaining stack reported by FreeRTOS. Idle tasks are omitted from the table because their counters drive the per-core progress bars. Columns expand across wide terminals and the priority column is hidden on narrow terminals.
Usage:
ltop
Controls:
Up/Down or j/k scroll one row.
Page Up/Page Down, Home/End, or g/G move through the task list.
r resets the sampling baseline.
q, Esc, or the app-exit key exits.
rfscan
Graphical receiver-energy monitor for attached radios that report the live_rssi capability. Requires a graphical display and the radio_scanner group. Radio drivers provide live energy readings and validate tuning and bandwidth settings through the common radio API; the app does not select support by radio model.
rfscan
rfscan radio0
radio scan radio0
Select the radio at launch with rfscan <name>; it remains selected for that app session. With no argument the app selects the first attached radio. The radio configuration TUI also opens the scanner with s on a graphical display. From a text-only shell, use session create rfscan display0 radio0 to open it on the device display.
Channel plots live RSSI against time. Time cycles through 10 seconds, 1 minute, 10 minutes and 1 hour. Sampling intervals increase with the time window; short bursts can be missed. The graph shows the latest level and the minimum and peak since starting or clearing. When another service is already receiving, the scanner observes without retuning, consuming packets or taking its radio claim. Transmit and sleep intervals produce no energy samples. An idle radio is claimed and placed in receive mode until scanning stops.
PgUp zooms into the RSSI plot's Y axis and PgDown zooms out. Available spans are 5, 10, 25, 50, 100 and 200 dB; the default is 100 dB. In Channel, the plot centers on the average RSSI of samples inside the displayed time window. In Band, it centers on the average of the current frequency-bin readings. Empty bins and held peaks do not affect the average. Each zoom key centers the axis on that average, then holds the axis steady until the next zoom key. The Avg readout continues updating; values outside the selected span are clipped to the plot edges. Zoom leaves scanning and the alert threshold unchanged and does not change waterfall shading. A threshold outside the visible Y range keeps working but its horizontal marker is hidden.
Channel controls are Start/Stop, Time, Step, Freq and Thresh. Step selects a frequency increment of 25, 62.5, 125 or 250 kHz without interrupting monitoring. With Freq selected, Up/Down increases/decreases the requested frequency by that increment; the frequency is shown to 1 Hz. Changing frequency applies it and resumes acquisition automatically when monitoring was running. Tuning requires an unclaimed radio and preserves the current receiver bandwidth. A busy radio reports its owner. Stop, suspension or exit restores the previous frequency and operating state.
Enter on a setting opens a prefilled input popup. Type to replace the current value, use Left/Right, Home/End, Backspace or Delete to edit, or Ctrl+U to clear the field. Enter applies and Esc cancels. Frequency uses MHz; Step and BW use kHz, including decimal values. A BW of 0 keeps the current receiver filter. Range accepts start and end frequencies in MHz separated by a space; Time accepts 1–3600 seconds. Values that would exceed the supported number of scan bins are rejected or require a larger step. Radio drivers validate frequency and bandwidth when scanning starts. Opening or cancelling a popup leaves the current scan running.
Thresh controls an audible energy alert in both tabs. The default is −90 dBm; Up/Down changes it by 1, 1, 1, 2, 5 or 10 dB for Y spans of 5, 10, 25, 50, 100 or 200 dB, respectively, and cycles through Off at the ends of the −130 to −30 dBm range. Enter accepts an exact dBm value or off. Changing the threshold leaves scanning running. A dashed horizontal line marks the threshold; fresh samples at or above it request a short beep at most once per second, including brief hits between graphical refreshes. Alerts use the selected audio output and global volume/mute, skip busy audio, and are cancelled on Stop, suspension or exit. Audio failures are reported without ending the scan. The center frequency of the strongest sample responsible for the last alert is shown beside the threshold as Threshold: >=-90dBm Triggered: 868.375000MHz. Before the first alert, Triggered shows --. The frequency remains visible while scanning moves on; the next alert updates it.
Band shows received energy against frequency, a dashed peak trace and a waterfall with the newest completed sweep at the top. Darker pixels mean stronger energy. The waterfall uses dithering on 1-bit displays; buttons use solid backgrounds, outlines and inverse selection. Range cycles through ±250 kHz, ±1 MHz, ±2 MHz and a preset band: 433.05–434.79, 863–870 or 902–928 MHz, selected from the current frequency. Step cycles through 25, 62.5, 125 and 250 kHz; wide ranges increase the step when necessary to stay within 129 frequency bins. BW selects the current receiver bandwidth or 62.5, 125 or 250 kHz. Drivers can round the requested bandwidth up to a supported filter; the running scan shows the applied bandwidth. Unsupported settings are rejected by the radio driver. The final frequency shown is the last sampled bin.
Three status lines above the graph show tuning settings, RSSI readings and the alert threshold with the last alert frequency. Errors replace the RSSI readings. The top header shows LIVE with owns RX, observe RX or RX paused for a band sweep; stopped acquisition shows STOPPED. In Channel, the header also shows the time window. The tuning row shows the frequency or band range, with BW: and Step: labels. Sampling runs independently of graphical refreshes. Radios advertising fast_tune use lightweight frequency changes and driver-defined settling times; other live_rssi radios remain supported through normal configuration. Sweep speed depends on the driver, bandwidth, range and frequency step.
Band scans require exclusive ownership of the selected radio. A busy radio shows its owner; stop that owner explicitly before scanning. Normal packet reception on that radio is paused during a sweep. Band scanning has no idle timeout. Stop, app suspension, app exit or a scan error ends the sweep. The scanner restores the previous configuration and operating state and releases its claim; a restoration failure is reported. Changing settings or switching tabs automatically resumes acquisition if it was running; a stopped scan stays stopped. Rejected settings restore the previous selection and resume it when radio ownership permits. Returning to a suspended app leaves it stopped until Start is selected.
| Control | Action |
| Tab, or tap a tab | Switch between Channel and Band |
| Left/Right | Select a bottom function |
| PgUp/PgDown | Zoom the RSSI Y axis in/out, centered on the displayed average |
| Up/Down | Next/previous value of the selected function; Up starts and Down stops when Start/Stop is selected |
| Enter, or tap a bottom button | Toggle Start/Stop, or open the selected setting's input popup |
| Space | Start or stop |
r, s, t | Time/range, step, threshold |
f in Channel, b in Band | Increase frequency by Step, or advance bandwidth |
c | Clear history and peak hold |
Ctrl+Alt+Del, q or Esc | Exit |
RSSI measures energy inside the receiver filter without decoding its source. Frequency bins are sampled sequentially; receiver bandwidth limits frequency detail. Scan only within the attached module's supported RF band and antenna range. This display does not provide raw I/Q samples.
logic
On-device logic analyzer waveform viewer. It displays the latest capture made by the shared logic analyzer service or the SUMP job. With pin arguments it makes a new local capture before opening the viewer.
Usage:
logic
logic <pin[,pin...]> [rate-hz] [samples] [trigger=<pin>]
Examples:
logic
logic 1,2,3,17
logic 1,2 500000 8192
logic 1,2,3,17 10000 4096 trigger=1
trigger=<pin> waits for the next rising or falling edge on that runtime-safe GPIO before sampling. The trigger pin may be one of the captured data pins, as in the last example, or a separate GPIO that is not displayed as a channel.
Controls:
- Left/Right pans through the capture.
+/- or Page Up/Page Down changes the time scale.
r captures again with the current local configuration when the SUMP job is stopped.
a or Home shows the complete capture.
q, Esc, or the app-exit key exits.
The app is compiled only for boards with graphics and runtime-safe GPIOs. While the SUMP job is running, the app remains a viewer and automatically reloads captures received from the host.
lua
Embedded Lua runtime. It can run an interactive REPL or execute .lua scripts from storage. Lua scripts can use SolarOS service bindings when the selected firmware includes the corresponding packages. Foreground scripts can consume touch coordinates, relative mouse motion, buttons, and joystick axes through solaros.input.
Usage:
lua
lua file.lua [args...]
Controls:
exit([code]) returns from the REPL or script and reports the optional exit code to the launching shell.
- App-exit key interrupts running code or exits.
notes
Markdown-backed checklist and category manager. It stores unchecked and checked items and supports one level of category folding. A persistent bottom help bar shows the available controls, with status or text input directly above it.
Usage:
notes [file.md]
Controls:
Up/Down navigate.
Space toggles an item. After an active item is marked done, the selection remains on the next active item when one is available.
a adds an unchecked item below the selected item. On a category, it adds the first item in that category; from the done section, it adds at the end of the active items.
c adds a category.
Enter edits the selected line.
d or Delete deletes the selected item/category.
t tidies the note by deleting all completed items.
Shift+Up/Shift+Down reorders items within a category or moves the selected category together with all of its items.
Left/Right collapse/expand a category.
q, Esc, or app-exit key exits.
plot
Graphical plotter for DAQ CSV files and live scalar streams. It is compatible with CSV generated by the daq job.
Usage:
plot <scalar-stream...> [--rate ms]
plot -f <file.csv> [column...]
Examples:
plot temperature humidity --rate 1000
plot -f /logs/env.csv temperature humidity
--rate is a best-effort live sampling interval in milliseconds. Rates below 25 ms request faster runtime ticks while the screen redraws no more often than once every 25 ms, so fast acquisition does not force an equally fast display refresh.
Controls:
Left/Right pan.
Up/Down select series.
+/- or Page Up/Page Down adjust visible window/zoom.
a or r resets the view.
Space pauses/resumes live mode.
q, Esc, or app-exit key exits.
playground
Browse the configured community catalog as a foldable category tree, search applications, and install, update, uninstall, or run Python and Lua scripts.
Usage:
playground
playground search QUERY...
playground install APP-ID [auto|flash|sd]
playground run APP-ID [ARG...]
playground delete
playground refresh
playground reload
playground source [repository-or-catalog-url|reset]
playground storage [flash|sd]
Press / to search, Enter to open an application, and use the actions shown in the bottom bar. Press i on an application in the catalog tree or details page to install it, and u to uninstall it after confirmation. Packages are verified by size and SHA-256 and installed under /playground/ on the permanently configured flash or sd storage. Community scripts run with the normal permissions of their runtime and are not sandboxed.
Catalog and application files are stored under /playground/ on the selected filesystem. playground delete recursively removes that entire directory, clears the loaded catalog from memory, and also removes the legacy hidden .solar/playground directory when present. Source and storage preferences are retained.
The shell subcommands use the local catalog for browsing and installation: refresh downloads and saves it, reload loads that saved copy without network access, search prints matches, and install downloads and verifies an application by ID. run reads an installed application's own manifest, so it does not require a catalog reload. It launches the declared Python or Lua runtime without creating a Playground session. Installation also adds the app ID to the managed /.shell/playground aliases, so qr-share --file /notes/wifi.txt is equivalent to playground run qr-share --file /notes/wifi.txt. Uninstalling removes the generated alias. Arguments are forwarded unchanged to the selected script. Opening the TUI reloads the saved catalog automatically and does not refresh it. At the top-level catalog tree, Esc, q, and the app-exit key exit Playground.
playground storage shows the persistent catalog and default application storage. Set it with playground storage flash or playground storage sd. Without a saved preference, Playground selects SD when it is mounted and flash otherwise. Omitting the install target, or specifying auto, uses the selected setting.
See Playground for controls, storage layout, source selection, and the trust model.
python
Embedded MicroPython runtime. It can run an interactive REPL, .py scripts, or .mpy files from storage. Python scripts can use SolarOS service bindings when the selected firmware includes the corresponding packages. Foreground scripts can consume touch coordinates, relative mouse motion, buttons, and joystick axes through solaros.input.
Usage:
python
python file.py [args...]
python file.mpy [args...]
Controls:
exit([code]) returns from the REPL or script and reports the optional exit code to the launching shell.
- App-exit key interrupts running code or exits.
reader
Graphical document reader for plain text, Markdown, and EPUB. It remembers reading position and zoom per opened file when storage is available.
Usage:
reader [--pager] <file.txt|file.md|file.epub|man:topic>
With --pager, Up and Down page instead of scrolling one layout row. Each forward page starts with the final visible row from the previous page, so mixed font sizes and wrapped Markdown retain a precise reading overlap.
Controls:
Up/Down scroll by layout row, or page when --pager is active.
Page Up/Page Down page with the same precise row overlap.
Home/End jump to start/end.
+/- adjust zoom.
Ctrl+F or / opens Find. F3 or n jumps to the next case-insensitive match; N jumps to the previous match. Search wraps.
Esc exits search state first; otherwise exits.
q or app-exit key exits.
writer
Resumable graphical Markdown editor for PSRAM display boards. Inactive blocks are formatted like reader; the block containing the cursor and every block touched by a selection show their exact Markdown source. edit remains the portable text editor for port shells and boards without graphics or PSRAM.
Usage:
writer [file.md]
Without a path, Writer opens an untitled document and asks for a path on the first save. Existing files larger than 256 KiB are rejected without changing them. Saves use a synced same-directory staging file, backup rename, verified replacement, and rollback. Cursor, scroll anchor, and zoom metadata plus idle recovery snapshots live centrally under /.writer or the active persistent storage root. Writer offers a differing recovery snapshot when the document is opened again. Recovery uses R to recover, D to discard the snapshot, or C to cancel; the unsaved-changes dialog similarly uses S, D, and C.
Controls:
- Left/Right move by UTF-8 codepoint; Up/Down move to the adjacent editable visual line or Markdown block, including directly adjacent headings.
Ctrl+Left/Ctrl+Right move by word. The insertion cursor blinks while idle.
Shift with arrows, Page Up/Page Down, Home/End, or document movement extends the selection. Ctrl+Home/Ctrl+End jump to the document boundaries.
Ctrl+A, Ctrl+C, Ctrl+X, and Ctrl+V select all, copy, cut, and paste through the shared SolarOS clipboard.
Ctrl+B, Ctrl+I, and Ctrl+K add or remove bold, italic, and link source.
F1 opens formatting for inline code, headings 1–4, bullet or numbered lists, quotes, fenced code, and rules. Esc closes the active menu/dialog; from the editor it exits, using the save/discard/cancel prompt when dirty.
Ctrl+F opens Find and F3 jumps to the next case-insensitive match, wrapping at the end. Ctrl+R prompts for find and replacement text.
Ctrl+S saves, Ctrl+Z/Ctrl+Y undo and redo, and Ctrl++/Ctrl+- adjust zoom.
Esc and the app-exit key open the save/discard/cancel prompt when the document is dirty. Writer writes recovery before suspend or stop and resumes in place.
scp
SCP file transfer over SSH. It supports password or key authentication through the shared SSH transport and host lookup/known-host storage. When user@ is omitted, SCP uses the NVS-backed SolarOS identity user. Tab completion reads aliases from /.ssh/hosts, preserves an explicit user@ prefix, and appends : after a unique host match.
Usage:
scp [-P port] local [user@]host:remote
scp [-P port] local [user@]host:
scp [-P port] [user@]host:remote local
scp [-P port] [user@]host:remote-glob dir
scp [-P port] [user@]host:remote
Remote download paths can use * or ?. The local target must be an existing directory for remote wildcard downloads.
SCP runs inline in the shell. Its progress and result remain in the terminal scrollback, and usage errors or a completed transfer return directly to the prompt.
Controls:
- App-exit key cancels an active transfer.
sftpsync
Incremental file and directory synchronization over SSH. The SolarOS client uses the server's SFTP subsystem, so it works with a normal SSH server without requiring a matching remote program. It skips regular files whose size and modification time already match. It does not delete destination-only files.
Usage:
sftpsync [-r|-a] [-n] [-P port] local [user@]host:remote
sftpsync [-r|-a] [-n] [-P port] [user@]host:remote local
-r, -a, or --recursive enables directory traversal. -n or --dry-run prints the changes without writing them. -P selects a non-default SSH port. Password and key authentication, known hosts, and aliases in /.ssh/hosts are shared with ssh and scp. A local or remote directory operand names the directory to synchronize; its contents are updated below that path. Traversal is limited to eight nested directory levels. Symbolic links and other special file types are skipped. Active transfers show an updating progress bar for each changed file, followed by the aggregate file and byte counts.
Controls:
Ctrl+C or the app-exit key cancels the password prompt or an active synchronization.
sheet
CSV viewer for small data tables. It is intended as a companion to daq logs and simple spreadsheet-like inspection.
Usage:
sheet <file.csv>
Controls:
- Arrows move through cells.
- Page keys scroll.
- App-exit key exits.
ssh
Interactive SSH client. It supports password and key authentication, known hosts, hostname lookup through /.ssh/hosts, UTF-8 text, VT-style controls, and remote full-screen terminal applications. When user@ is omitted, SSH uses the NVS-backed SolarOS identity user. Tab completion reads aliases from /.ssh/hosts and preserves an explicit user@ prefix.
Usage:
ssh [user@]host [port]
On display shells, SSH owns a resumable terminal buffer. Switching to the SolarOS shell does not mix the local and remote scrollback, and switching back restores the SSH buffer. On UART and USB CDC port shells, SSH uses the shared port scrollback. Disconnecting returns directly to the local prompt. Remote terminal control sequences still work, including full-screen applications.
Controls:
- Most keys are sent to the remote host, including Esc, Alt+key, cursor keys, Ctrl combinations, and function keys.
- App-exit key closes the SSH app.
Alt+Tab leaves the session running in the background on display builds.
synth
Open the native synthesizer and sound designer:
synth [--headless]
The app uses the same eight-voice fixed-point engine exposed to Python and Lua, but keyboard and MIDI events reach it directly. On a display, the default graphical interface provides the complete editor described below. --headless suppresses graphical rendering while keeping the same note keys, editing keys, published parameters, control bindings, and MIDI input active. This permits use from a port shell on a headless board after a playback device such as the LEDC PWM audio expansion has been attached. Its Play tab contains the waveform, envelope, volume, ADSR, and piano controls. The Wave tab is a graphical wavetable editor with selectable 16, 32, or 64-point resolution; edits reshape held notes immediately while the piano remains playable, and switching tabs does not rewrite the custom wavetable. The Filter tab adds a resonant low-pass response graph, cutoff, resonance, envelope amount, and an independent graphical ADSR filter envelope. The Oscillator 2 tab adds a second per-note source with waveform, octave, fine detune, and unity-safe mix controls. Both oscillators share the filter and envelopes. The Preset tab provides eight factory sounds and eight persistent user slots. User presets capture both oscillators, both envelopes, the filter, mono/poly mode, glide, and the complete custom wavetable. The Glide tab provides polyphonic or monophonic last-note playback, a hold mode for toggle-style piano keys, and portamento. Hold is session performance state and is not part of a preset. The display also reports active voices, output sample rate, and audio errors. The volume button changes the shared SolarOS speaker volume.
On display targets smaller than 240 pixels wide or 200 pixels high, Synth automatically replaces the full editor with a parameter HUD. The selected control gets a large value and level bar; waveform controls and the Wave tab retain compact graphs; Presets shows one slot at a time. Targets smaller than 112 by 56 pixels use a footer-free micro layout. Physical note keys and MIDI remain active. External parameter/control changes focus the changed parameter until the next local navigation action, which makes a 128-by-64 SH1106 or SSD1306 useful as a synthesizer appliance display. After attaching it as oled0, open Synth on that target with session create synth oled0. During active playing, compact displays defer visualization until note input is quiet so synchronous display transfers cannot take priority over new notes. MIDI notes highlight the matching pitch class on the on-screen piano just like physical note keys.
Controls:
A W S E D F T G Y/Z H U J K play one chromatic octave. The positions are physical, so the Y position is the Z key on a German keyboard. Note input remains active on all tabs.
Tab cycles through Play, Filter, Wave, Oscillator 2, Glide, and Presets. Number keys 1 through 6 select those tabs in that order.
X hides or shows the on-screen piano keyboard on every tab. When it is hidden, the tab's knobs, graphs, panels, or preset list use the freed space; the physical note keys and MIDI input remain active.
- On Glide, select Hold and use
Up or Enter to enable it. Each physical or terminal piano-key press then toggles its note on or off, and releasing the key does not stop the note. Use Down or Enter to disable hold and release all latched app-key notes. MIDI Note On, Note Off, and sustain keep their normal behavior.
- On Play,
Left/Right selects the waveform, global volume, or an ADSR knob; Up/Down changes it, and +/- changes note velocity.
- On Wave,
Left/Right moves the edit cursor and Up/Down changes the waveform. Shifted arrows move or draw faster, and +/- changes brush size.
- On Wave,
Enter cycles the editor through 16, 32, and 64 points. New sessions start at 16 points. The current waveform is resampled rather than reset.
- On Wave,
B applies the next square, triangle, saw, Supersaw, sine, or flat baseline; R restores that baseline, M smooths, N normalizes, 0 clears, and Backspace or Delete exchanges the current waveform with the undo state.
- On Filter,
Left/Right selects cutoff, resonance, envelope amount, or a filter ADSR knob; Up/Down changes it, and +/- changes note velocity.
- On Oscillator 2,
Left/Right selects waveform, octave, fine detune, or mix; Up/Down changes it, and +/- changes note velocity. Octave ranges from -2 through +2, detune from -100 through +100 cents, and mix from 0 through 100 percent.
- On Preset, arrows select a factory or user slot and
Enter loads it. V saves the current sound to the selected user slot. Factory presets are read-only.
Page Up/Page Down changes octave from 2 through 6 on any tab.
Esc or the app-exit key exits and immediately releases audio ownership.
For an external MIDI keyboard, create and start a named MIDI bus before opening Synth:
expansion bus create midi midi0 tx=gpio1 rx=gpio2
job start midi midi0
synth
MIDI Note On/Off, velocity, sustain (CC64), All Sound Off (CC120), and All Notes Off (CC123) are supported on all 16 channels.
Published parameters
While Synth is running, control parameters exposes these native continuous parameters:
synth.volume: 0 through 100 percent, linear.
synth.envelope.attack: 0 through 10000 ms, linear.
synth.envelope.decay: 0 through 10000 ms, linear.
synth.envelope.sustain: 0 through 100 percent, linear.
synth.envelope.release: 0 through 10000 ms, linear.
synth.filter.cutoff: 40 through 18000 Hz, logarithmic.
synth.filter.resonance: 0 through 100 percent, linear.
synth.filter.envelope.amount: 0 through 100 percent, linear.
synth.filter.envelope.attack: 0 through 10000 ms, linear.
synth.filter.envelope.decay: 0 through 10000 ms, linear.
synth.filter.envelope.sustain: 0 through 100 percent, linear.
synth.filter.envelope.release: 0 through 10000 ms, linear.
synth.osc2.octave: -2 through +2 octaves, linear.
synth.osc2.detune: -100 through +100 cents, linear.
synth.osc2.mix: 0 through 100 percent, linear.
synth.glide: 0 through 2500 ms, linear.
These paths exist only while the app is active. Bindings remain configured when Synth is suspended or stopped and reconnect when it resumes. See man controls for control creation, soft pickup, and MIDI CC mappings.
telnet
Telnet client for classic TCP terminal sessions. It supports basic Telnet option negotiation, terminal type reporting, window size reporting, and raw mode.
Usage:
telnet host [port]
telnet -r host [port]
Press Tab after a host prefix to complete aliases from .ssh/hosts on the default storage, including in raw mode.
Controls:
- App-exit key closes the Telnet app.
Alt+Tab leaves the session running in the background on display builds.
view
Graphical image viewer. It supports the image formats compiled into the current firmware, including common PNG/JPEG/GIF/WebP paths and automatic animated GIF playback when the media package is enabled. Images are decoded as RGB on a negotiated indexed-color display and as grayscale on a one-bit display. In the default fit mode, JPEG color conversion writes display-sized output directly, so a large source photograph does not require a full-size RGB destination.
Usage:
view [-fit|-actual] <image>
Controls:
- Arrows pan.
f toggles fit/actual mode. Fit mode scales the image up or down to the screen.
0 selects actual size.
1 selects fit-to-screen.
Esc or app-exit key exits.
vplay
Graphical MPEG-1 media player with optional MP2 audio. The bottom controls show playback status and elapsed time above the volume bar; full screen hides the controls and time. The middle Stop/Play icon indicates pause while paused.
The viewport and controls adapt to small targets, including 240×64 displays. Full screen uses the entire display for video. The minimum target size is 84×48 pixels.
Usage:
vplay [-fit|-actual] <file.mpg>
Supports MPEG-1 program streams (.mpg and .mpeg) containing one MPEG-1 video track and optionally one MPEG-1 Layer II (MP2) audio track. MPEG-2, MPEG-4/H.264, AVI, transport streams, and MP3/AAC audio are not supported. The source limit is 640x480; lower resolutions and frame rates are recommended for smooth playback. Playback speed depends on source complexity, display transfer speed, and output size.
Controls:
Space pauses/resumes.
Up/Down changes global volume.
Enter stops playback or restarts the selected file.
</> seeks backward/forward ten seconds, preserving pause state.
Left/Right selects the previous/next MPEG file in the same folder, in filename order. At either end, the selection stays unchanged.
- The shared Previous, Rewind, Stop/Play, Forward, and Next buttons provide the same actions when clicked or tapped.
f toggles full screen, hiding the header, volume strip, and transport buttons.
0 selects actual size and 1 selects fit. Actual-size video larger than the viewport is cropped centrally.
d toggles frame/timing log diagnostics.
Esc or app-exit key exits.
Playback pauses while its session is suspended and returns to the shell at the end of the file. Audio uses the selected SolarOS output and acts as the playback clock when present; a file with audio requires an available playback output. Video dimensions and frame rate must remain constant, and the initial audio/video timestamp difference must be no more than ten seconds.
SEEKING means the player is preparing a new position. Files without usable seek timestamps take longer. Stop or close playback to cancel. Positions beyond the end select the last frame.
sketch
Pointer-driven graphical paint application. Its layout follows classic desktop paint programs: Save, Open, Import, and the sidebar controls share one aligned, equal-sized button grid; color and pattern choices are in the bottom bar. Sketch uses a compact four-color canvas and stores finished documents as interoperable indexed-color PNG files. Color TFTs show the native palette; one-bit displays use the existing dithered rendering path. Runtime rotation preserves the drawing and its saved dimensions. The canvas fits inside the new viewport, and pointer input follows the fitted image. Gray margins mark space outside the drawable canvas after rotation.
Usage:
sketch
sketch file.png
Sketch requires at least 200×200 pixels. On smaller targets it reports that the display resolution is too low and returns to the shell.
Sketch is intentionally not gated on a pointer hardware capability. If no ready absolute or relative pointer is registered when the app starts, a popup explains that a pointer can be attached at any time. Press Enter or Esc to dismiss it; the first attached pointer is accepted without restarting the app.
Controls:
- Click Save to update the current PNG. For an untitled or imported image, the first save selects the next free
Sketches/sketchNNN.png path on persistent storage. A failed save preserves the previous document.
- Click Open to select a PNG as the current document. Click Import to flatten a PNG, JPEG, or GIF into the current four-color canvas; the next save creates a new PNG instead of overwriting the imported source.
- The sidebar selects pen, straight-line, rectangle, ellipse, bucket fill, or eraser drawing. The weight control cycles through 1, 2, 4, and 8 pixels. Eraser uses the selected weight and restores white pixels. Clear immediately resets the complete canvas to white.
- Line, rectangle, and ellipse show an exact non-destructive preview during the drag, including the selected color, pattern, and stroke weight. Releasing the pointer commits that preview to the canvas.
- The first four bottom swatches select white, red, blue, or black. The next four select solid, checker, dot, or diagonal-hatch application.
- Keyboard fallbacks are
S Save, O Open, I Import, P Pen, L Line, R Rectangle, E Ellipse, B bucket fill, X eraser, and C clear. Arrows and Enter operate the file browser.
Q, Esc, or the app-exit key exits. Suspending and resuming retains the current cold-allocated canvas; closing the app releases it and all browser or image resources.
web
Simple graphical web browser for lightweight HTML pages. It shares document and image rendering infrastructure with reader where possible. Embedded and direct PNG, JPEG, GIF, and WebP images retain color on indexed-color displays; one-bit displays keep the grayscale decode and dither path. Direct MJPEG URLs are shown as live video using the same JPEG renderer.
Usage:
web http://host/
web https://host/path
web http://camera-host/camera.mjpeg
MJPEG playback uses a bounded 512 KiB assembly buffer and a single pending decoded frame. If decoding or display presentation falls behind the source, web drops the stale pending frame and keeps the newest one instead of growing latency or memory use. Reload, Back, Forward, and app exit cancel the active HTTP stream. The stream inherits the security properties of its URL; plain HTTP provides no encryption or peer authentication.
Controls:
- The top toolbar provides Back, Reload, and Forward. Pointer or touch presses activate the toolbar and open links or form controls directly.
Left or b goes back, Right or f goes forward, and r reloads while retaining the current reading position.
Up/Down or k/j scroll one rendered line. Page Up/Page Down, Home, and End provide larger movement.
n or Tab selects the next link or form control; p selects the previous one. Selection stops at each end, and Enter activates it.
- Links and plain page text use the same font size. Links remain underlined, headings remain bold, and
+/- or Ctrl++/Ctrl+- reflow page text through the same five zoom levels as Reader.
Ctrl+F opens Find and F3 jumps to the next case-insensitive match in rendered page text, wrapping at the end.
Esc or app-exit key exits.
zoo
Browse the hosted model zoo and install complete model bundles on mounted SD or flash storage. The text interface works on the display and VT100 port shells. It requires the Zoo package, ESP32-S3, PSRAM, and networking.
zoo
zoo refresh
zoo source [HTTPS-CATALOG-URL|reset]
zoo storage [sd|flash]
zoo install ID VERSION
The default source is https://solar-os.eu/zoo/catalog.json. The first launch fetches it; later launches use the cached catalog. r refreshes the cache. Up/Down and Page Up/Page Down select entries, Enter opens details, Alt+S searches, and d downloads the selected version. Arrows and page keys scroll long details. * marks an installed bundle and ! marks an incompatible entry. c or Esc cancels a running operation. q or Esc returns from details or exits the browser; the app-exit key exits from either view.
Source and storage preferences persist. Without an explicit storage preference, Zoo uses mounted SD, then flash. Files are stored under dl/zoo/ on that volume; each installed bundle is in models/ID/VERSION/. Downloads are streamed to storage, checked against archive, manifest, model, and asset hashes, and installed through a staged directory swap. Interrupted downloads preserve the previous installation.
The browser shows task, modality, result adapter, license, validation status, and reported free-memory requirements. Runtime compatibility is not a device inference test, and unreported memory remains unknown. The device accepts catalogs up to 512 KiB and 256 entries; larger catalogs fail without replacing the cache. Installation also needs enough storage for the ZIP and extracted files.
Installation does not load a model. Use the displayed bundle.json path with model bundle PATH or the native, Python, or Lua inference APIs. Loaded models remain resident until explicitly unloaded or the device reboots. See Model bundles for inputs, results, and execution, and Native image pipelines for a Zoo-to-camera workflow and a Python detection example. Catalog task names describe the model; check its result adapter before selecting a script. Raw tensor bundles need model-specific interpretation.
Quick reference
Use apps to list applications installed in the current firmware. Start an app by entering its name and arguments. Use the app-exit key to return to the shell; resumable applications can also be switched through sessions. This page is the complete usage and controls reference for foreground applications.