Can read, but cannot write serial ports on Ubuntu 16.04

Can read, but cannot write serial ports on Ubuntu 16.04



I have a strange problem with trying to write data to a serial port.
I am running Ubuntu 16.04 on a NUC7i7DNBE, and am trying to make a serial connection to an Arduino UNO. The Serial API that I am using is found here: http://docs.ros.org/kinetic/api/serial/html/classserial_1_1Serial.html



I have writen a simple program which opens the serial port "ttyACM0" to communcate with the arduino. I have tested this code on another computer running Ubuntu 16.04, and everything worked fine, the only permissions I had to set where adding the user to the dialout group.



On the NUC, however, I have added the user to the dialout group. This allowed the program to read from the Arduino, but it still does not write to the Arduino. The Arduino IDE will write to the Arduino just fine, but my program will not.



I am assuming that I am having trouble with serial write permissions in Ubuntu.



Steps I have taken:



I have added a rule in /etc/udev/rules.d/ which states:



SUBSYSTEMS=="tty", GROUP="dialout", MODE="0666"



Afterward, I sent the commands:



sudo chown root:root /etc/udev/rules.d/50-AVCusb.rules



sudo chmod 0644 /etc/udev/rules.d/50-AVCusb.rules



udevadm control --reload-rules



I followed some information found on stack exchange to get to this point:
https://unix.stackexchange.com/questions/111593/allow-non-root-user-to-read-write-dev-files



I have tried using an FTDI device to write to the Arduino port. The FTDI device uses the ttyUSB0 port rather than the ttyACM0 port. The result was the same; can read, but can't write.



I have also run my external hard-drive on the NUC to see if there was any kind of hardware issue. When I ran the program from my external hard drive, I had no problem reading from and writing to the Arduino.



I have not dealt much with Ubuntu permissions or ports in general, please help me find and upload any other information that you may need in order to help me solve this problem.



Code on NUC:


#include <ros/ros.h>
#include <serial/serial.h>

using namespace serial;

Serial ser;
static const uint8_t MOTOR_ID = 0;

void writeMotor(uint8_t byte)

size_t size = 4;
uint8_t buffer[size];
buffer[0] = 'G'; //PID
buffer[1] = 'O';
buffer[2] = MOTOR_ID; //address
buffer[3] = byte; //data byte
ser.write(buffer, size);



int main()

ros::init(argc, argv, "servo_esc_driver");

std::string port = "/dev/ttyACM0";
Timeout timeout = Timeout(0, 0, 0, 0, 0);
bytesize_t bytesize = eightbits;
parity_t parity = parity_none;
stopbits_t stopbits = stopbits_one;
flowcontrol_t flowcontrol = flowcontrol_none;

try
ser.setPort(port);
ser.setBaudrate(115200);
ser.setTimeout(timeout);
ser.setBytesize(bytesize);
ser.setParity(parity);
ser.setStopbits(stopbits);
ser.setFlowcontrol(flowcontrol);
ser.open();

catch (SerialException e)
ROS_FATAL_NAMED("Failed to connect to the Arduino UNO, %s.", e.what());
ros::shutdown();
return 0;


uint8_t byte = 90;
writeMotor(byte);




Full Code on Arduino


#include <Servo.h>
const byte N = 2;
//Servo esc;
//Servo servo;
Servo servo[N];
//int escPos = 90;
//int servoPos = 90;
int pos[N];
static const byte ESC_PIN = 7;
static const byte SERVO_PIN = 8;
static const byte RPM_FEEDBACK_PIN = 0; //interrpt 0, pin 2
static const byte SERVO_FEEDBACK_PIN = A0;

//const float MUL = 0.7058823529; //180/255
unsigned long lastTime_servoFeedback = 0;
static const byte MOTOR_ID = 0; //ID for diferentiating data recieved and sent over serial connections
static const byte SERVO_ID = 1;

//added for motor data timeout safety feature
static const unsigned long MOTOR_DATA_TIMEOUT = 200; //4 x 50 ms (50 ms time period expected)
static unsigned long lastTimeMotorData = 0;
static const byte NEUTRAL = 90;

unsigned long last_rpm_pulse_update_ms = 0; //used for detecting a stopped car, and rejecting old data when writing to the serial port
unsigned long last_rpm_pulse_time_us = 0;//keeps track of rpms by comparing to system timer
static const long REV_PERIOD_MAX_US = 100000; //in us
unsigned long rev_period = REV_PERIOD_MAX_US; //100 ms is considered too long to be in motion
boolean forward = true;
/*Scratch that, I want these parameters set in ROS:
static const float wheel_radius = 0.05 // meters
static const float revs_to_mps_MUL = //assuming 2.85 gear ratio for brushless motor differential: https://forums.traxxas.com/showthread.php?9080733-Diff-gear-ratios
*/
//boolean rpm_period_updated = false; //rpms must be updated every 100 ms, otherwise the car has stopped, and velocity data should show 0 m/s

void rpm_feedback()

//Serial.println("in rpm_feedback");
last_rpm_pulse_update_ms = millis(); //notice the 'ms' here we want to use millisecond for checking whether or not data is vallid. millis() can count up to 50 days while micros() only counts up to 70 minutes, thus millis() is used here.
unsigned long time_now = micros(); //use time now for accurate time calculations
unsigned long rev_period_temp = time_now - last_rpm_pulse_time_us; //get spur-gear revoltion period
if(rev_period_temp > 0) rev_period = rev_period_temp; //revs are within
else rev_period = REV_PERIOD_MAX_US;

last_rpm_pulse_time_us = time_now; //using 'time_now' ensures that the time taken to get to this point in code does not interfere with rev_period accuracy - - - micros(); //reset time
if(pos[MOTOR_ID] < 90) //determine the direction that the vehicle is traveling in

forward = false;
else forward = true;

//rpm_period_updated = true; not needed, only last_rpm_pulse_time_ms is needed for checking


void setup()
// put your setup code here, to run once:

pinMode(RPM_FEEDBACK_PIN, INPUT_PULLUP);
attachInterrupt(RPM_FEEDBACK_PIN, rpm_feedback,FALLING); //arduino reference recomends using digitalPinToInterrupt(RPM_FEEDBACK_PIN) but the command is not recognized here

analogReference(EXTERNAL); //Using external reference for servo position
for(int i = 0; i < N; i++) //initialize

pos[i] = 90;
servo[i].attach(ESC_PIN + i);

Serial.begin(115200);


void loop()
// put your main code here, to run repeatedly:
if(Serial.available() >= 1)

if(Serial.read() == 'G')

unsigned long t = millis();
while((Serial.available() < 3) && ((millis() - t) < 10)); //wait for the rest of the package, or timeout
if(Serial.available() >= 3)

char buf[3];
Serial.readBytes(buf, 3);
if((buf[0] == 'O') && (buf[1] >= 0) && (buf[1] < 2))

pos[buf[1]] = byte(buf[2]);
if(buf[1] == MOTOR_ID) lastTimeMotorData = millis(); //time stamp of last motor data retreival
//Serial.print("buf[2]: ");
//Serial.println(byte(buf[2]), DEC);
//Serial.print("pos: ");
//Serial.println(pos[buf[1]]);





if((millis() - lastTimeMotorData) > MOTOR_DATA_TIMEOUT) pos[MOTOR_ID] = NEUTRAL; //stop the motor if data is not being received

for(int i = 0; i < N; i++)

servo[i].write(pos[i]);

if((millis() - lastTime_servoFeedback) >= 50) // 20Hz 20) //50Hz matches current ROS driver settings

lastTime_servoFeedback = millis();
int servo_feedback = analogRead(SERVO_FEEDBACK_PIN);
Serial.write('G'); //PID
Serial.write('O');
Serial.write(SERVO_ID);
//Serial.print(servo_feedback);
Serial.write(lowByte(servo_feedback));
Serial.write(highByte(servo_feedback));

//Serial.println(servo_feedback);

float rev_frequency;
if((last_rpm_pulse_update_ms + 100) < millis()) rev_frequency = 0; //use millis() since it can count up to 50 days, and will not have a chance of a hiccup after 70 minutes of using micros()
//instead, correct period when slowing down, also stop when the maximum threshold is reached
//if((micros() - last_rpm_pulse_time_us) >= REV_PERIOD_MAX_US) rev_frequency = 0; //car is stopped in this case. I decided not to try correcting the period as mentioned above
else rev_frequency = (float) 1/rev_period*1000000;
byte *rev_freq_bytes_to_transmit = (byte *) &rev_frequency;
if(forward == false) rev_frequency = -rev_frequency; //a negative frequency is used for reverse
Serial.write('G'); //PID
Serial.write('O');
Serial.write(MOTOR_ID); //used for addressing
Serial.write(rev_freq_bytes_to_transmit, 4);





Some good information may be:


snuc@usuavc:~$ udevadm info -a -n /dev/ttyACM0

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/tty/ttyACM0':
KERNEL=="ttyACM0"
SUBSYSTEM=="tty"
DRIVER==""

looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0':
KERNELS=="1-4:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="cdc_acm"
ATTRSauthorized=="1"
ATTRSbAlternateSetting==" 0"
ATTRSbInterfaceClass=="02"
ATTRSbInterfaceNumber=="00"
ATTRSbInterfaceProtocol=="01"
ATTRSbInterfaceSubClass=="02"
ATTRSbNumEndpoints=="01"
ATTRSbmCapabilities=="6"
ATTRSsupports_autosuspend=="1"

looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-4':
KERNELS=="1-4"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRSauthorized=="1"
ATTRSavoid_reset_quirk=="0"
ATTRSbConfigurationValue=="1"
ATTRSbDeviceClass=="02"
ATTRSbDeviceProtocol=="00"
ATTRSbDeviceSubClass=="00"
ATTRSbMaxPacketSize0=="8"
ATTRSbMaxPower=="100mA"
ATTRSbNumConfigurations=="1"
ATTRSbNumInterfaces==" 2"
ATTRSbcdDevice=="0001"
ATTRSbmAttributes=="c0"
ATTRSbusnum=="1"
ATTRSconfiguration==""
ATTRSdevnum=="4"
ATTRSdevpath=="4"
ATTRSidProduct=="0043"
ATTRSidVendor=="2341"
ATTRSltm_capable=="no"
ATTRSmanufacturer=="Arduino (www.arduino.cc)"
ATTRSmaxchild=="0"
ATTRSquirks=="0x0"
ATTRSremovable=="removable"
ATTRSserial=="55330313635351207081"
ATTRSspeed=="12"
ATTRSurbnum=="6990"
ATTRSversion==" 1.10"

looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1':
KERNELS=="usb1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRSauthorized=="1"
ATTRSauthorized_default=="1"
ATTRSavoid_reset_quirk=="0"
ATTRSbConfigurationValue=="1"
ATTRSbDeviceClass=="09"
ATTRSbDeviceProtocol=="01"
ATTRSbDeviceSubClass=="00"
ATTRSbMaxPacketSize0=="64"
ATTRSbMaxPower=="0mA"
ATTRSbNumConfigurations=="1"
ATTRSbNumInterfaces==" 1"
ATTRSbcdDevice=="0415"
ATTRSbmAttributes=="e0"
ATTRSbusnum=="1"
ATTRSconfiguration==""
ATTRSdevnum=="1"
ATTRSdevpath=="0"
ATTRSidProduct=="0002"
ATTRSidVendor=="1d6b"
ATTRSinterface_authorized_default=="1"
ATTRSltm_capable=="no"
ATTRSmanufacturer=="Linux 4.15.0-32-generic xhci-hcd"
ATTRSmaxchild=="12"
ATTRSproduct=="xHCI Host Controller"
ATTRSquirks=="0x0"
ATTRSremovable=="unknown"
ATTRSserial=="0000:00:14.0"
ATTRSspeed=="480"
ATTRSurbnum=="76"
ATTRSversion==" 2.00"

looking at parent device '/devices/pci0000:00/0000:00:14.0':
KERNELS=="0000:00:14.0"
SUBSYSTEMS=="pci"
DRIVERS=="xhci_hcd"
ATTRSbroken_parity_status=="0"
ATTRSclass=="0x0c0330"
ATTRSconsistent_dma_mask_bits=="64"
ATTRSd3cold_allowed=="1"
ATTRSdbc=="disabled"
ATTRSdevice=="0x9d2f"
ATTRSdma_mask_bits=="64"
ATTRSdriver_override=="(null)"
ATTRSenable=="1"
ATTRSirq=="122"
ATTRSlocal_cpulist=="0-7"
ATTRSlocal_cpus=="ff"
ATTRSmsi_bus=="1"
ATTRSnuma_node=="-1"
ATTRSrevision=="0x21"
ATTRSsubsystem_device=="0x2070"
ATTRSsubsystem_vendor=="0x8086"
ATTRSvendor=="0x8086"

looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""





Post the code please . What are you using for handling serial port ? Rosserial??
– Mohammad Ali
Aug 28 at 1:29





Sorry, I have added the code now
– Raisintoe
Aug 28 at 3:02





Does Your Programm Can Write As Root using sudo On NUC?? And How Do You Know It's Not writing? You Are not Checking the returned size_t From Write Function
– Mohammad Ali
Aug 28 at 3:27



sudo


size_t





The Arduino RX LED does not light up, this is how I know nothing is being sent. I tried now as you suggested, and the ser.write(byte) command return 0 every write.
– Raisintoe
Aug 28 at 3:33






Your Permissions Looks Fine From What I see. You Can Double Check That With ls -l /dev/ttyACM0 and u should see something like 110110110 for "0666".if its not you can manually make it Writable using chmod +w /dev/ttyACM0 and check again
– Mohammad Ali
Aug 28 at 3:44



ls -l /dev/ttyACM0


110110110


chmod +w /dev/ttyACM0




1 Answer
1



I decided that the problem was with the ROS version of serial. I decided to try some native linux library, termios, and had success writing to the port!



I found this example code:
https://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux



The problem lies in the ros serial installation somehow.





Beware the low quality of that example code. It even admits that "it lacks error handling, doesn't buffer data, and uses very inefficient polling, wasting lot of CPU cycles." Nor is it robust code that will perform well in all situations. Instead see Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systems
– sawdust
Aug 28 at 8:27






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)