串口通信之(二)数据上传到上位机 (modbus rtu slave)

后来才得知,还需要将数据上传到上位机。大概就是别人的主机,通过串口发命令给我们的主机,根据接收到命令,将相应的数据返回给上位机。同样需要通过modbus协议的rtu来通信。

这里附上一个博客可以作为参考,https://blog.csdn.net/lsh159357/article/details/84936294

这个博客是串口通信,也用modbus协议,不过不是rtu,而是tcp通信的。不过也可以借鉴。

可以将其中ModbusFactory 的 createModbusSlaveTCP方法,改用createRtuSlave方法来创建rtu的slave端,存储数据一样的。如下:

        String commPortId = "COM2";
        int baudRate = 9600;
        int flowControlIn = 0;
        int flowControlOut = 0;
        int dataBits = 8;
        int stopBits = 1;
        int parity = 0;

        SerialPortWrapperImpl wrapper = new SerialPortWrapperImpl(commPortId, baudRate, flowControlIn, flowControlOut, dataBits, stopBits, parity);
        ModbusFactory modbusFactory = new ModbusFactory();
        final ModbusSlaveSet listener = modbusFactory.createRtuSlave(wrapper);

 

不过,毕竟是人家写的东西,始终会有些不适用的地方。比如,每个寄存器,只能存储一个整数,不能存小数(这也很正常,通常都 用两个寄存器来存小数)。再比如,不能存储一个字节的boolean类型数据。

所以有得研究。

其实,终究还是对这个太陌生了。上一篇不是讲过rxtxComm是对串口进行读写操作的吗,直接操作呗。于是就动手。其实代码和前一篇差不多。前一篇是需要自己发送请求命令,然后对接收到的传感器数据进行解析。而这里,只需要接收上位机的命令,然后整理报文,将相应的数据返回 回去即可。 使用netty+rxtxComm,代码如下:

Slave.java

public class Slave{
    private String portName;//串口名
    private int baudrate;//波特率
    RxtxChannel channel;
    List<String> data;//虚拟的寄存器,用来存数据


    public Slave(String portName, int baudrate,List list) {
        this.data=list;
        this.portName = portName;
        this.baudrate = baudrate;
    }

    public void run(){

        try {
            OioEventLoopGroup group = new OioEventLoopGroup();
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channelFactory(new ChannelFactory<RxtxChannel>() {
                        public RxtxChannel newChannel() {
                            return channel;
                        }
                    })
                     .handler(new ChannelInitializer<RxtxChannel>() {
                        @Override
                        public void initChannel(RxtxChannel ch) throws Exception {
                            ch.pipeline().addLast(
                                    new PacketDecoder(),
                                    new RxtxHandler(data)
                            );
                        }
                    });

            channel = new RxtxChannel();
            channel.config().setBaudrate(baudrate);
            channel.config().setDatabits(RxtxChannelConfig.Databits.DATABITS_8);
            channel.config().setParitybit(RxtxChannelConfig.Paritybit.EVEN);
            channel.config().setStopbits(RxtxChannelConfig.Stopbits.STOPBITS_1);
            ChannelFuture future = b.connect(new RxtxDeviceAddress(portName)).sync();
            System.out.println("rxtx启动成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }





    //程序入口
    public static void main(String[] args) {
        Slave slave=new Slave();
        slave.run();
        slave.request();
    }

}
RxtxHandler.java

public class RxtxHandler  extends ChannelInboundHandlerAdapter {

    private List<String> data;//虚拟的寄存器,用于存数据。这里存的是16进制的字符串,这样可以存任意类型的数据

    public RxtxHandler(List<String> data) {
        this.data = data;
    }

    /**
     * 客户端接收到数据的回调
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{

        if(msg instanceof String ){
            String packet = (String)msg;
        
                System.out.println("获取主机请求:"+packet);

                int starAddr = InputCommandUtil.getStarAddrInt(packet);

                int dataCountInt = InputCommandUtil.getReqDataCountInt(packet);
                //获取相应命令相应的寄存器的数据
                List<String> strings =ListUtil.subList(data,starAddr,starAddr + dataCountInt);
                //组装返回的命令,包括报文头,长度,crc
                String resCommand = OutputCommandUtil.packet(strings);

                writeAndFlush(ctx.channel(),resCommand);
       }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        cause.printStackTrace();

        ctx.close();
    }


    private void writeAndFlush(Channel channel,String hexString) {

        byte[] bytes = ByteUtil.hexStringToBytes(hexString);

        ByteBuf buffer = channel.alloc().buffer();

        ByteBuf byteBuf = buffer.writeBytes(bytes);

        channel.writeAndFlush(byteBuf);
    }

PacketDecoder .java

public class PacketDecoder extends ByteToMessageDecoder {
    StringBuilder sb=new StringBuilder();
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
        byte[] read =new byte[in.readableBytes()];
        in.readBytes(read);
        String packet = ByteUtil.BinaryToHexString(read).toUpperCase();
        //System.out.println("ByteToMessageDecoder "+packet);
        sb.append(packet);
        if (!InputCommandUtil.isMasterCmd(packet) && !InputCommandUtil.isSlaveCmd(packet)) {
            return;
        }
        out.add(sb.toString());
        sb.setLength(0);
    }
}

 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Modbus RTU是一种串行通信协议,通常用于工业自动化环境中的设备通信。主站(Master)指控制器或计算机,可以通过Modbus RTU协议与从站(Slave)通信。以下是一个简单的上位机Modbus RTU主站的源代码: ``` #include <ModbusMaster.h> // Create a ModbusMaster object ModbusMaster node; void setup() { // Initialize serial communication with baud rate of 9600 Serial.begin(9600); // Initialize Modbus communication using Serial node.begin(1, Serial); node.setSlave(1); node.setTimeOut(1000); } void loop() { // Read holding register (address 0x10) of Modbus slave (address 1) uint16_t result = node.readHoldingRegisters(0x10, 1); if (result == node.ku8MBSuccess) { // Display the result if reading was successful Serial.print("Holding Register value = "); Serial.println(node.getResponseBuffer(0), HEX); } else { // Display error message if reading failed Serial.println("Error reading holding register!"); } // Wait for 1 second before reading again delay(1000); } ``` 以上代码使用了ModbusMaster库,该库可以通过Arduino IDE的“库管理器”进行安装。在设置中,我们指定上位机的地址为1,从站的地址也为1。在循环中,我们使用readHoldingRegisters函数读取从站地址为1、地址为0x10的寄存器。如果读取成功,则打印该寄存器的值,否则打印错误信息。在每次读取后,我们让主程序暂停1秒,然后再读取。您可以根据需要修改代码,以适合您的Modbus设备。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值