Add text input

This commit is contained in:
Sofia 2026-06-01 21:17:06 +03:00
parent da2eb1baad
commit b6517d2ee6
4 changed files with 134 additions and 45 deletions

View File

@ -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<Mutex<RefCell<Option<Box<dyn SimpleATCommand>>>>>,
@ -240,3 +232,104 @@ impl NumberInput {
&self.written
}
}
impl KeypadButton {
pub fn get_chars(&self) -> Option<Vec<char>> {
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<LastPressed>,
}
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
}
}

View File

@ -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> {

View File

@ -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);
}
}

View File

@ -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,13 +307,12 @@ 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<Box<dyn State>> {
self.input.poll(&mut data.io);
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(),
@ -339,6 +338,9 @@ impl State for MessageState {
} else {
None
}
} else {
None
}
}
fn draw(&self, data: &mut StateData) {