Table of contents
Exercise: Implement the poll function to communicate with the slave
Modbus Slave & Poll
1. Download the software and install it by default
2. Crack, click connection->connect, enter the serial number
3. use
set first
Then connect ( note that the Slave end is turned on first, and the Poll end is turned on later )
View host ip address
win+R key, enter ipconfig
Network debugging assistant
Note that the protocol is sent correctly, note that the register type must match, connect first and then send
Wireshark uses
Capture selection:
If windows is connected to a wired network, select Local Area Connection/Ethernet
If connected to a wireless network, select WLAN
If it is only communication on this machine, choose NPCAP Loopback apdater
filter criteria:
Filter port: tcp.port == 502
Filter ip address: ip.addr == 192.168.xx.xx (set according to your own ip address under Windows)
Exercise: Implement the poll function to communicate with the slave
Read holding registers: function parameters (register start address, number of registers, slave ID)
#include <stdio.h>
#include <stdlib.h> // atoi
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
int sockfd; //定义文件描述符
void set_slave_id(uint8_t *p, int id) //设置从机id
{
*(p + 6) = id;
}
//读保持寄存器 (发送数据首地址, 功能码, 寄存器地址,寄存器数量,存放接受数据首地址)
void read_registers(uint8_t *p, int function, int addr, int nb, uint8_t *dest)
{
int i;
*(p + 5) = 6; //后面字节数
*(p + 7) = (char)function; //功能码
*(p + 8) = addr >> 8; //寄存器高字节地址
*(p + 9) = addr & 0xff; //寄存器低字节地址
*(p + 10) = nb >> 8; //寄存器数量高位
*(p + 11) = nb & 0xff; //寄存器数量低位
/*
1101 0101 1010 1001
addr>>8 0000 0000 1101 0101 高字节地址
addr&0xff 1101 0101 1010 1001
& 0000 0000 1111 1111
= 0000 0000 1010 1001 低字节地址
*/
send(sockfd, p,12,0);//注意这里不能sizeof(p),p为指针
recv(sockfd, dest,64,0);//注意这里不能sizeof(dest),dest为指针
}
void write_coil(uint8_t *p, int function, int addr, int nb, uint8_t *dest)
{
int i = 0;
*(p + 5) = 6; //后面字节数
*(p + 7) = (char)function; //功能码
*(p + 8) = addr >> 8; //线圈高位地址
*(p + 9) = addr & 0xff; //线圈低位地址
if (nb == 1)
*(p + 10) = 0xff;
else if (nb == 0)
*(p + 10) = 0x00;
*(p + 11) = 0x00;
send(sockfd, p, 12, 0);
recv(sockfd, dest, 64, 0);
}
int main(int argc, char const *argv[])
{
struct sockaddr_in s;
uint8_t data[12] = {0};
uint8_t dest[64] = {0};
int i;
//1.socket创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
socklen_t len = sizeof(struct sockaddr_in);
//2.填充结构体
s.sin_family = AF_INET; //协议族
s.sin_port = htons(atoi(argv[2])); // htons:小端转大端 atoi:将数字字符串转换为数值
s.sin_addr.s_addr = inet_addr(argv[1]); //字符串转点分十进制
//3.connect请求连接
if (connect(sockfd, (struct sockaddr *)&s, len) < 0)
{
perror("connect err");
return -1;
}
//4.设置从机ID
set_slave_id(data, 1);
//5.循环发送
while (1)
{
printf("开始读\n");
read_registers(data, 0x03, 0, 2, dest);
printf("recv data:");
for (i = 0; i < dest[8]; i++)
printf("%d ", dest[9 + i]);
printf("\n");
sleep(5);
write_coil(data,0x05,0,1,dest);//线圈置一
printf("线圈置位后:\n");
printf("%d %d \n",dest[10],dest[11]);
sleep(5);
}
//5.关闭套接字
close(sockfd);
return 0;
}
operation result