1
\$\begingroup\$

I'm developing a Modbus Slave device, and I'm having a little trouble understanding how to name/address my registers.

Let's say I have 2 Discrete Inputs and 2 Input registers. From Modicon standard, the Discrete Inputs will have register number 10001 and 10002, while the Input registers have 30001 and 30002. Reading here and there, it looks like these will translate to register addresses 10000, 10001 and 30000, 30001.

Now, how to access these registers? What will be passed in the Modbus message starting address field? This is my firmware and can do whatever I want, but what is the standard? (this is what I don't understand)

I see 2 options:

  • absolute address approach: if the user wants to read the first Input register he'll send 30000 in the starting address field; from firmware side, I know it is an Input register both from the function code and from the register address itself, and I know it's the first one since I received exactly the address of the first one
  • offset approach: if the user wants to read the first Input register he'll send 0 as starting register; from firmware side, I know it is an Input register from the function code, and I will look for the first Input register since I received a 0 offset

The first approach seems more reasonable for me, since it avoids registers access errors;

  • absolute address approach: if the user sends a read Input register function code for address 10000 I will answer with an error code; maybe the user wanted to read an Input register, but sent the wrong address, or maybe he wanted to read a Discrete Input but sent the wrong function code; better notify him with an error than send a data that he'll probably misuse
  • offset approach: if the user sends a read Input register function code for address 0 I will answer with the data; but what if he wanted to read a Discrete Input but sent the wrong function code? Dumb him, ok. But since the same starting address can refer to 2 data types (or more, if there are Coils and holding Registers, too), I'll also feel a little guilty

Besides my preferences, I have a "real case scenario" that support the offset approach. I've interfaced my device with a gateway that translate the Modbus to another fieldbus protocol. To configure this gateway I need to define the Modbus addresses that will be periodically read/write. I need to use 6-digits addresses. So, in the example above, the 2 Input registers will be 300001 and 300002, and the 2 Discrete Inputs will be 100001 and 100002. From firmware side, for both data types I receive 0 and 1 as starting addresses, so the second approach (starting address indicates the offset) is used.

Again, is there any standard way to approach the Modbus addressing?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

About a decade ago, I've written a simple Modbus library for Linux. Apologies for the self-promotion. @Ron Beyer: at the time, I never heard of extended addressing - at the level od Modbus requests, the base and count are always unsigned 16bit types, and : the low-level addresses riding inside the Modbus request frames are always zero-based, starting literally from zero. At the framing level of Modbus addressing, the "object type" is encoded in the Modbus function: discrete inputs, coils, input registers, holding registers. And, the address space in that low-level frame always starts at 0. Thus, theoretically, if you only ever speak the low-level addresses, you can have 64k discrete inputs, and 64k coils, and 64k input registers, and 64k holding registers - at the low level, they are parallel address spaces. You can have a coil #2 and an input register #2 and they are two different objects, no ambiguity in their addressing/access.

Then there's the "upper layer of addressing" (anyone know the official name?), where you have e.g. 10001 to be the first discrete input, and 40001 the first holding register. I.e. this "uniform upper layer address space" has the Modbus object types identified by base offsets - and, the counting system is one-based.

If you need to convert between the upper-layer and lower-layer addresses, you need to subtract that nominal type-specific base offset and handle "off by one" (like Pascal to C array element counting/indexing).

As Ron Beyer has said, reality can be a mess.

Some vendors speak the raw framing-level zero-based register addresses, other vendors speak the upper-layer "uniform" addresses. Typically, I would expect vendors of "distributed I/O endpoint gadgets with Modbus support" to speak the raw register addresses. Versus: documentation to PLC programming, OPC servers or SCADA might speak the upper, more abstract "uniform address space". But especially with cheap SE Asian Modbus-capable slave gadgets, the documentation is often unclear about what layer it speaks (still chances are that anything above 10000 means the "upper" layer) and I've even seen broken implementations that had the "off by one" wrong for the layer they were speaking. And, you often do not know whether to ask for input vs. holding registers, or discrete inputs vs. coils, when reading = sometimes either works and the address spaces in fact overlap... If unsure, proceed by cautious trial and error.

Actually... the vendor's documentation on any sensors, actuators, drives etc. with Modbus capability tends to focus on its "business domain" = mechanical dimensions, power supply range, physical performance properties, ramps/curves, physical unit scales and encoding, environmental conditions, handling safety precautions, a vocabulary index, a legal disclaimer, a list of ordering codes, a global list of local service contacts... and the darn map of Modbus regiters, which is the first thing you need as an integrator (even at a pre-sales stage), being the only thing you cannot possibly figure out based on common sense, that tends to be buried somewhere real deep :-) and poorly annotated/explained. The best guarded secret.

Just kidding. It's actually getting better through the years.

EDIT: I've just noticed your notes about 1-based addresses above 64k, and a gateway device that converts between two fieldbusses.

I have met gateways by the HMS Corp of Sweden that can speak several protocols, including Modbus. These boxes tend to have their internal proprietary memory space and organization, and the native fieldbus-specific objects or address spaces get mapped into the internal uniform memory space. Understandably, that mapping is more or less freely configurable, and the resulting internal datapoint map is definitely custom or proprietary - there's not much point in asking for a standard in such hybrid devices.

\$\endgroup\$
4
  • \$\begingroup\$ Bingo! I'm using an HMS device. Focusing on the Modbus side (not the internal memory), the wizard recognize the register tipe given the Modbus address you use. So if you define a register with address 400001, the wizard automatically assigns a (R/W) Holding Register related function. On the slave side, when I receive the Modbus message, the Starting address field contains a 0. This looks like a (somehow) widely used approach. Long story short, given your comment, it looks like the important thing is to clearly state this in the manual, regardless the "standard". \$\endgroup\$
    – il_mix
    Jun 12, 2020 at 20:34
  • \$\begingroup\$ @il_mix: if I understand you correctly, you are developing a Modbus slave device. If that's your situation, I would strongly suggest to adhere to the zero-based bank of addresses, starting from address zero, per Modbus object type. I.e. the Modbus object type is specified by the function code, not by a "type-specific offset". You do not need to care about the upper layer addressing. In your manual, you can take care to clarify this. Focus on the low-level addressing, and also mention how this likely translates to the semi-standard global/uniform address space, used by some software. \$\endgroup\$
    – frr
    Jun 14, 2020 at 4:29
  • 1
    \$\begingroup\$ that's basically my idea. I'm writing the manual this very days, and I added a reference table with Register Name | Register number | Register address. In the Register number field I write the 6-digits address (e.g. 400001), while in Register address I'll write the 0-based address (e.g. 0). Same in the subchapters where I explain the functionality of every single register. \$\endgroup\$
    – il_mix
    Jun 15, 2020 at 7:18
  • \$\begingroup\$ Yes, that's a nice bullet-proof approach, congrats. Apparently the 6-digit addressing scheme (e.g. 400001 for the first holding register) is called "extended notation". The original Modicon notation would be a five-digit number, such as 40001. Which limits your upper layers to accessing just 10k objects of each type. Which is likely no problem with most slave devices... By now I'm just nitipicking anyway. Good luck with your project. \$\endgroup\$
    – frr
    Jun 15, 2020 at 8:38
0
\$\begingroup\$

There should be a standard, but there really isn't. I've seen both approaches in industrial devices, coupled with the "0 or 1" confusion.

This gets compounded greatly with extended addressing, where coil ranges are 100000 to 199999, registers 400000 to 499999, etc. In that case, 10,001 is a valid request to either get the coil, or the register.

So is there a standard way? No, not in practice. Please (speaking as an engineer who works with these devices often), document your approach in your manuals well, it makes integration a lot easier in the end and many manufacturers skip this part or don't put much effort into it.

\$\endgroup\$
1
  • \$\begingroup\$ As said to @frr, whathever my approach will be, it looks like the important thing is to clearly state everything in the manual, regardless the "standard". \$\endgroup\$
    – il_mix
    Jun 12, 2020 at 20:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.