最近到手一款科星的2路网络继电器,官方有提供上位机方便调试,但是要运用在具体项目中就需要自己编写程序,本文基于ROS平台通过C++编写程序通过socket控制该继电器。
目录
(一)测试报文
(二)创建调试节点
(三)继电器控制节点
3.1 入口
3.2 初始化
3.2 判断调试命令
3.4 连接网络
3.5 执行命令与读取继电器状态
(四)运行结果
首先通过官方上位机对继电器继续配置,设定好继电器连接的wifi网络以及自身的ip和端口,再切换到ubuntu系统,下载安装网络调试助手,根据官方提供的指令集详解中的报文进行测试
在正式开始编写继电器的控制程序前,先创建一个调试节点,用于对继电器控制节点发送命令,控制继电器开关,通过四位数控制继电器的各个功能。
#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;
}
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();
}
重点在于初始化和订阅上面的调试节点获得命令。
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继电器,另外是定义了一个发布话题,将后面获取的继电器状态发布出去,方便在项目中应用。
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中订阅的话题消息传入这个回调函数,根据调试节点中规定的命令条件编写判断,分别进行不同功能。
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通信,在接受到连接网络的命令后创建一个套接字(目前只预留了两个位置,同时只能连接一个),后面对这个套接字进行操作,关闭网络连接时注销这个套接字。
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读取网络继电器回馈的报文,可以判断命令是否成功执行。并可以通过查询继电器状态的报文主动获取网络继电器当前的状态。
由于没有连接上网络继电器,第一次发送命令时会直接崩溃,第二次直接发送指令报文,能正确识别并提醒。