Jetson Nano UART C/C++ Example

I tried the jetsonhacks python example:

https://www.jetsonhacks.com/2019/10/10/jetson-nano-uart/

with a TTL-232R-3V3 cable from the J41 UART to Windows 10/Putty. It worked flawlessly. The J44 UART serial console also works flawlessly as I pointed out in thread #18. But I need a C++ example.

I tried my C++ example below (adapted from an RPi example) on the J41 UART port (“/dev/ttyTHS1”), but no luck. I get an “unable to open” error. If I run it in sudo mode, it seems to open the port, but nothing streams to Putty. When I go back to the python example, it works again. Any ideas?

#include <stdio.h>
#include <unistd.h>      //Used for UART
#include <sys/fcntl.h>   //Used for UART
#include <termios.h>     //Used for UART
#include <string>


using namespace std;

int main()
{
    printf("Hello World\n\n");


	//----- SETUP USART 0 -----
	//-------------------------

	int uart0_filestream = -1;
	
	//OPEN THE UART
	//The flags (defined in fcntl.h):
	//	Access modes (use 1 of these):
	//		O_RDONLY - Open for reading only.
	//		O_RDWR - Open for reading and writing.
	//		O_WRONLY - Open for writing only.
	//
	//	O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
	//											if there is no input immediately available (instead of blocking). Likewise, write requests can also return
	//											immediately with a failure status if the output can't be written immediately.
	//
	//	O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.uart0_filestream = open("/dev/ttyTHS1", O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode
      uart0_filestream = open("/dev/ttyTHS1", O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode
	//uart0_filestream = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode
	

    if (uart0_filestream == -1)
	{
		//ERROR - CAN'T OPEN SERIAL PORT
		printf("Error - Unable to open UART.  Ensure it is not in use by another application\n");
	}
	
	//CONFIGURE THE UART
	//The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):

	//	Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
	//	CSIZE:- CS5, CS6, CS7, CS8
	//	CLOCAL - Ignore modem status lines
	//	CREAD - Enable receiver
	//	IGNPAR = Ignore characters with parity errors
	//	ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
	//	PARENB - Parity enable
	//	PARODD - Odd parity (else even)
	struct termios options;

	tcgetattr(uart0_filestream, &options);
	options.c_cflag = B115200 | CS8 | CLOCAL | CREAD;		//<Set baud rate
	options.c_iflag = IGNPAR;
	options.c_oflag = 0;
	options.c_lflag = 0;
	tcflush(uart0_filestream, TCIFLUSH);
	tcsetattr(uart0_filestream, TCSANOW, &options);


    //--------------------------------------------------------------
    // TRANSMITTING BYTES
    //--------------------------------------------------------------
	unsigned char tx_buffer[20];
	unsigned char *p_tx_buffer;
	
	p_tx_buffer = &tx_buffer[0];

	*p_tx_buffer++ = 'H';
	*p_tx_buffer++ = 'e';
	*p_tx_buffer++ = 'l';
	*p_tx_buffer++ = 'l';
	*p_tx_buffer++ = 'o';
	
	if (uart0_filestream != -1)
	{
		int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0]));		//Filestream, bytes to write, number of bytes to write

        printf("Count = %d\n", count);

		if (count < 0)
		{
			printf("UART TX error\n");
		}
	}


    //--------------------------------------------------------------
    // RECEIVING BYTES
    //--------------------------------------------------------------
/*
	if (uart0_filestream != -1)
	{
		// Read up to 255 characters from the port if they are there
		unsigned char rx_buffer[256];
		int rx_length = read(uart0_filestream, (void*)rx_buffer, 255);		//Filestream, buffer to store in, number of bytes to read (max)
		if (rx_length < 0)
		{
			//An error occured (will occur if there are no bytes)
		}
		else if (rx_length == 0)
		{
			//No data waiting
		}
		else
		{
			//Bytes received
			rx_buffer[rx_length] = '

#include <stdio.h>
#include <unistd.h> //Used for UART
#include <sys/fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include

using namespace std;

int main()
{
printf(“Hello World\n\n”);

//----- SETUP USART 0 -----
//-------------------------

int uart0_filestream = -1;

//OPEN THE UART
//The flags (defined in fcntl.h):
//	Access modes (use 1 of these):
//		O_RDONLY - Open for reading only.
//		O_RDWR - Open for reading and writing.
//		O_WRONLY - Open for writing only.
//
//	O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
//											if there is no input immediately available (instead of blocking). Likewise, write requests can also return
//											immediately with a failure status if the output can't be written immediately.
//
//	O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.uart0_filestream = open("/dev/ttyTHS1", O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode
  uart0_filestream = open("/dev/ttyTHS1", O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode
//uart0_filestream = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode


if (uart0_filestream == -1)
{
	//ERROR - CAN'T OPEN SERIAL PORT
	printf("Error - Unable to open UART.  Ensure it is not in use by another application\n");
}

//CONFIGURE THE UART
//The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):

//	Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
//	CSIZE:- CS5, CS6, CS7, CS8
//	CLOCAL - Ignore modem status lines
//	CREAD - Enable receiver
//	IGNPAR = Ignore characters with parity errors
//	ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
//	PARENB - Parity enable
//	PARODD - Odd parity (else even)
struct termios options;

tcgetattr(uart0_filestream, &options);
options.c_cflag = B115200 | CS8 | CLOCAL | CREAD;		//<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);
tcsetattr(uart0_filestream, TCSANOW, &options);


//--------------------------------------------------------------
// TRANSMITTING BYTES
//--------------------------------------------------------------
unsigned char tx_buffer[20];
unsigned char *p_tx_buffer;

p_tx_buffer = &tx_buffer[0];

*p_tx_buffer++ = 'H';
*p_tx_buffer++ = 'e';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'o';

if (uart0_filestream != -1)
{
	int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0]));		//Filestream, bytes to write, number of bytes to write

    printf("Count = %d\n", count);

	if (count < 0)
	{
		printf("UART TX error\n");
	}
}


//--------------------------------------------------------------
// RECEIVING BYTES
//--------------------------------------------------------------

/*
if (uart0_filestream != -1)
{
// Read up to 255 characters from the port if they are there
unsigned char rx_buffer[256];
int rx_length = read(uart0_filestream, (void*)rx_buffer, 255); //Filestream, buffer to store in, number of bytes to read (max)
if (rx_length < 0)
{
//An error occured (will occur if there are no bytes)
}
else if (rx_length == 0)
{
//No data waiting
}
else
{
//Bytes received
rx_buffer[rx_length] = ‘\0’;
printf(“%i bytes read : %s\n”, rx_length, rx_buffer);
}
}
*/

//----- CLOSE THE UART -----
close(uart0_filestream);

printf("Goodbye World\n\n");

}

';
			printf("%i bytes read : %s\n", rx_length, rx_buffer);
		}
	}
*/

//----- CLOSE THE UART -----
	close(uart0_filestream);

    printf("Goodbye World\n\n");

}

xplanescientist I can assume that Python opens the port in a different mode. The reason can be covered in 32 line. Try to experiment with the modes of opening the file / dev / ttyTHS1. This is a hypothesis. You can try to intercept calls from Python and see how he does it (the most important in which mode).

xplanescientist, try to use this command:

Strace

Strace is used for debugging and troubleshooting the execution of an executable on Linux environment. It displays the system calls used by the process, and the signals received by the process.

xplanescientist. Try to execute your programm under root. Use “sudo su” to entering root mode and execute your programm. Not use sudo.

Finally, UART success on J41 (“/dev/ttyTHS1”) using C++. Overcame issues related to software and hardware problems.

Firstly, as it turns out, the python3 example on jetsonhacks was NOT properly working. The Nano was correctly receiving bytes, but the Nano was NOT transmitting. I traced it to my TTL-232R-3V3 FTDI cable. I replaced it with the Adafruit TTL-USB cable (based on the SiLabs CP2012 chipset), and then the python test worked in both directions.

https://www.jetsonhacks.com/2019/10/10/jetson-nano-uart/

The software part… the options in my original C++ code were incorrect. After studying the termios library, the options were repaired. Here is the working C++ example:

#include <stdio.h>
#include <unistd.h>       // Used for UART
#include <sys/fcntl.h>    // Used for UART
#include <termios.h>      // Used for UART
#include <string>

using namespace std;

// Define Constants
const char *uart_target = "/dev/ttyTHS1";
#define     NSERIAL_CHAR   256
#define     VMINX          1
#define     BAUDRATE       B115200

int main()
{
    printf("Hello World\n\n");

    int ii, jj, kk;

	// SETUP SERIAL WORLD
    int fid = -1;
	struct termios  port_options;   // Create the structure                          

    tcgetattr(fid, &port_options);	// Get the current attributes of the Serial port 

	
    //------------------------------------------------
	//  OPEN THE UART
    //------------------------------------------------
	// The flags (defined in fcntl.h):
	//	Access modes (use 1 of these):
	//		O_RDONLY - Open for reading only.
	//		O_RDWR   - Open for reading and writing.
	//		O_WRONLY - Open for writing only.
	//	    O_NDELAY / O_NONBLOCK (same function) 
    //               - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
    //                 if there is no input immediately available (instead of blocking). Likewise, write requests can also return
	//				   immediately with a failure status if the output can't be written immediately.
    //                 Caution: VMIN and VTIME flags are ignored if O_NONBLOCK flag is set.
	//	    O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.fid = open("/dev/ttyTHS1", O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode

    fid = open(uart_target, O_RDWR | O_NOCTTY );

	tcflush(fid, TCIFLUSH);
 	tcflush(fid, TCIOFLUSH);
 	
    usleep(1000000);  // 1 sec delay

    if (fid == -1)
	{
		printf("Error - Unable to open UART.  Ensure it is not in use by another application\n");
	}
	
    //------------------------------------------------
	// CONFIGURE THE UART
    //------------------------------------------------
	// flags defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html
	//	Baud rate:
    //         - B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, 
    //           B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, 
    //           B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
	//	CSIZE: - CS5, CS6, CS7, CS8
	//	CLOCAL - Ignore modem status lines
	//	CREAD  - Enable receiver
	//	IGNPAR = Ignore characters with parity errors
	//	ICRNL  - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
	//	PARENB - Parity enable
	//	PARODD - Odd parity (else even)

    port_options.c_cflag &= ~PARENB;            // Disables the Parity Enable bit(PARENB),So No Parity   
    port_options.c_cflag &= ~CSTOPB;            // CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit 
    port_options.c_cflag &= ~CSIZE;	            // Clears the mask for setting the data size             
    port_options.c_cflag |=  CS8;               // Set the data bits = 8                                 	 
    port_options.c_cflag &= ~CRTSCTS;           // No Hardware flow Control                         
    port_options.c_cflag |=  CREAD | CLOCAL;                  // Enable receiver,Ignore Modem Control lines       				
    port_options.c_iflag &= ~(IXON | IXOFF | IXANY);          // Disable XON/XOFF flow control both input & output
    port_options.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // Non Cannonical mode                            
    port_options.c_oflag &= ~OPOST;                           // No Output Processing

    port_options.c_lflag = 0;               //  enable raw input instead of canonical,
 		
    port_options.c_cc[VMIN]  = VMINX;       // Read at least 1 character
    port_options.c_cc[VTIME] = 0;           // Wait indefinetly 
		
    cfsetispeed(&port_options,BAUDRATE);    // Set Read  Speed 
    cfsetospeed(&port_options,BAUDRATE);    // Set Write Speed 

    // Set the attributes to the termios structure
    int att = tcsetattr(fid, TCSANOW, &port_options);

    if (att != 0 )
    {
        printf("\nERROR in Setting port attributes");
    }
    else
    {
        printf("\nSERIAL Port Good to Go.\n");
    }

    // Flush Buffers
    tcflush(fid, TCIFLUSH);
    tcflush(fid, TCIOFLUSH);

    usleep(500000);   // 0.5 sec delay

//--------------------------------------------------------------
    // TRANSMITTING BYTES
    //--------------------------------------------------------------
    unsigned char tx_buffer[20];
    unsigned char *p_tx_buffer;
	
    p_tx_buffer = &tx_buffer[0];

    *p_tx_buffer++ = 'H';
    *p_tx_buffer++ = 'e';
    *p_tx_buffer++ = 'l';
    *p_tx_buffer++ = 'l';
    *p_tx_buffer++ = 'o';
    *p_tx_buffer++ = ' ';
    *p_tx_buffer++ = 'f';
    *p_tx_buffer++ = 'r';
    *p_tx_buffer++ = 'o';
    *p_tx_buffer++ = 'm';
    *p_tx_buffer++ = ' ';
    *p_tx_buffer++ = 'N';
    *p_tx_buffer++ = 'a';
    *p_tx_buffer++ = 'n';
    *p_tx_buffer++ = 'o';

printf("fid 1=%d\n", fid );
	
	if (fid != -1)
	{
		int count = write(fid, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0]));		//Filestream, bytes to write, number of bytes to write

        usleep(1000);   // .001 sec delay

        printf("Count = %d\n", count);

		if (count < 0)  printf("UART TX error\n");
	}

    usleep(1000000);  // 1 sec delay

//--------------------------------------------------------------
    // RECEIVING BYTES - AND BUILD MESSAGE RECEIVED
    //--------------------------------------------------------------
    unsigned char rx_buffer[VMINX];
    unsigned char serial_message[NSERIAL_CHAR];
    bool          pickup = true;
    int           rx_length;
    int           nread = 0;    

	tcflush(fid, TCIOFLUSH);

    usleep(1000);   // .001 sec delay

    printf("Ready to receive message.\n");

for (ii=0; ii<NSERIAL_CHAR; ii++)  serial_message[ii]=' ';

	while (pickup && fid != -1)
	{
        nread++;

        rx_length = read(fid, (void*)rx_buffer, VMINX);   // Filestream, buffer to store in, number of bytes to read (max)

        printf("Event %d, rx_length=%d, Read=%s\n",  nread, rx_length, rx_buffer );

		if (rx_length < 0)
		{
			//An error occured (will occur if there are no bytes)
		}

		if (rx_length == 0)
		{
			//No data waiting
		}
		
        if (rx_length>=0)
		{
            if (nread<=NSERIAL_CHAR)   serial_message[nread-1] = rx_buffer[0];   // Build message 1 character at a time

            if (rx_buffer[0]=='#')   pickup=false;                               // # symbol is terminator
		}
	}

    printf("\nMessage Received: %s", serial_message);

//-------------------------------------------
    //  CLOSE THE SERIAL PORT
    //-------------------------------------------
    close(fid);

printf("\n\nGoodbye World\n\n");

}

Execution requires sudo:

g++ uart.cpp -o uart.exe
sudo ./uart.exe

xplanescientist Congratulations! I’m glad that everything worked out for you. Can you provide your example with more detailed comments?

More success. I was able to connect the Jetson Nano J41 UART to a Teensy 3.5 microcontroller, which is a powerful 32-bit Arduino-like module. Read & Write transpired in both directions at 115200 baud and 8N1 settings. No need for any special TTL-USB cable, just a simple 3-wire connection. See photo:

[url]Teensy Serial Communication with Jetson Nano

Xplanescientist well done! My congratulations!