oreo-cli controls the Elixpo Oreo Badge from a computer over USB. The command it installs is oreo. With it you can:
see the badge's status
send photos, videos and documents to the Transfer app
work with files and apps
set up Wi-Fi
follow the badge's logs
open a MicroPython shell
update OreoOS
It finds the badge by its USB ID (303a:83ec), so you never have to guess /dev/ttyACM0 or COM5.
The code lives in cli/. The Python package is oreo-cli and the command is oreo; the package also installs oreo-cli as a second name for the same command. The CLI is released separately from OreoOS, with tags cli-v<version>. See cli/CHANGELOG.md for what each release changed.
Status: 1.0.0, stable. Commands, options, output and exit codes are stable. Later 1.x releases may add to them but will not change them. Every command is tested against a simulated badge, and the checks to run by hand are listed in Testing on a badge. If something goes wrong, run
oreo doctorand attach its output to an issue.
Related guides:
USB.md: using the badge from a computer (ports, drivers, permissions, and raw
mpremotefor advanced use).TRANSFER.md: how the Transfer app and its code exchange work.
Contents: Install · First run · Interactive UI · Commands · Sending files: oreo transfer · Wi-Fi setup · Updating OreoOS · Exit status · Troubleshooting · FAQ · Transfer speed · Testing on a badge · Releasing
Install#
You need Python 3.9 or newer and either pipx or uv. The installer:
picks pipx if you have it, and uv otherwise;
downloads the release wheel and its
.sha256file;checks the SHA-256;
installs
oreoas an isolated tool.
If you have neither pipx nor uv, it tells you how to get one and stops.
Linux and macOS
curl --proto '=https' --tlsv1.2 -LsSf \
https://raw.githubusercontent.com/elixpo/oreo/main/cli/install.sh | sh
Windows (PowerShell)
irm https://raw.githubusercontent.com/elixpo/oreo/main/cli/install.ps1 | iex
To upgrade, run the same command again. It replaces the installed version. Check what you have with oreo --version.
Installer options#
| Variable | Meaning |
|---|---|
OREO_CLI_VERSION | Release to install: latest (the default), or a version written as cli-v1.0.0, v1.0.0 or 1.0.0 |
OREO_CLI_INSTALLER | pipx or uv (default: pipx if present, otherwise uv) |
OREO_CLI_REPOSITORY | GitHub repository to install from (default elixpo/oreo) |
OREO_CLI_DOWNLOAD_BASE | Download the wheel from this URL instead of the release. For testing; needs a pinned version |
To install a specific version:
curl --proto '=https' --tlsv1.2 -LsSf \
https://raw.githubusercontent.com/elixpo/oreo/main/cli/install.sh |
OREO_CLI_VERSION=cli-v1.0.0 sh
$env:OREO_CLI_VERSION = "cli-v1.0.0"
irm https://raw.githubusercontent.com/elixpo/oreo/main/cli/install.ps1 | iex
The repository's "Latest" GitHub release is always OreoOS, not the CLI. So for latest, the installers ask the GitHub API for the newest cli-vX.Y.Z tag. The API allows 60 requests an hour without a token. Pre-releases such as 1.1.0rc1 are installed only when you name them. Pinning a version skips the API call.
Install with pipx or uv directly#
Every release has assets with fixed names:
| Asset | What it is |
|---|---|
oreo_cli-<version>-py3-none-any.whl (+ .sha256) | The package |
oreo_cli-<version>.tar.gz (+ .sha256) | Source distribution, with the tests |
SHA256SUMS | Both checksums in one file |
pipx and uv can install the wheel straight from its release URL:
pipx install https://github.com/elixpo/oreo/releases/download/cli-v1.0.0/oreo_cli-1.0.0-py3-none-any.whl
# or
uv tool install https://github.com/elixpo/oreo/releases/download/cli-v1.0.0/oreo_cli-1.0.0-py3-none-any.whl
To check the file before you install it:
V=1.0.0
curl -LO https://github.com/elixpo/oreo/releases/download/cli-v$V/oreo_cli-$V-py3-none-any.whl
curl -LO https://github.com/elixpo/oreo/releases/download/cli-v$V/SHA256SUMS
sha256sum --check --ignore-missing SHA256SUMS # macOS: shasum -a 256 -c --ignore-missing SHA256SUMS
gh attestation verify oreo_cli-$V-py3-none-any.whl -R elixpo/oreo
pipx install oreo_cli-$V-py3-none-any.whl # or: uv tool install oreo_cli-$V-py3-none-any.whl
gh attestation verify checks the GitHub build-provenance attestation. It confirms that the file was built by this repository's cli-release workflow from the tagged commit.
oreo-cli is not on PyPI; install it from the GitHub release.
From a checkout#
pipx install ./cli # or, for development: pip install -e cli/
Dependencies#
These are installed with the CLI:
richpyserialmpremote1.28.x, which must match the badge's MicroPython 1.28Pillow, for photos and for your GitHub avatar (oreo setup)textual8.x, for the full-screen view
Converting videos also needs ffmpeg on your PATH (for oreo transfer send and oreo add). Photos and documents do not.
| System | Command |
|---|---|
| Debian / Ubuntu | sudo apt install ffmpeg |
| macOS | brew install ffmpeg |
| Windows | winget install ffmpeg |
Linux serial permissions#
To open the badge's port you need to be in the dialout group (uucp on Arch):
sudo usermod -aG dialout "$USER" # then log out and back in
oreo doctor checks this. macOS and Windows need no setup.
Uninstall#
pipx uninstall oreo-cli # or: uv tool uninstall oreo-cli
The CLI stores nothing else on the computer.
First run#
Plug the badge into a USB port with a data cable. On the breadboard build (ESP32-S3-DevKitC-1), use the port labelled USB, not UART (see USB.md).
Run:
oreo devs # is the badge there? (never opens the port) oreo # the full-screen Oreo viewIf no badge is found, run
oreo doctor.
Interactive UI#
oreo with no command (or oreo ui) switches the terminal to a full-screen Oreo view that handles everything about the badge. It opens at once, waits for a badge to be plugged in if there is none, and asks which one to use if there are several. Leaving it puts your terminal back as it was.
Oreo Badge OREO01 — OreoOS v2.0.0 · /dev/ttyACM0 · OreoOS paused
╭──────────────────────────╮╭────────────────────────────────────────────────────────╮
│ 1 Status ││ ╭─ Oreo Badge ─────────────────────────────────────╮ │
│ 2 Files ││ │ OreoOS v2.0.0 (2026-11-07) │ │
│ 3 Apps ││ │ firmware MicroPython 1.28.0 │ │
│ 4 Wi-Fi ││ │ port /dev/ttyACM0 │ │
│ 5 Transfer ││ │ Wi-Fi connected 192.168.1.42, 2 saved │ │
│ 6 Profile ││ │ battery 87% (4.01 V) │ │
│ 7 Install app ││ ╰──────────────────────────────────────────────────╯ │
│ 8 Update OreoOS ││ storage 1.2 MiB used of 8.0 MiB, 6.8 MiB free │
│ ──────────────────────── ││ Category Size Files │
│ s Shell (REPL) ││ system 812.0 KiB 96 ... │
│ q Quit ││ │
│ ──────────────────────── │╰────────────────────────────────────────────────────────╯
│ source │ ⠹ copying to /documents: song.mp3 1.1 MiB / 3.0 MiB ━━━━── 37% Esc cancel
│ github.com/elixpo/oreo │╭────────────────────────────────────────────────────────╮
│ docs ││ badge on /dev/ttyACM0; pausing OreoOS while this view │
│ oreo.elixpo.com/docs/cli ││ ✓ notes.md 1.2 KiB │
│ ││ │
╰──────────────────────────╯╰────────────────────────────────────────────────────────╯
s Shell r Refresh esc Cancel l Log q Quit d Download u Upload here x Delete
The header shows the badge, its port, the OreoOS version and whether OreoOS is paused. The sidebar lists the sections, and under them the links to the source code and to this guide (clickable in terminals that support links). The bar under the section shows what is running (with a progress bar and Esc to cancel), or "ready" when nothing is. The log under it keeps what each operation reported: three lines, which l makes about half the height and back. When an operation fails the log grows by itself, and shrinks again at the next action. The footer always shows the keys for the section you are in. The mouse works too, but every action has a key.
| Key | Section or action |
|---|---|
| 1 | Status: versions, storage, Wi-Fi, battery (oreo info) |
| 2 | Files: Enter opens a folder, Backspace goes up, d downloads, u uploads files or folders into the folder shown, a adds photos, videos or documents (oreo add), x deletes (asks first; OreoOS's own files are refused) |
| 3 | Apps: installed apps and the App Store; f fetches the catalogue, i installs, u updates all, x removes |
| 4 | Wi-Fi: saved networks; n scans, c connects (password asked without echo), x forgets |
| 5 | Transfer: type the files, Enter; OreoOS restarts, open Transfer on the badge and type the code it shows (oreo transfer send) |
| 6 | Profile: e edits it field by field, f loads the keys from an .env-style file (oreo setup) |
| 7 | Install app: an app folder from this computer (oreo app install) |
| 8 | Update OreoOS: c checks, u updates (oreo update) |
| s | Shell: the badge's MicroPython REPL in this terminal; Ctrl-] or Ctrl-X comes back to the view |
| r | Refresh the section shown |
| l | A bigger log, or back to three lines |
| Esc | Cancel the running operation (in a text field with nothing running: back to the sidebar) |
| q, Ctrl-C | Quit; OreoOS starts again |
How it behaves:
It never freezes. Every badge operation runs in the background, with a spinner or a progress bar. There is only ever one command on the serial port: something asked for while another operation runs waits its turn (the bar shows "+1 waiting").
Cancel stops copies, downloads, App Store installs and Transfer between two chunks, and a half-copied file is removed, as with Ctrl-C in the plain commands. Scans and Wi-Fi joins are interrupted on the badge. Once an OreoOS update starts it cannot be cancelled (quitting waits for it).
Questions are dialogs. Confirmations, the Transfer code and passwords appear in a box; Esc cancels the operation that asked.
OreoOS is paused while the view is open, so the badge's screen shows what the computer is doing during long operations (with an OreoOS that has
oreoOS/hostui.py). It restarts when you quit: with q, with Ctrl-C, and if anything fails.--reset hard|noneworks as for the commands. The Transfer section is the exception: the Transfer app runs inside OreoOS, so it restarts OreoOS first, and the next section that needs the badge pauses it again. A profile saved or an app installed takes effect when OreoOS restarts, which happens when you quit.Unplugged? The view goes back to waiting and picks the badge up again when it returns.
The view is built with Textual, which is installed with the CLI. Where it cannot run, oreo falls back to the numbered menu below: when standard input or output is not a terminal, with TERM=dumb, or when Textual is missing. oreo ui says why instead of falling back. To always get the menu, run oreo menu or set OREO_UI=menu.
The numbered menu#
oreo menu (and oreo where the full-screen view cannot run) shows the badge's status and then a menu:
| Key | Action |
|---|---|
| 1 | status |
| 2 | files |
| 3 | apps |
| 4 | Wi-Fi |
| 5 | add a photo, video or document |
| t | send files to Transfer (the same flow as oreo transfer send) |
| p | owner profile (the same flow as oreo setup) |
| i | install an app from a folder (the same checks as oreo app install) |
| u | update OreoOS |
| s | shell |
| q | quit |
In the file view, u <path> uploads a file from the computer into the folder shown, and p downloads one. Ctrl-C stops the current action and goes back to the menu. OreoOS stays paused while the menu is open and starts again when you quit. Without a terminal (in a script, for example), oreo prints the status and exits.
t is the one entry that needs OreoOS running: it checks and converts the files first, then restarts OreoOS and asks for the code the Transfer app shows (apps drawer -> Tools -> Transfer). OreoOS keeps running afterwards; the next entry that needs the badge pauses it again. A profile saved with p and an app installed with i take effect when OreoOS restarts, which happens when you quit the menu.
To send photos and documents while OreoOS keeps running, use the Transfer app instead (see oreo transfer).
How oreo uses the badge#
The badge is only touched when a command needs it.
--help,oreo help,oreo devs, mistyped arguments and "no badge found" never open the serial port.One badge is picked automatically. If several are connected, choose one with
--portor$OREO_PORT(seeoreo devs).Most commands pause OreoOS and then restart it. Such a command stops OreoOS, does its work and then soft-resets the badge so OreoOS starts again.
--reset harddoes a hardware reset instead, and--reset noneleaves the badge at the MicroPython prompt.Some commands do not stop OreoOS.
oreo transfertalks to the Transfer app while it runs, andoreo logsonly listens.The badge's screen shows long operations. While OreoOS is paused for
push,pull,add,app install,apps install/updateandupdate, the badge shows what is happening and how far it has got (throughoreoOS/hostui.py, in OreoOS versions that have it), and clears the screen at the end. The updates ride along with the commands the CLI sends anyway, so they cost no extra time; an OreoOS without it works as before. (oreo transferneeds none: the Transfer app shows its own progress.)Destructive actions ask first. These are
rm -r,apps remove,app remove,transfer rm,update,update --cleanandflash. Without a terminal, the answer is "no" unless you pass--yes.
Global options#
Put these before the command, for example oreo --port /dev/ttyACM1 info.
| Option | Meaning |
|---|---|
-p, --port PORT | Serial port to use (default: the one badge found; also $OREO_PORT) |
--reset soft|hard|none | How OreoOS is restarted after a command (default soft) |
-y, --yes | Answer yes to confirmations |
--no-color | Plain output |
-v, --verbose | Show the badge's log lines and full tracebacks |
--version | Print the CLI version |
Commands#
| Command | What it does | Stops OreoOS? |
|---|---|---|
oreo / oreo ui | The full-screen Oreo view | while it is open |
oreo menu | The numbered menu, for simple terminals | while it is open |
oreo devs | List badges | no (never opens a port) |
oreo info | Versions, storage, Wi-Fi, battery | briefly |
oreo transfer / oreo send | Send files to the Transfer app, using the code on the badge | no |
oreo add | Send photos, videos and documents without opening an app | briefly |
oreo ls / push / pull / rm | Any file on the badge | briefly |
oreo apps | App Store: list, install, update, remove | briefly |
oreo setup | Owner profile: GitHub user, name, timezone, weather | briefly, then restarts |
oreo new | Make a new app folder from the app template | no (never opens a port) |
oreo app | Install, list or remove an app from a folder on this computer | briefly, then restarts |
oreo wifi | Scan, connect, list and forget networks | briefly |
oreo update | Update OreoOS (downloaded by the badge) | yes, then restarts |
oreo logs | Follow the console | no |
oreo shell / run / dev | MicroPython prompt, scripts, live editing | while it runs |
oreo flash | Reflash the firmware (developers) | yes |
oreo doctor | Diagnose cable, port, permissions, firmware | briefly |
oreo help <command> (or oreo <command> --help) shows each command's options and examples.
oreo devs: list badges#
oreo devs # badges, by USB ID 303a:83ec
oreo devs --all # also other USB serial devices, with what they probably are
This never opens a port. It exits with status 3 when no badge is connected.
oreo info: status#
oreo info
Shows:
the OreoOS version and date, and the MicroPython version;
the board and port;
space used by the system, apps, gallery, documents and other files;
Wi-Fi status and the Bluetooth boot setting;
battery and free RAM.
oreo transfer: send files to the Transfer app#
The easiest way to put photos, videos and documents on the badge. OreoOS keeps running the whole time, and you prove you are at the right badge by typing the code it shows. How it works inside is in TRANSFER.md.
On the badge, open the apps drawer, go to Tools and open Transfer. It shows a six-character code, for example
K7M2QX.On the computer, run:
oreo transfer send holiday.jpg clip.mp4 talk-notes.mdWhen asked, type the code from the badge. Case does not matter, and you can include spaces or dashes (
k7m-2qxis fine).Each file is converted, sent, checked on the badge and saved. Photos and videos appear in the Gallery, documents in the Reader, and the badge shows a notification for each file.
$ oreo transfer send holiday.jpg talk-notes.md
holiday.jpg -> holiday.rz565 41.2 KB (6% of the original)
talk-notes.md -> talk-notes.mdz 2.1 KB (38% of the original)
✓ Transfer is open on the badge (OreoOS 2.x.y), 9.8 MB free
Code shown on the badge: K7M2QX
✓ code accepted
✓ holiday.rz565 is in the Gallery (/apps/gallery/assets/optimized/holiday.rz565, SHA-256 checked)
✓ talk-notes.mdz is in the Reader (/documents/talk-notes.mdz, SHA-256 checked)
✓ 2 file(s) sent
(The output above is an illustration; the exact wording and sizes vary.)
Other actions, in the same session style (open Transfer, type the code):
oreo transfer list # photos, videos and documents on the badge, and free space
oreo transfer rm holiday.rz565 # remove one (asks first; names from `transfer list`)
oreo send holiday.jpg # short for `oreo transfer send`
oreo transfer with no action means send.
| Option | Meaning |
|---|---|
--code CODE | The code shown on the badge. Without it, oreo asks. A script with no terminal must pass it |
--wait SECONDS | How long to wait for the Transfer app to be open (default 60) |
--kind image|video|document | Override the file type detection |
--seconds N | Video length to keep (default 10) |
--photo-max N | Longest side of a photo in pixels (default 240) |
What gets sent:
| Input | Sent as | Lands in |
|---|---|---|
Photos (.jpg, .png, ...) | .rz565 | Gallery (/apps/gallery/assets/optimized) |
Videos (.mp4, .mov, ...; needs ffmpeg) | .rv565, 180×135 at 24 fps | Gallery |
.md / .txt | .mdz / .txtz | Reader (/documents) |
Files already in a badge format (.rz565, .r565, .rv565, .mdz, .txtz) | as they are | Gallery or Reader |
Anything else is refused before the badge is touched. Every file is converted and checked before the badge is contacted, so a bad file never leaves a session half done.
Things to know:
The code changes. Every session gets a new code: when the CLI says goodbye, when the session is cancelled, and after a lockout. RIGHT on the badge draws a new one while no computer is connected.
Wrong codes. In a terminal,
oreotells you how many tries are left and asks again. After 5 wrong codes, the badge locks for 60 seconds and then shows a new code. With--code, or without a terminal, one wrong code ends the command.Waiting for the app. If Transfer is not open,
oreowaits. After a couple of seconds it tells you to open it, and it gives up after--waitseconds. It sends nothing to the badge until the app has announced itself, so a closed app is never sent bytes it cannot read.Cancelling. B on the badge cancels the session and Ctrl-C on the computer stops it. A half-sent file is always thrown away; the old file with the same name, if any, is kept.
Same name. A file with the same name replaces the old one, and
oreosays "replaced the old one".Limits. One file can be up to 8 MiB, and the badge keeps 256 KiB free for OreoOS. A file that does not fit fails on its own and the others still go through. The command then exits with status 1 and lists what was not sent.
Timeouts. The badge drops a computer that stays silent: after 5 minutes while waiting for the code, after 2 minutes once the code is accepted, and after 20 seconds in the middle of a file.
While Transfer is open, close it before using other
oreocommands. The app turns off Ctrl-C on the USB console so that no byte can interrupt OreoOS. Commands that pause OreoOS (info,add,push,shell,update, ...) therefore cannot stop it, and they fail with "the badge did not answer" (exit 4). Press HOME or B on the badge to leave Transfer, then run the command again.Only one program can have the port open. Stop
oreo logs,mpremoteor a serial monitor before runningoreo transfer.
oreo add: photos, videos and documents without the Transfer app#
oreo add holiday.jpg # Gallery
oreo add clip.mp4 --seconds 8 # Gallery (needs ffmpeg)
oreo add talk-notes.md reading.txt # Reader
Same conversion and destinations as oreo transfer send. The difference is that add needs no app and no code: it pauses OreoOS and copies the files over the MicroPython REPL, then restarts OreoOS. Use it in scripts, or when you have the badge to yourself; use oreo transfer when you want OreoOS to stay up, or want the person holding the badge to approve the transfer.
Before anything is copied, the converted size is checked against the media budget (5 MiB for photos and videos) and the badge's free space.
| Option | Meaning |
|---|---|
--kind image|video|document | Override detection |
--seconds N | Video length (default 10) |
--fps N | Video frame rate (default 24) |
--width N, --height N | Video size (default 180×135) |
--photo-max N | Longest side of a photo (default 240) |
--save DIR | Also keep the converted files on the computer |
oreo ls / push / pull / rm: files#
oreo ls # the badge's root folder
oreo ls /apps
oreo push notes.txt /documents/
oreo push -r myapp /apps/myapp # a whole folder
oreo pull /wifi_seen.json # into the current folder
oreo pull -r /apps/pet ./pet-backup
oreo rm /documents/old.mdz
oreo rm -r /apps/myapp # asks first
pushchecks free space before writing anything. Each file is written to a temporary.partfile. The badge checks its size and SHA-256 and only then renames it into place, so a damaged copy leaves the old file untouched.--no-verifyskips the hash.pushcopies files as they are. To make a photo or video viewable, useoreo transfer sendororeo add, which convert it first.rmrefuses OreoOS's own files (/oreoOS,/oreoWare,/assets, the built-in apps,/main.py,/secrets.py,/wifi.json, ...) and anything inside those folders unless you add--force. Removing them can stop the badge from booting.A badge path can be written with a leading
:(:documents/). In Git Bash on Windows you need it (see Troubleshooting).
oreo apps: the App Store#
oreo apps # installed apps and the catalogue
oreo apps refresh # fetch the latest catalogue
oreo apps install snake
oreo apps update # every app with a newer version
oreo apps update snake
oreo apps remove snake # asks first
The badge downloads apps itself, over its own Wi-Fi, so set up Wi-Fi first (see Wi-Fi setup). Before refresh, install and update, oreo brings Wi-Fi up from the badge's saved networks and then shows the download progress. Built-in apps (including Transfer) cannot be removed.
oreo setup: owner profile#
oreo setup # show the profile, ask for each value
oreo setup --show # only print it (the key is hidden)
oreo setup --github-user octocat --name "Mona Lisa" --timezone 5.5
oreo setup --lat 12.97 --lon 77.59 --weather-name Bengaluru
oreo setup --owm-key-stdin < owm-key.txt # the OpenWeatherMap key, kept out of history
oreo setup --designation "" # remove a value
oreo setup --from-file .env.local # the profile keys from an .env-style file
oreo setup --set DISPLAY_NAME="Mona Lisa" --set TIMEZONE_OFFSET=5.5 # one key at a time
oreo setup --avatar # fetch your GitHub avatar again
oreo setup --github-user octocat --no-avatar # leave the avatar alone
Keys use the .env.local names: GITHUB_USER, DISPLAY_NAME, DISPLAY_DESIGNATION (or the older DESIGNATION), TIMEZONE_OFFSET, OWM_API_KEY, and optionally WEATHER_LAT, WEATHER_LON, WEATHER_NAME. Without a pinned latitude and longitude, Weather finds the badge's own location from its network (PROFILE.md).
--from-file FILEreads an.env-style file (comments,exportand quotes are fine). It takes only the profile keys, skips empty ones, and leaves everything else in the file alone: it prints which keys it took and the names of the others, never a value.--set KEY=VALUEsets one key (repeatable);KEY=removes it.When several are given, later ones win: the file, then
--set, then the named options such as--name.
Writes /profile.json on the badge, which Badge, Commits, Weather and the clock read through oreoOS/profile.py (see PROFILE.md). No checkout or .env.local is needed.
It first shows what the badge has now and where each value comes from (
profile.jsonorsecrets.py, whichtools/deploy.pywrites).Asked interactively, Enter keeps the value shown and
-removes it, so thesecrets.pyvalue (if any) shows through again. Values given as options are saved without prompting; an empty option value removes the key.Values are checked before the badge is touched: the timezone is hours from UTC between -12 and +14 in quarter hours (
5.5,-4,+05:30,UTC+5:45), latitude -90 to 90, longitude -180 to 180, GitHub user names as GitHub allows them, and short plain-ASCII names that fit the screen (the badge font has no other characters).The OpenWeatherMap key is read without echo and never printed. The badge only reports whether one is set, so the key never comes back to the computer.
--owm-key KEYworks but warns that the key may land in your shell history; prefer the prompt or--owm-key-stdin.The new file is merged on the badge, written to
/profile.json.tmp, read back and then renamed over/profile.json, so a cut-off save never leaves a half-written profile. If the power goes between removing the old file and the rename, OreoOS reads the.tmpcopy, and the next save repairs it.OreoOS restarts afterwards so the apps read the new profile (
--reset noneleaves it at the MicroPython prompt). A new timezone applies at the next time sync, which happens as OreoOS starts.
Your avatar. After the profile, oreo setup puts the GitHub avatar of GITHUB_USER on the badge for the Badge app. This computer downloads it (https://github.com/<user>.png, not the rate-limited API), crops it to a 72x72 circle in RGB565 with Pillow, and copies it over USB to /avatar.bin, together with the user it was made for. The badge itself downloads and decodes nothing.
It is fetched only when
/avatar.binis missing or was made for another user.--avatarfetches it again (after you change your GitHub picture); given alone, it does only that.--no-avatarleaves the file alone.The Badge app shows it only while its user is the profile's
GITHUB_USER; otherwise it draws your initials. Without a GitHub user, setup removes/avatar.bin.If the download fails (offline, unknown user), the profile is still saved and setup says so; an avatar made for someone else is removed, never left behind. Run
oreo setup --avatarlater./avatar.binsits next to/profile.json, outside every app folder, so OreoOS updates and deploys keep it (tools/deploy.py --cleanwipes both).--shownames whose avatar is on the badge.
oreo new: start an app#
oreo new my_game # ./my_game, shown as "My Game"
oreo new "My Game" --author octocat --dir ~/oreo-apps
Copies the app template (a counter: A adds, B takes one away; the same files as app_templates/ in the OreoOS repo, shipped inside oreo-cli) into a new folder, and fills in the app id, the name shown on the badge, the author (from --author, else git config github.user or user.name) and the icon module. The id comes from the name: My Game -> my_game. Edit src/app.py, then oreo app install ./my_game. The app guide is APPS.md.
oreo app: install an app from a folder#
oreo app install ./my_game # check, copy to /apps/my_game/, restart OreoOS
oreo app install ./badge --force # over a built-in app (working on OreoOS itself)
oreo app list # installed apps
oreo app remove my_game # asks first
install:
checks the folder with the same rules as
tools/validate_app.py(a copy ships in oreo-cli, and a test keeps them the same). Errors stop it before the badge is touched; warnings are shown;refuses to replace a built-in app unless you add
--force;checks free space, then copies only the files that belong on the badge (
manifest.json,main.py,__init__.py,src/,assets/optimized/) to/apps/<id>/, each one through a.partfile checked with SHA-256 (--no-verifyskips the hash);removes files an earlier install left that are no longer in the folder. Saved data in
/state/<id>.jsonstays;restarts OreoOS, so the app is in the Apps drawer.
OreoOS imports an app once per boot, so to try a change run oreo app install again: OreoOS restarts and loads the new code.
list and remove are the same as oreo apps list and oreo apps remove.
oreo wifi: Wi-Fi#
oreo wifi scan
oreo wifi connect "Home Net"
oreo wifi saved
oreo wifi forget "Old Net"
See Wi-Fi setup.
oreo update: update OreoOS#
oreo update # newest stable release
oreo update --check # only say whether there is a newer one
oreo update --channel beta # newest beta release
The badge updates itself with the same code its Settings > Updates page uses (oreoOS/ota.py). oreo steers it and shows each step:
Reads the OreoOS version on the badge.
Brings up the badge's Wi-Fi from its saved networks. Set up a network first (see Wi-Fi setup).
Asks GitHub, from the badge, for the newest release on the channel.
Compares the badge's files with the release and shows how many change and how much will be downloaded.
Asks you, checks the free space, and has the badge download the changed files into
/_ota, checking each file's SHA-256.Moves the files into place, as the badge does when it boots with an update waiting.
Restarts the badge, waits for it to come back on USB (it may come back under another port name) and reports the version it booted.
Apps, photos, documents, saved Wi-Fi and your profile are kept.
Ctrl-C works until step 6. During step 6 it is held until the files are in place.
Power loss during step 6 is safe: the badge finishes the update the next time it starts.
An update the badge already downloaded by itself is offered for installing.
Without a terminal,
oreo updateanswers "no" unless you pass--yes.GitHub limit: the badge asks GitHub without a token, which allows 60 requests an hour per network.
Developers: --repo deploys from an OreoOS checkout instead of a release:
oreo update --repo ~/src/oreo # ./deploy --board from that checkout
oreo update --repo ~/src/oreo --clean # wipe the badge's files first (asks)
With --repo, update also takes:
--force: upload every file;--no-bump: keep the OreoOS version;--override APPS: replace these app folders.
oreo logs: console output#
oreo logs # follow what OreoOS prints; Ctrl-C to stop
oreo logs --restart # restart OreoOS first, to see the boot log
logs does not stop OreoOS. It keeps following when the badge restarts, or is unplugged and plugged back in, even under a new port name. While the Transfer app is open you will see its @OTB ... protocol lines in the log; that is normal.
oreo shell: MicroPython REPL#
oreo shell
Pauses OreoOS and opens the badge's MicroPython prompt. Leave with Ctrl-] or Ctrl-X, and OreoOS starts again.
oreo run: run a script#
oreo run blink.py
Runs a file from the computer on the badge and streams its output. Ctrl-C stops the script on the badge.
oreo dev: live editing#
oreo dev ./scripts # mount ./scripts at /remote, open the REPL
oreo dev ./scripts --run blink.py # and run blink.py from it
OreoOS stops, the folder on the computer appears on the badge at /remote, and the REPL opens there. import mymodule loads your local copy, so edits need no copying. MicroPython imports a module once: after an edit, run import sys; del sys.modules['mymodule'] and import it again. To reach OreoOS's own modules, import sys; sys.path.append('/').
OreoOS does not run while you are in the REPL. To edit an app live, mount a folder that contains apps/<id>/ and start OreoOS from the REPL:
oreo dev ~/work # ~/work/apps/my_app/{manifest.json,main.py,...}
>>> import os; os.chdir("/"); exec(open("/main.py").read())
Apps under /remote/apps load from the computer instead of /apps (the console prints [dev] my_app from /remote/apps), and OreoOS re-reads an app's files every time it opens, so save on the computer, press HOME and reopen the app. Leaving oreo dev resets the badge and the installed apps are used again. To copy an app onto the badge instead, use oreo app install. Details: docs/APPS.md.
Leave with Ctrl-] or Ctrl-X. Ctrl-D soft-resets the badge, which starts OreoOS and ends live editing.
oreo flash: firmware#
oreo flash --repo ~/src/oreo # erase, write firmware, install OreoOS (asks)
oreo flash --repo ~/src/oreo --build # run ./build-firmware first
Needs an OreoOS checkout. Erasing always asks first, and it removes everything on the badge, including photos, documents and Store apps.
oreo doctor: diagnose the connection#
oreo doctor
Checks, in this order:
the computer: Python, pyserial, mpremote, ffmpeg;
the badge's USB ID, and what any other USB IDs it finds probably are;
on Linux, membership of
dialout/uucp;only then, it opens the port to read the firmware and OreoOS versions.
It ends with numbered next steps, failures first. It exits with 0 when nothing failed (warnings are fine) and 1 otherwise.
oreo help#
oreo help # list commands
oreo help transfer # one command, with examples
Wi-Fi setup#
The badge uses Wi-Fi only for the internet: the App Store, OreoOS updates, GitHub data (Badge, Commits), weather and the clock. Files never go over Wi-Fi; they go over USB with oreo transfer. The badge joins 2.4 GHz networks only.
The badge has no keyboard, so networks with a password are added from the computer:
oreo wifi scan # networks the badge can see, with signal and security
oreo wifi connect "Home Net" # asks for the password without echo
oreo wifi saved # names of saved networks
oreo wifi forget "Old Net"
connect joins the network and saves it, so OreoOS joins it by itself from then on. Open networks can also be joined on the badge, from Settings > WiFi.
| Option | Meaning |
|---|---|
--open | The network has no password |
--no-save | Join now, but do not save it |
--save-only | Save it without trying it now (for a network that is not in range) |
--password-stdin | Read the password from standard input, for scripts |
--timeout SECONDS | How long to wait for the network (default 15) |
# In a script, so the password never appears on the command line:
printf '%s\n' "$WIFI_PASSWORD" | oreo wifi connect "Home Net" --password-stdin
Before touching the badge, connect checks that the network name is 1 to 32 bytes and that the password is 8 to 63 characters (or 64 hex digits).
The password stays private. It is read before the badge is touched and sent to the badge once. It is never printed, logged, returned or stored on the computer, and it is removed from error messages. oreo wifi saved lists names only.
Results are reported plainly:
wrong password;
not in range: the badge sees only 2.4 GHz networks; check
oreo wifi scan;did not answer in time: move closer, or try
--timeout 30.
Networks written into the deploy-time .env file come back at the next boot even after forget, and oreo warns about that. To forget them for good, remove them from .env and deploy again.
Exit status#
These are stable across 1.x, so scripts can rely on them:
| Code | Meaning |
|---|---|
0 | Success |
1 | The command failed; the message says why |
2 | Usage error: a missing or invalid argument (checked before the badge is touched) |
3 | No badge found, or several and no --port |
4 | The badge could not be reached: port missing or busy, no answer, connection lost |
5 | The badge answers but has no OreoOS, or one too old for the command |
130 | Cancelled: Ctrl-C, or "no" at a confirmation |
Special cases:
oreo devsexits3when no badge is connected.oreo doctorexits1when a check failed.oreo transferexits1when the Transfer app is not open, the code is wrong, the badge is locked, or a file was refused. A failure to open the port is4, as for other commands. With no code typed, it exits130.
Troubleshooting#
Start with oreo doctor. USB.md has more on cables, ports and drivers.
Connection
| Symptom | What to do |
|---|---|
no Oreo Badge found (USB 303a:83ec) | Use a data cable (some are charge-only) and, on the DevKitC-1, the USB port. oreo devs --all shows what is connected. |
A 303a:1001 device | Stock ESP32-S3 USB-Serial/JTAG: the board has no OreoOS firmware yet, or it is in the bootloader. Flash it with oreo flash --repo <checkout>. |
A 303a:0002 device | Download mode (BOOT was held at reset). Press RESET without BOOT. |
A 303a:4001 device | Generic MicroPython, not the Oreo Badge build. Flash the badge firmware. |
| A CP210x, CH340, CH343 or CH9102 device | That is the devkit's UART bridge. Plug into the native USB port instead. |
2 badges connected | Pick one: oreo --port /dev/ttyACM1 info or export OREO_PORT=/dev/ttyACM1. |
Permission denied on Linux | Join dialout (uucp on Arch), then log out and back in. |
| Port busy | Close other programs using it: another oreo, oreo logs, mpremote, Thonny, a serial monitor, the Arduino IDE. |
the badge did not answer (exit 4) | Is the Transfer app open? Leave it (HOME) and try again. Otherwise OreoOS is busy with something that delays Ctrl-C (a download, an app): wait a few seconds and retry. If it stays stuck, press RESET or replug. |
lost the connection to the badge (exit 4) | It was unplugged or restarted. Plug it back in, check with oreo devs, and run the command again. |
this needs OreoOS (exit 5) | The badge has MicroPython but no OreoOS: oreo flash --repo <checkout>. |
The badge stays at the >>> prompt | A command ran with --reset none, or OreoOS failed to start. Try oreo --reset hard info, then oreo logs --restart. |
Git Bash: looks like a path on this computer | Git Bash rewrote /documents into a Windows path. Write :documents/, or set MSYS_NO_PATHCONV=1. |
Transfer
| Symptom | What to do |
|---|---|
open Transfer on the badge, then the Transfer app is not open on the badge | Open the apps drawer, then Tools > Transfer, and run the command again. Give a slow start more time with --wait 120. |
wrong code | Type the code exactly as the badge shows it. The characters 0, O, 1, I and L are never used, so what looks like 0 is not one. |
too many wrong codes / wait 60s | The badge locked after 5 wrong codes. Wait for the countdown; it then shows a new code. |
the session was cancelled on the badge | Someone pressed B on the badge. Run the command again. |
the Transfer app was closed | The app was left in the middle of a session. Open it and run the command again. |
the badge waited too long | The session timed out (5 min without the code, 2 min idle, 20 s mid-file). Run the command again. |
not enough free space | Free space with oreo transfer list and oreo transfer rm, or oreo info / oreo rm. |
larger than 8192 KB | Shorten the video with --seconds. To make it smaller still, use oreo add with --width / --height. |
the badge only takes ... | Send photos, videos, .md or .txt; other files cannot be shown on the badge. |
SHA-256 mismatch / damaged chunk | The copy was damaged on the way and dropped. Run it again; if it repeats, try another cable or USB port. |
speaks Transfer protocol v... | The CLI and OreoOS are from different generations. Update both: rerun the installer, then oreo update. |
no code given | No terminal to ask in. Pass --code ABC123. |
Updates, apps and media
| Symptom | What to do |
|---|---|
oreo update: could not get online | Set up Wi-Fi first: oreo wifi scan, oreo wifi connect <name>. |
oreo update: could not reach GitHub | Check the network. GitHub allows 60 requests an hour per network without a token. |
oreo update: did not come back on USB | It may still be installing. Wait, then run oreo info. If the screen stays dark, press RESET. |
oreo update: too old to be updated from the CLI | Update once from the badge (Settings > Updates) or with oreo update --repo. |
apps install fails | Check Wi-Fi first: oreo wifi saved, oreo wifi connect <name>. |
| Wi-Fi: not in range | The badge sees 2.4 GHz only. Check oreo wifi scan. |
not enough space on the badge | See what uses it with oreo info; free some with oreo rm or oreo apps remove. |
ffmpeg was not found | Install ffmpeg (see Dependencies). Only videos need it. |
checksum mismatch after copy | Run it again. If it repeats, try another cable or USB port. |
An installer says pipx or uv ... neither was found | Install one of them (the message has the command), then run the installer again. |
| Something else | Re-run with -v, and include the oreo doctor output in an issue. |
FAQ#
Is it oreo or oreo-cli? The package is oreo-cli. The command is oreo; oreo-cli also works and runs the same thing.
Do I need to clone the OreoOS repository? No. Everything except oreo flash and the --repo developer options works from the installed CLI alone.
Can I send files over Wi-Fi? No. The old Send Files page and the upload web page are gone. Wi-Fi on the badge is for the internet only; files go over USB with oreo transfer or oreo add.
Why do I have to type a code? The code is shown only on the badge's screen, so typing it proves you can see the badge you are sending to. Nothing is written until the code is accepted. See TRANSFER.md.
oreo transfer or oreo add? Both convert files the same way and put them in the same places. oreo transfer keeps OreoOS running and needs the Transfer app open and the code. oreo add needs neither, but pauses OreoOS while it copies. It also has the video size options (--fps, --width, --height) and --save.
Can I send any file with oreo transfer? Only things the badge can show: photos, videos and Markdown or text documents. For anything else (app files, your profile.json), use oreo push.
Does the CLI need the internet? Only to install it. oreo update, oreo apps install and oreo apps refresh need the badge to be online, because the badge does the downloading.
Does oreo update touch my photos, apps or Wi-Fi? No. Only OreoOS's own files change.
What about Bluetooth? Bluetooth file sharing is not available yet. It is planned last, together with the mobile app (issues #101, #103, #124).
Several badges on one computer? oreo devs lists them. Pick one with --port or $OREO_PORT.
Windows and macOS? Both are supported. On Windows the port is a COM name (--port com5 works). On macOS, oreo uses the /dev/cu.* name even if you give the /dev/tty.* one.
Where are my settings stored on the computer? Nowhere. The CLI keeps no configuration and no history.
Transfer speed#
oreo push, oreo add and the interactive upload send files as base64 inside short MicroPython snippets over the raw REPL ("raw paste" mode):
Each file goes to a
.partfile in 8 KiB chunks.The first chunk rides along with the command that opens the file. The last rides with the step that checks the size and SHA-256 on the badge and only then renames the file into place.
Free space is checked once per command, and each folder is created once per session.
The numbers below were measured on the simulated badge (execs and bytes of code; python3 cli/tests/test_push_speed.py). They were turned into time with a model of the USB link: a 1.5 ms round trip, MicroPython's 256-byte raw-paste window, and 150 KB/s flash writes. So the times are estimates until they are measured on a badge.
| Case | Execs | Code per file byte | Estimate |
|---|---|---|---|
| 1 MiB file, 4 KiB chunks (0.1.0) | 257 | 1.34 | ~43 KiB/s |
| 1 MiB file, 8 KiB chunks (1.0.0) | 129 | 1.34 | ~44 KiB/s |
| 40 files of 1.5 KiB, 0.1.0 | 240 | ||
| 40 files of 1.5 KiB, 1.0.0 | 81 | 1.99 | ~32 KiB/s |
For big files, the chunk size hardly matters. The time goes to raw-paste flow control (one round trip per 256 bytes of code) and to flash writes. For many small files, what counts is fewer execs per file (2 instead of 6).
oreo transfer uses a different path: a line protocol to the Transfer app, with 3 KiB chunks and up to 4 chunks in flight before it waits for an acknowledgement (see TRANSFER.md). Its speed has not been measured on a badge yet.
Testing on a badge#
CI has no badge. These are the checks to run by hand on one; please attach the output when you report a problem.
oreo doctor: all checks pass, ending in "all good".oreo info, thenoreo(the menu): status, files (open a folder,uan upload,pa download), the apps list, saved Wi-Fi, and q restarts OreoOS.oreo pusha 1 MiB file and a folder of small files. Note the time and speed shown,oreo pullthem back and compare them (sha256sum).Pull the cable during a large
oreo push. Expect "lost the connection" (exit 4) and no traceback. After replugging, the old file at the destination is intact, and no.partfile is left after the next push.oreo wifi scan,oreo wifi connect <name>,oreo apps install <app>.oreo update --check, thenoreo updateon a badge one release behind: versions shown, progress, restart, "installed and running". Also try it with the badge offline (expect the Wi-Fi hint) and when it is up to date.oreo logs, then press RESET on the badge: logging carries on after the restart. Ctrl-C stops it (also on Windows).oreo shell, andoreo runa script that prints. Ctrl-C stops it and OreoOS restarts.Transfer: open Tools > Transfer, then
oreo transfer senda photo, a 10-second video and a.mdfile. Each appears in the Gallery or Reader, with a notification. Also check:a wrong code: the tries left are shown;
five wrong codes: a 60 s lockout, then a new code;
B on the badge mid-file: no half file is left;
Ctrl-C mid-file;
oreo transfer listandoreo transfer rm;running the command with Transfer closed, then opening it while
oreowaits;afterwards, with Transfer closed,
oreo infostill works;with Transfer open,
oreo infofails with exit 4 and the hint.
On Windows (PowerShell and Git Bash) and macOS:
oreo devs,oreo info,oreo push notes.txt :documents/,oreo transfer send photo.jpg, andoreo --port com5 info/oreo --port /dev/tty.usbmodem* info.The full-screen view (
oreo), on Linux, macOS and Windows Terminal:started with no badge: the waiting screen; plug it in and it opens; with two badges, the chooser;
every section loads; switching sections quickly never errors (one command on the port at a time);
Files: upload a 1 MiB file and press Esc half way (no
.partleft), download a folder, delete a file;Wi-Fi scan and connect; App Store fetch and install; profile edit; app install from a folder; Transfer with the code; update check;
s opens the REPL and Ctrl-] comes back to the view;
q, Ctrl-C, and pulling the cable: the terminal is restored, and OreoOS starts again (or the view waits for the badge to come back).
oreo setup --github-user <you>: "saved /avatar.bin"; open Badge and your avatar is in the ring.oreo setup --github-user octocatreplaces it; offline,oreo setup --github-user <someone>keeps the profile, warns, and Badge shows initials (never the previous face).oreo setup --avatarfetches it again;--shownames its user.With an OreoOS that has
oreoOS/hostui.py: the badge's screen shows the progress oforeo push/pullof a large file,oreo add,oreo app install,oreo apps installandoreo update, and is cleared when each finishes; with an older OreoOS, the same commands work as before.
Releasing#
Maintainers only. CLI releases are independent of OreoOS releases and do not touch them:
the
cli-releaseworkflow runs only forcli-v*tags (on pull requests that changecli/, it builds and tests without releasing);its tags do not match the badge's OTA prefixes (
stable/,beta/);the release is created with
--latest=false, so the repository's "Latest" release stays OreoOS.
Steps:
In a pull request, set
__version__incli/oreo_cli/__init__.py, move theUnreleasednotes incli/CHANGELOG.mdunder a new## [<version>] - <date>heading, and merge it.Tag the merge commit and push the tag:
git tag cli-v1.0.0 <commit-on-main> git push origin cli-v1.0.0The workflow then:
runs the CLI tests on Linux (Python 3.9 and 3.13) and macOS;
checks that the tag matches
__version__and thatCHANGELOG.mdhas a section for it;builds the wheel and sdist from
cli/(with timestamps from the commit), writes the.sha256files andSHA256SUMS, and smoke-tests the wheel and both installers on Linux and Windows;adds a build-provenance attestation for both packages;
waits for the
cli-releaseenvironment, then publishes the GitHub release with the changelog section as its notes. To make releases need an approval, add required reviewers to that environment in the repository settings. Versions witha,borrcare marked as pre-releases.
If a release run fails after the tag was pushed, fix the problem, delete the tag (git push origin :refs/tags/cli-v1.0.0), and tag again. The installers skip a tag that has no published release.