Commit f0db03dd4816b26cd55432aac820c61eeeccd96b
1 parent
b0c05667
New quirks handler (closes #38 #533)
Useful functions when you are confronted with equipment which does not respect the protocol, which behaves strangely or when you wish to move away from the standard. Thank you @mhei for the great initial version.
Showing
9 changed files
with
142 additions
and
6 deletions
docs/index.md
| ... | ... | @@ -251,6 +251,11 @@ error codes into error message strings; for details refer to |
| 251 | 251 | |
| 252 | 252 | ## Miscellaneous |
| 253 | 253 | |
| 254 | +To deviate from the Modbus standard, you can enable or disable quirks with: | |
| 255 | + | |
| 256 | +- [modbus_disable_quirks](modbus_disable_quirks.md) | |
| 257 | +- [modbus_enable_quirks](modbus_enable_quirks.md) | |
| 258 | + | |
| 254 | 259 | The `_LIBMODBUS_VERSION_STRING_` constant indicates the libmodbus version the |
| 255 | 260 | program has been compiled against. The variables 'libmodbus_version_major', |
| 256 | 261 | 'libmodbus_version_minor', 'libmodbus_version_micro' give the version the | ... | ... |
docs/modbus_disable_quirks.md
0 → 100644
| 1 | +# modbus_disable_quirks | |
| 2 | + | |
| 3 | +## Name | |
| 4 | + | |
| 5 | +modbus_disable_quirks - disable a list of quirks according to a mask | |
| 6 | + | |
| 7 | +## Synopsis | |
| 8 | + | |
| 9 | +```c | |
| 10 | +int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask); | |
| 11 | +``` | |
| 12 | + | |
| 13 | +## Description | |
| 14 | + | |
| 15 | +The function shall disable the quirks according to the provided mask. It's | |
| 16 | +useful to revert changes applied by a previous call to | |
| 17 | +[modbus_enable_quirks](modbus_enable_quirks.md) | |
| 18 | + | |
| 19 | +To reset all quirks, you can use the specific value `MODBUS_QUIRK_ALL`. | |
| 20 | + | |
| 21 | +```c | |
| 22 | +modbus_enable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE | MODBUS_QUIRK_REPLY_TO_BROADCAST); | |
| 23 | + | |
| 24 | +... | |
| 25 | + | |
| 26 | +// Reset all quirks | |
| 27 | +modbus_disable_quirks(ctx, MODBUS_QUIRK_ALL); | |
| 28 | +``` | |
| 29 | + | |
| 30 | +## Return value | |
| 31 | + | |
| 32 | +The function shall return 0 if successful. Otherwise it shall return -1 and set | |
| 33 | +errno. | |
| 34 | + | |
| 35 | +## See also | |
| 36 | + | |
| 37 | +- [modbus_enable_quirks](modbus_enable_quirks.md) | ... | ... |
docs/modbus_enable_quirks.md
0 → 100644
| 1 | +# modbus_enable_quirks | |
| 2 | + | |
| 3 | +## Name | |
| 4 | + | |
| 5 | +modbus_enable_quirks - enable a list of quirks according to a mask | |
| 6 | + | |
| 7 | +## Synopsis | |
| 8 | + | |
| 9 | +```c | |
| 10 | +int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask); | |
| 11 | +``` | |
| 12 | + | |
| 13 | +## Description | |
| 14 | + | |
| 15 | +The function is only useful when you are confronted with equipment which does | |
| 16 | +not respect the protocol, which behaves strangely or when you wish to move away | |
| 17 | +from the standard. | |
| 18 | + | |
| 19 | +In that case, you can enable a specific quirk to workaround the issue, libmodbus | |
| 20 | +offers the following flags: | |
| 21 | + | |
| 22 | +- `MODBUS_QUIRK_MAX_SLAVE` allows slave adresses between 247 and 255. | |
| 23 | +- `MODBUS_QUIRK_REPLY_TO_BROADCAST` force a reply to a broacast request when the | |
| 24 | + device is a slave in RTU mode (should be enabled on the slave device). | |
| 25 | + | |
| 26 | +You can combine the flags by using the OR logical operator. | |
| 27 | + | |
| 28 | +## Return value | |
| 29 | + | |
| 30 | +The function shall return 0 if successful. Otherwise it shall return -1 and set | |
| 31 | +errno. | |
| 32 | + | |
| 33 | +## See also | |
| 34 | + | |
| 35 | +- [modbus_disable_quirks](modbus_disable_quirks.md) | ... | ... |
src/modbus-private.h
src/modbus-rtu.c
| ... | ... | @@ -91,8 +91,10 @@ static const uint8_t table_crc_lo[] = { |
| 91 | 91 | * internal slave ID in slave mode */ |
| 92 | 92 | static int _modbus_set_slave(modbus_t *ctx, int slave) |
| 93 | 93 | { |
| 94 | + int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247; | |
| 95 | + | |
| 94 | 96 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ |
| 95 | - if (slave >= 0 && slave <= 247) { | |
| 97 | + if (slave >= 0 && slave <= max_slave) { | |
| 96 | 98 | ctx->slave = slave; |
| 97 | 99 | } else { |
| 98 | 100 | errno = EINVAL; | ... | ... |
src/modbus-tcp.c
| ... | ... | @@ -76,8 +76,10 @@ static int _modbus_tcp_init_win32(void) |
| 76 | 76 | |
| 77 | 77 | static int _modbus_set_slave(modbus_t *ctx, int slave) |
| 78 | 78 | { |
| 79 | + int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247; | |
| 80 | + | |
| 79 | 81 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ |
| 80 | - if (slave >= 0 && slave <= 247) { | |
| 82 | + if (slave >= 0 && slave <= max_slave) { | |
| 81 | 83 | ctx->slave = slave; |
| 82 | 84 | } else if (slave == MODBUS_TCP_SLAVE) { |
| 83 | 85 | /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to | ... | ... |
src/modbus.c
No preview for this file type
src/modbus.h
| ... | ... | @@ -176,6 +176,14 @@ typedef enum |
| 176 | 176 | MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2) |
| 177 | 177 | } modbus_error_recovery_mode; |
| 178 | 178 | |
| 179 | +typedef enum | |
| 180 | +{ | |
| 181 | + MODBUS_QUIRK_NONE = 0, | |
| 182 | + MODBUS_QUIRK_MAX_SLAVE = (1<<1), | |
| 183 | + MODBUS_QUIRK_REPLY_TO_BROADCAST = (1<<2), | |
| 184 | + MODBUS_QUIRK_ALL = 0xFF | |
| 185 | +} modbus_quirks; | |
| 186 | + | |
| 179 | 187 | MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave); |
| 180 | 188 | MODBUS_API int modbus_get_slave(modbus_t* ctx); |
| 181 | 189 | MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery); |
| ... | ... | @@ -237,6 +245,8 @@ MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req, |
| 237 | 245 | int req_length, modbus_mapping_t *mb_mapping); |
| 238 | 246 | MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, |
| 239 | 247 | unsigned int exception_code); |
| 248 | +MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask); | |
| 249 | +MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask); | |
| 240 | 250 | |
| 241 | 251 | /** |
| 242 | 252 | * UTILS FUNCTIONS | ... | ... |
tests/unit-test-client.c
| ... | ... | @@ -451,9 +451,26 @@ int main(int argc, char *argv[]) |
| 451 | 451 | printf("* modbus_write_registers: "); |
| 452 | 452 | ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); |
| 453 | 453 | |
| 454 | - /** SLAVE REPLY **/ | |
| 454 | + /** SLAVE ADDRESS **/ | |
| 455 | 455 | old_slave = modbus_get_slave(ctx); |
| 456 | 456 | |
| 457 | + printf("\nTEST SLAVE ADDRESS:\n"); | |
| 458 | + | |
| 459 | + printf("1/2 Not compliant slave address is refused: "); | |
| 460 | + rc = modbus_set_slave(ctx, 248); | |
| 461 | + ASSERT_TRUE(rc == -1, "Slave address of 248 shouldn't be allowed"); | |
| 462 | + | |
| 463 | + printf("2/2 Not compliant slave address is allowed: "); | |
| 464 | + modbus_enable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE); | |
| 465 | + rc = modbus_set_slave(ctx, 248); | |
| 466 | + ASSERT_TRUE(rc == 0, "Not compliant slave address should have been accepted"); | |
| 467 | + | |
| 468 | + modbus_disable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE); | |
| 469 | + rc = modbus_set_slave(ctx, old_slave); | |
| 470 | + ASSERT_TRUE(rc == 0, "Uanble to restore slave value") | |
| 471 | + | |
| 472 | + /** SLAVE REPLY **/ | |
| 473 | + | |
| 457 | 474 | printf("\nTEST SLAVE REPLY:\n"); |
| 458 | 475 | modbus_set_slave(ctx, INVALID_SERVER_ID); |
| 459 | 476 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, | ... | ... |