cembeddedavr

Arduino Nano not reading Data from UART Shell properly


Recently I've been getting into bare-metal programming with the Arduino-Nano, and wanted to play around with the UART protocool. I made a simple "shell" using rust on my host machine that when sending the command "ledON" or "ledOFF" It would turn on or off the built in LED on the nano.

The C code on the Nano is as follows

Header file (avr-uart.h):

#include <stdint.h>


// Register Defines

#define UBRR0L      (*(volatile uint8_t *)0xC4)
#define UBRR0H      (*(volatile uint8_t *)0xC5)
#define UCSR0A      (*(volatile uint8_t *)0xC0)
#define UCSR0B      (*(volatile uint8_t *)0xC1)
#define UCSR0C      (*(volatile uint8_t *)0xC2)
#define UDR0        (*(volatile uint8_t *)0xC6)


// Register Bit Defines  
#define RXEN0       5                   // USART Receiver
#define TXEN0       4                   // USART Transmitter 
#define USBS0       3                   // Sets number of stop bits 
#define UCSZ00      1                   // Sets Character Size
#define UDRE0       5
#define RXC0        7

// Constant Defines 
#define CLOCK_SPEED     16000000                // MCU Clock Speed 
#define BAUD        115200                  // BAUD rate 
#define MYUBRR      CLOCK_SPEED / 16 / BAUD - 1         // UART Baud Rate 



/*
 * Function Desc: Function to handle Data buffer transfer
 * @param data  => Buffer holding the data to be transmitted 
*/
void USART_Transmit(unsigned char data) {

    // Wait for empty transmit buffer
    while ( !( UCSR0A & (1 << UDRE0)));

    // Puts data in the buffer and sends data 
    UDR0 = data; 
}




/*
 * Function Desc: Function to handle Data Reception 
*/
volatile uint8_t USART_Recieve(void) {
    
    while ( !( UCSR0A & (1 << RXC0)));

    return UDR0;

}

/*
 * Function Desc: Function to handling Printing strings to UART
 * @param *data => Pointer to data buffer
*/
void USART_Print(const char* data) {

    while (*data) {
        USART_Transmit(*data++);
    }
}

/*
 * Function Desc: Function to handle USART setup for Data Transmission and Reception
 * @param ubrr  => BAUD Rate 
*/
void USART_Init(unsigned int ubrr) {

    // This sets BAUD Rate 
    UBRR0H = (unsigned char) (ubrr >> 8);   // Zeroes out higher bit register (according to datasheet) 
    UBRR0L = (unsigned char) ubrr;      // Sets lower bit register baud rate 


    // Enable Receiver and Transmitter 
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);

    // Sets frame format 
    UCSR0C = (1 << USBS0) | (3 << UCSZ00);

}

Main File:

#include "avr-uart.h"
#include <stdint.h>
#include <string.h>

#define BUFFER_SIZE 50
#define DDRB        (*(volatile uint8_t *)0x24)         // Address of DDRB Register
#define PORTB       (*(volatile uint8_t *)0x25)         // Address of PORTB Register
#define PB5     5                       // PB5 (Port B, bit 5)

int main(void) {

  // Sets the DDRB Register to Output
  DDRB |= (1 << PB5);

  // Initializes UART on the Micro-controller
  USART_Init(MYUBRR);

  char commandBuffer[BUFFER_SIZE];

  uint8_t buffer = 0;

  while (1) {

    // Save the received character in a single char variable
    char received = USART_Recieve();

    // Echo the character
    USART_Transmit(received);

    if (received == '\n' || received == '\r') {

      commandBuffer[buffer] = '\0';

      if (strcmp(commandBuffer, "ledON") == 0) {

        PORTB |= (1 << PB5); // Turns on the LED
        USART_Print("\r\n[NANO] INTERACTED WITH LED\r\n");
      }

      if (strcmp(commandBuffer, "ledOFF") == 0) {

        PORTB &= ~(1 << PB5);   // Turns off the LED
        USART_Print("\r\n[NANO] INTERACTED WITH LED\r\n");
      }

      else {
        USART_Print("\r\n[NANO] INVALID COMMAND\r\n");
      }

      buffer = 0;
    }

    else {
      if (buffer < BUFFER_SIZE - 1) {
        commandBuffer[buffer++] = received;
      }
    }
  }
}

And finally the simple shell I wrote in rust:

use std::io::{self, Write, Read};
use std::time::Duration;
use serialport::SerialPort;

fn main() {


    // Variables for Serial Port name and BAUD rate 
    let port_name = "/dev/ttyUSB1";
    let baud = 115200;

    println!("[!] Initializing Arduino-Nano c0smic Shell! :)\n");


    // Opens a serial port connection to the Arduino
    let mut port = serialport::new(port_name, baud)
        .timeout(Duration::from_millis(1000))
        .open().expect("Failed to open port");

    println!("[!] Successfully connected to serial port /dev/ttyUSB1!\n");
 
    println!("Shell is initialized, you may start sending commands :)\n");

    let mut inputBuffer = String::new();

    loop {

        print!(">> ");
        io::stdout().flush().unwrap();

        inputBuffer.clear();
        // Creates input buffer for commands 

        // Clears reads line 
        io::stdin().read_line(&mut inputBuffer).expect("Failed to read line!");

        // Trims command buffer 
        let inputBuffer: String = inputBuffer.trim().parse().expect("Failed to clean buffer");


        if inputBuffer == "ledON" || inputBuffer == "ledOFF" {
           

            // Sends data as bytes 
            port.write_all(format!("{}\n", inputBuffer).as_bytes()).unwrap();

            // Makes the CPU wait (sleep) for 500 ms 
            std::thread::sleep(Duration::from_millis(500));


            // Creates buffer for receiving data from Nano 
            let mut buffer = [0u8; 128];

            match port.read(&mut buffer) {
                Ok(n) if n > 0 => {
                    let response = String::from_utf8_lossy(&buffer[..n]);
                    println!("{}", response);
                }
    
                // Checks if a response is received or if there is an error 
                Ok(_) => println!("No response receieved"),
                Err(e) => println!("Failed to read: {}", e),
            }

        } else {
            println!("[!] Error ::: Invalid Command!\n");
        }
    }

}

The Makefile for compilation:

default: build

build:
    avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p main.c -o main 

burn: build 
    avr-objcopy -O ihex -R .eeprom main main.hex 

    avrdude -F -V -c arduino -pm328p -P /dev/ttyUSB1 -b 115200 -U flash:w:main.hex 

I was able to confirm that the rust shell is able to connect to the Arduino and actually send data (as the RX LED flickers everytime I send the command) but everytime the shell waits for a response, I get the error "Timed Out". From what I gather, the Arduino Nano isn't able to read the data sent properly. Any help is appreciated. Tyia!


Solution

  • Consider trying this already proven solution: https://github.com/johncobb/avr_328p_usart Also, please consider using the toolchain header files like avr/io.h.

    I was able to use the posted code to see output after these changes:

    // Register Bit Defines  
    #define RXEN0       4                   // USART Receiver
    #define TXEN0       3                   // USART Transmitter 
    #define UCSZ00      1                   // Sets Character Size
    #define UCSZ01      2
    

    And Updating the function USART_Init like this:

    UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
    

    The value USBS0 might be correct, but I don't know the specific bits enough to give good advice so I am copying the value used by the reference solution.

    Here is the file avr-uart.h that is working for me:

    #include <stdint.h>
    
    
    // Register Defines
    
    #define UBRR0L      (*(volatile uint8_t *)0xC4)
    #define UBRR0H      (*(volatile uint8_t *)0xC5)
    #define UCSR0A      (*(volatile uint8_t *)0xC0)
    #define UCSR0B      (*(volatile uint8_t *)0xC1)
    #define UCSR0C      (*(volatile uint8_t *)0xC2)
    #define UDR0        (*(volatile uint8_t *)0xC6)
    
    
    // Register Bit Defines  
    #define RXEN0       4                   // USART Receiver
    #define TXEN0       3                   // USART Transmitter 
    #define USBS0       3                   // Sets number of stop bits 
    #define UCSZ00      1                   // Sets Character Size
    #define UCSZ01      2
    #define UDRE0       5
    #define RXC0        7
    
    // Constant Defines 
    #define CLOCK_SPEED     16000000                // MCU Clock Speed 
    #define BAUD        115200                  // BAUD rate 
    #define MYUBRR      CLOCK_SPEED / 16 / BAUD - 1         // UART Baud Rate 
    
    
    
    /*
     * Function Desc: Function to handle Data buffer transfer
     * @param data  => Buffer holding the data to be transmitted 
    */
    void USART_Transmit(unsigned char data) {
    
        // Wait for empty transmit buffer
        while ( !( UCSR0A & (1 << UDRE0)));
    
        // Puts data in the buffer and sends data 
        UDR0 = data; 
    }
    
    
    
    
    /*
     * Function Desc: Function to handle Data Reception 
    */
    volatile uint8_t USART_Recieve(void) {
        
        while ( !( UCSR0A & (1 << RXC0)));
    
        return UDR0;
    
    }
    
    /*
     * Function Desc: Function to handling Printing strings to UART
     * @param *data => Pointer to data buffer
    */
    void USART_Print(const char* data) {
    
        while (*data) {
            USART_Transmit(*data++);
        }
    }
    
    /*
     * Function Desc: Function to handle USART setup for Data Transmission and Reception
     * @param ubrr  => BAUD Rate 
    */
    void USART_Init(unsigned int ubrr) {
    
        // This sets BAUD Rate 
        UBRR0H = (unsigned char) (ubrr >> 8);   // Zeroes out higher bit register (according to datasheet) 
        UBRR0L = (unsigned char) ubrr;      // Sets lower bit register baud rate 
    
    
        // Enable Receiver and Transmitter 
        UCSR0B = (1 << RXEN0) | (1 << TXEN0);
    
        // Sets frame format 
        UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
    
    }