基于Modbus协议实现远程控制和数据检测

该项目使用ModbusTCP协议,通过Web服务器实现远程对工业设备的开关控制和实时数据采集。设备不断通过Modbus发送传感器数据到服务器,用户可在网页查看。此外,文章介绍了Modbus的历史、类型及其优势,并详细描述了使用ModbusTCP进行多任务编程的流程,包括创建连接、读写操作等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目基本情况:

远程对工业现场的设备进行开关控制和实时数据采集,基于modbus tcp协议,和webserver服务器,实现客户在网页进行获取设备的传感器数据和控制设备上的开关;

基本流程:网页通过web服务器对设备发送指令:如果是收集数据:因为设备通过modbus协议不断的将即时数据发送到服务器,当服务器获取到网页端的请求是获取传感器数据时就把设备的通过http协议发送给网页,客户就可以即时查看设备数据了,如果是控制设备,在网页上输入控制命令,服务器通过modbus协议格式对设备进行控制开关。

起源:

Modbus由Modicon公司于1979年开发,是一种工业现场总线协议标准。

Modbus通信协议具有多个变种,其中有支持串口,以太网多个版本,其中最著名的是Modbus RTUModbus ASCII和Modbus TCP三种

分类:

1) Modbus RTU: 运行在串口上的协议,采用二进制表现形式以及紧凑型数据结构,通信效率高,应用广泛

2) Modbus ASCII: 运行在串口上的协议,采用ASCII码传输,并且利用特殊字符作为其字节的开始与结束标识,其传输效率要远远低于Modbus RTU协议,一般只有在通信数据量较小的情况下才考虑使用Modbus ASCII通信协议

3) Modbus TCP: 运行在以太网上的协议

为什么要使用modbus协议?

Modbus协议是现在国内工业领域应用最多协议,不只PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备

优势免费、简单、容易使用

Modbus TCP特点:(本项目使用的是modbus tcp协议)

Modbus TCP:运行在以太网上的协议:

1) 采用主机和从机之间问答方式进行通信

2) Modbus TCP是应用层协议,基于传输层TCP协议实现

3) Modbus TCP端口号默认为502

任务:

编程实现采集传感器数据和控制硬件设备(传感器和硬件通过slave模拟)

传感器:2个,光线传感器、加速度传感器(x\y\z)

硬件设备:2个,led灯、蜂鸣器

要求:

1. 多任务编程:多线程、多进程

2. 循环1s采集一次数据,并将数据打印至终端

3. 同时从终端输入指令控制硬件设备

0 1 :led灯打开

0 0:led灯关闭

1 1:蜂鸣器开

1 0 : 蜂鸣器关

编程流程

1. 创建实例

modbus_new_tcp

2. 设置从机ID

modbus_set_slave

3. 和从机进行连接

modbus_connect

4. 寄存器进行操作

功能码对应函数

5. 关闭套接字

modbus_close

6. 释放实例

modbus_free

#include <stdio.h>
#include <stdlib.h>
#include <modbus-tcp.h>
#include <modbus.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <errno.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

void *handler(void *arg);    //子线程
int create_shimd(char *buf); //创建共享内存

modbus_t *ctx = NULL;    //定义modbus类型的 指针ctx;
uint16_t dest[64] = {0}; //定义数组 接收从机的回答;
uint8_t data[64] = {0};  //定义数组 接收从机的回答;
int addr, status;

key_t key; //创建key值

int main(int argc, char const *argv[])
{

  if (argc != 3)
  {
    printf("please input%s<ip> <port>\n", argv[0]);
    return -1;
  }
  key = ftok("/home/hq/app.c", 'b');
  if (key < 0)
  {
    perror("ftok err");
    return -1;
  }

  //1.以tcp方式创建modbus实例,并初始化
  ctx = modbus_new_tcp(argv[1], atoi(argv[2]));
  if (ctx != NULL)
  {
    printf("modbus_new_tcp ok\n");
  }
  else
  {
    printf("modbus_new_tcp err\n");
    return -1;
  }

  //2.设置从机ID
  modbus_set_slave(ctx, 1);

  //3.和从机(slave)建立连接
  modbus_connect(ctx);

  //创建一个线程
  pthread_t tid;
  pthread_create(&tid, NULL, handler, NULL);
  pthread_detach(tid);
  while (1) //主线程
  {
    //创建消息队列
    struct msgbuf
    {
      long type; //暗号 ,另外的程序必须用同样type值才能接收
      int a;
      int b;
      char c[128];
    };
    struct msgbuf msg; //定义结构体变量
    int msgid;
    msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
    if (msgid < 0)
    {
      if (errno == EEXIST)
        msgid = msgget(key, 06666);
      else
      {
        perror("msgget err");
        return -1;
      }
    }               
    size_t s = sizeof(msg) - sizeof(long);               
    msgrcv(msgid, &msg, s, 0, 0); //第四个参数0:读取消息队列中第一个消息
    //写入多个连续线圈的状态
    //printf("(数字之间用1个空格隔开):\n0 1(led灯打开),0 0(led灯关闭),1 1(蜂鸣器开),1 0(蜂鸣器关)\n请输入:");
    // int num = scanf("%d %d", &addr, &status);
    modbus_write_bit(ctx, msg.a, msg.b);
    modbus_read_bits(ctx, msg.a, 1, data); //读多个线圈//此时读一个
    if (msg.a == 0)
      sprintf(msg.c,"led灯的状态为:(1开,0关):%d\n\n", data[0]);
    else if (msg.a == 1)
    {
      sprintf(msg.c,"蜂鸣器的状态为:(1开,0关):%d\n\n", data[0]);
    }

      msgsnd(msgid, &msg, s, 0);//0 阻塞 把控制后的数据发回浏览器
  }
  // pthread_join(tid,NULL);//阻塞,等待子线程结束后回收子线程资源后不阻塞 主线程才能执行剩余代码退出
  modbus_close(ctx); //关闭套接字,先关闭,后释放(因为sockfd保存在实例空间里,先释放就不知道sockfd描述符是什么了)
  modbus_free(ctx);  //释放modbus实例

  return 0;
}

void *handler(void *arg) //子线程
{

  while (1)
  {
    //读取003H保持寄存器的值,可读取多个连续保持寄存器的值
    int ret = modbus_read_registers(ctx, 0, 4, dest);
    printf("成功读取到%d个保持寄存器数据\n", ret);
    printf("光线:%#04x 加速度x:%#04x 加速度y:%#04x 加速度z:%#04x",
           dest[0], dest[1], dest[2], dest[3]);
    printf("\n");
    char buf[128];
    sprintf(buf, "光线:%#04x 加速度x:%#04x 加速度y:%#04x 加速度z:%#04x",
            dest[0], dest[1], dest[2], dest[3]);
    create_shimd(buf);
    sleep(5);
  }
  pthread_exit(NULL);
}
int create_shimd(char *buf) //创建共享内存
{
  //1.2创建共享内存
  int shmid;
  shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
  if (shmid < 0)
  {
    if (errno == EEXIST)
      shmid = shmget(key, 128, 0666);
    else
    {
      perror("shmget err");
      return -1;
    }
  }
  char *m = NULL;
  //映射共享内存
  m = (char *)shmat(shmid, NULL, 0);
  //读取共享内存的数据
  strcpy(m, buf);
  return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值