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
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@ When connected the emberOne usbserial firmware will create two serial ports. Usu
3. command bus
- always 0x00
4. command page
- I2C: 0x05
- GPIO: 0x06
- ADC: 0x07
- LED: 0x08
- I2C: 0x05
- GPIO: 0x06
- ADC: 0x07
- LED: 0x08
- System: 0x09
5. command
- varies by command page. See below
6. data
Expand Down Expand Up @@ -151,3 +152,23 @@ Data:
Example:

- Set LED Magenta: `09 00 00 00 08 10 FF 00 FF`

**System**

Commands require a magic payload to guard against accidental
reboot from line noise or a framing desync. No response is
sent; the host detects success by observing USB re-enumeration.

Commands:

- Reboot: 0x01 (magic: `DE AD BE EF`)
- Reboot to bootloader: 0x02 (magic: `B0 07 10 AD`)

Data:

- [magic (4 bytes)]

Examples:

- Reboot: `0A 00 00 00 09 01 DE AD BE EF`
- Reboot to bootloader: `0A 00 00 00 09 02 B0 07 10 AD`
9 changes: 9 additions & 0 deletions src/control/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const ADC_COMMAND: u8 = 7;
pub mod led;
const LED_COMMAND: u8 = 8;

pub mod system;
const SYSTEM_COMMAND: u8 = 9;

#[derive(defmt::Format)]
struct Command {
Expand All @@ -36,6 +38,7 @@ enum CommandInner {
Gpio(gpio::Command),
Adc(adc::Command),
Led(led::Command),
System(system::Command),
Error(CommandError),
}

Expand Down Expand Up @@ -63,6 +66,11 @@ impl Command {
bus: buf[1],
inner: CommandInner::Led(led::Command::from_bytes(&buf[3..])?),
}),
SYSTEM_COMMAND => Ok(Self {
id,
bus: buf[1],
inner: CommandInner::System(system::Command::from_bytes(&buf[3..])?),
}),
_ => Err(CommandError::Invalid),
}
}
Expand Down Expand Up @@ -126,6 +134,7 @@ impl Controller {
CommandInner::Gpio(cmd) => cmd.handle(self).await,
CommandInner::Adc(cmd) => cmd.handle(self).await,
CommandInner::Led(cmd) => cmd.handle(self).await,
CommandInner::System(cmd) => cmd.handle(self).await,
CommandInner::Error(err) => Err(err),
};

Expand Down
46 changes: 46 additions & 0 deletions src/control/system.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use defmt::{info, panic};
use heapless::Vec;

use super::CommandError;

// Commands require a magic payload to guard against accidental reboot
// from line noise or a framing desync.
const REBOOT_MAGIC: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
const REBOOT_TO_BOOTLOADER_MAGIC: [u8; 4] = [0xB0, 0x07, 0x10, 0xAD];

#[derive(defmt::Format)]
pub enum Command {
Reboot,
RebootToBootloader,
}

impl Command {
pub fn from_bytes(buf: &[u8]) -> Result<Self, CommandError> {
match buf {
[0x01, rest @ ..] if rest == REBOOT_MAGIC => Ok(Self::Reboot),
[0x02, rest @ ..] if rest == REBOOT_TO_BOOTLOADER_MAGIC => {
Ok(Self::RebootToBootloader)
}
_ => Err(CommandError::Invalid),
}
}
}

impl super::ControllerCommand for Command {
async fn handle(
&self,
_controller: &mut super::Controller,
) -> Result<Vec<u8, 256>, CommandError> {
match self {
Command::Reboot => {
info!("System: rebooting");
cortex_m::peripheral::SCB::sys_reset()
}
Command::RebootToBootloader => {
info!("System: rebooting to bootloader");
embassy_rp::rom_data::reset_to_usb_boot(0, 0);
panic!("reset_to_usb_boot returned");
}
}
}
}