A src/commands.rs => src/commands.rs +6 -0
@@ 0,0 1,6 @@
+pub mod hello_world_command;
+pub mod command_handler;
+pub mod command;
+pub mod command_argument;
+pub mod set_command;
+pub mod reset_command;<
\ No newline at end of file
A src/commands/command.rs => src/commands/command.rs +7 -0
@@ 0,0 1,7 @@
+use crate::commands::command_argument::CommandArgument;
+
+pub struct Command<'d>
+{
+ pub full: &'d [char],
+ pub parsed_arguments: &'d [CommandArgument<'d>],
+}<
\ No newline at end of file
A src/commands/command_argument.rs => src/commands/command_argument.rs +62 -0
@@ 0,0 1,62 @@
+use esp_println::{print, println};
+
+#[derive(Clone, Copy, Eq, PartialEq)]
+pub struct CommandArgument<'d>
+{
+ pub data: &'d [char],
+}
+
+impl<'d> CommandArgument<'d>
+{
+ pub fn new(data: &'d [char]) -> Self
+ {
+ Self {
+ data
+ }
+ }
+
+ pub fn chars(&self) -> &'d [char]
+ {
+ self.data
+ }
+
+ pub fn try_to_integer(&self) -> Option<u32>
+ {
+ let length = self.data.len();
+ let mut multiplier = 1u32;
+ for _ in 0..length - 1 {
+ multiplier *= 10;
+ }
+
+ let mut result = 0u32;
+
+ for c in self.chars().iter() {
+ let num = (*c as u32) - (b'0' as u32);
+ if num > 9 {
+ return None;
+ }
+
+ result += num * multiplier;
+ multiplier /= 10;
+ }
+
+ Some(result)
+ }
+
+ pub fn compare(&self, to: &str) -> bool {
+ if self.data.len() != to.len() {
+ return false;
+ }
+
+ let to = to.as_bytes();
+ for (i, c) in self.data.iter().enumerate() {
+ let compare_against = to[i];
+
+ if compare_against != (*c) as u8 {
+ return false;
+ }
+ }
+
+ true
+ }
+}<
\ No newline at end of file
A src/commands/command_handler.rs => src/commands/command_handler.rs +230 -0
@@ 0,0 1,230 @@
+use core::slice::IterMut;
+use embedded_hal::serial::{Read, Write};
+use esp_println::{print, println};
+use nb::block;
+use nb::Error::{Other, WouldBlock};
+use crate::command_handler::CommandHandleError::{CommandNotRead, NotFound};
+use crate::command_handler::CommandReadError::{BufferOverflowed, CommandLoadedAlready, UnexpectedEndOfLine};
+use crate::commands::command::Command;
+use crate::commands::command_argument::CommandArgument;
+use crate::map::Map;
+
+pub trait SpecificCommandHandler {
+ fn handle(&self, command: &mut CommandData) -> Result<(), CommandHandleError>;
+ fn help(&self) -> &'static str;
+}
+
+pub struct CommandData<'d, 'a> {
+ pub command: Command<'d>,
+ pub map: &'d mut Map<'a>,
+}
+
+pub struct CommandHandler<'d, const BUFFER_SIZE: usize, const HANDLERS_COUNT: usize> {
+ buffer_position: usize,
+ command_loaded: bool,
+ buffer: [char; BUFFER_SIZE],
+ handlers: [(&'d str, &'d dyn SpecificCommandHandler); HANDLERS_COUNT],
+ handling_special: u8
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum CommandReadError {
+ UnexpectedEndOfLine,
+ BufferOverflowed,
+ CommandLoadedAlready,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum CommandHandleError {
+ NotFound,
+ WrongArguments,
+ CommandNotRead,
+}
+
+impl<'d, const BUFFER_SIZE: usize, const HANDLERS_COUNT: usize> CommandHandler<'d, BUFFER_SIZE, HANDLERS_COUNT> {
+ pub fn new(handlers: [(&'d str, &'d dyn SpecificCommandHandler); HANDLERS_COUNT], buffer: [char; BUFFER_SIZE]) -> Self
+ {
+ Self {
+ command_loaded: false,
+ handling_special: 0,
+ buffer_position: 0,
+ buffer,
+ handlers,
+ }
+ }
+
+ pub fn reset(&mut self) -> ()
+ {
+ self.buffer_position = 0;
+ self.command_loaded = false;
+ self.handling_special = 0;
+ }
+
+ fn handle_special<Serial>(&mut self, serial: &mut Serial, data: u8) -> bool
+ where Serial: Read<u8> + Write<u8>
+ {
+ if self.handling_special > 0 { // some special characters have char length 2.
+ self.handling_special -= 1;
+ return false;
+ }
+
+ match data {
+ b'\x08' => { // backspace
+ if self.buffer_position > 0 {
+ self.buffer_position -= 1;
+ block!(serial.write(b'\x08')).ok().unwrap();
+ block!(serial.write(b' ')).ok().unwrap();
+ block!(serial.write(b'\x08')).ok().unwrap();
+ }
+ false
+ },
+ b'\x1b' => { // other special characters
+ self.handling_special = 2;
+ false
+ },
+ _ => true
+ }
+ }
+
+ pub fn read_command<Serial>(&mut self, serial: &mut Serial) -> nb::Result<(), CommandReadError>
+ where Serial: Read<u8> + Write<u8>
+ {
+ if self.command_loaded {
+ return Err(Other(CommandLoadedAlready));
+ }
+
+ let read = serial.read();
+
+ if self.buffer_position >= BUFFER_SIZE {
+ return Err(Other(BufferOverflowed));
+ }
+
+ match read {
+ Ok(data) => {
+ if self.handle_special(serial, data) {
+ self.buffer[self.buffer_position] = data as char
+ } else { // special character handled
+ return Err(WouldBlock);
+ }
+ }
+ Err(_) => return Err(WouldBlock)
+ };
+
+ let read = self.buffer[self.buffer_position];
+ self.buffer_position += 1;
+
+ block!(serial.write(read as u8)).ok().unwrap();
+
+ if read != '\r' {
+ return Err(WouldBlock);
+ }
+
+ block!(serial.write(b'\n')).ok().unwrap();
+
+ if self.buffer_position <= 1 {
+ self.reset();
+ return Err(Other(UnexpectedEndOfLine));
+ }
+
+ self.command_loaded = true;
+ Ok(())
+ }
+
+ fn parse_command<'a>(&self, buffer: &'a [char], args: &'a mut [CommandArgument<'a>]) -> Command<'a>
+ {
+ let mut last_arg_index = 0;
+ let mut length = 0;
+ let mut args_iter = args.iter_mut();
+
+ for i in 0..buffer.len() {
+ let argument_length = i - last_arg_index;
+
+ if buffer[i] == ' ' {
+ if argument_length > 0 {
+ *args_iter.next().unwrap() = CommandArgument::new(&buffer[last_arg_index..i]);
+ length += 1;
+ }
+
+ last_arg_index = i + 1;
+ }
+
+ if buffer[i] == '\r' || buffer[i] == '\n' {
+ if argument_length > 0 {
+ *args_iter.next().unwrap() = CommandArgument::new(&buffer[last_arg_index..i]);
+ length += 1;
+ }
+
+ break;
+ }
+ }
+
+ Command {
+ full: buffer,
+ parsed_arguments: &args[0..length],
+ }
+ }
+
+ fn handle_help(&self) -> Result<(), CommandHandleError>
+ {
+ println!("Available commands:\r");
+ for (cmd, handler) in self.handlers {
+ println!(" {0} {1}\r", cmd, handler.help());
+ }
+
+ Ok(())
+ }
+
+ pub fn handle_command(&mut self, map: &mut Map) -> Result<(), CommandHandleError>
+ {
+ if !self.command_loaded {
+ return Err(CommandNotRead);
+ }
+
+ let buffer = &self.buffer[0..self.buffer_position];
+ let mut args = [CommandArgument::new(buffer); BUFFER_SIZE];
+
+ let command = self.parse_command(buffer, &mut args);
+
+ if command.parsed_arguments.len() == 0 {
+ self.reset();
+ return Err(NotFound);
+ }
+
+ let first_argument = command.parsed_arguments[0];
+
+ if first_argument.compare("HELP") {
+ let help_handled = self.handle_help();
+ self.reset();
+ return help_handled;
+ }
+
+ for (handler_command, handler) in self.handlers {
+ if !first_argument.compare(handler_command) {
+ continue;
+ }
+
+ let mut command_data = CommandData {
+ command,
+ map,
+ };
+
+ let handled = handler.handle(&mut command_data);
+ self.reset();
+ return handled;
+ }
+
+ self.reset();
+ Err(NotFound)
+ }
+}
+
+// HELP
+// DEFAULT <R> <G> <B>
+// SET <X:id or name> <R> <G> <B>
+// ALL <R> <G> <B>
+// RESET
+// ITER [X] [Y] (X start, Y end)
+// SNAKE [X] [Y] (X start, Y end)
+// RAINBOW -- show rainbow
+
+// WIFI <SSID> <PASS>
M src/main.rs => src/main.rs +61 -0
@@ 3,6 3,7 @@
mod strip;
mod map;
+mod commands;
use embedded_hal::serial::{Read, Write};
use esp_backtrace as _;
@@ 13,8 14,14 @@ use hal::uart::TxRxPins;
use nb::block;
use nb::Error::{WouldBlock, Other};
use smart_leds::{RGB8, SmartLedsWrite};
+use crate::commands::command_handler::{CommandHandler};
+use crate::commands::command_handler;
+use crate::commands::hello_world_command::HelloWorldCommand;
+use crate::commands::reset_command::ResetCommand;
+use crate::commands::set_command::SetCommand;
use crate::map::Map;
use crate::strip::StripTiming;
+
const LEDS_COUNT: usize = 72;
const COMMAND_BUFFER: usize = 200;
@@ 84,7 91,61 @@ fn main() -> ! {
let mut rgb_data: [RGB8; 72] = [RGB8 { r: 0, g: 0, b: 0 }; 72];
let mut map = map::Map::new(&map::INDEX_MAP, &mut rgb_data);
+ let mut handler = CommandHandler::new(
+ [
+ ],
+ ['\0'; COMMAND_BUFFER],
+ );
+
+ block!(serial.write(b'>')).ok().unwrap();
+ block!(serial.write(b' ')).ok().unwrap();
loop {
+ let handled = match handler.read_command(&mut serial) {
+ Ok(()) => {
+ println!("\r");
+ let result = handler.handle_command(&mut map);
+
+ match result {
+ Ok(()) => Ok(true),
+ Err(err) => Err(err)
+ }
+ }
+ Err(err) => {
+ match err {
+ Other(error) => {
+ match error {
+ command_handler::CommandReadError::BufferOverflowed => println!("Command is too long.\r"),
+ command_handler::CommandReadError::UnexpectedEndOfLine => (),
+ command_handler::CommandReadError::CommandLoadedAlready => println!("FATAL: Previous command not processed correctly.\r")
+ };
+ Ok(true)
+ }
+ _ => Ok(false)
+ }
+ }
+ };
+
+ let new_command = match handled {
+ Ok(handled) => {
+ handled
+ }
+ Err(err) => {
+ match err {
+ command_handler::CommandHandleError::NotFound => println!("Command not found.\r"),
+ command_handler::CommandHandleError::WrongArguments => println!("Wrong arguments.\r"),
+ command_handler::CommandHandleError::CommandNotRead => println!("FATAL: Command is not prepared.\r")
+ }
+ true
+ }
+ };
+
+ if new_command {
+ println!("\r");
+ block!(serial.write(b'>')).ok().unwrap();
+ block!(serial.write(b' ')).ok().unwrap();
+ }
+ strip.write(map.get_map().cloned()).unwrap();
+ delay.delay_us(500u32);
}
}=
\ No newline at end of file