Files
GemBluetoothEmbeddedDevice/gem-remotes-esp32/src/test_console.rs
2024-08-18 10:32:39 -04:00

232 lines
8.0 KiB
Rust

// === Constants ==============================================================
const MAX_COMMAND_SIZE: usize = 64; // in bytes; we want to test wifi ssid which
// can be 32 bytes.
const MAX_HISTORY_SIZE: usize = 128; // in bytes
const SLEEP_TIME_MS: u64 = 10; // should be > 1/CONFIG_FREERTOS_HZ in sdkconfig
// currently this is 1ms, which is also about as
// long as it would take uart buffers to overflow
// at maximum rate. But this is intended for
// human input only. It's noticeably laggy at 50.
// ============================================================================
use ::{
anyhow::Result,
embedded_cli::{
cli::{CliBuilder, CliHandle},
Command,
},
esp_idf_svc::timer::EspTaskTimerService,
std::{
convert::Infallible,
io::{stdin, stdout, Read, Write},
//time::Duration, // could also use core::time::Duration?
},
core::time::Duration,
};
use crate::commands::Commands;
use async_channel::Sender;
use log::*; //{trace, debug, info, warn, error}
#[derive(Command)]
pub enum Menu<'a> {
/// Simulate the PIC controller sending aus n Up character
PicRecvUp,
/// Simulate the PIC controller sending us a Down character
PicRecvDown,
/// Simulate the PIC controller sending us a Stop character
PicRecvStop,
/// Send a bluetooth characteristic: Up
BluetoothUp {
/// 0 for not pressed, 1 for pressed
data: u8,
},
/// Send a bluetooth characteristic: Down
BluetoothDown {
/// 0 for not pressed, 1 for pressed
data: u8,
},
/// Send a bluetooth characteristic: Stop
BluetoothStop {
/// 0 for not pressed, 1 for pressed
data: u8,
},
/// Send a bluetooth characteristic: Learn
BluetoothLearn {
/// 0 for not pressed, 1 for pressed
data: u8,
},
/// Send a bluetooth characteristic: Auto
BluetoothAuto {
/// 0 for not pressed, 1 for pressed
data: u8,
},
/// Send a bluetooth characteristic: Limits
BluetoothTopLimit { data: u8 },
/// Send a bluetooth characteristic: Limits
BluetoothBottomLimit { data: u8 },
/// Send a bluetooth characteristic: Wifi SSID
BluetoothWifiSsid { ssid: &'a str },
/// Send a bluetooth characteristic: Wifi Password
BluetoothWifiPassword { wifipass: &'a str },
Log { level: u8 },
}
pub fn process_menu(
cli: &mut CliHandle<'_, SimpleWriter, Infallible>,
command: Menu<'_>,
dispatch: &Sender<Commands>,
) -> Result<(), Infallible> {
match command {
// We ignore sending errors throughout because the Cli interface is only for
// testing and debugging.
Menu::PicRecvUp => {
cli.writer().write_str("Sending PicButtonUp Received command")?;
let _ = dispatch.send_blocking(Commands::PicRecvUp);
}
Menu::PicRecvDown => {
cli.writer().write_str("Sending PicButtonDown command")?;
let _ = dispatch.send_blocking(Commands::PicRecvDown);
}
Menu::PicRecvStop => {
cli.writer().write_str("Sending PicButtonStop command")?;
let _ = dispatch.send_blocking(Commands::PicRecvStop);
}
Menu::BluetoothUp { data } => {
cli.writer()
.write_str("Sending BluetoothUp")?;
let _ = dispatch.send_blocking(Commands::BluetoothUp { data: data });
}
Menu::BluetoothDown { data } => {
cli.writer()
.write_str("Sending BluetoothDown")?;
let _ = dispatch.send_blocking(Commands::BluetoothDown { data: data });
}
Menu::BluetoothStop { data } => {
cli.writer()
.write_str("SendingBluetoothStop")?;
let _ = dispatch.send_blocking(Commands::BluetoothStop { data: data });
}
Menu::BluetoothLearn { data } => {
cli.writer()
.write_str("TODO: simulate bluetooth characteristic change")?;
let _ = data;
}
Menu::BluetoothAuto { data } => {
cli.writer()
.write_str("TODO: simulate bluetooth characteristic change")?;
let _ = data;
}
Menu::BluetoothTopLimit { data } => {
cli.writer()
.write_str("TODO: simulate bluetooth characteristic change")?;
let _ = data;
}
Menu::BluetoothBottomLimit { data } => {
cli.writer()
.write_str("TODO: simulate bluetooth characteristic change")?;
let _ = data;
}
Menu::BluetoothWifiSsid { ssid } => {
cli.writer()
.write_str("TODO: simulate bluetooth characteristic change")?;
let _ = ssid;
}
Menu::BluetoothWifiPassword { wifipass } => {
cli.writer()
.write_str("TODO: simulate bluetooth characteristic change {}")?;
let _ = wifipass;
}
Menu::Log { level } => {
match level {
0 => {
cli.writer().write_str("Log set to Off")?;
log::set_max_level(log::LevelFilter::Off);
}
1 => {
cli.writer().write_str("Log set to Error")?;
log::set_max_level(log::LevelFilter::Error);
}
2 => {
cli.writer().write_str("Log set to Warn")?;
log::set_max_level(log::LevelFilter::Warn);
}
3 => {
cli.writer().write_str("Log set to Info")?;
log::set_max_level(log::LevelFilter::Info);
}
4 => {
cli.writer().write_str("Log set to Debug")?;
log::set_max_level(log::LevelFilter::Debug);
}
_ => {
cli.writer().write_str("Log set to Trace")?;
log::set_max_level(log::LevelFilter::Trace);
}
}
error!("error test");
warn!("warn test");
info!("info test");
debug!("debug test");
trace!("trace test");
}
}
Ok(())
}
// === SimpleWriter ===========================================================
pub struct SimpleWriter {}
impl embedded_io::ErrorType for SimpleWriter {
type Error = Infallible;
}
impl embedded_io::Write for SimpleWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
stdout().write(&buf).unwrap();
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
let _ = stdout().flush();
Ok(())
}
}
pub async fn start_cli(dispatch: Sender<Commands>) -> anyhow::Result<()> {
let timer_service = EspTaskTimerService::new()?;
info!("Setting up command line listener and processor");
let writer = SimpleWriter {};
let mut cli = CliBuilder::default()
.writer(writer)
.command_buffer([0_u8; MAX_COMMAND_SIZE])
.history_buffer([0_u8; MAX_HISTORY_SIZE])
.prompt("$> ")
.build()?;
let mut reader = stdin();
let mut buf = [0_u8; 1];
let mut async_timer = timer_service.timer_async()?;
loop {
async_timer.after(Duration::from_millis(SLEEP_TIME_MS)).await?;
match reader.read_exact(&mut buf) {
Ok(_) => {
//cli.process_byte::<Menu, _>(buf[0], &mut Menu::processor(process_menu))?;
cli.process_byte::<Menu, _>(buf[0], &mut Menu::processor(
|cli, command| {
process_menu(cli, command, &dispatch)
}))?;
}
Err(e) => match e.kind() {
std::io::ErrorKind::WouldBlock => {} // This is expected if there is no input
_ => {warn!("Error waiting for input: {}", e)}
}
};
}
}