From b6517d2ee6f73d7e9f79be34e577407cc22423af Mon Sep 17 00:00:00 2001 From: Sofia Date: Mon, 1 Jun 2026 21:17:06 +0300 Subject: [PATCH] Add text input --- src/async_io.rs | 119 ++++++++++++++++++++++++++++++++++++++++----- src/at_commands.rs | 2 - src/main.rs | 4 -- src/states.rs | 54 ++++++++++---------- 4 files changed, 134 insertions(+), 45 deletions(-) diff --git a/src/async_io.rs b/src/async_io.rs index e3c0a2b..18d90b2 100644 --- a/src/async_io.rs +++ b/src/async_io.rs @@ -1,5 +1,6 @@ use core::{cell::RefCell, char, marker::PhantomData}; +use alloc::vec; use alloc::{ boxed::Box, collections::{btree_map::BTreeMap, btree_set::BTreeSet}, @@ -8,7 +9,6 @@ use alloc::{ vec::Vec, }; use critical_section::Mutex; -use esp_hal::delay::Delay; use crate::at_commands::{ATCommand, ATParseError, ATResponseParser, SimpleATCommand}; @@ -54,16 +54,19 @@ impl Keypad { critical_section::with(|cs| { let mut was_pressed = self.was_pressed.borrow_ref_mut(cs); let mut is_pressed = self.is_pressed.borrow_ref_mut(cs); - let mut num_presses = self.presses.borrow_ref_mut(cs); + let mut num_presses = self.presses.borrow_ref(cs).clone(); *was_pressed = is_pressed.clone(); *is_pressed = presses; for button in is_pressed.iter() { if !was_pressed.contains(button) { let num = num_presses.get(button).copied().unwrap_or(0); + log::info!("{:?}", *button); num_presses.insert(*button, num + 1); } } + + *self.presses.borrow_ref_mut(cs) = num_presses; }); } @@ -103,17 +106,6 @@ impl Keypad { } } -#[derive(Debug, Clone)] -pub enum ConstructedATCommand { - /// Single-part command - Single(String), - /// Two-part AT-command - AddInfo(String, String), -} - -unsafe impl Send for ConstructedATCommand {} -unsafe impl Sync for ConstructedATCommand {} - #[derive(Clone)] pub struct AsyncIO { at_command: Rc>>>>, @@ -240,3 +232,104 @@ impl NumberInput { &self.written } } + +impl KeypadButton { + pub fn get_chars(&self) -> Option> { + match self { + KeypadButton::Keypad1 => Some(vec![' ', '1']), + KeypadButton::Keypad2 => Some(vec!['a', 'b', 'c', '2']), + KeypadButton::Keypad3 => Some(vec!['d', 'e', 'f', '3']), + KeypadButton::Keypad4 => Some(vec!['g', 'h', 'i', '4']), + KeypadButton::Keypad5 => Some(vec!['j', 'k', 'l', '5']), + KeypadButton::Keypad6 => Some(vec!['m', 'n', 'o', '6']), + KeypadButton::Keypad7 => Some(vec!['p', 'q', 'r', 's', '7']), + KeypadButton::Keypad8 => Some(vec!['t', 'u', 'v', '8']), + KeypadButton::Keypad9 => Some(vec!['w', 'x', 'y', 'z', '9']), + KeypadButton::Keypad0 => Some(vec![' ', '0']), + KeypadButton::KeypadStar => Some(vec!['*']), + KeypadButton::KeypadHash => Some(vec!['#']), + _ => None, + } + } +} + +#[derive(Debug, Clone)] +pub struct LastPressed { + key: KeypadButton, + idx: usize, +} + +impl LastPressed { + pub fn increment(&mut self) { + let chars = self.key.get_chars(); + if let Some(chars) = chars { + self.idx = (self.idx + 1) % chars.len() + } + } + + pub fn get_char(&self) -> char { + self.key.get_chars().unwrap()[self.idx] + } +} + +static TEXT_KEYS: [KeypadButton; 13] = [ + KeypadButton::Keypad0, + KeypadButton::Keypad1, + KeypadButton::Keypad2, + KeypadButton::Keypad3, + KeypadButton::Keypad4, + KeypadButton::Keypad5, + KeypadButton::Keypad6, + KeypadButton::Keypad7, + KeypadButton::Keypad8, + KeypadButton::Keypad9, + KeypadButton::Keypad0, + KeypadButton::KeypadStar, + KeypadButton::KeypadHash, +]; + +#[derive(Debug, Default, Clone)] +pub struct TextInput { + written: String, + last_pressed: Option, +} + +impl TextInput { + pub fn poll(&mut self, io: &mut AsyncIO) -> bool { + let mut wrote = false; + for button in io.keypad.just_pressed_buttons() { + if let Some(mut last_pressed) = self.last_pressed.take() { + if last_pressed.key == button { + last_pressed.increment(); + self.last_pressed = Some(last_pressed); + wrote = true; + } else if TEXT_KEYS.contains(&button) { + self.written += &last_pressed.get_char().to_string(); + self.last_pressed = Some(LastPressed { + key: button, + idx: 0, + }); + wrote = true; + } else if button == KeypadButton::KeypadA { + self.written += &last_pressed.get_char().to_string(); + wrote = true; + } + } else if TEXT_KEYS.contains(&button) { + self.last_pressed = Some(LastPressed { + key: button, + idx: 0, + }); + wrote = true; + } + } + wrote + } + + pub fn read(&self) -> String { + let mut written = self.written.clone(); + if let Some(last_pressed) = &self.last_pressed { + written += &last_pressed.get_char().to_string(); + } + written + } +} diff --git a/src/at_commands.rs b/src/at_commands.rs index ccf1ea5..0e40454 100644 --- a/src/at_commands.rs +++ b/src/at_commands.rs @@ -4,8 +4,6 @@ use alloc::{borrow::ToOwned, format}; use core::fmt::Write; use esp_hal::{Blocking, delay::Delay, gpio::Output, uart::Uart}; -use crate::async_io::ConstructedATCommand; - static RESPONSES: [&'static str; 3] = ["OK", "ERROR", "DOWNLOAD"]; pub struct ATCommands<'a, 'd> { diff --git a/src/main.rs b/src/main.rs index ee9b505..d62f670 100644 --- a/src/main.rs +++ b/src/main.rs @@ -256,10 +256,6 @@ fn thread_2_main( delay.delay_millis(5); } - if buttons_pressed.len() > 0 { - log::info!("{:?}", buttons_pressed) - } - async_io.keypad.handle_presses(buttons_pressed); } } diff --git a/src/states.rs b/src/states.rs index edf2643..0c23d39 100644 --- a/src/states.rs +++ b/src/states.rs @@ -9,7 +9,7 @@ use alloc::{ use esp_hal::time::{Duration, Instant}; use crate::{ - async_io::{ATPromise, KeypadButton, NumberInput}, + async_io::{ATPromise, KeypadButton, NumberInput, TextInput}, at_commands::{ ATCommand, ATInformationCommand, ATParseError, CheckPinCommand, CheckPinResult, EnterPinCommand, EnterPinResult, SMSFormat, SelectSMSFormatCommand, SendSMSCommand, @@ -307,35 +307,37 @@ impl State for PhoneNumberState { pub struct MessageState { number: String, - input: NumberInput, + input: TextInput, } impl State for MessageState { fn update(&mut self, data: &mut StateData) -> Option> { - self.input.poll(&mut data.io); - - if data.io.keypad.get_presses(KeypadButton::KeypadA) > 0 { - Some(Box::new(ATCommandState::with( - "Sending SMS..".to_owned(), - SendSMSCommand { - destination: self.number.clone(), - message: self.input.read().clone(), - }, - |resp| match resp.unwrap() { - SendSMSResponse::MessageRefrence(refrence) => Box::new(TextState { - text: format!("SMS sent\nRef: {}", refrence), - after: PhoneNumberState::default(), - }), - SendSMSResponse::Error => Box::new(TextState { - text: "ERROR!".to_owned(), - after: PhoneNumberState::default(), - }), - SendSMSResponse::ErrorMessage(msg) => Box::new(TextState { - text: format!("Error:\n{}", msg), - after: PhoneNumberState::default(), - }), - }, - ))) + if !self.input.poll(&mut data.io) { + if data.io.keypad.get_presses(KeypadButton::KeypadA) > 0 { + Some(Box::new(ATCommandState::with( + "Sending SMS..".to_owned(), + SendSMSCommand { + destination: self.number.clone(), + message: self.input.read().clone(), + }, + |resp| match resp.unwrap() { + SendSMSResponse::MessageRefrence(refrence) => Box::new(TextState { + text: format!("SMS sent\nRef: {}", refrence), + after: PhoneNumberState::default(), + }), + SendSMSResponse::Error => Box::new(TextState { + text: "ERROR!".to_owned(), + after: PhoneNumberState::default(), + }), + SendSMSResponse::ErrorMessage(msg) => Box::new(TextState { + text: format!("Error:\n{}", msg), + after: PhoneNumberState::default(), + }), + }, + ))) + } else { + None + } } else { None }