Adicionar primeiro

This commit is contained in:
2025-06-06 21:17:25 +01:00
parent c188084ba4
commit 282e7f517b
841 changed files with 199592 additions and 1 deletions

View File

@@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// Stack callback functions prototypes
#ifndef _ESP_MODBUS_CALLBACKS_H_
#define _ESP_MODBUS_CALLBACKS_H_
#include "mb.h"
#include "mb_m.h"
typedef eMBErrorCode (*reg_input_cb)(UCHAR*, USHORT, USHORT);
typedef eMBErrorCode (*reg_holding_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_coils_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_discrete_cb)(UCHAR*, USHORT, USHORT);
#endif /* _ESP_MODBUS_CALLBACKS_H_ */

View File

@@ -0,0 +1,532 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "mbc_master.h" // for master interface define
#include "esp_modbus_master.h" // for public interface defines
#include "esp_modbus_callbacks.h" // for callback functions
#include "sdkconfig.h"
static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_MASTER";
// This file implements public API for Modbus master controller.
// These functions are wrappers for interface functions of the controller
static mb_master_interface_t* master_interface_ptr = NULL;
void mbc_master_init_iface(void* handler)
{
master_interface_ptr = (mb_master_interface_t*) handler;
}
/**
* Modbus controller destroy function
*/
esp_err_t mbc_master_destroy(void)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->destroy != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->destroy();
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master destroy failure, error=(0x%x).",
(int)error);
return error;
}
esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_info)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->get_cid_info != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->get_cid_info(cid, param_info);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master get cid info failure, error=(0x%x).",
(int)error);
return error;
}
/**
* Get parameter data for corresponding characteristic
*/
esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->get_parameter != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->get_parameter(cid, name, value, type);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master get parameter failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return error;
}
/**
* Send custom Modbus request defined as mb_param_request_t structure
*/
esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->send_request != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->send_request(request, data_ptr);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master send request failure error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Set Modbus parameter description table
*/
esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor,
const uint16_t num_elements)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->set_descriptor != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->set_descriptor(descriptor, num_elements);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master set descriptor failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Set parameter value for characteristic selected by name and cid
*/
esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->set_parameter != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->set_parameter(cid, name, value, type);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master set parameter failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Setup Modbus controller parameters
*/
esp_err_t mbc_master_setup(void* comm_info)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->setup != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->setup(comm_info);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master setup failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Modbus controller stack start function
*/
esp_err_t mbc_master_start(void)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->start != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->start();
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master start failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
eMBErrorCode eMBMasterRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_discrete != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
return error;
}
eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_coils != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_coils(pucRegBuffer, usAddress,
usNCoils, eMode);
return error;
}
eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_holding != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_holding(pucRegBuffer, usAddress,
usNRegs, eMode);
return error;
}
eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_input != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
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)
{
esp_err_t err = ESP_OK;
MB_RETURN_ON_FALSE((src), ESP_ERR_INVALID_STATE, TAG,"incorrect data pointer.");
MB_RETURN_ON_FALSE((dest), ESP_ERR_INVALID_STATE, TAG,"incorrect data pointer.");
void *pdest = dest;
void *psrc = src;
// Transfer parameter data into value of characteristic
switch(param_type)
{
case PARAM_TYPE_U8:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8) {
*((uint8_t*)pdest) = *((uint8_t*)psrc);
}
break;
case PARAM_TYPE_U16:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) {
*((uint16_t*)pdest) = *((uint16_t*)psrc);
}
break;
case PARAM_TYPE_U32:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
*((uint32_t*)pdest) = *((uint32_t*)psrc);
}
break;
case PARAM_TYPE_FLOAT:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
*((float*)pdest) = *(float*)psrc;
}
break;
case PARAM_TYPE_ASCII:
case PARAM_TYPE_BIN:
memcpy((void*)dest, (void*)src, (size_t)param_size);
break;
#if CONFIG_FMB_EXT_TYPE_SUPPORT
case PARAM_TYPE_I8_A:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
mb_set_int8_a((val_16_arr *)pdest, (*(int8_t*)psrc));
ESP_LOGV(TAG, "Convert uint8 B[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I8_B:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
mb_set_int8_b((val_16_arr *)pdest, (int8_t)((*(uint16_t*)psrc) >> 8));
ESP_LOGV(TAG, "Convert int8 A[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U8_A:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
mb_set_uint8_a((val_16_arr *)pdest, (*(uint8_t*)psrc));
ESP_LOGV(TAG, "Convert uint8 A[%d] 0x%02" PRIx16 " = %02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U8_B:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
uint8_t data = (uint8_t)((*(uint16_t*)psrc) >> 8);
mb_set_uint8_b((val_16_arr *)pdest, data);
ESP_LOGV(TAG, "Convert uint8 B[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I16_AB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I16) {
mb_set_int16_ab((val_16_arr *)pdest, *(int16_t*)psrc);
ESP_LOGV(TAG, "Convert int16 AB[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I16_BA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I16) {
mb_set_int16_ba((val_16_arr *)pdest, *(int16_t*)psrc);
ESP_LOGV(TAG, "Convert int16 BA[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U16_AB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) {
mb_set_uint16_ab((val_16_arr *)pdest, *(uint16_t*)psrc);
ESP_LOGV(TAG, "Convert uint16 AB[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U16_BA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) {
mb_set_uint16_ba((val_16_arr *)pdest, *(uint16_t*)psrc);
ESP_LOGV(TAG, "Convert uint16 BA[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I32_ABCD:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_abcd((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_ABCD:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_abcd((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_ABCD:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_abcd((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I32_CDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_cdab((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_CDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_cdab((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_CDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_cdab((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I32_BADC:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_badc((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_BADC:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_badc((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_BADC:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_badc((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I32_DCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_dcba((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_DCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_dcba((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_DCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_dcba((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I64_ABCDEFGH:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_abcdefgh((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_ABCDEFGH:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_abcdefgh((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert double ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_ABCDEFGH:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_abcdefgh((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_I64_HGFEDCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_hgfedcba((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_HGFEDCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_hgfedcba((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert double HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_HGFEDCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_hgfedcba((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_I64_GHEFCDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_ghefcdab((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_GHEFCDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_ghefcdab((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert uint64 GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_GHEFCDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_ghefcdab((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_I64_BADCFEHG:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_badcfehg((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_BADCFEHG:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_badcfehg((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert uint64 BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_BADCFEHG:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_badcfehg((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
#endif
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u).",
__FUNCTION__, (unsigned)param_type);
err = ESP_ERR_NOT_SUPPORTED;
break;
}
return err;
}

View File

@@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "mbc_master.h" // for master interface define
#include "esp_modbus_master.h" // for public slave defines
#include "mbc_serial_master.h" // for public interface defines
/**
* Initialization of Modbus master serial
*/
esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler)
{
void* port_handler = NULL;
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
switch(port_type)
{
case MB_PORT_SERIAL_MASTER:
error = mbc_serial_master_create(&port_handler);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
}
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_master_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_master.h" // for public interface defines
#include "mbc_tcp_master.h" // for public interface defines
/**
* Initialization of Modbus TCP Master controller interface
*/
esp_err_t mbc_master_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_master_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_master_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -0,0 +1,530 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_timer.h" // for esp_timer_get_time()
#include "sdkconfig.h" // for KConfig defines
#include "mbc_slave.h" // for slave private type definitions
#include "mbutils.h" // for stack bit setting utilities
#include "esp_modbus_common.h" // for common defines
#include "esp_modbus_slave.h" // for public slave defines
#include "esp_modbus_callbacks.h" // for modbus callbacks function pointers declaration
#ifdef CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT
#define MB_ID_BYTE0(id) ((uint8_t)(id))
#define MB_ID_BYTE1(id) ((uint8_t)(((uint16_t)(id) >> 8) & 0xFF))
#define MB_ID_BYTE2(id) ((uint8_t)(((uint32_t)(id) >> 16) & 0xFF))
#define MB_ID_BYTE3(id) ((uint8_t)(((uint32_t)(id) >> 24) & 0xFF))
#define MB_CONTROLLER_SLAVE_ID (CONFIG_FMB_CONTROLLER_SLAVE_ID)
#define MB_SLAVE_ID_SHORT (MB_ID_BYTE3(MB_CONTROLLER_SLAVE_ID))
// Slave ID constant
static uint8_t mb_slave_id[] = { MB_ID_BYTE0(MB_CONTROLLER_SLAVE_ID),
MB_ID_BYTE1(MB_CONTROLLER_SLAVE_ID),
MB_ID_BYTE2(MB_CONTROLLER_SLAVE_ID) };
#endif
#define REG_SIZE(type, nregs) ((type == MB_PARAM_INPUT) || (type == MB_PARAM_HOLDING)) ? (nregs >> 1) : (nregs << 3)
// Common interface pointer for slave port
static mb_slave_interface_t* slave_interface_ptr = NULL;
static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_SLAVE";
// Searches the register in the area specified by type, returns descriptor if found, else NULL
static mb_descr_entry_t* mbc_slave_find_reg_descriptor(mb_param_type_t type, uint16_t addr, size_t regs)
{
mb_descr_entry_t* it;
uint16_t reg_size = 0;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
if (LIST_EMPTY(&mbs_opts->mbs_area_descriptors[type])) {
return NULL;
}
// search for the register in each area
for (it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[type]); it != NULL; it = LIST_NEXT(it, entries)) {
reg_size = REG_SIZE(type, it->size);
if ((addr >= it->start_offset)
&& (it->p_data)
&& (regs >= 1)
&& ((addr + regs) <= (it->start_offset + reg_size))
&& (reg_size >= 1)) {
return it;
}
}
return NULL;
}
static void mbc_slave_free_descriptors(void) {
mb_descr_entry_t* it;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
for (int descr_type = 0; descr_type < MB_PARAM_COUNT; descr_type++) {
while ((it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[descr_type]))) {
LIST_REMOVE(it, entries);
free(it);
}
}
}
void mbc_slave_init_iface(void* handler)
{
slave_interface_ptr = (mb_slave_interface_t*) handler;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
// Initialize list head for register areas
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_COIL]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE]);
}
/**
* Modbus controller destroy function
*/
esp_err_t mbc_slave_destroy(void)
{
esp_err_t error = ESP_OK;
// Is initialization done?
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
// Check if interface has been initialized
MB_SLAVE_CHECK((slave_interface_ptr->destroy != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
// Call the slave port destroy function
error = slave_interface_ptr->destroy();
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave destroy failure error=(0x%x).",
(int)error);
// Destroy all opened descriptors
mbc_slave_free_descriptors();
free(slave_interface_ptr);
slave_interface_ptr = NULL;
return error;
}
/**
* Setup Modbus controller parameters
*/
esp_err_t mbc_slave_setup(void* comm_info)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->setup != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->setup(comm_info);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave setup failure error=(0x%x).",
(int)error);
return error;
}
/**
* Start Modbus controller start function
*/
esp_err_t mbc_slave_start(void)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->start != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
#ifdef CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT
// Set the slave ID if the KConfig option is selected
eMBErrorCode status = eMBSetSlaveID(MB_SLAVE_ID_SHORT, TRUE, (UCHAR*)mb_slave_id, sizeof(mb_slave_id));
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack set slave ID failure.");
#endif
error = slave_interface_ptr->start();
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave start failure error=(0x%x).",
(int)error);
return error;
}
/**
* Blocking function to get event on parameter group change for application task
*/
mb_event_group_t mbc_slave_check_event(mb_event_group_t group)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EVENT_NO_EVENTS,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->check_event != NULL),
MB_EVENT_NO_EVENTS,
"Slave interface is not correctly initialized.");
mb_event_group_t event = slave_interface_ptr->check_event(group);
return event;
}
/**
* Function to get notification about parameter change from application task
*/
esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->get_param_info != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->get_param_info(reg_info, timeout);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave get parameter info failure error=(0x%x).",
(int)error);
return error;
}
/**
* Function to set area descriptors for modbus parameters
*/
esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->set_descriptor != NULL) {
error = slave_interface_ptr->set_descriptor(descr_data);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave set descriptor failure error=(0x%x).",
(int)error);
} else {
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
// Check if the address is already in the descriptor list
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(descr_data.type, descr_data.start_offset, 1);
MB_SLAVE_CHECK((it == NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor or already defined.");
mb_descr_entry_t* new_descr = (mb_descr_entry_t*) heap_caps_malloc(sizeof(mb_descr_entry_t),
MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
MB_SLAVE_CHECK((new_descr != NULL), ESP_ERR_NO_MEM, "mb can not allocate memory for descriptor.");
new_descr->start_offset = descr_data.start_offset;
new_descr->type = descr_data.type;
new_descr->p_data = descr_data.address;
new_descr->size = descr_data.size;
LIST_INSERT_HEAD(&mbs_opts->mbs_area_descriptors[descr_data.type], new_descr, entries);
error = ESP_OK;
}
return error;
}
// The helper function to get time stamp in microseconds
static uint64_t mbc_slave_get_time_stamp(void)
{
uint64_t time_stamp = esp_timer_get_time();
return time_stamp;
}
// Helper function to send parameter information to application task
static esp_err_t mbc_slave_send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
uint8_t* par_address, uint16_t par_size)
{
MB_SLAVE_ASSERT(slave_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
esp_err_t error = ESP_FAIL;
mb_param_info_t par_info;
// Check if queue is not full the send parameter information
par_info.type = par_type;
par_info.size = par_size;
par_info.address = par_address;
par_info.time_stamp = mbc_slave_get_time_stamp();
par_info.mb_offset = mb_offset;
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle, &par_info, MB_PAR_INFO_TOUT);
if (pdTRUE == status) {
ESP_LOGD(TAG, "Queue send parameter info (type, address, size): %d, 0x%" PRIx32 ", %u",
(int)par_type, (uint32_t)par_address, (unsigned)par_size);
error = ESP_OK;
} else if (errQUEUE_FULL == status) {
ESP_LOGD(TAG, "Parameter queue is overflowed.");
}
return error;
}
// Helper function to send notification
static esp_err_t mbc_slave_send_param_access_notification(mb_event_group_t event)
{
MB_SLAVE_ASSERT(slave_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
esp_err_t err = ESP_FAIL;
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group, (EventBits_t)event);
if (bits & event) {
ESP_LOGD(TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (int)event);
err = ESP_OK;
}
return err;
}
/*
* Below are the common slave read/write register callback functions
* The concrete slave port can override them using interface function pointers
*/
// Callback function for reading of MB Input Registers
eMBErrorCode mbc_reg_input_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
address--; // address of register is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_INPUT, address, n_regs);
if (it != NULL) {
uint16_t input_reg_start = (uint16_t)it->start_offset; // Get Modbus start address
uint8_t* input_buffer = (uint8_t*)it->p_data; // Get instance address
uint16_t regs = n_regs;
uint16_t reg_index;
// If input or configuration parameters are incorrect then return an error to stack layer
reg_index = (uint16_t)(address - input_reg_start);
reg_index <<= 1; // register Address to byte address
input_buffer += reg_index;
uint8_t* buffer_start = input_buffer;
while (regs > 0) {
_XFER_2_RD(reg_buffer, input_buffer);
reg_index += 2;
regs -= 1;
}
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_INPUT_REG_RD);
// Send parameter info to application task
(void)mbc_slave_send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
} else {
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Holding Registers
// Executed by stack when request to read/write holding registers is received
eMBErrorCode mbc_reg_holding_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs, eMBRegisterMode mode)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
address--; // address of register is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_HOLDING, address, n_regs);
if (it != NULL) {
uint16_t reg_holding_start = (uint16_t)it->start_offset; // Get Modbus start address
uint8_t* holding_buffer = (uint8_t*)it->p_data; // Get instance address
uint16_t regs = n_regs;
reg_index = (uint16_t) (address - reg_holding_start);
reg_index <<= 1; // register Address to byte address
holding_buffer += reg_index;
uint8_t* buffer_start = holding_buffer;
switch (mode) {
case MB_REG_READ:
while (regs > 0) {
_XFER_2_RD(reg_buffer, holding_buffer);
reg_index += 2;
regs -= 1;
};
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
// Send parameter info
(void)mbc_slave_send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
break;
case MB_REG_WRITE:
while (regs > 0) {
_XFER_2_WR(holding_buffer, reg_buffer);
holding_buffer += 2;
reg_index += 2;
regs -= 1;
};
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
// Send parameter info
(void)mbc_slave_send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
break;
}
} else {
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Coils Registers
eMBErrorCode mbc_reg_coils_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_coils, eMBRegisterMode mode)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
uint16_t coils = n_coils;
address--; // The address is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_COIL, address, n_coils);
if (it != NULL) {
uint16_t reg_coils_start = (uint16_t)it->start_offset; // MB offset of coils
uint8_t* reg_coils_buf = (uint8_t*)it->p_data;
reg_index = (uint16_t) (address - it->start_offset);
CHAR* coils_data_buf = (CHAR*)(reg_coils_buf + (reg_index >> 3));
switch (mode) {
case MB_REG_READ:
while (coils > 0) {
uint8_t result = xMBUtilGetBits((uint8_t*)reg_coils_buf, reg_index, 1);
xMBUtilSetBits(reg_buffer, reg_index - (address - reg_coils_start), 1, result);
reg_index++;
coils--;
}
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_COILS_RD);
(void)mbc_slave_send_param_info(MB_EVENT_COILS_RD, (uint16_t)address,
(uint8_t*)(coils_data_buf), (uint16_t)n_coils);
break;
case MB_REG_WRITE:
while (coils > 0) {
uint8_t result = xMBUtilGetBits(reg_buffer,
reg_index - (address - reg_coils_start), 1);
xMBUtilSetBits((uint8_t*)reg_coils_buf, reg_index, 1, result);
reg_index++;
coils--;
}
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_COILS_WR);
(void)mbc_slave_send_param_info(MB_EVENT_COILS_WR, (uint16_t)address,
(uint8_t*)coils_data_buf, (uint16_t)n_coils);
break;
} // switch ( eMode )
} else {
// If the configuration or input parameters are incorrect then return error to stack
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Discrete Input Registers
eMBErrorCode mbc_reg_discrete_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_discrete)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
uint16_t reg_bit_index;
uint16_t n_reg;
uint8_t* discrete_input_buf;
// It already plus one in modbus function method.
address--;
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_DISCRETE, address, n_discrete);
if (it != NULL) {
uint16_t reg_discrete_start = (uint16_t)it->start_offset; // MB offset of registers
n_reg = (n_discrete >> 3) + 1;
discrete_input_buf = (uint8_t*)it->p_data; // the storage address
reg_index = (uint16_t) (address - reg_discrete_start) / 8; // Get register index in the buffer for bit number
reg_bit_index = (uint16_t)(address - reg_discrete_start) % 8; // Get bit index
uint8_t* temp_buf = &discrete_input_buf[reg_index];
while (n_reg > 0) {
*reg_buffer++ = xMBUtilGetBits(&discrete_input_buf[reg_index++], reg_bit_index, 8);
n_reg--;
}
reg_buffer--;
// Last discrete
n_discrete = n_discrete % 8;
// Filling zero to high bit
*reg_buffer = *reg_buffer << (8 - n_discrete);
*reg_buffer = *reg_buffer >> (8 - n_discrete);
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_DISCRETE_RD);
(void)mbc_slave_send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)address,
(uint8_t*)temp_buf, (uint16_t)n_discrete);
} else {
status = MB_ENOREG;
}
return status;
}
/**
* Below are the stack callback functions to read/write registers
*/
eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
// Check if the callback is overridden in concrete port
if (slave_interface_ptr->slave_reg_cb_discrete) {
error = slave_interface_ptr->slave_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
} else {
error = mbc_reg_discrete_slave_cb(pucRegBuffer, usAddress, usNDiscrete);
}
return error;
}
eMBErrorCode eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->slave_reg_cb_coils) {
error = slave_interface_ptr->slave_reg_cb_coils(pucRegBuffer, usAddress, usNCoils, eMode);
} else {
error = mbc_reg_coils_slave_cb(pucRegBuffer, usAddress, usNCoils, eMode);
}
return error;
}
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->slave_reg_cb_holding) {
error = slave_interface_ptr->slave_reg_cb_holding(pucRegBuffer, usAddress, usNRegs, eMode);
} else {
error = mbc_reg_holding_slave_cb(pucRegBuffer, usAddress, usNRegs, eMode);
}
return error;
}
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
eMBErrorCode error = ESP_ERR_INVALID_STATE;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->slave_reg_cb_input) {
error = slave_interface_ptr->slave_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
} else {
error = mbc_reg_input_slave_cb(pucRegBuffer, usAddress, usNRegs);
}
return error;
}

View File

@@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "sdkconfig.h" // for KConfig defines
#include "mbc_slave.h" // for slave interface define
#include "esp_modbus_slave.h" // for public slave defines
#include "mbc_serial_slave.h" // for public interface defines
/**
* Initialization of Modbus Serial slave controller
*/
esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler)
{
void* port_handler = NULL;
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
switch(port_type)
{
case MB_PORT_SERIAL_SLAVE:
// Call constructor function of actual port implementation
error = mbc_serial_slave_create(&port_handler);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
}
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_slave_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_slave.h" // for public slave defines
#include "mbc_tcp_slave.h" // for public interface defines
/**
* Initialization of Modbus TCP Slave controller
*/
esp_err_t mbc_slave_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_slave_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_slave_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -0,0 +1,157 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_IFACE_COMMON_H
#define _MB_IFACE_COMMON_H
#include <inttypes.h> // needs to be included for default system types (such as PRIxx)
#include "driver/uart.h" // for UART types
#include "sdkconfig.h"
#if CONFIG_FMB_EXT_TYPE_SUPPORT
#include "mb_endianness_utils.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if __has_include("esp_check.h")
#include "esp_check.h"
#include "esp_log.h"
#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) ESP_RETURN_ON_FALSE(a, err_code, tag, format __VA_OPT__(,) __VA_ARGS__)
#else
// if cannot include esp_check then use custom check macro
#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) do { \
if (!(a)) { \
ESP_LOGE(tag, "%s(%" PRIu32 "): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \
return err_code; \
} \
} while(0)
#endif
#define MB_CONTROLLER_STACK_SIZE (CONFIG_FMB_CONTROLLER_STACK_SIZE) // Stack size for Modbus controller
#define MB_CONTROLLER_PRIORITY (CONFIG_FMB_PORT_TASK_PRIO - 1) // priority of MB controller task
// Default port defines
#define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus
#define MB_DEVICE_SPEED (115200) // Default Modbus speed for now hard defined
#define MB_UART_PORT (UART_NUM_MAX - 1) // Default UART port number
#define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info
#define MB_PARITY_NONE (UART_PARITY_DISABLE)
// The Macros below handle the endianness while transfer N byte data into buffer (convert from network byte order)
#define _XFER_2_RD(dst, src) { \
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 1); \
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 0); \
(src) += 2; \
}
#define _XFER_2_WR(dst, src) { \
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
}
/**
* @brief Types of actual Modbus implementation
*/
typedef enum
{
MB_PORT_SERIAL_MASTER = 0x00, /*!< Modbus port type serial master. */
MB_PORT_SERIAL_SLAVE, /*!< Modbus port type serial slave. */
MB_PORT_TCP_MASTER, /*!< Modbus port type TCP master. */
MB_PORT_TCP_SLAVE, /*!< Modbus port type TCP slave. */
MB_PORT_COUNT, /*!< Modbus port count. */
MB_PORT_INACTIVE = 0xFF
} mb_port_type_t;
/**
* @brief Event group for parameters notification
*/
typedef enum
{
MB_EVENT_NO_EVENTS = 0x00,
MB_EVENT_HOLDING_REG_WR = BIT0, /*!< Modbus Event Write Holding registers. */
MB_EVENT_HOLDING_REG_RD = BIT1, /*!< Modbus Event Read Holding registers. */
MB_EVENT_INPUT_REG_RD = BIT3, /*!< Modbus Event Read Input registers. */
MB_EVENT_COILS_WR = BIT4, /*!< Modbus Event Write Coils. */
MB_EVENT_COILS_RD = BIT5, /*!< Modbus Event Read Coils. */
MB_EVENT_DISCRETE_RD = BIT6, /*!< Modbus Event Read Discrete bits. */
MB_EVENT_STACK_STARTED = BIT7 /*!< Modbus Event Stack started */
} mb_event_group_t;
/**
* @brief Type of Modbus parameter
*/
typedef enum {
MB_PARAM_HOLDING = 0x00, /*!< Modbus Holding register. */
MB_PARAM_INPUT, /*!< Modbus Input register. */
MB_PARAM_COIL, /*!< Modbus Coils. */
MB_PARAM_DISCRETE, /*!< Modbus Discrete bits. */
MB_PARAM_COUNT,
MB_PARAM_UNKNOWN = 0xFF
} mb_param_type_t;
/*!
* \brief Modbus serial transmission modes (RTU/ASCII).
*/
typedef enum {
MB_MODE_RTU, /*!< RTU transmission mode. */
MB_MODE_ASCII, /*!< ASCII transmission mode. */
MB_MODE_TCP, /*!< TCP communication mode. */
MB_MODE_UDP /*!< UDP communication mode. */
} mb_mode_type_t;
/*!
* \brief Modbus TCP type of address.
*/
typedef enum {
MB_IPV4 = 0, /*!< TCP IPV4 addressing */
MB_IPV6 = 1 /*!< TCP IPV6 addressing */
} mb_tcp_addr_type_t;
/**
* @brief Device communication structure to setup Modbus controller
*/
typedef union {
// Serial communication structure
struct {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint8_t slave_addr; /*!< Modbus slave address field (dummy for master) */
uart_port_t port; /*!< Modbus communication port (UART) number */
uint32_t baudrate; /*!< Modbus baudrate */
uart_parity_t parity; /*!< Modbus UART parity settings */
uint16_t dummy_port; /*!< Dummy field, unused */
};
// TCP/UDP communication structure
struct {
mb_mode_type_t ip_mode; /*!< Modbus communication mode */
uint8_t slave_uid; /*!< Modbus slave address field for UID */
uint16_t ip_port; /*!< Modbus port */
mb_tcp_addr_type_t ip_addr_type; /*!< Modbus address type */
void* ip_addr; /*!< Modbus address table for connection */
void* ip_netif_ptr; /*!< Modbus network interface */
};
} mb_communication_info_t;
/**
* common interface method types
*/
typedef esp_err_t (*iface_init)(void**); /*!< Interface method init */
typedef esp_err_t (*iface_destroy)(void); /*!< Interface method destroy */
typedef esp_err_t (*iface_setup)(void*); /*!< Interface method setup */
typedef esp_err_t (*iface_start)(void); /*!< Interface method start */
#ifdef __cplusplus
}
#endif
#endif // _MB_IFACE_COMMON_H

View File

@@ -0,0 +1,351 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_MB_MASTER_INTERFACE_H
#define _ESP_MB_MASTER_INTERFACE_H
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "soc/soc.h" // for BITN definitions
#include "esp_modbus_common.h" // for common types
#ifdef __cplusplus
extern "C" {
#endif
#define MB_MASTER_CHECK(a, err_code, format, ...) MB_RETURN_ON_FALSE(a, err_code, TAG, format __VA_OPT__(,) __VA_ARGS__)
#define MB_MASTER_ASSERT(con) do { \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%u, errno_str: !(%s)", (unsigned)errno, strerror(errno)); assert(0 && #con); } \
} while (0)
/*!
* \brief The macro to access arrays of elements for type conversion.
*/
#define MB_EACH_ELEM(psrc, pdest, arr_size, elem_size) \
(int i = 0; (i < (arr_size / elem_size)); i++, pdest += elem_size, psrc += elem_size)
/*!
* \brief Modbus descriptor table parameter type defines.
*/
typedef enum {
PARAM_TYPE_U8 = 0x00, /*!< Unsigned 8 */
PARAM_TYPE_U16 = 0x01, /*!< Unsigned 16 */
PARAM_TYPE_U32 = 0x02, /*!< Unsigned 32 */
PARAM_TYPE_FLOAT = 0x03, /*!< Float type */
PARAM_TYPE_ASCII = 0x04, /*!< ASCII type */
PARAM_TYPE_BIN = 0x07, /*!< BIN type */
PARAM_TYPE_I8_A = 0x0A, /*!< I8 signed integer in high byte of register */
PARAM_TYPE_I8_B = 0x0B, /*!< I8 signed integer in low byte of register */
PARAM_TYPE_U8_A = 0x0C, /*!< U8 unsigned integer written to hi byte of register */
PARAM_TYPE_U8_B = 0x0D, /*!< U8 unsigned integer written to low byte of register */
PARAM_TYPE_I16_AB = 0x0E, /*!< I16 signed integer, big endian */
PARAM_TYPE_I16_BA = 0x0F, /*!< I16 signed integer, little endian */
PARAM_TYPE_U16_AB = 0x10, /*!< U16 unsigned integer, big endian*/
PARAM_TYPE_U16_BA = 0x11, /*!< U16 unsigned integer, little endian */
PARAM_TYPE_I32_ABCD = 0x12, /*!< I32 ABCD signed integer, big endian */
PARAM_TYPE_I32_CDAB = 0x13, /*!< I32 CDAB signed integer, big endian, reversed register order */
PARAM_TYPE_I32_BADC = 0x14, /*!< I32 BADC signed integer, little endian, reversed register order */
PARAM_TYPE_I32_DCBA = 0x15, /*!< I32 DCBA signed integer, little endian */
PARAM_TYPE_U32_ABCD = 0x16, /*!< U32 ABCD unsigned integer, big endian */
PARAM_TYPE_U32_CDAB = 0x17, /*!< U32 CDAB unsigned integer, big endian, reversed register order */
PARAM_TYPE_U32_BADC = 0x18, /*!< U32 BADC unsigned integer, little endian, reversed register order */
PARAM_TYPE_U32_DCBA = 0x19, /*!< U32 DCBA unsigned integer, little endian */
PARAM_TYPE_FLOAT_ABCD = 0x1A, /*!< Float ABCD floating point, big endian */
PARAM_TYPE_FLOAT_CDAB = 0x1B, /*!< Float CDAB floating point big endian, reversed register order */
PARAM_TYPE_FLOAT_BADC = 0x1C, /*!< Float BADC floating point, little endian, reversed register order */
PARAM_TYPE_FLOAT_DCBA = 0x1D, /*!< Float DCBA floating point, little endian */
PARAM_TYPE_I64_ABCDEFGH = 0x1E, /*!< I64, ABCDEFGH signed integer, big endian */
PARAM_TYPE_I64_HGFEDCBA = 0x1F, /*!< I64, HGFEDCBA signed integer, little endian */
PARAM_TYPE_I64_GHEFCDAB = 0x20, /*!< I64, GHEFCDAB signed integer, big endian, reversed register order */
PARAM_TYPE_I64_BADCFEHG = 0x21, /*!< I64, BADCFEHG signed integer, little endian, reversed register order */
PARAM_TYPE_U64_ABCDEFGH = 0x22, /*!< U64, ABCDEFGH unsigned integer, big endian */
PARAM_TYPE_U64_HGFEDCBA = 0x23, /*!< U64, HGFEDCBA unsigned integer, little endian */
PARAM_TYPE_U64_GHEFCDAB = 0x24, /*!< U64, GHEFCDAB unsigned integer, big endian, reversed register order */
PARAM_TYPE_U64_BADCFEHG = 0x25, /*!< U64, BADCFEHG unsigned integer, little endian, reversed register order */
PARAM_TYPE_DOUBLE_ABCDEFGH = 0x26, /*!< Double ABCDEFGH floating point, big endian*/
PARAM_TYPE_DOUBLE_HGFEDCBA = 0x27, /*!< Double HGFEDCBA floating point, little endian*/
PARAM_TYPE_DOUBLE_GHEFCDAB = 0x28, /*!< Double GHEFCDAB floating point, big endian, reversed register order */
PARAM_TYPE_DOUBLE_BADCFEHG = 0x29 /*!< Double BADCFEHG floating point, little endian, reversed register order */
} mb_descr_type_t;
/*!
* \brief Modbus descriptor table parameter size in bytes.
*/
typedef enum {
PARAM_SIZE_U8 = 0x01, /*!< Unsigned 8 */
PARAM_SIZE_U8_REG = 0x02, /*!< Unsigned 8, register value */
PARAM_SIZE_I8_REG = 0x02, /*!< Signed 8, register value */
PARAM_SIZE_I16 = 0x02, /*!< Unsigned 16 */
PARAM_SIZE_U16 = 0x02, /*!< Unsigned 16 */
PARAM_SIZE_I32 = 0x04, /*!< Signed 32 */
PARAM_SIZE_U32 = 0x04, /*!< Unsigned 32 */
PARAM_SIZE_FLOAT = 0x04, /*!< Float 32 size */
PARAM_SIZE_ASCII = 0x08, /*!< ASCII size default*/
PARAM_SIZE_ASCII24 = 0x18, /*!< ASCII24 size */
PARAM_SIZE_I64 = 0x08, /*!< Signed integer 64 size */
PARAM_SIZE_U64 = 0x08, /*!< Unsigned integer 64 size */
PARAM_SIZE_DOUBLE = 0x08, /*!< Double 64 size */
PARAM_MAX_SIZE
} mb_descr_size_t;
/*!
* \brief Modbus parameter options for description table
*/
typedef union {
struct {
int opt1; /*!< Parameter option1 */
int opt2; /*!< Parameter option2 */
int opt3; /*!< Parameter option3 */
};
struct {
int min; /*!< Parameter minimum value */
int max; /*!< Parameter maximum value */
int step; /*!< Step of parameter change tracking */
};
} mb_parameter_opt_t;
/**
* @brief Permissions for the characteristics
*/
typedef enum {
PAR_PERMS_READ = 1 << BIT0, /**< the characteristic of the device are readable */
PAR_PERMS_WRITE = 1 << BIT1, /**< the characteristic of the device are writable*/
PAR_PERMS_TRIGGER = 1 << BIT2, /**< the characteristic of the device are triggerable */
PAR_PERMS_READ_WRITE = PAR_PERMS_READ | PAR_PERMS_WRITE, /**< the characteristic of the device are readable & writable */
PAR_PERMS_READ_TRIGGER = PAR_PERMS_READ | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & triggerable */
PAR_PERMS_WRITE_TRIGGER = PAR_PERMS_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are writable & triggerable */
PAR_PERMS_READ_WRITE_TRIGGER = PAR_PERMS_READ_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & writable & triggerable */
} mb_param_perms_t;
/**
* @brief Characteristics descriptor type is used to describe characteristic and
* link it with Modbus parameters that reflect its data.
*/
typedef struct {
uint16_t cid; /*!< Characteristic cid */
const char* param_key; /*!< The key (name) of the parameter */
const char* param_units; /*!< The physical units of the parameter */
uint8_t mb_slave_addr; /*!< Slave address of device in the Modbus segment */
mb_param_type_t mb_param_type; /*!< Type of modbus parameter */
uint16_t mb_reg_start; /*!< This is the Modbus register address. This is the 0 based value. */
uint16_t mb_size; /*!< Size of mb parameter in registers */
uint16_t param_offset; /*!< Parameter name (OFFSET in the parameter structure) */
mb_descr_type_t param_type; /*!< Float, U8, U16, U32, ASCII, etc. */
mb_descr_size_t param_size; /*!< Number of bytes in the parameter. */
mb_parameter_opt_t param_opts; /*!< Parameter options used to check limits and etc. */
mb_param_perms_t access; /*!< Access permissions based on mode */
} mb_parameter_descriptor_t;
/**
* @brief Modbus register request type structure
*/
typedef struct {
uint8_t slave_addr; /*!< Modbus slave address */
uint8_t command; /*!< Modbus command to send */
uint16_t reg_start; /*!< Modbus start register */
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
*
* @param[out] handler handler(pointer) to master data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_master_init_tcp(void** handler);
/**
* @brief Initialize Modbus Master controller and stack for Serial port
*
* @param[out] handler handler(pointer) to master data structure
* @param[in] port_type type of stack
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler);
/**
* @brief Initialize Modbus Master controller interface handle
*
* @param[in] handler - pointer to master data structure
*/
void mbc_master_init_iface(void* handler);
/**
* @brief Destroy Modbus controller and stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Parameter error
*/
esp_err_t mbc_master_destroy(void);
/**
* @brief Start Modbus communication stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Modbus stack start error
*/
esp_err_t mbc_master_start(void);
/**
* @brief Set Modbus communication parameters for the controller
*
* @param comm_info Communication parameters structure.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Incorrect parameter data
*/
esp_err_t mbc_master_setup(void* comm_info);
/***************************** Specific interface functions ********************************************
* Interface functions below provide basic methods to read/write access to slave devices in Modbus
* segment as well as API to read specific supported characteristics linked to Modbus parameters
* of devices in Modbus network.
*******************************************************************************************************/
/**
* @brief Assign parameter description table for Modbus controller interface.
*
* @param[in] descriptor pointer to parameter description table
* @param num_elements number of elements in the table
*
* @return
* - esp_err_t ESP_OK - set descriptor successfully
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument in function call
*/
esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements);
/**
* @brief Send data request as defined in parameter request, waits response
* from slave and returns status of command execution. This function provides standard way
* for read/write access to Modbus devices in the network.
*
* @param[in] request pointer to request structure of type mb_param_request_t
* @param[in] data_ptr pointer to data buffer to send or received data (dependent of command field in request)
*
* @return
* - esp_err_t ESP_OK - request was successful
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave
* - esp_err_t ESP_ERR_TIMEOUT - operation timeout or no response from slave
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
*/
esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr);
/**
* @brief Get information about supported characteristic defined as cid. Uses parameter description table to get
* this information. The function will check if characteristic defined as a cid parameter is supported
* and returns its description in param_info. Returns ESP_ERR_NOT_FOUND if characteristic is not supported.
*
* @param[in] cid characteristic id
* @param param_info pointer to pointer of characteristic data.
*
* @return
* - esp_err_t ESP_OK - request was successful and buffer contains the supported characteristic name
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
* - esp_err_t ESP_ERR_NOT_FOUND - the characteristic (cid) not found
* - esp_err_t ESP_FAIL - unknown error during lookup table processing
*/
esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_info);
/**
* @brief Read parameter from modbus slave device whose name is defined by name and has cid.
* The additional data for request is taken from parameter description (lookup) table.
*
* @param[in] cid id of the characteristic for parameter
* @param[in] name pointer into string name (key) of parameter (null terminated)
* @param[out] value pointer to data buffer of parameter
* @param[out] type parameter type associated with the name returned from parameter description table.
*
* @return
* - esp_err_t ESP_OK - request was successful and value buffer contains
* representation of actual parameter data from slave
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
* - esp_err_t ESP_ERR_TIMEOUT - operation timed out and no response from slave
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
* - esp_err_t ESP_ERR_NOT_FOUND - the parameter is not found in the parameter description table
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
*/
esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type);
/**
* @brief Set characteristic's value defined as a name and cid parameter.
* The additional data for cid parameter request is taken from master parameter lookup table.
*
* @param[in] cid id of the characteristic for parameter
* @param[in] name pointer into string name (key) of parameter (null terminated)
* @param[out] value pointer to data buffer of parameter (actual representation of json value field in binary form)
* @param[out] type pointer to parameter type associated with the name returned from parameter lookup table.
*
* @return
* - esp_err_t ESP_OK - request was successful and value was saved in the slave device registers
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave during processing of parameter
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
* - esp_err_t ESP_ERR_TIMEOUT - operation timed out and no response from slave
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
*/
esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type);
/**
* @brief The helper function to set data of parameters according to its type
*
* @param[in] dest the destination address of the parameter
* @param[in] src the source address of the parameter
* @param[out] param_type type of parameter from data dictionary
* @param[out] param_size the storage size of the characteristic (in bytes).
* Describes the size of data to keep into data instance during mapping.
*
* @return
* - esp_err_t ESP_OK - request was successful and value was saved in the slave device registers
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
*/
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
#endif // _ESP_MB_MASTER_INTERFACE_H

View File

@@ -0,0 +1,148 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_MB_SLAVE_INTERFACE_H
#define _ESP_MB_SLAVE_INTERFACE_H
// Public interface header for slave
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "soc/soc.h" // for BITN definitions
#include "freertos/FreeRTOS.h" // for task creation and queues access
#include "freertos/event_groups.h" // for event groups
#include "esp_modbus_common.h" // for common types
#ifdef __cplusplus
extern "C" {
#endif
#define MB_SLAVE_CHECK(a, err_code, format, ...) MB_RETURN_ON_FALSE(a, err_code, TAG, format __VA_OPT__(,) __VA_ARGS__)
#define MB_SLAVE_ASSERT(con) do { \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%u, errno_str: !(%s)", (unsigned)errno, strerror(errno)); assert(0 && #con); } \
} while (0)
/**
* @brief Parameter access event information type
*/
typedef struct {
uint32_t time_stamp; /*!< Timestamp of Modbus Event (uS)*/
uint16_t mb_offset; /*!< Modbus register offset */
mb_event_group_t type; /*!< Modbus event type */
uint8_t* address; /*!< Modbus data storage address */
size_t size; /*!< Modbus event register size (number of registers)*/
} mb_param_info_t;
/**
* @brief Parameter storage area descriptor
*/
typedef struct {
uint16_t start_offset; /*!< Modbus start address for area descriptor */
mb_param_type_t type; /*!< Type of storage area descriptor */
void* address; /*!< Instance address for storage area descriptor */
size_t size; /*!< Instance size for area descriptor (bytes) */
} mb_register_area_descriptor_t;
/**
* @brief Initialize Modbus Slave controller and stack for TCP port
*
* @param[out] handler handler(pointer) to master data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_slave_init_tcp(void** handler);
/**
* @brief Initialize Modbus Slave controller and stack for Serial port
*
* @param[out] handler handler(pointer) to master data structure
* @param[in] port_type the type of port
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler);
/**
* @brief Initialize Modbus Slave controller interface handle
*
* @param[in] handler - pointer to slave interface data structure
*/
void mbc_slave_init_iface(void* handler);
/**
* @brief Destroy Modbus controller and stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Parameter error
*/
esp_err_t mbc_slave_destroy(void);
/**
* @brief Start Modbus communication stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Modbus stack start error
*/
esp_err_t mbc_slave_start(void);
/**
* @brief Set Modbus communication parameters for the controller
*
* @param comm_info Communication parameters structure.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Incorrect parameter data
*/
esp_err_t mbc_slave_setup(void* comm_info);
/**
* @brief Wait for specific event on parameter change.
*
* @param group Group event bit mask to wait for change
*
* @return
* - mb_event_group_t event bits triggered
*/
mb_event_group_t mbc_slave_check_event(mb_event_group_t group);
/**
* @brief Get parameter information
*
* @param[out] reg_info parameter info structure
* @param timeout Timeout in milliseconds to read information from
* parameter queue
* @return
* - ESP_OK Success
* - ESP_ERR_TIMEOUT Can not get data from parameter queue
* or queue overflow
*/
esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout);
/**
* @brief Set Modbus area descriptor
*
* @param descr_data Modbus registers area descriptor structure
*
* @return
* - ESP_OK: The appropriate descriptor is set
* - ESP_ERR_INVALID_ARG: The argument is incorrect
*/
esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,556 @@
/*
* SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
/**
* @brief Defines the constant values based on native compiler byte ordering.
*/
#define MB_BO16_0 0
#define MB_BO16_1 1
#define MB_BO32_0 0
#define MB_BO32_1 1
#define MB_BO32_2 2
#define MB_BO32_3 3
#define MB_BO64_0 0
#define MB_BO64_1 1
#define MB_BO64_2 2
#define MB_BO64_3 3
#define MB_BO64_4 4
#define MB_BO64_5 5
#define MB_BO64_6 6
#define MB_BO64_7 7
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The sized array types used for mapping of extended values
*/
typedef uint8_t val_16_arr[2];
typedef uint8_t val_32_arr[4];
typedef uint8_t val_64_arr[8];
/**
* @brief Get int8_t (low byte) value represenatation from register
*
* @return
* - int8_t value of converted from register value
*/
int8_t mb_get_int8_a(val_16_arr *pi16);
/**
* @brief Set i8 value to the register value pointed by pi16
*
* @return
* - uint16_t value which represents the actual hex value of the register
*/
uint16_t mb_set_int8_a(val_16_arr *pi16, int8_t i8);
/**
* @brief Get int8_t (high byte) value from the register value pointed by pi16
*
* @return
* - uint16_t value which represents the actual hex value of the register
*/
int8_t mb_get_int8_b(val_16_arr *pi16);
/**
* @brief Set i8 (high byte) value from the register value pointed by pi16
*
* @return
* - uint16_t value which represents the actual hex value of the register
*/
uint16_t mb_set_int8_b(val_16_arr *pi16, int8_t i8);
/**
* @brief Get uint8_t (low byte) value represenatation from register poined by pu16
*
* @return
* - uint8_t the value of converted from register value
*/
uint8_t mb_get_uint8_a(val_16_arr *pu16);
/**
* @brief Set u8 (low byte) value into the register value pointed by pu16
*
* @return
* - uint16_t the value which represents the actual hex value of the register
*/
uint16_t mb_set_uint8_a(val_16_arr *pu16, uint8_t u8);
/**
* @brief Get uint8_t (high byte) value from the register value pointed by pu16
*
* @return
* - uint16_t the value which represents the actual hex value of the register
*/
uint8_t mb_get_uint8_b(val_16_arr *pu16);
/**
* @brief Set u8 (high byte) value into the register value pointed by pu16
*
* @return
* - uint16_t the value which represents the actual hex value of the register
*/
uint16_t mb_set_uint8_b(val_16_arr *pu16, uint8_t u8);
/**
* @brief Get int16_t value from the register value pointed by pu16 with ab endianness
*
* @return
* - int16_t the value which represents the converted value from register
*/
int16_t mb_get_int16_ab(val_16_arr *pi16);
/**
* @brief Set i16 value to the register pointed by pi16 with ab endianness
*
* @return
* - int16_t the value which represents the converted value from register
*/
uint16_t mb_set_int16_ab(val_16_arr *pi16, int16_t i16);
/**
* @brief Get uint16_t value from the register value pointed by pu16 with ab endianness
*
* @return
* - uint16_t value which represents the converted register value
*/
uint16_t mb_get_uint16_ab(val_16_arr *pu16);
/**
* @brief Set u16 value to the register pointed by pu16 with ab endianness
*
* @return
* - uint16_t value which represents the converted value from register
*/
uint16_t mb_set_uint16_ab(val_16_arr *pu16, uint16_t u16);
/**
* @brief Get int16_t value from the register value pointed by pu16 with ba endianness
*
* @return
* - int16_t value which represents the converted register value
*/
int16_t mb_get_int16_ba(val_16_arr *pi16);
/**
* @brief Set i16 value to the register pointed by pi16 with ba endianness
*
* @return
* - uint16_t value which represents the converted value from register
*/
uint16_t mb_set_int16_ba(val_16_arr *pi16, int16_t i16);
/**
* @brief Get uint16_t value from the register value pointed by pu16 with ba endianness
*
* @return
* - uint16_t value which represents the converted register value
*/
uint16_t mb_get_uint16_ba(val_16_arr *pu16);
/**
* @brief Set u16 value to the register pointed by pu16 with ba endianness
*
* @return
* - uint16_t value which represents the converted value from register
*/
uint16_t mb_set_uint16_ba(val_16_arr *pu16, uint16_t u16);
/**
* @brief Get int32_t value from the register value pointed by pi32 with abcd endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_abcd(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with abcd endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_abcd(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with abcd endianness
*
* @return
* - uint32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_abcd(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with abcd endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_abcd(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get int32_t value from the register value pointed by pi32 with badc endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_badc(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with badc endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_badc(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with badc endianness
*
* @return
* - unt32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_badc(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with badc endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_badc(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get int32_t value from the register value pointed by pi32 with cdab endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_cdab(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with cdab endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_cdab(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with cdab endianness
*
* @return
* - int32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_cdab(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with cdab endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_cdab(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get int32_t value from the register value pointed by pi32 with dcba endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_dcba(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with dcba endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_dcba(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with dcba endianness
*
* @return
* - uint32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_dcba(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with dcba endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_dcba(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get float value from the register pointed by pf with abcd endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_abcd(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with abcd endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_abcd(val_32_arr *pf, float f);
/**
* @brief Get float value from the register pointed by pf with badc endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_badc(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with badc endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_badc(val_32_arr *pf, float f);
/**
* @brief Get float value from the register pointed by pf with cdab endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_cdab(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with cdab endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_cdab(val_32_arr *pf, float f);
/**
* @brief Get float value from the register pointed by pf with dcba endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_dcba(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with dcba endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_dcba(val_32_arr *pf, float f);
/**
* @brief Get double value from the register pointed by pd with abcdefgh endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_abcdefgh(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_abcdefgh(val_64_arr *pd, double d);
/**
* @brief Get double value from the register pointed by pd with hgfedcba endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_hgfedcba(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_hgfedcba(val_64_arr *pd, double d);
/**
* @brief Get double value from the register pointed by pd with ghefcdab endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_ghefcdab(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_ghefcdab(val_64_arr *pd, double d);
/**
* @brief Get double value from the register pointed by pd with badcfehg endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_badcfehg(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_badcfehg(val_64_arr *pd, double d);
/**
* @brief Get int64_t value from the register pointed by pi64 with abcdefgh endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_abcdefgh(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_abcdefgh(val_64_arr *pi, int64_t i);
/**
* @brief Get int64_t value from the register pointed by pi64 with ghefcdab endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_ghefcdab(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_ghefcdab(val_64_arr *pi, int64_t i);
/**
* @brief Get int64_t value from the register pointed by pi64 with hgfedcba endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_hgfedcba(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_hgfedcba(val_64_arr *pi, int64_t i);
/**
* @brief Get int64_t value from the register pointed by pi64 with badcfehg endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_badcfehg(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_badcfehg(val_64_arr *pi, int64_t i);
/**
* @brief Get uint64_t value from the register pointed by pui with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_abcdefgh(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pi with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_abcdefgh(val_64_arr *pui, uint64_t ui);
/**
* @brief Get uint64_t value from the register pointed by pui with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_hgfedcba(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pui with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_hgfedcba(val_64_arr *pui, uint64_t ui);
/**
* @brief Get uint64_t value from the register pointed by pui with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_ghefcdab(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pui with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_ghefcdab(val_64_arr *pui, uint64_t ui);
/**
* @brief Get uint64_t value from the register pointed by pui with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_badcfehg(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pui with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_badcfehg(val_64_arr *pui, uint64_t ui);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbcontroller.h
// mbcontroller - common Modbus controller header file
#ifndef _MODBUS_CONTROLLER_COMMON
#define _MODBUS_CONTROLLER_COMMON
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "string.h" // for strerror()
#include "errno.h" // for errno
#include "esp_err.h" // for error handling
#include "driver/uart.h" // for uart port number defines
#include "sdkconfig.h" // for KConfig options
#include "esp_modbus_master.h"
#include "esp_modbus_slave.h"
#endif

View File

@@ -0,0 +1,683 @@
/*
* SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "mb_endianness_utils.h"
#define INLINE inline __attribute__((always_inline))
static INLINE int16_t mb_get_int16_generic(int n0, int n1, val_16_arr *psrc)
{
val_16_arr *pv = psrc;
union {
val_16_arr arr;
int16_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO16_0];
bov.arr[n1] = (*pv)[MB_BO16_1];
return (bov.value);
}
static INLINE uint16_t mb_get_uint16_generic(int n0, int n1, val_16_arr *psrc)
{
val_16_arr *pv = psrc;
union {
val_16_arr arr;
uint16_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO16_0];
bov.arr[n1] = (*pv)[MB_BO16_1];
return (bov.value);
}
static INLINE uint16_t mb_set_uint16_generic(int n0, int n1, val_16_arr *pdest, uint16_t val)
{
val_16_arr *pv = pdest;
union {
val_16_arr arr;
uint16_t value;
} bov;
bov.value = val;
(*pv)[MB_BO16_0] = bov.arr[n0];
(*pv)[MB_BO16_1] = bov.arr[n1];
return (*((uint16_t *)pv));
}
static INLINE int16_t mb_set_int16_generic(int n0, int n1, val_16_arr *pdest, int16_t val)
{
val_16_arr *pv = pdest;
union {
val_16_arr arr;
int16_t value;
} bov;
bov.value = val;
(*pv)[MB_BO16_0] = bov.arr[n0];
(*pv)[MB_BO16_1] = bov.arr[n1];
return (*((uint16_t *)pv));
}
static INLINE uint32_t mb_get_uint32_generic(int n0, int n1, int n2, int n3, val_32_arr *psrc)
{
val_32_arr *pv = psrc;
union {
val_32_arr arr;
uint32_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO32_0];
bov.arr[n1] = (*pv)[MB_BO32_1];
bov.arr[n2] = (*pv)[MB_BO32_2];
bov.arr[n3] = (*pv)[MB_BO32_3];
return (bov.value);
}
static INLINE int32_t mb_get_int32_generic(int n0, int n1, int n2, int n3, val_32_arr *psrc)
{
val_32_arr *pv = psrc;
union {
val_32_arr arr;
int32_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO32_0];
bov.arr[n1] = (*pv)[MB_BO32_1];
bov.arr[n2] = (*pv)[MB_BO32_2];
bov.arr[n3] = (*pv)[MB_BO32_3];
return (bov.value);
}
static INLINE float mb_get_float_generic(int n0, int n1, int n2, int n3, val_32_arr *psrc)
{
val_32_arr *pv = psrc;
union {
val_32_arr arr;
float value;
} bov;
bov.arr[n0] = (*pv)[MB_BO32_0];
bov.arr[n1] = (*pv)[MB_BO32_1];
bov.arr[n2] = (*pv)[MB_BO32_2];
bov.arr[n3] = (*pv)[MB_BO32_3];
return (bov.value);
}
static INLINE uint32_t mb_set_int32_generic(int n0, int n1, int n2, int n3, val_32_arr *pdest, int32_t val)
{
val_32_arr *pv = pdest;
union {
val_32_arr arr;
int32_t value;
} bov;
bov.value = val;
(*pv)[MB_BO32_0] = bov.arr[n0];
(*pv)[MB_BO32_1] = bov.arr[n1];
(*pv)[MB_BO32_2] = bov.arr[n2];
(*pv)[MB_BO32_3] = bov.arr[n3];
return (*((uint32_t *)pv));
}
static INLINE uint32_t mb_set_uint32_generic(int n0, int n1, int n2, int n3, val_32_arr *pdest, uint32_t val)
{
val_32_arr *pv = pdest;
union {
val_32_arr arr;
uint32_t value;
} bov;
bov.value = val;
(*pv)[MB_BO32_0] = bov.arr[n0];
(*pv)[MB_BO32_1] = bov.arr[n1];
(*pv)[MB_BO32_2] = bov.arr[n2];
(*pv)[MB_BO32_3] = bov.arr[n3];
return (*((uint32_t *)pv));
}
static INLINE uint32_t mb_set_float_generic(int n0, int n1, int n2, int n3, val_32_arr *pdest, float val)
{
val_32_arr *pv = pdest;
union {
val_32_arr arr;
float value;
} bov;
bov.value = val;
(*pv)[MB_BO32_0] = bov.arr[n0];
(*pv)[MB_BO32_1] = bov.arr[n1];
(*pv)[MB_BO32_2] = bov.arr[n2];
(*pv)[MB_BO32_3] = bov.arr[n3];
return (*((uint32_t *)pv));
}
static INLINE int64_t mb_get_int64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *psrc)
{
val_64_arr *pv64 = psrc;
union {
val_64_arr arr;
int64_t value;
} bo64;
bo64.arr[n0] = (*pv64)[MB_BO64_0];
bo64.arr[n1] = (*pv64)[MB_BO64_1];
bo64.arr[n2] = (*pv64)[MB_BO64_2];
bo64.arr[n3] = (*pv64)[MB_BO64_3];
bo64.arr[n4] = (*pv64)[MB_BO64_4];
bo64.arr[n5] = (*pv64)[MB_BO64_5];
bo64.arr[n6] = (*pv64)[MB_BO64_6];
bo64.arr[n7] = (*pv64)[MB_BO64_7];
return (bo64.value);
}
static INLINE uint64_t mb_get_uint64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *psrc)
{
val_64_arr *pv64 = psrc;
union {
val_64_arr arr;
uint64_t value;
} bo64;
bo64.arr[n0] = (*pv64)[MB_BO64_0];
bo64.arr[n1] = (*pv64)[MB_BO64_1];
bo64.arr[n2] = (*pv64)[MB_BO64_2];
bo64.arr[n3] = (*pv64)[MB_BO64_3];
bo64.arr[n4] = (*pv64)[MB_BO64_4];
bo64.arr[n5] = (*pv64)[MB_BO64_5];
bo64.arr[n6] = (*pv64)[MB_BO64_6];
bo64.arr[n7] = (*pv64)[MB_BO64_7];
return (bo64.value);
}
static INLINE double mb_get_double_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *psrc)
{
val_64_arr *pv64 = psrc;
union {
val_64_arr arr;
double value;
} bo64;
bo64.arr[n0] = (*pv64)[MB_BO64_0];
bo64.arr[n1] = (*pv64)[MB_BO64_1];
bo64.arr[n2] = (*pv64)[MB_BO64_2];
bo64.arr[n3] = (*pv64)[MB_BO64_3];
bo64.arr[n4] = (*pv64)[MB_BO64_4];
bo64.arr[n5] = (*pv64)[MB_BO64_5];
bo64.arr[n6] = (*pv64)[MB_BO64_6];
bo64.arr[n7] = (*pv64)[MB_BO64_7];
return (bo64.value);
}
static INLINE uint64_t mb_set_int64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *pdest, int64_t val)
{
val_64_arr *pv = pdest;
union {
val_64_arr arr;
int64_t value;
} bo64;
bo64.value = val;
(*pv)[MB_BO64_0] = bo64.arr[n0];
(*pv)[MB_BO64_1] = bo64.arr[n1];
(*pv)[MB_BO64_2] = bo64.arr[n2];
(*pv)[MB_BO64_3] = bo64.arr[n3];
(*pv)[MB_BO64_4] = bo64.arr[n4];
(*pv)[MB_BO64_5] = bo64.arr[n5];
(*pv)[MB_BO64_6] = bo64.arr[n6];
(*pv)[MB_BO64_7] = bo64.arr[n7];
return (*((uint64_t *)pv));
}
static INLINE uint64_t mb_set_uint64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *pdest, uint64_t val)
{
val_64_arr *pv = pdest;
union {
val_64_arr arr;
uint64_t value;
} bo64;
bo64.value = val;
(*pv)[MB_BO64_0] = bo64.arr[n0];
(*pv)[MB_BO64_1] = bo64.arr[n1];
(*pv)[MB_BO64_2] = bo64.arr[n2];
(*pv)[MB_BO64_3] = bo64.arr[n3];
(*pv)[MB_BO64_4] = bo64.arr[n4];
(*pv)[MB_BO64_5] = bo64.arr[n5];
(*pv)[MB_BO64_6] = bo64.arr[n6];
(*pv)[MB_BO64_7] = bo64.arr[n7];
return (*((uint64_t *)pv));
}
static INLINE uint64_t mb_set_double_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *pdest, double val)
{
val_64_arr *pv = pdest;
union {
val_64_arr arr;
double value;
} bo64;
bo64.value = val;
(*pv)[MB_BO64_0] = bo64.arr[n0];
(*pv)[MB_BO64_1] = bo64.arr[n1];
(*pv)[MB_BO64_2] = bo64.arr[n2];
(*pv)[MB_BO64_3] = bo64.arr[n3];
(*pv)[MB_BO64_4] = bo64.arr[n4];
(*pv)[MB_BO64_5] = bo64.arr[n5];
(*pv)[MB_BO64_6] = bo64.arr[n6];
(*pv)[MB_BO64_7] = bo64.arr[n7];
return (*((uint64_t *)pv));
}
int8_t mb_get_int8_a(pi16)
val_16_arr *pi16;
{
return((int8_t)(*pi16)[MB_BO16_0]);
}
uint16_t mb_set_int8_a(pi16, i8)
val_16_arr *pi16;
int8_t i8;
{
(*pi16)[MB_BO16_0] = (uint8_t)i8;
(*pi16)[MB_BO16_1] = 0;
return (*((uint16_t *)pi16));
}
int8_t mb_get_int8_b(pi16)
val_16_arr *pi16;
{
return((int8_t)(*pi16)[MB_BO16_1]);
}
uint16_t mb_set_int8_b(pi16, i8)
val_16_arr *pi16;
int8_t i8;
{
(*pi16)[MB_BO16_0] = 0;
(*pi16)[MB_BO16_1] = (int8_t)i8;
return (*((uint16_t *)pi16));
}
uint8_t mb_get_uint8_a(pu16)
val_16_arr *pu16;
{
return((uint8_t)(*pu16)[MB_BO16_0]);
}
uint16_t mb_set_uint8_a(pu16, u8)
val_16_arr *pu16;
uint8_t u8;
{
(*pu16)[MB_BO16_0] = (uint8_t)u8;
(*pu16)[MB_BO16_1] = 0;
return (*((uint16_t *)pu16));
}
uint8_t mb_get_uint8_b(pu16)
val_16_arr *pu16;
{
return((uint8_t)(*pu16)[MB_BO16_1]);
}
uint16_t mb_set_uint8_b(pu16, u8)
val_16_arr *pu16;
uint8_t u8;
{
(*pu16)[MB_BO16_0] = 0;
(*pu16)[MB_BO16_1] = (uint8_t)u8;
return (*((uint16_t *)pu16));
}
int16_t mb_get_int16_ab(pi16)
val_16_arr *pi16;
{
return mb_get_int16_generic(0, 1, pi16);
}
uint16_t mb_set_int16_ab(pi16, i16)
val_16_arr *pi16;
int16_t i16;
{
return mb_set_int16_generic(0, 1, pi16, i16);
}
uint16_t mb_get_uint16_ab(pu16)
val_16_arr *pu16;
{
return mb_get_uint16_generic(0, 1, pu16);
}
uint16_t mb_set_uint16_ab(pu16, u16)
val_16_arr *pu16;
uint16_t u16;
{
return mb_set_uint16_generic(0, 1, pu16, u16);
}
int16_t mb_get_int16_ba(pi16)
val_16_arr *pi16;
{
return mb_get_int16_generic(1, 0, pi16);
}
uint16_t mb_set_int16_ba(pi16, i16)
val_16_arr *pi16;
int16_t i16;
{
return mb_set_int16_generic(1, 0, pi16, i16);
}
uint16_t mb_get_uint16_ba(pu16)
val_16_arr *pu16;
{
return mb_get_int16_generic(1, 0, pu16);
}
uint16_t mb_set_uint16_ba(pu16, u16)
val_16_arr *pu16;
uint16_t u16;
{
return mb_set_int16_generic(1, 0, pu16, u16);
}
int32_t mb_get_int32_abcd(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(0, 1, 2, 3, pi32);
}
uint32_t mb_set_int32_abcd(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(0, 1, 2, 3, pi32, i32);
}
uint32_t mb_get_uint32_abcd(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(0, 1, 2, 3, pu32);
}
uint32_t mb_set_uint32_abcd(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(0, 1, 2, 3, pu32, u32);
}
int32_t mb_get_int32_badc(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(1, 0, 3, 2, pi32);
}
uint32_t mb_set_int32_badc(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(1, 0, 3, 2, pi32, i32);
}
uint32_t mb_get_uint32_badc(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(1, 0, 3, 2, pu32);
}
uint32_t mb_set_uint32_badc(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(1, 0, 3, 2, pu32, u32);
}
int32_t mb_get_int32_cdab(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(2, 3, 0, 1, pi32);
}
uint32_t mb_set_int32_cdab(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(2, 3, 0, 1, pi32, i32);
}
uint32_t mb_get_uint32_cdab(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(2, 3, 0, 1, pu32);
}
uint32_t mb_set_uint32_cdab(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(2, 3, 0, 1, pu32, u32);
}
int32_t mb_get_int32_dcba(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(3, 2, 1, 0, pi32);
}
uint32_t mb_set_int32_dcba(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(3, 2, 1, 0, pi32, i32);
}
uint32_t mb_get_uint32_dcba(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(3, 2, 1, 0, pu32);
}
uint32_t mb_set_uint32_dcba(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(3, 2, 1, 0, pu32, u32);
}
float mb_get_float_abcd(pf)
val_32_arr *pf;
{
return mb_get_float_generic(0, 1, 2, 3, pf);
}
uint32_t mb_set_float_abcd(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(0, 1, 2, 3, pf, f);
}
float mb_get_float_badc(pf)
val_32_arr *pf;
{
return mb_get_float_generic(1, 0, 3, 2, pf);
}
uint32_t mb_set_float_badc(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(1, 0, 3, 2, pf, f);
}
float mb_get_float_cdab(pf)
val_32_arr *pf;
{
return mb_get_float_generic(2, 3, 0, 1, pf);
}
uint32_t mb_set_float_cdab(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(2, 3, 0, 1, pf, f);
}
float mb_get_float_dcba(pf)
val_32_arr *pf;
{
return mb_get_float_generic(3, 2, 1, 0, pf);
}
uint32_t mb_set_float_dcba(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(3, 2, 1, 0, pf, f);
}
double mb_get_double_abcdefgh(pd)
val_64_arr *pd;
{
return mb_get_double_generic(0, 1, 2, 3, 4, 5, 6, 7, pd);
}
uint64_t mb_set_double_abcdefgh(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(0, 1, 2, 3, 4, 5, 6, 7, pd, d);
}
double mb_get_double_hgfedcba(pd)
val_64_arr *pd;
{
return mb_get_double_generic(7, 6, 5, 4, 3, 2, 1, 0, pd);
}
uint64_t mb_set_double_hgfedcba(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(7, 6, 5, 4, 3, 2, 1, 0, pd, d);
}
double mb_get_double_ghefcdab(pd)
val_64_arr *pd;
{
return mb_get_double_generic(6, 7, 4, 5, 2, 3, 0, 1, pd);
}
uint64_t mb_set_double_ghefcdab(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(6, 7, 4, 5, 2, 3, 0, 1, pd, d);
}
double mb_get_double_badcfehg(pd)
val_64_arr *pd;
{
return mb_get_double_generic(1, 0, 3, 2, 5, 4, 7, 6, pd);
}
uint64_t mb_set_double_badcfehg(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(1, 0, 3, 2, 5, 4, 7, 6, pd, d);
}
int64_t mb_get_int64_abcdefgh(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(0, 1, 2, 3, 4, 5, 6, 7, pi64);
}
uint64_t mb_set_int64_abcdefgh(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(0, 1, 2, 3, 4, 5, 6, 7, pi, i);
}
int64_t mb_get_int64_hgfedcba(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(7, 6, 5, 4, 3, 2, 1, 0, pi64);
}
uint64_t mb_set_int64_hgfedcba(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(7, 6, 5, 4, 3, 2, 1, 0, pi, i);
}
int64_t mb_get_int64_ghefcdab(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(6, 7, 4, 5, 2, 3, 0, 1, pi64);
}
uint64_t mb_set_int64_ghefcdab(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(6, 7, 4, 5, 2, 3, 0, 1, pi, i);
}
int64_t mb_get_int64_badcfehg(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(1, 0, 3, 2, 5, 4, 7, 6, pi64);
}
uint64_t mb_set_int64_badcfehg(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(1, 0, 3, 2, 5, 4, 7, 6, pi, i);
}
uint64_t mb_get_uint64_abcdefgh(pui)
val_64_arr *pui;
{
return mb_get_uint64_generic(0, 1, 2, 3, 4, 5, 6, 7, pui);
}
uint64_t mb_set_uint64_abcdefgh(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(0, 1, 2, 3, 4, 5, 6, 7, pui, ui);
}
uint64_t mb_get_uint64_hgfedcba(pui)
val_64_arr *pui;
{
return mb_get_uint64_generic(7, 6, 5, 4, 3, 2, 1, 0, pui);
}
uint64_t mb_set_uint64_hgfedcba(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(7, 6, 5, 4, 3, 2, 1, 0, pui, ui);
}
uint64_t mb_get_uint64_ghefcdab(pui)
val_64_arr *pui;
{
return mb_get_uint64_generic(6, 7, 4, 5, 2, 3, 0, 1, pui);
}
uint64_t mb_set_uint64_ghefcdab(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(6, 7, 4, 5, 2, 3, 0, 1, pui, ui);
}
uint64_t mb_get_uint64_badcfehg(pui)
val_64_arr *pui;
{
return mb_get_int64_generic(1, 0, 3, 2, 5, 4, 7, 6, pui);
}
uint64_t mb_set_uint64_badcfehg(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(1, 0, 3, 2, 5, 4, 7, 6, pui, ui);
}

View File

@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_CONTROLLER_MASTER_H
#define _MB_CONTROLLER_MASTER_H
#include <sys/queue.h> // for list
#include "freertos/FreeRTOS.h" // for task creation and queue access
#include "freertos/task.h" // for task api access
#include "freertos/event_groups.h" // for event groups
#include "driver/uart.h" // for UART types
#include "errno.h" // for errno
#include "esp_log.h" // for log write
#include "string.h" // for strerror()
#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 ------------------------------------------*/
/**
* @brief Request mode for parameter to use in data dictionary
*/
typedef enum {
MB_PARAM_READ, /*!< Read parameter values. */
MB_PARAM_WRITE /*!< Write parameter values. */
} mb_param_mode_t;
/**
* @brief Device communication parameters for master
*/
typedef struct {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint8_t dummy; /*!< Dummy field */
uart_port_t port; /*!< Modbus communication port (UART) number */
uint32_t baudrate; /*!< Modbus baudrate */
uart_parity_t parity; /*!< Modbus UART parity settings */
} mb_master_comm_info_t;
#if MB_MASTER_TCP_ENABLED
/**
* @brief Modbus slave addr list item for the master
*/
typedef struct mb_slave_addr_entry_s{
uint16_t index; /*!< Index of the slave address */
const char* ip_address; /*!< IP address string of the slave */
uint8_t slave_addr; /*!< Short slave address */
void* p_data; /*!< pointer to data structure */
LIST_ENTRY(mb_slave_addr_entry_s) entries; /*!< The slave address entry */
} mb_slave_addr_entry_t;
#endif
/**
* @brief Modbus controller handler structure
*/
typedef struct {
mb_port_type_t port_type; /*!< Modbus port type */
mb_communication_info_t mbm_comm; /*!< Modbus communication info */
uint8_t* mbm_reg_buffer_ptr; /*!< Modbus data buffer pointer */
uint16_t mbm_reg_buffer_size; /*!< Modbus data buffer size */
TaskHandle_t mbm_task_handle; /*!< Modbus task handle */
EventGroupHandle_t mbm_event_group; /*!< Modbus controller event group */
const mb_parameter_descriptor_t* mbm_param_descriptor_table; /*!< Modbus controller parameter description table */
size_t mbm_param_descriptor_size; /*!< Modbus controller parameter description table size*/
#if MB_MASTER_TCP_ENABLED
LIST_HEAD(mbm_slave_addr_info_, mb_slave_addr_entry_s) mbm_slave_list; /*!< Slave address information list */
uint16_t mbm_slave_list_count;
#endif
} mb_master_options_t;
typedef esp_err_t (*iface_get_cid_info)(uint16_t, const mb_parameter_descriptor_t**); /*!< Interface get_cid_info method */
typedef esp_err_t (*iface_get_parameter)(uint16_t, char*, uint8_t*, uint8_t*); /*!< Interface get_parameter method */
typedef esp_err_t (*iface_send_request)(mb_param_request_t*, void*); /*!< Interface send_request method */
typedef esp_err_t (*iface_set_descriptor)(const mb_parameter_descriptor_t*, const uint16_t); /*!< Interface set_descriptor method */
typedef esp_err_t (*iface_set_parameter)(uint16_t, char*, uint8_t*, uint8_t*); /*!< Interface set_parameter method */
/**
* @brief Modbus controller interface structure
*/
typedef struct {
// Master object interface options
mb_master_options_t opts;
// Public interface methods
iface_init init; /*!< Interface method init */
iface_destroy destroy; /*!< Interface method destroy */
iface_setup setup; /*!< Interface method setup */
iface_start start; /*!< Interface method start */
iface_get_cid_info get_cid_info; /*!< Interface get_cid_info method */
iface_get_parameter get_parameter; /*!< Interface get_parameter method */
iface_send_request send_request; /*!< Interface send_request method */
iface_set_descriptor set_descriptor; /*!< Interface set_descriptor method */
iface_set_parameter set_parameter; /*!< Interface set_parameter method */
// Modbus register calback function pointers
reg_discrete_cb master_reg_cb_discrete; /*!< Stack callback discrete rw method */
reg_input_cb master_reg_cb_input; /*!< Stack callback input rw method */
reg_holding_cb master_reg_cb_holding; /*!< Stack callback holding rw method */
reg_coils_cb master_reg_cb_coils; /*!< Stack callback coils rw method */
} mb_master_interface_t;
#endif //_MB_CONTROLLER_MASTER_H

View File

@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_CONTROLLER_SLAVE_H
#define _MB_CONTROLLER_SLAVE_H
#include "driver/uart.h" // for uart defines
#include "errno.h" // for errno
#include "sys/queue.h" // for list
#include "esp_log.h" // for log write
#include "string.h" // for strerror()
#include "esp_modbus_slave.h" // for public type defines
#include "esp_modbus_callbacks.h" // for callback functions
/* ----------------------- Defines ------------------------------------------*/
#define MB_INST_MIN_SIZE (2) // The minimal size of Modbus registers area in bytes
#define MB_INST_MAX_SIZE (65535 * 2) // The maximum size of Modbus area in bytes
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
/**
* @brief Device communication parameters for master
*/
typedef struct {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint8_t slave_addr; /*!< Slave address field */
uart_port_t port; /*!< Modbus communication port (UART) number */
uint32_t baudrate; /*!< Modbus baudrate */
uart_parity_t parity; /*!< Modbus UART parity settings */
} mb_slave_comm_info_t;
/**
* @brief Modbus area descriptor list item
*/
typedef struct mb_descr_entry_s{
uint16_t start_offset; /*!< Modbus start address for area descriptor */
mb_param_type_t type; /*!< Type of storage area descriptor */
void* p_data; /*!< Instance address for storage area descriptor */
size_t size; /*!< Instance size for area descriptor (bytes) */
LIST_ENTRY(mb_descr_entry_s) entries; /*!< The Modbus area descriptor entry */
} mb_descr_entry_t;
/**
* @brief Modbus controller handler structure
*/
typedef struct {
mb_port_type_t port_type; /*!< port type */
mb_communication_info_t mbs_comm; /*!< communication info */
TaskHandle_t mbs_task_handle; /*!< task handle */
EventGroupHandle_t mbs_event_group; /*!< controller event group */
QueueHandle_t mbs_notification_queue_handle; /*!< controller notification queue */
LIST_HEAD(mbs_area_descriptors_, mb_descr_entry_s) mbs_area_descriptors[MB_PARAM_COUNT]; /*!< register area descriptors */
} mb_slave_options_t;
typedef mb_event_group_t (*iface_check_event)(mb_event_group_t); /*!< Interface method check_event */
typedef esp_err_t (*iface_get_param_info)(mb_param_info_t*, uint32_t); /*!< Interface method get_param_info */
typedef esp_err_t (*iface_set_descriptor)(mb_register_area_descriptor_t); /*!< Interface method set_descriptor */
/**
* @brief Request mode for parameter to use in data dictionary
*/
typedef struct
{
mb_slave_options_t opts; /*!< Modbus slave options */
// Functional pointers to internal static functions of the implementation (public interface methods)
iface_init init; /*!< Interface method init */
iface_destroy destroy; /*!< Interface method destroy */
iface_setup setup; /*!< Interface method setup */
iface_start start; /*!< Interface method start */
iface_check_event check_event; /*!< Interface method check_event */
iface_get_param_info get_param_info; /*!< Interface method get_param_info */
iface_set_descriptor set_descriptor; /*!< Interface method set_descriptor */
// Modbus register calback function pointers
reg_discrete_cb slave_reg_cb_discrete; /*!< Stack callback discrete rw method */
reg_input_cb slave_reg_cb_input; /*!< Stack callback input rw method */
reg_holding_cb slave_reg_cb_holding; /*!< Stack callback holding rw method */
reg_coils_cb slave_reg_cb_coils; /*!< Stack callback coils rw method */
} mb_slave_interface_t;
#endif

View File

@@ -0,0 +1,495 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbascii.c,v 1.17 2010/06/06 13:47:07 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbconfig.h"
#include "mbascii.h"
#include "mbframe.h"
#include "mbcrc.h"
#include "mbport.h"
#if MB_SLAVE_ASCII_ENABLED > 0
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
STATE_RX_IDLE, /*!< Receiver is in idle state. */
STATE_RX_RCV, /*!< Frame is beeing received. */
STATE_RX_WAIT_EOF /*!< Wait for End of Frame. */
} eMBRcvState;
typedef enum
{
STATE_TX_IDLE, /*!< Transmitter is in idle state. */
STATE_TX_START, /*!< Starting transmission (':' sent). */
STATE_TX_DATA, /*!< Sending of data (Address, Data, LRC). */
STATE_TX_END, /*!< End of transmission. */
STATE_TX_NOTIFY /*!< Notify sender that the frame has been sent. */
} eMBSndState;
typedef enum
{
BYTE_HIGH_NIBBLE, /*!< Character for high nibble of byte. */
BYTE_LOW_NIBBLE /*!< Character for low nibble of byte. */
} eMBBytePos;
/* ----------------------- Shared variables ---------------------------------*/
/* We reuse the Modbus RTU buffer because only one driver is active */
extern volatile UCHAR ucMbSlaveBuf[];
/* ----------------------- Static functions ---------------------------------*/
static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter );
static UCHAR prvucMBBIN2CHAR( UCHAR ucByte );
static UCHAR prvucMBLRC( UCHAR * pucFrame, USHORT usLen );
/* ----------------------- Static variables ---------------------------------*/
static volatile eMBSndState eSndState;
static volatile eMBRcvState eRcvState;
static volatile UCHAR *ucASCIIBuf = ucMbSlaveBuf;
static volatile USHORT usRcvBufferPos;
static volatile eMBBytePos eBytePos;
static volatile UCHAR *pucSndBufferCur;
static volatile USHORT usSndBufferCount;
static volatile UCHAR ucLRC;
static volatile UCHAR ucMBLFCharacter;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBASCIIInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
( void )ucSlaveAddress;
ENTER_CRITICAL_SECTION( );
ucMBLFCharacter = MB_ASCII_DEFAULT_LF;
if( xMBPortSerialInit( ucPort, ulBaudRate, MB_ASCII_BITS_PER_SYMB, eParity ) != TRUE )
{
eStatus = MB_EPORTERR;
}
else if( xMBPortTimersInit( MB_ASCII_TIMEOUT_MS * 20UL ) != TRUE )
{
eStatus = MB_EPORTERR;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
void
eMBASCIIStart( void )
{
ENTER_CRITICAL_SECTION( );
vMBPortSerialEnable( TRUE, FALSE );
eRcvState = STATE_RX_IDLE;
EXIT_CRITICAL_SECTION( );
/* No special startup required for ASCII. */
( void )xMBPortEventPost( EV_READY );
}
void
eMBASCIIStop( void )
{
ENTER_CRITICAL_SECTION( );
vMBPortSerialEnable( FALSE, FALSE );
vMBPortTimersDisable( );
EXIT_CRITICAL_SECTION( );
}
eMBErrorCode
eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucASCIIBuf;
USHORT usFrameLength = usRcvBufferPos;
if( xMBPortSerialGetRequest( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
ENTER_CRITICAL_SECTION( );
assert( usFrameLength < MB_SER_PDU_SIZE_MAX );
/* Length and CRC check */
if( ( usFrameLength >= MB_ASCII_SER_PDU_SIZE_MIN )
&& ( prvucMBLRC( ( UCHAR * ) pucMBASCIIFrame, usFrameLength ) == 0 ) )
{
/* Save the address field. All frames are passed to the upper layed
* and the decision if a frame is used is done there.
*/
*pucRcvAddress = pucMBASCIIFrame[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
*pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & pucMBASCIIFrame[MB_SER_PDU_PDU_OFF];
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
eMBErrorCode
eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR usLRC;
/* Check if the receiver is still in idle state. If not we where too
* slow with processing the received frame and the master sent another
* frame on the network. We have to abort sending the frame.
*/
if( eRcvState == STATE_RX_IDLE )
{
ENTER_CRITICAL_SECTION( );
/* First byte before the Modbus-PDU is the slave address. */
pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
usSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usSndBufferCount += usLength;
/* Calculate LRC checksum for Modbus-Serial-Line-PDU. */
usLRC = prvucMBLRC( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
ucASCIIBuf[usSndBufferCount++] = usLRC;
/* Activate the transmitter. */
eSndState = STATE_TX_START;
EXIT_CRITICAL_SECTION( );
if ( xMBPortSerialSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
BOOL
xMBASCIIReceiveFSM( void )
{
BOOL xNeedPoll = FALSE;
UCHAR ucByte;
UCHAR ucResult;
assert( eSndState == STATE_TX_IDLE );
xNeedPoll = xMBPortSerialGetByte( ( CHAR * ) & ucByte );
switch ( eRcvState )
{
/* A new character is received. If the character is a ':' the input
* buffer is cleared. A CR-character signals the end of the data
* block. Other characters are part of the data block and their
* ASCII value is converted back to a binary representation.
*/
case STATE_RX_RCV:
/* Enable timer for character timeout. */
vMBPortTimersEnable( );
if( ucByte == ':' )
{
/* Empty receive buffer. */
eBytePos = BYTE_HIGH_NIBBLE;
usRcvBufferPos = 0;
}
else if( ucByte == MB_ASCII_DEFAULT_CR )
{
eRcvState = STATE_RX_WAIT_EOF;
}
else
{
ucResult = prvucMBCHAR2BIN( ucByte );
switch ( eBytePos )
{
/* High nibble of the byte comes first. We check for
* a buffer overflow here. */
case BYTE_HIGH_NIBBLE:
if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
ucASCIIBuf[usRcvBufferPos] = ( UCHAR )( ucResult << 4 );
eBytePos = BYTE_LOW_NIBBLE;
break;
}
else
{
/* not handled in Modbus specification but seems
* a resonable implementation. */
eRcvState = STATE_RX_IDLE;
/* Disable previously activated timer because of error state. */
vMBPortTimersDisable( );
}
break;
case BYTE_LOW_NIBBLE:
ucASCIIBuf[usRcvBufferPos] |= ucResult;
usRcvBufferPos++;
eBytePos = BYTE_HIGH_NIBBLE;
break;
}
}
break;
case STATE_RX_WAIT_EOF:
if( ucByte == ucMBLFCharacter )
{
/* Disable character timeout timer because all characters are
* received. */
vMBPortTimersDisable( );
/* Receiver is again in idle state. */
eRcvState = STATE_RX_IDLE;
/* Notify the caller of eMBASCIIReceive that a new frame
* was received. */
(void)xMBPortEventPost( EV_FRAME_RECEIVED );
}
else if( ucByte == ':' )
{
/* Empty receive buffer and back to receive state. */
eBytePos = BYTE_HIGH_NIBBLE;
usRcvBufferPos = 0;
eRcvState = STATE_RX_RCV;
/* Enable timer for character timeout. */
vMBPortTimersEnable( );
}
else
{
/* Frame is not okay. Delete entire frame. */
eRcvState = STATE_RX_IDLE;
}
break;
case STATE_RX_IDLE:
if( ucByte == ':' )
{
/* Enable timer for character timeout. */
vMBPortTimersEnable( );
/* Reset the input buffers to store the frame. */
usRcvBufferPos = 0;
eBytePos = BYTE_HIGH_NIBBLE;
eRcvState = STATE_RX_RCV;
}
break;
}
return xNeedPoll;
}
BOOL
xMBASCIITransmitFSM( void )
{
BOOL xNeedPoll = TRUE;
UCHAR ucByte;
assert( eRcvState == STATE_RX_IDLE );
switch ( eSndState )
{
/* Start of transmission. The start of a frame is defined by sending
* the character ':'. */
case STATE_TX_START:
ucByte = ':';
xMBPortSerialPutByte( ( CHAR )ucByte );
eSndState = STATE_TX_DATA;
eBytePos = BYTE_HIGH_NIBBLE;
break;
/* Send the data block. Each data byte is encoded as a character hex
* stream with the high nibble sent first and the low nibble sent
* last. If all data bytes are exhausted we send a '\r' character
* to end the transmission. */
case STATE_TX_DATA:
if( usSndBufferCount > 0 )
{
switch ( eBytePos )
{
case BYTE_HIGH_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucSndBufferCur >> 4 ) );
xMBPortSerialPutByte( ( CHAR ) ucByte );
eBytePos = BYTE_LOW_NIBBLE;
break;
case BYTE_LOW_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucSndBufferCur & 0x0F ) );
xMBPortSerialPutByte( ( CHAR )ucByte );
pucSndBufferCur++;
eBytePos = BYTE_HIGH_NIBBLE;
usSndBufferCount--;
break;
}
}
else
{
xMBPortSerialPutByte( MB_ASCII_DEFAULT_CR );
eSndState = STATE_TX_END;
}
break;
/* Finish the frame by sending a LF character. */
case STATE_TX_END:
xMBPortSerialPutByte( ( CHAR )ucMBLFCharacter );
/* We need another state to make sure that the CR character has
* been sent. */
eSndState = STATE_TX_NOTIFY;
break;
/* Notify the task which called eMBASCIISend that the frame has
* been sent. */
case STATE_TX_NOTIFY:
eSndState = STATE_TX_IDLE;
xMBPortEventPost( EV_FRAME_TRANSMIT );
xNeedPoll = FALSE;
break;
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_TX_IDLE:
break;
}
return xNeedPoll;
}
BOOL MB_PORT_ISR_ATTR
xMBASCIITimerT1SExpired( void )
{
switch ( eRcvState )
{
/* If we have a timeout we go back to the idle state and wait for
* the next frame.
*/
case STATE_RX_RCV:
case STATE_RX_WAIT_EOF:
eRcvState = STATE_RX_IDLE;
break;
default:
assert( ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_WAIT_EOF )
|| (eRcvState == STATE_RX_IDLE ));
break;
}
vMBPortTimersDisable( );
/* no context switch required. */
return FALSE;
}
static UCHAR
prvucMBCHAR2BIN( UCHAR ucCharacter )
{
if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )
{
return ( UCHAR )( ucCharacter - '0' );
}
else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )
{
return ( UCHAR )( ucCharacter - 'A' + 0x0A );
}
else
{
return 0xFF;
}
}
static UCHAR
prvucMBBIN2CHAR( UCHAR ucByte )
{
if( ucByte <= 0x09 )
{
return ( UCHAR )( '0' + ucByte );
}
else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )
{
return ( UCHAR )( ucByte - 0x0A + 'A' );
}
else
{
/* Programming error. */
assert( 0 );
}
return '0';
}
static UCHAR
prvucMBLRC( UCHAR * pucFrame, USHORT usLen )
{
UCHAR ucLRC = 0; /* LRC char initialized */
while( usLen-- )
{
ucLRC += *pucFrame++; /* Add buffer byte without carry */
}
/* Return twos complement */
ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );
return ucLRC;
}
#endif

View File

@@ -0,0 +1,85 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbascii.h,v 1.8 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_ASCII_H
#define _MB_ASCII_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Defines ------------------------------------------*/
#define MB_ASCII_DEFAULT_CR '\r' /*!< Default CR character for Modbus ASCII. */
#define MB_ASCII_DEFAULT_LF '\n' /*!< Default LF character for Modbus ASCII. */
#define MB_ASCII_SER_PDU_SIZE_MIN 3 /*!< Minimum size of a Modbus ASCII frame. */
/* ----------------------- Function declaration -----------------------------*/
#if MB_SLAVE_ASCII_ENABLED > 0
eMBErrorCode eMBASCIIInit( UCHAR slaveAddress, UCHAR ucPort,
ULONG ulBaudRate, eMBParity eParity );
void eMBASCIIStart( void );
void eMBASCIIStop( void );
eMBErrorCode eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
USHORT * pusLength );
eMBErrorCode eMBASCIISend( UCHAR slaveAddress, const UCHAR * pucFrame,
USHORT usLength );
BOOL xMBASCIIReceiveFSM( void );
BOOL xMBASCIITransmitFSM( void );
BOOL xMBASCIITimerT1SExpired( void );
#endif
#if MB_MASTER_ASCII_ENABLED > 0
eMBErrorCode eMBMasterASCIIInit( UCHAR ucPort,
ULONG ulBaudRate, eMBParity eParity );
void eMBMasterASCIIStart( void );
void eMBMasterASCIIStop( void );
eMBErrorCode eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
USHORT * pusLength );
eMBErrorCode eMBMasterASCIISend( UCHAR slaveAddress, const UCHAR * pucFrame,
USHORT usLength );
BOOL xMBMasterASCIIReceiveFSM( void );
BOOL xMBMasterASCIITransmitFSM( void );
BOOL xMBMasterASCIITimerT1SExpired( void );
#endif
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,587 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbascii.c,v 1.17 2010/06/06 13:47:07 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbconfig.h"
#include "mbascii.h"
#include "mbframe.h"
#include "mbcrc.h"
#include "mbport.h"
#if MB_MASTER_ASCII_ENABLED > 0
/* ----------------------- Defines ------------------------------------------*/
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
STATE_M_RX_INIT, /*!< Receiver is in initial state. */
STATE_M_RX_IDLE, /*!< Receiver is in idle state. */
STATE_M_RX_RCV, /*!< Frame is beeing received. */
STATE_M_RX_WAIT_EOF, /*!< Wait for End of Frame. */
STATE_M_RX_ERROR, /*!< If the frame is invalid. */
} eMBMasterAsciiRcvState;
typedef enum
{
STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */
STATE_M_TX_START, /*!< Starting transmission (':' sent). */
STATE_M_TX_DATA, /*!< Sending of data (Address, Data, LRC). */
STATE_M_TX_END, /*!< End of transmission. */
STATE_M_TX_NOTIFY, /*!< Notify sender that the frame has been sent. */
STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */
} eMBMasterAsciiSndState;
typedef enum
{
BYTE_HIGH_NIBBLE, /*!< Character for high nibble of byte. */
BYTE_LOW_NIBBLE /*!< Character for low nibble of byte. */
} eMBBytePos;
/* ----------------------- Shared values -----------------------------------*/
/* These Modbus values are shared in ASCII mode*/
extern volatile UCHAR ucMasterRcvBuf[];
extern volatile UCHAR ucMasterSndBuf[];
/* ----------------------- Static functions ---------------------------------*/
static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter );
static UCHAR prvucMBBIN2CHAR( UCHAR ucByte );
static UCHAR prvucMBLRC( UCHAR * pucFrame, USHORT usLen );
/* ----------------------- Static variables ---------------------------------*/
static volatile eMBMasterAsciiSndState eSndState;
static volatile eMBMasterAsciiRcvState eRcvState;
static volatile UCHAR *ucMasterASCIIRcvBuf = ucMasterRcvBuf;
static volatile UCHAR *ucMasterASCIISndBuf = ucMasterSndBuf;
static volatile USHORT usMasterRcvBufferPos;
static volatile eMBBytePos eBytePos;
static volatile UCHAR *pucMasterSndBufferCur;
static volatile USHORT usMasterSndBufferCount;
static volatile UCHAR ucLRC;
static volatile UCHAR ucMBLFCharacter;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBMasterASCIIInit( UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
ENTER_CRITICAL_SECTION( );
ucMBLFCharacter = MB_ASCII_DEFAULT_LF;
if( xMBMasterPortSerialInit( ucPort, ulBaudRate, MB_ASCII_BITS_PER_SYMB, eParity ) != TRUE )
{
eStatus = MB_EPORTERR;
}
else if( xMBMasterPortTimersInit( MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS ) != TRUE )
{
eStatus = MB_EPORTERR;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
void
eMBMasterASCIIStart( void )
{
ENTER_CRITICAL_SECTION( );
eRcvState = STATE_M_RX_IDLE;
vMBMasterPortSerialEnable( TRUE, FALSE );
xMBMasterPortEventPost(EV_MASTER_READY);
EXIT_CRITICAL_SECTION( );
}
void
eMBMasterASCIIStop( void )
{
ENTER_CRITICAL_SECTION( );
vMBMasterPortSerialEnable( FALSE, FALSE );
vMBMasterPortTimersDisable( );
EXIT_CRITICAL_SECTION( );
}
eMBErrorCode
eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucMasterASCIIRcvBuf;
USHORT usFrameLength = usMasterRcvBufferPos;
if( xMBMasterPortSerialGetResponse( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
ENTER_CRITICAL_SECTION( );
assert( usFrameLength < MB_SER_PDU_SIZE_MAX );
assert( pucMBASCIIFrame );
/* Length and CRC check */
if( ( usFrameLength >= MB_ASCII_SER_PDU_SIZE_MIN )
&& ( prvucMBLRC( ( UCHAR * ) pucMBASCIIFrame, usFrameLength ) == 0 ) )
{
/* Save the address field. All frames are passed to the upper layed
* and the decision if a frame is used is done there.
*/
*pucRcvAddress = pucMBASCIIFrame[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
*pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & pucMBASCIIFrame[MB_SER_PDU_PDU_OFF];
} else {
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
eMBErrorCode
eMBMasterASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR usLRC;
if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
/* Check if the receiver is still in idle state. If not we where too
* slow with processing the received frame and the master sent another
* frame on the network. We have to abort sending the frame.
*/
if(eRcvState == STATE_M_RX_IDLE)
{
ENTER_CRITICAL_SECTION( );
/* First byte before the Modbus-PDU is the slave address. */
pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
usMasterSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usMasterSndBufferCount += usLength;
/* Calculate LRC checksum for Modbus-Serial-Line-PDU. */
usLRC = prvucMBLRC( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
pucMasterSndBufferCur[usMasterSndBufferCount++] = usLRC;
/* Activate the transmitter. */
eSndState = STATE_M_TX_START;
EXIT_CRITICAL_SECTION( );
if ( xMBMasterPortSerialSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBMasterPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
return eStatus;
}
BOOL
xMBMasterASCIIReceiveFSM( void )
{
BOOL xNeedPoll = FALSE;
UCHAR ucByte;
UCHAR ucResult;
assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
/* Always read the character. */
xNeedPoll = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
switch ( eRcvState )
{
/* If we have received a character in the init state we have to
* wait until the frame is finished.
*/
case STATE_M_RX_INIT:
vMBMasterPortTimersT35Enable( );
break;
/* In the error state we wait until all characters in the
* damaged frame are transmitted.
*/
case STATE_M_RX_ERROR:
vMBMasterPortTimersRespondTimeoutEnable( );
break;
/* In the idle state we wait for a new character. If a character
* is received the t1.5 and t3.5 timers are started and the
* receiver is in the state STATE_RX_RECEIVE and disable early
* the timer of respond timeout .
*/
case STATE_M_RX_IDLE:
/* Waiting for the start of frame character during respond timeout */
vMBMasterPortTimersRespondTimeoutEnable( );
if( ucByte == ':' )
{
/* Reset the input buffers to store the frame in receive state. */
usMasterRcvBufferPos = 0;
eBytePos = BYTE_HIGH_NIBBLE;
eRcvState = STATE_M_RX_RCV;
eSndState = STATE_M_TX_IDLE;
}
break;
/* A new character is received. If the character is a ':' the input
* buffer is cleared. A CR-character signals the end of the data
* block. Other characters are part of the data block and their
* ASCII value is converted back to a binary representation.
*/
case STATE_M_RX_RCV:
/* Enable timer timeout. */
vMBMasterPortTimersT35Enable( );
if( ucByte == ':' )
{
/* Empty receive buffer. */
eBytePos = BYTE_HIGH_NIBBLE;
usMasterRcvBufferPos = 0;
}
else if( ucByte == MB_ASCII_DEFAULT_CR )
{
eRcvState = STATE_M_RX_WAIT_EOF;
}
else
{
ucResult = prvucMBCHAR2BIN( ucByte );
switch ( eBytePos )
{
/* High nibble of the byte comes first. We check for
* a buffer overflow here. */
case BYTE_HIGH_NIBBLE:
if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
ucMasterASCIIRcvBuf[usMasterRcvBufferPos] = ( UCHAR )( ucResult << 4 );
eBytePos = BYTE_LOW_NIBBLE;
break;
}
else
{
/* not handled in Modbus specification but seems
* a resonable implementation. */
eRcvState = STATE_M_RX_ERROR;
/* Disable previously activated timer because of error state. */
vMBPortTimersDisable( );
}
break;
case BYTE_LOW_NIBBLE:
ucMasterASCIIRcvBuf[usMasterRcvBufferPos] |= ucResult;
usMasterRcvBufferPos++;
eBytePos = BYTE_HIGH_NIBBLE;
break;
}
}
break;
case STATE_M_RX_WAIT_EOF:
if( ucByte == ucMBLFCharacter )
{
/* Disable character timeout timer because all characters are
* received. */
vMBMasterPortTimersDisable( );
/* Receiver is again in idle state. */
eRcvState = STATE_M_RX_IDLE;
/* Notify the caller of eMBMasterASCIIReceive that a new frame
* was received. */
(void)xMBMasterPortEventPost( EV_MASTER_FRAME_RECEIVED );
xNeedPoll = FALSE;
}
else if( ucByte == ':' )
{
/* Start of frame character received but last message is not completed.
* Empty receive buffer and back to receive state. */
eBytePos = BYTE_HIGH_NIBBLE;
usMasterRcvBufferPos = 0;
eRcvState = STATE_M_RX_IDLE;
/* Enable timer for respond timeout and wait for next frame. */
vMBMasterPortTimersRespondTimeoutEnable( );
}
else
{
/* Frame is not okay. Delete entire frame. */
eRcvState = STATE_M_RX_IDLE;
}
break;
}
return xNeedPoll;
}
BOOL
xMBMasterASCIITransmitFSM( void )
{
BOOL xNeedPoll = TRUE;
UCHAR ucByte;
BOOL xFrameIsBroadcast = FALSE;
assert( eRcvState == STATE_M_RX_IDLE );
switch ( eSndState )
{
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_M_TX_XFWR:
break;
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_M_TX_IDLE:
break;
/* Start of transmission. The start of a frame is defined by sending
* the character ':'. */
case STATE_M_TX_START:
ucByte = ':';
xMBMasterPortSerialPutByte( ( CHAR )ucByte );
eSndState = STATE_M_TX_DATA;
eBytePos = BYTE_HIGH_NIBBLE;
break;
/* Send the data block. Each data byte is encoded as a character hex
* stream with the high nibble sent first and the low nibble sent
* last. If all data bytes are exhausted we send a '\r' character
* to end the transmission. */
case STATE_M_TX_DATA:
if( usMasterSndBufferCount > 0 )
{
switch ( eBytePos )
{
case BYTE_HIGH_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur >> 4 ) );
xMBMasterPortSerialPutByte( ( CHAR ) ucByte );
eBytePos = BYTE_LOW_NIBBLE;
break;
case BYTE_LOW_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur & 0x0F ) );
xMBMasterPortSerialPutByte( ( CHAR )ucByte );
pucMasterSndBufferCur++;
eBytePos = BYTE_HIGH_NIBBLE;
usMasterSndBufferCount--;
break;
}
}
else
{
xMBMasterPortSerialPutByte( MB_ASCII_DEFAULT_CR );
eSndState = STATE_M_TX_END;
}
break;
/* Finish the frame by sending a LF character. */
case STATE_M_TX_END:
xMBMasterPortSerialPutByte( ( CHAR )ucMBLFCharacter );
/* We need another state to make sure that the CR character has
* been sent. */
eSndState = STATE_M_TX_NOTIFY;
break;
/* Notify the task which called eMBMasterASCIISend that the frame has
* been sent. */
case STATE_M_TX_NOTIFY:
xFrameIsBroadcast = ( ucMasterASCIISndBuf[MB_SEND_BUF_PDU_OFF - MB_SER_PDU_PDU_OFF]
== MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
vMBMasterRequestSetType( xFrameIsBroadcast );
eSndState = STATE_M_TX_XFWR;
/* If the frame is broadcast ,master will enable timer of convert delay,
* else master will enable timer of respond timeout. */
if ( xFrameIsBroadcast == TRUE )
{
vMBMasterPortTimersConvertDelayEnable( );
}
else
{
vMBMasterPortTimersRespondTimeoutEnable( );
}
xNeedPoll = FALSE;
break;
}
return xNeedPoll;
}
BOOL MB_PORT_ISR_ATTR
xMBMasterASCIITimerT1SExpired( void )
{
BOOL xNeedPoll = FALSE;
switch ( eRcvState )
{
/* Timer t35 expired. Startup phase is finished. */
case STATE_M_RX_INIT:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
break;
/* Start of message is not received during respond timeout.
* Process error. */
case STATE_M_RX_IDLE:
eRcvState = STATE_M_RX_ERROR;
break;
/* A recieve timeout expired and no any new character received.
* Wait for respond time and go to error state to inform listener about error */
case STATE_M_RX_RCV:
eRcvState = STATE_M_RX_ERROR;
break;
/* An error occured while receiving the frame. */
case STATE_M_RX_ERROR:
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
xNeedPoll = xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
break;
/* If we have a timeout we go back to the idle state and wait for
* the next frame.
*/
case STATE_M_RX_WAIT_EOF:
eRcvState = STATE_M_RX_IDLE;
break;
default:
assert( 0 );
break;
}
eRcvState = STATE_M_RX_IDLE;
switch (eSndState)
{
/* A frame was send finish and convert delay or respond timeout expired.
* If the frame is broadcast,The master will idle,and if the frame is not
* broadcast.*/
case STATE_M_TX_XFWR:
if ( xMBMasterRequestIsBroadcast( ) == FALSE ) {
vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
}
break;
/* Function called in an illegal state. */
default:
assert( ( eSndState == STATE_M_TX_START ) || ( eSndState == STATE_M_TX_IDLE )
|| ( eSndState == STATE_M_TX_DATA ) || ( eSndState == STATE_M_TX_END )
|| ( eSndState == STATE_M_TX_NOTIFY ) );
break;
}
eSndState = STATE_M_TX_IDLE;
vMBMasterPortTimersDisable( );
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
if (xMBMasterGetCurTimerMode() == MB_TMODE_CONVERT_DELAY) {
xNeedPoll = xMBMasterPortEventPost( EV_MASTER_EXECUTE );
}
vMBMasterPortTimersDisable( );
/* no context switch required. */
return xNeedPoll;
}
static UCHAR
prvucMBCHAR2BIN( UCHAR ucCharacter )
{
if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )
{
return ( UCHAR )( ucCharacter - '0' );
}
else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )
{
return ( UCHAR )( ucCharacter - 'A' + 0x0A );
}
else
{
return 0xFF;
}
}
static UCHAR
prvucMBBIN2CHAR( UCHAR ucByte )
{
if( ucByte <= 0x09 )
{
return ( UCHAR )( '0' + ucByte );
}
else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )
{
return ( UCHAR )( ucByte - 0x0A + 'A' );
}
else
{
/* Programming error. */
assert( 0 );
}
return '0';
}
static UCHAR
prvucMBLRC( UCHAR * pucFrame, USHORT usLen )
{
UCHAR ucLRC = 0; /* LRC char initialized */
while( usLen-- )
{
ucLRC += *pucFrame++; /* Add buffer byte without carry */
}
/* Return twos complement */
ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );
return ucLRC;
}
#endif

View File

@@ -0,0 +1,280 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfunccoils.c,v 1.8 2007/02/18 23:47:16 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_COILCNT_MAX ( 0x07D0 )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_FUNC_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX ( 0x07B0 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_READ_COILS_ENABLED
eMBException
eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usCoilCount;
UCHAR ucNBytes;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usCoilCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8 );
usCoilCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usCoilCount >= 1 ) &&
( usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_COILS;
*usLen += 1;
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usCoilCount & 0x0007 ) != 0 )
{
ucNBytes = ( UCHAR )( usCoilCount / 8 + 1 );
}
else
{
ucNBytes = ( UCHAR )( usCoilCount / 8 );
}
*pucFrameCur++ = ucNBytes;
*usLen += 1;
eRegStatus =
eMBRegCoilsCB( pucFrameCur, usRegAddress, usCoilCount,
MB_REG_READ );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen += ucNBytes;;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#if MB_FUNC_WRITE_COIL_ENABLED > 0
eMBException
eMBFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
UCHAR ucBuf[2];
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) &&
( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) ||
( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) )
{
ucBuf[1] = 0;
if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF )
{
ucBuf[0] = 1;
}
else
{
ucBuf[0] = 0;
}
eRegStatus =
eMBRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
eMBException
eMBFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usCoilCnt;
UCHAR ucByteCount;
UCHAR ucByteCountVerify;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen > ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 );
usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] );
ucByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF];
/* Compute the number of expected bytes in the request. */
if( ( usCoilCnt & 0x0007 ) != 0 )
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 );
}
else
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 );
}
if( ( usCoilCnt >= 1 ) &&
( usCoilCnt <= MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX ) &&
( ucByteCountVerify == ucByteCount ) )
{
eRegStatus =
eMBRegCoilsCB( &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF],
usRegAddress, usCoilCnt, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif
#endif

View File

@@ -0,0 +1,398 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfunccoils_m.c,v 1.60 2013/10/12 15:10:12 Armink Add Master Functions
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
#include "mbutils.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
#define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_REQ_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ( 0x07B0 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE ( 5 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_READ_COILS_ENABLED
/**
* This function will request read coil.
*
* @param ucSndAddr salve address
* @param usCoilAddr coil start address
* @param usNCoils coil total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_COILS;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usCoilAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usCoilAddr;
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF ] = usNCoils >> 8;
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] = usNCoils;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usCoilCount;
UCHAR ucByteCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if ( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usCoilCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF] << 8 );
usCoilCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] );
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usCoilCount & 0x0007 ) != 0 )
{
ucByteCount = ( UCHAR )( usCoilCount / 8 + 1 );
}
else
{
ucByteCount = ( UCHAR )( usCoilCount / 8 );
}
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usCoilCount >= 1 ) &&
( ucByteCount == pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] ) )
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegCoilsCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usCoilCount, MB_REG_READ );
/* If an error occurred convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
/**
* This function will request write one coil.
*
* @param ucSndAddr salve address
* @param usCoilAddr coil start address
* @param usCoilData data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*
* @see eMBMasterReqWriteMultipleCoils
*/
eMBMasterReqErrCode
eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( ( usCoilData != 0xFF00 ) && ( usCoilData != 0x0000 ) ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_SINGLE_COIL;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usCoilAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usCoilAddr;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF ] = usCoilData >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usCoilData;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
UCHAR ucBuf[2];
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) &&
( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) ||
( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) )
{
ucBuf[1] = 0;
if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF )
{
ucBuf[0] = 1;
}
else
{
ucBuf[0] = 0;
}
eRegStatus =
eMBMasterRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif // #if MB_FUNC_WRITE_COIL_ENABLED > 0
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
/**
* This function will request write multiple coils.
*
* @param ucSndAddr salve address
* @param usCoilAddr coil start address
* @param usNCoils coil total number
* @param usCoilData data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*
* @see eMBMasterReqWriteCoil
*/
eMBMasterReqErrCode
eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut)
{
UCHAR *ucMBFrame;
USHORT usRegIndex = 0;
UCHAR ucByteCount;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( usNCoils > MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_COILS;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usCoilAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usCoilAddr;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF] = usNCoils >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF + 1] = usNCoils ;
if( ( usNCoils & 0x0007 ) != 0 )
{
ucByteCount = ( UCHAR )( usNCoils / 8 + 1 );
}
else
{
ucByteCount = ( UCHAR )( usNCoils / 8 );
}
ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = ucByteCount;
ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF;
while( ucByteCount > usRegIndex)
{
*ucMBFrame++ = pucDataBuffer[usRegIndex++];
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + ucByteCount );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usCoilCnt;
UCHAR ucByteCount;
UCHAR ucByteCountVerify;
UCHAR *ucMBFrame;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, the *usLen is not need check. */
if( ( *usLen == MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 );
usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] );
ucByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF];
/* Compute the number of expected bytes in the request. */
if( ( usCoilCnt & 0x0007 ) != 0 )
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 );
}
else
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 );
}
if( ( usCoilCnt >= 1 ) && ( ucByteCountVerify == ucByteCount ) )
{
eRegStatus =
eMBMasterRegCoilsCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF],
usRegAddress, usCoilCnt, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
#endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0

View File

@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncdiag.c,v 1.3 2006/12/07 22:10:34 wolti Exp $
*/

View File

@@ -0,0 +1,143 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeRTOS Modbus Libary: A Modbus serial implementation for FreeRTOS
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncdisc.c,v 1.10 2007/09/12 10:15:56 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_DISCCNT_MAX ( 0x07D0 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_READ_COILS_ENABLED
eMBException
eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usDiscreteCnt;
UCHAR ucNBytes;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usDiscreteCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF] << 8 );
usDiscreteCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usDiscreteCnt >= 1 ) &&
( usDiscreteCnt < MB_PDU_FUNC_READ_DISCCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_DISCRETE_INPUTS;
*usLen += 1;
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usDiscreteCnt & 0x0007 ) != 0 )
{
ucNBytes = ( UCHAR ) ( usDiscreteCnt / 8 + 1 );
}
else
{
ucNBytes = ( UCHAR ) ( usDiscreteCnt / 8 );
}
*pucFrameCur++ = ucNBytes;
*usLen += 1;
eRegStatus =
eMBRegDiscreteCB( pucFrameCur, usRegAddress, usDiscreteCnt );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen += ucNBytes;;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif

View File

@@ -0,0 +1,167 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncdisc_m.c,v 1.60 2013/10/15 8:48:20 Armink Add Master Functions Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
/**
* This function will request read discrete inputs.
*
* @param ucSndAddr salve address
* @param usDiscreteAddr discrete start address
* @param usNDiscreteIn discrete total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_DISCRETE_INPUTS;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usDiscreteAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usDiscreteAddr;
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF ] = usNDiscreteIn >> 8;
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF + 1] = usNDiscreteIn;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usDiscreteCnt;
UCHAR ucNBytes;
UCHAR *ucMBFrame;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usDiscreteCnt = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF] << 8 );
usDiscreteCnt |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF + 1] );
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usDiscreteCnt & 0x0007 ) != 0 )
{
ucNBytes = ( UCHAR )( usDiscreteCnt / 8 + 1 );
}
else
{
ucNBytes = ( UCHAR )( usDiscreteCnt / 8 );
}
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if ((usDiscreteCnt >= 1) && ucNBytes == pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF])
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegDiscreteCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usDiscreteCnt );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif // #if MB_SERIAL_MASTER_RTU_ENABLED || MB_SERIAL_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@@ -0,0 +1,318 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncholding.c,v 1.12 2007/02/18 23:48:22 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_FUNC_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_REGCNT_MAX ( 0x007D )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_FUNC_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX ( 0x0078 )
#define MB_PDU_FUNC_READWRITE_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF ( MB_PDU_DATA_OFF + 6 )
#define MB_PDU_FUNC_READWRITE_BYTECNT_OFF ( MB_PDU_DATA_OFF + 8 )
#define MB_PDU_FUNC_READWRITE_WRITE_VALUES_OFF ( MB_PDU_DATA_OFF + 9 )
#define MB_PDU_FUNC_READWRITE_SIZE_MIN ( 9 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_WRITE_HOLDING_ENABLED
eMBException
eMBFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
/* Make callback to update the value. */
eRegStatus = eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF],
usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
eMBException
eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usRegCount;
UCHAR ucRegByteCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen >= ( MB_PDU_FUNC_WRITE_MUL_SIZE_MIN + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF + 1] );
ucRegByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF];
if( ( usRegCount >= 1 ) &&
( usRegCount <= MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX ) &&
( ucRegByteCount == ( UCHAR ) ( 2 * usRegCount ) ) )
{
/* Make callback to update the register values. */
eRegStatus =
eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF],
usRegAddress, usRegCount, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting
* address and the quantity of registers. We reuse the
* old values in the buffer because they are still valid.
*/
*usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
eMBException
eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usRegCount;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 ) && ( usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_HOLDING_REGISTER;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = ( UCHAR ) ( usRegCount * 2 );
*usLen += 1;
/* Make callback to fill the buffer. */
eRegStatus = eMBRegHoldingCB( pucFrameCur, usRegAddress, usRegCount, MB_REG_READ );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
*usLen += usRegCount * 2;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
eMBException
eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegReadAddress;
USHORT usRegReadCount;
USHORT usRegWriteAddress;
USHORT usRegWriteCount;
UCHAR ucRegWriteByteCount;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen >= ( MB_PDU_FUNC_READWRITE_SIZE_MIN + MB_PDU_SIZE_MIN ) )
{
usRegReadAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_ADDR_OFF] << 8U );
usRegReadAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_ADDR_OFF + 1] );
usRegReadAddress++;
usRegReadCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF] << 8U );
usRegReadCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF + 1] );
usRegWriteAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF] << 8U );
usRegWriteAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF + 1] );
usRegWriteAddress++;
usRegWriteCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF] << 8U );
usRegWriteCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF + 1] );
ucRegWriteByteCount = pucFrame[MB_PDU_FUNC_READWRITE_BYTECNT_OFF];
if( ( usRegReadCount >= 1 ) && ( usRegReadCount <= 0x7D ) &&
( usRegWriteCount >= 1 ) && ( usRegWriteCount <= 0x79 ) &&
( ( 2 * usRegWriteCount ) == ucRegWriteByteCount ) )
{
/* Make callback to update the register values. */
eRegStatus = eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_READWRITE_WRITE_VALUES_OFF],
usRegWriteAddress, usRegWriteCount, MB_REG_WRITE );
if( eRegStatus == MB_ENOERR )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READWRITE_MULTIPLE_REGISTERS;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = ( UCHAR ) ( usRegReadCount * 2 );
*usLen += 1;
/* Make the read callback. */
eRegStatus =
eMBRegHoldingCB( pucFrameCur, usRegReadAddress, usRegReadCount, MB_REG_READ );
if( eRegStatus == MB_ENOERR )
{
*usLen += 2 * usRegReadCount;
}
}
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
return eStatus;
}
#endif
#endif

View File

@@ -0,0 +1,461 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncholding_m.c,v 1.60 2013/09/02 14:13:40 Armink Add Master Functions Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
//#include "mb.h"
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_REGCNT_MAX ( 0x007D )
#define MB_PDU_FUNC_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
#define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_REQ_WRITE_MUL_REGCNT_MAX ( 0x0078 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE ( 4 )
#define MB_PDU_REQ_READWRITE_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READWRITE_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF ( MB_PDU_DATA_OFF + 6 )
#define MB_PDU_REQ_READWRITE_WRITE_BYTECNT_OFF ( MB_PDU_DATA_OFF + 8 )
#define MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF ( MB_PDU_DATA_OFF + 9 )
#define MB_PDU_REQ_READWRITE_SIZE_MIN ( 9 )
#define MB_PDU_FUNC_READWRITE_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READWRITE_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READWRITE_SIZE_MIN ( 1 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_WRITE_HOLDING_ENABLED
/**
* This function will request write holding register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usRegData register data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_REGISTER;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF] = usRegData >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usRegData ;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_SIZE_MIN + MB_PDU_FUNC_WRITE_SIZE ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
/* Make callback to update the value. */
eRegStatus = eMBMasterRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF],
usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
/**
* This function will request write multiple holding register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usNRegs register total number
* @param pusDataBuffer data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr,
USHORT usRegAddr, USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut )
{
UCHAR *ucMBFrame;
USHORT usRegIndex = 0;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_REGISTERS;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] = usNRegs ;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = usNRegs * 2;
ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF;
while( usNRegs > usRegIndex)
{
*ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8;
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + 2*usNRegs );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usRegCount;
UCHAR ucRegByteCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, the *usLen is not need check. */
if( ( *usLen == MB_PDU_SIZE_MIN + MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] );
ucRegByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF];
if( ucRegByteCount == 2 * usRegCount )
{
/* Make callback to update the register values. */
eRegStatus = eMBMasterRegHoldingCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF],
usRegAddress, usRegCount, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
/**
* This function will request read holding register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usNRegs register total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_HOLDING_REGISTER;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usRegCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 ) && ( 2 * usRegCount == pucFrame[MB_PDU_FUNC_READ_BYTECNT_OFF] ) )
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegHoldingCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usRegCount, MB_REG_READ );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
/**
* This function will request read and write holding register.
*
* @param ucSndAddr salve address
* @param usReadRegAddr read register start address
* @param usNReadRegs read register total number
* @param pusDataBuffer data to be written
* @param usWriteRegAddr write register start address
* @param usNWriteRegs write register total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr,
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut )
{
UCHAR *ucMBFrame;
USHORT usRegIndex = 0;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READWRITE_MULTIPLE_REGISTERS;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF] = usReadRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF + 1] = usReadRegAddr;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF] = usNReadRegs >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] = usNReadRegs ;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF] = usWriteRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF + 1] = usWriteRegAddr;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF] = usNWriteRegs >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] = usNWriteRegs ;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_BYTECNT_OFF] = usNWriteRegs * 2;
ucMBFrame += MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF;
while( usNWriteRegs > usRegIndex)
{
*ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8;
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READWRITE_SIZE_MIN + 2*usNWriteRegs );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegReadAddress;
USHORT usRegReadCount;
USHORT usRegWriteAddress;
USHORT usRegWriteCount;
UCHAR *ucMBFrame;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READWRITE_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegReadAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF] << 8U );
usRegReadAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF + 1] );
usRegReadAddress++;
usRegReadCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF] << 8U );
usRegReadCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] );
usRegWriteAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF] << 8U );
usRegWriteAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF + 1] );
usRegWriteAddress++;
usRegWriteCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF] << 8U );
usRegWriteCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] );
if( ( 2 * usRegReadCount ) == pucFrame[MB_PDU_FUNC_READWRITE_READ_BYTECNT_OFF] )
{
/* Make callback to update the register values. */
eRegStatus = eMBMasterRegHoldingCB( &ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF],
usRegWriteAddress, usRegWriteCount, MB_REG_WRITE );
if( eRegStatus == MB_ENOERR )
{
/* Make the read callback. */
eRegStatus = eMBMasterRegHoldingCB(&pucFrame[MB_PDU_FUNC_READWRITE_READ_VALUES_OFF],
usRegReadAddress, usRegReadCount, MB_REG_READ);
}
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
return eStatus;
}
#endif
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncinput.c,v 1.10 2007/09/12 10:15:56 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_REGCNT_MAX ( 0x007D )
#define MB_PDU_FUNC_READ_RSP_BYTECNT_OFF ( MB_PDU_DATA_OFF )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_READ_INPUT_ENABLED
eMBException
eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usRegCount;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 )
&& ( usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = ( UCHAR )( usRegCount * 2 );
*usLen += 1;
eRegStatus =
eMBRegInputCB( pucFrameCur, usRegAddress, usRegCount );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
*usLen += usRegCount * 2;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read input register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif

View File

@@ -0,0 +1,154 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncinput_m.c,v 1.60 2013/10/12 14:23:40 Armink Add Master Functions Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
#define MB_PDU_FUNC_READ_RSP_BYTECNT_OFF ( MB_PDU_DATA_OFF )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_READ_INPUT_ENABLED
/**
* This function will request read input register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usNRegs register total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadInputRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_INPUT_REGISTER;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usRegCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 ) && ( 2 * usRegCount == pucFrame[MB_PDU_FUNC_READ_BYTECNT_OFF] ) )
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegInputCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usRegCount );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncother.c,v 1.8 2006/12/07 22:10:34 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
/* ----------------------- Static variables ---------------------------------*/
static UCHAR ucMBSlaveID[MB_FUNC_OTHER_REP_SLAVEID_BUF];
static USHORT usMBSlaveIDLen;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning,
UCHAR const *pucAdditional, USHORT usAdditionalLen )
{
eMBErrorCode eStatus = MB_ENOERR;
/* the first byte and second byte in the buffer is reserved for
* the parameter ucSlaveID and the running flag. The rest of
* the buffer is available for additional data. */
if( usAdditionalLen + 2 < MB_FUNC_OTHER_REP_SLAVEID_BUF )
{
usMBSlaveIDLen = 0;
ucMBSlaveID[usMBSlaveIDLen++] = ucSlaveID;
ucMBSlaveID[usMBSlaveIDLen++] = ( UCHAR )( xIsRunning ? 0xFF : 0x00 );
if( usAdditionalLen > 0 )
{
memcpy( &ucMBSlaveID[usMBSlaveIDLen], pucAdditional,
( size_t )usAdditionalLen );
usMBSlaveIDLen += usAdditionalLen;
}
}
else
{
eStatus = MB_ENORES;
}
return eStatus;
}
eMBException
eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen )
{
memcpy( &pucFrame[MB_PDU_DATA_OFF], &ucMBSlaveID[0], ( size_t )usMBSlaveIDLen );
*usLen = ( USHORT )( MB_PDU_DATA_OFF + usMBSlaveIDLen );
return MB_EX_NONE;
}
#endif
#endif

View File

@@ -0,0 +1,148 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbutils.c,v 1.6 2007/02/18 23:49:07 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbproto.h"
/* ----------------------- Defines ------------------------------------------*/
#define BITS_UCHAR 8U
/* ----------------------- Start implementation -----------------------------*/
void
xMBUtilSetBits( UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits,
UCHAR ucValue )
{
USHORT usWordBuf;
USHORT usMask;
USHORT usByteOffset;
USHORT usNPreBits;
USHORT usValue = ucValue;
assert( ucNBits <= 8 );
assert( ( size_t )BITS_UCHAR == sizeof( UCHAR ) * 8 );
/* Calculate byte offset for first byte containing the bit values starting
* at usBitOffset. */
usByteOffset = ( USHORT )( ( usBitOffset ) / BITS_UCHAR );
/* How many bits precede our bits to set. */
usNPreBits = ( USHORT )( usBitOffset - usByteOffset * BITS_UCHAR );
/* Move bit field into position over bits to set */
usValue <<= usNPreBits;
/* Prepare a mask for setting the new bits. */
usMask = ( USHORT )( ( 1 << ( USHORT ) ucNBits ) - 1 );
usMask <<= usBitOffset - usByteOffset * BITS_UCHAR;
/* copy bits into temporary storage. */
usWordBuf = ucByteBuf[usByteOffset];
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR;
/* Zero out bit field bits and then or value bits into them. */
usWordBuf = ( USHORT )( ( usWordBuf & ( ~usMask ) ) | usValue );
/* move bits back into storage */
ucByteBuf[usByteOffset] = ( UCHAR )( usWordBuf & 0xFF );
ucByteBuf[usByteOffset + 1] = ( UCHAR )( usWordBuf >> BITS_UCHAR );
}
UCHAR
xMBUtilGetBits( UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits )
{
USHORT usWordBuf;
USHORT usMask;
USHORT usByteOffset;
USHORT usNPreBits;
/* Calculate byte offset for first byte containing the bit values starting
* at usBitOffset. */
usByteOffset = ( USHORT )( ( usBitOffset ) / BITS_UCHAR );
/* How many bits precede our bits to set. */
usNPreBits = ( USHORT )( usBitOffset - usByteOffset * BITS_UCHAR );
/* Prepare a mask for setting the new bits. */
usMask = ( USHORT )( ( 1 << ( USHORT ) ucNBits ) - 1 );
/* copy bits into temporary storage. */
usWordBuf = ucByteBuf[usByteOffset];
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR;
/* throw away unneeded bits. */
usWordBuf >>= usNPreBits;
/* mask away bits above the requested bitfield. */
usWordBuf &= usMask;
return ( UCHAR ) usWordBuf;
}
eMBException
prveMBError2Exception( eMBErrorCode eErrorCode )
{
eMBException eStatus;
switch ( eErrorCode )
{
case MB_ENOERR:
eStatus = MB_EX_NONE;
break;
case MB_ENOREG:
eStatus = MB_EX_ILLEGAL_DATA_ADDRESS;
break;
case MB_ETIMEDOUT:
eStatus = MB_EX_SLAVE_BUSY;
break;
default:
eStatus = MB_EX_SLAVE_DEVICE_FAILURE;
break;
}
return eStatus;
}

View File

@@ -0,0 +1,425 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mb.h,v 1.17 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_H
#define _MB_H
#include "port.h"
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
#include "mbport.h"
#include "mbproto.h"
/*! \defgroup modbus Modbus
* \code #include "mb.h" \endcode
*
* This module defines the interface for the application. It contains
* the basic functions and types required to use the Modbus protocol stack.
* A typical application will want to call eMBInit() first. If the device
* is ready to answer network requests it must then call eMBEnable() to activate
* the protocol stack. In the main loop the function eMBPoll() must be called
* periodically. The time interval between pooling depends on the configured
* Modbus timeout. If an RTOS is available a separate task should be created
* and the task should always call the function eMBPoll().
*
* \code
* // Initialize protocol stack in RTU mode for a slave with address 10 = 0x0A
* eMBInit( MB_RTU, 0x0A, 38400, MB_PAR_EVEN );
* // Enable the Modbus Protocol Stack.
* eMBEnable( );
* for( ;; )
* {
* // Call the main polling loop of the Modbus protocol stack.
* eMBPoll( );
* ...
* }
* \endcode
*/
/* ----------------------- Defines ------------------------------------------*/
/*! \ingroup modbus
* \brief Use the default Modbus TCP port (502)
*/
#define MB_TCP_PORT_USE_DEFAULT 0
#define MB_FUNC_CODE_MAX 127
/* ----------------------- Type definitions ---------------------------------*/
/*! \ingroup modbus
* \brief Modbus serial transmission modes (RTU/ASCII).
*
* Modbus serial supports two transmission modes. Either ASCII or RTU. RTU
* is faster but has more hardware requirements and requires a network with
* a low jitter. ASCII is slower and more reliable on slower links (E.g. modems)
*/
typedef enum
{
MB_RTU, /*!< RTU transmission mode. */
MB_ASCII, /*!< ASCII transmission mode. */
MB_TCP /*!< TCP mode. */
} eMBMode;
/*! \ingroup modbus
* \brief If register should be written or read.
*
* This value is passed to the callback functions which support either
* reading or writing register values. Writing means that the application
* registers should be updated and reading means that the modbus protocol
* stack needs to know the current register values.
*
* \see eMBRegHoldingCB( ), eMBRegCoilsCB( ), eMBRegDiscreteCB( ) and
* eMBRegInputCB( ).
*/
typedef enum
{
MB_REG_READ, /*!< Read register values and pass to protocol stack. */
MB_REG_WRITE /*!< Update register values. */
} eMBRegisterMode;
/*! \ingroup modbus
* \brief Errorcodes used by all function in the protocol stack.
*/
typedef enum
{
MB_ENOERR, /*!< no error. */
MB_ENOREG, /*!< illegal register address. */
MB_EINVAL, /*!< illegal argument. */
MB_EPORTERR, /*!< porting layer error. */
MB_ENORES, /*!< insufficient resources. */
MB_EIO, /*!< I/O error. */
MB_EILLSTATE, /*!< protocol stack in illegal state. */
MB_ETIMEDOUT /*!< timeout error occurred. */
} eMBErrorCode;
/* ----------------------- Function prototypes ------------------------------*/
/*! \ingroup modbus
* \brief Initialize the Modbus protocol stack.
*
* This functions initializes the ASCII or RTU module and calls the
* init functions of the porting layer to prepare the hardware. Please
* note that the receiver is still disabled and no Modbus frames are
* processed until eMBEnable( ) has been called.
*
* \param eMode If ASCII or RTU mode should be used.
* \param ucSlaveAddress The slave address. Only frames sent to this
* address or to the broadcast address are processed.
* \param ucPort The port to use. E.g. 1 for COM1 on windows. This value
* is platform dependent and some ports simply choose to ignore it.
* \param ulBaudRate The baudrate. E.g. 19200. Supported baudrates depend
* on the porting layer.
* \param eParity Parity used for serial transmission.
*
* \return If no error occurs the function returns eMBErrorCode::MB_ENOERR.
* The protocol is then in the disabled state and ready for activation
* by calling eMBEnable( ). Otherwise one of the following error codes
* is returned:
* - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid
* slave addresses are in the range 1 - 247.
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress,
UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity );
/*! \ingroup modbus
* \brief Initialize the Modbus protocol stack for Modbus TCP.
*
* This function initializes the Modbus TCP Module. Please note that
* frame processing is still disabled until eMBEnable( ) is called.
*
* \param usTCPPort The TCP port to listen on.
* \param ucSlaveUid The UID field for slave to listen on.
* \return If the protocol stack has been initialized correctly the function
* returns eMBErrorCode::MB_ENOERR. Otherwise one of the following error
* codes is returned:
* - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid
* slave addresses are in the range 1 - 247.
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBTCPInit( UCHAR ucSlaveUid, USHORT usTCPPort );
/*! \ingroup modbus
* \brief Release resources used by the protocol stack.
*
* This function disables the Modbus protocol stack and release all
* hardware resources. It must only be called when the protocol stack
* is disabled.
*
* \note Note all ports implement this function. A port which wants to
* get an callback must define the macro MB_PORT_HAS_CLOSE to 1.
*
* \return If the resources where released it return eMBErrorCode::MB_ENOERR.
* If the protocol stack is not in the disabled state it returns
* eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBClose( void );
/*! \ingroup modbus
* \brief Enable the Modbus protocol stack.
*
* This function enables processing of Modbus frames. Enabling the protocol
* stack is only possible if it is in the disabled state.
*
* \return If the protocol stack is now in the state enabled it returns
* eMBErrorCode::MB_ENOERR. If it was not in the disabled state it
* return eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBEnable( void );
/*! \ingroup modbus
* \brief Disable the Modbus protocol stack.
*
* This function disables processing of Modbus frames.
*
* \return If the protocol stack has been disabled it returns
* eMBErrorCode::MB_ENOERR. If it was not in the enabled state it returns
* eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBDisable( void );
/*! \ingroup modbus
* \brief The main pooling loop of the Modbus protocol stack.
*
* This function must be called periodically. The timer interval required
* is given by the application dependent Modbus slave timeout. Internally the
* function calls xMBPortEventGet() and waits for an event from the receiver or
* transmitter state machines.
*
* \return If the protocol stack is not in the enabled state the function
* returns eMBErrorCode::MB_EILLSTATE. Otherwise it returns
* eMBErrorCode::MB_ENOERR.
*/
eMBErrorCode eMBPoll( void );
/*! \ingroup modbus
* \brief Configure the slave id of the device.
*
* This function should be called when the Modbus function <em>Report Slave ID</em>
* is enabled ( By defining MB_FUNC_OTHER_REP_SLAVEID_ENABLED in mbconfig.h ).
*
* \param ucSlaveID Values is returned in the <em>Slave ID</em> byte of the
* <em>Report Slave ID</em> response.
* \param xIsRunning If TRUE the <em>Run Indicator Status</em> byte is set to 0xFF.
* otherwise the <em>Run Indicator Status</em> is 0x00.
* \param pucAdditional Values which should be returned in the <em>Additional</em>
* bytes of the <em> Report Slave ID</em> response.
* \param usAdditionalLen Length of the buffer <code>pucAdditonal</code>.
*
* \return If the static buffer defined by MB_FUNC_OTHER_REP_SLAVEID_BUF in
* mbconfig.h is to small it returns eMBErrorCode::MB_ENORES. Otherwise
* it returns eMBErrorCode::MB_ENOERR.
*/
eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning,
UCHAR const *pucAdditional,
USHORT usAdditionalLen );
/*! \ingroup modbus
* \brief Registers a callback handler for a given function code.
*
* This function registers a new callback handler for a given function code.
* The callback handler supplied is responsible for interpreting the Modbus PDU and
* the creation of an appropriate response. In case of an error it should return
* one of the possible Modbus exceptions which results in a Modbus exception frame
* sent by the protocol stack.
*
* \param ucFunctionCode The Modbus function code for which this handler should
* be registers. Valid function codes are in the range 1 to 127.
* \param pxHandler The function handler which should be called in case
* such a frame is received. If \c NULL a previously registered function handler
* for this function code is removed.
*
* \return eMBErrorCode::MB_ENOERR if the handler has been installed. If no
* more resources are available it returns eMBErrorCode::MB_ENORES. In this
* case the values in mbconfig.h should be adjusted. If the argument was not
* valid it returns eMBErrorCode::MB_EINVAL.
*/
eMBErrorCode eMBRegisterCB( UCHAR ucFunctionCode,
pxMBFunctionHandler pxHandler );
/* ----------------------- Callback -----------------------------------------*/
/*! \defgroup modbus_registers Modbus Registers
* \code #include "mb.h" \endcode
* The protocol stack does not internally allocate any memory for the
* registers. This makes the protocol stack very small and also usable on
* low end targets. In addition the values don't have to be in the memory
* and could for example be stored in a flash.<br>
* Whenever the protocol stack requires a value it calls one of the callback
* function with the register address and the number of registers to read
* as an argument. The application should then read the actual register values
* (for example the ADC voltage) and should store the result in the supplied
* buffer.<br>
* If the protocol stack wants to update a register value because a write
* register function was received a buffer with the new register values is
* passed to the callback function. The function should then use these values
* to update the application register values.
*/
/*! \ingroup modbus_registers
* \brief Callback function used if the value of a <em>Input Register</em>
* is required by the protocol stack. The starting register address is given
* by \c usAddress and the last register is given by <tt>usAddress +
* usNRegs - 1</tt>.
*
* \param pucRegBuffer A buffer where the callback function should write
* the current value of the modbus registers to.
* \param usAddress The starting address of the register. Input registers
* are in the range 1 - 65535.
* \param usNRegs Number of registers the callback function must supply.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application can not supply values
* for registers within this range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> exception frame is sent as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Holding Register</em> value is
* read or written by the protocol stack. The starting register address
* is given by \c usAddress and the last register is given by
* <tt>usAddress + usNRegs - 1</tt>.
*
* \param pucRegBuffer If the application registers values should be updated the
* buffer points to the new registers values. If the protocol stack needs
* to now the current values the callback function should write them into
* this buffer.
* \param usAddress The starting address of the register.
* \param usNRegs Number of registers to read or write.
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application register
* values should be updated from the values in the buffer. For example
* this would be the case when the Modbus master has issued an
* <b>WRITE SINGLE REGISTER</b> command.
* If the value eMBRegisterMode::MB_REG_READ the application should copy
* the current values into the buffer \c pucRegBuffer.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application can not supply values
* for registers within this range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> exception frame is sent as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Coil Register</em> value is
* read or written by the protocol stack. If you are going to use
* this function you might use the functions xMBUtilSetBits( ) and
* xMBUtilGetBits( ) for working with bitfields.
*
* \param pucRegBuffer The bits are packed in bytes where the first coil
* starting at address \c usAddress is stored in the LSB of the
* first byte in the buffer <code>pucRegBuffer</code>.
* If the buffer should be written by the callback function unused
* coil values (I.e. if not a multiple of eight coils is used) should be set
* to zero.
* \param usAddress The first coil number.
* \param usNCoils Number of coil values requested.
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application values should
* be updated from the values supplied in the buffer \c pucRegBuffer.
* If eMBRegisterMode::MB_REG_READ the application should store the current
* values in the buffer \c pucRegBuffer.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
* within the requested address range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Input Discrete Register</em> value is
* read by the protocol stack.
*
* If you are going to use his function you might use the functions
* xMBUtilSetBits( ) and xMBUtilGetBits( ) for working with bitfields.
*
* \param pucRegBuffer The buffer should be updated with the current
* coil values. The first discrete input starting at \c usAddress must be
* stored at the LSB of the first byte in the buffer. If the requested number
* is not a multiple of eight the remaining bits should be set to zero.
* \param usAddress The starting address of the first discrete input.
* \param usNDiscrete Number of discrete input values.
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If no such discrete inputs exists.
* In this case a <b>ILLEGAL DATA ADDRESS</b> exception frame is sent
* as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete );
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,438 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mb_m.h,v 1.60 2013/09/03 10:20:05 Armink Add Master Functions $
*/
#ifndef _MB_M_H
#define _MB_M_H
#include "mbconfig.h"
#include "port.h"
#include "mb.h"
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
#include "mbport.h"
#include "mbproto.h"
/*! \defgroup modbus Modbus
* \code #include "mb.h" \endcode
*
* This module defines the interface for the application. It contains
* the basic functions and types required to use the Modbus Master protocol stack.
* A typical application will want to call eMBMasterInit() first. If the device
* is ready to answer network requests it must then call eMBEnable() to activate
* the protocol stack. In the main loop the function eMBMasterPoll() must be called
* periodically. The time interval between pooling depends on the configured
* Modbus timeout. If an RTOS is available a separate task should be created
* and the task should always call the function eMBMasterPoll().
*
* \code
* // Initialize protocol stack in RTU mode for a Master
* eMBMasterInit( MB_RTU, 38400, MB_PAR_EVEN );
* // Enable the Modbus Protocol Stack.
* eMBMasterEnable( );
* for( ;; )
* {
* // Call the main polling loop of the Modbus Master protocol stack.
* eMBMasterPoll( );
* ...
* }
* \endcode
*/
/* ----------------------- Defines ------------------------------------------*/
/*! \ingroup modbus
* \brief Use the default Modbus Master TCP port (502)
*/
#define MB_MASTER_TCP_PORT_USE_DEFAULT 0
/*! \ingroup modbus
* \brief Errorcodes used by all function in the Master request.
*/
typedef enum
{
MB_MRE_NO_ERR, /*!< no error. */
MB_MRE_NO_REG, /*!< illegal register address. */
MB_MRE_ILL_ARG, /*!< illegal argument. */
MB_MRE_REV_DATA, /*!< receive data error. */
MB_MRE_TIMEDOUT, /*!< timeout error occurred. */
MB_MRE_MASTER_BUSY, /*!< master is busy now. */
MB_MRE_EXE_FUN /*!< execute function error. */
} eMBMasterReqErrCode;
/*! \ingroup modbus
* \brief Transaction information structure.
*/
typedef struct
{
uint64_t xTransId;
UCHAR ucDestAddr;
UCHAR ucFuncCode;
eMBException eException;
UCHAR ucFrameError;
} TransactionInfo_t;
/*! \ingroup modbus
* \brief TimerMode is Master 3 kind of Timer modes.
*/
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.*/
} 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.
*
* This functions initializes the ASCII or RTU module and calls the
* init functions of the porting layer to prepare the hardware. Please
* note that the receiver is still disabled and no Modbus frames are
* processed until eMBMasterEnable( ) has been called.
*
* \param eMode If ASCII or RTU mode should be used.
* \param ucPort The port to use. E.g. 1 for COM1 on windows. This value
* is platform dependent and some ports simply choose to ignore it.
* \param ulBaudRate The baudrate. E.g. 19200. Supported baudrates depend
* on the porting layer.
* \param eParity Parity used for serial transmission.
*
* \return If no error occurs the function returns eMBErrorCode::MB_ENOERR.
* The protocol is then in the disabled state and ready for activation
* by calling eMBMasterEnable( ). Otherwise one of the following error codes
* is returned:
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort,
ULONG ulBaudRate, eMBParity eParity );
/*! \ingroup modbus
* \brief Initialize the Modbus Master protocol stack for Modbus TCP.
*
* This function initializes the Modbus TCP Module. Please note that
* frame processing is still disabled until eMBEnable( ) is called.
*
* \param usTCPPort The TCP port to listen on.
* \return If the protocol stack has been initialized correctly the function
* returns eMBErrorCode::MB_ENOERR. Otherwise one of the following error
* codes is returned:
* - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid
* slave addresses are in the range 1 - 247.
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBMasterTCPInit( USHORT usTCPPort );
/*! \ingroup modbus
* \brief Release resources used by the protocol stack.
*
* This function disables the Modbus Master protocol stack and release all
* hardware resources. It must only be called when the protocol stack
* is disabled.
*
* \note Note all ports implement this function. A port which wants to
* get an callback must define the macro MB_PORT_HAS_CLOSE to 1.
*
* \return If the resources where released it return eMBErrorCode::MB_ENOERR.
* If the protocol stack is not in the disabled state it returns
* eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBMasterClose( void );
/*! \ingroup modbus
* \brief Enable the Modbus Master protocol stack.
*
* This function enables processing of Modbus Master frames. Enabling the protocol
* stack is only possible if it is in the disabled state.
*
* \return If the protocol stack is now in the state enabled it returns
* eMBErrorCode::MB_ENOERR. If it was not in the disabled state it
* return eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBMasterEnable( void );
/*! \ingroup modbus
* \brief Disable the Modbus Master protocol stack.
*
* This function disables processing of Modbus frames.
*
* \return If the protocol stack has been disabled it returns
* eMBErrorCode::MB_ENOERR. If it was not in the enabled state it returns
* eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBMasterDisable( void );
/*! \ingroup modbus
* \brief The main pooling loop of the Modbus Master protocol stack.
*
* This function must be called periodically. The timer interval required
* is given by the application dependent Modbus slave timeout. Internally the
* function calls xMBMasterPortEventGet() and waits for an event from the receiver or
* transmitter state machines.
*
* \return If the protocol stack is not in the enabled state the function
* returns eMBErrorCode::MB_EILLSTATE. Otherwise it returns
* eMBErrorCode::MB_ENOERR.
*/
eMBErrorCode eMBMasterPoll( void );
/*! \ingroup modbus
* \brief Registers a callback handler for a given function code.
*
* This function registers a new callback handler for a given function code.
* The callback handler supplied is responsible for interpreting the Modbus PDU and
* the creation of an appropriate response. In case of an error it should return
* one of the possible Modbus exceptions which results in a Modbus exception frame
* sent by the protocol stack.
*
* \param ucFunctionCode The Modbus function code for which this handler should
* be registers. Valid function codes are in the range 1 to 127.
* \param pxHandler The function handler which should be called in case
* such a frame is received. If \c NULL a previously registered function handler
* for this function code is removed.
*
* \return eMBErrorCode::MB_ENOERR if the handler has been installed. If no
* more resources are available it returns eMBErrorCode::MB_ENORES. In this
* case the values in mbconfig.h should be adjusted. If the argument was not
* valid it returns eMBErrorCode::MB_EINVAL.
*/
eMBErrorCode eMBMasterRegisterCB( UCHAR ucFunctionCode,
pxMBFunctionHandler pxHandler );
/* ----------------------- Callback -----------------------------------------*/
/*! \defgroup modbus_master registers Modbus Registers
* \code #include "mb_m.h" \endcode
* The protocol stack does not internally allocate any memory for the
* registers. This makes the protocol stack very small and also usable on
* low end targets. In addition the values don't have to be in the memory
* and could for example be stored in a flash.<br>
* Whenever the protocol stack requires a value it calls one of the callback
* function with the register address and the number of registers to read
* as an argument. The application should then read the actual register values
* (for example the ADC voltage) and should store the result in the supplied
* buffer.<br>
* If the protocol stack wants to update a register value because a write
* register function was received a buffer with the new register values is
* passed to the callback function. The function should then use these values
* to update the application register values.
*/
/*! \ingroup modbus_registers
* \brief Callback function used if the value of a <em>Input Register</em>
* is required by the protocol stack. The starting register address is given
* by \c usAddress and the last register is given by <tt>usAddress +
* usNRegs - 1</tt>.
*
* \param pucRegBuffer A buffer where the callback function should write
* the current value of the modbus registers to.
* \param usAddress The starting address of the register. Input registers
* are in the range 1 - 65535.
* \param usNRegs Number of registers the callback function must supply.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
* within the requested address range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Holding Register</em> value is
* read or written by the protocol stack. The starting register address
* is given by \c usAddress and the last register is given by
* <tt>usAddress + usNRegs - 1</tt>.
*
* \param pucRegBuffer If the application registers values should be updated the
* buffer points to the new registers values. If the protocol stack needs
* to now the current values the callback function should write them into
* this buffer.
* \param usAddress The starting address of the register.
* \param usNRegs Number of registers to read or write.
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application register
* values should be updated from the values in the buffer. For example
* this would be the case when the Modbus master has issued an
* <b>WRITE SINGLE REGISTER</b> command.
* If the value eMBRegisterMode::MB_REG_READ the application should copy
* the current values into the buffer \c pucRegBuffer.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
* within the requested address range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Coil Register</em> value is
* read or written by the protocol stack. If you are going to use
* this function you might use the functions xMBUtilSetBits( ) and
* xMBUtilGetBits( ) for working with bitfields.
*
* \param pucRegBuffer The bits are packed in bytes where the first coil
* starting at address \c usAddress is stored in the LSB of the
* first byte in the buffer <code>pucRegBuffer</code>.
* If the buffer should be written by the callback function unused
* coil values (I.e. if not a multiple of eight coils is used) should be set
* to zero.
* \param usAddress The first coil number.
* \param usNCoils Number of coil values requested.
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application values should
* be updated from the values supplied in the buffer \c pucRegBuffer.
* If eMBRegisterMode::MB_REG_READ the application should store the current
* values in the buffer \c pucRegBuffer.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
* within the requested address range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Input Discrete Register</em> value is
* read by the protocol stack.
*
* If you are going to use his function you might use the functions
* xMBUtilSetBits( ) and xMBUtilGetBits( ) for working with bitfields.
*
* \param pucRegBuffer The buffer should be updated with the current
* coil values. The first discrete input starting at \c usAddress must be
* stored at the LSB of the first byte in the buffer. If the requested number
* is not a multiple of eight the remaining bits should be set to zero.
* \param usAddress The starting address of the first discrete input.
* \param usNDiscrete Number of discrete input values.
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
* within the requested address range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete );
/*! \ingroup modbus
*\brief These Modbus functions are called for user when Modbus run in Master Mode.
*/
eMBMasterReqErrCode
eMBMasterReqReadInputRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr,
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 );
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 );
eMBMasterReqErrCode
eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut );
eMBException
eMBMasterFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen );
eMBException
eMBMasterFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
/* \ingroup modbus
* \brief These functions are interface for Modbus Master
*/
void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame );
UCHAR ucMBMasterGetDestAddress( void );
void vMBMasterSetDestAddress( UCHAR Address );
BOOL xMBMasterGetCBRunInMasterMode( void );
void vMBMasterSetCBRunInMasterMode( BOOL IsMasterMode );
USHORT usMBMasterGetPDUSndLength( void );
void vMBMasterSetPDUSndLength( USHORT SendPDULength );
void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode );
void vMBMasterRequestSetType( BOOL xIsBroadcast );
eMBMasterTimerMode xMBMasterGetCurTimerMode( void );
BOOL xMBMasterRequestIsBroadcast( void );
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 -----------------------------------------*/
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,200 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbconfig.h,v 1.15 2010/06/06 13:54:40 wolti Exp $
* $Id: mbconfig.h,v 1.60 2013/08/13 21:19:55 Armink Add Master Functions $
*/
#ifndef _MB_CONFIG_H
#define _MB_CONFIG_H
#include <inttypes.h> // needs to be included for default system types (such as PRIxx)
#include "sdkconfig.h" // for KConfig options
#if __has_include("esp_idf_version.h")
#include "esp_idf_version.h"
#endif
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Defines ------------------------------------------*/
/*! \defgroup modbus_cfg Modbus Configuration
*
* Most modules in the protocol stack are completly optional and can be
* excluded. This is specially important if target resources are very small
* and program memory space should be saved.<br>
*
* All of these settings are available in the file <code>mbconfig.h</code>
*/
/*! \addtogroup modbus_cfg
* @{
*/
/*! \brief If Modbus Master ASCII support is enabled. */
#define MB_MASTER_ASCII_ENABLED ( CONFIG_FMB_COMM_MODE_ASCII_EN )
/*! \brief If Modbus Master RTU support is enabled. */
#define MB_MASTER_RTU_ENABLED ( CONFIG_FMB_COMM_MODE_RTU_EN )
/*! \brief If Modbus Master TCP support is enabled. */
#define MB_MASTER_TCP_ENABLED ( CONFIG_FMB_COMM_MODE_TCP_EN )
/*! \brief If Modbus Slave ASCII support is enabled. */
#define MB_SLAVE_ASCII_ENABLED ( CONFIG_FMB_COMM_MODE_ASCII_EN )
/*! \brief If Modbus Slave RTU support is enabled. */
#define MB_SLAVE_RTU_ENABLED ( CONFIG_FMB_COMM_MODE_RTU_EN )
/*! \brief If Modbus Slave TCP support is enabled. */
#define MB_TCP_ENABLED ( CONFIG_FMB_COMM_MODE_TCP_EN )
#if !CONFIG_FMB_COMM_MODE_ASCII_EN && !CONFIG_FMB_COMM_MODE_RTU_EN && !MB_MASTER_TCP_ENABLED && !MB_TCP_ENABLED
#error "None of Modbus communication mode is enabled. Please enable one of (ASCII, RTU, TCP) mode in Kconfig."
#endif
#ifdef ESP_IDF_VERSION
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0))
// Features supported from 4.4
#define MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD 1
#endif
#endif
/*! \brief The option is required for support of RTU over TCP.
*/
#define MB_TCP_UID_ENABLED ( CONFIG_FMB_TCP_UID_ENABLED )
/*! \brief This option defines the number of data bits per ASCII character.
*
* A parity bit is added before the stop bit which keeps the actual byte size at 10 bits.
*/
#ifdef CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB
#define MB_ASCII_BITS_PER_SYMB ( CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB )
#endif
#define MB_EVENT_QUEUE_SIZE ( CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE )
#define MB_EVENT_QUEUE_TIMEOUT ( pdMS_TO_TICKS( CONFIG_FMB_EVENT_QUEUE_TIMEOUT ) )
/*! \brief The character timeout value for Modbus ASCII.
*
* The character timeout value is not fixed for Modbus ASCII and is therefore
* a configuration option. It should be set to the maximum expected delay
* time of the network.
*/
#ifdef CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS
#define MB_ASCII_TIMEOUT_MS ( CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS )
#endif
/*! \brief Timeout to wait in ASCII prior to enabling transmitter.
*
* If defined the function calls vMBPortSerialDelay with the argument
* MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS to allow for a delay before
* the serial transmitter is enabled. This is required because some
* targets are so fast that there is no time between receiving and
* transmitting the frame. If the master is to slow with enabling its
* receiver then he will not receive the response correctly.
*/
#ifndef CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS
#define MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ( 0 )
#else
#define MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ( CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
#endif
/*! \brief Maximum number of Modbus functions codes the protocol stack
* should support.
*
* The maximum number of supported Modbus functions must be greater than
* the sum of all enabled functions in this file and custom function
* handlers. If set to small adding more functions will fail.
*/
#define MB_FUNC_HANDLERS_MAX ( 16 )
/*! \brief Number of bytes which should be allocated for the <em>Report Slave ID
* </em>command.
*
* This number limits the maximum size of the additional segment in the
* report slave id function. See eMBSetSlaveID( ) for more information on
* how to set this value. It is only used if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
* is set to <code>1</code>.
*/
#define MB_FUNC_OTHER_REP_SLAVEID_BUF ( 32 )
/*! \brief If the <em>Report Slave ID</em> function should be enabled. */
#define MB_FUNC_OTHER_REP_SLAVEID_ENABLED ( CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT )
/*! \brief If the <em>Read Input Registers</em> function should be enabled. */
#define MB_FUNC_READ_INPUT_ENABLED ( 1 )
/*! \brief If the <em>Read Holding Registers</em> function should be enabled. */
#define MB_FUNC_READ_HOLDING_ENABLED ( 1 )
/*! \brief If the <em>Write Single Register</em> function should be enabled. */
#define MB_FUNC_WRITE_HOLDING_ENABLED ( 1 )
/*! \brief If the <em>Write Multiple registers</em> function should be enabled. */
#define MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED ( 1 )
/*! \brief If the <em>Read Coils</em> function should be enabled. */
#define MB_FUNC_READ_COILS_ENABLED ( 1 )
/*! \brief If the <em>Write Coils</em> function should be enabled. */
#define MB_FUNC_WRITE_COIL_ENABLED ( 1 )
/*! \brief If the <em>Write Multiple Coils</em> function should be enabled. */
#define MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED ( 1 )
/*! \brief If the <em>Read Discrete Inputs</em> function should be enabled. */
#define MB_FUNC_READ_DISCRETE_INPUTS_ENABLED ( 1 )
/*! \brief If the <em>Read/Write Multiple Registers</em> function should be enabled. */
#define MB_FUNC_READWRITE_HOLDING_ENABLED ( 1 )
/*! \brief Check the option to place timer handler into IRAM */
#define MB_PORT_TIMER_ISR_IN_IRAM ( CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD )
/*! @} */
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
/*! \brief If master send a broadcast frame, the master will wait time of convert to delay,
* then master can send other frame */
#define MB_MASTER_DELAY_MS_CONVERT ( CONFIG_FMB_MASTER_DELAY_MS_CONVERT )
/*! \brief If master send a frame which is not broadcast,the master will wait sometime for slave.
* And if slave is not respond in this time,the master will process this timeout error.
* Then master can send other frame */
#define MB_MASTER_TIMEOUT_MS_RESPOND ( CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND )
/*! \brief The total slaves in Modbus Master system.
* \note : The slave ID must be continuous from 1.*/
#define MB_MASTER_TOTAL_SLAVE_NUM ( 247 )
#endif
#endif

View File

@@ -0,0 +1,116 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbframe.h,v 1.9 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_FRAME_H
#define _MB_FRAME_H
#include "port.h"
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/*!
* Constants which defines the format of a modbus frame. The example is
* shown for a Modbus RTU/ASCII frame. Note that the Modbus PDU is not
* dependent on the underlying transport.
*
* <code>
* <------------------------ MODBUS SERIAL LINE PDU (1) ------------------->
* <----------- MODBUS PDU (1') ---------------->
* +-----------+---------------+----------------------------+-------------+
* | Address | Function Code | Data | CRC/LRC |
* +-----------+---------------+----------------------------+-------------+
* | | | |
* (2) (3/2') (3') (4)
*
* (1) ... MB_SER_PDU_SIZE_MAX = 256
* (2) ... MB_SER_PDU_ADDR_OFF = 0
* (3) ... MB_SER_PDU_PDU_OFF = 1
* (4) ... MB_SER_PDU_SIZE_CRC = 2
*
* (1') ... MB_PDU_SIZE_MAX = 253
* (2') ... MB_PDU_FUNC_OFF = 0
* (3') ... MB_PDU_DATA_OFF = 1
* </code>
*/
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_SIZE_MAX 253 /*!< Maximum size of a PDU. */
#define MB_PDU_SIZE_MIN 1 /*!< Function Code */
#define MB_PDU_FUNC_OFF 0 /*!< Offset of function code in PDU. */
#define MB_PDU_DATA_OFF 1 /*!< Offset for response data in PDU. */
#define MB_SER_PDU_SIZE_MAX MB_SERIAL_BUF_SIZE /*!< Maximum size of a Modbus frame. */
#define MB_SER_PDU_SIZE_LRC 1 /*!< Size of LRC field in PDU. */
#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */
#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */
#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */
#define MB_TCP_TID 0
#define MB_TCP_PID 2
#define MB_TCP_LEN 4
#define MB_TCP_UID 6
#define MB_TCP_FUNC 7
#if MB_MASTER_TCP_ENABLED
#define MB_SEND_BUF_PDU_OFF MB_TCP_FUNC
#else
#define MB_SEND_BUF_PDU_OFF MB_SER_PDU_PDU_OFF
#endif
#define MB_TCP_PSEUDO_ADDRESS 255
/* ----------------------- Prototypes 0-------------------------------------*/
typedef void ( *pvMBFrameStart ) ( void );
typedef void ( *pvMBFrameStop ) ( void );
typedef eMBErrorCode( *peMBFrameReceive ) ( UCHAR * pucRcvAddress,
UCHAR ** pucFrame,
USHORT * pusLength );
typedef eMBErrorCode( *peMBFrameSend ) ( UCHAR slaveAddress,
const UCHAR * pucFrame,
USHORT usLength );
typedef void( *pvMBFrameClose ) ( void );
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfunc.h,v 1.12 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_FUNC_H
#define _MB_FUNC_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
#if MB_FUNC_OTHER_REP_SLAVEID_BUF > 0
eMBException eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_READ_INPUT_ENABLED > 0
eMBException eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
eMBException eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
eMBException eMBFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
eMBException eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_READ_COILS_ENABLED > 0
eMBException eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
eMBException eMBFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
eMBException eMBFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
eMBException eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen );
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
eMBException eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
#endif
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,284 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbport.h,v 1.19 2010/06/06 13:54:40 wolti Exp $
*/
#ifndef _MB_PORT_H
#define _MB_PORT_H
#include "mbconfig.h" // for options
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
#if CONFIG_UART_ISR_IN_IRAM
#define MB_PORT_SERIAL_ISR_FLAG ESP_INTR_FLAG_IRAM
#else
#define MB_PORT_SERIAL_ISR_FLAG ESP_INTR_FLAG_LOWMED
#endif
#if CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD && MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
#define MB_PORT_ISR_ATTR IRAM_ATTR
#else
#define MB_PORT_ISR_ATTR
#endif
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
EV_READY = 0x01, /*!< Startup finished. */
EV_FRAME_RECEIVED = 0x02, /*!< Frame received. */
EV_EXECUTE = 0x04, /*!< Execute function. */
EV_FRAME_SENT = 0x08, /*!< Frame sent. */
EV_FRAME_TRANSMIT = 0x10 /*!< Frame transmit. */
} eMBEventType;
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
typedef enum {
EV_MASTER_NO_EVENT = 0x0000,
EV_MASTER_TRANS_START = 0x0001, /*!< Transaction start flag */
EV_MASTER_READY = 0x0002, /*!< Startup finished. */
EV_MASTER_FRAME_RECEIVED = 0x0004, /*!< Frame received. */
EV_MASTER_EXECUTE = 0x0008, /*!< Execute function. */
EV_MASTER_FRAME_SENT = 0x0010, /*!< Frame sent. */
EV_MASTER_FRAME_TRANSMIT = 0x0020, /*!< Frame transmission. */
EV_MASTER_ERROR_PROCESS = 0x0040, /*!< Frame error process. */
EV_MASTER_PROCESS_SUCCESS = 0x0080, /*!< Request process success. */
EV_MASTER_ERROR_RESPOND_TIMEOUT = 0x0100, /*!< Request respond timeout. */
EV_MASTER_ERROR_RECEIVE_DATA = 0x0200, /*!< Request receive data error. */
EV_MASTER_ERROR_EXECUTE_FUNCTION = 0x0400 /*!< Request execute function error. */
} eMBMasterEventEnum;
typedef enum {
EV_ERROR_INIT, /*!< No error, initial state. */
EV_ERROR_RESPOND_TIMEOUT, /*!< Slave respond timeout. */
EV_ERROR_RECEIVE_DATA, /*!< Receive frame data error. */
EV_ERROR_EXECUTE_FUNCTION, /*!< Execute function error. */
EV_ERROR_OK /*!< No error, processing completed. */
} 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 */
} xMBMasterEventType;
#endif
/*! \ingroup modbus
* \brief Parity used for characters in serial mode.
*
* The parity which should be applied to the characters sent over the serial
* link. Please note that this values are actually passed to the porting
* layer and therefore not all parity modes might be available.
*/
typedef enum
{
MB_PAR_NONE, /*!< No parity. */
MB_PAR_ODD, /*!< Odd parity. */
MB_PAR_EVEN /*!< Even parity. */
} eMBParity;
/* ----------------------- Supporting functions -----------------------------*/
BOOL xMBPortEventInit( void );
BOOL xMBPortEventPost( eMBEventType eEvent );
BOOL xMBPortEventGet( /*@out@ */ eMBEventType * eEvent );
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
BOOL xMBMasterPortEventInit( void );
BOOL xMBMasterPortEventPost( eMBMasterEventEnum eEvent );
BOOL xMBMasterPortEventGet( /*@out@ */ xMBMasterEventType * eEvent );
eMBMasterEventEnum
xMBMasterPortFsmWaitConfirmation( eMBMasterEventEnum eEventMask, ULONG ulTimeout);
void vMBMasterOsResInit( void );
BOOL xMBMasterRunResTake( LONG time );
void vMBMasterRunResRelease( void );
uint64_t xMBMasterPortGetTransactionId( void );
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
/* ----------------------- Serial port functions ----------------------------*/
BOOL xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
UCHAR ucDataBits, eMBParity eParity );
void vMBPortClose( void );
void xMBPortSerialClose( void );
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );
BOOL xMBPortSerialGetByte( CHAR * pucByte );
BOOL xMBPortSerialPutByte( CHAR ucByte );
BOOL xMBPortSerialGetRequest( UCHAR **ppucMBSerialFrame, USHORT * pusSerialLength ) __attribute__ ((weak));
BOOL xMBPortSerialSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength ) __attribute__ ((weak));
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
BOOL xMBMasterPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
UCHAR ucDataBits, eMBParity eParity );
void vMBMasterPortClose( void );
void xMBMasterPortSerialClose( void );
void vMBMasterPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );
BOOL xMBMasterPortSerialGetByte( CHAR * pucByte );
BOOL xMBMasterPortSerialPutByte( CHAR ucByte );
BOOL xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength );
BOOL xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength );
void vMBMasterRxFlush( void );
#endif
/* ----------------------- Timers functions ---------------------------------*/
BOOL xMBPortTimersInit( USHORT usTimeOut50us );
void xMBPortTimersClose( void );
void vMBPortTimersEnable( void );
void vMBPortTimersDisable( void );
void vMBPortTimersDelay( USHORT usTimeOutMS );
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
BOOL xMBMasterPortTimersInit( USHORT usTimeOut50us );
void xMBMasterPortTimersClose( void );
void vMBMasterPortTimersT35Enable( void );
void vMBMasterPortTimersConvertDelayEnable( void );
void vMBMasterPortTimersRespondTimeoutEnable( void );
void vMBMasterPortTimersDisable( void );
/* ----------------- Callback for the master error process ------------------*/
void vMBMasterErrorCBRespondTimeout( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucSendData, USHORT ucSendLength );
void vMBMasterErrorCBReceiveData( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );
void vMBMasterErrorCBExecuteFunction( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );
void vMBMasterCBRequestSuccess( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );
#endif
/* ----------------------- Callback for the protocol stack ------------------*/
/*!
* \brief Callback function for the porting layer when a new byte is
* available.
*
* Depending upon the mode this callback function is used by the RTU or
* ASCII transmission layers. In any case a call to xMBPortSerialGetByte()
* must immediately return a new character.
*
* \return <code>TRUE</code> if a event was posted to the queue because
* a new byte was received. The port implementation should wake up the
* tasks which are currently blocked on the eventqueue.
*/
extern BOOL( *pxMBFrameCBByteReceived ) ( void );
extern BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
extern BOOL( *pxMBPortCBTimerExpired ) ( void );
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
extern BOOL( *pxMBMasterFrameCBByteReceived ) ( void );
extern BOOL( *pxMBMasterFrameCBTransmitterEmpty ) ( void );
extern BOOL( *pxMBMasterPortCBTimerExpired ) ( void );
#endif
/* ----------------------- TCP port functions -------------------------------*/
#if MB_TCP_ENABLED
BOOL xMBTCPPortInit( USHORT usTCPPort );
void vMBTCPPortClose( void );
void vMBTCPPortEnable( void );
void vMBTCPPortDisable( void );
BOOL xMBTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength );
BOOL xMBTCPPortSendResponse( UCHAR *pucMBTCPFrame, USHORT usTCPLength );
#endif
#if MB_MASTER_TCP_ENABLED
BOOL xMBMasterTCPPortInit( USHORT usTCPPort );
void vMBMasterTCPPortClose( void );
void vMBMasterTCPPortEnable( void );
void vMBMasterTCPPortDisable( void );
BOOL xMBMasterTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength );
BOOL xMBMasterTCPPortSendResponse( UCHAR *pucMBTCPFrame, USHORT usTCPLength );
#endif
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbproto.h,v 1.14 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_PROTO_H
#define _MB_PROTO_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Defines ------------------------------------------*/
#define MB_ADDRESS_BROADCAST ( 0 ) /*! Modbus broadcast address. */
#define MB_ADDRESS_MIN ( 1 ) /*! Smallest possible slave address. */
#define MB_ADDRESS_MAX ( 247 ) /*! Biggest possible slave address. */
#define MB_FUNC_NONE ( 0 )
#define MB_FUNC_READ_COILS ( 1 )
#define MB_FUNC_READ_DISCRETE_INPUTS ( 2 )
#define MB_FUNC_WRITE_SINGLE_COIL ( 5 )
#define MB_FUNC_WRITE_MULTIPLE_COILS ( 15 )
#define MB_FUNC_READ_HOLDING_REGISTER ( 3 )
#define MB_FUNC_READ_INPUT_REGISTER ( 4 )
#define MB_FUNC_WRITE_REGISTER ( 6 )
#define MB_FUNC_WRITE_MULTIPLE_REGISTERS ( 16 )
#define MB_FUNC_READWRITE_MULTIPLE_REGISTERS ( 23 )
#define MB_FUNC_DIAG_READ_EXCEPTION ( 7 )
#define MB_FUNC_DIAG_DIAGNOSTIC ( 8 )
#define MB_FUNC_DIAG_GET_COM_EVENT_CNT ( 11 )
#define MB_FUNC_DIAG_GET_COM_EVENT_LOG ( 12 )
#define MB_FUNC_OTHER_REPORT_SLAVEID ( 17 )
#define MB_FUNC_ERROR ( 128u )
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
MB_EX_NONE = 0x00,
MB_EX_ILLEGAL_FUNCTION = 0x01,
MB_EX_ILLEGAL_DATA_ADDRESS = 0x02,
MB_EX_ILLEGAL_DATA_VALUE = 0x03,
MB_EX_SLAVE_DEVICE_FAILURE = 0x04,
MB_EX_ACKNOWLEDGE = 0x05,
MB_EX_SLAVE_BUSY = 0x06,
MB_EX_MEMORY_PARITY_ERROR = 0x08,
MB_EX_GATEWAY_PATH_FAILED = 0x0A,
MB_EX_GATEWAY_TGT_FAILED = 0x0B
} eMBException;
typedef eMBException( *pxMBFunctionHandler ) ( UCHAR * pucFrame, USHORT * pusLength );
typedef struct
{
UCHAR ucFunctionCode;
pxMBFunctionHandler pxHandler;
} xMBFunctionHandler;
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,115 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbutils.h,v 1.5 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_UTILS_H
#define _MB_UTILS_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/*! \defgroup modbus_utils Utilities
*
* This module contains some utility functions which can be used by
* the application. It includes some special functions for working with
* bitfields backed by a character array buffer.
*
*/
/*! \addtogroup modbus_utils
* @{
*/
/*! \brief Function to set bits in a byte buffer.
*
* This function allows the efficient use of an array to implement bitfields.
* The array used for storing the bits must always be a multiple of two
* bytes. Up to eight bits can be set or cleared in one operation.
*
* \param ucByteBuf A buffer where the bit values are stored. Must be a
* multiple of 2 bytes. No length checking is performed and if
* usBitOffset / 8 is greater than the size of the buffer memory contents
* is overwritten.
* \param usBitOffset The starting address of the bits to set. The first
* bit has the offset 0.
* \param ucNBits Number of bits to modify. The value must always be smaller
* than 8.
* \param ucValues Thew new values for the bits. The value for the first bit
* starting at <code>usBitOffset</code> is the LSB of the value
* <code>ucValues</code>
*
* \code
* ucBits[2] = {0, 0};
*
* // Set bit 4 to 1 (read: set 1 bit starting at bit offset 4 to value 1)
* xMBUtilSetBits( ucBits, 4, 1, 1 );
*
* // Set bit 7 to 1 and bit 8 to 0.
* xMBUtilSetBits( ucBits, 7, 2, 0x01 );
*
* // Set bits 8 - 11 to 0x05 and bits 12 - 15 to 0x0A;
* xMBUtilSetBits( ucBits, 8, 8, 0x5A);
* \endcode
*/
void xMBUtilSetBits( UCHAR * ucByteBuf, USHORT usBitOffset,
UCHAR ucNBits, UCHAR ucValues );
/*! \brief Function to read bits in a byte buffer.
*
* This function is used to extract up bit values from an array. Up to eight
* bit values can be extracted in one step.
*
* \param ucByteBuf A buffer where the bit values are stored.
* \param usBitOffset The starting address of the bits to set. The first
* bit has the offset 0.
* \param ucNBits Number of bits to modify. The value must always be smaller
* than 8.
*
* \code
* UCHAR ucBits[2] = {0, 0};
* UCHAR ucResult;
*
* // Extract the bits 3 - 10.
* ucResult = xMBUtilGetBits( ucBits, 3, 8 );
* \endcode
*/
UCHAR xMBUtilGetBits( UCHAR * ucByteBuf, USHORT usBitOffset,
UCHAR ucNBits );
/*! @} */
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,441 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mb.c,v 1.28 2010/06/06 13:54:40 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbfunc.h"
#include "mbport.h"
#if MB_SLAVE_RTU_ENABLED
#include "mbrtu.h"
#endif
#if MB_SLAVE_ASCII_ENABLED
#include "mbascii.h"
#endif
#if MB_TCP_ENABLED
#include "mbtcp.h"
#endif
#ifndef MB_PORT_HAS_CLOSE
#define MB_PORT_HAS_CLOSE 1
#endif
/* ----------------------- Static variables ---------------------------------*/
static UCHAR ucMBAddress;
static eMBMode eMBCurrentMode;
volatile UCHAR ucMbSlaveBuf[MB_SERIAL_BUF_SIZE];
static enum
{
STATE_ENABLED,
STATE_DISABLED,
STATE_NOT_INITIALIZED
} eMBState = STATE_NOT_INITIALIZED;
/* Functions pointer which are initialized in eMBInit( ). Depending on the
* mode (RTU or ASCII) the are set to the correct implementations.
*/
static peMBFrameSend peMBFrameSendCur;
static pvMBFrameStart pvMBFrameStartCur;
static pvMBFrameStop pvMBFrameStopCur;
static peMBFrameReceive peMBFrameReceiveCur;
static pvMBFrameClose pvMBFrameCloseCur;
/* Callback functions required by the porting layer. They are called when
* an external event has happend which includes a timeout or the reception
* or transmission of a character.
*/
BOOL( *pxMBFrameCBByteReceived ) ( void );
BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
BOOL( *pxMBPortCBTimerExpired ) ( void );
BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );
BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );
/* An array of Modbus functions handlers which associates Modbus function
* codes with implementing functions.
*/
static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
{MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
#endif
#if MB_FUNC_READ_INPUT_ENABLED > 0
{MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister},
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
{MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister},
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
{MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister},
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_READ_COILS_ENABLED > 0
{MB_FUNC_READ_COILS, eMBFuncReadCoils},
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
{MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil},
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
{MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils},
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
{MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs},
#endif
};
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
/* check preconditions */
if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) )
{
eStatus = MB_EINVAL;
}
else
{
ucMBAddress = ucSlaveAddress;
switch ( eMode )
{
#if MB_SLAVE_RTU_ENABLED > 0
case MB_RTU:
pvMBFrameStartCur = eMBRTUStart;
pvMBFrameStopCur = eMBRTUStop;
peMBFrameSendCur = eMBRTUSend;
peMBFrameReceiveCur = eMBRTUReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;
#endif
#if MB_SLAVE_ASCII_ENABLED > 0
case MB_ASCII:
pvMBFrameStartCur = eMBASCIIStart;
pvMBFrameStopCur = eMBASCIIStop;
peMBFrameSendCur = eMBASCIISend;
peMBFrameReceiveCur = eMBASCIIReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;
#endif
default:
eStatus = MB_EINVAL;
}
if( eStatus == MB_ENOERR )
{
if( !xMBPortEventInit( ) )
{
/* port dependent event module initalization failed. */
eStatus = MB_EPORTERR;
}
else
{
eMBCurrentMode = eMode;
eMBState = STATE_DISABLED;
}
}
}
return eStatus;
}
#if MB_TCP_ENABLED > 0
eMBErrorCode
eMBTCPInit( UCHAR ucSlaveUid, USHORT ucTCPPort )
{
eMBErrorCode eStatus = MB_ENOERR;
/* Check preconditions */
if( ucSlaveUid > MB_ADDRESS_MAX )
{
eStatus = MB_EINVAL;
}
else if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR )
{
eMBState = STATE_DISABLED;
}
else if( !xMBPortEventInit( ) )
{
/* Port dependent event module initalization failed. */
eStatus = MB_EPORTERR;
}
else
{
pvMBFrameStartCur = eMBTCPStart;
pvMBFrameStopCur = eMBTCPStop;
peMBFrameReceiveCur = eMBTCPReceive;
peMBFrameSendCur = eMBTCPSend;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL;
ucMBAddress = ucSlaveUid;
eMBCurrentMode = MB_TCP;
eMBState = STATE_DISABLED;
}
return eStatus;
}
#endif
eMBErrorCode
eMBRegisterCB( UCHAR ucFunctionCode, pxMBFunctionHandler pxHandler )
{
int i;
eMBErrorCode eStatus;
if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= MB_FUNC_CODE_MAX ) )
{
ENTER_CRITICAL_SECTION( );
if( pxHandler != NULL )
{
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
if( ( xFuncHandlers[i].pxHandler == NULL ) ||
( xFuncHandlers[i].pxHandler == pxHandler ) )
{
xFuncHandlers[i].ucFunctionCode = ucFunctionCode;
xFuncHandlers[i].pxHandler = pxHandler;
break;
}
}
eStatus = ( i != MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR : MB_ENORES;
}
else
{
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
{
xFuncHandlers[i].ucFunctionCode = 0;
xFuncHandlers[i].pxHandler = NULL;
break;
}
}
/* Remove can't fail. */
eStatus = MB_ENOERR;
}
EXIT_CRITICAL_SECTION( );
}
else
{
eStatus = MB_EINVAL;
}
return eStatus;
}
eMBErrorCode
eMBClose( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED )
{
if( pvMBFrameCloseCur != NULL )
{
pvMBFrameCloseCur( );
}
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBEnable( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED )
{
/* Activate the protocol stack. */
pvMBFrameStartCur( );
eMBState = STATE_ENABLED;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBDisable( void )
{
eMBErrorCode eStatus;
if( eMBState == STATE_ENABLED )
{
pvMBFrameStopCur( );
eMBState = STATE_DISABLED;
eStatus = MB_ENOERR;
}
else if( eMBState == STATE_DISABLED )
{
eStatus = MB_ENOERR;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBPoll( void )
{
static UCHAR *ucMBFrame = NULL;
static UCHAR ucRcvAddress;
static UCHAR ucFunctionCode;
static USHORT usLength;
static eMBException eException;
int i;
eMBErrorCode eStatus = MB_ENOERR;
eMBEventType eEvent;
/* Check if the protocol stack is ready. */
if( eMBState != STATE_ENABLED )
{
return MB_EILLSTATE;
}
/* Check if there is a event available. If not return control to caller.
* Otherwise we will handle the event. */
if( xMBPortEventGet( &eEvent ) == TRUE )
{
switch ( eEvent )
{
case EV_READY:
ESP_LOGD(MB_PORT_TAG, "%s:EV_READY", __func__);
break;
case EV_FRAME_RECEIVED:
ESP_LOGD(MB_PORT_TAG, "EV_FRAME_RECEIVED");
eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
if( eStatus == MB_ENOERR )
{
/* Check if the frame is for us. If not ignore the frame. */
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST )
|| ( ucRcvAddress == MB_TCP_PSEUDO_ADDRESS ) )
{
( void )xMBPortEventPost( EV_EXECUTE );
ESP_LOG_BUFFER_HEX_LEVEL(MB_PORT_TAG, &ucMBFrame[MB_PDU_FUNC_OFF], usLength, ESP_LOG_DEBUG);
}
}
break;
case EV_EXECUTE:
if ( !ucMBFrame ) {
return MB_EILLSTATE;
}
ESP_LOGD(MB_PORT_TAG, "%s:EV_EXECUTE", __func__);
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
/* No more function handlers registered. Abort. */
if( xFuncHandlers[i].ucFunctionCode == 0 )
{
break;
}
if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
{
eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
break;
}
}
/* If the request was not sent to the broadcast address we
* return a reply. In case of TCP the slave answers to broadcast address. */
if( ( ucRcvAddress != MB_ADDRESS_BROADCAST ) || ( eMBCurrentMode == MB_TCP ) )
{
if( eException != MB_EX_NONE )
{
/* An exception occurred. Build an error frame. */
usLength = 0;
ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
ucMBFrame[usLength++] = eException;
}
if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
{
vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
}
eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
}
break;
case EV_FRAME_TRANSMIT:
ESP_LOGD(MB_PORT_TAG, "%s:EV_FRAME_TRANSMIT", __func__);
break;
case EV_FRAME_SENT:
ESP_LOGD(MB_PORT_TAG, "%s:EV_FRAME_SENT", __func__);
break;
default:
break;
}
}
return eStatus;
}

View File

@@ -0,0 +1,641 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbrtu_m.c,v 1.60 2013/08/20 11:18:10 Armink Add Master Functions $
*/
/* ----------------------- System includes ----------------------------------*/
#include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbfunc.h"
#include "mbport.h"
#if MB_MASTER_RTU_ENABLED
#include "mbrtu.h"
#endif
#if MB_MASTER_ASCII_ENABLED
#include "mbascii.h"
#endif
#if MB_MASTER_TCP_ENABLED
#include "mbtcp.h"
#include "mbtcp_m.h"
#endif
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#ifndef MB_PORT_HAS_CLOSE
#define MB_PORT_HAS_CLOSE 1
#endif
/*------------------------ Shared variables ---------------------------------*/
_lock_t xMBMLock; // base modbus object lock
volatile UCHAR ucMasterSndBuf[MB_SERIAL_BUF_SIZE];
volatile UCHAR ucMasterRcvBuf[MB_SERIAL_BUF_SIZE];
static _Atomic USHORT usMasterSendPDULength = 0;
static _Atomic eMBMasterErrorEventType eMBMasterCurErrorType = EV_ERROR_INIT;
static _Atomic BOOL xMBRunInMasterMode = FALSE;
static _Atomic UCHAR ucMBMasterDestAddress = 0;
static _Atomic BOOL xFrameIsBroadcast = FALSE;
static _Atomic eMBMasterTimerMode eMasterCurTimerMode;
/* ----------------------- Static variables ---------------------------------*/
static uint64_t xCurTransactionId = 0;
static UCHAR *pucMBSendFrame = NULL;
static UCHAR *pucMBRecvFrame = NULL;
static UCHAR ucRecvAddress = 0;
static eMBMode eMBMasterCurrentMode;
/* The transaction information structure which keep last processing state */
static TransactionInfo_t xTransactionInfo = {0};
static enum
{
STATE_ENABLED,
STATE_DISABLED,
STATE_NOT_INITIALIZED
} eMBState = STATE_NOT_INITIALIZED;
/* Functions pointer which are initialized in eMBInit( ). Depending on the
* mode (RTU or ASCII) the are set to the correct implementations.
* Using for Modbus Master,Add by Armink 20130813
*/
static peMBFrameSend peMBMasterFrameSendCur;
static pvMBFrameStart pvMBMasterFrameStartCur;
static pvMBFrameStop pvMBMasterFrameStopCur;
static peMBFrameReceive peMBMasterFrameReceiveCur;
static pvMBFrameClose pvMBMasterFrameCloseCur;
/* Callback functions required by the porting layer. They are called when
* an external event has happend which includes a timeout or the reception
* or transmission of a character.
* Using for Modbus Master,Add by Armink 20130813
*/
BOOL( *pxMBMasterFrameCBByteReceived ) ( void );
BOOL( *pxMBMasterFrameCBTransmitterEmpty ) ( void );
BOOL( *pxMBMasterPortCBTimerExpired ) ( void );
BOOL( *pxMBMasterFrameCBReceiveFSMCur ) ( void );
BOOL( *pxMBMasterFrameCBTransmitFSMCur ) ( void );
/* An array of Modbus functions handlers which associates Modbus function
* codes with implementing functions.
*/
static xMBFunctionHandler xMasterFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
{MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
#endif
#if MB_FUNC_READ_INPUT_ENABLED > 0
{MB_FUNC_READ_INPUT_REGISTER, eMBMasterFuncReadInputRegister},
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
{MB_FUNC_READ_HOLDING_REGISTER, eMBMasterFuncReadHoldingRegister},
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBMasterFuncWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
{MB_FUNC_WRITE_REGISTER, eMBMasterFuncWriteHoldingRegister},
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBMasterFuncReadWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_READ_COILS_ENABLED > 0
{MB_FUNC_READ_COILS, eMBMasterFuncReadCoils},
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
{MB_FUNC_WRITE_SINGLE_COIL, eMBMasterFuncWriteCoil},
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
{MB_FUNC_WRITE_MULTIPLE_COILS, eMBMasterFuncWriteMultipleCoils},
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
{MB_FUNC_READ_DISCRETE_INPUTS, eMBMasterFuncReadDiscreteInputs},
#endif
};
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_TCP_ENABLED
eMBErrorCode
eMBMasterTCPInit( USHORT ucTCPPort )
{
eMBErrorCode eStatus = MB_ENOERR;
if( ( eStatus = eMBMasterTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) {
eMBState = STATE_DISABLED;
}
else if( !xMBMasterPortEventInit( ) ) {
/* Port dependent event module initialization failed. */
eStatus = MB_EPORTERR;
} else {
pvMBMasterFrameStartCur = eMBMasterTCPStart;
pvMBMasterFrameStopCur = eMBMasterTCPStop;
peMBMasterFrameReceiveCur = eMBMasterTCPReceive;
peMBMasterFrameSendCur = eMBMasterTCPSend;
pxMBMasterPortCBTimerExpired = xMBMasterTCPTimerExpired;
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterTCPPortClose : NULL;
eMBMasterCurrentMode = MB_TCP;
eMBState = STATE_DISABLED;
ucRecvAddress = MB_TCP_PSEUDO_ADDRESS;
xCurTransactionId = 0;
xTransactionInfo.xTransId = 0;
xTransactionInfo.ucDestAddr = 0;
xTransactionInfo.ucFuncCode = 0;
xTransactionInfo.eException = MB_EX_NONE;
xTransactionInfo.ucFrameError = 0;
/* initialize the state values. */
atomic_init(&usMasterSendPDULength, 0);
atomic_init(&eMBMasterCurErrorType, EV_ERROR_INIT);
atomic_init(&xMBRunInMasterMode, FALSE);
atomic_init(&ucMBMasterDestAddress, MB_TCP_PSEUDO_ADDRESS);
// initialize the OS resource for modbus master.
vMBMasterOsResInit();
if (xMBMasterPortTimersInit(MB_MASTER_TIMEOUT_MS_RESPOND * MB_TIMER_TICS_PER_MS) != TRUE)
{
eStatus = MB_EPORTERR;
}
}
return eStatus;
}
#endif
eMBErrorCode
eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
switch (eMode)
{
#if MB_MASTER_RTU_ENABLED > 0
case MB_RTU:
pvMBMasterFrameStartCur = eMBMasterRTUStart;
pvMBMasterFrameStopCur = eMBMasterRTUStop;
peMBMasterFrameSendCur = eMBMasterRTUSend;
peMBMasterFrameReceiveCur = eMBMasterRTUReceive;
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM;
pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM;
pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired;
eMBMasterCurrentMode = eMode;
eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity);
break;
#endif
#if MB_MASTER_ASCII_ENABLED > 0
case MB_ASCII:
pvMBMasterFrameStartCur = eMBMasterASCIIStart;
pvMBMasterFrameStopCur = eMBMasterASCIIStop;
peMBMasterFrameSendCur = eMBMasterASCIISend;
peMBMasterFrameReceiveCur = eMBMasterASCIIReceive;
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM;
pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM;
pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired;
eMBMasterCurrentMode = eMode;
eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity );
break;
#endif
default:
eStatus = MB_EINVAL;
break;
}
if (eStatus == MB_ENOERR)
{
if (!xMBMasterPortEventInit())
{
/* port dependent event module initalization failed. */
eStatus = MB_EPORTERR;
}
else
{
eMBState = STATE_DISABLED;
ucRecvAddress = 0;
xCurTransactionId = 0;
xTransactionInfo.xTransId = 0;
xTransactionInfo.ucDestAddr = 0;
xTransactionInfo.ucFuncCode = 0;
xTransactionInfo.eException = MB_EX_NONE;
xTransactionInfo.ucFrameError = 0;
/* initialize the state values. */
atomic_init(&usMasterSendPDULength, 0);
atomic_init(&eMBMasterCurErrorType, EV_ERROR_INIT);
atomic_init(&xMBRunInMasterMode, FALSE);
atomic_init(&ucMBMasterDestAddress, 0);
}
/* initialize the OS resource for modbus master. */
vMBMasterOsResInit();
}
return eStatus;
}
eMBErrorCode
eMBMasterClose( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED )
{
if( pvMBMasterFrameCloseCur != NULL )
{
pvMBMasterFrameCloseCur( );
}
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBMasterEnable( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED )
{
/* Activate the protocol stack. */
pvMBMasterFrameStartCur( );
/* Release the resource, because it created in busy state */
//vMBMasterRunResRelease( );
eMBState = STATE_ENABLED;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBMasterDisable( void )
{
eMBErrorCode eStatus;
if( eMBState == STATE_ENABLED )
{
pvMBMasterFrameStopCur( );
eMBState = STATE_DISABLED;
eStatus = MB_ENOERR;
}
else if( eMBState == STATE_DISABLED )
{
eStatus = MB_ENOERR;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBMasterPoll( void )
{
int i;
int j;
eMBErrorCode eStatus = MB_ENOERR;
xMBMasterEventType xEvent;
eMBMasterErrorEventType errorType = EV_ERROR_INIT;
static eMBException eException = MB_EX_NONE;
static UCHAR ucFunctionCode = 0;
static USHORT usRecvLength = 0;
/* Check if the protocol stack is ready. */
if( eMBState != STATE_ENABLED ) {
return MB_EILLSTATE;
}
/* Check if there is a event available. If not return control to caller.
* Otherwise we will handle the event. */
if ( xMBMasterPortEventGet( &xEvent ) == TRUE ) {
switch( xEvent.eEvent ) {
// In some cases it is possible that more than one event set
// together (even from one subset mask) than process them consistently
case EV_MASTER_READY:
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_READY", xEvent.xTransactionId);
vMBMasterSetErrorType( EV_ERROR_INIT );
vMBMasterRunResRelease( );
break;
case EV_MASTER_FRAME_TRANSMIT:
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_TRANSMIT", xEvent.xTransactionId);
/* Master is busy now. */
vMBMasterGetPDUSndBuf( &pucMBSendFrame );
ESP_LOG_BUFFER_HEX_LEVEL("POLL transmit buffer", (void*)pucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), pucMBSendFrame, usMBMasterGetPDUSndLength() );
if (eStatus != MB_ENOERR) {
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":Frame send error = %d", xEvent.xTransactionId, (unsigned)eStatus );
}
xCurTransactionId = xEvent.xTransactionId;
break;
case EV_MASTER_FRAME_SENT:
if (xCurTransactionId == xEvent.xTransactionId) {
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_SENT", xEvent.xTransactionId );
ESP_LOG_BUFFER_HEX_LEVEL("POLL sent buffer", (void*)pucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
}
break;
case EV_MASTER_FRAME_RECEIVED:
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_RECEIVED", xEvent.xTransactionId );
eStatus = peMBMasterFrameReceiveCur( &ucRecvAddress, &pucMBRecvFrame, &usRecvLength);
if (xCurTransactionId == xEvent.xTransactionId) {
MB_PORT_CHECK(pucMBSendFrame, MB_EILLSTATE, "Send buffer initialization fail.");
// Check if the frame is for us. If not ,send an error process event.
if ( ( eStatus == MB_ENOERR ) && ( ( ucRecvAddress == ucMBMasterGetDestAddress() )
|| ( ucRecvAddress == MB_TCP_PSEUDO_ADDRESS) ) ) {
if ( ( pucMBRecvFrame[MB_PDU_FUNC_OFF] & ~MB_FUNC_ERROR ) == ( pucMBSendFrame[MB_PDU_FUNC_OFF] ) ) {
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ": Packet data received successfully (%u).", xEvent.xTransactionId, (unsigned)eStatus);
ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)pucMBRecvFrame, (uint16_t)usRecvLength, ESP_LOG_DEBUG);
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
} else {
ESP_LOGE( MB_PORT_TAG, "Drop incorrect frame, receive_func(%u) != send_func(%u)",
pucMBRecvFrame[MB_PDU_FUNC_OFF], pucMBSendFrame[MB_PDU_FUNC_OFF]);
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
} else {
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ": Packet data receive failed (addr=%u)(%u).",
xEvent.xTransactionId, (unsigned)ucRecvAddress, (unsigned)eStatus);
}
} else {
// Ignore the `EV_MASTER_FRAME_RECEIVED` event because the respond timeout occurred
// and this is likely respond to previous transaction
ESP_LOGE( MB_PORT_TAG, "Drop data received outside of transaction (%" PRIu64 ")", xEvent.xTransactionId );
}
break;
case EV_MASTER_EXECUTE:
if (xCurTransactionId == xEvent.xTransactionId) {
if ( xMBMasterRequestIsBroadcast()
&& (( ucMBMasterGetCommMode() == MB_RTU ) || ( ucMBMasterGetCommMode() == MB_ASCII ) ) ) {
pucMBRecvFrame = pucMBSendFrame;
}
MB_PORT_CHECK(pucMBRecvFrame, MB_EILLSTATE, "receive buffer initialization fail.");
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_EXECUTE", xEvent.xTransactionId);
ucFunctionCode = pucMBRecvFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
/* If receive frame has exception. The receive function code highest bit is 1.*/
if (ucFunctionCode & MB_FUNC_ERROR) {
eException = (eMBException)pucMBRecvFrame[MB_PDU_DATA_OFF];
} else {
for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
/* No more function handlers registered. Abort. */
if (xMasterFuncHandlers[i].ucFunctionCode == 0) {
break;
}
if (xMasterFuncHandlers[i].ucFunctionCode == ucFunctionCode) {
vMBMasterSetCBRunInMasterMode(TRUE);
/* If master request is broadcast,
* the master need execute function for all slave.
*/
if ( xMBMasterRequestIsBroadcast() ) {
USHORT usLength = usMBMasterGetPDUSndLength();
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++)
{
vMBMasterSetDestAddress(j);
eException = xMasterFuncHandlers[i].pxHandler(pucMBRecvFrame, &usLength);
}
} else {
eException = xMasterFuncHandlers[i].pxHandler(pucMBRecvFrame, &usRecvLength);
}
vMBMasterSetCBRunInMasterMode( FALSE );
break;
}
}
}
/* If master has exception, will send error process event. Otherwise the master is idle.*/
if ( eException != MB_EX_NONE ) {
vMBMasterSetErrorType( EV_ERROR_EXECUTE_FUNCTION );
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
} else {
if ( eMBMasterGetErrorType( ) == EV_ERROR_INIT ) {
vMBMasterSetErrorType(EV_ERROR_OK);
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":set event EV_ERROR_OK", xEvent.xTransactionId );
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
}
} else {
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_EXECUTE is expired", xEvent.xTransactionId );
}
break;
case EV_MASTER_ERROR_PROCESS:
if (xCurTransactionId == xEvent.xTransactionId) {
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_ERROR_PROCESS", xEvent.xTransactionId);
/* Execute specified error process callback function. */
errorType = eMBMasterGetErrorType( );
vMBMasterGetPDUSndBuf( &pucMBSendFrame );
switch ( errorType )
{
case EV_ERROR_RESPOND_TIMEOUT:
vMBMasterErrorCBRespondTimeout( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_RECEIVE_DATA:
vMBMasterErrorCBReceiveData( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBRecvFrame, usRecvLength,
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_EXECUTE_FUNCTION:
vMBMasterErrorCBExecuteFunction( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBRecvFrame, usRecvLength,
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_OK:
vMBMasterCBRequestSuccess( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBRecvFrame, usRecvLength,
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
default:
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":incorrect error type = %d.", xEvent.xTransactionId, (int)errorType);
break;
}
}
vMBMasterPortTimersDisable( );
uint64_t xProcTime = xCurTransactionId ? ( xEvent.xPostTimestamp - xCurTransactionId ) : 0;
ESP_LOGD( MB_PORT_TAG, "Transaction (%" PRIu64 "), processing time(us) = %" PRId64, xCurTransactionId, xProcTime );
MB_ATOMIC_SECTION {
xTransactionInfo.xTransId = xCurTransactionId;
xTransactionInfo.ucDestAddr = ucMBMasterGetDestAddress();
xTransactionInfo.ucFuncCode = ucFunctionCode;
xTransactionInfo.eException = eException;
xTransactionInfo.ucFrameError = errorType;
}
xCurTransactionId = 0;
vMBMasterSetErrorType( EV_ERROR_INIT );
vMBMasterRunResRelease( );
break;
default:
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":Unexpected event triggered 0x%02x.", xEvent.xTransactionId, (int)xEvent.eEvent );
break;
}
} else {
// Something went wrong and task unblocked but there are no any correct events set
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ": Unexpected event triggered 0x%02x.", xEvent.xTransactionId, (int)xEvent.eEvent );
eStatus = MB_EILLSTATE;
}
return eStatus;
}
// Get whether the Modbus Master is run in master mode.
BOOL xMBMasterGetCBRunInMasterMode( void )
{
return atomic_load(&xMBRunInMasterMode);
}
// Set whether the Modbus Master is run in master mode.
void vMBMasterSetCBRunInMasterMode( BOOL IsMasterMode )
{
atomic_store(&xMBRunInMasterMode, IsMasterMode);
}
// Get Modbus Master send destination address.
UCHAR ucMBMasterGetDestAddress( void )
{
return atomic_load(&ucMBMasterDestAddress);
}
// Set Modbus Master send destination address.
void vMBMasterSetDestAddress( UCHAR Address )
{
atomic_store(&ucMBMasterDestAddress, Address);
}
// Get Modbus Master current error event type.
eMBMasterErrorEventType inline eMBMasterGetErrorType( void )
{
return atomic_load(&eMBMasterCurErrorType);
}
// Set Modbus Master current error event type.
void IRAM_ATTR vMBMasterSetErrorType( eMBMasterErrorEventType errorType )
{
atomic_store(&eMBMasterCurErrorType, errorType);
}
/* Get Modbus Master send PDU's buffer address pointer.*/
void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame )
{
*pucFrame = ( UCHAR * ) &ucMasterSndBuf[MB_SEND_BUF_PDU_OFF];
}
/* Set Modbus Master send PDU's buffer length.*/
void vMBMasterSetPDUSndLength( USHORT SendPDULength )
{
atomic_store(&usMasterSendPDULength, SendPDULength);
}
/* Get Modbus Master send PDU's buffer length.*/
USHORT usMBMasterGetPDUSndLength( void )
{
return atomic_load(&usMasterSendPDULength);
}
/* Set Modbus Master current timer mode.*/
void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode )
{
atomic_store(&eMasterCurTimerMode, eMBTimerMode);
}
/* Get Modbus Master current timer mode.*/
eMBMasterTimerMode MB_PORT_ISR_ATTR xMBMasterGetCurTimerMode( void )
{
return atomic_load(&eMasterCurTimerMode);
}
/* The master request is broadcast? */
BOOL MB_PORT_ISR_ATTR xMBMasterRequestIsBroadcast( void )
{
return atomic_load(&xFrameIsBroadcast);
}
/* The master request is broadcast? */
void vMBMasterRequestSetType( BOOL xIsBroadcast )
{
atomic_store(&xFrameIsBroadcast, xIsBroadcast);
}
// Get Modbus Master communication mode.
eMBMode ucMBMasterGetCommMode(void)
{
return eMBMasterCurrentMode;
}
/* Get current transaction information */
BOOL xMBMasterGetLastTransactionInfo( uint64_t *pxTransId, UCHAR *pucDestAddress,
UCHAR *pucFunctionCode, UCHAR *pucException,
USHORT *pusErrorType )
{
BOOL xState = (eMBState == STATE_ENABLED);
if (xState && pxTransId && pucDestAddress && pucFunctionCode
&& pucException && pusErrorType) {
MB_ATOMIC_SECTION {
*pxTransId = xTransactionInfo.xTransId;
*pucDestAddress = xTransactionInfo.ucDestAddr;
*pucFunctionCode = xTransactionInfo.ucFuncCode;
*pucException = xTransactionInfo.eException;
*pusErrorType = xTransactionInfo.ucFrameError;
}
}
return xState;
}
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@@ -0,0 +1,110 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbcrc.c,v 1.7 2007/02/18 23:50:27 wolti Exp $
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "mbconfig.h"
#if (MB_MASTER_RTU_ENABLED || MB_SLAVE_RTU_ENABLED || CONFIG_MB_UTEST)
static const UCHAR aucCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
static const UCHAR aucCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
0x41, 0x81, 0x80, 0x40
};
USHORT
usMBCRC16( UCHAR * pucFrame, USHORT usLen )
{
UCHAR ucCRCHi = 0xFF;
UCHAR ucCRCLo = 0xFF;
int iIndex;
while( usLen-- )
{
iIndex = ucCRCLo ^ *( pucFrame++ );
ucCRCLo = ( UCHAR )( ucCRCHi ^ aucCRCHi[iIndex] );
ucCRCHi = aucCRCLo[iIndex];
}
return ( USHORT )( ucCRCHi << 8 | ucCRCLo );
}
#endif

View File

@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbcrc.h,v 1.5 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_CRC_H
#define _MB_CRC_H
USHORT usMBCRC16( UCHAR * pucFrame, USHORT usLen );
#endif

View File

@@ -0,0 +1,372 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbrtu.c,v 1.18 2007/09/12 10:15:56 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbrtu.h"
#include "mbframe.h"
#include "mbcrc.h"
#include "mbport.h"
#if MB_SLAVE_RTU_ENABLED > 0
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
STATE_RX_INIT, /*!< Receiver is in initial state. */
STATE_RX_IDLE, /*!< Receiver is in idle state. */
STATE_RX_RCV, /*!< Frame is beeing received. */
STATE_RX_ERROR /*!< If the frame is invalid. */
} eMBRcvState;
typedef enum
{
STATE_TX_IDLE, /*!< Transmitter is in idle state. */
STATE_TX_XMIT /*!< Transmitter is in transfer state. */
} eMBSndState;
/* ----------------------- Shared variables ---------------------------------*/
extern volatile UCHAR ucMbSlaveBuf[];
/* ----------------------- Static variables ---------------------------------*/
static volatile eMBSndState eSndState;
static volatile eMBRcvState eRcvState;
static volatile UCHAR *pucSndBufferCur;
static volatile USHORT usSndBufferCount;
static volatile USHORT usRcvBufferPos;
static volatile UCHAR *ucRTUBuf = ucMbSlaveBuf;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
ULONG usTimerT35_50us;
( void )ucSlaveAddress;
ENTER_CRITICAL_SECTION( );
/* Modbus RTU uses 8 Databits. */
if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
{
eStatus = MB_EPORTERR;
}
else
{
/* If baudrate > 19200 then we should use the fixed timer values
* t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
*/
if( ulBaudRate > 19200 )
{
usTimerT35_50us = 35; /* 1800us. */
}
else
{
/* The timer reload value for a character is given by:
*
* ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
* = 11 * Ticks_per_1s / Baudrate
* = 220000 / Baudrate
* The reload for t3.5 is 1.5 times this value and similary
* for t3.5.
*/
usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
}
if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
{
eStatus = MB_EPORTERR;
}
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
void
eMBRTUStart( void )
{
ENTER_CRITICAL_SECTION( );
/* Initially the receiver is in the state STATE_RX_INIT. we start
* the timer and if no character is received within t3.5 we change
* to STATE_RX_IDLE. This makes sure that we delay startup of the
* modbus protocol stack until the bus is free.
*/
eRcvState = STATE_RX_INIT;
vMBPortSerialEnable( TRUE, FALSE );
#if CONFIG_FMB_TIMER_PORT_ENABLED
vMBPortTimersEnable( );
#else
pxMBPortCBTimerExpired();
#endif
EXIT_CRITICAL_SECTION( );
}
void
eMBRTUStop( void )
{
ENTER_CRITICAL_SECTION( );
vMBPortSerialEnable( FALSE, FALSE );
vMBPortTimersDisable( );
EXIT_CRITICAL_SECTION( );
}
eMBErrorCode
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBRTUFrame = ( UCHAR* ) ucRTUBuf;
USHORT usFrameLength = usRcvBufferPos;
if( xMBPortSerialGetRequest( &pucMBRTUFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
ENTER_CRITICAL_SECTION( );
assert( usFrameLength < MB_SER_PDU_SIZE_MAX );
/* Length and CRC check */
if( ( usFrameLength >= MB_SER_PDU_SIZE_MIN )
&& ( usMBCRC16( ( UCHAR * ) pucMBRTUFrame, usFrameLength ) == 0 ) )
{
/* Save the address field. All frames are passed to the upper layed
* and the decision if a frame is used is done there.
*/
*pucRcvAddress = pucMBRTUFrame[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
*pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & pucMBRTUFrame[MB_SER_PDU_PDU_OFF];
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT usCRC16;
/* Check if the receiver is still in idle state. If not we where to
* slow with processing the received frame and the master sent another
* frame on the network. We have to abort sending the frame.
*/
if( eRcvState == STATE_RX_IDLE )
{
ENTER_CRITICAL_SECTION( );
/* First byte before the Modbus-PDU is the slave address. */
pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
usSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usSndBufferCount += usLength;
/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
/* Activate the transmitter. */
eSndState = STATE_TX_XMIT;
EXIT_CRITICAL_SECTION( );
if( xMBPortSerialSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
return eStatus;
}
BOOL
xMBRTUReceiveFSM( void )
{
BOOL xStatus = FALSE;
UCHAR ucByte;
assert( eSndState == STATE_TX_IDLE );
/* Always read the character. */
xStatus = xMBPortSerialGetByte( ( CHAR * ) & ucByte );
switch ( eRcvState )
{
/* If we have received a character in the init state we have to
* wait until the frame is finished.
*/
case STATE_RX_INIT:
vMBPortTimersEnable( );
break;
/* In the error state we wait until all characters in the
* damaged frame are transmitted.
*/
case STATE_RX_ERROR:
vMBPortTimersEnable( );
break;
/* In the idle state we wait for a new character. If a character
* is received the t1.5 and t3.5 timers are started and the
* receiver is in the state STATE_RX_RCV.
*/
case STATE_RX_IDLE:
usRcvBufferPos = 0;
ucRTUBuf[usRcvBufferPos++] = ucByte;
eRcvState = STATE_RX_RCV;
/* Enable t3.5 timers. */
vMBPortTimersEnable( );
break;
/* We are currently receiving a frame. Reset the timer after
* every character received. If more than the maximum possible
* number of bytes in a modbus frame is received the frame is
* ignored.
*/
case STATE_RX_RCV:
if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
if( xStatus ) {
ucRTUBuf[usRcvBufferPos++] = ucByte;
}
}
else
{
eRcvState = STATE_RX_ERROR;
}
vMBPortTimersEnable( );
break;
}
return xStatus;
}
BOOL
xMBRTUTransmitFSM( void )
{
BOOL xNeedPoll = TRUE;
assert( eRcvState == STATE_RX_IDLE );
switch ( eSndState )
{
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_TX_IDLE:
break;
case STATE_TX_XMIT:
/* check if we are finished. */
if( usSndBufferCount != 0 )
{
xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
pucSndBufferCur++; /* next byte in sendbuffer. */
usSndBufferCount--;
}
else
{
xMBPortEventPost( EV_FRAME_TRANSMIT );
xNeedPoll = FALSE;
eSndState = STATE_TX_IDLE;
vMBPortTimersEnable( );
}
break;
}
return xNeedPoll;
}
BOOL MB_PORT_ISR_ATTR
xMBRTUTimerT35Expired( void )
{
BOOL xNeedPoll = FALSE;
switch ( eRcvState )
{
/* Timer t35 expired. Startup phase is finished. */
case STATE_RX_INIT:
xNeedPoll = xMBPortEventPost( EV_READY );
break;
/* A frame was received and t35 expired. Notify the listener that
* a new frame was received. */
case STATE_RX_RCV:
xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );
break;
/* An error occured while receiving the frame. */
case STATE_RX_ERROR:
break;
/* Function called in an illegal state. */
default:
assert( ( eRcvState == STATE_RX_IDLE ) || ( eRcvState == STATE_RX_ERROR ) );
}
vMBPortTimersDisable( );
eRcvState = STATE_RX_IDLE;
return xNeedPoll;
}
#endif

View File

@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbrtu.h,v 1.9 2006/12/07 22:10:34 wolti Exp $
*/
#include "mbconfig.h"
#ifndef _MB_RTU_H
#define _MB_RTU_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Defines ------------------------------------------*/
#define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */
#if MB_SLAVE_RTU_ENABLED
eMBErrorCode eMBRTUInit( UCHAR slaveAddress, UCHAR ucPort, ULONG ulBaudRate,
eMBParity eParity );
void eMBRTUStart( void );
void eMBRTUStop( void );
eMBErrorCode eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength );
eMBErrorCode eMBRTUSend( UCHAR slaveAddress, const UCHAR * pucFrame, USHORT usLength );
BOOL xMBRTUReceiveFSM( void );
BOOL xMBRTUTransmitFSM( void );
BOOL xMBRTUTimerT15Expired( void );
BOOL xMBRTUTimerT35Expired( void );
#endif
#if MB_MASTER_RTU_ENABLED
eMBErrorCode eMBMasterRTUInit( UCHAR ucPort, ULONG ulBaudRate,eMBParity eParity );
void eMBMasterRTUStart( void );
void eMBMasterRTUStop( void );
eMBErrorCode eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength );
eMBErrorCode eMBMasterRTUSend( UCHAR slaveAddress, const UCHAR * pucFrame, USHORT usLength );
BOOL xMBMasterRTUReceiveFSM( void );
BOOL xMBMasterRTUTransmitFSM( void );
BOOL xMBMasterRTUTimerExpired( void );
#endif
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,439 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2013 China Beijing Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbrtu_m.c,v 1.60 2013/08/17 11:42:56 Armink Add Master Functions $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbrtu.h"
#include "mbframe.h"
#include "mbcrc.h"
#include "mbport.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_RTU_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
STATE_M_RX_INIT, /*!< Receiver is in initial state. */
STATE_M_RX_IDLE, /*!< Receiver is in idle state. */
STATE_M_RX_RCV, /*!< Frame is beeing received. */
STATE_M_RX_ERROR, /*!< If the frame is invalid. */
} eMBMasterRcvState;
typedef enum
{
STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */
STATE_M_TX_XMIT, /*!< Transmitter is in transfer state. */
STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */
} eMBMasterSndState;
#if MB_MASTER_RTU_ENABLED > 0
/*------------------------ Shared variables ---------------------------------*/
extern volatile UCHAR ucMasterRcvBuf[];
extern volatile UCHAR ucMasterSndBuf[];
/* ----------------------- Static variables ---------------------------------*/
static volatile eMBMasterSndState eSndState;
static volatile eMBMasterRcvState eRcvState;
static volatile UCHAR *pucMasterSndBufferCur;
static volatile USHORT usMasterSndBufferCount;
static volatile USHORT usMasterRcvBufferPos;
static volatile UCHAR *ucMasterRTURcvBuf = ucMasterRcvBuf;
static volatile UCHAR *ucMasterRTUSndBuf = ucMasterSndBuf;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBMasterRTUInit(UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
ULONG usTimerT35_50us;
ENTER_CRITICAL_SECTION( );
/* Modbus RTU uses 8 Databits. */
if( xMBMasterPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
{
eStatus = MB_EPORTERR;
}
else
{
/* If baudrate > 19200 then we should use the fixed timer values
* t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
*/
if( ulBaudRate > 19200 )
{
usTimerT35_50us = 35; /* 1800us. */
}
else
{
/* The timer reload value for a character is given by:
*
* ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
* = 11 * Ticks_per_1s / Baudrate
* = 220000 / Baudrate
* The reload for t3.5 is 1.5 times this value and similary
* for t3.5.
*/
usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
}
if( xMBMasterPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
{
eStatus = MB_EPORTERR;
}
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
void
eMBMasterRTUStart( void )
{
ENTER_CRITICAL_SECTION( );
/* Initially the receiver is in the state STATE_M_RX_INIT. we start
* the timer and if no character is received within t3.5 we change
* to STATE_M_RX_IDLE. This makes sure that we delay startup of the
* modbus protocol stack until the bus is free.
*/
eRcvState = STATE_M_RX_INIT;
vMBMasterPortSerialEnable( TRUE, FALSE );
vMBMasterPortTimersT35Enable( );
EXIT_CRITICAL_SECTION( );
}
void
eMBMasterRTUStop( void )
{
ENTER_CRITICAL_SECTION( );
vMBMasterPortSerialEnable( FALSE, FALSE );
vMBMasterPortTimersDisable( );
EXIT_CRITICAL_SECTION( );
}
eMBErrorCode
eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBRTUFrame = ( UCHAR* ) ucMasterRTURcvBuf;
USHORT usFrameLength = usMasterRcvBufferPos;
if( xMBMasterPortSerialGetResponse( &pucMBRTUFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
ENTER_CRITICAL_SECTION( );
assert( usFrameLength < MB_SER_PDU_SIZE_MAX );
assert( pucMBRTUFrame );
/* Length and CRC check */
if( ( usFrameLength >= MB_RTU_SER_PDU_SIZE_MIN )
&& ( usMBCRC16( ( UCHAR * ) pucMBRTUFrame, usFrameLength ) == 0 ) )
{
/* Save the address field. All frames are passed to the upper layer
* and the decision if a frame is used is done there.
*/
*pucRcvAddress = pucMBRTUFrame[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
*pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & pucMBRTUFrame[MB_SER_PDU_PDU_OFF];
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
eMBErrorCode
eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT usCRC16;
if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
/* Check if the receiver is still in idle state. If not we where to
* slow with processing the received frame and the master sent another
* frame on the network. We have to abort sending the frame.
*/
if( eRcvState == STATE_M_RX_IDLE )
{
ENTER_CRITICAL_SECTION( );
/* First byte before the Modbus-PDU is the slave address. */
pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
usMasterSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usMasterSndBufferCount += usLength;
/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
usCRC16 = usMBCRC16( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
pucMasterSndBufferCur[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
pucMasterSndBufferCur[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
EXIT_CRITICAL_SECTION( );
/* Activate the transmitter. */
eSndState = STATE_M_TX_XMIT;
if ( xMBMasterPortSerialSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
// The place to enable RS485 driver
vMBMasterPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
return eStatus;
}
BOOL
xMBMasterRTUReceiveFSM( void )
{
BOOL xStatus = FALSE;
UCHAR ucByte;
if ( ( eSndState != STATE_M_TX_IDLE ) && ( eSndState != STATE_M_TX_XFWR ) ) {
return FALSE;
}
/* Always read the character. */
xStatus = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
switch ( eRcvState )
{
/* If we have received a character in the init state we have to
* wait until the frame is finished.
*/
case STATE_M_RX_INIT:
vMBMasterPortTimersT35Enable( );
ESP_LOGD("DBG", "Start initialization phase.");
break;
/* In the error state we wait until all characters in the
* damaged frame are transmitted.
*/
case STATE_M_RX_ERROR:
vMBMasterPortTimersT35Enable( );
break;
/* In the idle state we wait for a new character. If a character
* is received the t1.5 and t3.5 timers are started and the
* receiver is in the state STATE_M_RX_RCV and disable early
* the timer of respond timeout .
*/
case STATE_M_RX_IDLE:
/* In time of respond timeout,the receiver receive a frame.
* Disable timer of respond timeout and change the transmiter state to idle.
*/
vMBMasterPortTimersDisable( );
usMasterRcvBufferPos = 0;
if( xStatus && ucByte ) {
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
eRcvState = STATE_M_RX_RCV;
eSndState = STATE_M_TX_IDLE;
}
/* Enable t3.5 timers. */
#if CONFIG_FMB_TIMER_PORT_ENABLED
vMBMasterPortTimersT35Enable( );
#endif
break;
/* We are currently receiving a frame. Reset the timer after
* every character received. If more than the maximum possible
* number of bytes in a modbus frame is received the frame is
* ignored.
*/
case STATE_M_RX_RCV:
if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
if ( xStatus ) {
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
}
}
else
{
eRcvState = STATE_M_RX_ERROR;
}
#if CONFIG_FMB_TIMER_PORT_ENABLED
vMBMasterPortTimersT35Enable( );
#endif
break;
}
return xStatus;
}
BOOL
xMBMasterRTUTransmitFSM( void )
{
BOOL xNeedPoll = TRUE;
BOOL xFrameIsBroadcast = FALSE;
if ( eRcvState != STATE_M_RX_IDLE ) {
return FALSE;
}
switch ( eSndState )
{
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_M_TX_XFWR:
xNeedPoll = FALSE;
break;
case STATE_M_TX_IDLE:
break;
case STATE_M_TX_XMIT:
/* check if we are finished. */
if( usMasterSndBufferCount != 0 )
{
xMBMasterPortSerialPutByte( ( CHAR )*pucMasterSndBufferCur );
pucMasterSndBufferCur++; /* next byte in sendbuffer. */
usMasterSndBufferCount--;
}
else
{
xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SEND_BUF_PDU_OFF - MB_SER_PDU_PDU_OFF]
== MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
vMBMasterRequestSetType( xFrameIsBroadcast );
eSndState = STATE_M_TX_XFWR;
/* If the frame is broadcast ,master will enable timer of convert delay,
* else master will enable timer of respond timeout. */
if ( xFrameIsBroadcast == TRUE )
{
vMBMasterPortTimersConvertDelayEnable( );
}
else
{
vMBMasterPortTimersRespondTimeoutEnable( );
}
}
break;
}
return xNeedPoll;
}
BOOL MB_PORT_ISR_ATTR
xMBMasterRTUTimerExpired(void)
{
BOOL xNeedPoll = FALSE;
switch (eRcvState)
{
/* Timer t35 expired. Startup phase is finished. */
case STATE_M_RX_INIT:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
ESP_EARLY_LOGD("DBG", "RTU timer, init FSM state.");
break;
/* A frame was received and t35 expired. Notify the listener that
* a new frame was received. */
case STATE_M_RX_RCV:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
break;
/* An error occured while receiving the frame. */
case STATE_M_RX_ERROR:
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
break;
/* Function called in an illegal state. */
default:
assert(eRcvState == STATE_M_RX_IDLE);
break;
}
eRcvState = STATE_M_RX_IDLE;
switch (eSndState)
{
/* A frame was send finish and convert delay or respond timeout expired.
* If the frame is broadcast,The master will idle,and if the frame is not
* broadcast. Notify the listener process error.*/
case STATE_M_TX_XFWR:
if ( xMBMasterRequestIsBroadcast( ) == FALSE ) {
vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
}
break;
/* Function called in an illegal state. */
default:
assert( ( eSndState == STATE_M_TX_XMIT ) || ( eSndState == STATE_M_TX_IDLE ));
break;
}
eSndState = STATE_M_TX_IDLE;
vMBMasterPortTimersDisable( );
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
if (xMBMasterGetCurTimerMode() == MB_TMODE_CONVERT_DELAY) {
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_EXECUTE);
}
return xNeedPoll;
}
#endif

View File

@@ -0,0 +1,167 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbtcp.c,v 1.3 2006/12/07 22:10:34 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbconfig.h"
#include "mbtcp.h"
#include "mbframe.h"
#include "mbport.h"
#if MB_TCP_ENABLED
/* ----------------------- Defines ------------------------------------------*/
/* ----------------------- MBAP Header --------------------------------------*/
/*
*
* <------------------------ MODBUS TCP/IP ADU(1) ------------------------->
* <----------- MODBUS PDU (1') ---------------->
* +-----------+---------------+------------------------------------------+
* | TID | PID | Length | UID |Code | Data |
* +-----------+---------------+------------------------------------------+
* | | | | |
* (2) (3) (4) (5) (6)
*
* (2) ... MB_TCP_TID = 0 (Transaction Identifier - 2 Byte)
* (3) ... MB_TCP_PID = 2 (Protocol Identifier - 2 Byte)
* (4) ... MB_TCP_LEN = 4 (Number of bytes - 2 Byte)
* (5) ... MB_TCP_UID = 6 (Unit Identifier - 1 Byte)
* (6) ... MB_TCP_FUNC = 7 (Modbus Function Code)
*
* (1) ... Modbus TCP/IP Application Data Unit
* (1') ... Modbus Protocol Data Unit
*/
#define MB_TCP_PROTOCOL_ID 0 /* 0 = Modbus Protocol */
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBTCPDoInit( USHORT ucTCPPort )
{
eMBErrorCode eStatus = MB_ENOERR;
if( xMBTCPPortInit( ucTCPPort ) == FALSE )
{
eStatus = MB_EPORTERR;
}
return eStatus;
}
void
eMBTCPStart( void )
{
ESP_LOGD(MB_PORT_TAG, "TCP Slave port enable.");
vMBTCPPortEnable( );
}
void
eMBTCPStop( void )
{
/* Make sure that no more clients are connected. */
ESP_LOGD(MB_PORT_TAG, "TCP Slave port disable.");
vMBTCPPortDisable( );
}
eMBErrorCode
eMBTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_EIO;
UCHAR *pucMBTCPFrame;
USHORT usLength;
USHORT usPID;
if( xMBTCPPortGetRequest( &pucMBTCPFrame, &usLength ) != FALSE )
{
usPID = pucMBTCPFrame[MB_TCP_PID] << 8U;
usPID |= pucMBTCPFrame[MB_TCP_PID + 1];
if( usPID == MB_TCP_PROTOCOL_ID )
{
*ppucFrame = &pucMBTCPFrame[MB_TCP_FUNC];
*pusLength = usLength - MB_TCP_FUNC;
eStatus = MB_ENOERR;
/* The regular Modbus TCP does not use any addresses. Fake the MBAP UID in this case.
* The MBAP UID field support is used for RTU over TCP option if enabled.
*/
#if MB_TCP_UID_ENABLED
*pucRcvAddress = pucMBTCPFrame[MB_TCP_UID];
#else
*pucRcvAddress = MB_TCP_PSEUDO_ADDRESS;
#endif
}
}
else
{
eStatus = MB_EIO;
}
return eStatus;
}
eMBErrorCode
eMBTCPSend( UCHAR _unused, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBTCPFrame = ( UCHAR * ) pucFrame - MB_TCP_FUNC;
USHORT usTCPLength = usLength + MB_TCP_FUNC;
/* The MBAP header is already initialized because the caller calls this
* function with the buffer returned by the previous call. Therefore we
* only have to update the length in the header. Note that the length
* header includes the size of the Modbus PDU and the UID Byte. Therefore
* the length is usLength plus one.
*/
pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U;
pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF;
if( xMBTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE )
{
eStatus = MB_EIO;
}
return eStatus;
}
#endif

View File

@@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbtcp.h,v 1.2 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_TCP_H
#define _MB_TCP_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Defines ------------------------------------------*/
#if MB_TCP_ENABLED
/* ----------------------- Function prototypes ------------------------------*/
eMBErrorCode eMBTCPDoInit( USHORT ucTCPPort );
void eMBTCPStart( void );
void eMBTCPStop( void );
eMBErrorCode eMBTCPReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
USHORT * pusLength );
eMBErrorCode eMBTCPSend( UCHAR _unused, const UCHAR * pucFrame,
USHORT usLength );
#endif
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbtcp.c,v 1.3 2006/12/07 22:10:34 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbconfig.h"
#include "mbtcp_m.h"
#include "mbframe.h"
#include "mbport.h"
#if MB_MASTER_TCP_ENABLED
/* ----------------------- Defines ------------------------------------------*/
/* ----------------------- MBAP Header --------------------------------------*/
/*
*
* <------------------------ MODBUS TCP/IP ADU(1) ------------------------->
* <----------- MODBUS PDU (1') ---------------->
* +-----------+---------------+------------------------------------------+
* | TID | PID | Length | UID |Code | Data |
* +-----------+---------------+------------------------------------------+
* | | | | |
* (2) (3) (4) (5) (6)
*
* (2) ... MB_TCP_TID = 0 (Transaction Identifier - 2 Byte)
* (3) ... MB_TCP_PID = 2 (Protocol Identifier - 2 Byte)
* (4) ... MB_TCP_LEN = 4 (Number of bytes - 2 Byte)
* (5) ... MB_TCP_UID = 6 (Unit Identifier - 1 Byte)
* (6) ... MB_TCP_FUNC = 7 (Modbus Function Code)
*
* (1) ... Modbus TCP/IP Application Data Unit
* (1') ... Modbus Protocol Data Unit
*/
#define MB_TCP_PROTOCOL_ID 0 /* 0 = Modbus Protocol */
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBMasterTCPDoInit( USHORT ucTCPPort )
{
eMBErrorCode eStatus = MB_ENOERR;
if( xMBMasterTCPPortInit( ucTCPPort ) == FALSE )
{
eStatus = MB_EPORTERR;
}
return eStatus;
}
void
eMBMasterTCPStart( void )
{
ESP_LOGD(MB_PORT_TAG, "TCP Master port enable.");
vMBMasterTCPPortEnable( );
}
void
eMBMasterTCPStop( void )
{
ESP_LOGD(MB_PORT_TAG, "TCP Master port disable.");
vMBMasterTCPPortDisable( );
}
eMBErrorCode
eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_EIO;
UCHAR *pucMBTCPFrame;
USHORT usLength;
USHORT usPID;
if( xMBMasterTCPPortGetRequest( &pucMBTCPFrame, &usLength ) != FALSE )
{
usPID = pucMBTCPFrame[MB_TCP_PID] << 8U;
usPID |= pucMBTCPFrame[MB_TCP_PID + 1];
if( usPID == MB_TCP_PROTOCOL_ID )
{
*ppucFrame = &pucMBTCPFrame[MB_TCP_FUNC];
*pusLength = usLength - MB_TCP_FUNC;
eStatus = MB_ENOERR;
/* Get MBAP UID field if its support is enabled.
* Otherwise just ignore this field.
*/
#if MB_TCP_UID_ENABLED
*pucRcvAddress = pucMBTCPFrame[MB_TCP_UID];
#else
*pucRcvAddress = MB_TCP_PSEUDO_ADDRESS;
#endif
}
}
else
{
eStatus = MB_EIO;
}
return eStatus;
}
eMBErrorCode
eMBMasterTCPSend( UCHAR ucAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBTCPFrame = ( UCHAR * ) pucFrame - MB_TCP_FUNC;
USHORT usTCPLength = usLength + MB_TCP_FUNC;
/* Note that the length in the MBAP header includes the size of the Modbus PDU
* and the UID Byte. Therefore the length is usLength plus one.
*/
pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U;
pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF;
/* Set UID field in the MBAP if it is supported.
* If the RTU over TCP is not supported, the UID = 0 or 0xFF.
*/
#if MB_TCP_UID_ENABLED
pucMBTCPFrame[MB_TCP_UID] = ucAddress;
#else
pucMBTCPFrame[MB_TCP_UID] = 0x00;
#endif
if( xMBMasterTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE )
{
eStatus = MB_EIO;
}
return eStatus;
}
#endif

View File

@@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbtcp.h,v 1.2 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_TCP_M_H
#define _MB_TCP_M_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Defines ------------------------------------------*/
#if MB_MASTER_TCP_ENABLED
/* ----------------------- Function prototypes ------------------------------*/
eMBErrorCode eMBMasterTCPDoInit( USHORT ucTCPPort );
void eMBMasterTCPStart( void );
void eMBMasterTCPStop( void );
eMBErrorCode eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
USHORT * pusLength );
eMBErrorCode eMBMasterTCPSend( UCHAR ucAddress, const UCHAR * pucFrame,
USHORT usLength );
BOOL xMBMasterTCPTimerExpired(void);
#endif
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,213 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: port.c,v 1.60 2015/02/01 9:18:05 Armink $
*/
/* ----------------------- System includes --------------------------------*/
/* ----------------------- Modbus includes ----------------------------------*/
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "port.h"
/* ----------------------- Variables ----------------------------------------*/
static _lock_t s_port_lock;
static UCHAR ucPortMode = 0;
/* ----------------------- Start implementation -----------------------------*/
INLINE int lock_obj(_lock_t *plock)
{
_lock_acquire(plock);
return 1;
}
INLINE void unlock_obj(_lock_t *plock)
{
_lock_release(plock);
}
INLINE void
vMBPortEnterCritical(void)
{
_lock_acquire(&s_port_lock);
}
INLINE void
vMBPortExitCritical(void)
{
_lock_release(&s_port_lock);
}
UCHAR
ucMBPortGetMode( void )
{
return ucPortMode;
}
void
vMBPortSetMode( UCHAR ucMode )
{
ENTER_CRITICAL_SECTION();
ucPortMode = ucMode;
EXIT_CRITICAL_SECTION();
}
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout)
{
BOOL xResult = (BaseType_t)xQueueReceive(xMbUartQueue, (void*)pxEvent, (TickType_t) xTimeout);
ESP_LOGD(MB_PORT_TAG, "%s, UART event: %u ", __func__, (unsigned)pxEvent->type);
return xResult;
}
#endif
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
/*
* The function is called from ASCII/RTU module to get processed data buffer. Sets the
* received buffer and its length using parameters.
*/
__attribute__ ((weak))
BOOL xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, " %s default", __func__);
return TRUE;
}
/*
* The function is called from ASCII/RTU module to set processed data buffer
* to be sent in transmitter state machine.
*/
__attribute__ ((weak))
BOOL xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
return TRUE;
}
#endif
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
__attribute__ ((weak))
BOOL xMBPortSerialGetRequest( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
return TRUE;
}
__attribute__ ((weak))
BOOL xMBPortSerialSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
return TRUE;
}
#endif
#if MB_TCP_DEBUG
// This function is kept to realize legacy freemodbus frame logging functionality
void
prvvMBTCPLogFrame( const CHAR * pucMsg, UCHAR * pucFrame, USHORT usFrameLen )
{
int i;
int res = 0;
int iBufPos = 0;
size_t iBufLeft = MB_TCP_FRAME_LOG_BUFSIZE;
static CHAR arcBuffer[MB_TCP_FRAME_LOG_BUFSIZE];
assert( pucFrame != NULL );
for ( i = 0; i < usFrameLen; i++ ) {
// Print some additional frame information.
switch ( i )
{
case 0:
// TID = Transaction Identifier.
res = snprintf( &arcBuffer[iBufPos], iBufLeft, "| TID = " );
break;
case 2:
// PID = Protocol Identifier.
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | PID = " );
break;
case 4:
// Length
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | LEN = " );
break;
case 6:
// UID = Unit Identifier.
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | UID = " );
break;
case 7:
// MB Function Code.
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | FUNC = " );
break;
case 8:
// MB PDU rest.
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | DATA = " );
break;
default:
res = 0;
break;
}
if( res == -1 ) {
break;
}
else {
iBufPos += res;
iBufLeft -= res;
}
// Print the data.
res = snprintf( &arcBuffer[iBufPos], iBufLeft, "%02X", pucFrame[i] );
if( res == -1 ) {
break;
} else {
iBufPos += res;
iBufLeft -= res;
}
}
if( res != -1 ) {
// Append an end of frame string.
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " |" );
if( res != -1 ) {
ESP_LOGD(pucMsg, "%s", arcBuffer);
}
}
}
#endif

View File

@@ -0,0 +1,254 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: port.h,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
#ifndef PORT_COMMON_H_
#define PORT_COMMON_H_
#include "sys/lock.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h" // for queue
#include "esp_log.h" // for ESP_LOGE macro
#include "esp_timer.h"
#include "driver/uart.h" // for uart_event_t
#if __has_include("driver/gptimer.h")
#include "driver/gptimer.h"
#else
#include "driver/timer.h"
#endif
#include "mbconfig.h"
#define INLINE inline __attribute__((always_inline))
#define PR_BEGIN_EXTERN_C extern "C" {
#define PR_END_EXTERN_C }
#define MB_PORT_TAG "MB_PORT_COMMON"
#define MB_BAUD_RATE_DEFAULT (115200)
#define MB_QUEUE_LENGTH (CONFIG_FMB_QUEUE_LENGTH)
#define MB_SERIAL_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_FMB_PORT_TASK_STACK_SIZE)
#define MB_SERIAL_TOUT (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks
// Set buffer size for transmission
#define MB_SERIAL_BUF_SIZE (CONFIG_FMB_SERIAL_BUF_SIZE)
// common definitions for serial port implementations
#define MB_SERIAL_TX_TOUT_MS (2200) // maximum time for transmission of longest allowed frame buffer
#define MB_SERIAL_TX_TOUT_TICKS (pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS)) // timeout for transmission
#define MB_SERIAL_RX_TOUT_MS (1)
#define MB_SERIAL_RX_TOUT_TICKS (pdMS_TO_TICKS(MB_SERIAL_RX_TOUT_MS)) // timeout for receive
#define MB_SERIAL_RESP_LEN_MIN (4)
// Common definitions for TCP port
#define MB_TCP_BUF_SIZE (256 + 7) // Must hold a complete Modbus TCP frame.
#define MB_TCP_DEFAULT_PORT (CONFIG_FMB_TCP_PORT_DEFAULT)
#define MB_TCP_STACK_SIZE (CONFIG_FMB_PORT_TASK_STACK_SIZE)
#define MB_TCP_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
// The task affinity for Modbus stack tasks
#define MB_PORT_TASK_AFFINITY (CONFIG_FMB_PORT_TASK_AFFINITY)
#define MB_TCP_READ_TIMEOUT_MS (100) // read timeout in mS
#define MB_TCP_READ_TIMEOUT (pdMS_TO_TICKS(MB_TCP_READ_TIMEOUT_MS))
#define MB_TCP_SEND_TIMEOUT_MS (500) // send event timeout in mS
#define MB_TCP_SEND_TIMEOUT (pdMS_TO_TICKS(MB_TCP_SEND_TIMEOUT_MS))
#define MB_TCP_PORT_MAX_CONN (CONFIG_FMB_TCP_PORT_MAX_CONN)
// Set the API unlock time to maximum response time
// The actual release time will be dependent on the timer time
#define MB_MAX_RESPONSE_TIME_MS (5000)
#define MB_TCP_FRAME_LOG_BUFSIZE (256)
#define MB_PORT_HAS_CLOSE (1) // Define to explicitly close port on destroy
// Define number of timer reloads per 1 mS
#define MB_TIMER_TICS_PER_MS (20UL)
#define MB_TIMER_TICK_TIME_US (1000 / MB_TIMER_TICS_PER_MS) // 50uS = one discreet for timer
#define MB_TCP_DEBUG (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) // Enable legacy debug output in TCP module.
#define MB_ATTR_WEAK __attribute__ ((weak))
#define MB_TCP_GET_FIELD(buffer, field) ((USHORT)((buffer[field] << 8U) | buffer[field + 1]))
#define MB_PORT_CHECK(a, ret_val, str, ...) \
if (!(a)) { \
ESP_LOGE(MB_PORT_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
return ret_val; \
}
// Macro to check if stack shutdown event is active
#define TCP_PORT_CHECK_SHDN(sema_ptr, callback_func) do { \
if (sema_ptr) { \
ESP_LOGD(TAG, "Shutdown stack from %s(%d)", __func__, __LINE__); \
callback_func(); \
} \
} while(0)
int lock_obj(_lock_t *plock);
void unlock_obj(_lock_t *plock);
#define CRITICAL_SECTION_INIT(lock) \
do \
{ \
_lock_init((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION_CLOSE(lock) \
do \
{ \
_lock_close((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION_LOCK(lock) \
do \
{ \
lock_obj((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION_UNLOCK(lock) \
do \
{ \
unlock_obj((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION(lock) for (int st = lock_obj((_lock_t *)&lock); (st > 0); unlock_obj((_lock_t *)&lock), st = -1)
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif /* __cplusplus */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef char BOOL;
typedef unsigned char UCHAR;
typedef char CHAR;
typedef unsigned short USHORT;
typedef short SHORT;
typedef unsigned long ULONG;
typedef long LONG;
#if MB_TCP_DEBUG
typedef enum
{
MB_LOG_DEBUG,
MB_LOG_INFO,
MB_LOG_WARN,
MB_LOG_ERROR
} eMBPortLogLevel;
#endif
typedef enum
{
MB_PROTO_TCP,
MB_PROTO_UDP,
} eMBPortProto;
typedef enum {
MB_PORT_IPV4 = 0, /*!< TCP IPV4 addressing */
MB_PORT_IPV6 = 1 /*!< TCP IPV6 addressing */
} eMBPortIpVer;
typedef struct {
esp_timer_handle_t xTimerIntHandle;
USHORT usT35Ticks;
BOOL xTimerState;
} xTimerContext_t;
void vMBPortEnterCritical(void);
void vMBPortExitCritical(void);
#define ENTER_CRITICAL_SECTION( ) { ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \
vMBPortEnterCritical(); }
#define EXIT_CRITICAL_SECTION( ) { vMBPortExitCritical(); \
ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); }
#define MB_PORT_CHECK_EVENT( event, mask ) ( event & mask )
#define MB_PORT_CLEAR_EVENT( event, mask ) do { event &= ~mask; } while(0)
#define MB_PORT_PARITY_GET(parity) ((parity != UART_PARITY_DISABLE) ? \
((parity == UART_PARITY_ODD) ? MB_PAR_ODD : MB_PAR_EVEN) : MB_PAR_NONE)
// Legacy Modbus logging function
#if MB_TCP_DEBUG
void vMBPortLog( eMBPortLogLevel eLevel, const CHAR * szModule,
const CHAR * szFmt, ... );
void prvvMBTCPLogFrame( const CHAR * pucMsg, UCHAR * pucFrame, USHORT usFrameLen );
#endif
void vMBPortSetMode( UCHAR ucMode );
UCHAR ucMBPortGetMode( void );
BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout);
/**
* This is modbus master user error handling funcion.
* If it is defined in the user application, then helps to handle the errors
* and received/sent buffers to transfer as well as handle the slave exception codes.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param usError - the error code, see the enumeration eMBMasterErrorEventType
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*/
void vMBMasterErrorCBUserHandler( uint64_t xTransId, USHORT usError, UCHAR ucDestAddress, const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength ) MB_ATTR_WEAK;
#ifdef __cplusplus
PR_END_EXTERN_C
#endif /* __cplusplus */
#endif /* PORT_COMMON_H_ */

View File

@@ -0,0 +1,121 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portevent.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "port.h"
#include "mbconfig.h"
#include "port_serial_slave.h"
/* ----------------------- Variables ----------------------------------------*/
static QueueHandle_t xQueueHdl;
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{
BOOL bStatus = FALSE;
if((xQueueHdl = xQueueCreate(MB_EVENT_QUEUE_SIZE, sizeof(eMBEventType))) != NULL)
{
vQueueAddToRegistry(xQueueHdl, "MbPortEventQueue");
bStatus = TRUE;
}
return bStatus;
}
void
vMBPortEventClose( void )
{
if(xQueueHdl != NULL)
{
vQueueDelete(xQueueHdl);
xQueueHdl = NULL;
}
}
BOOL MB_PORT_ISR_ATTR
xMBPortEventPost( eMBEventType eEvent )
{
BaseType_t xStatus, xHigherPriorityTaskWoken = pdFALSE;
assert(xQueueHdl != NULL);
if( (BOOL)xPortInIsrContext() == TRUE )
{
xStatus = xQueueSendFromISR(xQueueHdl, (const void*)&eEvent, &xHigherPriorityTaskWoken);
if ( xHigherPriorityTaskWoken )
{
portYIELD_FROM_ISR();
}
if (xStatus != pdTRUE) {
ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %u.", __func__, (unsigned)xStatus);
return FALSE;
}
}
else
{
xStatus = xQueueSend(xQueueHdl, (const void*)&eEvent, MB_EVENT_QUEUE_TIMEOUT);
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "%s: Post message failure.", __func__);
}
return TRUE;
}
BOOL
xMBPortEventGet(eMBEventType * peEvent)
{
assert(xQueueHdl != NULL);
BOOL xEventHappened = FALSE;
if (xQueueReceive(xQueueHdl, peEvent, portMAX_DELAY) == pdTRUE) {
xEventHappened = TRUE;
}
return xEventHappened;
}
QueueHandle_t
xMBPortEventGetHandle(void)
{
if(xQueueHdl != NULL)
{
return xQueueHdl;
}
return NULL;
}

View File

@@ -0,0 +1,358 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portevent.c v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
*/
/* ----------------------- Modbus includes ----------------------------------*/
#include <stdatomic.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "mb_m.h"
#include "mbport.h"
#include "mbconfig.h"
#include "port.h"
#include "mbport.h"
#include "freertos/semphr.h"
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
/* ----------------------- Defines ------------------------------------------*/
// Event bit mask for eMBMasterWaitRequestFinish()
#define MB_EVENT_REQ_MASK (EventBits_t)( EV_MASTER_PROCESS_SUCCESS | \
EV_MASTER_ERROR_RESPOND_TIMEOUT | \
EV_MASTER_ERROR_RECEIVE_DATA | \
EV_MASTER_ERROR_EXECUTE_FUNCTION )
/* ----------------------- Variables ----------------------------------------*/
static SemaphoreHandle_t xResourceMasterHdl;
static EventGroupHandle_t xEventGroupMasterHdl;
static EventGroupHandle_t xEventGroupMasterConfirmHdl;
static QueueHandle_t xQueueMasterHdl;
static _Atomic uint64_t xTransactionID = 0;
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBMasterPortEventInit( void )
{
xEventGroupMasterHdl = xEventGroupCreate();
xEventGroupMasterConfirmHdl = xEventGroupCreate();
MB_PORT_CHECK((xEventGroupMasterHdl != NULL) && (xEventGroupMasterConfirmHdl != NULL),
FALSE, "mb stack event group creation error.");
xQueueMasterHdl = xQueueCreate(MB_EVENT_QUEUE_SIZE, sizeof(xMBMasterEventType));
MB_PORT_CHECK(xQueueMasterHdl, FALSE, "mb stack event group creation error.");
vQueueAddToRegistry(xQueueMasterHdl, "MbMasterPortEventQueue");
atomic_init(&xTransactionID, 0);
return TRUE;
}
BOOL MB_PORT_ISR_ATTR
xMBMasterPortEventPost( eMBMasterEventEnum eEvent)
{
BaseType_t xStatus, xHigherPriorityTaskWoken = pdFALSE;
assert(xQueueMasterHdl != NULL);
xMBMasterEventType xEvent;
xEvent.xPostTimestamp = esp_timer_get_time();
if (eEvent & EV_MASTER_TRANS_START) {
atomic_store(&(xTransactionID), xEvent.xPostTimestamp);
}
xEvent.eEvent = (eEvent & ~EV_MASTER_TRANS_START);
if( (BOOL)xPortInIsrContext() == TRUE ) {
xStatus = xQueueSendFromISR(xQueueMasterHdl, (const void*)&xEvent, &xHigherPriorityTaskWoken);
if ( xHigherPriorityTaskWoken ) {
portYIELD_FROM_ISR();
}
if (xStatus != pdTRUE) {
ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %d.", __func__, xStatus);
return FALSE;
}
} else {
xStatus = xQueueSend(xQueueMasterHdl, (const void*)&xEvent, MB_EVENT_QUEUE_TIMEOUT);
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "%s: Post message failure.", __func__);
}
return TRUE;
}
BOOL
xMBMasterPortEventGet(xMBMasterEventType *peEvent)
{
assert(xQueueMasterHdl != NULL);
BOOL xEventHappened = FALSE;
if (xQueueReceive(xQueueMasterHdl, peEvent, portMAX_DELAY) == pdTRUE) {
peEvent->xTransactionId = atomic_load(&xTransactionID);
// Set event bits in confirmation group (for synchronization with port task)
xEventGroupSetBits(xEventGroupMasterConfirmHdl, peEvent->eEvent);
peEvent->xGetTimestamp = esp_timer_get_time();
xEventHappened = TRUE;
}
return xEventHappened;
}
eMBMasterEventEnum
xMBMasterPortFsmWaitConfirmation( eMBMasterEventEnum eEventMask, ULONG ulTimeout)
{
EventBits_t uxBits;
uxBits = xEventGroupWaitBits( xEventGroupMasterConfirmHdl, // The event group being tested.
eEventMask, // The bits within the event group to wait for.
pdFALSE, // Keep masked bits.
pdFALSE, // Don't wait for both bits, either bit will do.
ulTimeout); // Wait timeout for either bit to be set.
if (ulTimeout && uxBits) {
// Clear confirmation events that where set in the mask
xEventGroupClearBits( xEventGroupMasterConfirmHdl, (uxBits & eEventMask) );
}
return (eMBMasterEventEnum)(uxBits & eEventMask);
}
uint64_t xMBMasterPortGetTransactionId( )
{
return atomic_load(&xTransactionID);
}
// This function is initialize the OS resource for modbus master.
void vMBMasterOsResInit( void )
{
xResourceMasterHdl = xSemaphoreCreateBinary();
MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "%s: Resource create error.", __func__);
}
/**
* This function is take Mobus Master running resource.
* Note:The resource is define by Operating System.
*
* @param lTimeOut the waiting time.
*
* @return resource take result
*/
BOOL xMBMasterRunResTake( LONG lTimeOut )
{
BaseType_t xStatus = pdTRUE;
xStatus = xSemaphoreTake( xResourceMasterHdl, lTimeOut );
MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: Resource take failure.", __func__);
ESP_LOGD(MB_PORT_TAG,"%s:Take MB resource (%lu ticks).", __func__, lTimeOut);
return TRUE;
}
/**
* This function is release Modbus Master running resource.
* Note:The resource is define by Operating System. If you not use OS this function can be empty.
*/
void vMBMasterRunResRelease( void )
{
BaseType_t xStatus = pdFALSE;
xStatus = xSemaphoreGive( xResourceMasterHdl );
if (xStatus != pdTRUE) {
ESP_LOGD(MB_PORT_TAG,"%s: Release resource fail.", __func__);
}
}
/**
* This is modbus master respond timeout error process callback function.
* @note There functions will block modbus master poll while execute OS waiting.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*
*/
void vMBMasterErrorCBRespondTimeout(uint64_t xTransId, UCHAR ucDestAddress, const UCHAR* pucSendData, USHORT ucSendLength)
{
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RESPOND_TIMEOUT );
ESP_LOGD(MB_PORT_TAG,"%s:Callback respond timeout.", __func__);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_RESPOND_TIMEOUT, ucDestAddress,
NULL, 0,
pucSendData, ucSendLength );
}
}
/**
* This is modbus master receive data error process callback function.
* @note There functions will block modbus master poll while execute OS waiting.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*/
void vMBMasterErrorCBReceiveData(uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength)
{
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RECEIVE_DATA );
ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data failure.", __func__);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_RECEIVE_DATA, ucDestAddress,
pucRecvData, ucRecvLength,
pucSendData, ucSendLength );
}
}
/**
* This is modbus master execute function error process callback function.
* @note There functions will block modbus master poll while execute OS waiting.
* So,for real-time of system.Do not execute too much waiting process.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*
*/
void vMBMasterErrorCBExecuteFunction(uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength)
{
xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_EXECUTE_FUNCTION );
ESP_LOGD(MB_PORT_TAG,"%s:Callback execute data handler failure.", __func__);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_EXECUTE_FUNCTION, ucDestAddress,
pucRecvData, ucRecvLength,
pucSendData, ucSendLength );
}
}
/**
* This is modbus master request process success callback function.
* @note There functions will block modbus master poll while execute OS waiting.
* So,for real-time of system. Do not execute too much waiting process.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*/
void vMBMasterCBRequestSuccess(uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength)
{
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_PROCESS_SUCCESS );
ESP_LOGD(MB_PORT_TAG,"%s: Callback request success.", __func__);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_OK, ucDestAddress,
pucRecvData, ucRecvLength,
pucSendData, ucSendLength );
}
}
/**
* This function is wait for modbus master request finish and return result.
* Waiting result include request process success, request respond timeout,
* receive data error and execute function error.You can use the above callback function.
* @note If you are use OS, you can use OS's event mechanism. Otherwise you have to run
* much user custom delay for waiting.
*
* @return request error code
*/
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
eMBMasterEventEnum xRecvedEvent;
EventBits_t uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
MB_EVENT_REQ_MASK, // The bits within the event group to wait for.
pdTRUE, // Masked bits should be cleared before returning.
pdFALSE, // Don't wait for both bits, either bit will do.
portMAX_DELAY ); // Wait forever for either bit to be set.
xRecvedEvent = (eMBMasterEventEnum)(uxBits);
if (xRecvedEvent) {
ESP_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, (int)xRecvedEvent);
if (!(xRecvedEvent & MB_EVENT_REQ_MASK)) {
// if we wait for certain event bits but get from poll subset
ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, (int)xRecvedEvent);
}
xEventGroupSetBits( xEventGroupMasterConfirmHdl, (xRecvedEvent & MB_EVENT_REQ_MASK) );
if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS)) {
eErrStatus = MB_MRE_NO_ERR;
} else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RESPOND_TIMEOUT)) {
eErrStatus = MB_MRE_TIMEDOUT;
} else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RECEIVE_DATA)) {
eErrStatus = MB_MRE_REV_DATA;
} else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_EXECUTE_FUNCTION)) {
eErrStatus = MB_MRE_EXE_FUN;
}
} else {
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, (int)uxBits);
// https://github.com/espressif/esp-idf/issues/5275
// if a no event is received, that means vMBMasterPortEventClose()
// has been closed, so event group has been deleted by FreeRTOS, which
// triggers the send of 0 value to the event group to unlock this task
// waiting on it. For this patch, handles it as a time out without assert.
eErrStatus = MB_MRE_TIMEDOUT;
}
return eErrStatus;
}
void vMBMasterPortEventClose(void)
{
if (xEventGroupMasterHdl) {
vEventGroupDelete(xEventGroupMasterHdl);
xEventGroupMasterHdl = NULL;
}
if (xQueueMasterHdl) {
vQueueDelete(xQueueMasterHdl);
xQueueMasterHdl = NULL;
}
if (xEventGroupMasterConfirmHdl) {
vEventGroupDelete(xEventGroupMasterConfirmHdl);
xEventGroupMasterConfirmHdl = NULL;
}
if (xResourceMasterHdl) {
vSemaphoreDelete(xResourceMasterHdl);
xResourceMasterHdl = NULL;
}
}
#endif

View File

@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Demo Application
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include <stdlib.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- Modbus includes ----------------------------------*/
/* ----------------------- Variables ----------------------------------------*/
/* ----------------------- Start implementation -----------------------------*/
BOOL
bMBPortIsWithinException( void )
{
BOOL bIsWithinException = xPortInIsrContext();
return bIsWithinException;
}
void
vMBPortClose( void )
{
extern void vMBPortSerialClose( void );
extern void vMBPortTimerClose( void );
extern void vMBPortEventClose( void );
vMBPortSerialClose( );
vMBPortTimerClose( );
vMBPortEventClose( );
}

View File

@@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Demo Application
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include <stdlib.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbport.h"
/* ----------------------- Modbus includes ----------------------------------*/
/* ----------------------- Variables ----------------------------------------*/
/* ----------------------- Start implementation -----------------------------*/
void
vMBMasterPortClose( void )
{
extern void vMBMasterPortSerialClose( void );
extern void vMBMasterPortTimerClose( void );
extern void vMBMasterPortEventClose( void );
vMBMasterPortSerialClose( );
vMBMasterPortTimerClose( );
vMBMasterPortEventClose( );
}

View File

@@ -0,0 +1,288 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
#include "driver/uart.h"
#include "port.h"
#include "driver/uart.h"
#include "freertos/queue.h" // for queue support
#include "soc/uart_periph.h"
#include "driver/gpio.h"
#include "esp_log.h" // for esp_log
#include "esp_err.h" // for ESP_ERROR_CHECK macro
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "sdkconfig.h" // for KConfig options
#include "port_serial_slave.h"
// Note: This code uses mixed coding standard from legacy IDF code and used freemodbus stack
// A queue to handle UART event.
static QueueHandle_t xMbUartQueue;
static TaskHandle_t xMbTaskHandle;
static const CHAR *TAG = "MB_SERIAL";
// The UART hardware port number
static UCHAR ucUartNumber = UART_NUM_MAX - 1;
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
void vMBPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
{
// This function can be called from xMBRTUTransmitFSM() of different task
if (bTxEnable) {
bTxStateEnabled = TRUE;
} else {
bTxStateEnabled = FALSE;
}
if (bRxEnable) {
//uart_enable_rx_intr(ucUartNumber);
bRxStateEnabled = TRUE;
vTaskResume(xMbTaskHandle); // Resume receiver task
} else {
vTaskSuspend(xMbTaskHandle); // Block receiver task
bRxStateEnabled = FALSE;
}
}
static USHORT usMBPortSerialRxPoll(size_t xEventSize)
{
BOOL xReadStatus = TRUE;
USHORT usCnt = 0;
if (bRxStateEnabled) {
// Get received packet into Rx buffer
while(xReadStatus && (usCnt++ <= xEventSize)) {
// Call the Modbus stack callback function and let it fill the buffers.
xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM
}
uart_flush_input(ucUartNumber);
// Send event EV_FRAME_RECEIVED to allow stack process packet
#if !CONFIG_FMB_TIMER_PORT_ENABLED
pxMBPortCBTimerExpired();
#endif
ESP_LOGD(TAG, "RX: %u bytes\n", (unsigned)usCnt);
}
return usCnt;
}
BOOL xMBPortSerialTxPoll(void)
{
USHORT usCount = 0;
BOOL bNeedPoll = TRUE;
if( bTxStateEnabled ) {
// Continue while all response bytes put in buffer or out of buffer
while((bNeedPoll) && (usCount++ < MB_SERIAL_BUF_SIZE)) {
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM
}
ESP_LOGD(TAG, "MB_TX_buffer send: (%u) bytes\n", (unsigned)usCount);
// Waits while UART sending the packet
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
vMBPortSerialEnable(TRUE, FALSE);
MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
return TRUE;
}
return FALSE;
}
static void vUartTask(void *pvParameters)
{
uart_event_t xEvent;
USHORT usResult = 0;
for(;;) {
if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) {
ESP_LOGD(TAG, "MB_uart[%u] event:", (unsigned)ucUartNumber);
switch(xEvent.type) {
//Event of UART receving data
case UART_DATA:
ESP_LOGD(TAG,"Data event, length: %u", (unsigned)xEvent.size);
// This flag set in the event means that no more
// data received during configured timeout and UART TOUT feature is triggered
if (xEvent.timeout_flag) {
// Get buffered data length
ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size));
// Read received data and send it to modbus stack
usResult = usMBPortSerialRxPoll(xEvent.size);
ESP_LOGD(TAG,"Timeout occured, processed: %u bytes", (unsigned)usResult);
}
break;
//Event of HW FIFO overflow detected
case UART_FIFO_OVF:
ESP_LOGD(TAG, "hw fifo overflow");
xQueueReset(xMbUartQueue);
break;
//Event of UART ring buffer full
case UART_BUFFER_FULL:
ESP_LOGD(TAG, "ring buffer full");
xQueueReset(xMbUartQueue);
uart_flush_input(ucUartNumber);
break;
//Event of UART RX break detected
case UART_BREAK:
ESP_LOGD(TAG, "uart rx break");
break;
//Event of UART parity check error
case UART_PARITY_ERR:
ESP_LOGD(TAG, "uart parity error");
break;
//Event of UART frame error
case UART_FRAME_ERR:
ESP_LOGD(TAG, "uart frame error");
break;
default:
ESP_LOGD(TAG, "uart event type: %u", (unsigned)xEvent.type);
break;
}
}
}
vTaskDelete(NULL);
}
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
UCHAR ucDataBits, eMBParity eParity)
{
esp_err_t xErr = ESP_OK;
// Set communication port number
ucUartNumber = ucPORT;
// Configure serial communication parameters
UCHAR ucParity = UART_PARITY_DISABLE;
UCHAR ucData = UART_DATA_8_BITS;
switch(eParity){
case MB_PAR_NONE:
ucParity = UART_PARITY_DISABLE;
break;
case MB_PAR_ODD:
ucParity = UART_PARITY_ODD;
break;
case MB_PAR_EVEN:
ucParity = UART_PARITY_EVEN;
break;
default:
ESP_LOGE(TAG, "Incorrect parity option: %u", (unsigned)eParity);
return FALSE;
}
switch(ucDataBits){
case 5:
ucData = UART_DATA_5_BITS;
break;
case 6:
ucData = UART_DATA_6_BITS;
break;
case 7:
ucData = UART_DATA_7_BITS;
break;
case 8:
ucData = UART_DATA_8_BITS;
break;
default:
ucData = UART_DATA_8_BITS;
break;
}
uart_config_t xUartConfig = {
.baud_rate = ulBaudRate,
.data_bits = ucData,
.parity = ucParity,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 2,
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
.source_clk = UART_SCLK_DEFAULT,
#else
.source_clk = UART_SCLK_APB,
#endif
};
// Set UART config
xErr = uart_param_config(ucUartNumber, &xUartConfig);
MB_PORT_CHECK((xErr == ESP_OK),
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (int)xErr);
// Install UART driver, and get the queue.
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial driver failure, uart_driver_install() returned (0x%x).", (int)xErr);
#if !CONFIG_FMB_TIMER_PORT_ENABLED
// Set timeout for TOUT interrupt (T3.5 modbus time)
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (int)xErr);
#endif
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
uart_set_always_rx_timeout(ucUartNumber, true);
// Create a task to handle UART events
BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
MB_SERIAL_TASK_STACK_SIZE,
NULL, MB_SERIAL_TASK_PRIO,
&xMbTaskHandle, MB_PORT_TASK_AFFINITY);
if (xStatus != pdPASS) {
vTaskDelete(xMbTaskHandle);
// Force exit from function with failure
MB_PORT_CHECK(FALSE, FALSE,
"mb stack serial task creation error. xTaskCreate() returned (0x%x).",
(int)xStatus);
} else {
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
}
return TRUE;
}
void vMBPortSerialClose(void)
{
(void)vTaskSuspend(xMbTaskHandle);
(void)vTaskDelete(xMbTaskHandle);
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
}
BOOL xMBPortSerialPutByte(CHAR ucByte)
{
// Send one byte to UART transmission buffer
// This function is called by Modbus stack
UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
return (ucLength == 1);
}
// Get one byte from intermediate RX buffer
BOOL xMBPortSerialGetByte(CHAR* pucByte)
{
assert(pucByte != NULL);
USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
return (usLength == 1);
}

View File

@@ -0,0 +1,371 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portserial.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions $
*/
#include <string.h>
#include "driver/uart.h"
#include "soc/dport_access.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "sdkconfig.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "port.h"
#include "mbport.h"
#include "mb_m.h"
#include "mbrtu.h"
#include "mbconfig.h"
#include "port_serial_master.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_SERIAL_RX_SEMA_TOUT_MS (1000)
#define MB_SERIAL_RX_SEMA_TOUT (pdMS_TO_TICKS(MB_SERIAL_RX_SEMA_TOUT_MS))
#define MB_SERIAL_RX_FLUSH_RETRY (2)
/* ----------------------- Static variables ---------------------------------*/
static const CHAR *TAG = "MB_MASTER_SERIAL";
// A queue to handle UART event.
static QueueHandle_t xMbUartQueue;
static TaskHandle_t xMbTaskHandle;
// The UART hardware port number
static UCHAR ucUartNumber = UART_NUM_MAX - 1;
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
static SemaphoreHandle_t xMasterSemaRxHandle; // Rx blocking semaphore handle
static BOOL xMBMasterPortRxSemaInit( void )
{
xMasterSemaRxHandle = xSemaphoreCreateBinary();
MB_PORT_CHECK((xMasterSemaRxHandle != NULL), FALSE , "%s: RX semaphore create failure.", __func__);
return TRUE;
}
static void vMBMasterPortRxSemaClose( void )
{
if (xMasterSemaRxHandle) {
vSemaphoreDelete(xMasterSemaRxHandle);
xMasterSemaRxHandle = NULL;
}
}
static BOOL xMBMasterPortRxSemaTake( LONG lTimeOut )
{
BaseType_t xStatus = pdTRUE;
xStatus = xSemaphoreTake(xMasterSemaRxHandle, lTimeOut );
MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: RX semaphore take failure.", __func__);
ESP_LOGV(MB_PORT_TAG,"%s:Take RX semaphore (%" PRIu64 " ticks).", __func__, (uint64_t)lTimeOut);
return TRUE;
}
static void vMBMasterRxSemaRelease( void )
{
BaseType_t xStatus = pdFALSE;
xStatus = xSemaphoreGive(xMasterSemaRxHandle);
if (xStatus != pdTRUE) {
ESP_LOGD(MB_PORT_TAG,"%s:RX semaphore is free.", __func__);
}
}
static BOOL vMBMasterRxSemaIsBusy( void )
{
BaseType_t xStatus = pdFALSE;
xStatus = (uxSemaphoreGetCount(xMasterSemaRxHandle) == 0) ? TRUE : FALSE;
return xStatus;
}
void vMBMasterRxFlush( void )
{
size_t xSize = 1;
esp_err_t xErr = ESP_OK;
for (int xCount = 0; (xCount < MB_SERIAL_RX_FLUSH_RETRY) && xSize; xCount++) {
xErr = uart_get_buffered_data_len(ucUartNumber, &xSize);
MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", (int)xErr);
BaseType_t xStatus = xQueueReset(xMbUartQueue);
if (xStatus) {
xErr = uart_flush_input(ucUartNumber);
MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", (int)xErr);
}
}
}
void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
{
// This function can be called from xMBRTUTransmitFSM() of different task
if (bTxEnable) {
vMBMasterRxFlush();
bTxStateEnabled = TRUE;
} else {
bTxStateEnabled = FALSE;
}
if (bRxEnable) {
bRxStateEnabled = TRUE;
vMBMasterRxSemaRelease();
vTaskResume(xMbTaskHandle); // Resume receiver task
} else {
vTaskSuspend(xMbTaskHandle); // Block receiver task
bRxStateEnabled = FALSE;
}
}
static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize)
{
BOOL xStatus = TRUE;
USHORT usCnt = 0;
xStatus = xMBMasterPortRxSemaTake(MB_SERIAL_RX_SEMA_TOUT);
if (xStatus) {
while(xStatus && (usCnt++ <= xEventSize)) {
// Call the Modbus stack callback function and let it fill the stack buffers.
xStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
}
// The buffer is transferred into Modbus stack and is not needed here any more
uart_flush_input(ucUartNumber);
ESP_LOGD(TAG, "Received data: %u(bytes in buffer)", (unsigned)usCnt);
#if !CONFIG_FMB_TIMER_PORT_ENABLED
vMBMasterSetCurTimerMode(MB_TMODE_T35);
xStatus = pxMBMasterPortCBTimerExpired();
if (!xStatus) {
xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
ESP_LOGD(TAG, "Send additional RX ready event.");
}
#endif
} else {
ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%u bytes) received. ",
__func__, (unsigned)xEventSize);
}
return usCnt;
}
BOOL xMBMasterPortSerialTxPoll(void)
{
USHORT usCount = 0;
BOOL bNeedPoll = TRUE;
if( bTxStateEnabled ) {
// Continue while all response bytes put in buffer or out of buffer
while(bNeedPoll && (usCount++ < MB_SERIAL_BUF_SIZE)) {
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // callback to transmit FSM
}
ESP_LOGD(TAG, "MB_TX_buffer sent: (%u) bytes.", (unsigned)(usCount - 1));
// Waits while UART sending the packet
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
vMBMasterPortSerialEnable(TRUE, FALSE);
MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
return TRUE;
}
return FALSE;
}
// UART receive event task
static void vUartTask(void* pvParameters)
{
uart_event_t xEvent;
USHORT usResult = 0;
for(;;) {
if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) {
ESP_LOGD(TAG, "MB_uart[%u] event:", (unsigned)ucUartNumber);
switch(xEvent.type) {
//Event of UART receiving data
case UART_DATA:
ESP_LOGD(TAG,"Data event, len: %u.", (unsigned)xEvent.size);
// This flag set in the event means that no more
// data received during configured timeout and UART TOUT feature is triggered
if (xEvent.timeout_flag) {
// Response is received but previous packet processing is pending
// Do not wait completion of processing and just discard received data as incorrect
if (vMBMasterRxSemaIsBusy()) {
vMBMasterRxFlush();
break;
}
// Get buffered data length
ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size));
// Read received data and send it to modbus stack
usResult = usMBMasterPortSerialRxPoll(xEvent.size);
ESP_LOGD(TAG,"Timeout occured, processed: %u bytes", (unsigned)usResult);
}
break;
//Event of HW FIFO overflow detected
case UART_FIFO_OVF:
ESP_LOGD(TAG, "hw fifo overflow.");
xQueueReset(xMbUartQueue);
break;
//Event of UART ring buffer full
case UART_BUFFER_FULL:
ESP_LOGD(TAG, "ring buffer full.");
xQueueReset(xMbUartQueue);
uart_flush_input(ucUartNumber);
break;
//Event of UART RX break detected
case UART_BREAK:
ESP_LOGD(TAG, "uart rx break.");
break;
//Event of UART parity check error
case UART_PARITY_ERR:
ESP_LOGD(TAG, "uart parity error.");
xQueueReset(xMbUartQueue);
uart_flush_input(ucUartNumber);
break;
//Event of UART frame error
case UART_FRAME_ERR:
ESP_LOGD(TAG, "uart frame error.");
xQueueReset(xMbUartQueue);
uart_flush_input(ucUartNumber);
break;
default:
ESP_LOGD(TAG, "uart event type: %u.", (unsigned)xEvent.type);
break;
}
}
}
vTaskDelete(NULL);
}
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
esp_err_t xErr = ESP_OK;
// Set communication port number
ucUartNumber = ucPORT;
// Configure serial communication parameters
UCHAR ucParity = UART_PARITY_DISABLE;
UCHAR ucData = UART_DATA_8_BITS;
switch(eParity){
case MB_PAR_NONE:
ucParity = UART_PARITY_DISABLE;
break;
case MB_PAR_ODD:
ucParity = UART_PARITY_ODD;
break;
case MB_PAR_EVEN:
ucParity = UART_PARITY_EVEN;
break;
default:
ESP_LOGE(TAG, "Incorrect parity option: %u", (unsigned)eParity);
return FALSE;
}
switch(ucDataBits){
case 5:
ucData = UART_DATA_5_BITS;
break;
case 6:
ucData = UART_DATA_6_BITS;
break;
case 7:
ucData = UART_DATA_7_BITS;
break;
case 8:
ucData = UART_DATA_8_BITS;
break;
default:
ucData = UART_DATA_8_BITS;
break;
}
uart_config_t xUartConfig = {
.baud_rate = ulBaudRate,
.data_bits = ucData,
.parity = ucParity,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 2,
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
.source_clk = UART_SCLK_DEFAULT,
#else
.source_clk = UART_SCLK_APB,
#endif
};
// Set UART config
xErr = uart_param_config(ucUartNumber, &xUartConfig);
MB_PORT_CHECK((xErr == ESP_OK),
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (int)xErr);
// Install UART driver, and get the queue.
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial driver failure, uart_driver_install() returned (0x%x).", (int)xErr);
// Set timeout for TOUT interrupt (T3.5 modbus time)
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (int)xErr);
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
uart_set_always_rx_timeout(ucUartNumber, true);
MB_PORT_CHECK((xMBMasterPortRxSemaInit()), FALSE,
"mb serial RX semaphore create fail.");
// Create a task to handle UART events
BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
MB_SERIAL_TASK_STACK_SIZE,
NULL, MB_SERIAL_TASK_PRIO,
&xMbTaskHandle, MB_PORT_TASK_AFFINITY);
if (xStatus != pdPASS) {
vTaskDelete(xMbTaskHandle);
// Force exit from function with failure
MB_PORT_CHECK(FALSE, FALSE,
"mb stack serial task creation error. xTaskCreate() returned (0x%x).", (int)xStatus);
} else {
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
}
ESP_LOGD(MB_PORT_TAG,"%s Init serial.", __func__);
return TRUE;
}
void vMBMasterPortSerialClose(void)
{
vMBMasterPortRxSemaClose();
(void)vTaskDelete(xMbTaskHandle);
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
}
BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
{
// Send one byte to UART transmission buffer
// This function is called by Modbus stack
UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
return (ucLength == 1);
}
// Get one byte from intermediate RX buffer
BOOL xMBMasterPortSerialGetByte(CHAR* pucByte)
{
assert(pucByte != NULL);
USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
return (usLength == 1);
}

View File

@@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "sdkconfig.h"
#if CONFIG_FMB_TIMER_PORT_ENABLED
static const char *TAG = "MBS_TIMER";
static xTimerContext_t* pxTimerContext = NULL;
/* ----------------------- Start implementation -----------------------------*/
static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
{
pxMBPortCBTimerExpired(); // Timer expired callback function
pxTimerContext->xTimerState = TRUE;
ESP_EARLY_LOGD(TAG, "Slave timeout triggered.");
}
#endif
BOOL xMBPortTimersInit(USHORT usTimeOut50us)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
"Modbus timeout discreet is incorrect.");
MB_PORT_CHECK(!pxTimerContext, FALSE,
"Modbus timer is already created.");
pxTimerContext = calloc(1, sizeof(xTimerContext_t));
if (!pxTimerContext) {
return FALSE;
}
pxTimerContext->xTimerIntHandle = NULL;
// Save timer reload value for Modbus T35 period
pxTimerContext->usT35Ticks = usTimeOut50us;
esp_timer_create_args_t xTimerConf = {
.callback = vTimerAlarmCBHandler,
.arg = NULL,
#if (MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD && CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD)
.dispatch_method = ESP_TIMER_ISR,
#else
.dispatch_method = ESP_TIMER_TASK,
#endif
.name = "MBS_T35timer"
};
// Create Modbus timer
esp_err_t xErr = esp_timer_create(&xTimerConf, &(pxTimerContext->xTimerIntHandle));
if (xErr) {
return FALSE;
}
#endif
return TRUE;
}
void vMBPortTimersEnable(void)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
MB_PORT_CHECK((pxTimerContext && pxTimerContext->xTimerIntHandle), ; ,
"timer is not initialized.");
uint64_t xToutUs = (pxTimerContext->usT35Ticks * MB_TIMER_TICK_TIME_US);
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_start_once(pxTimerContext->xTimerIntHandle, xToutUs);
pxTimerContext->xTimerState = FALSE;
#endif
}
void MB_PORT_ISR_ATTR
vMBPortTimersDisable(void)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
// Disable timer alarm
esp_timer_stop(pxTimerContext->xTimerIntHandle);
#endif
}
void vMBPortTimerClose(void)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
// Delete active timer
if (pxTimerContext) {
if (pxTimerContext->xTimerIntHandle) {
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_delete(pxTimerContext->xTimerIntHandle);
}
free(pxTimerContext);
pxTimerContext = NULL;
}
#endif
}
void vMBPortTimersDelay(USHORT usTimeOutMS)
{
vTaskDelay(usTimeOutMS / portTICK_PERIOD_MS);
}

View File

@@ -0,0 +1,150 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: porttimer_m.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbport.h"
#include "sdkconfig.h"
static const char *TAG = "MBM_TIMER";
/* ----------------------- Variables ----------------------------------------*/
static xTimerContext_t* pxTimerContext = NULL;
/* ----------------------- Start implementation -----------------------------*/
static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
{
pxMBMasterPortCBTimerExpired(); // Timer expired callback function
pxTimerContext->xTimerState = TRUE;
ESP_EARLY_LOGD(TAG, "Timer mode: (%u) triggered", (unsigned)xMBMasterGetCurTimerMode());
}
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
{
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
"Modbus timeout discreet is incorrect.");
MB_PORT_CHECK(!pxTimerContext, FALSE,
"Modbus timer is already created.");
pxTimerContext = calloc(1, sizeof(xTimerContext_t));
if (!pxTimerContext) {
return FALSE;
}
pxTimerContext->xTimerIntHandle = NULL;
// Save timer reload value for Modbus T35 period
pxTimerContext->usT35Ticks = usTimeOut50us;
esp_timer_create_args_t xTimerConf = {
.callback = vTimerAlarmCBHandler,
.arg = NULL,
#if (MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD && CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD)
.dispatch_method = ESP_TIMER_ISR,
#else
.dispatch_method = ESP_TIMER_TASK,
#endif
.name = "MBM_T35timer"
};
// Create Modbus timer
esp_err_t xErr = esp_timer_create(&xTimerConf, &(pxTimerContext->xTimerIntHandle));
if (xErr) {
return FALSE;
}
return TRUE;
}
// Set timer alarm value
static BOOL xMBMasterPortTimersEnable(uint64_t xToutUs)
{
MB_PORT_CHECK(pxTimerContext && (pxTimerContext->xTimerIntHandle), FALSE,
"timer is not initialized.");
MB_PORT_CHECK((xToutUs > 0), FALSE,
"incorrect tick value for timer = (0x%llu).", xToutUs);
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_start_once(pxTimerContext->xTimerIntHandle, xToutUs);
pxTimerContext->xTimerState = FALSE;
return TRUE;
}
void vMBMasterPortTimersT35Enable(void)
{
uint64_t xToutUs = (pxTimerContext->usT35Ticks * MB_TIMER_TICK_TIME_US);
// Set current timer mode, don't change it.
vMBMasterSetCurTimerMode(MB_TMODE_T35);
// Set timer alarm
(void)xMBMasterPortTimersEnable(xToutUs);
}
void vMBMasterPortTimersConvertDelayEnable(void)
{
// Covert time in milliseconds into ticks
uint64_t xToutUs = (MB_MASTER_DELAY_MS_CONVERT * 1000);
// Set current timer mode
vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);
ESP_LOGD(MB_PORT_TAG,"%s Convert delay enable.", __func__);
(void)xMBMasterPortTimersEnable(xToutUs);
}
void vMBMasterPortTimersRespondTimeoutEnable(void)
{
uint64_t xToutUs = (MB_MASTER_TIMEOUT_MS_RESPOND * 1000);
vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);
ESP_LOGD(MB_PORT_TAG,"%s Respond enable timeout.", __func__);
(void)xMBMasterPortTimersEnable(xToutUs);
}
void MB_PORT_ISR_ATTR
vMBMasterPortTimersDisable()
{
// Disable timer alarm
esp_timer_stop(pxTimerContext->xTimerIntHandle);
}
void vMBMasterPortTimerClose(void)
{
// Delete active timer
if (pxTimerContext) {
if (pxTimerContext->xTimerIntHandle) {
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_delete(pxTimerContext->xTimerIntHandle);
}
free(pxTimerContext);
pxTimerContext = NULL;
}
}

View File

@@ -0,0 +1,735 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_master.c
// Serial master implementation of the Modbus controller
#include <sys/time.h> // for calculation of time stamp in milliseconds
#include "esp_log.h" // for log_write
#include <string.h> // for memcpy
#include "freertos/FreeRTOS.h" // for task creation and queue access
#include "freertos/task.h" // for task api access
#include "freertos/event_groups.h" // for event groups
#include "freertos/queue.h" // for queue api access
#include "mb_m.h" // for modbus stack master types definition
#include "port.h" // for port callback functions
#include "mbutils.h" // for mbutils functions definition for stack callback
#include "sdkconfig.h" // for KConfig values
#include "esp_modbus_common.h" // for common types
#include "esp_modbus_master.h" // for public master types
#include "mbc_master.h" // for private master types
#include "mbc_serial_master.h" // for serial master create function and types
// The Modbus Transmit Poll function defined in port
extern BOOL xMBMasterPortSerialTxPoll(void);
/*-----------------------Master mode use these variables----------------------*/
// Actual wait time depends on the response timer
#define MB_SERIAL_API_RESP_TICS (pdMS_TO_TICKS(MB_MAX_RESPONSE_TIME_MS))
static mb_master_interface_t* mbm_interface_ptr = NULL;
static const char *TAG = "MB_CONTROLLER_MASTER";
// Modbus event processing task
static void modbus_master_task(void *pvParameters)
{
// The interface must be initialized before start of state machine
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
// Main Modbus stack processing cycle
for (;;) {
// Wait for poll events
BaseType_t status = xEventGroupWaitBits(mbm_opts->mbm_event_group,
(BaseType_t)(MB_EVENT_STACK_STARTED),
pdFALSE, // do not clear bits
pdFALSE,
portMAX_DELAY);
// Check if stack started then poll for data
if (status & MB_EVENT_STACK_STARTED) {
(void)eMBMasterPoll(); // Allow stack to process data
// Send response buffer if ready to be sent
BOOL xSentState = xMBMasterPortSerialTxPoll();
if (xSentState) {
// Let state machine know that request frame was transmitted out
(void)xMBMasterPortEventPost(EV_MASTER_FRAME_SENT);
}
}
}
}
// Setup Modbus controller parameters
static esp_err_t mbc_serial_master_setup(void* comm_info)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface uninitialized.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
const mb_master_comm_info_t* comm_info_ptr = (mb_master_comm_info_t*)comm_info;
// Check communication options
MB_MASTER_CHECK(((comm_info_ptr->mode == MB_MODE_RTU) || (comm_info_ptr->mode == MB_MODE_ASCII)),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).", (unsigned)comm_info_ptr->mode);
MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_MAX), ESP_ERR_INVALID_ARG,
"mb wrong port to set = (%u).", (unsigned)comm_info_ptr->port);
MB_MASTER_CHECK((comm_info_ptr->parity <= UART_PARITY_ODD), ESP_ERR_INVALID_ARG,
"mb wrong parity option = (%u).", (unsigned)comm_info_ptr->parity);
// Save the communication options
mbm_opts->mbm_comm = *(mb_communication_info_t*)comm_info_ptr;
return ESP_OK;
}
// Modbus controller stack start function
static esp_err_t mbc_serial_master_start(void)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface uninitialized.");
eMBErrorCode status = MB_EIO;
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbm_opts->mbm_comm;
// Initialize Modbus stack using mbcontroller parameters
status = eMBMasterSerialInit((eMBMode)comm_info->mode, (UCHAR)comm_info->port,
(ULONG)comm_info->baudrate,
MB_PORT_PARITY_GET(comm_info->parity));
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack initialization failure, eMBInit() returns (0x%x).", (int)status);
status = eMBMasterEnable();
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack set slave ID failure, eMBMasterEnable() returned (0x%x).", (int)status);
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupSetBits(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
return ESP_OK;
}
// Modbus controller destroy function
static esp_err_t mbc_serial_master_destroy(void)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface uninitialized.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
eMBErrorCode mb_error = MB_ENOERR;
// Stop polling by clearing correspondent bit in the event group
EventBits_t flag = xEventGroupClearBits(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Desable and then destroy the Modbus stack
mb_error = eMBMasterDisable();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
(void)vTaskDelete(mbm_opts->mbm_task_handle);
(void)vEventGroupDelete(mbm_opts->mbm_event_group);
mb_error = eMBMasterClose();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (int)mb_error);
free(mbm_interface_ptr); // free the memory allocated for options
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
mbm_interface_ptr = NULL;
return ESP_OK;
}
// Set Modbus parameter description table
static esp_err_t mbc_serial_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements)
{
MB_MASTER_CHECK((descriptor != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((num_elements >= 1),
ESP_ERR_INVALID_ARG, "mb table size is incorrect.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
const mb_parameter_descriptor_t *reg_ptr = descriptor;
// Go through all items in the table to check all Modbus registers
for (uint16_t counter = 0; counter < (num_elements); counter++, reg_ptr++)
{
// Below is the code to check consistency of the table format and required fields.
MB_MASTER_CHECK((reg_ptr->cid == counter),
ESP_ERR_INVALID_ARG, "mb descriptor cid field is incorrect.");
MB_MASTER_CHECK((reg_ptr->param_key != NULL),
ESP_ERR_INVALID_ARG, "mb descriptor param key is incorrect.");
MB_MASTER_CHECK((reg_ptr->mb_size > 0),
ESP_ERR_INVALID_ARG, "mb descriptor param size is incorrect.");
}
mbm_opts->mbm_param_descriptor_table = descriptor;
mbm_opts->mbm_param_descriptor_size = num_elements;
return ESP_OK;
}
// Send custom Modbus request defined as mb_param_request_t structure
static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, void* data_ptr)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface uninitialized.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((request != NULL),
ESP_ERR_INVALID_ARG, "mb request structure.");
MB_MASTER_CHECK((data_ptr != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect data pointer.");
eMBMasterReqErrCode mb_error = MB_MRE_MASTER_BUSY;
esp_err_t error = ESP_FAIL;
if (xMBMasterRunResTake(MB_SERIAL_API_RESP_TICS)) {
uint8_t mb_slave_addr = request->slave_addr;
uint8_t mb_command = request->command;
uint16_t mb_offset = request->reg_start;
uint16_t mb_size = request->reg_size;
// Set the buffer for callback function processing of received data
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
mbm_opts->mbm_reg_buffer_size = mb_size;
vMBMasterRunResRelease();
// Calls appropriate request function to send request and waits response
switch(mb_command)
{
case MB_FUNC_READ_COILS:
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size , (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_SINGLE_COIL:
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_MULTIPLE_COILS:
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (UCHAR*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS);
break;
case MB_FUNC_READ_DISCRETE_INPUTS:
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_READ_HOLDING_REGISTER:
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_REGISTER:
mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
(USHORT)mb_offset, (USHORT)mb_size,
(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (USHORT*)data_ptr,
(USHORT)mb_offset, (USHORT)mb_size,
(LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_READ_INPUT_REGISTER:
mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG) MB_SERIAL_API_RESP_TICS );
break;
default:
ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ", __FUNCTION__, (unsigned)mb_command);
mb_error = MB_MRE_NO_REG;
break;
}
}
// Propagate the Modbus errors to higher level
switch(mb_error)
{
case MB_MRE_NO_ERR:
error = ESP_OK;
break;
case MB_MRE_NO_REG:
error = ESP_ERR_NOT_SUPPORTED; // Invalid register request
break;
case MB_MRE_TIMEDOUT:
error = ESP_ERR_TIMEOUT; // Slave did not send response
break;
case MB_MRE_EXE_FUN:
case MB_MRE_REV_DATA:
error = ESP_ERR_INVALID_RESPONSE; // Invalid response from slave
break;
case MB_MRE_MASTER_BUSY:
error = ESP_ERR_INVALID_STATE; // Master is busy (previous request is pending)
break;
default:
ESP_LOGE(TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, (int)mb_error);
error = ESP_FAIL;
break;
}
return error;
}
static esp_err_t mbc_serial_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_buffer)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface uninitialized.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((param_buffer != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect data buffer pointer.");
MB_MASTER_CHECK((mbm_opts->mbm_param_descriptor_table != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect descriptor table or not set.");
MB_MASTER_CHECK((cid < mbm_opts->mbm_param_descriptor_size),
ESP_ERR_NOT_FOUND, "mb incorrect cid of characteristic.");
// It is assumed that characteristics cid increased in the table
const mb_parameter_descriptor_t* reg_info = &mbm_opts->mbm_param_descriptor_table[cid];
MB_MASTER_CHECK((reg_info->param_key != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect characteristic key.");
*param_buffer = reg_info;
return ESP_OK;
}
// Helper function to get modbus command for each type of Modbus register area
static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_param_mode_t mode)
{
uint8_t command = 0;
switch(param_type)
{ //
case MB_PARAM_HOLDING:
command = (mode == MB_PARAM_WRITE) ?
MB_FUNC_WRITE_MULTIPLE_REGISTERS :
MB_FUNC_READ_HOLDING_REGISTER;
break;
case MB_PARAM_INPUT:
command = MB_FUNC_READ_INPUT_REGISTER;
break;
case MB_PARAM_COIL:
command = (mode == MB_PARAM_WRITE) ?
MB_FUNC_WRITE_MULTIPLE_COILS :
MB_FUNC_READ_COILS;
break;
case MB_PARAM_DISCRETE:
if (mode != MB_PARAM_WRITE) {
command = MB_FUNC_READ_DISCRETE_INPUTS;
} else {
ESP_LOGE(TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (unsigned)mode);
}
break;
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, (unsigned)param_type);
break;
}
return command;
}
// Helper to search parameter by name in the parameter description table
// and fills Modbus request fields accordingly
static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode,
mb_param_request_t* request,
mb_parameter_descriptor_t* reg_data)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface uninitialized.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
esp_err_t error = ESP_ERR_NOT_FOUND;
MB_MASTER_CHECK((name != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect parameter name.");
MB_MASTER_CHECK((request != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect request parameter.");
MB_MASTER_CHECK((mode <= MB_PARAM_WRITE),
ESP_ERR_INVALID_ARG, "mb incorrect mode.");
MB_MASTER_ASSERT(mbm_opts->mbm_param_descriptor_table != NULL);
const mb_parameter_descriptor_t* reg_ptr = mbm_opts->mbm_param_descriptor_table;
for (uint16_t counter = 0; counter < (mbm_opts->mbm_param_descriptor_size); counter++, reg_ptr++)
{
// Check the cid of the parameter is equal to record number in the table
// Check the length of name and parameter key strings from table
size_t param_key_len = strlen((const char*)reg_ptr->param_key);
if (param_key_len != strlen((const char*)name)) {
continue; // The length of strings is different then check next record in the table
}
// Compare the name of parameter with parameter key from table
int comp_result = memcmp((const void*)name, (const void*)reg_ptr->param_key, (size_t)param_key_len);
if (comp_result == 0) {
// Returns an error in case of broadcast read request
if (!reg_ptr->mb_slave_addr && (mode == MB_PARAM_READ)) {
error = ESP_ERR_INVALID_ARG;
break;
}
// The correct line is found in the table and reg_ptr points to the found parameter description
request->slave_addr = reg_ptr->mb_slave_addr;
request->reg_start = reg_ptr->mb_reg_start;
request->reg_size = reg_ptr->mb_size;
request->command = mbc_serial_master_get_command(reg_ptr->mb_param_type, mode);
MB_MASTER_CHECK((request->command > 0), ESP_ERR_INVALID_ARG,
"mb incorrect command or parameter type.");
if (reg_data != NULL) {
*reg_data = *reg_ptr; // Set the cid registered parameter data
}
error = ESP_OK;
break;
}
}
return error;
}
static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type)
{
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t* pdata = NULL;
error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid)) {
MB_MASTER_CHECK(((reg_info.mb_size << 1) >= reg_info.param_size),
MB_EILLSTATE,
"Incorrect characteristic data.");
// alloc buffer to store parameter data
pdata = calloc(1, (reg_info.mb_size << 1));
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
error = mbc_serial_master_send_request(&request, pdata);
if (error == ESP_OK) {
// If data pointer is NULL then we don't need to set value (it is still in the cache of cid)
if (value != NULL) {
error = mbc_master_set_param_data((void*)value, (void*)pdata,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
error = ESP_ERR_INVALID_STATE;
} else {
ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
}
} else {
ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
free(pdata);
// Set the type of parameter found in the table
*type = reg_info.param_type;
} else {
ESP_LOGE(TAG, "%s: The requested cid(%u) is not configured correctly. Check data dictionary for correctness.",
__FUNCTION__, (unsigned)cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
}
// Set parameter value for characteristic selected by name and cid
static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type)
{
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t* pdata = NULL;
error = mbc_serial_master_set_request(name, MB_PARAM_WRITE, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid)) {
MB_MASTER_CHECK(((reg_info.mb_size << 1) >= reg_info.param_size),
MB_EILLSTATE,
"Incorrect characteristic data.");
pdata = calloc(1, (reg_info.mb_size << 1)); // alloc parameter buffer
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
// Transfer value of characteristic into parameter buffer
error = mbc_master_set_param_data((void*)pdata, (void*)value,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
free(pdata);
return ESP_ERR_INVALID_STATE;
}
// Send request to write characteristic data
error = mbc_serial_master_send_request(&request, pdata);
if (error == ESP_OK) {
ESP_LOGD(TAG, "%s: Good response for set cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
} else {
ESP_LOGD(TAG, "%s: Bad response to set cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
// Set the type of parameter found in the table
*type = reg_info.param_type;
free(pdata);
} else {
ESP_LOGE(TAG, "%s: The requested cid(%u) is not configured correctly. Check data dictionary for correctness.",
__FUNCTION__, (unsigned)cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
}
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
// These are executed by modbus stack to read appropriate type of registers.
/**
* Modbus master input register callback function.
*
* @param pucRegBuffer input register buffer
* @param usAddress input register address
* @param usNRegs input register number
*
* @return result
*/
// Callback function for reading of MB Input Registers
eMBErrorCode eMBRegInputCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
MB_EILLSTATE,
"Master interface uninitialized.");
MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
"Master stack processing error.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
// Number of input registers to be transferred
USHORT usRegInputNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR* pucInputBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr; // Get instance address
USHORT usRegs = usNRegs;
eMBErrorCode eStatus = MB_ENOERR;
// If input or configuration parameters are incorrect then return an error to stack layer
if ((pucInputBuffer != NULL)
&& (usNRegs >= 1)
&& (usRegInputNregs == usRegs)) {
while (usRegs > 0) {
_XFER_2_RD(pucInputBuffer, pucRegBuffer);
usRegs -= 1;
}
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master holding register callback function.
*
* @param pucRegBuffer holding register buffer
* @param usAddress holding register address
* @param usNRegs holding register number
* @param eMode read or write
*
* @return result
*/
// Callback function for reading of MB Holding Registers
// Executed by stack when request to read/write holding registers is received
eMBErrorCode eMBRegHoldingCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
MB_EILLSTATE,
"Master interface uninitialized.");
MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
"Master stack processing error.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
USHORT usRegHoldingNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR* pucHoldingBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
eMBErrorCode eStatus = MB_ENOERR;
USHORT usRegs = usNRegs;
// Check input and configuration parameters for correctness
if ((pucHoldingBuffer != NULL)
&& (usRegHoldingNregs == usNRegs)
&& (usNRegs >= 1)) {
switch (eMode) {
case MB_REG_WRITE:
while (usRegs > 0) {
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
usRegs -= 1;
};
break;
case MB_REG_READ:
while (usRegs > 0) {
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
pucHoldingBuffer += 2;
usRegs -= 1;
};
break;
}
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master coils callback function.
*
* @param pucRegBuffer coils buffer
* @param usAddress coils address
* @param usNCoils coils number
* @param eMode read or write
*
* @return result
*/
// Callback function for reading of MB Coils Registers
eMBErrorCode eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
MB_EILLSTATE, "Master interface uninitialized.");
MB_MASTER_CHECK((pucRegBuffer != NULL),
MB_EINVAL, "Master stack processing error.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
USHORT usRegCoilNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR* pucRegCoilsBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT usCoils = usNCoils;
usAddress--; // The address is already + 1
if ((usRegCoilNregs >= 1)
&& (pucRegCoilsBuf != NULL)
&& (usNCoils == usRegCoilNregs)) {
iRegIndex = (usAddress % 8);
switch (eMode) {
case MB_REG_WRITE:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress % 8) , 1, ucResult);
iRegIndex++;
usCoils--;
}
break;
case MB_REG_READ:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1, ucResult);
iRegIndex++;
usCoils--;
}
break;
} // switch ( eMode )
} else {
// If the configuration or input parameters are incorrect then return error to stack
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master discrete callback function.
*
* @param pucRegBuffer discrete buffer
* @param usAddress discrete address
* @param usNDiscrete discrete number
*
* @return result
*/
// Callback function for reading of MB Discrete Input Registers
eMBErrorCode eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
MB_EILLSTATE, "Master interface uninitialized.");
MB_MASTER_CHECK((pucRegBuffer != NULL),
MB_EINVAL, "Master stack processing error.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
USHORT usRegDiscreteNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegBitIndex, iNReg;
UCHAR* pucDiscreteInputBuf;
iNReg = usNDiscrete / 8 + 1;
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
// It is already plus one in Modbus function method.
usAddress--;
if ((usRegDiscreteNregs >= 1)
&& (pucRegDiscreteBuf != NULL)
&& (usNDiscrete >= 1)) {
iRegBitIndex = (USHORT)(usAddress) % 8; // Get bit index
while (iNReg > 1)
{
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex - (usAddress % 8), 8, *pucRegBuffer++);
iNReg--;
}
// last discrete
usNDiscrete = usNDiscrete % 8;
// xMBUtilSetBits has bug when ucNBits is zero
if (usNDiscrete != 0)
{
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex - (usAddress % 8), usNDiscrete, *pucRegBuffer++);
}
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
// Initialization of resources for Modbus serial master controller
esp_err_t mbc_serial_master_create(void** handler)
{
// Allocate space for master interface structure
if (mbm_interface_ptr == NULL) {
mbm_interface_ptr = malloc(sizeof(mb_master_interface_t));
}
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
// Initialize interface properties
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
mbm_opts->port_type = MB_PORT_SERIAL_MASTER;
vMBPortSetMode((UCHAR)MB_PORT_SERIAL_MASTER);
mbm_opts->mbm_comm.mode = MB_MODE_RTU;
mbm_opts->mbm_comm.port = MB_UART_PORT;
mbm_opts->mbm_comm.baudrate = MB_DEVICE_SPEED;
mbm_opts->mbm_comm.parity = MB_PARITY_NONE;
// Initialization of active context of the modbus controller
BaseType_t status = 0;
// Parameter change notification queue
mbm_opts->mbm_event_group = xEventGroupCreate();
MB_MASTER_CHECK((mbm_opts->mbm_event_group != NULL),
ESP_ERR_NO_MEM, "mb event group error.");
// Create modbus controller task
status = xTaskCreatePinnedToCore((void*)&modbus_master_task,
"modbus_matask",
MB_CONTROLLER_STACK_SIZE,
NULL, // No parameters
MB_CONTROLLER_PRIORITY,
&mbm_opts->mbm_task_handle,
MB_PORT_TASK_AFFINITY);
if (status != pdPASS) {
vTaskDelete(mbm_opts->mbm_task_handle);
MB_MASTER_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (0x%x).", (int)status);
}
MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect
// Initialize public interface methods of the interface
mbm_interface_ptr->init = mbc_serial_master_create;
mbm_interface_ptr->destroy = mbc_serial_master_destroy;
mbm_interface_ptr->setup = mbc_serial_master_setup;
mbm_interface_ptr->start = mbc_serial_master_start;
mbm_interface_ptr->get_cid_info = mbc_serial_master_get_cid_info;
mbm_interface_ptr->get_parameter = mbc_serial_master_get_parameter;
mbm_interface_ptr->send_request = mbc_serial_master_send_request;
mbm_interface_ptr->set_descriptor = mbc_serial_master_set_descriptor;
mbm_interface_ptr->set_parameter = mbc_serial_master_set_parameter;
mbm_interface_ptr->master_reg_cb_discrete = eMBRegDiscreteCBSerialMaster;
mbm_interface_ptr->master_reg_cb_input = eMBRegInputCBSerialMaster;
mbm_interface_ptr->master_reg_cb_holding = eMBRegHoldingCBSerialMaster;
mbm_interface_ptr->master_reg_cb_coils = eMBRegCoilsCBSerialMaster;
*handler = mbm_interface_ptr;
return ESP_OK;
}

View File

@@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_master.h Modbus controller serial master implementation header file
#ifndef _MODBUS_SERIAL_CONTROLLER_MASTER
#define _MODBUS_SERIAL_CONTROLLER_MASTER
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "soc/soc.h" // for BITN definitions
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_common.h" // for common defines
/**
* @brief Initialize Modbus controller and stack
*
* @param[out] handler handler(pointer) to master data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
*/
esp_err_t mbc_serial_master_create(void** handler);
#endif // _MODBUS_SERIAL_CONTROLLER_MASTER

View File

@@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
#ifndef _PORT_SERIAL_MASTER_H
#define _PORT_SERIAL_MASTER_H
/* ----------------------- Platform includes --------------------------------*/
#include "driver/uart.h"
#include "esp_log.h" // for ESP_LOGE macro
#include "mb_m.h"
#include "port.h"
/* ----------------------- Defines ------------------------------------------*/
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif /* __cplusplus */
void vMBPortSetMode( UCHAR ucMode );
#ifdef __cplusplus
PR_END_EXTERN_C
#endif /* __cplusplus */
#endif

View File

@@ -0,0 +1,237 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_slave.c
// Implementation of the Modbus controller serial slave
#include <sys/time.h> // for calculation of time stamp in milliseconds
#include "esp_log.h" // for log_write
#include "mb.h" // for mb types definition
#include "mbutils.h" // for mbutils functions definition for stack callback
#include "sdkconfig.h" // for KConfig values
#include "esp_modbus_common.h" // for common defines
#include "esp_modbus_slave.h" // for public slave interface types
#include "mbc_slave.h" // for private slave interface types
#include "mbc_serial_slave.h" // for serial slave implementation definitions
#include "port_serial_slave.h"
// Shared pointer to interface structure
static mb_slave_interface_t* mbs_interface_ptr = NULL;
static const char *TAG = "MB_CONTROLLER_SLAVE";
// Modbus task function
static void modbus_slave_task(void *pvParameters)
{
// Modbus interface must be initialized before start
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_ASSERT(mbs_opts != NULL);
// Main Modbus stack processing cycle
for (;;) {
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group,
(BaseType_t)(MB_EVENT_STACK_STARTED),
pdFALSE, // do not clear bits
pdFALSE,
portMAX_DELAY);
// Check if stack started then poll for data
if (status & MB_EVENT_STACK_STARTED) {
(void)eMBPoll(); // allow stack to process data
// Send response buffer
BOOL xSentState = xMBPortSerialTxPoll();
if (xSentState) {
(void)xMBPortEventPost( EV_FRAME_SENT );
}
}
}
}
// Setup Modbus controller parameters
static esp_err_t mbc_serial_slave_setup(void* comm_info)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((comm_info != NULL), ESP_ERR_INVALID_ARG,
"mb wrong communication settings.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
mb_slave_comm_info_t* comm_settings = (mb_slave_comm_info_t*)comm_info;
MB_SLAVE_CHECK(((comm_settings->mode == MB_MODE_RTU) || (comm_settings->mode == MB_MODE_ASCII)),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).",
(unsigned)comm_settings->mode);
MB_SLAVE_CHECK((comm_settings->slave_addr <= MB_ADDRESS_MAX),
ESP_ERR_INVALID_ARG, "mb wrong slave address = (%u).", (unsigned)comm_settings->slave_addr);
MB_SLAVE_CHECK((comm_settings->port < UART_NUM_MAX), ESP_ERR_INVALID_ARG,
"mb wrong port to set = (%u).", (unsigned)comm_settings->port);
MB_SLAVE_CHECK((comm_settings->parity <= UART_PARITY_ODD), ESP_ERR_INVALID_ARG,
"mb wrong parity option = (%u).", (unsigned)comm_settings->parity);
// Set communication options of the controller
mbs_opts->mbs_comm = *(mb_communication_info_t*)comm_settings;
return ESP_OK;
}
// Start Modbus controller start function
static esp_err_t mbc_serial_slave_start(void)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
eMBErrorCode status = MB_EIO;
const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbs_opts->mbs_comm;
// Initialize Modbus stack using mbcontroller parameters
status = eMBInit((eMBMode)comm_info->mode,
(UCHAR)comm_info->slave_addr,
(UCHAR)comm_info->port,
(ULONG)comm_info->baudrate,
MB_PORT_PARITY_GET(comm_info->parity));
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack initialization failure, eMBInit() returns (0x%x).", (int)status);
status = eMBEnable();
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack set slave ID failure, eMBEnable() returned (0x%x).", (int)status);
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupSetBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
return ESP_OK;
}
// Blocking function to get event on parameter group change for application task
static mb_event_group_t mbc_serial_slave_check_event(mb_event_group_t group)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_ASSERT(mbs_opts->mbs_event_group != NULL);
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group, (BaseType_t)group,
pdTRUE , pdFALSE, portMAX_DELAY);
return (mb_event_group_t)status;
}
// Function to get notification about parameter change from application task
static esp_err_t mbc_serial_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
esp_err_t err = ESP_ERR_TIMEOUT;
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
MB_SLAVE_CHECK((reg_info != NULL), ESP_ERR_INVALID_ARG, "mb register information is invalid.");
BaseType_t status = xQueueReceive(mbs_opts->mbs_notification_queue_handle,
reg_info, pdMS_TO_TICKS(timeout));
if (status == pdTRUE) {
err = ESP_OK;
}
return err;
}
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
// These are executed by modbus stack to read appropriate type of registers.
// This is required to suppress warning when register start address is zero
#pragma GCC diagnostic ignored "-Wtype-limits"
// Modbus controller destroy function
static esp_err_t mbc_serial_slave_destroy(void)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
eMBErrorCode mb_error = MB_ENOERR;
// Stop polling by clearing correspondent bit in the event group
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Disable and then destroy the Modbus stack
mb_error = eMBDisable();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
(void)vTaskDelete(mbs_opts->mbs_task_handle);
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
mb_error = eMBClose();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (int)mb_error);
mbs_interface_ptr = NULL;
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
return ESP_OK;
}
// Initialization of Modbus controller
esp_err_t mbc_serial_slave_create(void** handler)
{
// Allocate space for options
if (mbs_interface_ptr == NULL) {
mbs_interface_ptr = malloc(sizeof(mb_slave_interface_t));
}
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
vMBPortSetMode((UCHAR)MB_PORT_SERIAL_SLAVE);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
mbs_opts->port_type = MB_PORT_SERIAL_SLAVE; // set interface port type
// Set default values of communication options
mbs_opts->mbs_comm.mode = MB_MODE_RTU;
mbs_opts->mbs_comm.slave_addr = MB_DEVICE_ADDRESS;
mbs_opts->mbs_comm.port = MB_UART_PORT;
mbs_opts->mbs_comm.baudrate = MB_DEVICE_SPEED;
mbs_opts->mbs_comm.parity = MB_PARITY_NONE;
// Initialization of active context of the Modbus controller
BaseType_t status = 0;
// Parameter change notification queue
mbs_opts->mbs_event_group = xEventGroupCreate();
MB_SLAVE_CHECK((mbs_opts->mbs_event_group != NULL),
ESP_ERR_NO_MEM, "mb event group error.");
// Parameter change notification queue
mbs_opts->mbs_notification_queue_handle = xQueueCreate(
MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
sizeof(mb_param_info_t));
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
ESP_ERR_NO_MEM, "mb notify queue creation error.");
// Create Modbus controller task
status = xTaskCreatePinnedToCore((void*)&modbus_slave_task,
"modbus_slave_task",
MB_CONTROLLER_STACK_SIZE,
NULL,
MB_CONTROLLER_PRIORITY,
&mbs_opts->mbs_task_handle,
MB_PORT_TASK_AFFINITY);
if (status != pdPASS) {
vTaskDelete(mbs_opts->mbs_task_handle);
MB_SLAVE_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (0x%x).", (int)status);
}
MB_SLAVE_ASSERT(mbs_opts->mbs_task_handle != NULL); // The task is created but handle is incorrect
// Initialize interface function pointers
mbs_interface_ptr->check_event = mbc_serial_slave_check_event;
mbs_interface_ptr->destroy = mbc_serial_slave_destroy;
mbs_interface_ptr->get_param_info = mbc_serial_slave_get_param_info;
mbs_interface_ptr->init = mbc_serial_slave_create;
mbs_interface_ptr->set_descriptor = NULL; // Use common set descriptor function
mbs_interface_ptr->setup = mbc_serial_slave_setup;
mbs_interface_ptr->start = mbc_serial_slave_start;
// Initialize stack callback function pointers
mbs_interface_ptr->slave_reg_cb_discrete = NULL; // implemented in common layer
mbs_interface_ptr->slave_reg_cb_input = NULL;
mbs_interface_ptr->slave_reg_cb_holding = NULL;
mbs_interface_ptr->slave_reg_cb_coils = NULL;
*handler = (void*)mbs_interface_ptr;
return ESP_OK;
}

View File

@@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_slave.h Modbus controller serial slave implementation header file
#ifndef _MODBUS_SERIAL_CONTROLLER_SLAVE
#define _MODBUS_SERIAL_CONTROLLER_SLAVE
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "esp_modbus_common.h" // for common defines
/* ----------------------- Defines ------------------------------------------*/
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
/*
* @brief Initialize Modbus controller and stack
*
* @param[out] handler handler(pointer) to master data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
*/
esp_err_t mbc_serial_slave_create(void** handler);
#endif // _MODBUS_SERIAL_CONTROLLER_SLAVE

View File

@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
#ifndef _PORT_SERIAL_SLAVE_H
#define _PORT_SERIAL_SLAVE_H
/* ----------------------- Platform includes --------------------------------*/
#include "driver/uart.h"
#include "esp_log.h"
#include "port.h"
/* ----------------------- Defines ------------------------------------------*/
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif /* __cplusplus */
BOOL xMBPortSerialTxPoll( void );
void vMBPortSetMode( UCHAR ucMode );
#ifdef __cplusplus
PR_END_EXTERN_C
#endif /* __cplusplus */
#endif

View File

@@ -0,0 +1,785 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_master.c
// TCP master implementation of the Modbus controller
#include <sys/time.h> // for calculation of time stamp in milliseconds
#include "esp_log.h" // for log_write
#include <string.h> // for memcpy
#include <sys/queue.h> // for list
#include "freertos/FreeRTOS.h" // for task creation and queue access
#include "freertos/task.h" // for task api access
#include "freertos/event_groups.h" // for event groups
#include "freertos/queue.h" // for queue api access
#include "mb_m.h" // for modbus stack master types definition
#include "port.h" // for port callback functions and defines
#include "mbutils.h" // for mbutils functions definition for stack callback
#include "sdkconfig.h" // for KConfig values
#include "esp_modbus_common.h" // for common types
#include "esp_modbus_master.h" // for public master types
#include "mbc_master.h" // for private master types
#include "mbc_tcp_master.h" // for tcp master create function and types
#include "port_tcp_master.h" // for tcp master port defines and types
#if MB_MASTER_TCP_ENABLED
/*-----------------------Master mode use these variables----------------------*/
#define MB_TCP_CONNECTION_TOUT (pdMS_TO_TICKS(CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000))
// Actual wait time depends on the response timer
#define MB_TCP_API_RESP_TICS (pdMS_TO_TICKS(MB_MAX_RESPONSE_TIME_MS))
static mb_master_interface_t* mbm_interface_ptr = NULL;
static const char *TAG = "MB_CONTROLLER_MASTER";
// Searches the slave address in the address info list and returns address info if found, else NULL
static mb_slave_addr_entry_t* mbc_tcp_master_find_slave_addr(uint8_t slave_addr)
{
mb_slave_addr_entry_t* it;
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
if (LIST_EMPTY(&mbm_opts->mbm_slave_list)) {
return NULL;
}
LIST_FOREACH(it, &mbm_opts->mbm_slave_list, entries) {
if (slave_addr == it->slave_addr) {
return it;
}
}
return NULL;
}
static esp_err_t mbc_tcp_master_add_slave(uint16_t index, uint8_t slave_addr, const char* ip_addr)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
// Initialize interface properties
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
mb_slave_addr_entry_t* new_slave_entry = (mb_slave_addr_entry_t*) heap_caps_malloc(sizeof(mb_slave_addr_entry_t),
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
MB_MASTER_CHECK((new_slave_entry != NULL), ESP_ERR_NO_MEM, "mb can not allocate memory for slave entry.");
new_slave_entry->index = index;
new_slave_entry->ip_address = ip_addr;
new_slave_entry->slave_addr = slave_addr;
new_slave_entry->p_data = NULL;
LIST_INSERT_HEAD(&mbm_opts->mbm_slave_list, new_slave_entry, entries);
MB_MASTER_CHECK((mbm_opts->mbm_slave_list_count < (MB_TCP_PORT_MAX_CONN - 1)),
ESP_ERR_INVALID_STATE, "mb max number of slaves < %d.", MB_TCP_PORT_MAX_CONN);
mbm_opts->mbm_slave_list_count++;
return ESP_OK;
}
static void mbc_tcp_master_free_slave_list(void)
{
mb_slave_addr_entry_t* it;
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
// Initialize interface properties
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
while ((it = LIST_FIRST(&mbm_opts->mbm_slave_list))) {
LIST_REMOVE(it, entries);
mbm_opts->mbm_slave_list_count--;
free(it);
}
}
// Modbus event processing task
static void modbus_tcp_master_task(void *pvParameters)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_ASSERT(mbm_opts != NULL);
// Main Modbus stack processing cycle
for (;;) {
// Wait for poll events
BaseType_t status = xEventGroupWaitBits(mbm_opts->mbm_event_group,
(BaseType_t)(MB_EVENT_STACK_STARTED),
pdFALSE, // do not clear bits
pdFALSE,
portMAX_DELAY);
// Check if stack started then poll for data
if (status & MB_EVENT_STACK_STARTED) {
(void)eMBMasterPoll(); // Allow stack to process data
}
}
}
// Setup Modbus controller parameters
static esp_err_t mbc_tcp_master_setup(void* comm_info)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb incorrect options pointer.");
const mb_communication_info_t* comm_info_ptr = (mb_communication_info_t*)comm_info;
// Check communication options
MB_MASTER_CHECK((comm_info_ptr->ip_mode == MB_MODE_TCP),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).", (unsigned)comm_info_ptr->ip_mode);
MB_MASTER_CHECK((comm_info_ptr->ip_addr != NULL),
ESP_ERR_INVALID_ARG, "mb wrong slave ip address table.");
MB_MASTER_CHECK(((comm_info_ptr->ip_addr_type == MB_IPV4) || (comm_info_ptr->ip_addr_type == MB_IPV6)),
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (%u).", (unsigned)comm_info_ptr->ip_addr_type);
MB_MASTER_CHECK((comm_info_ptr->ip_netif_ptr != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect iface address.");
// Save the communication options
mbm_opts->mbm_comm = *(mb_communication_info_t*)comm_info_ptr;
return ESP_OK;
}
// Modbus controller stack start function
static esp_err_t mbc_tcp_master_start(void)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
eMBErrorCode status = MB_EIO;
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb incorrect options pointer.");
const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbm_opts->mbm_comm;
// Initialize Modbus stack using mbcontroller parameters
status = eMBMasterTCPInit((USHORT)comm_info->ip_port);
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack initialization failure, eMBMasterInit() returns (0x%x).", status);
MB_MASTER_CHECK((mbm_opts->mbm_param_descriptor_size >= 1), ESP_ERR_INVALID_ARG, "mb table size is incorrect.");
bool result = false;
const char** comm_ip_table = (const char**)comm_info->ip_addr;
MB_MASTER_CHECK((comm_ip_table != NULL), ESP_ERR_INVALID_ARG, "mb ip table address is incorrect.");
eMBPortProto proto = (comm_info->ip_mode == MB_MODE_TCP) ? MB_PROTO_TCP : MB_PROTO_UDP;
eMBPortIpVer ip_ver = (comm_info->ip_addr_type == MB_IPV4) ? MB_PORT_IPV4 : MB_PORT_IPV6;
vMBTCPPortMasterSetNetOpt(comm_info->ip_netif_ptr, ip_ver, proto);
status = eMBMasterEnable();
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack enable failure, eMBMasterEnable() returned (0x%x).", (int)status);
// Add slave IP address for each slave to initialize connection
mb_slave_addr_entry_t *p_slave_info;
LIST_FOREACH(p_slave_info, &mbm_opts->mbm_slave_list, entries) {
result = (BOOL)xMBTCPPortMasterAddSlaveIp(p_slave_info->index, p_slave_info->ip_address, p_slave_info->slave_addr);
MB_MASTER_CHECK(result, ESP_ERR_INVALID_STATE, "mb stack add slave IP failed: %s.", *comm_ip_table);
}
// Add end of list condition
(void)xMBTCPPortMasterAddSlaveIp(0xFF, NULL, 0xFF);
// Wait for connection done event
bool start = (bool)xMBTCPPortMasterWaitEvent(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED, MB_TCP_CONNECTION_TOUT);
MB_MASTER_CHECK((start), ESP_ERR_INVALID_STATE,
"mb stack could not connect to slaves for %d seconds.",
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC);
return ESP_OK;
}
static esp_err_t mbc_tcp_master_destroy(void)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb incorrect options pointer.");
eMBErrorCode mb_error = MB_ENOERR;
// Stop polling by clearing correspondent bit in the event group
xEventGroupClearBits(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
// Disable and then destroy the Modbus port
mb_error = eMBMasterDisable();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
(void)vTaskDelete(mbm_opts->mbm_task_handle);
mbm_opts->mbm_task_handle = NULL;
mb_error = eMBMasterClose();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (int)mb_error);
(void)vEventGroupDelete(mbm_opts->mbm_event_group);
mbm_opts->mbm_event_group = NULL;
mbc_tcp_master_free_slave_list();
free(mbm_interface_ptr); // free the memory allocated for options
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
mbm_interface_ptr = NULL;
return ESP_OK;
}
// Set Modbus parameter description table
static esp_err_t mbc_tcp_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
MB_MASTER_CHECK((descriptor != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((num_elements >= 1), ESP_ERR_INVALID_ARG, "mb table size is incorrect.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb options.");
const char** comm_ip_table = (const char**)mbm_opts->mbm_comm.ip_addr;
MB_MASTER_CHECK((comm_ip_table != NULL), ESP_ERR_INVALID_ARG, "mb ip table address is incorrect.");
const mb_parameter_descriptor_t *reg_ptr = descriptor;
uint16_t slave_cnt = 0;
mb_slave_addr_entry_t* p_slave = NULL;
// Go through all items in the table to check all Modbus registers
for (int idx = 0; idx < (num_elements); idx++, reg_ptr++)
{
// Below is the code to check consistency of the table format and required fields.
MB_MASTER_CHECK((reg_ptr->cid == idx), ESP_ERR_INVALID_ARG, "mb descriptor cid field is incorrect.");
MB_MASTER_CHECK((reg_ptr->param_key != NULL), ESP_ERR_INVALID_ARG, "mb descriptor param key is incorrect.");
MB_MASTER_CHECK((reg_ptr->mb_size > 0), ESP_ERR_INVALID_ARG, "mb descriptor param size is incorrect.");
// Is the slave already in the list?
p_slave = mbc_tcp_master_find_slave_addr(reg_ptr->mb_slave_addr);
// Add it to slave list if not there.
if (!p_slave) {
// Is the IP address correctly defined for the slave?
MB_MASTER_CHECK((comm_ip_table[slave_cnt]), ESP_ERR_INVALID_STATE, "mb missing IP address for cid #%u.", (unsigned)reg_ptr->cid);
// Add slave to the list
MB_MASTER_ASSERT(mbc_tcp_master_add_slave(idx, reg_ptr->mb_slave_addr, comm_ip_table[slave_cnt++]) == ESP_OK);
}
}
mbm_opts->mbm_param_descriptor_table = descriptor;
mbm_opts->mbm_param_descriptor_size = num_elements;
return ESP_OK;
}
// Send custom Modbus request defined as mb_param_request_t structure
static esp_err_t mbc_tcp_master_send_request(mb_param_request_t* request, void* data_ptr)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((request != NULL), ESP_ERR_INVALID_ARG, "mb request structure.");
MB_MASTER_CHECK((data_ptr != NULL), ESP_ERR_INVALID_ARG, "mb incorrect data pointer.");
eMBMasterReqErrCode mb_error = MB_MRE_MASTER_BUSY;
esp_err_t error = ESP_FAIL;
if (xMBMasterRunResTake(MB_TCP_API_RESP_TICS)) {
uint8_t mb_slave_addr = request->slave_addr;
uint8_t mb_command = request->command;
uint16_t mb_offset = request->reg_start;
uint16_t mb_size = request->reg_size;
// Set the buffer for callback function processing of received data
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
mbm_opts->mbm_reg_buffer_size = mb_size;
vMBMasterRunResRelease();
// Calls appropriate request function to send request and waits response
switch(mb_command)
{
case MB_FUNC_READ_COILS:
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_SINGLE_COIL:
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT *)data_ptr, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_MULTIPLE_COILS:
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (UCHAR *)data_ptr,
(LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READ_DISCRETE_INPUTS:
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READ_HOLDING_REGISTER:
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_REGISTER:
mb_error = eMBMasterReqWriteHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT *)data_ptr, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqWriteMultipleHoldingRegister((UCHAR)mb_slave_addr,
(USHORT)mb_offset, (USHORT)mb_size,
(USHORT *)data_ptr, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (USHORT *)data_ptr,
(USHORT)mb_offset, (USHORT)mb_size,
(LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READ_INPUT_REGISTER:
mb_error = eMBMasterReqReadInputRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
default:
ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ", __FUNCTION__, (unsigned)mb_command);
mb_error = MB_MRE_NO_REG;
break;
}
}
// Propagate the Modbus errors to higher level
switch(mb_error)
{
case MB_MRE_NO_ERR:
error = ESP_OK;
break;
case MB_MRE_NO_REG:
error = ESP_ERR_NOT_SUPPORTED; // Invalid register request
break;
case MB_MRE_TIMEDOUT:
error = ESP_ERR_TIMEOUT; // Slave did not send response
break;
case MB_MRE_EXE_FUN:
case MB_MRE_REV_DATA:
error = ESP_ERR_INVALID_RESPONSE; // Invalid response from slave
break;
case MB_MRE_MASTER_BUSY:
error = ESP_ERR_INVALID_STATE; // Master is busy (previous request is pending)
break;
default:
ESP_LOGE(TAG, "%s: Incorrect return code (0x%x) ", __FUNCTION__, (unsigned)mb_error);
error = ESP_FAIL;
break;
}
return error;
}
static esp_err_t mbc_tcp_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_buffer)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_CHECK((param_buffer != NULL), ESP_ERR_INVALID_ARG, "mb incorrect data buffer pointer.");
MB_MASTER_CHECK((mbm_opts->mbm_param_descriptor_table != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor table or not set.");
MB_MASTER_CHECK((cid < mbm_opts->mbm_param_descriptor_size), ESP_ERR_NOT_FOUND, "mb incorrect cid of characteristic.");
// It is assumed that characteristics cid increased in the table
const mb_parameter_descriptor_t* reg_info = &mbm_opts->mbm_param_descriptor_table[cid];
MB_MASTER_CHECK((reg_info->param_key != NULL), ESP_ERR_INVALID_ARG, "mb incorrect characteristic key.");
*param_buffer = reg_info;
return ESP_OK;
}
// Helper function to get modbus command for each type of Modbus register area
static uint8_t mbc_tcp_master_get_command(mb_param_type_t param_type, mb_param_mode_t mode)
{
uint8_t command = 0;
switch(param_type)
{ //
case MB_PARAM_HOLDING:
command = (mode == MB_PARAM_WRITE) ? MB_FUNC_WRITE_MULTIPLE_REGISTERS : MB_FUNC_READ_HOLDING_REGISTER;
break;
case MB_PARAM_INPUT:
command = MB_FUNC_READ_INPUT_REGISTER;
break;
case MB_PARAM_COIL:
command = (mode == MB_PARAM_WRITE) ? MB_FUNC_WRITE_MULTIPLE_COILS : MB_FUNC_READ_COILS;
break;
case MB_PARAM_DISCRETE:
if (mode != MB_PARAM_WRITE) {
command = MB_FUNC_READ_DISCRETE_INPUTS;
} else {
ESP_LOGE(TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (unsigned)mode);
}
break;
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, (unsigned)param_type);
break;
}
return command;
}
// Helper to search parameter by name in the parameter description table and fills Modbus request fields accordingly
static esp_err_t mbc_tcp_master_set_request(char* name, mb_param_mode_t mode, mb_param_request_t* request,
mb_parameter_descriptor_t* reg_data)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
esp_err_t error = ESP_ERR_NOT_FOUND;
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect parameter name.");
MB_MASTER_CHECK((request != NULL), ESP_ERR_INVALID_ARG, "mb incorrect request parameter.");
MB_MASTER_CHECK((mode <= MB_PARAM_WRITE), ESP_ERR_INVALID_ARG, "mb incorrect mode.");
MB_MASTER_ASSERT(mbm_opts->mbm_param_descriptor_table != NULL);
const mb_parameter_descriptor_t* reg_ptr = mbm_opts->mbm_param_descriptor_table;
for (uint16_t counter = 0; counter < (mbm_opts->mbm_param_descriptor_size); counter++, reg_ptr++)
{
// Check the cid of the parameter is equal to record number in the table
// Check the length of name and parameter key strings from table
size_t param_key_len = strlen((const char*)reg_ptr->param_key);
if (param_key_len != strlen((const char*)name)) {
continue; // The length of strings is different then check next record in the table
}
// Compare the name of parameter with parameter key from table
int comp_result = memcmp((const void*)name, (const void*)reg_ptr->param_key, (size_t)param_key_len);
if (comp_result == 0) {
// The correct line is found in the table and reg_ptr points to the found parameter description
request->slave_addr = reg_ptr->mb_slave_addr;
request->reg_start = reg_ptr->mb_reg_start;
request->reg_size = reg_ptr->mb_size;
request->command = mbc_tcp_master_get_command(reg_ptr->mb_param_type, mode);
MB_MASTER_CHECK((request->command > 0), ESP_ERR_INVALID_ARG, "mb incorrect command or parameter type.");
if (reg_data != NULL) {
*reg_data = *reg_ptr; // Set the cid registered parameter data
}
error = ESP_OK;
break;
}
}
return error;
}
// Get parameter data for corresponding characteristic
static esp_err_t mbc_tcp_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type)
{
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t* pdata = NULL;
error = mbc_tcp_master_set_request(name, MB_PARAM_READ, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid)) {
// alloc buffer to store parameter data
pdata = calloc(1, (reg_info.mb_size << 1));
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
error = mbc_tcp_master_send_request(&request, pdata);
if (error == ESP_OK) {
// If data pointer is NULL then we don't need to set value (it is still in the cache of cid)
if (value != NULL) {
error = mbc_master_set_param_data((void*)value, (void*)pdata,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
error = ESP_ERR_INVALID_STATE;
} else {
ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
}
} else {
ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
free(pdata);
// Set the type of parameter found in the table
*type = reg_info.param_type;
} else {
ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.", __FUNCTION__, reg_info.cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
}
// Set parameter value for characteristic selected by name and cid
static esp_err_t mbc_tcp_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type)
{
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t* pdata = NULL;
error = mbc_tcp_master_set_request(name, MB_PARAM_WRITE, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid)) {
pdata = calloc(1, (reg_info.mb_size << 1)); // alloc parameter buffer
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
// Transfer value of characteristic into parameter buffer
error = mbc_master_set_param_data((void*)pdata, (void*)value,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
free(pdata);
return ESP_ERR_INVALID_STATE;
}
// Send request to write characteristic data
error = mbc_tcp_master_send_request(&request, pdata);
if (error == ESP_OK) {
ESP_LOGD(TAG, "%s: Good response for set cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
} else {
ESP_LOGD(TAG, "%s: Bad response to set cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
free(pdata);
// Set the type of parameter found in the table
*type = reg_info.param_type;
} else {
ESP_LOGE(TAG, "%s: The requested cid(%u) not found in the data dictionary.",
__FUNCTION__, (unsigned)reg_info.cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
}
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
// These are executed by modbus stack to read appropriate type of registers.
/**
* Modbus master input register callback function.
*
* @param pucRegBuffer input register buffer
* @param usAddress input register address
* @param usNRegs input register number
*
* @return result
*/
// Callback function for reading of MB Input Registers
eMBErrorCode eMBRegInputCBTcpMaster(UCHAR* pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_ASSERT(pucRegBuffer != NULL);
USHORT usRegInputNregs = (USHORT)mbm_opts->mbm_reg_buffer_size; // Number of input registers to be transferred
UCHAR* pucInputBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr; // Get instance address
USHORT usRegs = usNRegs;
eMBErrorCode eStatus = MB_ENOERR;
// If input or configuration parameters are incorrect then return an error to stack layer
if ((pucInputBuffer != NULL)
&& (usNRegs >= 1)
&& (usRegInputNregs == usRegs)) {
while (usRegs > 0) {
_XFER_2_RD(pucInputBuffer, pucRegBuffer);
usRegs -= 1;
}
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master holding register callback function.
*
* @param pucRegBuffer holding register buffer
* @param usAddress holding register address
* @param usNRegs holding register number
* @param eMode read or write
*
* @return result
*/
// Callback function for reading of MB Holding Registers
// Executed by stack when request to read/write holding registers is received
eMBErrorCode eMBRegHoldingCBTcpMaster(UCHAR *pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t *mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_ASSERT(pucRegBuffer != NULL);
USHORT usRegHoldingNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR *pucHoldingBuffer = (UCHAR *)mbm_opts->mbm_reg_buffer_ptr;
eMBErrorCode eStatus = MB_ENOERR;
USHORT usRegs = usNRegs;
// Check input and configuration parameters for correctness
if ((pucHoldingBuffer != NULL) && (usRegHoldingNregs == usNRegs) && (usNRegs >= 1))
{
switch (eMode)
{
case MB_REG_WRITE:
while (usRegs > 0)
{
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
usRegs -= 1;
};
break;
case MB_REG_READ:
while (usRegs > 0)
{
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
pucHoldingBuffer += 2;
usRegs -= 1;
};
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master coils callback function.
*
* @param pucRegBuffer coils buffer
* @param usAddress coils address
* @param usNCoils coils number
* @param eMode read or write
*
* @return result
*/
// Callback function for reading of MB Coils Registers
eMBErrorCode eMBRegCoilsCBTcpMaster(UCHAR *pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_ASSERT(NULL != pucRegBuffer);
USHORT usRegCoilNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR* pucRegCoilsBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT usCoils = usNCoils;
usAddress--; // The address is already + 1
if ((usRegCoilNregs >= 1)
&& (pucRegCoilsBuf != NULL)
&& (usNCoils == usRegCoilNregs)) {
iRegIndex = (usAddress % 8);
switch (eMode) {
case MB_REG_WRITE:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress % 8) , 1, ucResult);
iRegIndex++;
usCoils--;
}
break;
case MB_REG_READ:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1, ucResult);
iRegIndex++;
usCoils--;
}
break;
} // switch ( eMode )
} else {
// If the configuration or input parameters are incorrect then return error to stack
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master discrete callback function.
*
* @param pucRegBuffer discrete buffer
* @param usAddress discrete address
* @param usNDiscrete discrete number
*
* @return result
*/
// Callback function for reading of MB Discrete Input Registers
eMBErrorCode eMBRegDiscreteCBTcpMaster(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
MB_MASTER_ASSERT(pucRegBuffer != NULL);
USHORT usRegDiscreteNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegBitIndex, iNReg;
UCHAR* pucDiscreteInputBuf;
iNReg = usNDiscrete / 8 + 1;
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
// It is already plus one in Modbus function method.
usAddress--;
if ((usRegDiscreteNregs >= 1)
&& (pucRegDiscreteBuf != NULL)
&& (usNDiscrete >= 1)) {
iRegBitIndex = (USHORT)(usAddress) % 8; // Get bit index
while (iNReg > 1)
{
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex - (usAddress % 8), 8, *pucRegBuffer++);
iNReg--;
}
// last discrete
usNDiscrete = usNDiscrete % 8;
// xMBUtilSetBits has bug when ucNBits is zero
if (usNDiscrete != 0)
{
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex - (usAddress % 8), usNDiscrete, *pucRegBuffer++);
}
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
// Initialization of resources for Modbus TCP master controller
esp_err_t mbc_tcp_master_create(void** handler)
{
// Allocate space for master interface structure
if (mbm_interface_ptr == NULL) {
mbm_interface_ptr = malloc(sizeof(mb_master_interface_t));
}
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
// Initialize interface properties
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
mbm_opts->port_type = MB_PORT_TCP_MASTER;
vMBPortSetMode((UCHAR)MB_PORT_TCP_MASTER);
mbm_opts->mbm_comm.ip_mode = MB_MODE_TCP;
mbm_opts->mbm_comm.ip_port = MB_TCP_DEFAULT_PORT;
// Initialization of active context of the modbus controller
BaseType_t status = 0;
// Parameter change notification queue
mbm_opts->mbm_event_group = xEventGroupCreate();
MB_MASTER_CHECK((mbm_opts->mbm_event_group != NULL), ESP_ERR_NO_MEM, "mb event group error.");
// Create modbus controller task
status = xTaskCreate((void*)&modbus_tcp_master_task,
"modbus_tcp_master_task",
MB_CONTROLLER_STACK_SIZE,
NULL, // No parameters
MB_CONTROLLER_PRIORITY,
&mbm_opts->mbm_task_handle);
if (status != pdPASS) {
vTaskDelete(mbm_opts->mbm_task_handle);
MB_MASTER_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (%u).", (unsigned)status);
}
MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect
LIST_INIT(&mbm_opts->mbm_slave_list); // Init slave address list
mbm_opts->mbm_slave_list_count = 0;
// Initialize public interface methods of the interface
mbm_interface_ptr->init = mbc_tcp_master_create;
mbm_interface_ptr->destroy = mbc_tcp_master_destroy;
mbm_interface_ptr->setup = mbc_tcp_master_setup;
mbm_interface_ptr->start = mbc_tcp_master_start;
mbm_interface_ptr->get_cid_info = mbc_tcp_master_get_cid_info;
mbm_interface_ptr->get_parameter = mbc_tcp_master_get_parameter;
mbm_interface_ptr->send_request = mbc_tcp_master_send_request;
mbm_interface_ptr->set_descriptor = mbc_tcp_master_set_descriptor;
mbm_interface_ptr->set_parameter = mbc_tcp_master_set_parameter;
mbm_interface_ptr->master_reg_cb_discrete = eMBRegDiscreteCBTcpMaster;
mbm_interface_ptr->master_reg_cb_input = eMBRegInputCBTcpMaster;
mbm_interface_ptr->master_reg_cb_holding = eMBRegHoldingCBTcpMaster;
mbm_interface_ptr->master_reg_cb_coils = eMBRegCoilsCBTcpMaster;
*handler = mbm_interface_ptr;
return ESP_OK;
}
#endif //#if MB_MASTER_TCP_ENABLED

View File

@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_master.h Modbus controller TCP master implementation header file
#ifndef _MODBUS_TCP_CONTROLLER_MASTER
#define _MODBUS_TCP_CONTROLLER_MASTER
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "esp_modbus_common.h" // for common defines
/* ----------------------- Defines ------------------------------------------*/
#define MB_INST_MIN_SIZE (2) // The minimal size of Modbus registers area in bytes
#define MB_INST_MAX_SIZE (CONFIG_MB_INST_MAX_SIZE) // The maximum size of Modbus area in bytes
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
/**
* @brief Create Modbus Master controller and stack for TCP port
*
* @param[out] handler handler(pointer) to master data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
*/
esp_err_t mbc_tcp_master_create(void** handler);
#endif // _MODBUS_TCP_CONTROLLER_SLAVE

View File

@@ -0,0 +1,138 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 TCP Port
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
* Parts of crt0.S Copyright (c) 1995, 1996, 1998 Cygnus Support
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: port.h,v 1.2 2006/09/04 14:39:20 wolti Exp $
*/
#ifndef _PORT_TCP_SLAVE_H
#define _PORT_TCP_SLAVE_H
/* ----------------------- Platform includes --------------------------------*/
#include "esp_log.h"
#include "lwip/sys.h"
#include "freertos/event_groups.h"
#include "port.h"
/* ----------------------- Defines ------------------------------------------*/
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Type definitions ---------------------------------*/
typedef struct {
int xIndex; /*!< Slave information index */
int xSockId; /*!< Socket ID of slave */
int xError; /*!< Socket error */
int xRcvErr; /*!< Socket receive error */
const char* pcIpAddr; /*!< TCP/UDP IP address */
UCHAR ucSlaveAddr; /*!< Slave short address */
UCHAR* pucRcvBuf; /*!< Receive buffer pointer */
USHORT usRcvPos; /*!< Receive buffer position */
int pcPort; /*!< TCP/UDP port number */
eMBPortProto xMbProto; /*!< Protocol type */
int64_t xSendTimeStamp; /*!< Send request time stamp */
int64_t xRecvTimeStamp; /*!< Receive response time stamp */
uint16_t usTidCnt; /*!< Transaction identifier (TID) for slave */
} MbSlaveInfo_t;
typedef struct {
TaskHandle_t xMbTcpTaskHandle; /*!< Master TCP/UDP handling task handle */
QueueHandle_t xConnectQueue; /*!< Master connection queue */
USHORT usPort; /*!< Master TCP/UDP port number */
USHORT usMbSlaveInfoCount; /*!< Master count of connected slaves */
USHORT ucCurSlaveIndex; /*!< Master current processing slave index */
eMBPortIpVer eMbIpVer; /*!< Master IP version */
eMBPortProto eMbProto; /*!< Master protocol type */
void* pvNetIface; /*!< Master netif interface pointer */
MbSlaveInfo_t** pxMbSlaveInfo; /*!< Master information structure for each connected slave */
MbSlaveInfo_t* pxMbSlaveCurrInfo; /*!< Master current slave information */
} MbPortConfig_t;
typedef struct {
USHORT usIndex; /*!< index of the address info */
const char* pcIPAddr; /*!< represents the IP address of the slave */
UCHAR ucSlaveAddr; /*!< slave unit ID (UID) field for MBAP frame */
} MbSlaveAddrInfo_t;
/* ----------------------- Function prototypes ------------------------------*/
// The functions below are used by Modbus controller interface to configure Modbus port.
/**
* Registers slave IP address
*
* @param usIndex index of element in the configuration
* @param pcIpStr IP address to register
* @param ucSlaveAddress slave element index
*
* @return TRUE if address registered successfully, else FALSE
*/
BOOL xMBTCPPortMasterAddSlaveIp(const USHORT usIndex, const CHAR* pcIpStr, UCHAR ucSlaveAddress);
/**
* Keeps FSM event handle and mask then wait for Master stack to start
*
* @param xEventHandle Master event handle
* @param xEvent event mask to start Modbus stack FSM
* @param usTimeout - timeout in ticks to wait for stack to start
*
* @return TRUE if stack started, else FALSE
*/
BOOL xMBTCPPortMasterWaitEvent(EventGroupHandle_t xEventHandle, EventBits_t xEvent, USHORT usTimeout);
/**
* Set network options for Master port
*
* @param pvNetIf netif interface pointer
* @param xIpVersion IP version option for the Master port
* @param xProto Protocol version option for the Master port
*
* @return None
*/
void vMBTCPPortMasterSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto);
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -0,0 +1,217 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_slave.c
// Implementation of the Modbus controller TCP slave
#include <sys/time.h> // for calculation of time stamp in milliseconds
#include "esp_log.h" // for log_write
#include "mb.h" // for mb types definition
#include "mbutils.h" // for mbutils functions definition for stack callback
#include "port.h" // for port callback functions and defines
#include "sdkconfig.h" // for KConfig values
#include "esp_modbus_common.h" // for common defines
#include "esp_modbus_slave.h" // for public slave interface types
#include "mbc_slave.h" // for private slave interface types
#include "mbc_tcp_slave.h" // for tcp slave mb controller defines
#include "port_tcp_slave.h" // for tcp slave port defines
#if MB_TCP_ENABLED
// Shared pointer to interface structure
static mb_slave_interface_t* mbs_interface_ptr = NULL;
static const char *TAG = "MB_CONTROLLER_SLAVE";
// Modbus task function
static void modbus_tcp_slave_task(void *pvParameters)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
// Main Modbus stack processing cycle
for (;;) {
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group,
(BaseType_t)(MB_EVENT_STACK_STARTED),
pdFALSE, // do not clear bits
pdFALSE,
portMAX_DELAY);
// Check if stack started then poll for data
if (status & MB_EVENT_STACK_STARTED) {
(void)eMBPoll(); // allow stack to process data
}
}
}
// Setup Modbus controller parameters
static esp_err_t mbc_tcp_slave_setup(void* comm_info)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_CHECK((comm_info != NULL), ESP_ERR_INVALID_ARG,
"mb wrong communication settings.");
mb_communication_info_t* comm_settings = (mb_communication_info_t*)comm_info;
MB_SLAVE_CHECK((comm_settings->ip_mode == MB_MODE_TCP),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).", (unsigned)comm_settings->ip_mode);
MB_SLAVE_CHECK(((comm_settings->ip_addr_type == MB_IPV4) || (comm_settings->ip_addr_type == MB_IPV6)),
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (%u).", (unsigned)comm_settings->ip_addr_type);
MB_SLAVE_CHECK((comm_settings->ip_netif_ptr != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect iface address.");
// Set communication options of the controller
mbs_opts->mbs_comm = *comm_settings;
return ESP_OK;
}
// Start Modbus controller start function
static esp_err_t mbc_tcp_slave_start(void)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
eMBErrorCode status = MB_EIO;
// Initialize Modbus stack using mbcontroller parameters
status = eMBTCPInit((UCHAR)mbs_opts->mbs_comm.slave_uid, (USHORT)mbs_opts->mbs_comm.ip_port);
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack initialization failure, eMBInit() returns (0x%x).", (int)status);
eMBPortProto proto = (mbs_opts->mbs_comm.ip_mode == MB_MODE_TCP) ? MB_PROTO_TCP : MB_PROTO_UDP;
eMBPortIpVer ip_ver = (mbs_opts->mbs_comm.ip_addr_type == MB_IPV4) ? MB_PORT_IPV4 : MB_PORT_IPV6;
vMBTCPPortSlaveSetNetOpt(mbs_opts->mbs_comm.ip_netif_ptr, ip_ver, proto, (char*)mbs_opts->mbs_comm.ip_addr);
status = eMBEnable();
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb TCP stack start failure, eMBEnable() returned (0x%x).", (int)status);
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupSetBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
return ESP_OK;
}
// Modbus controller destroy function
static esp_err_t mbc_tcp_slave_destroy(void)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
eMBErrorCode mb_error = MB_ENOERR;
// Stop polling by clearing correspondent bit in the event group
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Disable and then destroy the Modbus stack
mb_error = eMBDisable();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
(void)vTaskDelete(mbs_opts->mbs_task_handle);
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
(void)vMBTCPPortClose();
mbs_interface_ptr = NULL;
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
return ESP_OK;
}
// Blocking function to get event on parameter group change for application task
static mb_event_group_t mbc_tcp_slave_check_event(mb_event_group_t group)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_ASSERT(mbs_opts->mbs_event_group != NULL);
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group, (BaseType_t)group,
pdTRUE , pdFALSE, portMAX_DELAY);
return (mb_event_group_t)status;
}
// Function to get notification about parameter change from application task
static esp_err_t mbc_tcp_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
esp_err_t err = ESP_ERR_TIMEOUT;
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
MB_SLAVE_CHECK((reg_info != NULL), ESP_ERR_INVALID_ARG, "mb register information is invalid.");
BaseType_t status = xQueueReceive(mbs_opts->mbs_notification_queue_handle,
reg_info, pdMS_TO_TICKS(timeout));
if (status == pdTRUE) {
err = ESP_OK;
}
return err;
}
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
// These are executed by modbus stack to read appropriate type of registers.
// This is required to suppress warning when register start address is zero
#pragma GCC diagnostic ignored "-Wtype-limits"
// Initialization of Modbus controller
esp_err_t mbc_tcp_slave_create(void** handler)
{
// Allocate space for options
if (mbs_interface_ptr == NULL) {
mbs_interface_ptr = malloc(sizeof(mb_slave_interface_t));
}
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
mbs_opts->port_type = MB_PORT_TCP_SLAVE; // set interface port type
vMBPortSetMode((UCHAR)MB_PORT_TCP_SLAVE);
// Set default values of communication options
mbs_opts->mbs_comm.ip_port = MB_TCP_DEFAULT_PORT;
// Initialization of active context of the Modbus controller
BaseType_t status = 0;
// Parameter change notification queue
mbs_opts->mbs_event_group = xEventGroupCreate();
MB_SLAVE_CHECK((mbs_opts->mbs_event_group != NULL),
ESP_ERR_NO_MEM, "mb event group error.");
// Parameter change notification queue
mbs_opts->mbs_notification_queue_handle = xQueueCreate(
MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
sizeof(mb_param_info_t));
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
ESP_ERR_NO_MEM, "mb notify queue creation error.");
// Create Modbus controller task
status = xTaskCreatePinnedToCore((void*)&modbus_tcp_slave_task,
"mbs_port_tcp_task",
MB_CONTROLLER_STACK_SIZE,
NULL,
MB_CONTROLLER_PRIORITY,
&mbs_opts->mbs_task_handle,
MB_PORT_TASK_AFFINITY);
if (status != pdPASS) {
vTaskDelete(mbs_opts->mbs_task_handle);
MB_SLAVE_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (%u).", (unsigned)status);
}
// The task is created but handle is incorrect
MB_SLAVE_ASSERT(mbs_opts->mbs_task_handle != NULL);
// Initialization of interface pointers
mbs_interface_ptr->init = mbc_tcp_slave_create;
mbs_interface_ptr->destroy = mbc_tcp_slave_destroy;
mbs_interface_ptr->setup = mbc_tcp_slave_setup;
mbs_interface_ptr->start = mbc_tcp_slave_start;
mbs_interface_ptr->check_event = mbc_tcp_slave_check_event;
mbs_interface_ptr->get_param_info = mbc_tcp_slave_get_param_info;
mbs_interface_ptr->set_descriptor = NULL; // Use common descriptor setter
// Initialize stack callback function pointers
mbs_interface_ptr->slave_reg_cb_discrete = NULL; // implemented in common layer
mbs_interface_ptr->slave_reg_cb_input = NULL;
mbs_interface_ptr->slave_reg_cb_holding = NULL;
mbs_interface_ptr->slave_reg_cb_coils = NULL;
*handler = (void*)mbs_interface_ptr;
return ESP_OK;
}
#endif //#if MB_TCP_ENABLED

View File

@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_slave.h Modbus controller TCP slave implementation header file
#ifndef _MODBUS_TCP_CONTROLLER_SLAVE
#define _MODBUS_TCP_CONTROLLER_SLAVE
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "esp_modbus_common.h" // for common defines
/* ----------------------- Defines ------------------------------------------*/
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
/**
* @brief Initialize Modbus controller and stack for TCP slave
*
* @param[out] handler handler(pointer) to slave data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
*/
esp_err_t mbc_tcp_slave_create(void** handler);
#endif // _MODBUS_TCP_CONTROLLER_SLAVE

View File

@@ -0,0 +1,758 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 TCP Port
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
* Parts of crt0.S Copyright (c) 1995, 1996, 1998 Cygnus Support
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: port.c,v 1.2 2006/09/04 14:39:20 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include <stdio.h>
#include <string.h>
#include "esp_err.h"
#include "esp_timer.h"
#include "sys/time.h"
#include "esp_netif.h"
/* ----------------------- lwIP includes ------------------------------------*/
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "net/if.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "port.h"
#include "mbframe.h"
#include "port_tcp_slave.h"
#include "esp_modbus_common.h" // for common types for network options
#if MB_TCP_ENABLED
/* ----------------------- Defines -----------------------------------------*/
#define MB_TCP_DISCONNECT_TIMEOUT ( CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000000 ) // disconnect timeout in uS
#define MB_TCP_RESP_TIMEOUT_MS ( MB_MASTER_TIMEOUT_MS_RESPOND - 1 ) // slave response time limit
#define MB_TCP_NET_LISTEN_BACKLOG ( SOMAXCONN )
/* ----------------------- Prototypes ---------------------------------------*/
void vMBPortEventClose( void );
/* ----------------------- Static variables ---------------------------------*/
static const char *TAG = "MB_TCP_SLAVE_PORT";
static int xListenSock = -1;
static SemaphoreHandle_t xShutdownSema = NULL;
static MbSlavePortConfig_t xConfig = { 0 };
/* ----------------------- Static functions ---------------------------------*/
// The helper function to get time stamp in microseconds
static int64_t xMBTCPGetTimeStamp(void)
{
int64_t xTimeStamp = esp_timer_get_time();
return xTimeStamp;
}
static void vxMBTCPPortMStoTimeVal(USHORT usTimeoutMs, struct timeval *pxTimeout)
{
pxTimeout->tv_sec = usTimeoutMs / 1000;
pxTimeout->tv_usec = (usTimeoutMs - (pxTimeout->tv_sec * 1000)) * 1000;
}
static QueueHandle_t xMBTCPPortRespQueueCreate(void)
{
QueueHandle_t xRespQueueHandle = xQueueCreate(2, sizeof(void*));
MB_PORT_CHECK((xRespQueueHandle != NULL), NULL, "TCP respond queue creation failure.");
return xRespQueueHandle;
}
static void vMBTCPPortRespQueueDelete(QueueHandle_t xRespQueueHandle)
{
vQueueDelete(xRespQueueHandle);
}
static void* vxMBTCPPortRespQueueRecv(QueueHandle_t xRespQueueHandle)
{
void* pvResp = NULL;
MB_PORT_CHECK(xRespQueueHandle != NULL, NULL, "Response queue is not initialized.");
BaseType_t xStatus = xQueueReceive(xRespQueueHandle,
(void*)&pvResp,
pdMS_TO_TICKS(MB_TCP_RESP_TIMEOUT_MS));
if (xStatus != pdTRUE) {
ESP_LOGD(TAG, "Could not get respond confirmation.");
}
return pvResp;
}
static BOOL vxMBTCPPortRespQueueSend(QueueHandle_t xRespQueueHandle, void* pvResp)
{
MB_PORT_CHECK(xRespQueueHandle != NULL, FALSE, "Response queue is not initialized.");
BaseType_t xStatus = xQueueSend(xConfig.xRespQueueHandle,
(const void*)&pvResp,
pdMS_TO_TICKS(MB_TCP_RESP_TIMEOUT_MS));
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "FAIL to send to response queue.");
return TRUE;
}
static void vMBTCPPortServerTask(void *pvParameters);
/* ----------------------- Begin implementation -----------------------------*/
BOOL
xMBTCPPortInit( USHORT usTCPPort )
{
BOOL bOkay = FALSE;
xConfig.pxMbClientInfo = calloc(MB_TCP_PORT_MAX_CONN + 1, sizeof(MbClientInfo_t*));
if (!xConfig.pxMbClientInfo) {
ESP_LOGE(TAG, "TCP client info allocation failure.");
return FALSE;
}
for(int idx = 0; idx < MB_TCP_PORT_MAX_CONN; xConfig.pxMbClientInfo[idx] = NULL, idx++);
xConfig.xRespQueueHandle = xMBTCPPortRespQueueCreate();
if (!xConfig.xRespQueueHandle) {
ESP_LOGE(TAG, "Response queue allocation failure.");
return FALSE;
}
xConfig.usPort = usTCPPort;
xConfig.eMbProto = MB_PROTO_TCP;
xConfig.usClientCount = 0;
xConfig.pvNetIface = NULL;
xConfig.xIpVer = MB_PORT_IPV4;
xConfig.pcBindAddr = NULL;
// Create task for packet processing
BaseType_t xErr = xTaskCreatePinnedToCore(vMBTCPPortServerTask,
"tcp_slave_task",
MB_TCP_STACK_SIZE,
NULL,
MB_TCP_TASK_PRIO,
&xConfig.xMbTcpTaskHandle,
MB_PORT_TASK_AFFINITY);
if (xErr != pdTRUE)
{
ESP_LOGE(TAG, "Server task creation failure.");
vTaskDelete(xConfig.xMbTcpTaskHandle);
} else {
ESP_LOGI(TAG, "Protocol stack initialized.");
bOkay = TRUE;
}
return bOkay;
}
void vMBTCPPortSlaveSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto, CHAR* pcBindAddrStr)
{
// Set network options
xConfig.pvNetIface = pvNetIf;
xConfig.eMbProto = xProto;
xConfig.xIpVer = xIpVersion;
xConfig.pcBindAddr = pcBindAddrStr;
}
static int xMBTCPPortAcceptConnection(int xListenSockId, char** pcIPAddr)
{
MB_PORT_CHECK(pcIPAddr, -1, "Wrong IP address pointer.");
MB_PORT_CHECK((xListenSockId > 0), -1, "Incorrect listen socket ID.");
// Address structure large enough for both IPv4 or IPv6 address
struct sockaddr_storage xSrcAddr;
CHAR cAddrStr[128];
int xSockId = -1;
CHAR* pcStr = NULL;
socklen_t xSize = sizeof(struct sockaddr_storage);
// Accept new socket connection if not active
xSockId = accept(xListenSockId, (struct sockaddr *)&xSrcAddr, &xSize);
if (xSockId < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno=%u", (unsigned)errno);
close(xSockId);
} else {
// Get the sender's ip address as string
if (xSrcAddr.ss_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&xSrcAddr)->sin_addr.s_addr, cAddrStr, sizeof(cAddrStr) - 1);
}
#if CONFIG_LWIP_IPV6
else if (xSrcAddr.ss_family == PF_INET6) {
inet6_ntoa_r(((struct sockaddr_in6 *)&xSrcAddr)->sin6_addr, cAddrStr, sizeof(cAddrStr) - 1);
}
#endif
else {
// Make sure ss_family is valid
abort();
}
ESP_LOGI(TAG, "Socket (#%d), accept client connection from address: %s", (int)xSockId, cAddrStr);
pcStr = calloc(1, strlen(cAddrStr) + 1);
if (pcStr && pcIPAddr) {
memcpy(pcStr, cAddrStr, strlen(cAddrStr));
pcStr[strlen(cAddrStr)] = '\0';
*pcIPAddr = pcStr; // Set IP address of connected client
}
}
return xSockId;
}
static BOOL xMBTCPPortCloseConnection(MbClientInfo_t* pxInfo)
{
MB_PORT_CHECK(pxInfo, FALSE, "Client info is NULL.");
if (pxInfo->xSockId == -1) {
ESP_LOGE(TAG, "Wrong socket info or disconnected socket: %d.", (int)pxInfo->xSockId);
return FALSE;
}
// Empty tcp buffer before shutdown
(void)recv(pxInfo->xSockId, &pxInfo->pucTCPBuf[0], MB_PDU_SIZE_MAX, MSG_DONTWAIT);
if (shutdown(pxInfo->xSockId, SHUT_RDWR) == -1)
{
ESP_LOGE(TAG, "Socket (#%d), shutdown failed: errno %u", (int)pxInfo->xSockId, (unsigned)errno);
}
close(pxInfo->xSockId);
pxInfo->xSockId = -1;
if (xConfig.usClientCount) {
xConfig.usClientCount--; // decrement counter of client connections
} else {
xConfig.pxCurClientInfo = NULL;
}
return TRUE;
}
static void vMBTCPPortFreeClientInfo(MbClientInfo_t *pxClientInfo)
{
if (pxClientInfo) {
if (pxClientInfo->pucTCPBuf) {
free((void *)pxClientInfo->pucTCPBuf);
}
if (pxClientInfo->pcIpAddr) {
free((void *)pxClientInfo->pcIpAddr);
}
free((void *)pxClientInfo);
}
}
static void vMBTCPPortShutdown(void)
{
xSemaphoreGive(xShutdownSema);
vTaskDelete(NULL);
xConfig.xMbTcpTaskHandle = NULL;
for (int i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
MbClientInfo_t *pxClientInfo = xConfig.pxMbClientInfo[i];
if ((pxClientInfo != NULL) && (pxClientInfo->xSockId > 0)) {
xMBTCPPortCloseConnection(pxClientInfo);
vMBTCPPortFreeClientInfo(pxClientInfo);
xConfig.pxMbClientInfo[i] = NULL;
}
}
free(xConfig.pxMbClientInfo);
}
static int xMBTCPPortRxPoll(MbClientInfo_t *pxClientInfo, ULONG xTimeoutMs)
{
int xRet = ERR_CLSD;
struct timeval xTimeVal;
fd_set xReadSet;
int64_t xStartTimeStamp = 0;
// Receive data from connected client
if (pxClientInfo && pxClientInfo->xSockId > -1) {
// Set receive timeout
vxMBTCPPortMStoTimeVal(xTimeoutMs, &xTimeVal);
xStartTimeStamp = xMBTCPGetTimeStamp();
while (1)
{
FD_ZERO(&xReadSet);
FD_SET(pxClientInfo->xSockId, &xReadSet);
xRet = select(pxClientInfo->xSockId + 1, &xReadSet, NULL, NULL, &xTimeVal);
if (xRet == -1)
{
// If select an error occurred
xRet = ERR_CLSD;
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
break;
} else if (xRet == 0) {
// timeout occurred
if ((xStartTimeStamp + xTimeoutMs * 1000) > xMBTCPGetTimeStamp()) {
ESP_LOGD(TAG, "Socket (#%d) Read timeout.", (int)pxClientInfo->xSockId);
xRet = ERR_TIMEOUT;
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
break;
}
}
if (FD_ISSET(pxClientInfo->xSockId, &xReadSet)) {
// If new buffer received then read Modbus packet into buffer
MB_PORT_CHECK((pxClientInfo->usTCPBufPos + pxClientInfo->usTCPFrameBytesLeft < MB_TCP_BUF_SIZE),
ERR_BUF, "Socket (#%d), incorrect request buffer size = %u, ignore.",
(int)pxClientInfo->xSockId,
(unsigned)(pxClientInfo->usTCPBufPos + pxClientInfo->usTCPFrameBytesLeft));
int xLength = recv(pxClientInfo->xSockId, &pxClientInfo->pucTCPBuf[pxClientInfo->usTCPBufPos],
pxClientInfo->usTCPFrameBytesLeft, MSG_DONTWAIT);
if (xLength < 0) {
// If an error occurred during receiving
ESP_LOGE(TAG, "Receive failed: length=%u, errno=%u", (unsigned)xLength, (unsigned)errno);
xRet = (err_t)xLength;
break;
} else if (xLength == 0) {
// Socket connection closed
ESP_LOGD(TAG, "Socket (#%d)(%s), connection closed.",
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
xRet = ERR_CLSD;
break;
} else {
// New data received
pxClientInfo->usTCPBufPos += xLength;
pxClientInfo->usTCPFrameBytesLeft -= xLength;
if (pxClientInfo->usTCPBufPos >= MB_TCP_FUNC) {
// Length is a byte count of Modbus PDU (function code + data) and the
// unit identifier.
xLength = (int)MB_TCP_GET_FIELD(pxClientInfo->pucTCPBuf, MB_TCP_LEN);
// Is the frame already complete.
if (pxClientInfo->usTCPBufPos < (MB_TCP_UID + xLength)) {
// The incomplete frame is received
pxClientInfo->usTCPFrameBytesLeft = xLength + MB_TCP_UID - pxClientInfo->usTCPBufPos;
} else if (pxClientInfo->usTCPBufPos == (MB_TCP_UID + xLength)) {
#if MB_TCP_DEBUG
prvvMBTCPLogFrame(TAG, (UCHAR*)&pxClientInfo->pucTCPBuf[0], pxClientInfo->usTCPBufPos);
#endif
// Copy TID field from incoming packet
pxClientInfo->usTidCnt = MB_TCP_GET_FIELD(pxClientInfo->pucTCPBuf, MB_TCP_TID);
xRet = pxClientInfo->usTCPBufPos;
break;
} else if ((pxClientInfo->usTCPBufPos + xLength) >= MB_TCP_BUF_SIZE) {
ESP_LOGE(TAG, "Incorrect buffer received (%u) bytes.", (unsigned)xLength);
// This should not happen. We can't deal with such a client and
// drop the connection for security reasons.
xRet = ERR_BUF;
break;
}
} // if ( pxClientInfo->usTCPBufPos >= MB_TCP_FUNC )
} // if data received
} // if (FD_ISSET(pxClientInfo->xSockId, &xReadSet))
} // while (1)
}
return (xRet);
}
// Create a listening socket on pcBindIp: Port
static int
vMBTCPPortBindAddr(const CHAR* pcBindIp)
{
int xPar, xRet;
int xListenSockFd = -1;
struct addrinfo xHint;
struct addrinfo* pxAddrList;
struct addrinfo* pxCurAddr;
CHAR* pcStr = NULL;
memset( &xHint, 0, sizeof( xHint ) );
// Bind to IPv6 and/or IPv4, but only in the desired protocol
// Todo: Find a reason why AF_UNSPEC does not work for IPv6
xHint.ai_family = (xConfig.xIpVer == MB_PORT_IPV4) ? AF_INET : AF_INET6;
xHint.ai_socktype = (xConfig.eMbProto == MB_PROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
// The LWIP has an issue when connection to IPv6 socket
xHint.ai_protocol = (xConfig.eMbProto == MB_PROTO_UDP) ? IPPROTO_UDP : IPPROTO_TCP;
xHint.ai_flags = AI_NUMERICSERV;
if (pcBindIp == NULL) {
xHint.ai_flags |= AI_PASSIVE;
} else {
xHint.ai_flags |= AI_CANONNAME;
}
if (asprintf(&pcStr, "%u", xConfig.usPort) == -1) {
abort();
}
xRet = getaddrinfo(pcBindIp, pcStr, &xHint, &pxAddrList);
free(pcStr);
if (xRet != 0) {
return -1;
}
// Try the sockaddr until a binding succeeds
for (pxCurAddr = pxAddrList; pxCurAddr != NULL; pxCurAddr = pxCurAddr->ai_next)
{
xListenSockFd = (int)socket(pxCurAddr->ai_family, pxCurAddr->ai_socktype,
pxCurAddr->ai_protocol);
if (xListenSockFd < 0)
{
continue;
}
xPar = 1;
// Allow multi client connections
if (setsockopt(xListenSockFd, SOL_SOCKET, SO_REUSEADDR,
(const char*)&xPar, sizeof(xPar)) != 0)
{
close(xListenSockFd);
xListenSockFd = -1;
continue;
}
if (bind(xListenSockFd, (struct sockaddr *)pxCurAddr->ai_addr,
(socklen_t)pxCurAddr->ai_addrlen) != 0 )
{
close(xListenSockFd);
xListenSockFd = -1;
continue;
}
// Listen only makes sense for TCP
if (xConfig.eMbProto == MB_PROTO_TCP)
{
if (listen(xListenSockFd, MB_TCP_NET_LISTEN_BACKLOG) != 0)
{
ESP_LOGE(TAG, "Error occurred during listen: errno=%u", (unsigned)errno);
close(xListenSockFd);
xListenSockFd = -1;
continue;
}
}
// Bind was successful
pcStr = (pxCurAddr->ai_canonname == NULL) ? (CHAR*)"ANY_IP" : pxCurAddr->ai_canonname;
ESP_LOGI(TAG, "Socket (#%d), listener %s on port: %u, errno=%u",
(int)xListenSockFd, pcStr, (unsigned)xConfig.usPort, (unsigned)errno);
break;
}
freeaddrinfo(pxAddrList);
return(xListenSockFd);
}
static void vMBTCPPortServerTask(void *pvParameters)
{
int xErr = 0;
fd_set xReadSet;
int i;
CHAR* pcClientIp = NULL;
struct timeval xTimeVal;
// Main connection cycle
while (1) {
// Create listen socket
xListenSock = vMBTCPPortBindAddr(xConfig.pcBindAddr);
if (xListenSock < 0) {
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
continue;
}
// Connections handling cycle
while (1) {
// clear the socket set
FD_ZERO(&xReadSet);
// add master socket to set
FD_SET(xListenSock, &xReadSet);
int xMaxSd = xListenSock;
xConfig.usClientCount = 0;
vxMBTCPPortMStoTimeVal(1, &xTimeVal);
// Initialize read set and file descriptor according to
// all registered connected clients
for (i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
if ((xConfig.pxMbClientInfo[i] != NULL) && (xConfig.pxMbClientInfo[i]->xSockId > 0)) {
// calculate max file descriptor for select
xMaxSd = (xConfig.pxMbClientInfo[i]->xSockId > xMaxSd) ?
xConfig.pxMbClientInfo[i]->xSockId : xMaxSd;
FD_SET(xConfig.pxMbClientInfo[i]->xSockId, &xReadSet);
xConfig.usClientCount++;
}
}
vxMBTCPPortMStoTimeVal(MB_TCP_RESP_TIMEOUT_MS, &xTimeVal);
// Wait for an activity on one of the sockets during timeout
xErr = select(xMaxSd + 1, &xReadSet, NULL, NULL, &xTimeVal);
if ((xErr < 0) && (errno != EINTR)) {
// error occurred during wait for read
ESP_LOGE(TAG, "select() errno = %u.", (unsigned)errno);
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
continue;
} else if (xErr == 0) {
// If timeout happened, something is wrong
ESP_LOGD(TAG, "select() timeout, errno = %u.", (unsigned)errno);
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
}
// If something happened on the master socket, then its an incoming connection.
if (FD_ISSET(xListenSock, &xReadSet) && xConfig.usClientCount < MB_TCP_PORT_MAX_CONN) {
MbClientInfo_t* pxClientInfo = NULL;
// find first empty place to insert connection info
for (i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
pxClientInfo = xConfig.pxMbClientInfo[i];
if (pxClientInfo == NULL) {
break;
}
}
// if request for new connection but no space left
if (pxClientInfo != NULL) {
if (xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] == NULL) {
ESP_LOGE(TAG, "Fail to accept connection %u, only %u connections supported.", (unsigned)(i + 1), (unsigned)MB_TCP_PORT_MAX_CONN);
}
xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] = pxClientInfo; // set last connection info
} else {
// allocate memory for new client info
pxClientInfo = calloc(1, sizeof(MbClientInfo_t));
if (!pxClientInfo) {
ESP_LOGE(TAG, "Client info allocation fail.");
vMBTCPPortFreeClientInfo(pxClientInfo);
pxClientInfo = NULL;
} else {
// Accept new client connection
pxClientInfo->xSockId = xMBTCPPortAcceptConnection(xListenSock, &pcClientIp);
if (pxClientInfo->xSockId < 0) {
ESP_LOGE(TAG, "Fail to accept connection for client %u.", (unsigned)(xConfig.usClientCount - 1));
// Accept connection fail, then free client info and continue polling.
vMBTCPPortFreeClientInfo(pxClientInfo);
pxClientInfo = NULL;
continue;
}
pxClientInfo->pucTCPBuf = calloc(MB_TCP_BUF_SIZE, sizeof(UCHAR));
if (!pxClientInfo->pucTCPBuf) {
ESP_LOGE(TAG, "Fail to allocate buffer for client %u.", (unsigned)(xConfig.usClientCount - 1));
vMBTCPPortFreeClientInfo(pxClientInfo);
pxClientInfo = NULL;
continue;
}
// Fill the connection info structure
xConfig.pxMbClientInfo[i] = pxClientInfo;
pxClientInfo->xIndex = i;
xConfig.usClientCount++;
pxClientInfo->pcIpAddr = pcClientIp;
pxClientInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] = NULL;
pxClientInfo->usTCPFrameBytesLeft = MB_TCP_FUNC;
pxClientInfo->usTCPBufPos = 0;
}
}
}
// Handle data request from client
if (xErr > 0) {
// Handling client connection requests
for (i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
MbClientInfo_t* pxClientInfo = xConfig.pxMbClientInfo[i];
if ((pxClientInfo != NULL) && (pxClientInfo->xSockId > 0)) {
if (FD_ISSET(pxClientInfo->xSockId, &xReadSet)) {
// Other sockets are ready to be read
xErr = xMBTCPPortRxPoll(pxClientInfo, MB_TCP_READ_TIMEOUT_MS);
// If an invalid data received from socket or connection fail
// or if timeout then drop connection and restart
if (xErr < 0) {
uint64_t xTimeStamp = xMBTCPGetTimeStamp();
// If data update is timed out
switch(xErr)
{
case ERR_TIMEOUT:
ESP_LOGE(TAG, "Socket (#%d)(%s), data receive timeout, time[us]: %" PRIu64 ", close active connection.",
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
(uint64_t)(xTimeStamp - pxClientInfo->xRecvTimeStamp));
break;
case ERR_CLSD:
ESP_LOGE(TAG, "Socket (#%d)(%s), connection closed by peer.",
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
break;
case ERR_BUF:
default:
ESP_LOGE(TAG, "Socket (#%d)(%s), read data error: 0x%x",
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr, (int)xErr);
break;
}
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
// Close client connection
xMBTCPPortCloseConnection(pxClientInfo);
// This client does not respond, then unregister it
vMBTCPPortFreeClientInfo(pxClientInfo);
xConfig.pxMbClientInfo[i] = NULL;
xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] = NULL;
// If no any active connections, break
if (!xConfig.usClientCount) {
xConfig.pxCurClientInfo = NULL;
break;
}
} else {
pxClientInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
// set current client info to active client from which we received request
xConfig.pxCurClientInfo = pxClientInfo;
// Complete frame received, inform state machine to process frame
xMBPortEventPost(EV_FRAME_RECEIVED);
ESP_LOGD(TAG, "Socket (#%d)(%s), get packet TID=0x%X, %d bytes.",
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
(int)pxClientInfo->usTidCnt, (int)xErr);
// Wait while response is not processed by stack by timeout
UCHAR* pucSentBuffer = vxMBTCPPortRespQueueRecv(xConfig.xRespQueueHandle);
if (pucSentBuffer == NULL) {
ESP_LOGD(TAG, "Response is ignored, time exceeds configured %d [ms].",
(unsigned)MB_TCP_RESP_TIMEOUT_MS);
} else {
USHORT usSentTid = MB_TCP_GET_FIELD(pucSentBuffer, MB_TCP_TID);
if (usSentTid != pxClientInfo->usTidCnt) {
ESP_LOGE(TAG, "Sent TID(%x) != Recv TID(%x), ignore packet.",
(int)usSentTid, (int)pxClientInfo->usTidCnt);
}
}
// Get time stamp of last data update
pxClientInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
ESP_LOGD(TAG, "Client %d, Socket(#%d), processing time = %" PRIu64 "(us).",
(int)pxClientInfo->xIndex, (int)pxClientInfo->xSockId,
(uint64_t)(pxClientInfo->xSendTimeStamp - pxClientInfo->xRecvTimeStamp));
}
} else {
if (pxClientInfo) {
// client is not ready to be read
int64_t xTime = xMBTCPGetTimeStamp() - pxClientInfo->xRecvTimeStamp;
if (xTime > MB_TCP_DISCONNECT_TIMEOUT) {
ESP_LOGE(TAG, "Client %d, Socket(#%d) do not answer for %" PRIu64 " (us). Drop connection...",
(int)pxClientInfo->xIndex, (int)pxClientInfo->xSockId, (uint64_t)xTime);
xMBTCPPortCloseConnection(pxClientInfo);
// This client does not respond, then delete registered data
vMBTCPPortFreeClientInfo(pxClientInfo);
xConfig.pxMbClientInfo[i] = NULL;
}
} else {
ESP_LOGE(TAG, "Client %d is disconnected.", (int)i);
}
}
} // if ((pxClientInfo != NULL)
} // Handling client connection requests
}
} // while(1) // Handle connection cycle
} // Main connection cycle
vTaskDelete(NULL);
}
void
vMBTCPPortClose( )
{
// Try to exit the task gracefully, so select could release its internal callbacks
// that were allocated on the stack of the task we're going to delete
xShutdownSema = xSemaphoreCreateBinary();
if (xShutdownSema == NULL || // if no semaphore (alloc issues) or couldn't acquire it, just delete the task
xSemaphoreTake(xShutdownSema, 2 * pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)) != pdTRUE) {
ESP_LOGE(TAG, "Task couldn't exit gracefully within timeout -> abruptly deleting the task");
vTaskDelete(xConfig.xMbTcpTaskHandle);
}
close(xListenSock);
xListenSock = -1;
vMBTCPPortRespQueueDelete(xConfig.xRespQueueHandle);
if (xShutdownSema) {
vSemaphoreDelete(xShutdownSema);
xShutdownSema = NULL;
}
vMBPortEventClose();
}
void vMBTCPPortEnable( void )
{
}
void
vMBTCPPortDisable( void )
{
}
BOOL
xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
{
BOOL xRet = FALSE;
if (xConfig.pxCurClientInfo) {
*ppucMBTCPFrame = &xConfig.pxCurClientInfo->pucTCPBuf[0];
*usTCPLength = xConfig.pxCurClientInfo->usTCPBufPos;
// Reset the buffer.
xConfig.pxCurClientInfo->usTCPBufPos = 0;
xConfig.pxCurClientInfo->usTCPFrameBytesLeft = MB_TCP_FUNC;
xRet = TRUE;
}
return xRet;
}
BOOL
xMBTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength )
{
BOOL bFrameSent = FALSE;
fd_set xWriteSet;
fd_set xErrorSet;
int xErr = -1;
struct timeval xTimeVal;
if (xConfig.pxCurClientInfo) {
FD_ZERO(&xWriteSet);
FD_ZERO(&xErrorSet);
FD_SET(xConfig.pxCurClientInfo->xSockId, &xWriteSet);
FD_SET(xConfig.pxCurClientInfo->xSockId, &xErrorSet);
vxMBTCPPortMStoTimeVal(MB_TCP_SEND_TIMEOUT_MS, &xTimeVal);
// Check if socket writable
xErr = select(xConfig.pxCurClientInfo->xSockId + 1, NULL, &xWriteSet, &xErrorSet, &xTimeVal);
if ((xErr == -1) || FD_ISSET(xConfig.pxCurClientInfo->xSockId, &xErrorSet)) {
ESP_LOGE(TAG, "Socket(#%d) , send select() error = %u.",
(int)xConfig.pxCurClientInfo->xSockId, (unsigned)errno);
return FALSE;
}
// Apply TID field from request to the frame before send response
pucMBTCPFrame[MB_TCP_TID] = (UCHAR)(xConfig.pxCurClientInfo->usTidCnt >> 8U);
pucMBTCPFrame[MB_TCP_TID + 1] = (UCHAR)(xConfig.pxCurClientInfo->usTidCnt & 0xFF);
// Write message into socket and disable Nagle's algorithm
xErr = send(xConfig.pxCurClientInfo->xSockId, pucMBTCPFrame, usTCPLength, TCP_NODELAY);
if (xErr < 0) {
ESP_LOGE(TAG, "Socket(#%d), fail to send data, errno = %u",
(int)xConfig.pxCurClientInfo->xSockId, (unsigned)errno);
xConfig.pxCurClientInfo->xError = xErr;
} else {
bFrameSent = TRUE;
vxMBTCPPortRespQueueSend(xConfig.xRespQueueHandle, (void*)pucMBTCPFrame);
}
} else {
ESP_LOGD(TAG, "Port is not active. Release lock.");
vxMBTCPPortRespQueueSend(xConfig.xRespQueueHandle, (void*)pucMBTCPFrame);
}
return bFrameSent;
}
#endif //#if MB_TCP_ENABLED

View File

@@ -0,0 +1,107 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 TCP Port
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
* Parts of crt0.S Copyright (c) 1995, 1996, 1998 Cygnus Support
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: port.h,v 1.2 2006/09/04 14:39:20 wolti Exp $
*/
#ifndef _PORT_TCP_SLAVE_H
#define _PORT_TCP_SLAVE_H
/* ----------------------- Platform includes --------------------------------*/
#include "esp_log.h"
#include "lwip/opt.h"
#include "lwip/sys.h"
#include "port.h"
#include "esp_modbus_common.h" // for common types for network options
/* ----------------------- Defines ------------------------------------------*/
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Type definitions ---------------------------------*/
typedef struct {
int xIndex; /*!< Modbus info index */
int xSockId; /*!< Socket id */
int xError; /*!< TCP/UDP sock error */
const char* pcIpAddr; /*!< TCP/UDP IP address (string) */
UCHAR* pucTCPBuf; /*!< buffer pointer */
USHORT usTCPBufPos; /*!< buffer active position */
USHORT usTCPFrameBytesLeft; /*!< buffer left bytes to receive transaction */
int64_t xSendTimeStamp; /*!< send request timestamp */
int64_t xRecvTimeStamp; /*!< receive response timestamp */
USHORT usTidCnt; /*!< last TID counter from packet */
} MbClientInfo_t;
typedef struct {
TaskHandle_t xMbTcpTaskHandle; /*!< Server task handle */
QueueHandle_t xRespQueueHandle; /*!< Response queue handle */
MbClientInfo_t* pxCurClientInfo; /*!< Current client info */
MbClientInfo_t** pxMbClientInfo; /*!< Pointers to information about connected clients */
USHORT usPort; /*!< TCP/UDP port number */
CHAR* pcBindAddr; /*!< IP address to bind */
eMBPortProto eMbProto; /*!< Protocol type used by port */
USHORT usClientCount; /*!< Client connection count */
void* pvNetIface; /*!< Network netif interface pointer for port */
eMBPortIpVer xIpVer; /*!< IP protocol version */
} MbSlavePortConfig_t;
/* ----------------------- Function prototypes ------------------------------*/
/**
* Function to setup communication options for TCP/UDP Modbus port
*
* @param pvNetIf netif interface pointer
* @param xIpVersion IP version
* @param xProto protocol type option
* @param pcBindAddr IP bind address
*
* @return error code
*/
void vMBTCPPortSlaveSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto, CHAR* pcBindAddr);
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif