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

feature/add user error handler #62

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
107 changes: 106 additions & 1 deletion docs/en/master_api_overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ The following overview describes how to setup Modbus master communication. The o
2. :ref:`modbus_api_master_configure_descriptor` - Configure data descriptors to access slave parameters.
3. :ref:`modbus_api_master_setup_communication_options` - Allows to setup communication options for selected port.
4. :ref:`modbus_api_master_start_communication` - Start stack and sending / receiving data.
5. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources.
5. :ref:`modbus_api_master_expose_information` - Expose extra information from stack.
6. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources.

.. _modbus_api_master_configure_descriptor:

Expand Down Expand Up @@ -433,6 +434,110 @@ The function writes characteristic's value defined as a name and cid parameter i
ESP_LOGE(TAG, "Set data fail, err = 0x%x (%s).", (int)err, (char*)esp_err_to_name(err));
}

.. _modbus_api_master_expose_information:

Expose Extra Information
^^^^^^^^^^^^^^^^^^^^^^^^

In case the does not clarify some information, such as slave exception code returned in the response, the functions below can be useful.

:cpp:func:`mbc_master_get_transaction_info`

Allows to return the below information as a :cpp:type:`mb_trans_info_t` structure.

.. list-table:: Table 4 Transaction extended information
:widths: 2 68
:header-rows: 1

* - Field
- Description
* - uint64_t ``trans_id``
- The unique transaction identificator stored as uint64_t timestamp.
* - uint8_t ``dest_addr``
- Destination short address (or UID - Unit Identificator) of the slave being accessed.
* - uint8_t ``func_code``
- The last transaction function code.
* - uint8_t ``exception``
- The last transaction exception code returned by slave. :cpp:type:`eMBException`.
* - uint16_t ``err_type``
- The last transaction error type.
:cpp:enumerator:`EV_ERROR_INIT` = 0, No error, initial state or the request is in progress.
:cpp:enumerator:`EV_ERROR_RESPOND_TIMEOUT` = 1, Slave respond timeout. No response during response timeout.
:cpp:enumerator:`EV_ERROR_RECEIVE_DATA` = 2, Receive frame data error.
:cpp:enumerator:`EV_ERROR_EXECUTE_FUNCTION` = 3, Execute function error. Function is not supported or slave returned an error.
:cpp:enumerator:`EV_ERROR_OK` = 4, No error, processing completed successfully.

.. warning:: The functionality described in this section is for advanced users and should to be handled correctly.

.. note:: The above function returns the latest transaction information which may not be actual if another IO call is performed from higher priority task right before the :cpp:func:`mbc_master_get_transaction_info`. In this case the ``trans_id`` field can clarify if the returned information is obsolete. The transaction ID is just a timestamp of type `uint64_t` returned by function `esp_timer_get_time()`. In this case it is possible determining if the information retrieved corresponds to the actual request using timestamp kept before the IO call and transaction identificator.

.. code:: c

#define MAX_TRANSACTION_TOUT_US 640000

uint64_t start_timestamp = esp_timer_get_time(); // Get current timestamp in microseconds
esp_err_t err = mbc_master_get_parameter(param_descriptor->cid, (char*)param_descriptor->param_key, (uint8_t*)temp_data, &type);

mb_trans_info_t tinfo = {0};
if (mbc_master_get_transaction_info(&tinfo) == ESP_OK) {
ESP_LOGI("TRANSACTION_INFO", "Id: %" PRIu64 ", Addr: %x, FC: %x, Exp: %u, Err: %x",
(uint64_t)tinfo.trans_id, (int)tinfo.dest_addr,
(unsigned)tinfo.func_code, (unsigned)tinfo.exception,
(int)tinfo.err_type);
}

if (tinfo.trans_id >= (start_timestamp + MAX_TRANSACTION_TOUT_US)) {
ESP_LOGI("TRANSACTION_INFO", "Transaction Id: %" PRIu64 " is expired", tinfo.trans_id);
}

Below is the way to expose the transaction information and request/response buffers defining the user error handling function. This funcion defined as described in the code below will be executed from internal final state machine before returning from blocking :cpp:func:`mbc_master_set_parameter` or :cpp:func:`mbc_master_get_parameter` functions and expose the internal parameters.

.. code:: c

#define MB_PDU_DATA_OFF 1

#define EV_ERROR_EXECUTE_FUNCTION 3

void vMBMasterErrorCBUserHandler( uint64_t trans_id, uint16_t err_type, uint8_t dest_addr, const uint8_t *precv_buf, uint16_t recv_length,
const uint8_t *psent_buf, uint16_t sent_length )
{
ESP_LOGW("USER_ERR_CB", "The transaction %" PRIu64 ", error type: %u", trans_id, err_type);
if ((err_type == EV_ERROR_EXECUTE_FUNCTION) && precv_buf && recv_length) {
ESP_LOGW("USER_ERR_CB", "The command is unsupported or an exception on slave happened: %x", (int)precv_buf[MB_PDU_DATA_OFF]);
}
if (precv_buf && recv_length) {
ESP_LOG_BUFFER_HEX_LEVEL("Received buffer", (void *)precv_buf, (uint16_t)recv_length, ESP_LOG_WARN);
}
if (psent_buf && sent_length) {
ESP_LOG_BUFFER_HEX_LEVEL("Sent buffer", (void *)psent_buf, (uint16_t)sent_length, ESP_LOG_WARN);
}
}

.. list-table:: Table 5 Transaction user handler parameters
:widths: 2 68
:header-rows: 1

* - Field
- Description
* - uint64_t ``trans_id``;
- The unique transaction identificator stored as uint64_t timestamp.
* - uint16_t ``err_type``;
- The last transaction error type.
* - uint8_t ``dest_addr``;
- Destination short address (or UID - Unit Identificator) of the slave being accessed.
* - ``precv_buf``;
- The last transaction internal receive buffer pointer that points to the Modbus PDU frame. NULL - not actual.
* - ``recv_length``;
- The last transaction receive buffer length.
* - ``psent_buf``;
- The last transaction internal sent buffer pointer that points to the Modbus PDU frame. NULL - not actual.
* - ``sent_length``;
- The last transaction sent buffer length.

The user handler function can be useful to check the Modbus frame buffers and expose some information right before returning from the call :cpp:func:`mbc_master_set_parameter` or :cpp:func:`mbc_master_get_parameter` functions.

.. warning:: The above handler function may prevent the Modbus FSM to work properly! The body of the handler needs to be as short as possible and contain just simple functionality that will not block processing for relatively long time. This is user software responcibility to not break the Modbus functionality using the function.

.. _modbus_api_master_destroy:

Modbus Master Teardown
Expand Down
24 changes: 20 additions & 4 deletions freemodbus/common/esp_modbus_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ esp_err_t mbc_master_start(void)
}

eMBErrorCode eMBMasterRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
USHORT usNDiscrete)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
Expand All @@ -194,7 +194,7 @@ eMBErrorCode eMBMasterRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
}

eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
Expand All @@ -209,7 +209,7 @@ eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
}

eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
Expand All @@ -224,7 +224,7 @@ eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
}

eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
USHORT usNRegs)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
Expand All @@ -237,6 +237,22 @@ eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
return error;
}

/**
* Helper function to get current transaction info
*/
esp_err_t mbc_master_get_transaction_info(mb_trans_info_t *ptinfo)
{
MB_MASTER_CHECK((ptinfo),
ESP_ERR_INVALID_ARG,
"Wrong argument.");
MB_MASTER_CHECK(xMBMasterGetLastTransactionInfo(&ptinfo->trans_id, &ptinfo->dest_addr,
&ptinfo->func_code, &ptinfo->exception,
&ptinfo->err_type),
ESP_ERR_INVALID_STATE,
"Master can not get transaction info.");
return ESP_OK;
}

// Helper function to set parameter buffer according to its type
esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size)
{
Expand Down
23 changes: 23 additions & 0 deletions freemodbus/common/include/esp_modbus_master.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ typedef struct {
uint16_t reg_size; /*!< Modbus number of registers */
} mb_param_request_t;

/**
* @brief Modbus transacion info structure
*/
typedef struct {
uint64_t trans_id; /*!< Modbus unique transaction identificator */
uint16_t err_type; /*!< Modbus last transaction error type */
uint8_t dest_addr; /*!< Modbus destination short address (or UID) */
uint8_t func_code; /*!< Modbus last transaction function code */
uint8_t exception; /*!< Modbus last transaction exception code returned by slave */
} mb_trans_info_t;

/**
* @brief Initialize Modbus controller and stack for TCP port
*
Expand Down Expand Up @@ -321,6 +332,18 @@ esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uin
*/
esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size);

/**
* @brief The helper function to expose transaction info from modbus layer
*
* @param[in] ptinfo the pointer to transaction info structure
*
* @return
* - esp_err_t ESP_OK - the transaction info is saved in the appropriate parameter structure
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
*/
esp_err_t mbc_master_get_transaction_info(mb_trans_info_t *ptinfo);

#ifdef __cplusplus
}
#endif
Expand Down
1 change: 1 addition & 0 deletions freemodbus/common/mbc_master.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "esp_modbus_common.h" // for common types
#include "esp_modbus_master.h" // for public master types
#include "esp_modbus_callbacks.h"
#include "mb_m.h" // this is required to expose current transaction info

/* ----------------------- Defines ------------------------------------------*/

Expand Down
2 changes: 1 addition & 1 deletion freemodbus/modbus/ascii/mbascii_m.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ typedef enum
/* These Modbus values are shared in ASCII mode*/
extern volatile UCHAR ucMasterRcvBuf[];
extern volatile UCHAR ucMasterSndBuf[];
extern volatile eMBMasterTimerMode eMasterCurTimerMode;
//extern volatile eMBMasterTimerMode eMasterCurTimerMode;

/* ----------------------- Static functions ---------------------------------*/
static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter );
Expand Down
33 changes: 20 additions & 13 deletions freemodbus/modbus/include/mb_m.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,15 @@ typedef enum
*/
typedef enum
{
MB_TMODE_T35, /*!< Master receive frame T3.5 timeout. */
MB_TMODE_RESPOND_TIMEOUT, /*!< Master wait respond for slave. */
MB_TMODE_CONVERT_DELAY /*!< Master sent broadcast ,then delay sometime.*/
MB_TMODE_T35, /*!< Master receive frame T3.5 timeout. */
MB_TMODE_RESPOND_TIMEOUT, /*!< Master wait respond for slave. */
MB_TMODE_CONVERT_DELAY /*!< Master sent broadcast ,then delay sometime.*/
} eMBMasterTimerMode;

extern _lock_t xMBMLock; // Modbus lock object

#define MB_ATOMIC_SECTION CRITICAL_SECTION(xMBMLock)

/* ----------------------- Function prototypes ------------------------------*/
/*! \ingroup modbus
* \brief Initialize the Modbus Master protocol stack.
Expand All @@ -128,7 +132,7 @@ typedef enum
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort,
ULONG ulBaudRate, eMBParity eParity );
ULONG ulBaudRate, eMBParity eParity );

/*! \ingroup modbus
* \brief Initialize the Modbus Master protocol stack for Modbus TCP.
Expand Down Expand Up @@ -220,7 +224,7 @@ eMBErrorCode eMBMasterPoll( void );
* valid it returns eMBErrorCode::MB_EINVAL.
*/
eMBErrorCode eMBMasterRegisterCB( UCHAR ucFunctionCode,
pxMBFunctionHandler pxHandler );
pxMBFunctionHandler pxHandler );

/* ----------------------- Callback -----------------------------------------*/

Expand Down Expand Up @@ -261,7 +265,7 @@ eMBErrorCode eMBMasterRegisterCB( UCHAR ucFunctionCode,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs );
USHORT usNRegs );

/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Holding Register</em> value is
Expand Down Expand Up @@ -290,7 +294,7 @@ eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode );
USHORT usNRegs, eMBRegisterMode eMode );

/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Coil Register</em> value is
Expand Down Expand Up @@ -319,7 +323,7 @@ eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode );
USHORT usNCoils, eMBRegisterMode eMode );

/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Input Discrete Register</em> value is
Expand All @@ -342,7 +346,7 @@ eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete );
USHORT usNDiscrete );

/*! \ingroup modbus
*\brief These Modbus functions are called for user when Modbus run in Master Mode.
Expand All @@ -353,20 +357,20 @@ eMBMasterReqErrCode
eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr,
USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut );
USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr,
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut );
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut );
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut );

Expand Down Expand Up @@ -409,6 +413,9 @@ eMBMasterErrorEventType eMBMasterGetErrorType( void );
void vMBMasterSetErrorType( eMBMasterErrorEventType errorType );
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void );
eMBMode ucMBMasterGetCommMode( void );
BOOL xMBMasterGetLastTransactionInfo( uint64_t *pxTransId, UCHAR *pucDestAddress,
UCHAR *pucFunctionCode, UCHAR *pucException,
USHORT *pusErrorType );

/* ----------------------- Callback -----------------------------------------*/

Expand Down
27 changes: 16 additions & 11 deletions freemodbus/modbus/include/mbport.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

#include "mbconfig.h" // for options


#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
Expand Down Expand Up @@ -92,10 +93,10 @@ typedef enum {
} eMBMasterErrorEventType;

typedef struct _MbEventType {
eMBMasterEventEnum eEvent; /*!< event itself. */
uint64_t xTransactionId; /*!< ID of the transaction */
uint64_t xPostTimestamp; /*!< timestamp of event posted */
uint64_t xGetTimestamp; /*!< timestamp of event get */
eMBMasterEventEnum eEvent; /*!< event itself. */
uint64_t xTransactionId; /*!< ID of the transaction */
uint64_t xPostTimestamp; /*!< timestamp of event posted */
uint64_t xGetTimestamp; /*!< timestamp of event get */
} xMBMasterEventType;

#endif
Expand Down Expand Up @@ -207,16 +208,20 @@ void vMBMasterPortTimersDisable( void );


/* ----------------- Callback for the master error process ------------------*/
void vMBMasterErrorCBRespondTimeout( UCHAR ucDestAddress, const UCHAR* pucPDUData,
USHORT ucPDULength );
void vMBMasterErrorCBRespondTimeout( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucSendData, USHORT ucSendLength );

void vMBMasterErrorCBReceiveData( UCHAR ucDestAddress, const UCHAR* pucPDUData,
USHORT ucPDULength );
void vMBMasterErrorCBReceiveData( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );

void vMBMasterErrorCBExecuteFunction( UCHAR ucDestAddress, const UCHAR* pucPDUData,
USHORT ucPDULength );
void vMBMasterErrorCBExecuteFunction( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );

void vMBMasterCBRequestSuccess( void );
void vMBMasterCBRequestSuccess( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );
#endif
/* ----------------------- Callback for the protocol stack ------------------*/
/*!
Expand Down