Skip to content

Allow users to change the system ID in the Vehicle Setup component - #4365

Merged
patrickelectric merged 9 commits into
bluerobotics:masterfrom
nukelet:configurable-vehicle-id
Sep 10, 2026
Merged

Allow users to change the system ID in the Vehicle Setup component#4365
patrickelectric merged 9 commits into
bluerobotics:masterfrom
nukelet:configurable-vehicle-id

Conversation

@nukelet

@nukelet nukelet commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

We would like to provide a unified way for users to configure the system ID for a vehicle. As it currently stands, there are two sources of truth for the vehicle ID that need to be kept in sync:

  • The MAV_SYSID parameter in the Ardupilot firmware
  • The MAV_SYSTEM_ID environment variable, which is used to determine the vehicle ID in mavlink-server/mavlink2rest

This PR aims to provide an interface in the UI for users to change the vehicle ID:

  • Introduce a new autopilot_manager endpoint, /system_id, to set the value for MAV_SYSTEM_ID through a POST request, making the change persistent across reboots by storing the variable in the environment field of bootstrap/startup.json
  • Add a simple component to the Vehicle Setup tab that allows users to modify the Vehicle ID to their desired value

Note that (currently) it is necessary to restart the BlueOS container in order to have the MAV_SYSTEM_ID environment variable propagated to all services (there is a button in the Vehicle ID setup UI for restarting the container).

Depends on #4364
Fixes #3604

@nukelet
nukelet force-pushed the configurable-vehicle-id branch 3 times, most recently from 8705e73 to d5fce82 Compare September 2, 2026 19:23
@nukelet
nukelet requested a balanced review from Copilot September 2, 2026 19:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Error handling, input validation, and coordination between the two system-ID updates must be corrected.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds unified vehicle system-ID configuration across ArduPilot and BlueOS services.

Changes:

  • Adds persistent MAV_SYSTEM_ID configuration.
  • Adds Vehicle Setup editing and restart controls.
  • Displays the active system ID.
File summaries
File Description
core/services/ardupilot_manager/settings.py Defines the startup configuration path.
core/services/ardupilot_manager/api/v1/routers/index.py Adds the system-ID persistence endpoint.
core/frontend/src/components/vehiclesetup/overview/VehicleInfo.vue Displays the active system ID.
core/frontend/src/components/vehiclesetup/overview/SystemId.vue Adds system-ID editing and restart controls.
core/frontend/src/components/vehiclesetup/Configure.vue Registers the Vehicle ID setup page.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 4
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread core/frontend/src/components/vehiclesetup/overview/SystemId.vue Outdated
Comment thread core/services/ardupilot_manager/api/v1/routers/index.py Outdated
Comment thread core/services/ardupilot_manager/api/v1/routers/index.py Outdated
Comment on lines +322 to +323
except Exception as error:
logger.warning(f"Unable to write MAV_SYSTEM_ID to bootstrap/startup.json: {error}")
@joaoantoniocardoso

joaoantoniocardoso commented Sep 3, 2026

Copy link
Copy Markdown
Member

Cool!

I didn't test it yet, but I have one point: if we move the parameter set part to the backend, we allow non-UI clients to complete the full change-mavlink-id journey. If so, I'd adjust the endpoint name to be more generic.

Thanks

@nukelet

nukelet commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Yeah, that's a good idea, I'll move the PARAM_SET bits to the backend and rename the endpoint to something like /system_id. I'll also address some of the comments from the bot review. Thanks for the suggestion!

@nukelet
nukelet force-pushed the configurable-vehicle-id branch 2 times, most recently from c489361 to cac5a82 Compare September 4, 2026 10:00
@nukelet
nukelet requested a balanced review from Copilot September 4, 2026 10:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The update can leave IDs inconsistent, and the verification loop unnecessarily delays every save.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment on lines +314 to +326
with open(autopilot.settings.startup_settings_file, "r+", encoding="utf-8") as startup_settings:
settings = json.load(startup_settings)
environment = settings["core"].get("environment", [])

# make sure to remove MAV_SYSTEM_ID if it is already defined in
# bootstrap/startup.json
environment = [v for v in environment if not v.startswith("MAV_SYSTEM_ID=")]
environment.append(f"MAV_SYSTEM_ID={value}")
settings["core"]["environment"] = environment

startup_settings.seek(0)
startup_settings.write(json.dumps(settings, indent=2))
startup_settings.truncate()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i disagree with the bot here, if we fail to write to startup.json then we have bigger problems to worry about. it's definitely not the weakest link here

Comment thread core/frontend/src/components/vehiclesetup/overview/SystemId.vue Outdated
Comment thread core/libs/commonwealth/src/commonwealth/mavlink_comm/VehicleManager.py Outdated
Comment thread core/services/ardupilot_manager/api/v1/routers/index.py
Implement support for changing the vehicle's system id on the firmware
side through a PARAM_SET message that configures the MAV_SYSID parameter.

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
…ration

This endpoint sets the MAV_SYSID firmware parameter and also sets the
MAV_SYSTEM_ID environment variable (used by mavlink2rest) to its new
value by updating bootstrap/startup.json.

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
Introduce the SystemId component with an input field for setting the
vehicle's system ID. It also comes with two buttons:

- A "Save" button that calls ardupilot_manager's /system_id endpoint to
  change the system id
- A "Reboot Core" button which restarts the blueos-core container since
  the MAV_SYSTEM_ID environment variable change requires a container
  reboot to propagate

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
Follow the same naming convention as the command_long_message and
param_set_message helpers.

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
@nukelet
nukelet force-pushed the configurable-vehicle-id branch from cac5a82 to ddefebf Compare September 4, 2026 10:33
@nukelet
nukelet requested a review from a team September 4, 2026 10:35
@nukelet

nukelet commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

I moved the system ID change operation entirely into the backend and renamed the endpoint to /system_id. You can poke at it with curl:

curl -X POST http://<blueos-addr>/autopilot-manager/v1.0/system_id\?value\=42 -v

This was a bit more painful to figure out than I anticipated since there isn't a clean way for the onboard computer to know that the MAV_SYSID parameter was updated successfully in the firmware. The frontend (system id 255) gets to hear about it immediately because Ardupilot will (following the MAVLink spec) acknowledge a PARAM_SET operation by broadcasting a PARAM_VALUE message containing the new value of the updated parameter; however, since the onboard computer (which emits the PARAM_SET) has the same system id as Ardupilot (who broadcasts the PARAM_VALUE) and mavlink-server doesn't allow loopback messages, BlueOS never gets the PARAM_VALUE confirmation (this took me an embarassing amount of time to figure out).

@patrickelectric patrickelectric left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in a good direction, let's continue

Comment on lines +180 to +182
while time.time() - start_time < timeout:
if await self.is_heart_beating():
continue

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if I follow, isn't this the same than waiting 30s ? also.. would be nice to put the reference where in the system reflects in this 30s value.

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this should have been a break instead of continue. definitely needed more coffee when wrapping up these changes

logger.warning(message)
return PlainTextResponse(message, status_code=503)

try:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC try here is unnecessary since you are using the index_to_http_exception decorator

url: '/version-chooser/v1.0/version/restart',
}).finally(() => {
// Give the backend a bit to go down, then reload so the user reconnects to the fresh core
setTimeout(() => window.location.reload(), 15000)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not guess, take a look in PowerMenu: waitForBackendToBeOnline

try:
await autopilot.vehicle_manager.set_system_id(value)

if autopilot.vehicle_manager.target_system != value:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, isn't this supposed to be done already in set_system_id ?

if autopilot.vehicle_manager.target_system != value:
return PlainTextResponse("Failed to set system ID", status_code=500)

with open(autopilot.settings.startup_settings_file, "r+", encoding="utf-8") as startup_settings:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be better to have it encapsulated by a class or something, maybe the settings itself, over opening and manipulating a settings file inside an api endpoint.

@patrickelectric patrickelectric Sep 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also.. maybe not even in autopilot.settings, but maybe autopilot.bootstrap.settings, with that you can create the abstraction in autopilot.bootstrap; also, if we are going to have shared logic of bootstrap here and with other services, does it worth to have a commonwealth place for it ?

…after restart

Wait for the BlueOS container to come back online instead of guessing
with a hardcoded timeout. Also add a spinner for better UX, in similar
fashion to the PowerMenu component.

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
Verify that a system id change was successful in a more clear and robust
way instead of relying on an unintuitive chain of side effects to notice
and propagate the change across MavlinkManager/VehicleManager.

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
- Drop the try/except since index_to_http_exception handles exceptions
  gracefully
- Drop the system system id change validation check (it's already
  handled by VehicleManager)

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
Add a small wrapper that allows setting persistent env vars, which is
currently achieved by writing to the "environment" property of
bootstrap/startup.json.

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
…SYSTEM_ID

Signed-off-by: Vinicius Peixoto <vinicius@nukelet.dev>
@nukelet

nukelet commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

@patrickelectric, I believe I addressed all the points from your review. Notable changes:

  • Frontend now shows a spinner + waits for the backend to come back online instead of guessing a timeout
  • Refactored the cursed validation logic to confirm the system id change by waiting for get_most_recent_vehicle_id() to match the value we attempted to configure. This check is (obviously) much faster now, taking only 2 or 3 seconds
  • Created a small EnvironmentManager class in commonwealth/utils with only a set_variable method (doing essentially what was being done in the /system_id endpoint before, e.g. writing the variable to bootstrap/startup.json). I'm keeping it small for now so as to satisfy only the needs of its single user (VehicleManager.set_system_id)

@nukelet

nukelet commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

btw, I left a bunch of FIXUP commits to make it easier to iterate on this round of chagnes, but I intend to squash them later

@patrickelectric patrickelectric left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR looks much better. I did a review on the surface, will do a better one later today.

timeout = 10.0
while time.time() - start_time < timeout:
if await self.mavlink2rest.get_most_recent_vehicle_id() == value:
self.set_target_system(value)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to be sure.. we did that in the start of the function, why do we need to do that again ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if the target_system is defined in the start of the function, and the vehicle did not update the system_id, the later if will check the previously defined value and not the new one.

@nukelet nukelet Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we did that in the start of the function, why do we need to do that again ?

we do it at the start because self.target_system is initially set to 1, so if the vehicle already has a system id other than 1 we will end up sending the PARAM_SET message to the wrong target system unless we update self.target_system first.

changing it inside the loop on the other hand means we confirmed the system id change was successful and that we can go ahead and update self.target_system

Also, if the target_system is defined in the start of the function, and the vehicle did not update the system_id, the later if will check the previously defined value and not the new one.

not sure i follow, the point is precisely that if we didn't update self.target_system within the while loop then the autopilot firmware didn't change MAV_SYSID at all?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

brain lapse

@patrickelectric
patrickelectric merged commit 30d0e43 into bluerobotics:master Sep 10, 2026
7 checks passed
@joaoantoniocardoso joaoantoniocardoso added the docs-needed Change needs to be documented label Sep 10, 2026
@rmackay9

Copy link
Copy Markdown

Thanks very much for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs-needed Change needs to be documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unified way to change vehicle ID

5 participants