Modbus RTU slave
Starting to figure this Modbus stuff out.
Really want to do Modbus TCP over ethernet, but starting out with Modbus RTU over serial connection.
Being over serial connection makes debugging tricky, but using a 1.8" TFT to help...
Anyway, can now trick the Chipkin Modbus scanner into thinking there's a device attached with address 10. It tries probing all the addresses until you reply with no data. So, this replies to the first probe with data and the second probe with no data.
Copied the CRC code from FreeModbus Libary in C for now, but perhaps replace later with code that uses the P2 CRC hardware...
Comments
Found what looks like good documentation here: https://modbus.org/docs/PI_MBUS_300.pdf
When I did that for the Prop1 I started from the Obex version - https://obex.parallax.com/obex/modbus-rtu/
I modified it a little. In particular I re-engineered command 04 (Read Multiple Input Registers) and made it use a reference table of compiled symbols so then the master could directly read a set of read-only variables without the slave's operations needing to go out of its way.
Here it is. Rather poorly commented though.
Oh, and it relied on a tweaked version of FullDuplexSerial that handled RS485 turnaround.
Thanks @evanh definitely will use that CRC code.
I did a P1 MODBUS RTU project a long time ago, converted the CRC code to Spin2 (just needed to change the ROR operator), and then converted to inline PASM2 for speed. I have verified against online CRC calculators.
@JonnyMac Ok, have to try that.
Got hung up once again on ser.rx() vs ser.rxcheck()...
Spent too much time figuring out why these don't work the same:
```
Pub GetC():c
repeat
c:=ser.rxcheck()
if (c>0)
quit
return c
Pub GetC2():c
c:=ser.rx()
```
Guess first one would work in usual cases, but here the returned byte is often 0, and that breaks it... Needs to be (c>=0).
Hopefully, this sticks in my brain longer this time...
Since Chip's first incarnation of fullduplexserial, the rxcheck() method returns -1 to indicate there is nothing available so, yes, we need to allow 0 as a valid byte. It's nice because it's non-blocking (unline rx() which is). In some of my serial libraries I have added a method called available() to that returns the number of bytes available in the RX buffer.
Guess an IsAscii() function is another way . But better memory is what is really needed…
So, the modbus scanner is very sensitive to response timing.
Think figured out that it somewhat follows the 3.5 character minimum delay mentioned in the modbus.org document.
But, there also seems to be a maximum delay, so it has to be just right.
Anyway, need to be able to code the response with CRC within that delay, it seems.
At 9600 baud, the scanner seemed happy with a delay of ~10 ms.
At 115200 baud, seems happy with ~80 us.
This is not exactly linear, so not sure what is going on here.
Thinking timing is just going to have to baud table lookup.
Might be best to just stick with one baud for now.
Will see if 80 us is enough time to calc CRC for general case.
There is a "timeout" setting in the scanner that seems to range from 3 to 30. But, don't want to mess with that for now...
But that's not very fast, 'innit?
V3 is about 16% faster than V2. That may help with higher baud rates (timing is in system ticks [@200MHz]).
Thought you were German(?) How do you know this Brit stuff?
With a small packet the function call / FCACHE overhead starts to dominate the runtime. On a larger one it'd be 2x or so.
Cultural osmosis. As a second-language English speaker you pick up all sorts of phrases.
If we believe the internet, a typical MODBUS RTU packet is 8-12 bytes. At 12 bytes the V2 to V3 improvement is about 25%.
On the extreme is a 256-byte packet, which is not likely to be very common. That said, V3 is a big improvement in that case.
Thanks @Wuerfel_21 ! Is it safe to use PA in FlexProp and PropTool with inline assembly? Guess so. Isn't there something about not using ptra?
Should keep better notes on that kind of stuff...
Yes, can't use PTRA, but the other registers are fine (as long as you don't expect them to keep their values afterwards)
@Wuerfel_21 Ok, great. Just tested V3 and works with modbus scanner.
The modbus scanner will sometimes not be 100% "good" with P2 replies on first pass. Better, if not perfect on later passes... Not sure how bad that is.
Going to work on better timing control and see if can improve that...
There may be some latency going through the FTDI adapter that can't be controlled. Although, maybe can be improved in device manager...
@Rayman Since your version 1e code uses jm_fullduplexserial, you could take advantage of the rx_tix() method to detect the timeout of the master message.
p_buf is a pointer to your message array, and the method returns the number of bytes in the message (0 when no message is available). CTO_TIX is the character timeout period in system ticks for your baud rate. One getting a valid message you'd set a timer for responding, build the message, and then send when the timer is expired. Maybe something like this:
Of course, RSP_DELAY is the hold-off time between the incoming query and the sending the response.
@JonnyMac Have noticed things like that in the driver. Need to look into these things:
Hoping that the Chipkin modbus scanner will let me know if have the timing right...
The modbus.org website seems to have some good info.
Did notice this:
Just like with SPI, people notice that the master/slave verbage is perhaps not the best and come up with alternatives. But, nobody really changes?
See they decided that in 2000, and yet documents from 2006 still refer to master/slave...
This document here is still looking to be all that is really needed: https://modbus.org/docs/PI_MBUS_300.pdf
Looking at what function codes need to implemented...
The scanner probes functions 1..4. Think all that is really needed for this application is 1 and 5. Maybe 4 though too...
Trying to decide if need to reply to other function calls.
Just found the section on "Exception Response". This is just what was looking for...
Now, can respond normally to however many coils or registers want to implement and reply with one of these exceptions for anything else.
Although, the scanner seemed happy with just replying with 0 in the #bytes field in the reply. This might be a better way...
Certainly don't have to implement more than you want to. The user config on the master is only going to request what's usable in the slave. Thereby not triggering non-functional Modbus commands to that slave.
Wishing that "read coil status" requests would be 8-bit aligned and in quantities of 8 bits.
But, the example shows something else. Guess it's not a huge amount of work to implement, just inconvenient...
Well, you could just round it up to next size of eight bits. Since you're using the binary data format it is always going to be 8 bit bytes in your reply. The master will do any truncating it wants to after the CRC has been validated.
@evanh
Getting close to an actually functional slave now.
Finding that the "Discover" window is hard to get right.
But, the main window is very nice and shows the actual binary query and the reply.
This is more helpful.
Also, the 1.8" lcd is pretty useless as is because is too slow and causes timeouts at 115200 baud.
Was also reading that 19200 baud is the usual default, but maybe that is old info?
This slave test might be right... Seems to give correct response to the modbus scanner, at least with simple test.
Unfortunately, the scanner won't let me test function #5, force coil value, so have to make something else for that.
Guess have to code up a Modbus RTU master too...
Hmm, opps, my last suggestion isn't very useful since such requests will not be starting on byte boundaries. I wouldn't be too concerned though, it's just a small amount of bit twiddling in the larger scheme. Modbus is no speed demon.
There's no hard baud rate to the spec. If all the hardware involved on the network can do 2M baud then there's no reason that couldn't be selected.
I have an Omron touchscreen here I rescued from going to the tip. It could be used as a Modbus master for testing slave functionality. It hasn't had power on it since the card factory was closed in 2018. It was connected to an Omron PLC so would've been configured for Omron's Hostlink protocol back then. ... It might be interesting to see if I can reprogram it without Windoze on hand.
... Grr, can't find the license key. Annoyingly, I never saved it as a text file.
Looks like it is working on Wine otherwise.