[C++/ROS]通过socket控制科星WIFI继电器

        最近到手一款科星的2路网络继电器,官方有提供上位机方便调试,但是要运用在具体项目中就需要自己编写程序,本文基于ROS平台通过C++编写程序通过socket控制该继电器。

[C++/ROS]通过socket控制科星WIFI继电器_第1张图片

目录

(一)测试报文

(二)创建调试节点

(三)继电器控制节点

3.1 入口

3.2 初始化

3.2 判断调试命令

3.4 连接网络

3.5 执行命令与读取继电器状态

(四)运行结果


(一)测试报文

        首先通过官方上位机对继电器继续配置,设定好继电器连接的wifi网络以及自身的ip和端口,再切换到ubuntu系统,下载安装网络调试助手,根据官方提供的指令集详解中的报文进行测试

[C++/ROS]通过socket控制科星WIFI继电器_第2张图片

(二)创建调试节点

        在正式开始编写继电器的控制程序前,先创建一个调试节点,用于对继电器控制节点发送命令,控制继电器开关,通过四位数控制继电器的各个功能。

#include 
#include "ros/ros.h"
#include "std_msgs/Int64.h"

int main(int argc, char *argv[]){
    setlocale(LC_ALL, "");

    ros::init(argc, argv, "manage");
    ros::NodeHandle nh;
    ros::Publisher manage_pub = nh.advertise("io_cmd", 10);
    ros::Rate loop_rate(10);

    std::cout << "选择输入: 编号+控制字,例如1001" << std::endl;
    std::cout << "请先输入  x+000  连接网络" << std::endl;
    std::cout << "001      ——打开DO1"<< std::endl;
    std::cout << "002       ——关闭DO1"<< std::endl;
    std::cout << "010       ——打开DO2"<< std::endl;
    std::cout << "020      ——关闭DO2"<< std::endl;
    std::cout << "011       ——全开"<< std::endl;
    std::cout << "022       ——全闭"<< std::endl;
    std::cout << "066      ——读取继电器状态"<< std::endl;
    std::cout << "077      ——读取开光量状态"<< std::endl;
    std::cout << "099      ——断开连接"<< std::endl;

    while (ros::ok())
    {
        std_msgs::Int64 msg;
        std::cout << "命令输入:";
        std::cin >>  msg.data;
        manage_pub.publish(msg);

        ros::spinOnce();
        loop_rate.sleep();

        }
    return 0;
}

(三)继电器控制节点

3.1 入口

int main(int argc, char *argv[]) {
    setlocale(LC_ALL, "");

    ros::init(argc, argv, "relay");
    ros::NodeHandle nh("~");

    init(nh);
    ros::Subscriber relay_sub = nh.subscribe("/io_cmd", 10, flag_message);

    ros::spin();
}

           重点在于初始化和订阅上面的调试节点获得命令。

3.2 初始化

void init(ros::NodeHandle &nh)
{
    nh.param("relay_addr_1", relay_addr_1, "192.168.1.18");
    nh.param("relay_addr_2", relay_addr_2, "192.168.1.19");

    relay_pub = nh.advertise("status_out", 10);

    ROS_INFO("Relay node initialization completed!");
}

        这里的初始化比较简单,主要是通过param传入多个继电器的ip,实现一次性通讯多个WIFI继电器,另外是定义了一个发布话题,将后面获取的继电器状态发布出去,方便在项目中应用。

3.2 判断调试命令

void flag_message(const std_msgs::Int64::ConstPtr& manage)
{
    int control_msg = manage->data;
    int relay_num = (int)control_msg/1000;
    // std::cout << relay_num <

        将main中订阅的话题消息传入这个回调函数,根据调试节点中规定的命令条件编写判断,分别进行不同功能。

3.4 连接网络

void recovery(const int &relay_num){
    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
        exit(0);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    if(relay_num == 1){
        servaddr.sin_addr.s_addr = inet_addr( relay_addr_1.c_str());
        servaddr.sin_port = htons(50000);
    }else if(relay_num == 2){
        servaddr.sin_addr.s_addr = inet_addr( relay_addr_2.c_str());
        servaddr.sin_port = htons(50000);  
    }else{
        ROS_WARN("WIFI relay No. %u failed to connect to the network",relay_num);
        return;
    }

    if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    {
        printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
        exit(0);
    }

    network_connections = true;

    ROS_INFO("%u号继电器连接网络",relay_num);
}

        继电器与主机的通讯依靠socket通信,在接受到连接网络的命令后创建一个套接字(目前只预留了两个位置,同时只能连接一个),后面对这个套接字进行操作,关闭网络连接时注销这个套接字。

3.5 执行命令与读取继电器状态

void send_relay_msg(const buff_send &relay_msg){
    send(sockfd, relay_msg.msgs, sizeof(relay_msg),0);
}

void read_out_status(const int &relay_num){
    unsigned char buff_recv[4096];

    int t = recv(sockfd, buff_recv, MAXLINE, 0);
    if(t > 0)
    {
        int status_bit = (unsigned int)buff_recv [5];
        switch(status_bit){
        case 0:
            send_topic_msg(relay_num*1000 + 1);
            ROS_INFO("DO1 OFF,DO2 OFF");
            break;
        case 1:
            send_topic_msg(relay_num*1000 + 2);
            ROS_INFO("DO1 ON,DO2 OFF");
            break;
        case 2:
            send_topic_msg(relay_num*1000 + 3);
            ROS_INFO("DO1 OFF,DO2 ON");
            break;
        case 3:
            send_topic_msg(relay_num*1000 + 4);
            ROS_INFO("DO1 ON,DO2 ON");
            break;
        default:
            ROS_WARN("[WIFI relay]Abnormal WIFI relay status!");
            return;
    }
    }else{
        ROS_WARN("[WIFI relay]Abnormal reading of WIFI relay data status!");
    }
}

void read_in_status(const int &relay_num){
    unsigned char buff_recv[4096];

    int t = recv(sockfd, buff_recv, MAXLINE, 0);
    if(t > 0)
    {
        int status_bit = (unsigned int)buff_recv [5];
        switch(status_bit){
        case 0:
            send_topic_msg(relay_num*1000 + 10);
            ROS_INFO("DI1 OFF,DI2 OFF");
            break;
        case 1:
            send_topic_msg(relay_num*1000 + 20);
            ROS_INFO("DI1 ON,DI2 OFF");
            break;
        case 2:
            send_topic_msg(relay_num*1000 + 30);
            ROS_INFO("DI1 OFF,DI2 ON");
            break;
        case 3:
            send_topic_msg(relay_num*1000 + 40);
            ROS_INFO("DI1 ON,DI2 ON");
            break;
        default:
            ROS_WARN("[WIFI relay]Abnormal WIFI relay status!");
            return;
    }
    }else{
        ROS_WARN("[WIFI relay]Abnormal reading of WIFI relay data status!");
    }
}

void read_feedback_status(const int &relay_num){
    char buff_recv[4096];

    int t = recv(sockfd, buff_recv, MAXLINE, 0);
    if(t > 0){
        send_topic_msg(relay_num*1000 + 88);// x088 继电器执行成功反馈
        // std::cout << buff_recv << std::endl;
        ROS_INFO("Sending msg :%c%c!",buff_recv[0],buff_recv[1]);
    }
}

在完成3.2的判断之后,将下发的命令对应的报文通过socket的send发送到网络继电器,同时通过read读取网络继电器回馈的报文,可以判断命令是否成功执行。并可以通过查询继电器状态的报文主动获取网络继电器当前的状态。

(四)运行结果

[C++/ROS]通过socket控制科星WIFI继电器_第3张图片

由于没有连接上网络继电器,第一次发送命令时会直接崩溃,第二次直接发送指令报文,能正确识别并提醒。

你可能感兴趣的:(c++,开发语言)