RT-Thread ART-Pi wifi 实现 libmodbus tcp 从机

验证环境

  • Win10 64位
  • Keil MDK 5.30
  • ART-Pi 开发板:STM32H750XBH6开发板
  • 工程:最小RT-Thread 系统,版本:RT-Thread v4.1.0 released

环境搭建

  • 首先需要配置好ART-Pi的WIFI,这部分配置方式可以查看前面的文章
  • 本次验证TCP 通信,基于libmodbus 实现 modbus-tcp 从机功能
  • 配置使能 libmodbus 软件包
2022-04-02_212711.png
2022-04-02_212733.png
  • 使用RT-Thread ENV 工具:pkgs --update,把软件包更新(下载)到本地 工程packages目录
  • 这里把 libmodbus 软件里面的 tcp 例程复制到工程的 applications 目录,目的是可以修改这个例程,如果在软件包中修改,不利于保存
2022-04-02_212956.png

修复编译

libmodbus软件包修改如下:

  • modbus.c 中:#include <errno.h> -> #include <sys/errno.h>
  • modbus-rtu.c中:#include <dfs_select.h> -> #include <sys/select.h>
  • modbus-private.h 中:增加 #include <sys/select.h>

测试例程

  • 这里作为从机,例程默认为从机,初步看了下,主机可以读取【线圈】与【保持寄存器】,这部分例程可以简单的修改下,利于主机验证功能
#include "modbus_tcp_test.h"
#include <modbus.h>
#include <stdio.h>
#include <string.h>
//#include <dfs_posix.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sal_socket.h>

// static void test_thread(void *param)
// {
//     uint16_t tab_reg[64] = {0};
//     modbus_t *ctx = RT_NULL;
    
//     ctx = modbus_new_tcp("192.168.1.103", 601, AF_INET);
//     modbus_set_slave(ctx, 3);
//     modbus_set_response_timeout(ctx, 0, 1000000);
// _mbtcp_start:
//     if(modbus_connect(ctx) < 0)
//         goto _mbtcp_restart;
    
//     int num = 0;
//     while (1)
//     {
//         memset(tab_reg, 0, 64 * 2);
//         int regs = modbus_read_registers(ctx, 0, 20, tab_reg);
//         if(regs < 0)
//             goto _mbtcp_restart;
//         printf("-------------------------------------------\n");
//         printf("[%4d][read num = %d]", num, regs);
//         num++;
//         int i;
//         for (i = 0; i < 20; i++)
//         {
//             printf("<%#x>", tab_reg[i]);
//         }
//         printf("\n");
//         printf("-------------------------------------------\n");
//         rt_thread_mdelay(1000);
//     }

// _mbtcp_restart:
//     //7-关闭modbus端口
//     modbus_close(ctx);
//     rt_thread_mdelay(2000);
//     goto _mbtcp_start;
    
//     //8-释放modbus资源
//     modbus_free(ctx);
// }

#define MAX_CLIENT_NUM  3
#define CLIENT_TIMEOUT  10      //单位 s

typedef struct
{
    int fd;
    rt_tick_t tick_timeout;
}client_session_t;

static void test_thread(void *param)
{
    int server_fd = -1;
    modbus_t *ctx = NULL;
    modbus_mapping_t *mb_mapping = NULL;
    client_session_t client_session[MAX_CLIENT_NUM];

    for (int i = 0; i < MAX_CLIENT_NUM; i++)
    {
        client_session[i].fd = -1;
        client_session[i].tick_timeout = rt_tick_get() + rt_tick_from_millisecond(CLIENT_TIMEOUT * 1000);
    }

    int max_fd = -1;
    fd_set readset;
    int rc;
    struct timeval select_timeout;
    select_timeout.tv_sec = 1;
    select_timeout.tv_usec = 0;

    ctx = modbus_new_tcp(RT_NULL, 1502, AF_INET);
    RT_ASSERT(ctx != RT_NULL);
    mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0,
                                    MODBUS_MAX_READ_REGISTERS, 0);
    RT_ASSERT(mb_mapping != RT_NULL);

    mb_mapping->tab_registers[0] = 'R';
    mb_mapping->tab_registers[1] = 'T';
    mb_mapping->tab_registers[2] = '-';
    mb_mapping->tab_registers[3] = 'T';
    mb_mapping->tab_registers[4] = 'h';
    mb_mapping->tab_registers[5] = 'r';
    mb_mapping->tab_registers[6] = 'e';
    mb_mapping->tab_registers[7] = 'a';
    mb_mapping->tab_registers[8] = 'd';

_mbtcp_start:
    server_fd = modbus_tcp_listen(ctx, 1);
    if (server_fd < 0)
        goto _mbtcp_restart;

    while (1)
    {
        max_fd = -1;
        FD_ZERO(&readset);
        FD_SET(server_fd, &readset);

        if(max_fd < server_fd)
            max_fd = server_fd;
        
        for (int i = 0; i < MAX_CLIENT_NUM; i++)
        {
            if(client_session[i].fd >= 0)
            {
                FD_SET(client_session[i].fd, &readset);
                if(max_fd < client_session[i].fd)
                    max_fd = client_session[i].fd;
            }
        }

        rc = select(max_fd + 1, &readset, RT_NULL, RT_NULL, &select_timeout);
        if(rc < 0)
        {
            goto _mbtcp_restart;
        }
        else if(rc > 0)
        {
            if(FD_ISSET(server_fd, &readset))
            {
                int client_sock_fd = modbus_tcp_accept(ctx, &server_fd);
                if(client_sock_fd >= 0)
                {
                    int index = -1;
                    for (int i = 0; i < MAX_CLIENT_NUM; i++)
                    {
                        if(client_session[i].fd < 0)
                        {
                            index = i;
                            break;
                        }
                    }
                    if(index >= 0)
                    {
                        client_session[index].fd = client_sock_fd;
                        client_session[index].tick_timeout = rt_tick_get() + rt_tick_from_millisecond(CLIENT_TIMEOUT * 1000);
                    }
                    else
                    {
                        close(client_sock_fd);
                    }
                }
            }

            for (int i = 0; i < MAX_CLIENT_NUM; i++)
            {
                if(client_session[i].fd >= 0)
                {
                    if(FD_ISSET(client_session[i].fd, &readset))
                    {
                        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
                        modbus_set_socket(ctx, client_session[i].fd);

                        rc = modbus_receive(ctx, query);
                        if (rc > 0)
                        {
                            mb_mapping->tab_registers[9] = rt_tick_get() % 1000;
                            mb_mapping->tab_registers[10] = rt_tick_get() / 1000;
                            rc = modbus_reply(ctx, query, rc, mb_mapping);
                            if(rc < 0)
                            {
                                close(client_session[i].fd);
                                client_session[i].fd = -1;
                            }
                            else
                            {
                                client_session[i].tick_timeout = rt_tick_get() + rt_tick_from_millisecond(CLIENT_TIMEOUT * 1000);
                            }
                        }
                        else
                        {
                            close(client_session[i].fd);
                            client_session[i].fd = -1;
                        }
                    }
                }
            }
        }

        // 客户端超时未收到数据断开
        for(int i =0;i<MAX_CLIENT_NUM;i++)
        {
            if(client_session[i].fd >= 0)
            {
                //超时
                if((rt_tick_get() - client_session[i].tick_timeout) < (RT_TICK_MAX / 2))
                {
                    close(client_session[i].fd);
                    client_session[i].fd = -1;
                }
            }
        }
    }

_mbtcp_restart:
    if(server_fd >= 0)
    {
        close(server_fd);
        server_fd = -1;
    }

    for(int i =0;i<MAX_CLIENT_NUM;i++)
    {
        if(client_session[i].fd >= 0)
        {
            close(client_session[i].fd);
            client_session[i].fd = -1;
        }
    }

    rt_thread_mdelay(5000);
    goto _mbtcp_start;

    modbus_free(ctx);
}

static int tcp_test_init(void)
{
    rt_thread_t tid;
    tid = rt_thread_create("mbtcp_test",
                           test_thread, RT_NULL,
                           2048,
                           12, 10);
    if (tid != RT_NULL)
        rt_thread_startup(tid);
    return RT_EOK;
}
//INIT_APP_EXPORT(tcp_test_init);
MSH_CMD_EXPORT(tcp_test_init, tcp_test_init);

  • 【注意点】因为使用wifi,我还没有搞清楚如何自动连接【路由器】自动上网,所以把modbus-tcp 例程改为了shell 命令,手动执行

功能验证

  • 编译通过后,下载到开发板
  • wifi 连接路由器,联网功能要通
  • 系统启动与操作步骤如下:
   ___  ______  _____         ______  _   ______  _____  _____  _____ 
  / _ \ | ___ \|_   _|        | ___ \(_)  | ___ \/  _  \/  _  \|_   _|
 / /_\ \| |_/ /  | |   ______ | |_/ / _   | |_/ /| | | || | | |  | |  
 |  _  ||    /   | |  |______||  __/ | |  | ___ \| | | || | | |  | |  
 | | | || |\ \   | |          | |    | |  | |_/ /\ \_/ /\ \_/ /  | |  
 \_| |_/\_| \_|  \_/          \_|    |_|  \____/  \___/  \___/   \_/  

 Powered by RT-Thread.

[D/drv.sdram] sdram init success, mapped at 0xC0000000, size is 33554432 bytes, data width is 16
[I/I2C] I2C bus [i2c2] registered

 \ | /
- RT -     Thread Operating System
 / | \     4.1.0 build Apr  2 2022 20:57:46
 2006 - 2022 Copyright by RT-Thread team
lwIP-2.0.3 initialized!
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/SFUD] Find a Winbond flash chip. Size is 16777216 bytes.
[I/SFUD] norflash0 flash device is initialize success.
[I/SFUD] Probe SPI flash norflash0 by SPI device spi10 success.
[D/FAL] (fal_flash_init:47) Flash device |                norflash0 | addr: 0x00000000 | len: 0x01000000 | blk_size: 0x00001000 |initialized finish.
[I/FAL] ==================== FAL partition table ====================
[I/FAL] | name       | flash_dev |   offset   |    length  |
[I/FAL] -------------------------------------------------------------
[I/FAL] | wifi_image | norflash0 | 0x00000000 | 0x00080000 |
[I/FAL] | bt_image   | norflash0 | 0x00080000 | 0x00080000 |
[I/FAL] | download   | norflash0 | 0x00100000 | 0x00200000 |
[I/FAL] | easyflash  | norflash0 | 0x00300000 | 0x00100000 |
[I/FAL] | filesystem | norflash0 | 0x00400000 | 0x00c00000 |
[I/FAL] =============================================================
[I/FAL] RT-Thread Flash Abstraction Layer initialize success.
[I/FAL] The FAL block device (filesystem) created successfully
filesystem mount to '/'
msh />
msh />
msh />
msh />[I/WWD] wifi initialize done. wiced version 3.3.1
[I/WLAN.dev] wlan init success
[I/WLAN.lwip] eth device init ok name:w0

msh />wifi scan   /* 搜索wifi 热点 */
             SSID                      MAC            security    rssi chn Mbps
------------------------------- -----------------  -------------- ---- --- ----
42e6ab17                        c8:f7:42:e6:ab:17  WPA2_MIXED_PSK -42    6   72
TP-LINK_7788                    64:6e:97:8c:77:88  WPA2_AES_PSK   -59   11  450
402                             c2:d2:47:c0:66:57  WPA2_AES_PSK   -62    6  144
402_Wi-Fi5                      c2:d2:47:c0:66:65  WPA2_AES_PSK   -63    6  144
CMCC-76ka                       ac:a4:6e:66:a8:d0  WPA2_MIXED_PSK -67    1  144
BreakDown                       9c:9d:7e:43:cb:39  WPA2_MIXED_PSK -70    7  144
114501                          64:6e:97:a4:c7:f6  WPA2_AES_PSK   -79   11  300
ChinaNet-3NZ5                   7c:fa:a0:10:d0:c0  WPA2_AES_PSK   -85    3  144
msh />
msh />
msh />wifi join TP-LINK_7788 xxxxx   /* 连接无线路由器,注意账号密码 */
[W/WWD] Join the network not authenticated!
[I/WWD] trying to reconnect...
[I/WLAN.mgnt] wifi connect success ssid:TP-LINK_7788
msh />
msh />
msh />[I/WLAN.lwip] Got IP address : 192.168.0.106

msh />
msh />ifconfig
network interface device: w0 (Default)
MTU: 1500
MAC: c0 84 7d 97 dd 02 
FLAGS: UP LINK_UP INTERNET_UP DHCP_ENABLE ETHARP BROADCAST IGMP
ip address: 192.168.0.106
gw address: 192.168.0.1
net mask  : 255.255.255.0
dns server #0: 192.168.1.1
dns server #1: 192.168.0.1
msh />

msh />tcp_test_init  /* 网络通了,就启动libmodbus tcp 例程 */

功能验证

  • 电脑端开启:Modbus Poll modbus 主机工具
  • 设置连接方式,注意端口号默认:1502
2022-04-02_214041.png
  • 读【保持寄存器】
2022-04-02_214104.png
  • 查看读取的寄存器信息
2022-04-02_214132.png
  • 功能验证初步通过
  • 偶尔有通信错误,并能检测出来,这说明了网络通信,不能保证百分百通信准确,需要应用协议进行判断

小结

  • 通过搭建libmodbus TCP 通信例程,熟悉嵌入式TCP 通信
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容