esp-rs / esp-hal

no_std Hardware Abstraction Layers for ESP32 microcontrollers
https://docs.esp-rs.org/esp-hal/
Apache License 2.0
700 stars 191 forks source link

UART AT character detection not working. #1700

Closed 762SPR closed 2 months ago

762SPR commented 2 months ago

The interrupt for AT character detection doesn't ever seem to fire for me.

UART setup:

let config = UartConfig {
        baudrate: 115200,
        data_bits: DataBits::DataBits8,
        parity: Parity::ParityNone,
        stop_bits: StopBits::STOP1,
        clock_source: ClockSource::Apb,
    };

    let mut uart0 = Uart::new_async_with_config(peripherals.UART0, config, Some(uart0_pins), &clocks);

    uart0.set_rx_fifo_full_threshold(UART_READ_BUF_SIZE as u16).unwrap();
    uart0.set_at_cmd(AtCmdConfig::new(None, None, None, 0x04 , None)); // set this to make an interrupt when EOT is received
    let (uart0_tx, uart0_rx) = uart0.split();

Note: I have also tried adding uart0.listen_at_cmd(); and default config with no difference

My reader is as follows:

#[task]
pub async fn from_uart_task(
    mut rx0: UartRx<'static, UART0, Async>, 
    ch_rx: Sender<'static, NoopRawMutex, String, 5>,
){
    let mut rbuf: [u8; UART_READ_BUF_SIZE * 4] = [0u8; UART_READ_BUF_SIZE * 4]; // todo big enough?
    loop{
        println!("Reading...");
        match rx0.read_async(&mut rbuf).await{ //Read::read(&mut rx0, &mut rbuf).await {
            Ok(n) => {
                println!("Read {} bytes from UART!", n);
            },
            Err(e) => println!("Async error: {:?}", e),
        };
    }
}

pretty simple. and the other device sending is:

#[task]
async fn uart_task(mut tx: UartTx<'static, peripherals::USART1, Async>) {
    loop {
        tx.write("testing!\n\x04".as_bytes()).await.ok();
        Timer::after_secs(1).await;
    }
}

I have checked with read_byte() that 0x04 is indeed being sent and received but it never seems to trigger the character detection. I have also tried other characters and nothing seems to work. I am getting buffer full interrupts just fine though. Is there some way I am missing to turn on character detection interrupts or is something broken?

bjoernQ commented 2 months ago

I checked the serial_interrupts and embassy_serial examples on ESP32-C6 and they work fine for me. Which target are you using?

762SPR commented 2 months ago

I am using an 8684 (C3) currently but should be able to test on a full size C3 soon. I may have missed some interrupt enable something somewhere but the examples don't look particularly complicated, although slightly outdated. I can dig a little more today but will likely just be working on my own manual implementation to keep things rolling on the project. Let me know if there is something I can do to help!

SergioGasquez commented 2 months ago

but should be able to test on a full size C3 soon

Out of curiosity, have you been able to test on a C3?

762SPR commented 2 months ago

No, I ended up just setting the fifo buffer size to 1 and just using the interrupt from that and checking each character so I could keep the project developing. I will have to extend the uart parsing later so I will try to remember to try the interrupt on the new boards.

SergioGasquez commented 2 months ago

Do you mind trying with main? You can patch esp-hal by adding in your Cargo.toml:

[patch.crates-io]
esp-hal ={ git = "https://github.com/esp-rs/esp-hal"}

You would also require some changes when initializing the uart:

    let (tx_pin, rx_pin) = (io.pins.gpio3, io.pins.gpio4);

    let config = Config {
        baudrate: 115200,
        data_bits: DataBits::DataBits8,
        parity: Parity::ParityNone,
        stop_bits: StopBits::STOP1,
        clock_source: ClockSource::Apb,
        rx_fifo_full_threshold: READ_BUF_SIZE as u16,
        rx_timeout: 10,
    };

    let mut uart0 =
        Uart::new_async_with_config(peripherals.UART0, config, &clocks, tx_pin, rx_pin).unwrap();

    uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); 

    let (uart0_tx, uart0_rx) = uart0.split();
bjoernQ commented 2 months ago

This works

//! connect GPIO 2 -> GPIO 4

#![no_std]
#![no_main]

//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
//% FEATURES: async embassy embassy-time-timg0 embassy-generic-timers

use embassy_executor::Spawner;
use embassy_time::Timer;
use esp_backtrace as _;
use esp_hal::{
    clock::ClockControl,
    gpio::Io,
    peripherals::{Peripherals, UART1},
    prelude::*,
    system::SystemControl,
    timer::timg::TimerGroup,
    uart::{
        config::{AtCmdConfig, Config},
        Uart,
        UartRx,
        UartTx,
    },
    Async,
};
use esp_println::println;

// rx_fifo_full_threshold
const READ_BUF_SIZE: usize = 64;
// EOT (CTRL-D)
const AT_CMD: u8 = 0x04;

#[embassy_executor::task]
async fn writer(mut tx: UartTx<'static, UART1, Async>) {
    loop {
        tx.write_async("testing!\x04".as_bytes()).await.ok();
        Timer::after_secs(1).await;
    }
}

#[embassy_executor::task]
async fn reader(mut rx: UartRx<'static, UART1, Async>) {
    const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16;

    let mut rbuf: [u8; MAX_BUFFER_SIZE] = [0u8; MAX_BUFFER_SIZE];
    let mut offset = 0;
    loop {
        println!("reading");
        let r = rx.read_async(&mut rbuf[offset..]).await;
        match r {
            Ok(len) => {
                offset += len;
                println!("Read: {len}, data: {:?}", &rbuf[..offset]);
                offset = 0;
            }
            Err(e) => println!("RX Error: {:?}", e),
        }
    }
}

#[main]
async fn main(spawner: Spawner) {
    esp_println::logger::init_logger_from_env();
    println!("Init!");
    let peripherals = Peripherals::take();
    let system = SystemControl::new(peripherals.SYSTEM);
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks);
    esp_hal_embassy::init(&clocks, timg0);

    let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);

    let (tx_pin, rx_pin) = (io.pins.gpio2, io.pins.gpio4);

    let config = Config::default().rx_fifo_full_threshold(READ_BUF_SIZE as u16);

    let mut uart0 =
        Uart::new_async_with_config(peripherals.UART1, config, &clocks, tx_pin, rx_pin).unwrap();
    uart0.set_at_cmd(AtCmdConfig::new(Some(0), Some(0), None, AT_CMD, None));

    let (tx, rx) = uart0.split();

    spawner.spawn(reader(rx)).ok();
    spawner.spawn(writer(tx)).ok();
}

But it's a bit pointless since currently we cannot disable rx_timeout - so even without AT-CMD detection the outcome will be the same since the timeout triggers the interrupt. Probably we should change rx_timeout to be Option in the config struct

jessebraham commented 2 months ago

I believe with #1759 merged this can be closed, however if I am mistaken please feel free to re-open the issue.