Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: modbus_rtu: add initial support (v2) #20386

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

basilfx
Copy link
Member

@basilfx basilfx commented Feb 13, 2024

Contribution description

This PR is a reboot of #13913 that got stale. I cloned it, rebased it onto master, adapted it to latest standards (hopefully) and proposed #19819. But after some actual testing, I decided that both PRs were not really useful, and made a lot of assumptions. For more flexibility, I rewrote most of the code. Some notable improvements:

  • No more hard-coded addresses. If you request address 1000, the slave does not need to allocate a buffer to hold 1000 registers.
  • Slave implementation uses a request callback, which makes it much simpler to implement dynamic behavior.
  • Support for exceptions.
  • Better documented, consistent naming with Modbus specification.

Contrary to the original PR, I removed a lot of #ifdefs that limited which Modbus functions would be supported. It was hard to manage IMHO.

I also extended the test application to test using a single or multiple devices. An example application that provides a Modbus slave implementation is also provided.

Testing procedure

A test application is provided in tests/drivers/modbus.

There are multiple ways to test this. The easiest is to connect RX/TX of one UART to TX/RX of another UART. That way, Modbus runs over RS-232, which should be sufficient to test majority of the implementation. If you take a board with two additional UARTs, then you can use the loopback mode, which will perform additional assertions by comparing data directly.

Alternatively, connect two or more boards via a RS-485 network. Let one run as master and the other as slaves.

See here for more information.

Issues/PRs references

Fixes #13869.

@github-actions github-actions bot added Area: doc Area: Documentation Area: tests Area: tests and testing framework Area: drivers Area: Device drivers Area: examples Area: Example Applications Area: Kconfig Area: Kconfig integration labels Feb 13, 2024
@basilfx basilfx force-pushed the feature/modbus_rtu_v2 branch from d6f6c6f to 3541174 Compare February 14, 2024 07:21
This adds initial support for Modbus RTU in master and slave mode. It
supports the following functions:

* MODBUS_FC_READ_COILS
* MODBUS_FC_READ_DISCRETE_INPUTS
* MODBUS_FC_READ_HOLDING_REGISTERS
* MODBUS_FC_READ_INPUT_REGISTERS
* MODBUS_FC_WRITE_SINGLE_COIL
* MODBUS_FC_WRITE_SINGLE_HOLDING_REGISTER
* MODBUS_FC_WRITE_MULTIPLE_COILS
* MODBUS_FC_WRITE_MULTIPLE_HOLDING_REGISTERS

Code has been structured to make it extensible in the future, but
preparing it for other uses than Modbus RTU is not part of this
commit.
This application will test the Modbus RTU implementation.
This example will demonstrate how to implement a Modbus slave, that
interacts with SAUL.
@basilfx basilfx force-pushed the feature/modbus_rtu_v2 branch from 3541174 to b8a4dfc Compare February 14, 2024 08:42
Comment on lines 51 to 54
/**
* @brief RTS pin. @p GPIO_UNDEF if not used.
*/
gpio_t pin_rts;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why dont you use ?

Suggested change
/**
* @brief RTS pin. @p GPIO_UNDEF if not used.
*/
gpio_t pin_rts;
gpio_t pin_rts; /**<< RTS pin. @p GPIO_UNDEF if not used.*/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update this. I just re-used what was already provided in the first PR :-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have addressed this for modbus_rtu_params_t and modbus_rtu_t. I did drop the @internal annotations, which I could not combine.

Copy link
Contributor

@MrKevinWeiss MrKevinWeiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice... I would like to see this in and would provide testing in the near future.

@@ -0,0 +1,34 @@
# Copyright (c) 2023-2024 Bas Stottelaar <[email protected]>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are no longer doing dependency modelling with kconfig... This can be dropped.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so I can just remove the Kconfig file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the Kconfig file.

@MrKevinWeiss MrKevinWeiss added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Mar 19, 2024
@riot-ci
Copy link

riot-ci commented Mar 19, 2024

Murdock results

✔️ PASSED

6f6f606 fixup! tests/drivers/modbus: add test application

Success Failures Total Runtime
10067 0 10067 09m:34s

Artifacts

@github-actions github-actions bot removed the Area: Kconfig Area: Kconfig integration label Mar 20, 2024
@basilfx basilfx force-pushed the feature/modbus_rtu_v2 branch from 4e6bfe2 to b6d822f Compare March 20, 2024 23:37
const modbus_rtu_params_t* params; /**< device parameters */
uint32_t timeout; /**< amount of time (usec) to wait for a slave to
begin sending a */
uint32_t rx_timeout; /**< time between two bytes before timeing out */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint32_t rx_timeout; /**< time between two bytes before timeing out */
uint32_t rx_timeout; /**< timeout between bytes*/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved.

modbus_t dev; /**< @ref modbus_t base class */
const modbus_rtu_params_t* params; /**< device parameters */
uint32_t timeout; /**< amount of time (usec) to wait for a slave to
begin sending a */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
begin sending a */
begin sending*/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved.

@basilfx basilfx force-pushed the feature/modbus_rtu_v2 branch from 88d0828 to 59e2a59 Compare March 25, 2024 23:30
@basilfx basilfx force-pushed the feature/modbus_rtu_v2 branch from 59e2a59 to d553305 Compare March 25, 2024 23:35
@basilfx
Copy link
Member Author

basilfx commented Mar 25, 2024

I made some additional changes to the implementation.

  • I removed message validation when using modbus_rtu_send_request. It is up to the caller to provide a valid message.
  • I also removed message validation in modbus_rtu_recv_request. Validation should now happen in the callback, because it checks 'application level' properties.
  • I removed the MODBUS_ERR_EXCEPTION error code: exceptions are a valid responses, so leave it up to the application to handle it.
  • I added support for zero-copy read requests: in this case, the message will directly point to the internal buffer, which does not need copying.
  • I added support for multi-slave implementations. Sounds like a lot, but I basically allow the request callback to handle any request message, so it is up to the callback to decide whether or not to send a response. This also allows for broadcasted requests (slave identifier == 0). It does require some additional checks in the callback, but I think its worth the flexibility.

Comment on lines 64 to 66
uint32_t timeout; /**< amount of time (usec) to wait for a slave to
begin sending */
uint32_t rx_timeout; /**< timeout between two bytes */
Copy link
Contributor

@kfessel kfessel Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u make them a little more descriptive

like "inter_fame_usec_min" and "byte_usec_max"

is that correct?

maybe its is more like frame_space_rx (1,5 chars ) , frame_space_ tx (3,5 chars)

please check me on this

there also isn't a difference in master vs slave am i wrong?

Copy link
Member Author

@basilfx basilfx Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved timeout to the modbus_rtu_params_t, and renamed rx_timeout to byte_timeout.

rx_timeout is calculated during initialization, to save some computation in hot path (debatable). I could remove it: it is based on baud rate, which was not stored before (it is via params).

Fields inside modbus_rtu_t are now marked as internal (with a comment). Also improved comments.

I think it is OK now.

@kfessel
Copy link
Contributor

kfessel commented Mar 27, 2024

@derMihai since you worked with modbus would you please take a short look

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: doc Area: Documentation Area: drivers Area: Device drivers Area: examples Area: Example Applications Area: tests Area: tests and testing framework CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request: ModBus RTU
5 participants