Skip to content
Draft
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
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub mod utils {
pub mod firmware;
pub mod flash_source;
pub mod radio;
pub mod udp;
}

use error::CliError;
Expand Down Expand Up @@ -1246,12 +1247,15 @@ async fn run() -> Result<()> {
modules::bootloader::reboot(&link_context, uri.as_str()).await?;
},
PlatformCommands::PowerOff => {
utils::udp::check_not_udp(&uri, "Powering off")?;
crazyflie_lib::Crazyflie::power_off_all(&link_context, uri.as_str()).await?;
},
PlatformCommands::Sleep => {
utils::udp::check_not_udp(&uri, "Sleep")?;
crazyflie_lib::Crazyflie::power_off_stm32_domain(&link_context, uri.as_str()).await?;
},
PlatformCommands::Wakeup => {
utils::udp::check_not_udp(&uri, "Wakeup")?;
crazyflie_lib::Crazyflie::power_on_stm32_domain(&link_context, uri.as_str()).await?;
}
PlatformCommands::Dfu => {
Expand Down
5 changes: 5 additions & 0 deletions src/modules/bootloader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::modules::memory::{
use crate::utils::firmware::{Firmware, FirmwareUpgrade, FlashStartOverride};
use crate::utils::display::*;
use crate::utils::radio::RadioUri;
use crate::utils::udp::check_not_udp;

const DECK_CTRL_DFU_FLASH_OFFSET: usize = 0x10000;
const DECK_CTRL_DFU_CFG_DEFAULT_OFFSET: usize = 0x17800;
Expand Down Expand Up @@ -235,6 +236,7 @@ async fn reset_and_get_bootloader_address(link: &Connection) -> Result<Vec<u8>>
/// return the URI to reach the bootloader at. The bootloader is reached
/// with the same Crazyradio as the firmware, the first one for usb://.
async fn enter_bootloader(context: &LinkContext, uri: &str) -> Result<String> {
check_not_udp(uri, "Restarting into the bootloader")?;
let radio = RadioUri::parse(uri).ok().flatten().and_then(|r| r.radio).unwrap_or(0);
let separator = if uri.contains('?') { "&" } else { "?" };
let link = context.open_link(&format!("{}{}safelink=0", uri, separator)).await?;
Expand Down Expand Up @@ -314,6 +316,7 @@ async fn read_info(link: &Connection) -> Result<(BootloaderInfo, BootloaderInfo)
/// Restart a Crazyflie that runs its firmware into its bootloader, and return
/// the address the bootloader listens on (on channel 0), for cfloader.
pub async fn enter_bootloader_for_cfloader(context: &LinkContext, uri: &str) -> Result<[u8; 5]> {
check_not_udp(uri, "Restarting into the bootloader")?;
let separator = if uri.contains('?') { "&" } else { "?" };
let link = context.open_link(&format!("{}{}safelink=0", uri, separator)).await?;
let new_address = reset_and_get_bootloader_address(&link).await;
Expand Down Expand Up @@ -422,6 +425,7 @@ pub async fn bootloader_versions(link_context: &LinkContext, uri: &str) -> Resul
/// which only receives it over the radio: the STM32 firmware does not pass it
/// on from USB. Returns whether the STM32 then showed up on USB in DFU mode.
pub async fn stm32_dfu(link_context: &LinkContext, uri: &str) -> Result<bool> {
check_not_udp(uri, "Restarting the STM32 into DFU")?;
if uri.starts_with("usb://") {
bail!("Restarting the STM32 into DFU needs a radio URI, the nRF51 does not get this command over USB");
}
Expand Down Expand Up @@ -451,6 +455,7 @@ fn stm32_dfu_present() -> Result<bool> {
}

pub async fn reboot(link_context: &LinkContext, uri: &str,) -> Result<()> {
check_not_udp(uri, "Rebooting")?;

let link = link_context.open_link(uri).await?;
send_command(&link, BootloaderCommand::ResetInit, None).await?;
Expand Down
5 changes: 5 additions & 0 deletions src/modules/swarm/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use tabled::Tabled;
use super::runner::{csv_row_for, split, Outcome, Runner, SwarmRow};
use crate::error::CliError;
use crate::modules::{bootloader, debug, deck, log, param, platform};
use crate::utils::udp::check_not_udp;
use crate::utils::display::{csv_row, print_table, table, Column, StreamTable};
use crate::{
AssertArgs, SwarmDebugCommands, SwarmDeckCommands, SwarmLogCommands, SwarmParamCommands,
Expand All @@ -34,6 +35,7 @@ pub async fn platform(runner: &Runner<'_>, command: &SwarmPlatformCommands, csv:
SwarmPlatformCommands::Reboot => (
runner
.each("Rebooting", async |t| {
check_not_udp(&t.uri, "Rebooting")?;
t.check_answers(link_context).await?;
bootloader::reboot(link_context, &t.link_uri).await
})
Expand All @@ -43,6 +45,7 @@ pub async fn platform(runner: &Runner<'_>, command: &SwarmPlatformCommands, csv:
SwarmPlatformCommands::PowerOff => (
runner
.each("Powering off", async |t| {
check_not_udp(&t.uri, "Powering off")?;
t.check_answers(link_context).await?;
Ok(Crazyflie::power_off_all(link_context, &t.link_uri).await?)
})
Expand All @@ -52,6 +55,7 @@ pub async fn platform(runner: &Runner<'_>, command: &SwarmPlatformCommands, csv:
SwarmPlatformCommands::Sleep => (
runner
.each("Putting to sleep", async |t| {
check_not_udp(&t.uri, "Sleep")?;
t.check_answers(link_context).await?;
Ok(Crazyflie::power_off_stm32_domain(link_context, &t.link_uri).await?)
})
Expand All @@ -61,6 +65,7 @@ pub async fn platform(runner: &Runner<'_>, command: &SwarmPlatformCommands, csv:
SwarmPlatformCommands::Wakeup => (
runner
.each("Waking up", async |t| {
check_not_udp(&t.uri, "Wakeup")?;
t.check_answers(link_context).await?;
Ok(Crazyflie::power_on_stm32_domain(link_context, &t.link_uri).await?)
})
Expand Down
14 changes: 9 additions & 5 deletions src/modules/swarm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,15 +1088,15 @@ struct ScanRow {
/// connection.
async fn scan(runner: &Runner<'_>, csv: bool) -> Result<()> {
let link_context = runner.link_context();
let usb_present = if runner.targets.iter().any(|t| t.radio.is_none()) {
let usb_present = if runner.targets.iter().any(|t| t.is_usb()) {
crate::scan_usb(link_context).await?
} else {
Vec::new()
};
let results = runner
.each("Checking", async |t| match t.radio {
Some(_) => Ok(t.answers(link_context).await),
None => Ok(usb_present.contains(&t.uri)),
.each("Checking", async |t| match t.is_usb() {
true => Ok(usb_present.contains(&t.uri)),
false => Ok(t.answers(link_context).await),
})
.await;
let online: Vec<bool> = results.into_iter().map(|r| r.unwrap_or(false)).collect();
Expand All @@ -1108,7 +1108,11 @@ async fn scan(runner: &Runner<'_>, csv: bool) -> Result<()> {
.map(|(t, online)| ScanRow {
name: t.name.clone(),
uri: t.uri.clone(),
radio: t.radio.map(|r| r.to_string()).unwrap_or_else(|| "USB".to_string()),
radio: match t.radio {
Some(radio) => radio.to_string(),
None if t.is_usb() => "USB".to_string(),
None => "UDP".to_string(),
},
online: if *online { "yes" } else { "no" }.to_string(),
})
.collect();
Expand Down
16 changes: 11 additions & 5 deletions src/modules/swarm/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub struct Target {
/// The URI to connect with: a real Crazyradio, and the timeout from the
/// settings.
pub link_uri: String,
/// The Crazyradio it was given, `None` for `usb://`.
/// The Crazyradio it was given, `None` for `usb://` and `udp://`.
pub radio: Option<usize>,
}

Expand Down Expand Up @@ -300,11 +300,17 @@ impl<'a> Runner<'a> {
}

impl Target {
/// Whether the Crazyflie answers a packet over the radio, without
/// connecting to it. `usb://` targets aren't checked and count as
/// answering: opening a link to a missing USB device fails anyway.
/// Whether the Crazyflie is attached over USB.
pub fn is_usb(&self) -> bool {
self.uri.starts_with("usb://")
}

/// Whether the Crazyflie answers a packet over the radio, or the
/// simulator over UDP, without connecting to it. `usb://` targets aren't
/// checked and count as answering: opening a link to a missing USB device
/// fails anyway.
pub async fn answers(&self, link_context: &LinkContext) -> bool {
if self.radio.is_none() {
if self.is_usb() {
return true;
}
for _ in 0..ANSWER_ATTEMPTS {
Expand Down
4 changes: 2 additions & 2 deletions src/modules/swarm/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ pub fn same_link(a: &str, b: &str) -> bool {

/// Check a URI that is about to go into a swarm.
pub fn check_uri(uri: &str) -> Result<()> {
if RadioUri::parse(uri)?.is_none() && !uri.starts_with("usb://") {
if RadioUri::parse(uri)?.is_none() && !uri.starts_with("usb://") && !uri.starts_with("udp://") {
bail!(CliError::InvalidValue(format!(
"URI '{}': only radio:// and usb:// URIs can be added to a swarm",
"URI '{}': only radio://, usb:// and udp:// URIs can be added to a swarm",
uri
)));
}
Expand Down
25 changes: 25 additions & 0 deletions src/utils/udp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! `udp://<host>:<port>` URIs reach the Crazyflie firmware simulator
//! (Simmyflie). It runs the STM32 firmware only: there is no nRF51 to take
//! power commands and no bootloader to flash through.

use anyhow::{bail, Result};

/// Fail for a `udp://` URI: `what` needs the nRF51 or the bootloader.
pub fn check_not_udp(uri: &str, what: &str) -> Result<()> {
if uri.starts_with("udp://") {
bail!("{} is not supported over udp://, the firmware simulator has no nRF51 or bootloader", what);
}
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn check_not_udp_rejects_udp_only() {
assert!(check_not_udp("udp://localhost:50000", "Rebooting").is_err());
assert!(check_not_udp("radio://0/80/2M/E7E7E7E7E7", "Rebooting").is_ok());
assert!(check_not_udp("usb://0", "Rebooting").is_ok());
}
}
Loading