Skip to content

Support for Device / Vendor specific Modbus functions#113

Draft
GraceGRD wants to merge 1 commit into
debevv:masterfrom
GraceGRD:custom_function
Draft

Support for Device / Vendor specific Modbus functions#113
GraceGRD wants to merge 1 commit into
debevv:masterfrom
GraceGRD:custom_function

Conversation

@GraceGRD
Copy link
Copy Markdown

@GraceGRD GraceGRD commented Feb 10, 2026

This PR allows support for implementing Device / Vendor specific functions.

Enable this feature by defining #define NMBS_SERVER_CUSTOM_FUNCTION_ENABLED.

I have tested this only for the Modbus RTU.

The handle_req_fc is modified to any function codes that are not handled by the nanoModbus library through the default case to be handled by handle_custom_function.

The handle_custom_function does only validate the CRC of the frame before passing the data and size through the callback function. The user is responsible for modifying the data buffer and patching the size. Then the function appends the CRC and sends back the response.

For this function to work the size of the Modbus frame must be known. I decided to pass this through the nmbs->platform.arg pointer like this (I'm not sure if this is the way to do it however this works for me):

    // Set platform.arg to RX DMA count so that the Modbus functions know the frame size.
    static uint16_t size;
    size= Uart_BytesToRead(Modbus_Uart_GetInstance());
    if (size> 2)
    {
      size-= 2; // Exclude CRC bytes
      nmbs_set_platform_arg(inNmbs, &size);
      nmbs_server_poll(inNmbs);
    }

Then register a handler for the custom function (example custom function below):
platform_conf.custom_function = CustomFunctionHandler;

nmbs_error CustomFunctionHandler(uint8_t code, uint8_t *data, uint16_t *size)
{
  switch (inFunctionCode)
  {
    case 0x41:
      return GetParam(data, size);
    default:
      return NMBS_EXCEPTION_ILLEGAL_FUNCTION;
  }
}
static nmbs_error GetParam(uint8_t *data, uint16_t *size)
{
  // Validate frame size
  if (*size != 2)
  {
    return NMBS_EXCEPTION_ILLEGAL_DATA_VALUE;
  }

  uint16_t addr = (data[0] << 8) | data[1];
  uint16_t value;

  // Obtain value of parameter
  if (!Config_GetParam(addr, &value))
  {

    return NMBS_EXCEPTION_SERVER_DEVICE_FAILURE;
  }

  // Encode parameter (shouldn't be necessary  as it is already there)
  data[0] = (uint8_t) (addr >> 8);
  data[1] = (uint8_t) addr;

  // Encode value
  data[2] = (uint8_t) (value >> 8);
  data[3] = (uint8_t) value;

  // Update the size
  *size = 4;

  return NMBS_ERROR_NONE;
}

@GraceGRD GraceGRD marked this pull request as draft February 10, 2026 07:52
@GraceGRD
Copy link
Copy Markdown
Author

Marked it as draft as I do believe this is a useful feature for the library however, I don't think this is ready for production yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant