Serial monitor can't read from Teensy 3.6 on Ubuntu 20.04 but can on Windows10

Status
Not open for further replies.

LouisM

Member
I have a Teensy 3.6. I can upload sketches (for example the 'Hello World' sketch in examples) from my Ubuntu laptop to the Teensy. The serial output can then be read on my windows 10 laptop after I connect the USB cable but not from the Ubuntu. The serial monitor just remains blank.

If I upload a long sketch I'm using to trigger a camera, it occasionally shows the serial output but it's very glitchy. Until yesterday I was able to consistently see the data in the serial monitor but there were issues with accessing it using a C++ code (basically the C++ code could only read the serial output from USB after the arduino serial monitor had been opened).

I'm basing quite a large project on this and would like to know if Teensy supports Ubuntu 20.04 or should I switch to another microcontroller.

Thank you.

Louis
 
Did you install the udev rules? Is ModemManager interfering? Those are the 2 most common problems on Linux systems.

I personally use Ubuntu 18.04 and it works great. I don't have any systems yet running 20.04.

In the Tools > Ports menu, you should see Teensy listed twice if it's running as USB serial. Selecting the one under Teensy Ports will run a native program which communicates with the Arduino IDE using stdin/stdout. Select under Serial Ports will do all the serial access through Java and the JSSC library. Does one of those work but not the other?

If neither of those work, you might try setting Tools > USB Type to MIDI or RawHID. Then upload, and Serial.print() uses HID protocol. You should only see it appear as a /dev/hidraw device under Teensy Ports, since normal Java-based Arduino doesn't know how to talk with HID. If this works, but both serial modes fail, it can at least confirm all the hardware on your PC talking to Teensy is good.

But again, the 2 most common problems are lacking the udev rules, or ModemManager interfering even when the udev rules tell it leave Teensy alone.
 
basically the C++ code could only read the serial output from USB after the arduino serial monitor had been opened

Just a blind guess, since we can't see this C++ code, but a problem which has come up many times before on all Linux system is "unix line discipline". Basically, you need to configure the serial device to use "raw mode". Arduino does that. So if you open the port from another program after Arduino has already opened it, the Linux device is still configured for raw mode.

To be clear, this is absolutely not a problem with Teensy or Linux. It's a very common problem with custom programs which open a serial port but fail to do all the "termios" stuff to properly configure the port.
 
I've been using Teensy on Ubuntu 20.04 since it came out in April (and on 19.10, 18.04 and 16.04 before that), and I've never had any problems with the serial monitor.

When selecting the 'Serial' USB mode, the Teensy shows up at /dev/ttyACM0 (or a higher number if you have multiple devices connected). You can simply open that device in your C++ code as a file or any other serial port and read/write to it.

How did you install the Arduino IDE? Did you disable/uninstall modemmanager? It is known to interfere with CDC/ACM devices.

Pieter
 
On the serial device raw mode, the Linux native functions are tcgetattr() to fetch the serial port parameters, cfmakeraw() to change the setting in the "struct termios" data, and tcsetattr() to write it back to the serial device. Type "man termios" for documentation.

If your program fails after Teensy has rebooted (and Linux has recreated the /dev/ttyACM0 device) but works after you've opened & closed the Arduino Serial Monitor, you almost certainly need to add these functions to your program to properly configure the serial device.

(and regarding whether you should buy a different dev board.... how many of the others on the market do you think would help you debug your C++ code like this?)
 
Just a blind guess, since we can't see this C++ code, but a problem which has come up many times before on all Linux system is "unix line discipline". Basically, you need to configure the serial device to use "raw mode". Arduino does that. So if you open the port from another program after Arduino has already opened it, the Linux device is still configured for raw mode.

To be clear, this is absolutely not a problem with Teensy or Linux. It's a very common problem with custom programs which open a serial port but fail to do all the "termios" stuff to properly configure the port.

The code I'm using is:
Code:
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <serial/serial.h>
#include <thread>
#include <chrono>
serial::Serial ser;

int main(int argc, char **argv)
{
ros::init(argc, argv, "Teensy_serial_node");
ros::NodeHandle nh;
ROS_INFO("Good evening.");

ros::Publisher from_Teensy_publisher = nh.advertise<std_msgs::String>("Timestamp_for_reconstruction", 10);
std::this_thread::sleep_for(std::chrono::milliseconds(5000)); //wait for node to initiate

 try 
    {
        ser.setPort("/dev/Ladybug_timestamping_teensy");
        ser.setBaudrate(9600);
        serial::Timeout to = serial::Timeout::simpleTimeout(1000);
        ser.setTimeout(to);
        ser.open();
    }
    catch (serial::IOException& e)
    {
        ROS_ERROR_STREAM("Unable to open port ");
        return -1;
    }

 if(ser.isOpen()){
        ROS_INFO_STREAM("Serial Port initialized");
    }else{
        return -1;
    }

ros::Rate loop_rate(5);
ROS_INFO("declared the loop rate.");

while(ros::ok()){
if(ser.available()){
			ros::spinOnce();			
            ROS_INFO_STREAM("Reading from serial port");
            std_msgs::String result;
            result.data = ser.read(ser.available());
            ROS_INFO_STREAM("Read: " << result.data);
			from_Teensy_publisher.publish(result);
        }
        loop_rate.sleep();
}


}

In the past, I've used Python to do such things, I'd be grateful for any guidance.

In the final version, the Teensy should wake up, wait to receive a message from the C++ code, and only then start sending data to the C++ code. I'm worried about USB issues in general with flushing buffers and I'm considering attempting to use the computer's RS232 port with the Teensy's Tx Rx pins to communicate instead.
 
For reference, this code works even if the arduino IDE was never opened. It is for ROS but doesn't use rosserial, which I have found to be unreliable in the past.
The only time this code breaks is if the computer is restarted without unplugging the Teensy at some point. This could probably be solved but isn't crucial for my purposes.

Paul Stoffregen was right that I wasn't configuring the Teensy properly when I was opening it. Thanks for the help!
Code:
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <thread>
#include <chrono>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

#include <fcntl.h> // Contains file controls like O_RDWR
#include <errno.h> // Error integer and strerror() function
#include <termios.h>
#include <unistd.h>

int serial_port = open("/dev/Ladybug_timestamping_teensy", O_RDWR);

void my_handler(sig_atomic_t s){ //this should close the USB port if you do ctrl+C
           ROS_INFO("Caught signal %d, will close USB port in 1 seconds\n",s);
		   tcflush(serial_port,TCIOFLUSH);
		   sleep(1);
		   close(serial_port);
           exit(1); 
}

int main(int argc, char **argv){
ros::init(argc, argv, "Teensy_serial_node");
ros::NodeHandle nh;
ROS_INFO("Good evening.");
int serial_port = open("/dev/Ladybug_timestamping_teensy", O_RDWR);

usleep(10000);
if (serial_port < 0) {
ROS_INFO("Error %i from open: %s\n", errno, strerror(errno));
}

ros::Publisher from_Teensy_publisher = nh.advertise<std_msgs::String>("Timestamp_for_reconstruction", 10);
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); //wait for node to initiate


struct termios tty; //we could call this anything, for convention we call it tty

if(tcgetattr(serial_port, &tty) != 0) {
    ROS_INFO("Error %i from tcgetattr: %s\n", errno, strerror(errno));
}

tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below
tty.c_cflag |= CS8; // 8 bits per byte (most common)
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
tty.c_lflag &= ~ICANON; // THIS ~~~~~~~~~IMPORTANT~~~~~~~~~~~~~~~~ we want raw serial data, this is what the arduino IDE does
tty.c_lflag &= ~ECHO; // Disable echo WE PROBABLY DONT NEED THESE THREE LINES
tty.c_lflag &= ~ECHOE; // Disable erasure WE PROBABLY DONT NEED THESE THREE LINES
tty.c_lflag &= ~ECHONL; // Disable new-line echo WE PROBABLY DONT NEED THESE THREE LINES
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // ~~~~~~~~~~AGAIN WE WANT RAW~~~~~~~~Disable any special handling of received bytes
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_cc[VTIME] = 10;    //timeout value
tty.c_cc[VMIN] = 0;//returns data immediately, change if you need to wait for a certain number of characters
cfsetispeed(&tty, B9600);// set Baud rate
cfsetospeed(&tty, B9600);// set Baud rate
// Save tty settings, also checking for error
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
    ROS_INFO("Error %i from tcsetattr: %s\n", errno, strerror(errno));
}

 unsigned char msg[] = { 'H'};
 write(serial_port, "H", sizeof(msg)); // this tells the Teensy to start sending data
ros::Rate loop_rate(60);
ROS_INFO("declared the loop rate.");
signal (SIGINT,my_handler);

while(ros::ok()){
			ros::spinOnce();
			ROS_INFO_STREAM("Reading from serial port");
			char read_buf [256];
			memset(&read_buf, '\0', sizeof(read_buf));
			int num_bytes = read(serial_port, &read_buf, sizeof(read_buf));
			// num bytes is the number of bytes read. n may be 0 if no bytes were received, and can also be negative to signal an error.
			if (num_bytes < 0) {//deal with the error
						ROS_INFO("Error reading: %s", strerror(errno));
			return 1;
			}
	//		ROS_INFO("Read %i bytes. Received message: %s", num_bytes, read_buf);

			std_msgs::String result;
                        result.data = read_buf;
                        ROS_INFO_STREAM("Read: " << result.data);
		        from_Teensy_publisher.publish(result);

        loop_rate.sleep();
}
}
 
Status
Not open for further replies.
Back
Top