Android 实现ModbusTcp 从机 -- JLibModbus

ModbusTCP Slave for Android

最近项目中 Socket 通讯使用到 ModbusTCP协议,记录下从调试到使用过程。

客户提出Modbus协议,一开始也是有点懵逼的,找遍各种资料,Github开源项目,很少有关于Android端作为从机的文章跟项目参考,时间不等人,只能自己下载源码分析到底怎么回事。最终决定使用JLibModbus开源库

Modbus 一般分主机poll(Client)跟从机slave(Server),网上很多都是以Android端作为主机端的案例,本文主要介绍以Android端作为从机slave。

主机端大家可以参考:https://www.cnblogs.com/ioufev/p/10831289.html

 

使用工具+资源jar

ModbusPoll:主机Poll端

ModbusSlave:从机Slave端

具体使用后面介绍,下载安装就不在这里多说,参考:https://www.cnblogs.com/hieroly/p/9063710.html

jlibmodbus-1.2.9.7.jar 

 

Android 端从机Slave创建

导入jar包后,下面是我自己创建从机的部分代码,应该是可以直接使用的

public class ModBusManager {

    private static final String TAG = "ModBusManager";

    private static ModBusManager modbusManager = null;
    private static ExecutorService singleThreadPool;
    private Context context;

    private ModbusSlave modbusSlave;
    private TcpParameters tcpParameters;
    private MyDataHolder dataHolder;

    public static ModBusManager getInstance(Context context) {

        if (null == modbusManager) {
            synchronized (ModBusManager.class) {
                if (null == modbusManager) {
                    modbusManager = new ModBusManager(context);
                }
            }
        }
        return modbusManager;
    }

    private ModBusManager(Context context) {
        this.context = context;
        singleThreadPool = Executors.newSingleThreadExecutor();
    }

    /**
     * 启动 从机 salve  (server)
     */
    public void startSalve() {

        singleThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Log.d(TAG, "run: startSalve");
                    //填写自己的端口号
                    tcpParameters = getParameters(Constant.SOCKET_PORT);
                    dataHolder = new MyDataHolder(context);

                    // 创建一个从机
                    modbusSlave = new ModbusSlaveTCP(tcpParameters);
                    // 设置控制台输出主机和从机命令交互日志
                    Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);
                    modbusSlave.setDataHolder(dataHolder);
                    modbusSlave.setReadTimeout(10000);
                    //填写自己的 Slave ID
                    modbusSlave.setServerAddress(Constant.SERVER_ID);
                    modbusSlave.listen();

                } catch (ModbusIOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 配置 参数
     *
     * @param port
     * @return
     */
    private TcpParameters getParameters(int port) {

        TcpParameters parameters = new TcpParameters();
        try {
            //此处设置Android 端 IP地址
            InetAddress address = InetAddress.getByName("192.168.30.211");

            parameters.setHost(address);
            // 设置从机TCP的是否长连接
            parameters.setKeepAlive(true);
            // 设置从机TCP的端口
            parameters.setPort(port);
            Log.d(TAG, "getParameters: address = " + address + "  port = " + port);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return parameters;
    }

    public MyDataHolder getDataHolder() {
        return dataHolder;
    }


    public void release() {
        try {

            if (null != modbusSlave) {
                modbusSlave.shutdown();
                modbusSlave = null;
            }
            if (null != dataHolder) {
                dataHolder.release();
                dataHolder = null;
            }
            if (null != tcpParameters) {
                tcpParameters = null;
            }

        } catch (ModbusIOException e) {
            e.printStackTrace();
        }
    }


}

主从机如何交互,前期为了弄明白这些,包括工具的使用,花了很大的功夫,大概讲述下

个人理解:主从机交互,从机(Salve)也就Socket的Server端,会创建一个连续地址存储空间,主机(Poll)Client端会对这个连续的地址写入值或者从这里面读取值。也就是主从机都可以读写操作这个连续地址存储获取或者修改数据。此处欢迎大佬详细给我介绍

数据读写

public class MyDataHolder extends DataHolder {

    private static final String TAG = "MyDataHolder";

    private Context context;

    MyDataHolder(Context context) {
        this.context = context;
        //很关键
        setHoldingRegisters(new ModbusHoldingRegisters(Modbus.MAX_START_ADDRESS));
    }

    //06
    @Override
    public void writeHoldingRegister(int offset, int value) throws IllegalDataAddressException, IllegalDataValueException {

        Log.d(TAG, "writeHoldingRegister:写单个寄存器  offset = " + offset + "  value = " + value);

        //转2进制
        Log.d(TAG, "writeHoldingRegister: value_2  = " + Integer.toBinaryString(value));
        //转16进制
        Log.d(TAG, "writeHoldingRegister: value_16  = " + Integer.toHexString(value));

        super.writeHoldingRegister(offset, value);
    }

    //10
    @Override
    public void writeHoldingRegisterRange(int offset, int[] range) throws IllegalDataAddressException, IllegalDataValueException {

        Log.d(TAG, "writeHoldingRegisterRange:写多个寄存器 offset = " + offset);


        super.writeHoldingRegisterRange(offset, range);
    }

    //03 读寄存器
    @Override
    public int[] readHoldingRegisterRange(int offset, int quantity) throws IllegalDataAddressException {

        Log.d(TAG, "readHoldingRegisterRange: 读取信息 offset = " + offset);
        return super.readHoldingRegisterRange(offset, quantity);
    }

}

数据读写关键在 DataHolder   ,大家可以自己看源码,对应很多功能码,代码只展示三种,主要我目前只用到这三种,就不额外举例。

注意:ModbusPoll 填写的 地址 和功能码都是 16进制 但是jlibModbus 读取出来会转换成10进制  所以 10对应的十进制16

Modbus Poll 使用 

打开后,点击Setup - read/Write Definition...(设置功能码) - 出来如上图所示画面

File - new 可以创建多个窗口,停留在哪个窗口点击Setup就是针对的具体设置,可同时开启多个

Slave ID  :

对应代码设置  modbusSlave.setServerAddress(0xD4)

两边设置的值要一致(Modbus Poll 是16进制的值,代码设置的是10进制的值)举例:代码设置0xD4 那么在工具中填应该是212

Function:

功能编码,对应上图 01 - 15 介绍

Address : 

连续存储的地址

对应代码 MyDataHolder 中三个方法的 offset 参数(也是10 进制 对应 16进制)

Quantity:

地址数量,图示 03,读取 从地址0开始的10个寄存器地址的值

06 写单个寄存器,值只能为1,写入一个值

16 写多个寄存器,给连续的地址写入多个值,起始地址是Address

举例写多个寄存器:

对ID 为 212 的从机写入地址从50开始连续10个寄存器(如上图所示)点击OK后,可以对数据的进制进行设置。

DisPlay中可以对填写数据的进制进行设置 hex - 16进制  Binary - 2进制 (不管工具端设置什么进制的数,Android 端读取都会转换成10进制,所以获取的数据,处理要进行对应的进制转换)

//转换部分 源码
public void setValue(int value) throws ModbusNumberException {

        if (!Modbus.checkRegisterValue(value)) {
            throw new ModbusNumberException("Register value out of range", value);
        }
        this.value = ((short) value) & 0xffff;//转10进制
    }

使用:开启+关闭

ModBusManager.getInstance(this).startSalve();
ModBusManager.getInstance(this).release();

开启从机监听,启动ModbusPoll 点击Connection填写对应的IP地址+port端口号,点击OK,就会连续发送数据到Android

注意:连接不上 注意排查 1,IP地址 2,端口号 3,Salve ID  4,有可能电脑的防火墙也需要关闭下

主机端写入数据时,从 MyDataHolder 处拦截,获取数据。

Android端也可以调用MyDataHolder.write......写单个寄存器/多个寄存器

写入成功等待主机端读取,读取的数据就是写入的数据,完成主从机的交互。

(关键 记得MyDataHolder中 setHoldingRegisters(new ModbusHoldingRegisters(Modbus.MAX_START_ADDRESS));)

(写入寄存器的值 转换成10进制写入)

我反编译jar源码提取了部分使用的,以上两个类应该不影响直接使用jar包

未完待续....................

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
用户可以根据需求在驱动库里选择相对应的通讯驱动程序,配置相应的通讯握手参数,自由定义需要控制和采集的变量名、通讯地址、数据类型和初始值。功能说明及图片展示: 1.安装完成首次进入将会显示三页功能简要展示页面。 2.点击“立即使用”按钮,进入登录界面(公测账户为admin,密码admin)。 3.登录成功进入APP主页,此时主页所有状态为初始未配置状态。 4.在主页状态下向右滑动,可打开功能模块选择列表。 5.点击“WIFI连接”进入连接现场设备网络页面,如果此时用户已通过手WIFI页面连接现场设备,则直接跳过此步骤。 6.连接成功后返回功能模块选择列表,点击“参数设置”进入通讯相关参数配置页面(首先进入驱动配置页面),目前驱动库中只有支持Modbus TCP的驱动,后续会持续更新,用户通过在驱动库列表中长按操作将选中的驱动挑选至已选列表中,如果要取消,可以在已选列表中通过同样的长按操作完成。 7.配置要访问的设备通讯参数,与主页的操作逻辑一致,在驱动配置页面向右滑动打开配置功能列表,点击“通讯参数配置”进入通讯参数配置页,然后点击右上角的加号,此时软件会根据已选的驱动类型自动添加一条相对应的通讯参数,用户可以通过双击的方式打开修改列表,然后在列表的某一条参数处通过长按的方式进入最终的修改对话框,修改完成后,可以通过长按的方式选定当前需要的参数配置信息到已选区域。 8.通讯参数配置完成后,向右滑动进入配置功能列表,点击“IO参数配置”进入地址段的分配,点击右上角的加号,在弹出的对话框中根据实际需求分配响应的起始地址以及地址数量,分配完成后,可在屏幕右侧边缘向左滑动调出隐藏功能菜单,通过功能菜单可查看和修改配置地址段的功能对应的IO点信息。 9.所有配置完成后,可返回主页,此时主页显示当前配置的信息,其中“控制操作”功能可以通过按钮发送布尔值,操作逻辑为当前值为0时发送1,当前值为1时发送0,而“参数设置”功能通过对子项长按可以设置调出输入框设置相应的参数值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值