Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ during development to quickly access various subsystems in the Crazyflie and sup
* Turn the platform on/off or put it to sleep/wake it up
* Run stability tests with the Crazyflie
* High-level commander (takeoff, land, go-to, trajectories)
* Lighthouse positioning system configuration (read/write base station geometry and calibration)
* Lighthouse positioning system configuration (read/write base station geometry and calibration, store and share configurations, write them to a whole swarm)
* Loco positioning system configuration (read/write anchor positions, cfclient-compatible YAML)
* AI-agent and scripting friendly (CSV output, classified exit codes, non-interactive mode, command timeouts)

Expand Down Expand Up @@ -87,7 +87,7 @@ Commands:
lh Lighthouse positioning system configuration
deck Decks attached to the Crazyflie
swarm Swarms of Crazyflies: store them and run commands on them
auth Your account for sharing swarms: sign in, sign out
auth Your account for sharing swarms and lighthouse configs: sign in, sign out
completions Generate a shell completion script (printed to stdout)
help Print this message or the help of the given subcommand(s)

Expand All @@ -111,7 +111,7 @@ Exit codes:
30 invalid value (range, type, malformed input)
40 --timeout expired on a bounded command
50 a swarm command failed on some of the Crazyflies
60 a check found differences (lh config check)
60 a check found differences (lh config check, swarm lh check)
```

To use the CLI you must first select which URI to use, this is done by scanning for available Crazyflies
Expand Down Expand Up @@ -245,7 +245,7 @@ prompt) a few flags make the output predictable:
`platform info`, `swarm config list`/`show`, `swarm scan`,
`swarm platform info`, `swarm param get`, `swarm log print`,
`swarm deck list`, `swarm debug assert`, `swarm bootload info`,
`lh config display`, `lh config check`). Other
`swarm lh check`, `lh config list`/`display`/`check`). Other
commands ignore the flag.

Exit codes:
Expand All @@ -260,7 +260,7 @@ Exit codes:
| 30 | Invalid value (range, type, malformed input) |
| 40 | `--timeout` expired on a bounded command |
| 50 | A swarm command failed on some of the Crazyflies |
| 60 | A check found differences (`lh config check`) |
| 60 | A check found differences (`lh config check`, `swarm lh check`) |

Worked example — read one parameter into a shell variable:

Expand Down
17 changes: 17 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ fn main() {
"or lists them when non-interactive):_default",
"or lists them when non-interactive):_cfcli_swarm_configs",
);
// Lighthouse config IDs: `--config`, `import --id` and the positional
// ID of the `lh config` commands and `swarm config lh`.
s = s.replace(":CONFIG:_default", ":CONFIG:_cfcli_lh_configs");
for help in [
"Stored lighthouse config to display",
"Stored lighthouse config to write",
"Stored lighthouse config to compare with",
"Stored lighthouse config to export",
"Lighthouse config to name",
"Lighthouse config ID (prompts for one if omitted)",
"The lighthouse config to move",
"Only this shared lighthouse config (<org>/<config>)",
"Lighthouse config ID, picked from a list if omitted (a local swarm names one on this computer, a shared swarm one in its organization)",
"<org>/<config> shares it on the server",
] {
s = s.replace(&format!("{}:_default", help), &format!("{}:_cfcli_lh_configs", help));
}
// Crazyflie names: `--from-swarm` takes one, `--cf`/`--exclude` a
// comma-separated list. `::CF` first, it also contains `:CF`.
s = s.replace("::CF:_default", "::CF:_cfcli_swarm_units");
Expand Down
7 changes: 6 additions & 1 deletion completions/addendum.bash
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ _cfcli_dynamic() {
*" swarm config select "*|*" swarm config delete "*|*" swarm config show "*|*" swarm config export "*)
kind="swarm-configs" ;;
*" swarm config remove "*|*" swarm config rename "*) kind="swarm-units" ;;
*" swarm config lh "*|*" lh config display "*|*" lh config save "*|*" lh config write "*|\
*" lh config check "*|*" lh config export "*|*" lh config name "*|*" lh config delete "*|*" lh config move "*|\
*" lh config pull "*|*" lh config push "*) kind="lh-configs" ;;
esac

# Option values: `--targets x,y` (plain list) / `--bin t=f` (key=value),
Expand All @@ -54,14 +57,16 @@ _cfcli_dynamic() {
--bin) kind="flash-targets"; suffix="="; nospace=1 ;;
--targets) kind="flash-targets" ;;
--swarm) kind="swarm-configs" ;;
--config) kind="lh-configs" ;;
--cf|--exclude) kind="swarm-units"; nospace=1 ;;
--from-swarm) kind="swarm-units" ;;
# Values that are a new name, a file or free text.
-o|--output|--id|--name|--description) kind="" ;;
-o|--output|-i|--input|--id|--name|--description) kind="" ;;
=) case "${COMP_WORDS[COMP_CWORD-2]}" in
--bin) kind="flash-targets"; suffix="="; nospace=1 ;;
--targets) kind="flash-targets" ;;
--swarm) kind="swarm-configs" ;;
--config) kind="lh-configs" ;;
--cf|--exclude) kind="swarm-units"; nospace=1 ;;
--from-swarm) kind="swarm-units" ;;
esac ;;
Expand Down
5 changes: 4 additions & 1 deletion completions/addendum.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
'cfcli;(swarm;)?log;print$' { $cfcliDynKind = 'log-names' }
'cfcli;swarm;config;(select|delete|show|export)$' { $cfcliDynKind = 'swarm-configs' }
'cfcli;swarm;config;(remove|rename)$' { $cfcliDynKind = 'swarm-units' }
'cfcli;swarm;config;lh$' { $cfcliDynKind = 'lh-configs' }
'cfcli;lh;config;(display|save|write|check|export|name|delete|move|pull|push)$' { $cfcliDynKind = 'lh-configs' }
}
# Option values: `--targets x,y` (plain list) / `--bin t=f` (key=value),
# space-separated form.
Expand All @@ -30,9 +32,10 @@
if ($cfcliPrev -eq '--targets') { $cfcliDynKind = 'flash-targets' }
if ($cfcliPrev -eq '--bin') { $cfcliDynKind = 'flash-targets'; $cfcliSuffix = '=' }
if ($cfcliPrev -eq '--swarm') { $cfcliDynKind = 'swarm-configs' }
if ($cfcliPrev -eq '--config') { $cfcliDynKind = 'lh-configs' }
if ($cfcliPrev -in '--cf', '--exclude', '--from-swarm') { $cfcliDynKind = 'swarm-units' }
# Values that are a new name, a file or free text.
if ($cfcliPrev -in '-o', '--output', '--id', '--name', '--description') { $cfcliDynKind = '' }
if ($cfcliPrev -in '-o', '--output', '-i', '--input', '--id', '--name', '--description') { $cfcliDynKind = '' }

if ($cfcliDynKind -ne '') {
# `cfcli __complete` handles comma-separated lists, returning fully
Expand Down
1 change: 1 addition & 0 deletions completions/addendum.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ _cfcli_config_set() { _cfcli__dyn config-keys '=' }
_cfcli_log_names() { _cfcli__dyn log-names '' }
_cfcli_flash_targets() { _cfcli__dyn flash-targets }
_cfcli_swarm_configs() { _cfcli__dyn swarm-configs }
_cfcli_lh_configs() { _cfcli__dyn lh-configs }
_cfcli_swarm_units() { _cfcli__dyn swarm-units }
_cfcli_swarm_unit_list() { _cfcli__dyn swarm-units '' }
# `--bin` is a comma-separated list of `target=file`. In the current segment
Expand Down
12 changes: 7 additions & 5 deletions docs/auth.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Signing in

The `auth` command signs cfcli in to the server that shares swarms between
cfcli, Swarmkeeper and the people you fly with. Signing in is optional:
The `auth` command signs cfcli in to the server that shares swarms and
lighthouse configurations between cfcli, Swarmkeeper and the people you fly
with. Signing in is optional:
everything else in cfcli works without it.

```text
Expand Down Expand Up @@ -44,8 +45,8 @@ cfcli auth status
```

Prints the server, your name, the key's name and the organizations your swarms
are shared in. When cfcli isn't signed in, or the key no longer works, it says
so and exits with code 20.
and lighthouse configurations are shared in. When cfcli isn't signed in, or
the key no longer works, it says so and exits with code 20.

## Sign out

Expand All @@ -58,7 +59,8 @@ Revokes cfcli's key on the server and forgets it.
## The key

cfcli signs in with an API key of its own, named "cfcli on &lt;computer&gt;". It
reads and uploads swarms as you in the organizations you are a member of. It
reads and uploads swarms and lighthouse configurations as you in the
organizations you are a member of. It
is listed on the server's API keys page, where you can revoke it; that signs
cfcli out too.

Expand Down
155 changes: 136 additions & 19 deletions docs/lighthouse.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,28 @@ the lighthouse memory.
Usage: cfcli lh config <COMMAND>

Commands:
display Display lighthouse configuration in human-readable form
read Read lighthouse configuration as YAML (to file or stdout)
write Write lighthouse configuration from YAML (from file or stdin)
check Compare the Crazyflie's lighthouse configuration with YAML (from file or stdin)
list List the stored lighthouse configs: local ones and shared ones (<org>/<config>)
display Display a lighthouse configuration: a stored one, a file, or the Crazyflie's
read Read the Crazyflie's lighthouse configuration as YAML (to file or stdout)
save Store the Crazyflie's lighthouse configuration as <CONFIG> (new, or an update)
write Write a lighthouse configuration to the Crazyflie
check Compare the Crazyflie's lighthouse configuration with one
import Store a lighthouse configuration file (from the Crazyflie client or read)
export Write a stored lighthouse configuration to a file the Crazyflie client opens (or stdout)
name Show or set the name shown for a lighthouse config (its ID stays the same; 'move' changes the ID)
delete Delete a stored lighthouse configuration
move Share a lighthouse config (cage -> org/cage), take it back (org/cage -> cage), or rename it
pull Get the latest version of the shared lighthouse configs (needed with sync off)
push Upload changes to shared lighthouse configs made with sync off or without the server
```

`display`, `write` and `check` take a stored configuration by its ID, or a
file with `-i`. `write` and `check` also read YAML piped in. With none of
these, `write` lists the stored configurations to pick one from, and `check`
uses the configuration the selected swarm names (see
[Swarms](/docs/swarm.md#lighthouse)). Without a terminal, as for a command in
a script or a cron job, `write` uses the selected swarm's too.

### YAML File Format

The format matches the one used by the Python `cflib` so configurations can be
Expand Down Expand Up @@ -100,6 +116,8 @@ Top-level fields:
cflib
- `geos` — map of `bs_id -> { origin, rotation }`
- `calibs` — map of `bs_id -> { uid, sweeps[2] }`
- `name` — optional, the name shown for a stored configuration (cflib
ignores it)

Either map can be omitted or empty if you only want to read/write one half.
The maps are written in base station order, so reading the same configuration
Expand All @@ -111,11 +129,12 @@ Render the current configuration in human-readable form, either from the
Crazyflie or from a YAML file.

```text
cfcli lh config display [-i <FILE>]
cfcli lh config display [<CONFIG> | -i <FILE>]
```

Options:

- `<CONFIG>` — a stored configuration instead of the Crazyflie's
- `-i, --input <FILE>` — read from a YAML file instead of the Crazyflie

When `--csv` is used (the global flag), `display` emits a long-format CSV
Expand Down Expand Up @@ -146,6 +165,9 @@ cfcli lh config display
# Pretty print a YAML file (no Crazyflie connection)
cfcli lh config display -i my_setup.yaml

# Pretty print a stored configuration
cfcli lh config display lab/cage

# Machine-readable CSV
cfcli lh config display --csv

Expand Down Expand Up @@ -183,16 +205,31 @@ diff <(cfcli lh config read) my_reference.yaml

### Write

Write a configuration from a YAML file (or stdin) to the Crazyflie.
Write a configuration to the Crazyflie.

```text
cfcli lh config write [-i <FILE>]
cfcli lh config write [<CONFIG> | -i <FILE>]
```

Options:

- `-i, --input <FILE>` — read YAML from a file. If omitted, YAML is read from
stdin.
- `<CONFIG>` — a stored configuration
- `-i, --input <FILE>` — read YAML from a file

With neither, YAML piped in is written. Otherwise, in a terminal, `write`
lists all the stored configurations (local ones and the shared ones in all
your organizations) to pick one from, starting at the one the selected swarm
names. Without a terminal it writes the selected swarm's configuration. Stdin
that isn't a terminal but has nothing in it (a script, a cron job) counts as
nothing piped in.

```text
$ cfcli lh config write
? Lighthouse config to write:
cage - Local cage (4 base stations)
> lab/cage - The cage (4 base stations)
other/room - Other room (4 base stations)
```

All base station slots the firmware supports are written. Slots present in
the YAML are uploaded as valid, while slots omitted from the YAML are written
Expand All @@ -210,23 +247,27 @@ default firmware.
# Write a config from a file
cfcli lh config write -i my_setup.yaml

# Write a stored (or shared) config
cfcli lh config write lab/cage

# Pick one of the stored configs
cfcli lh config write

# Pipe YAML in from stdin
cat my_setup.yaml | cfcli lh config write
```

### Check

Compare the configuration on the Crazyflie with a YAML file (or stdin), base
station by base station.
Compare the configuration on the Crazyflie with another one, base station by
base station.

```text
cfcli lh config check [-i <FILE>]
cfcli lh config check [<CONFIG> | -i <FILE>]
```

Options:

- `-i, --input <FILE>` — read YAML from a file. If omitted, YAML is read from
stdin.
The configuration is chosen as for [write](#write), except that without one
`check` doesn't ask: it uses the configuration the selected swarm names.

Values are compared exactly, as the Crazyflie stores them. For each base
station, the geometry and the calibration are:
Expand Down Expand Up @@ -263,13 +304,89 @@ bs_id,geometry,moved_m,turned_deg,calibration,file_uid,cf_uid
cfcli lh config check -i my_setup.yaml

# Write only when it differs
cfcli lh config check -i my_setup.yaml; [ $? -eq 60 ] && cfcli lh config write -i my_setup.yaml
cfcli lh config check lab/cage; [ $? -eq 60 ] && cfcli lh config write lab/cage
```

## Stored and shared configurations

cfcli keeps lighthouse configurations the way it keeps swarms: local ones in a
`lighthouse` folder next to the cfcli config, named `<config>`, and, when
signed in (`cfcli auth login`), shared ones on the server, named
`<org>/<config>`. A shared configuration has revisions, so when the base
stations are moved, everyone gets the new geometry, and the server's web
page shows what changed. With sync on (the default), commands check the
server for the latest version; with sync off, or without the server, they use
this computer's copy, and `pull` and `push` sync them, as for
[swarms](/docs/swarm.md#sync).

```text
# Store what a Crazyflie has (after estimating the geometry in cfclient)
cfcli lh config save lab/cage --name "The cage"

# ... or a file saved by the Crazyflie client
cfcli lh config import Lighthouse_Cage.yaml --id lab/cage --name "The cage"

# List them
$ cfcli lh config list
ID | Name | Base stations | Stored
---------+----------+---------------+-----------------------------
lab/cage | The cage | 4 | arc.bitcraze.io, revision 2

# Give it to a Crazyflie, or check one
cfcli lh config write lab/cage
cfcli lh config check lab/cage

# A file the Crazyflie client opens
cfcli lh config export lab/cage -o cage.yaml
```

`save` and `import` to an existing ID show what changes for each base station
and ask before replacing it (`--force` replaces without asking, and is needed
when not interactive). A configuration without any base station positions is
refused: estimate the geometry first. `base stations` in the list counts the
positioned ones; a Crazyflie also keeps the calibration of base stations it
has seen elsewhere.

`name` shows the name a configuration is shown with, or gives it one; the ID
stays the same, and a shared configuration gets a new revision. Files from
the Crazyflie client have no name, so this names one after importing it
(`import --name` names it at once):

```text
cfcli lh config name lab/cage "The cage"
cfcli lh config name lab/cage # prints: The cage
```

`move` shares a local configuration (`move cage lab/cage`), takes a shared one
back (which deletes it on the server for everyone in the organization), or
renames one. Swarms that fly in it keep the old ID: `move` lists them and the
`cfcli swarm config lh` command that gives them the new one. A local swarm
can only name a configuration on this computer, and a shared swarm one in its
organization, so not every swarm can follow: taking a shared configuration
back, for example, leaves its shared swarms without it. The server refuses a
shared swarm that names another organization's configuration, so that
everyone who sees the swarm can see it.

`delete` deletes a configuration, a shared one on the server for everyone in
the organization. Before asking, it lists the swarms that fly in it: the
local ones and, for a shared configuration, the organization's shared swarms
(the server's list, or this computer's copies when the server can't be
reached). They keep naming it, so give them another one, or none with
`cfcli swarm config lh --clear`.

```text
$ cfcli lh config delete lab/cage
Swarms that fly in lighthouse config 'lab/cage': 'lab/flight-test'
? Delete lighthouse config 'lab/cage' on arc.bitcraze.io, for everyone in lab? Yes
Deleted lighthouse config 'lab/cage'
'lab/flight-test' still names it: give it another with 'cfcli swarm config lh <CONFIG> --swarm <SWARM>', or none with '--clear'
```

## Copy a Configuration Between Crazyflies

Read from one Crazyflie and write to another by piping `read` into `write`,
overriding the `--uri` for each:
Store it from one Crazyflie and write it to the others (`save`, then `write`
or `swarm lh write`), or pipe `read` into `write`, overriding the `--uri` for
each:

```bash
cfcli --uri radio://0/80/2M/E7E7E7E7E7 lh config read \
Expand Down
Loading
Loading