WebServer industrial data collection system based on modbus

1. Introduction

In the field of industrial automation, data collection is a very important part. Through data collection, storage and display of industrial equipment, the production process can be monitored and optimized to improve production efficiency and quality. As a communication protocol, Modbus is widely used for data transmission in industrial control systems. Therefore, industrial data acquisition systems based on Modbus have become a common solution.

2, 1 Classification

  1. Modbus TCP: Modbus TCP is a Modbus transmission method based on TCP/IP protocol. It transmits data through Ethernet, uses the standard Modbus protocol format, and encapsulates Modbus data in TCP/IP messages for transmission. Modbus TCP supports higher communication speeds and larger data volumes, and can realize distributed control and monitoring through network connections.

  2. Modbus ASCII: Modbus ASCII is a character-based Modbus transmission method. It uses ASCII code to represent data and transmits it through the serial port. In Modbus ASCII, each byte is represented by two ASCII characters, so the transfer rate is slower. Modbus ASCII has better readability, but is less efficient for transmitting large amounts of data.

  3. Modbus RTU: Modbus RTU is a binary-based Modbus transmission method. It uses binary encoding to represent data and transmit it through the serial port. Modbus RTU has a higher transmission rate and higher transmission efficiency than Modbus ASCII because it directly transmits binary data without the need for ASCII code conversion. Modbus RTU is usually used for short-distance data transmission in a serial communication environment.

2, 2 Features

  • Modbus TCP is suitable for high-speed, long-distance communication Ethernet environments and supports multi-master and multi-slave communication.
  • Modbus ASCII is suitable for low-speed, short-distance serial communication environments and is easy to debug and diagnose.
  • Modbus RTU is suitable for serial communication environments with medium speed and distance, and has high efficiency, real-time performance and reliability.

3. System Design

3.1 Architecture Block Diagram

3, 2 process explanation

1. Manually enter the settings, data, etc. to configure the Modbus slave.

2. The Modbus host adopts multi-threading technology to achieve separation of reading and writing. The Modbus host collects data once per second, and then uses IPC such as shared memory to collect the data and share the data with the web server.

3. The webpage can click or send a GET or POST request to request the Web server to respond to the corresponding operation. If the webpage wants to turn off a light, that is to say, it wants to operate the coil of the slave machine, then it needs to send the request through the http protocol first, and the Webserver receives it. After receiving the data, perform corresponding judgment and processing, and send the data request to the Modbus host through the message queue. After the host receives the data, it operates the slave coil through the ModbusTCP communication protocol format to control the lights off through the web page.

4. Code analysis

Create shared memory code

struct shm *create_shared_memory()
{

    key_t key = ftok(".", 'a'); // 1. Create key
    if (key < 0)
    {
        perror("key err");
        exit(1);
    }

    int shmid = shmget(key, 128, IPC_CREAT | 0666); // 2. Create or open shared memory
    if (shmid < 0)
    {
        perror("shmget err");
        exit(1);
    }

    struct shm *p = shmat(shmid, NULL, 0); // 3. Mapping
    if (p == (struct shm *)-1)
    {
        perror("shmat err");
        exit(1);
    }

    return p;
}

4. 1 host collects data

void *handler(void *arg)
{
    struct shm *p = create_shared_memory();

    while (1)
    {
        modbus_read_registers(ctx, 0, 4, dest); // 4. Call the function corresponding to the function code
        sprintf(p->buf, "Light sense:%d x:%d y:%d z:%d", dest[0], dest[1], dest[2], dest[3]); // Print content
        sleep(1);
    }

    shmdt(p);
    pthread_exit(NULL);
}

4, 2 Host Write Coil

 ctx = modbus_new_tcp("192.168.0.83", 502); // 1. Create an instance
    modbus_set_slave(ctx, 1); // 2. Set slave ID
    modbus_connect(ctx); // 3. Connect

    pthread_t tid; // Create thread
    if (pthread_create( & amp;tid, NULL, handler, NULL))
    {
        perror("create err");
        return -1;
    }
    pthread_detach(tid); // No blocking, automatic recycling when the thread ends

    struct shm *p = create_shared_memory();
    p->flag = 1; // read and write

    while (1)
    {
        int i = p->con[0] - 48;
        int j = p->con[1] - 48;

        if (p->flag == 1 & amp; & amp; i >= 0 & amp; & amp; j >= 0)
        {
            switch(i)
            {
            case 0:
                modbus_write_bit(ctx, 0, j); // 4. Call the function corresponding to the function code
                break;
            case 1:
                modbus_write_bit(ctx, 1, j); // 4. Call the function corresponding to the function code
                break;
            default:
                printf("Input error\\
");
                break;
            }
            p->flag = 0;
        }
    }
    shmdt(p); // 4. Unmap
    modbus_close(ctx); // 5. Close the socket
    modbus_free(ctx); // 6. Release the instance

4. 3 Webserver responds to the web page to collect data

static int modbus_get(int sock, const char *input)
{
    struct shm *p = create_shared_memory();
    // read
    send(sock, p->buf, strlen(p->buf), 0);
    printf("%s\\
", p->buf);
    // 4. Unmap
    shmdt(p);
}

4. 4 Webserver responds to web page operations

static int modbus_set(int sock, const char *input)
{
    struct shm *p = create_shared_memory();
    // read and write
    p->flag = 0;
    if (p->flag == 0)
    {
        sprintf(p->con, "%c%c", input[11], input[13]);
        send(sock, p->con, strlen(p->con), 0);
        p->flag = 1;
    }
    // 4. Unmap
    shmdt(p);
}

4, 5 HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>System</title>
    <h1 align="center">WebServer industrial data collection system</h1>
    <script>
        function get_info() {
            //Get the label named username
            var data = document.getElementsByName("data");
            //Get the value attribute value of the first tag
            // v[0].value = "hello";
            var xhr = new XMLHttpRequest();//Create an XMLHttpRequest object

            var url = "";

            xhr.open("post", url, true);//Open the connection

            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 & amp; & amp; xhr.status === 200) {
                    data[0].value = xhr.responseText;
                }
            }
            xhr.send("modbus_get");
        }
        function set_equip(name, id) {
            var xhr = new XMLHttpRequest();//Create an XMLHttpRequest object
            var url = "";
            xhr.open("post", url, true);//Open the connection
            if (name == 'equip_1' & amp; & id == 'OFF')
                xhr.send("modbus_set 0 0");
            else if (name == 'equip_1' & amp; & id == 'ON')
                xhr.send("modbus_set 0 1");
            if (name == 'equip_2' & amp; & id == 'OFF')
                xhr.send("modbus_set 1 0");
            else if (name == 'equip_2' & amp; & id == 'ON')
                xhr.send("modbus_set 1 1");
        }
    </script>
</head>

<body name="back" style="background-color: orange;">

    <div style="text-align: center;margin-top: 100px;">
        <!-- Data display -->
        Data: <input type="text" name="data" value=" ">
        <!-- Refresh button -->
        <input type="button" name="flash" value="Refresh" onclick="get_info()"><br>
        <!-- Device 1: Switch -->
        LED light:
        <input type="radio" name="equip_1" id="OFF" checked="default" onclick="set_equip(name,id)">OFF
        <input type="radio" name="equip_1" id="ON" onclick="set_equip(name,id)">ON<br>
        <!-- Device 2: Switch -->
        buzzer:
        <input type="radio" name="equip_2" id="OFF" checked="default" onclick="set_equip(name,id)">OFF
        <input type="radio" name="equip_2" id="ON" onclick="set_equip(name,id)">ON
    </div>
</body>

</html>