串口通信

下面我们来介绍Viobot2的串口使用

../_images/image_aL3scoT5dR.png

1.接口定义

Viobot2面有一个2*4pin的接口母座,接头我们发货的时候会放在盒子里面的。

我们可以看到第一排从左到右引脚分别是GND SCL SDA GND

第二排从左到右分别是RX TX CANL CANH

对应的设备为ttyS0,完整的串口号为/dev/ttyS0

2.硬件连接

此处使用Viobot2与windows电脑的串口调试助手作为例子。我们先拿出一个市面上常用的串口转usb模块连接好Viobot2和我们自己的电脑。GND连接好,Viobot2的RX接模块的TX,Viobot2的TX接模块的RX。

../_images/image_-2f23VsaRD.png

3.代码测试

这里我们使用比较方便的ROS串口库,当然也可以自己写串口通信,这里只是作为一个样例,尽量简单方便,因为Viobot2出厂系统是自带了ROS环境的,即使开发者在主要开发时使用ROS,也可以根据这个教程直接使用Viobot2的串口。

ROS版本

(1)安装ros-serial库

这个库前期出厂的设备没有默认安装,需要用户自己联网安装一下

  sudo apt install ros-noetic-serial

(2)编译代码

将代码放到Baton里面自己的工作空间里面编译,代码也比较简单,初始化一个串口对象,然后进到一个1hz的循环里面,循环里面读取串口接收数据和发送字符串。

#include <ros/ros.h>
#include <string>
#include <iostream>
#include <sstream>
#include <serial/serial.h>
#include <std_msgs/String.h>

serial::Serial my_serial;

int my_serial_init(const char* port,uint32_t baudrate){
    my_serial.setPort(port);
    my_serial.setBaudrate(baudrate);
    serial::Timeout timeout = serial::Timeout::simpleTimeout(1000);
    my_serial.setTimeout(timeout);
    my_serial.setParity(serial::parity_t::parity_none);
    my_serial.setBytesize(serial::bytesize_t::eightbits);
    my_serial.setFlowcontrol(serial::flowcontrol_t::flowcontrol_none);
    my_serial.setStopbits(serial::stopbits_t::stopbits_one);
    try{
        my_serial.open();
    }
    catch(const std::exception &e){
        ROS_ERROR_STREAM("Unable to open port.");
        return -1;
    }

    if(my_serial.isOpen()){
        ROS_INFO_STREAM("Serial Port is opende.\n");
    }
    else{
        ROS_ERROR_STREAM("Unable to open port.");
        return -1;
    }
    return 0;
}

int main(int argc,char **argv){
    ros::init(argc,argv,"serial_demo");
    ros::NodeHandle nh;

    my_serial_init("/dev/ttyS0",115200);

    ros::Rate loop_rate(1);
    while(ros::ok()){
        size_t n = my_serial.available();
        if(n!=0){
            std_msgs::String msg_s;
            msg_s.data = my_serial.read(my_serial.available());
            //这里是把1秒内所有接收到的数据全部打印出来
            //用户可以自定根据接收到的数据进行自己的处理
            ROS_INFO_STREAM("Reading from serial port:"<< msg_s.data);
        }
        std::string msg = "hello, this is viobot serial send message!"; 
        my_serial.write(msg.c_str());
        loop_rate.sleep();
    }
    return 0;
}

(3)效果展示

source ./devel/setup.bash
roslaunch serial_demo serial_demo.launch 

代码实现的效果比较简单,就是单纯的每秒通过串口发送一串字符串到电脑,电脑手动发送字符串给Baton,每秒钟打印一次。

../_images/image_-H8IqcGeJs.png

ROS2版本

(1)安装ros-serial库

在ROS中可以直接安装官方提供的serial库,直接可用APT下载,而在ROS2中需要自行下载源码移植

sudo apt install ros-humble-serial-driver

(2)编译代码

cd user_ws/src/serial_demo/extern_lib/serial/build
rm -r *
cmake ..
sudo make install
sudo ldconfig
cd ../../../../..
colcon build

将代码放到Baton里面自己的工作空间里面编译,代码也比较简单,初始化一个串口对象,然后进到一个1hz的循环里面,循环里面读取串口接收数据和发送字符串。

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "serial/serial.h"

class Serial_node : public rclcpp::Node{
public:
    Serial_node(const std::string& name):Node(name){
        my_serial_init("/dev/ttyS0",115200);
        timer_ = this->create_wall_timer(std::chrono::milliseconds(1000), std::bind(&Serial_node::timer_callback, this));
    }

private:

    void timer_callback(){
        std::string msg = "hello, this is viobot serial send message!"; 
        my_serial_.write(msg.c_str());
        size_t n = my_serial_.available();
        if(n!=0){
            std::string msg_s;
            msg_s = my_serial_.read(my_serial_.available());
            RCLCPP_INFO(this->get_logger(), "Reading from serial port:%s", msg_s.c_str());
        }
    }

    int my_serial_init(const char* port,uint32_t baudrate){
        my_serial_.setPort(port);
        my_serial_.setBaudrate(baudrate);
        serial::Timeout timeout = serial::Timeout::simpleTimeout(1000);
        my_serial_.setTimeout(timeout);
        my_serial_.setParity(serial::parity_t::parity_none);
        my_serial_.setBytesize(serial::bytesize_t::eightbits);
        my_serial_.setFlowcontrol(serial::flowcontrol_t::flowcontrol_none);
        my_serial_.setStopbits(serial::stopbits_t::stopbits_one);
        try{
            my_serial_.open();
        }
        catch(const std::exception &e){
            RCLCPP_ERROR(this->get_logger(),"Unable to open port.");
            return -1;
        }

        if(my_serial_.isOpen()){
            RCLCPP_INFO(this->get_logger(),"Serial Port is opende.\n");
        }
        else{
            RCLCPP_ERROR(this->get_logger(),"Unable to open port.");
            return -1;
        }
        return 0;
    }
    
    serial::Serial my_serial_;
    rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char **argv){
    rclcpp::init(argc,argv);
    auto node = std::make_shared<Serial_node>("serial_demo");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

(3)效果展示

source ./install/setup.bash
ros2 run serial_demo serial_demo 

代码实现的效果比较简单,就是单纯的每秒通过串口发送一串字符串到电脑,电脑手动发送字符串给Viobot2,每秒钟打印一次。

../_images/image_-H8IqcGeJs.png