串口通信之(二)数据上传到上位机 (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);
    }
}

 

好的,下面是PLC与上位机通过Modbus RTU通信的示例代码: PLC端代码: ``` // 定义Modbus RTU通信参数 #define BAUDRATE 9600 #define PARITY 'N' #define DATABITS 8 #define STOPBITS 1 // 初始化串口 void init_serial() { Serial.begin(BAUDRATE, SERIAL_8N1); } // 读取Modbus RTU命令 void read_modbus_cmd() { byte cmd[8]; int i = 0; // 等待串口接收到8个字节的数据 while (Serial.available() < 8) { delay(1); } // 读取8个字节的数据 while (Serial.available() > 0 && i < 8) { cmd[i] = Serial.read(); i++; } // 处理Modbus RTU命令 // ... } // 发送Modbus RTU响应 void send_modbus_response(byte* response, int length) { Serial.write(response, length); } ``` 上位机端代码: ``` #include <modbus.h> // 定义Modbus RTU通信参数 #define SLAVE_ID 1 #define BAUDRATE 9600 #define PARITY 'N' #define DATABITS 8 #define STOPBITS 1 // 初始化Modbus RTU modbus_t* modbus_init() { modbus_t* ctx = modbus_new_rtu("/dev/ttyUSB0", BAUDRATE, PARITY, DATABITS, STOPBITS); modbus_set_slave(ctx, SLAVE_ID); modbus_connect(ctx); return ctx; } // 读取Modbus RTU寄存器 int read_modbus_register(modbus_t* ctx, int addr) { uint16_t data; modbus_read_registers(ctx, addr, 1, &data); return data; } // 写入Modbus RTU寄存器 void write_modbus_register(modbus_t* ctx, int addr, int value) { modbus_write_register(ctx, addr, value); } // 关闭Modbus RTU连接 void modbus_close(modbus_t* ctx) { modbus_close(ctx); modbus_free(ctx); } // 示例代码 int main() { modbus_t* ctx = modbus_init(); // 读取PLC的寄存器 int value = read_modbus_register(ctx, 0x100); // 写入PLC的寄存器 write_modbus_register(ctx, 0x101, 123); // 关闭Modbus RTU连接 modbus_close(ctx); return 0; } ``` 以上示例代码仅供参考,实际上下位机和PLC的代码需要根据具体情况进行编写和调试。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值