【问题标题】:Can read, but cannot write serial ports on Ubuntu 16.04在 Ubuntu 16.04 上可以读取,但不能写入串行端口
【发布时间】:2018-08-28 00:53:47
【问题描述】:

我在尝试将数据写入串行端口时遇到了一个奇怪的问题。 我在 NUC7i7DNBE 上运行 Ubuntu 16.04,并试图与 Arduino UNO 进行串行连接。我正在使用的串行 API 可以在这里找到:http://docs.ros.org/kinetic/api/serial/html/classserial_1_1Serial.html

我编写了一个简单的程序,它打开串行端口“ttyACM0”以与 arduino 通信。我已经在另一台运行 Ubuntu 16.04 的计算机上测试了这段代码,一切正常,这是我必须设置的唯一权限,将用户添加到拨出组。

但是,在 NUC 上,我已将用户添加到拨出组。这允许程序从 Arduino 读取,但它仍然不写入 Arduino。 Arduino IDE 可以正常写入 Arduino,但我的程序不会。

我假设我在 Ubuntu 中遇到串行写入权限问题。

我已采取的步骤:

  • 我已将用户添加到拨出组
  • 我在 /etc/udev/rules.d/ 中添加了一条规则:

    SUBSYSTEMS=="tty", GROUP="dialout", MODE="0666"
    
  • 之后,我发送了命令:

    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
    

我按照堆栈交换上的一些信息来了解这一点: https://unix.stackexchange.com/questions/111593/allow-non-root-user-to-read-write-dev-files

  • 我尝试使用 FTDI 设备写入 Arduino 端口。 FTDI 设备使用 ttyUSB0 端口而不是 ttyACM0 端口。结果是一样的;能读,不能写。

  • 我还在 NUC 上运行了我的外部硬盘驱动器,以查看是否存在任何类型的硬件问题。当我从外部硬盘驱动器运行程序时,我可以毫无问题地读取和写入 Arduino。

一般来说,我没有过多处理 Ubuntu 权限或端口,请帮助我查找并上传您可能需要的任何其他信息,以帮助我解决此问题。

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);

}

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 differentiating data received 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 valid. 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 revolution 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 recommends 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 retrieval
          //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);

  }
}

一些好的信息可能是:

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"
    ATTRS{authorized}=="1"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceClass}=="02"
    ATTRS{bInterfaceNumber}=="00"
    ATTRS{bInterfaceProtocol}=="01"
    ATTRS{bInterfaceSubClass}=="02"
    ATTRS{bNumEndpoints}=="01"
    ATTRS{bmCapabilities}=="6"
    ATTRS{supports_autosuspend}=="1"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-4':
    KERNELS=="1-4"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{authorized}=="1"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bDeviceClass}=="02"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bMaxPacketSize0}=="8"
    ATTRS{bMaxPower}=="100mA"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bNumInterfaces}==" 2"
    ATTRS{bcdDevice}=="0001"
    ATTRS{bmAttributes}=="c0"
    ATTRS{busnum}=="1"
    ATTRS{configuration}==""
    ATTRS{devnum}=="4"
    ATTRS{devpath}=="4"
    ATTRS{idProduct}=="0043"
    ATTRS{idVendor}=="2341"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="Arduino (www.arduino.cc)"
    ATTRS{maxchild}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{removable}=="removable"
    ATTRS{serial}=="55330313635351207081"
    ATTRS{speed}=="12"
    ATTRS{urbnum}=="6990"
    ATTRS{version}==" 1.10"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1':
    KERNELS=="usb1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{authorized}=="1"
    ATTRS{authorized_default}=="1"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bDeviceProtocol}=="01"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{bMaxPower}=="0mA"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bcdDevice}=="0415"
    ATTRS{bmAttributes}=="e0"
    ATTRS{busnum}=="1"
    ATTRS{configuration}==""
    ATTRS{devnum}=="1"
    ATTRS{devpath}=="0"
    ATTRS{idProduct}=="0002"
    ATTRS{idVendor}=="1d6b"
    ATTRS{interface_authorized_default}=="1"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="Linux 4.15.0-32-generic xhci-hcd"
    ATTRS{maxchild}=="12"
    ATTRS{product}=="xHCI Host Controller"
    ATTRS{quirks}=="0x0"
    ATTRS{removable}=="unknown"
    ATTRS{serial}=="0000:00:14.0"
    ATTRS{speed}=="480"
    ATTRS{urbnum}=="76"
    ATTRS{version}==" 2.00"

  looking at parent device '/devices/pci0000:00/0000:00:14.0':
    KERNELS=="0000:00:14.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="xhci_hcd"
    ATTRS{broken_parity_status}=="0"
    ATTRS{class}=="0x0c0330"
    ATTRS{consistent_dma_mask_bits}=="64"
    ATTRS{d3cold_allowed}=="1"
    ATTRS{dbc}=="disabled"
    ATTRS{device}=="0x9d2f"
    ATTRS{dma_mask_bits}=="64"
    ATTRS{driver_override}=="(null)"
    ATTRS{enable}=="1"
    ATTRS{irq}=="122"
    ATTRS{local_cpulist}=="0-7"
    ATTRS{local_cpus}=="ff"
    ATTRS{msi_bus}=="1"
    ATTRS{numa_node}=="-1"
    ATTRS{revision}=="0x21"
    ATTRS{subsystem_device}=="0x2070"
    ATTRS{subsystem_vendor}=="0x8086"
    ATTRS{vendor}=="0x8086"

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

【问题讨论】:

  • 请贴出代码。你用什么来处理串口?罗塞里亚尔??
  • 对不起,我已经添加了代码
  • 您的程序是否可以在 NUC 上使用 sudo 以 root 身份写入?你怎么知道它不是在写?您没有检查从写入函数返回的size_t
  • Arduino RX LED 不亮,这就是我知道什么都没有发送的原因。我现在按照你的建议尝试了,ser.write(byte) 命令每次写入都返回 0。
  • 从我看到的情况来看,您的权限看起来不错。您可以使用ls -l /dev/ttyACM0 仔细检查,您应该看到类似110110110 的“0666”。如果不是,您可以使用chmod +w /dev/ttyACM0 手动使其可写并再次检查

标签: serial-port ubuntu-16.04 arduino-uno ros tty


【解决方案1】:

我认为问题出在串行的 ROS 版本上。我决定尝试一些原生 linux 库 termios,并成功写入端口!

我找到了这个示例代码: https://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux

问题出在不知怎么的ros串口安装上。

【讨论】:

【解决方案2】:

不知道你是否还想用serial/serial.h解决这个问题,但我认为你的问题可能出在超时设置上。

我告诉你这个,因为我遇到了完全相同的问题,我可以读取传入的数据,但无法写入。

/dev/ttyUSB0 权限正常,但没有超时。

我在互联网上找到了以下配置,试了一下。现在我可以读写了。

try{
    ser.setPort("/dev/ttyUSB0");
    ser.setBaudrate(9600);
    serial::Timeout to = serial::Timeout::simpleTimeout(10);
    ser.setTimeout(to);
    ser.open();

    return true;
}
catch (SerialException e) {
    return 0;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    • 2014-10-01
    • 2014-04-07
    相关资源
    最近更新 更多