Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)

First off, let me start saying it’s a pleasure to answer such a well laid down question. Not everyone puts so much effort on explaining what they did and how they did it. Yours is a plus one question right after you finish reading it.

Now with your problem. You missed one very important step on the tutorial you followed. As you say Modbus is half-duplex1, you have only two wires and only one device is allowed to talk on the bus so you need a way to take control of the bus, so to speak. In your USB-to-RS485/422 cable, that is done automatically for you by the hardware on the cable (your cable, in particular, uses the ubiquitous FTDI chip that has a TXEN -TX enable- signal, see here for more details), that’s why you noticed the cable works well. On the other hand, your tiny 3$ transceiver is the poor brother, and it does not even have an UART, it’s just a single-ended to differential converter. That’s the reason you need to provide a DE/~RE (Drive Enable/Not Read Enable) signal for the poor guy to know when it is allowed to take control of the bus.

This is the warning you did not take from the tutorial:

IMPORTANT: Before writing values to the RS-485 module the pins DE & RE must be made HIGH.

That seems easy enough, but if you think how Modbus works… it’s actually not so easy. This line of code:

rr = client.read_input_registers(1, 2, unit=3)

should be doing quite a number of things if you are to communicate with RS485 half-duplex successfully: take control of the bus (in your setup setting the RE/~DE signal high), send the Modbus query frame asking for two registers on UNIT ID 3, right after finishing writing the query (after 3.5 characters’ time) release control of the bus (now setting RE/~DE low) and reading the answer from the slave.

As I explain in the link I already referred to above, there are several solutions to this problem. My preferred one (being more of a hardware guy) is doing the bus direction control signal by hardware (the best way is to have a transceiver that has this function implemented by hardware, like this one, but in the link you’ll also find a DIY solution using a 555 timer). Now, if you prefer to do it the software way, you have some choices. You can tweak pymodbus to toggle the control line according to the Modbus needs (there are some links included in the answer I’ve quoted) or, if you prefer a more out-of-the-box solution use libmodbus.

If you decide for this last option, you can find all details on how to build and install lidmodbus with half-duplex support using the GPIO pins on the Rpi and if you want to stay on Python, install the wrapper and test the basic example. There are also a couple of scope screenshots to see the difference between toggling the line via software vs. hardware. For most in-house or hobbyist purposes, you should be able to use the software toggling but I would not trust it for industrial or more critical applications.

To finish off, I think it’s worthwhile answering all your questions one by one:

As RS485 only works in the one way, I do not think that pymodbus is
the problem (?)… (My logic says that pymodbus builds in the RS485
standard, and if that underlying layer of RS485 does not work,
pymodbus will not. Is that assumption correct?)

Well, yes and no and maybe… As you read above, pymodbus is not really the problem. It is just expecting for you or your hardware to take care of the not so minor detail of controlling who accesses the bus. I think most people use this kind of library for Modbus TCP so this is never a problem for most users. In a general Modbus scenario where you have a PLC talking to another device via Modbus RTU on an RS485 link, the problem is dealt with by hardware, so you wouldn’t have to worry about it either.

I know some people are talking about that the Raspberry Pi is 3.3V on
the pins and does not work with 5V pin-units. Despite that does all
tutorials seem to ignore that fact and work. – Or are they just faking
that it works? The TTL specifications say that all above 2.5V will be
accepted as HIGH. SO in THEORY, 3.3V should be OK, just as the
tutorials suggest.

Correct, the MAX485 datahseet specifies the threshold values for VIH and VOL and as long as you use 5V for the power supply of your transceivers, the different logic levels won’t be an issue (in this particular case, note that this is not a general statement, other devices might fail or end up destroyed if you mix logic levels).

I have by purpose yet not attached any resistors on the tx/rx wires
for pull up/down. The tutorials don’t suggest them.

Most likely you won’t need to attach any terminating resistors to the bus for an in-house project. For long buses (in a factory or facility where devices can be hundreds of meters apart) you would probably worry about this issue. Your tiny transceiver actually has these terminating resistors already included so on its side, better not to add more resistance. For your cable, I did not have enough patience to find a manual (I don’t know if there is one; I have a similar cable and the only way to be sure was to remove the cover and look under its hood).

Once you have everything up and running note that on your client:

print(rr)

Should be:

print(rr.registers)

If what you want is to show the values you have read.

Leave a Comment