Может читать, но не может записывать последовательные порты в Ubuntu 16.04 - PullRequest
0 голосов
/ 28 августа 2018

У меня странная проблема с попыткой записи данных в последовательный порт. Я использую Ubuntu 16.04 на NUC7i7DNBE и пытаюсь установить последовательное соединение с 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==""

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Не знаю, хотите ли вы решить эту проблему с помощью 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;
0 голосов
/ 28 августа 2018

Я решил, что проблема была в ROS-версии сериала. Я решил попробовать какую-нибудь нативную библиотеку linux, termios, и успешно записал в порт!

Я нашел этот пример кода: https://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux

Проблема заключается как-то в последовательной установке ros.

...