Jetson Nano UART C/C++ Example

Recently purchased the Nano and installed the latest Arduino IDE and Teensyloader for the Teensy board (32-bit microcontroller). Compiling and uploading code is fast and on par with Windows 10. Process is smooth.

Eventually I want to communicate from the Nano with the Teensy board through UART and eventually SPI. Are there C++ examples for the Nano?

1 Like

hello xplanescientist,

please refer to https://github.com/NVIDIA/jetson-gpio to use python library to control digital input and output.
please also refer to Topic 1050148 for serial parameters configurations.
thanks

The Nano is just a Linux machine, so any Linux serial port example should work.

For the Teensy, using the USB connector for serial might be the simplest, as long as USB isn’t a robustness problem for your application.
On the Teensy, this is simply the “Serial” device; on the Jetson, this will be “/dev/ttyACM0” or whatever you name it in the udev rules.

You’ll want to use the <termios.h> header functions to configure the serial port device after you first open it, before you start reading/writing data. For example:

#include <termios.h>
#include <unistd.h>
#include <sys/fcntl.h>

struct MyPort {
  MyPort() : fd_(-1) {}
  ~MyPort() { if (fd_ != -1) ::close(fd_); }
  int fd_;
  bool open(char const *devname);
};

bool MyPort::open(char const *devname) {
  if (fd_ != -1) {
    ::close(fd_);
  }
  fd_ = ::open(devname, O_RDWR);
  if (fd_ < 0) {
    perror(devname);
    return false;
  }
  struct termios tio;
  ::tcgetattr(fd_, &tio);
  cfmakeraw(&tio);
  tio.c_iflag &= ~(IXON | IXOFF);
  cfsetspeed(&tio, B115200);
  tio.c_cc[VTIME] = 0;
  tio.c_cc[VMIN] = 0;
  int err = ::tcsetattr(fd_, TCSAFLUSH, &tio);
  if (err != 0) {
    perror(devname);
    ::close(fd_);
    fd_ = -1;
    return false;
  }
  return true;
}

There are other peculiarities about UNIX TTY devices; timeouts are annoying to work with and only come in 100000-microsecond increments and so forth. You may want to use O_NONBLK mode instead, or have a separate thread that talks to/from the serial port.

If you use a “real” serial port on the Teensy, then you’d need to use a “real” serial port on the Nano, too, but other than the device names, nothing else should really change. (Also: The USB serial port doesn’t care about what speed you set when you talk directly to the Teensy USB Serial endpoint – it will always transfer data at the fastest speed it can achieve.)

@JerryChang, thanks, but I’m looking for a working C/C++ example, not Python. It would be very useful if Nvidia had a central location for C++ examples and other languages on UART, I2C, SPI, and digital input/outout. Since these peripherals are part of the Jetson Nano board, examples should be readily available. Just a friendly recommendation from a customer wanting to buy more Nanos.

@snarky, thanks very much for the example. The USB serial will be useful, but in general I also need “real” serial for other modules such as XBEE, GPS, EMIC, etc. On the “timeouts”, are they more common with USB serial or “real”?

Regarding digital input/output examples, what is the fastest library out there? On the teensy, the reads are really fast, in the order of a few microseconds. I suppose the linux filesys approach will never be as fast. But I’d still like the fastest possible on the Nano. Do you have a fast example? This is what I found:

https://developer.ridgerun.com/wiki/index.php/Gpio-int-test.c

The timeouts are architecturally specified as part of the Linux (and UNIX) TTY driver.
They’re generally designed for command line input over a serial console (like a ADM3 or VT52 or teletype) and thus not a good match for the needs of a typical real-time system. So, the best thing to do is to either run in fully-blocking mode, or fully-polling mode.
For more information, read the “termios” man page: http://man7.org/linux/man-pages/man3/termios.3.html
(Other caveats: Turning off XON/XOFF is important if you use a binary serial protocol!)

The good news is that USB serial and UART serial devices follow the same API, only the device name changes (e g /dev/ttyTHS0 instead of /dev/ttyACM0 and so forth.)

@snarky, is your UART example a working program or a conceptual program? I tried compiling it, but get these errors:

xplanescientist@xplanescientist:~/robots/jetson/uart$ g++ snarky.c 
snarky.c: In member function ‘bool MyPort::open(const char*)’:
snarky.c:18:5: error: ‘perror’ was not declared in this scope
     perror(devname);
     ^~~~~~
snarky.c:30:5: error: ‘perror’ was not declared in this scope
     perror(devname);
     ^~~~~~
snarky.c:30:5: note: suggested alternative: ‘err’
     perror(devname);
     ^~~~~~
     err

I’m looking for a C/C++ example. The Nvidia people keep pointing me to python examples.

Iäm surprised – you ask for a C++ example, but you don’t know what to do when your code doesn’t include the appropriate function?
perror() is a standard C library function, and as a C++ programmer, you should know how to look up which header to include to declare it, if your current headers don’t.

man perror
NAME
       perror - print a system error message

SYNOPSIS
       #include <stdio.h>

       void perror(const char *s);

       #include <errno.h>

       const char * const sys_errlist[];
       int sys_nerr;
       int errno;       /* Not really declared this way; see errno(3) */

(This assumes you have installed the man pages, or have a convenient Linux box somewhere that already has it. Or you can just type “man perror” into the Chrome search bar.)

xplanescientist, read this. http://www.ing.iac.es/~docs/external/serial/serial.pdf

This should be a more or less fundamental source on your subject. There is one caveat, you cannot use pure C ++. It is better to use a wrapper library from C ++ to standard C.

I am not a professional C++ programmer, but a mere amateur programmer having come from the world of Arduino Due and Teensy 3.6, and have never come across the perror() function. The transition to the Jetson Nano has been difficult - it requires more low-level programming than I’m use to. It’s too bad Nvidia has not created convenient ready-to-use libraries for UART, SPI, Direct Register Access GPIO, etc. so people like myself can focus on building projects.

I hope the Jetson Nano is not solely intended for professional programmers. From a business case, it would make more sense to make it amenable to as many customers as possible including professionals, amateurs, tinkerers, and the huge Arduino/Teensy/etc. community looking for a single board computer capable of AI through multicore GPUs. This is huge.

I see! Yes, you need to install the “manual pages” so you can look up things like functions and commands.

If I remember correctly, in a terminal, do:

sudo apt update && sudo apt install man-db man

Then you can, as I showed above, try “man perror” to see what headers and libraries may be needed.
(As you can see from the man page excerpt above, it’s defined in <stdio.h>)

Regarding the business case for hobbyists, the whole Arduino ecosystem is probably worth less than a single consumer electronics design win. Most of the small little widgets and boards you can get for an Arduino or similar system are made by small businesses in basements and garages in China.
I think the business case for a cheap Jetson for NVIDIA is some variation of “marketing,” “mindshare,” and “cultivating a certain way of developing/thinking” that will let them have an easier time competing for the big-money business. Perhaps they also had a bunch of TX1 dies laying around, as purchasing insurance for Nintendo/Switch, and didn’t know what to do with them, so they gimped them down to half the GPU and made the Jetson Nano?

Alright, with the addition of <stdio.h> and the main() function, your example compiles without error. But I find it a bit cryptic. How is it used? I can see one would declare a MyPort struct and open a serial console as follows:

MyPort  serial;

serial.open(‘/dev/ttyUSB0‘);

Then what? How are bytes sent and received?

You use, for example, the read() and write() system calls.

It sounds like you need to read up on the Linux I/O model, file descriptors, and TTY device driver behavior, though. This is a rich system with lots of support for many use cases that have come up over the 40 years of lifetime of Linux, so there’s no “one size fits all” or “super simple” solution.

Here are some good references:
https://www.cmrr.umn.edu/~strupp/serial.html
http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/

If you run into things you don’t understand, you may need to separately Google those concepts, or, if it’s commands (like stty) or system calls (like fcntl()) then you can learn about them using “man”.

I’m trying to open the serial port on the J44 header ("/dev/ttyS0"), but no luck. The program compiles without issue and runs, but it fails to open the port.

I tried stopping and disabling the nvgetty service:

systemctl stop nvgetty
systemctl disable nvgetty

but linux says “Failed to stop nvgetty.service: Unit nvgetty.service not loaded.”

Here’s the program modified from R-Pi:

#include <stdio.h>
#include <unistd.h>			//Used for UART
#include <sys/fcntl.h>			//Used for UART
#include <termios.h>		//Used for 
#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/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
		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
#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/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
	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");

}

Hi xplanescientist.

Try to figure out which process opened this file “/dev/ttyS0”. Here are some articles on this topic:
https://superuser.com/questions/97844/how-can-i-determine-what-process-has-a-file-open-in-linux
https://alvinalexander.com/blog/post/linux-unix/linux-lsof-command

Perhaps some process uses this file exclusively.

The “lsof” is not a standard command, but it was easy to install: “sudo apt-get insall lsof -y”

Applying “lsof” yielded 27,566 open files. Wow. I narrowed it down with a grep:

$ lsof | grep tty

The only device in-use was: /dev/tt1

So not sure why the example program in thread #13 cannot open /dev/ttyS0. Also tried /dev/ttyTHS1, but no luck.

The example program compiled just fine, so I’m not sure what is going on. Has anyone successfully done UART with C or C++?

1 Like

Here’s how to use the UART on the J41 header, which presents as /dev/ttyTHS1

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

Note that on startup that the Jetson starts a console on /dev/ttyTHS1. This is started by the nvgetty service, which is a getty (get TTY) wrapper. If you are not using that UART as a console, then you would likely stop the nvgetty service and disable it, or modify the systemd startup appropriately. *See note below.

The UART on the J44 header is used as the system debug console. Because the process is owned as a system process, use sudo:

$ sudo lsof | grep ttyS0

You will see that the owner is a agetty

More than likely, that is why you cannot open the J44 UART, it is already in use by the system.

There are various ways to turn off the system debug console, but if you are not using the UART on the J44 header you should consider switching over there as that is certainly the path of least resistance. Otherwise, you can search the forum on how to disable the system debug console, or hack your way through it.

  • Note: In order to have a more complete answer here a console allows an external computer to connect to the Jetson as a terminal. If you run a Terminal program on the other computer and attach to the Jetson UART (typically with a TTL->USB cable), you will see the familiar “user name, password” prompts that allow you into the Jetson Nano kingdom. If you are connecting the UART directly to a hardware device, of course, this typically gets in the way. As you would expect people then report that the UART does not work, when it is actually trying to serve more than one master at once and is just a little confused.
1 Like

Hi xplanescientist,
Try to output more information to the console about the nature of the error. I think, as usual, all nonsense is small. This happens in most cases. Additionally, you can read about Linux kernel functions here:

https://en.wikipedia.org/wiki/The_Linux_Programming_Interface.

If you put the code on the GitHub, I can try to help. I’m in a sense a novice C ++ programmer. I can help if possible.

@Kangalow, yes, I had come across that jetsonhacks article, but it’s geared toward python and the UART on J41.

I chose the J44 header for UART because it’s a clean connection separate from the J41 header, which I want to save for my feedback control project needing GPIO and SPI interfaces.

I linked the J44 header to my Windows 10 PC with a TTL-232R-3V3 cable, and through Putty, I was able to use the serial console and login to the Jetson Nano and rummage around the linux directories. Worked flawlessly. But I have not found a straightforward way to disable this serial console, which I don’t need.

I may end up using the J41 UART because it may be easier to disable those Jetson services that use “/dev/ttyTHS1” on J41.

@blademoon, I’ll hack away a bit more before I put the C/C++ UART example on github. In the meantime, this is what I have so far:

https://devtalk.nvidia.com/default/topic/1057441/jetson-nano/nano-uart-c-c-example/post/5393606/#5393606

xplanescientist, ok. Your code is your right. I respect that. Hope to see it, it’s most interesting. Not to say that the topic is very necessary for me, but everything can come in handy. Good luck. ))) Any questions, please contact.

@xplanescientist. I especially liked the beginning and end of the program. Nostalgia)))

printf(“Hello World\n\n”);

printf(“Goodbye World\n\n”);

You have a sense of humor)))