| Crates.io | at-parser-rs |
| lib.rs | at-parser-rs |
| version | 0.1.6 |
| created_at | 2025-12-30 10:19:04.329206+00 |
| updated_at | 2026-01-21 10:53:15.275007+00 |
| description | A flexible AT command parser for embedded systems and communication devices with no_std support |
| homepage | https://github.com/HiHappyGarden/at-parser-rs |
| repository | https://github.com/HiHappyGarden/at-parser-rs.git |
| max_upload_size | |
| id | 2012427 |
| size | 76,291 |
A lightweight, no_std AT command parser library for embedded Rust applications.
AT-Parser-RS provides a flexible framework for implementing AT command interfaces in embedded systems. It supports the standard AT command syntax including execution, query, test, and set operations.
no_std compatible - suitable for bare-metal and embedded environmentsAT+CMD - Execute commandAT+CMD? - Query current valueAT+CMD=? - Test supported valuesAT+CMD=<args> - Set new value(s)The parser supports four standard AT command forms:
| Form | Syntax | Purpose | Example |
|---|---|---|---|
| Execute | AT+CMD |
Execute an action | AT+RST |
| Query | AT+CMD? |
Get current setting | AT+ECHO? |
| Test | AT+CMD=? |
Get supported values | AT+ECHO=? |
| Set | AT+CMD=<args> |
Set new value(s) | AT+ECHO=1 |
AtContext TraitThe main trait for implementing command handlers. Override only the methods your command needs to support:
pub trait AtContext {
fn exec(&self) -> AtResult<'static>;
fn query(&mut self) -> AtResult<'static>;
fn test(&mut self) -> AtResult<'static>;
fn set(&mut self, args: Args) -> AtResult<'static>;
}
All methods return NotSupported by default.
AtResult and AtErrorpub type AtResult<'a> = Result<&'a str, AtError>;
pub enum AtError {
UnknownCommand, // Command not found
NotSupported, // Operation not implemented
InvalidArgs, // Invalid argument(s)
}
Args StructureProvides access to comma-separated arguments:
pub struct Args<'a> {
pub raw: &'a str,
}
impl<'a> Args<'a> {
pub fn get(&self, index: usize) -> Option<&'a str>;
}
Implement the AtContext trait for your command handlers:
use at_parser_rs::context::AtContext;
use at_parser_rs::{AtResult, AtError, Args};
/// Echo command - returns/sets echo state
pub struct EchoModule {
pub echo: bool,
}
impl AtContext for EchoModule {
// Execute: return current echo state
fn exec(&self) -> AtResult<'static> {
if self.echo {
Ok("ECHO: ON")
} else {
Ok("ECHO: OFF")
}
}
// Query: return current echo value
fn query(&mut self) -> AtResult<'static> {
if self.echo { Ok("1") } else { Ok("0") }
}
// Set: enable/disable echo
fn set(&mut self, args: Args) -> AtResult<'static> {
let v = args.get(0).ok_or(AtError::InvalidArgs)?;
match v {
"0" => {
self.echo = false;
Ok("ECHO OFF")
}
"1" => {
self.echo = true;
Ok("ECHO ON")
}
_ => Err(AtError::InvalidArgs),
}
}
// Test: show valid values and usage
fn test(&mut self) -> AtResult<'static> {
Ok("Valid values: 0 (OFF), 1 (ON)")
}
}
/// Reset command - executes system reset
pub struct ResetModule;
impl AtContext for ResetModule {
fn exec(&self) -> AtResult<'static> {
// Trigger hardware reset
// reset_system();
Ok("OK - System reset")
}
fn test(&mut self) -> AtResult<'static> {
Ok("Reset the system")
}
}
For standard applications, create instances on the stack:
let mut echo = EchoModule { echo: false };
let mut reset = ResetModule;
For embedded/no_std environments with static mut (single-threaded only):
static mut ECHO: EchoModule = EchoModule { echo: false };
static mut RESET: ResetModule = ResetModule;
Note:
static mutrequiresunsafeblocks and is only safe in single-threaded contexts. For RTOS or multi-threaded applications, use proper synchronization primitives.
use at_parser_rs::parser::AtParser;
let mut parser = AtParser::new();
let commands: &mut [(&str, &mut dyn AtContext)] = &mut [
("AT+ECHO", &mut echo),
("AT+RST", &mut reset),
];
parser.set_commands(commands);
// Execute: show current state
match parser.execute("AT+ECHO") {
Ok(response) => println!("Response: {}", response), // "ECHO: OFF"
Err(e) => println!("Error: {:?}", e),
}
// Test: show valid values
match parser.execute("AT+ECHO=?") {
Ok(response) => println!("Valid: {}", response), // "Valid values: 0 (OFF), 1 (ON)"
Err(e) => println!("Error: {:?}", e),
}
// Set: enable echo
match parser.execute("AT+ECHO=1") {
Ok(response) => println!("Response: {}", response), // "ECHO ON"
Err(e) => println!("Error: {:?}", e),
}
// Query: get current value
match parser.execute("AT+ECHO?") {
Ok(response) => println!("Echo: {}", response), // "1"
Err(e) => println!("Error: {:?}", e),
}
// Execute reset
match parser.execute("AT+RST") {
Ok(response) => println!("Response: {}", response), // "OK - System reset"
Err(e) => println!("Error: {:?}", e),
}
// Unknown command
match parser.execute("AT+UNKNOWN") {
Ok(_) => {},
Err(AtError::UnknownCommand) => println!("Command not found"),
}
pub struct UartModule {
pub baudrate: u32,
pub data_bits: u8,
}
impl AtContext for UartModule {
// Query: return current configuration
fn query(&mut self) -> AtResult<'static> {
// In real code, format to a static buffer
Ok("115200,8")
}
// Set: configure UART
fn set(&mut self, args: Args) -> AtResult<'static> {
let baudrate = args.get(0)
.ok_or(AtError::InvalidArgs)?
.parse::<u32>()
.map_err(|_| AtError::InvalidArgs)?;
let data_bits = args.get(1)
.ok_or(AtError::InvalidArgs)?
.parse::<u8>()
.map_err(|_| AtError::InvalidArgs)?;
if ![7, 8].contains(&data_bits) {
return Err(AtError::InvalidArgs);
}
self.baudrate = baudrate;
self.data_bits = data_bits;
// Apply configuration to hardware
// configure_uart(baudrate, data_bits);
Ok("OK")
}
// Test: show valid configurations and usage
fn test(&mut self) -> AtResult<'static> {
Ok("AT+UART=<baudrate>,<data_bits> where baudrate: 9600-115200, data_bits: 7|8")
}
}
Usage:
parser.execute("AT+UART=?"); // "AT+UART=<baudrate>,<data_bits> where..."
parser.execute("AT+UART=115200,8"); // "OK"
parser.execute("AT+UART?"); // "115200,8"
The Args structure provides a simple interface for accessing comma-separated arguments:
fn set(&mut self, args: Args) -> AtResult<'static> {
let arg0 = args.get(0).ok_or(AtError::InvalidArgs)?;
let arg1 = args.get(1).ok_or(AtError::InvalidArgs)?;
let arg2 = args.get(2); // Optional argument
// Process arguments...
Ok("OK")
}
For numeric arguments:
let value = args.get(0)
.ok_or(AtError::InvalidArgs)?
.parse::<i32>()
.map_err(|_| AtError::InvalidArgs)?;
static mut MODULE: MyModule = MyModule::new();
// Safe in single-threaded context
use core::cell::RefCell;
use osal_rs::sync::Mutex;
static MODULE: Mutex<RefCell<MyModule>> = Mutex::new(RefCell::new(MyModule::new()));
&'static str when possible to avoid allocationsAtError variants for different failure modestest() to provide clear usage informationThe library includes several example files demonstrating different usage patterns:
complete_usage.rs - Complete demonstration with multiple command types (Echo, Reset, Info, LED)basic_parser.rs - Shows direct usage of the AtParser with comprehensive test casesembedded_basic.rs - Basic patterns and error handling for no_std/embedded environmentsembedded_error_handling.rs - Advanced patterns with custom error handling and macrosembedded_uart_config.rs - UART and device configuration with AtContext implementationRun examples with:
# Standard examples
cargo run --example complete_usage
cargo run --example basic_parser
# Embedded examples (no_std)
cargo run --example embedded_basic --no-default-features
cargo run --example embedded_error_handling --no-default-features
cargo run --example embedded_uart_config --no-default-features
This project is licensed under the same terms as the parent project.