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
14 changes: 11 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,15 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: build
args: --release --bin espflash --target x86_64-unknown-linux-musl
args: --release --all --target x86_64-unknown-linux-musl
- uses: actions/upload-artifact@v2
with:
name: espflash
path: target/x86_64-unknown-linux-musl/release/espflash
- uses: actions/upload-artifact@v2
with:
name: cargo-espflash
path: target/x86_64-unknown-linux-musl/release/cargo-espflash

build-windows:
name: Build Static Windows Binaries
Expand All @@ -123,8 +127,12 @@ jobs:
with:
use-cross: true
command: build
args: --release --bin espflash --target x86_64-pc-windows-gnu
args: --release --all --target x86_64-pc-windows-gnu
- uses: actions/upload-artifact@v2
with:
name: espflash.exe
path: target/x86_64-pc-windows-gnu/release/espflash.exe
path: target/x86_64-pc-windows-gnu/release/espflash.exe
- uses: actions/upload-artifact@v2
with:
name: cargo-espflash.exe
path: target/x86_64-pc-windows-gnu/release/cargo-espflash.exe
3 changes: 2 additions & 1 deletion cargo-espflash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ categories = [
miette = "2"
cargo_metadata = "0.14"
clap = "2.33"
crossterm = "0.21"
espflash = { version = "*", path = "../espflash" }
guess_host_triple = "0.1"
serde = { version = "1.0", features = ["derive"] }
serial = "0.4"
toml = "0.5"
thiserror = "1"
thiserror = "1"
57 changes: 57 additions & 0 deletions cargo-espflash/src/line_endings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Adapted from https://github.com/derekdreery/normalize-line-endings

struct Normalized<I> {
iter: I,
prev_was_cr: bool,
peeked: Option<u8>,
}

pub fn normalized(iter: impl Iterator<Item = u8>) -> impl Iterator<Item = u8> {
Normalized {
iter,
prev_was_cr: false,
peeked: None,
}
}

impl<I> Iterator for Normalized<I>
where
I: Iterator<Item = u8>,
{
type Item = u8;
fn next(&mut self) -> Option<u8> {
if let Some(peeked) = self.peeked.take() {
return Some(peeked);
}
match self.iter.next() {
Some(b'\n') if !self.prev_was_cr => {
self.peeked = Some(b'\n');
self.prev_was_cr = false;
Some(b'\r')
}
Some(b'\r') => {
self.prev_was_cr = true;
Some(b'\r')
}
any => {
self.prev_was_cr = false;
any
}
}
}
}

// tests
#[cfg(test)]
mod tests {
use std::iter::FromIterator;

#[test]
fn normalized() {
let input = b"This is a string \n with \n some \n\r\n random newlines\r\n\n";
assert_eq!(
&Vec::from_iter(super::normalized(input.iter().copied())),
b"This is a string \r\n with \r\n some \r\n\r\n random newlines\r\n\r\n"
);
}
}
17 changes: 14 additions & 3 deletions cargo-espflash/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ use error::Error;
use espflash::{Config, Flasher, PartitionTable};
use miette::{IntoDiagnostic, Result, WrapErr};
use serial::{BaudRate, SerialPort};

use std::{
fs,
path::PathBuf,
process::{exit, Command, ExitStatus, Stdio},
string::ToString,
};

use cargo_config::has_build_std;
use monitor::monitor;

mod cargo_config;
mod error;

use cargo_config::has_build_std;
mod line_endings;
mod monitor;

fn main() -> Result<()> {
let mut app = App::new(env!("CARGO_PKG_NAME"))
Expand Down Expand Up @@ -80,6 +82,11 @@ fn main() -> Result<()> {
.takes_value(true)
.value_name("SERIAL")
.help("Serial port connected to target device"),
)
.arg(
Arg::with_name("monitor")
.long("monitor")
.help("Open a serial monitor after flashing"),
),
);

Expand Down Expand Up @@ -178,6 +185,10 @@ fn main() -> Result<()> {
flasher.load_elf_to_flash(&elf_data, bootloader, partition_table)?;
}

if matches.is_present("monitor") {
monitor(flasher.into_serial()).into_diagnostic()?;
}

// We're all done!
Ok(())
}
Expand Down
129 changes: 129 additions & 0 deletions cargo-espflash/src/monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use crate::line_endings::normalized;
use crossterm::event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use miette::{IntoDiagnostic, Result};
use serial::{SerialPort, SystemPort};
use std::io::{stdout, ErrorKind, Read, Write};
use std::thread::sleep;
use std::time::Duration;

/// Converts key events from crossterm into appropriate character/escape sequences which are then
/// sent over the serial connection.
///
/// Adapted from https://github.com/dhylands/serial-monitor
fn handle_key_event(key_event: KeyEvent) -> Option<Vec<u8>> {
// The following escape sequences come from the MicroPython codebase.
//
// Up ESC [A
// Down ESC [B
// Right ESC [C
// Left ESC [D
// Home ESC [H or ESC [1~
// End ESC [F or ESC [4~
// Del ESC [3~
// Insert ESC [2~

let mut buf = [0; 4];

let key_str: Option<&[u8]> = match key_event.code {
KeyCode::Backspace => Some(b"\x08"),
KeyCode::Enter => Some(b"\r"),
KeyCode::Left => Some(b"\x1b[D"),
KeyCode::Right => Some(b"\x1b[C"),
KeyCode::Home => Some(b"\x1b[H"),
KeyCode::End => Some(b"\x1b[F"),
KeyCode::Up => Some(b"\x1b[A"),
KeyCode::Down => Some(b"\x1b[B"),
KeyCode::Tab => Some(b"\x09"),
KeyCode::Delete => Some(b"\x1b[3~"),
KeyCode::Insert => Some(b"\x1b[2~"),
KeyCode::Esc => Some(b"\x1b"),
KeyCode::Char(ch) => {
if key_event.modifiers & KeyModifiers::CONTROL == KeyModifiers::CONTROL {
buf[0] = ch as u8;
if ('a'..='z').contains(&ch) || (ch == ' ') {
buf[0] &= 0x1f;
Some(&buf[0..1])
} else if ('4'..='7').contains(&ch) {
// crossterm returns Control-4 thru 7 for \x1c thru \x1f
buf[0] = (buf[0] + 8) & 0x1f;
Some(&buf[0..1])
} else {
Some(ch.encode_utf8(&mut buf).as_bytes())
}
} else {
Some(ch.encode_utf8(&mut buf).as_bytes())
}
}
_ => None,
};
key_str.map(|slice| slice.into())
}

struct RawModeGuard;

impl RawModeGuard {
pub fn new() -> Result<Self> {
enable_raw_mode().into_diagnostic()?;
Ok(RawModeGuard)
}
}

impl Drop for RawModeGuard {
fn drop(&mut self) {
if let Err(e) = disable_raw_mode() {
eprintln!("{:#}", e)
}
}
}

pub fn monitor(mut serial: SystemPort) -> serial::Result<()> {
println!("Commands:");
println!(" CTRL+R Reset chip");
println!(" CTRL+C Exit");
println!();

let mut buff = [0; 128];
serial.set_timeout(Duration::from_millis(5))?;

let _raw_mode = RawModeGuard::new();
let stdout = stdout();
let mut stdout = stdout.lock();
loop {
let read_count = match serial.read(&mut buff) {
Ok(count) => Ok(count),
Err(e) if e.kind() == ErrorKind::TimedOut => Ok(0),
err => err,
}?;
if read_count > 0 {
let data: Vec<u8> = normalized(buff[0..read_count].iter().copied()).collect();
let data = String::from_utf8_lossy(&data);
stdout.write_all(data.as_bytes()).ok();
stdout.flush()?;
}
if poll(Duration::from_secs(0))? {
if let Event::Key(key) = read()? {
if key.modifiers.contains(KeyModifiers::CONTROL) {
match key.code {
KeyCode::Char('c') => break,
KeyCode::Char('r') => {
serial.set_dtr(false)?;
serial.set_rts(true)?;

sleep(Duration::from_millis(100));

serial.set_rts(false)?;
continue;
}
_ => {}
}
}
if let Some(bytes) = handle_key_event(key) {
serial.write_all(&bytes)?;
serial.flush()?;
}
}
}
}
Ok(())
}
16 changes: 10 additions & 6 deletions espflash/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use crate::encoder::SlipEncoder;
use crate::error::{ConnectionError, Error, RomError};
use binread::io::Cursor;
use binread::{BinRead, BinReaderExt};
use serial::{BaudRate, SerialPort, SerialPortSettings};
use serial::{BaudRate, SerialPort, SerialPortSettings, SystemPort};
use slip_codec::Decoder;

pub struct Connection {
serial: Box<dyn SerialPort>,
serial: SystemPort,
decoder: Decoder,
}

Expand All @@ -25,9 +25,9 @@ pub struct CommandResponse {
}

impl Connection {
pub fn new(serial: impl SerialPort + 'static) -> Self {
pub fn new(serial: SystemPort) -> Self {
Connection {
serial: Box::new(serial),
serial,
decoder: Decoder::new(),
}
}
Expand Down Expand Up @@ -99,7 +99,7 @@ impl Connection {
pub fn write_command(
&mut self,
command: u8,
data: impl LazyBytes<Box<dyn SerialPort>>,
data: impl LazyBytes<SystemPort>,
check: u32,
) -> Result<(), Error> {
let mut encoder = SlipEncoder::new(&mut self.serial)?;
Expand All @@ -112,7 +112,7 @@ impl Connection {
Ok(())
}

pub fn command<Data: LazyBytes<Box<dyn SerialPort>>>(
pub fn command<Data: LazyBytes<SystemPort>>(
&mut self,
command: u8,
data: Data,
Expand Down Expand Up @@ -148,6 +148,10 @@ impl Connection {
self.serial.flush()?;
Ok(())
}

pub fn into_serial(self) -> SystemPort {
self.serial
}
}

pub trait LazyBytes<W: Write> {
Expand Down
13 changes: 7 additions & 6 deletions espflash/src/flasher.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bytemuck::{__core::time::Duration, bytes_of, Pod, Zeroable};
use serial::{BaudRate, SerialPort};
use serial::{BaudRate, SystemPort};
use strum_macros::Display;

use std::thread::sleep;
Expand All @@ -11,7 +11,7 @@ use crate::{
Error, PartitionTable,
};

pub(crate) type Encoder<'a> = SlipEncoder<'a, Box<dyn SerialPort>>;
pub(crate) type Encoder<'a> = SlipEncoder<'a, SystemPort>;

pub(crate) const FLASH_SECTOR_SIZE: usize = 0x1000;
const FLASH_BLOCK_SIZE: usize = 0x100;
Expand Down Expand Up @@ -205,10 +205,7 @@ pub struct Flasher {
}

impl Flasher {
pub fn connect(
serial: impl SerialPort + 'static,
speed: Option<BaudRate>,
) -> Result<Self, Error> {
pub fn connect(serial: SystemPort, speed: Option<BaudRate>) -> Result<Self, Error> {
let mut flasher = Flasher {
connection: Connection::new(serial), // default baud is always 115200
chip: Chip::Esp8266, // dummy, set properly later
Expand Down Expand Up @@ -537,6 +534,10 @@ impl Flasher {
self.connection.flush()?;
Ok(())
}

pub fn into_serial(self) -> SystemPort {
self.connection.into_serial()
}
}

pub(crate) fn get_erase_size(offset: usize, size: usize) -> usize {
Expand Down