Menu

Documentation and examples

Oscar
2013-07-22
2013-07-30
  • Oscar

    Oscar - 2013-07-22

    Hi! I've got a simple driver working with jamod, but I'd like to use j2mod instead. But I can't find documentation for j2mod, or some code examples. Is there anywhere I can get some code examples or documentation especifically for j2mod? I'm particularly interested to know how you guys learn of the "extended" functions. For example, I'm reading/writing a Schneider PLC, and I can't find documentation from the manufacturer about the "extended" Modbus messages/functions. So I'm restricted to reading/writing single bits and 16-bit words.

    I've blindly tried to change all "import" references from net.wimpi.modbus to com.ghgande.j2mod.modbus without sucess (it was a simple test program using the facade ModbusTCPMaster class). It failed at readCoils() with:

    com.ghgande.j2mod.modbus.ModbusIOException: Executing transaction failed (tried 3 times)
    at com.ghgande.j2mod.modbus.io.ModbusTCPTransaction.execute(ModbusTCPTransaction.java:241)
    at com.ghgande.j2mod.modbus.facade.ModbusTCPMaster.readCoils(ModbusTCPMaster.java:174)
    at ModBusTeste.<init>(ModBusTeste.java:125)
    at ModBusTeste.main(ModBusTeste.java:50)

    Any help would be greatly appreciated! Thanks!

    Regards,

    Óscar

     

    Last edit: Oscar 2013-07-22
  • Josh Hansen

    Josh Hansen - 2013-07-22

    Hi, Oscar,

    It looks like the name replacement worked, but the request is failing -- if the name replacement didn't work, you would have compile errors, not runtime errors.

    Please provide:

    • A full code sample
    • Full exception + stack trace
    • j2mod version (0.96, 1.xx?)
    • Which functions you would like to use

    Most of the jamod documentation applies to j2mod, except for package names. There are a number of test examples in com.ghgande.j2mod.modbus.cmd.

    Julie has implemented a large percentage of the function codes is section 6 of the spec. The supported functions are listed in ModbusRequest.createModbusRequest(int functionCode). The spec is available as "MODBUS Protocol Specification" (after clicking the accept button) at:
    http://www.modbus.org/specs.php

    Finally, Wireshark is your friend. Use the spec to identify the request bytes, then use Wireshark to watch the request and response and see what is actually happening.

    Not all devices support all functions. Start with the "read single discrete/coil/input" functions to make sure those work, then progress to "read multiple ...".

    All the best,

    Josh

    EDIT: Trying to make the list look good.

     

    Last edit: Josh Hansen 2013-07-22
  • Oscar

    Oscar - 2013-07-29

    Hi Josh!

    Thanks for your reply! I have actually got the communication working with jamod, I've already developed a driver for my software and things are moving. But I'd like to get j2mod to work, and go with that instead.

    I will only have the Schneider PLC back in a couple of days for additional testing, so I'll report when I do.

    But maybe you can help me in the mean time with this: like I said, I'm communicating with a Schneider PLC (TWIDO). Since I found no docs for special Modbus functions to read the status of the PLC, PLC info, read/write floats, etc, I went ahead and implemented a simple driver for my software using only read/write coils (bit) and read/write registers (16-bit words). But this Schneider PLC is only responding to a reading of the first 123 (or thereabouts, I didn't take note) 16-bit words (MW0 to MW122), and the same number of coils (bits, M0 to M123). If I try to read the next register, it fails. This is very limiting... but on a PDF document I found online (Twido Modbus Addressing), Schneider says:


    Modbus Slave

    Modbus slave mode allows the controller to respond to standard Modbus queries
    from a Modbus master.

    [other info here...]

    The Modbus Data Link Layer has the following limitations:
    Address 1-247
    Bits: 128 bits on request
    Words: 125 words of 16 bits on request


    Is it possible they willingly limited Modbus to this small number of registers/bits? Or am I missing something obvious? We have a touch panel connected to the PLC, and it seems the comms are Modbus TCP/IP as well, and I bet there are no limits on the panel/PLC comms...

    Is it normal for manufacturers to render public their specific Modbus functions?

    Thanks for tolerating a Modbus newby! I've implemented drivers for OPC servers, serial and RS-485 I/O modules, Siemens PLCs (using specific libraries), some custom computer-based modules, databases, etc, and I thought Modbus would be easy-peasy. I still think I'm missing something obvious, though! :)

     
  • Josh Hansen

    Josh Hansen - 2013-07-29

    Hi Oscar,

    Modbus unfortunately has a rather small limit (in year 2013 terms; the original spec is from 1979) on the number of registers that can be retrieved in a single request. The overall Modbus TCP PDU (Protocol Data Unit) size limit is 253 bytes, which is about 127 registers (16 bit words). So, you might be encountering that, or some device similarity.

    From the spec:

    The size of the MODBUS PDU is limited by the size constraint inherited from the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256 bytes).
    Therefore:
    MODBUS PDU for serial line communication = 256 - Server address (1 byte) - CRC (2 bytes) = 253 bytes.

    Consequently:
    RS232 / RS485 ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256 bytes.
    TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes.


    Yes, it is normal for manufacturers to publish supported Modbus functions and addresses; without such publications it would be impossible to know what information is available from the device and how to retrieve (or update) it. However, it may be necessary to contact the manufacturer to get this information if they haven't published it on the internet.

    Not all registers / coils / etc. have information. There are up to 65K of each, so it is possible for there to be "gaps" (unused addresses). Attempting to request an address in a gap may generate an error such as "invalid address".

    Regarding jamod vs j2mod: were you able to use to jamod to query the device? If so, try replacing package names w/ j2mod for the same functionality. I'm assuming Modbus TCP, and not serial.

    General approach: get something small and simple working (e.g. request a single input register), then change one thing at a time until it breaks (e.g. change all jamod package names to j2mod names).

    Let us know how it goes!

    Josh

     
  • Josh Hansen

    Josh Hansen - 2013-07-29

    Sorry - didn't mean to shout "From the spec"; apparently my text was interpreted as "make this text giant font".

     
  • Oscar

    Oscar - 2013-07-30

    Here is the code I'm trying to run on j2mod. Maybe the problem is I'm using the facade objects?

    Many thanks for your support, Josh! :) I've already asked Schneider for the documentation, let's see if they come through.

    Based on your experience, if Schneider does have special Modbus functions, how does j2mod handle them? I'm sure there are no specific function calls for the dedicated functions, so maybe they use a standard Modbus function (ReadFileRecord ?) and then I have to give sense to the incoming bytes?

    Or do I have to extend j2mod, implementing the functions directly in TCP/IP packets?

    The following code reads and writes bits, then does the same with 16-bit words. I'm running the program supplying:

    ref = 0, count = 100 : OK
    ref = 100, count = 100 : FAIL

    I can't read/write anything above address 122. Maybe there is something my coleague (who is programming the PLC) needs to do before I can read those addresses? :S

    Code follows.

            System.out.println("Modbus com Schneider TWIDO!");
            try 
            {
                if (args.length < 3) {
                    System.exit(1);
                } else {
                    try {
                        astr = args[0];
                        int idx = astr.indexOf(':');
                        if(idx >= 0) {
                            port = Integer.parseInt(astr.substring(idx+1));
                            astr = astr.substring(0,idx);
                        }
                        addr = InetAddress.getByName(astr);
                        ref = Integer.decode(args[1]).intValue();
                        count = Integer.decode(args[2]).intValue();
                        if (args.length == 4) {
                            repeat = Integer.parseInt(args[3]);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        System.exit(1);
                    }
                }
    
                ModbusTCPMaster master = new ModbusTCPMaster( astr );
                master.connect();
    
                boolean aEscrever = true;
    
                System.out.println("Reading Bits.");
                for (int k = 0; k < repeat; k++)
                {
                    //System.out.println("Execute "+k+"!");
                    BitVector myData = null;
                    myData = master.readCoils( ref, count );
                    for (int i = 0; i < myData.size(); i++)
                    {
                        if (myData.getBit(i))
                            System.out.print("1");
                        else
                            System.out.print("0");
                    }
                    System.out.println();
    
                    aEscrever = !(myData.getBit(0));
    
                    //Thread.sleep(500);
                }
    
                System.out.println("Writing Bits "+aEscrever);
                for (int k = 0; k < 8; k++)
                {
                    master.writeCoil( 0, ref + k, aEscrever );
                }
    
                System.out.println("Reading Bits.");
                for (int k = 0; k < repeat; k++)
                {
                    //System.out.println("Execute "+k+"!");
                    BitVector myData = null;
                    myData = master.readCoils( ref, count );
                    for (int i = 0; i < myData.size(); i++)
                    {
                        if (myData.getBit(i))
                            System.out.print("1");
                        else
                            System.out.print("0");
                    }
                    System.out.println();
                    //Thread.sleep(500);
                }
    
                int anaAEscrever = 0;
    
                System.out.println("Reading Ints.");
                for (int k = 0; k < repeat; k++)
                {
                    //System.out.println("Execute "+k+"!");
                    Register[] myDatar = null;
                    myDatar = master.readMultipleRegisters( ref, count );
                    //System.out.print( myDatar[0].getValue() );
                    for (int i = 0; i < myDatar.length; i++)
                    {
                        System.out.print( " ; " + (int)(myDatar[i].toShort()) );
                    }
                    System.out.println();
    
                    anaAEscrever = (int)(myDatar[0].toShort()) + 1;
    
                    //Thread.sleep(500);
                }
    
                System.out.println("Writing Ints.");
                SimpleRegister reg = new SimpleRegister();
                reg.setValue( (short)anaAEscrever );
                master.writeSingleRegister( ref, reg );
    
                System.out.println("Reading Ints.");
                for (int k = 0; k < repeat; k++)
                {
                    //System.out.println("Execute "+k+"!");
                    Register[] myDatar = null;
                    myDatar = master.readMultipleRegisters( ref, count );
                    //System.out.print( myDatar[0].getValue() );
                    for (int i = 0; i < myDatar.length; i++)
                    {
                        System.out.print( " ; " + (int)(myDatar[i].toShort()) );
                    }
                    System.out.println();
                    //Thread.sleep(500);
                }
    
                System.out.println("Closing...");
                master.disconnect();
    
            } 
            catch (Exception ex) 
            {
                ex.printStackTrace();
            }
    
     

    Last edit: Oscar 2013-07-30

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.