simx00x 0.1.0

A no-std and no-alloc driver for SIM800L GSM modules (and probably similar modules)
Documentation
use std::{cell::Cell, collections::VecDeque, time::Instant};

fn reset_sim() -> bool {
    // * Reset the hardware, i.e., put the DTR pin low and then high again. *

    println!("Module stalled. Resetting...");

    // Auto-reset the software module
    true
}

fn main() {
    // Fake UART, just to make the example run.
    // For ESP this would be something like:
    // ```
    // let uart = esp_hal::uart::Uart::new(
    //    peripherals.UART2,
    //    esp_hal::uart::Config::default().with_baudrate(9600),
    // ).unwrap();
    // ```
    let uart = VecDeque::from([0]);

    let mut new_msg_callback = |sender: &str, msg: &str| {
        println!("New message from {sender}: {msg}");
    };

    // Using a cell for data shared between a callback and the main loop
    // solves ownership problems.
    let call_elapsed_time = Cell::new(None);
    let mut incoming_call_callback = |caller: &str| {
        if caller == "+123456789" {
            // Do something...
            call_elapsed_time.set(Some(Instant::now()));
            return true; // Accept call
        }
        false // Refuse call
    };

    let mut module_powered_down_cb = || {};

    // Create a `Config` object, with one of the following methods.
    // In order to use normal functions as callbacks the second one has to be used.

    let _cfg = simx00x::Config::default()
        .with_sim_card_pin("1234")
        // .with_module_stalled_callback(&mut reset_sim) // Normal functions cannot be used with this syntax
        .with_module_powered_down_callback(&mut module_powered_down_cb)
        .with_received_sms_callback(&mut new_msg_callback)
        .with_received_call_callback(&mut incoming_call_callback);

    let cfg = simx00x::Config {
        sim_card_pin: Some("1234"),
        auto_reset_when_stalled: true,
        module_stalled_callback: Some(&mut reset_sim),
        module_powered_down_callback: Some(&mut module_powered_down_cb),
        received_sms_callback: Some(&mut new_msg_callback),
        received_call_callback: Some(&mut incoming_call_callback),
        ..Default::default() // Leave the other callbacks unset
    };

    // Create the module object
    let mut sim = simx00x::SimX00X::new(uart, cfg);

    // Send USSD. The received response will be eventually available in the related callback.
    let _ = sim.send_ussd("*123#");

    let mut last_updated = Instant::now();
    let start = Instant::now();
    loop {
        // Ideally the loop should be infinite, but in this example let's stop it after 5 seconds...
        if start.elapsed().as_secs() >= 5 {
            break;
        }

        // Run the `update` method every 2 seconds.
        if last_updated.elapsed().as_secs() >= 2 {
            let _ = sim.update();
            last_updated = Instant::now();
        } else {
            // Run the `tick` method on every iteration.
            let _ = sim.tick();
        }

        // Something happens -> send SMS.
        let _ = sim.enqueue_sms("+123456789", "This is a test");

        // Something happens -> start call.
        let _ = sim.dial("+123456789");

        // If there is an ongoing call, hang it up after 30 seconds.
        if call_elapsed_time
            .get()
            .is_some_and(|t| t.elapsed().as_secs() > 30)
        {
            call_elapsed_time.set(None);
            sim.hang_up_call();
        }
    }
}