Merge branch 'feature/add_extended_float_support_v2' into 'release/v2.0'

add extended float support to v2

See merge request idf/esp-modbus!60
This commit is contained in:
Alex Lisitsyn
2024-04-19 17:36:32 +08:00
42 changed files with 3168 additions and 368 deletions

View File

@@ -75,6 +75,7 @@ after_script:
--manifest-file .build-test-rules.yml \
--parallel-count ${CI_NODE_TOTAL:-1} \
--parallel-index ${CI_NODE_INDEX:-1}
echo "delete build folders:" $(find . -type d -regex '^\./.*build_esp32[a-z]+[0-9]+[_a-z]*' -print -exec rm -rf {} +)
ls -lh > test_dir_${PWD##*/}.txt
# This template gets expanded multiple times, once for every IDF version.

View File

@@ -46,6 +46,10 @@ set(include_dirs mb_transports mb_controller/common/include mb_objects/include m
set(priv_include_dirs mb_controller/serial mb_controller/tcp mb_controller/common mb_transports/rtu mb_transports/ascii mb_transports/tcp)
if(CONFIG_FMB_EXT_TYPE_SUPPORT)
list(APPEND srcs "mb_controller/common/mb_endianness_utils.c")
endif()
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/modbus/" ${srcs})
add_prefix(include_dirs "${CMAKE_CURRENT_LIST_DIR}/modbus/" ${include_dirs})
add_prefix(priv_include_dirs "${CMAKE_CURRENT_LIST_DIR}/modbus/" ${priv_include_dirs})

17
Kconfig
View File

@@ -16,7 +16,7 @@ menu "Modbus configuration"
config FMB_TCP_PORT_MAX_CONN
int "Maximum allowed connections for TCP stack"
range 1 6
range 1 8
default 5
depends on FMB_COMM_MODE_TCP_EN
help
@@ -66,7 +66,7 @@ menu "Modbus configuration"
config FMB_MASTER_DELAY_MS_CONVERT
int "Slave conversion delay (Milliseconds)"
default 200
range 50 400
range 150 2000
help
If master sends a broadcast frame, it has to wait conversion time to delay,
then master can send next frame.
@@ -108,7 +108,7 @@ menu "Modbus configuration"
config FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS
int "Response timeout for ASCII communication mode (ms)"
default 1000
range 300 4000
range 200 5000
depends on FMB_COMM_MODE_ASCII_EN
help
This option defines response timeout of slave in milliseconds for ASCII communication mode.
@@ -116,7 +116,7 @@ menu "Modbus configuration"
config FMB_PORT_TASK_PRIO
int "Modbus port task priority"
range 3 21
range 3 23
default 10
help
Modbus port data processing task priority.
@@ -206,4 +206,13 @@ menu "Modbus configuration"
This option has dependency with the UART_ISR_IN_IRAM option which places UART interrupt
handler into IRAM to prevent delays related to processing of UART events.
config FMB_EXT_TYPE_SUPPORT
bool "Modbus uses extended types to support third party devices"
default n
help
If this option is set the Modbus stack supports extended list of types
in data dictionary and conversion API to work with the extended types
otherwise the only legacy types are supported. The extended types include
integer, float, double types with different endianness and size.
endmenu

View File

@@ -21,8 +21,10 @@ PROJECT_NAME = "IDF Programming Guide"
## and used to include in API reference documentation
INPUT = \
$(PROJECT_PATH)/modbus/mb_controller/common/include/esp_modbus_common.h \
$(PROJECT_PATH)/modbus/mb_controller/common/include/esp_modbus_slave.h \
$(PROJECT_PATH)/modbus/mb_controller/common/include/esp_modbus_master.h \
$(PROJECT_PATH)/modbus/mb_controller/common/include/mb_endianness_utils.h
## Get warnings for functions that have no documentation for their parameters or return value
##

37
docs/_static/diag_frame.diag vendored Normal file
View File

@@ -0,0 +1,37 @@
# Modbus float_abcd frame structure diagram
blockdiag mb_float_frame {
# global properties
span_width = 2;
span_height = 5;
node_height = 25;
default_fontsize = 16;
default_group_color = lightgrey;
class spacer [shape=none, width=10];
# tuning node properties and connections
0,1,2 [class=spacer];
0; note
1; header
2; response -- uid -- cmd -- len -- fl_abcd -- crc
group float_abcd_packet {
label = "PDU";
color = gray;
shape = line;
style = dashed;
group{uid,resp_uid};group{cmd,resp_cmd};group{len,resp_len};group{crc,resp_crc};
group float_abcd{
color = blue;
shape = line;
style = dashed;
fl_abcd;dt_abcd;
}
}
note[label="1: Unit Identificator, 2: Function code, 3: Data length, 4: Float data array, 5: Checksum",colwidth=6,color=lightyellow,shape=roundedbox]
header[label="FLOAT_ABCD = 0x4640e400 = 12345.0",colwidth=6,color=lightgreen]
response[label="RX:",color=yellow];
uid[label="UID",numbered=1];cmd[label="FC",numbered=2];
len[label="LENGTH",numbered=3];crc[label="CRC",numbered=5];
resp_uid[label="0x01"];resp_cmd[label="0x03"];resp_len[label="0x08"];resp_crc[label="0x9065"];
fl_abcd[label="FLOAT_ABCD",color=lightgreen,numbered=4];
dt_abcd[label="0xE4004640",shape=note];
}

View File

@@ -1,16 +1,20 @@
var DOCUMENTATION_VERSIONS = {
DEFAULTS: { has_targets: true,
supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ]
supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c2","esp32c3", "esp32c6", "esp32s2", "esp32s3, esp32p4" ]
},
VERSIONS: [
{ name: "latest" },
{ name: "v1.0.1", old:false },
{ name: "v1.0.0", old:true }
{ name: "v1.0.1", old:true },
{ name: "v2.0.0", old:false }
],
IDF_TARGETS: [
{ text: "ESP32", value: "esp32"},
{ text: "ESP32-S2", value: "esp32s2"},
{ text: "ESP32-S3", value: "esp32s3"},
{ text: "ESP32-C3", value: "esp32c3"}
{ text: "ESP32-C2", value: "esp32c2"},
{ text: "ESP32-C3", value: "esp32c3"},
{ text: "ESP32-C6", value: "esp32c6"},
{ text: "ESP32-H2", value: "esp32h2"},
{ text: "ESP32-P4", value: "esp32p4"}
]
};

61
docs/_static/modbus_frame_examples.diag vendored Normal file
View File

@@ -0,0 +1,61 @@
# Modbus frame packaging examples
blockdiag mb_master_frames {
# global properties
span_width = 5;
span_height = 5;
node_height = 25;
default_group_color = lightgrey;
default_fontsize = 15;
# tuning node properties and connections
group 16bit_packets {
label = "16bit frame";
color = red;
shape = line;
style = dashed;
16bit_notes;
}
group 32bit_packets {
label = "32bit frame";
color = green;
shape = line;
style = dashed;
group{32bit_notes};
}
group 64bit_packets {
label = "64bit frame";
color = blue;
shape = line;
style = dashed;
64bit_notes;
}
16bit_notes[label="UINT16, INT16 VALUE = 0x3039 = (uint16_t)12345", width=600, color=orange, shape = roundedbox];
req_u16_hd1[label= "TX:| UID | FC | REG_START | REG_LEN | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=16bit_packets];
req_u16_frm1[label="TX:| 01 | 03 | 00 04 | 00 02 | 85 CA |", color=lightgrey, width=380, colwidth=2,group=16bit_packets];
rsp_u16_hd1[label= "RX:| UID | FC | LEN | UINT16_AB1 | UINT16_AB2 | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=16bit_packets];
rsp_u16_frm1[label="RX:| 01 | 03 | 04 | 30 39 | 30 39 | F1 2C |", color=lightgrey, width=380, colwidth=2,group=16bit_packets];
rsp_u16_hd2[label= "RX:| UID | FC | LEN | UINT16_BA1 | UINT16_BA2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=16bit_packets];
rsp_u16_frm2[label="RX:| 01 | 03 | 04 | 39 30 | 39 30 | E4 E4 |\n", color=lightgrey, width=380, colwidth=2,group=16bit_packets];
32bit_notes[label="(UINT32, INT32) FLOAT32 VALUE = 0x4640e400 = 12345.0", width=600, color=lightgreen, shape = roundedbox];
req_fl_hd1[label= "TX:| UID | FC | REG_START | REG_LEN | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=32bit_packets];
req_fl_frm1[label="TX:| 01 | 03 | 00 XX | 00 04 | C5 CB |", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd1[label= "RX:| UID | FC | LEN | FLOAT_ABCD1 | FLOAT_ABCD2 | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=32bit_packets];
rsp_fl_frm1[label="RX:| 01 | 03 | 08 | E4 00 46 40 | E4 00 46 40 | 90 65 |", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd2[label= "RX:| UID | FC | LEN | FLOAT_CDAB1 | FLOAT_CDAB2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=32bit_packets];
rsp_fl_frm2[label="RX:| 01 | 03 | 08 | 46 40 E4 00 | 46 40 E4 00 | 18 71 |\n", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd3[label= "RX:| UID | FC | LEN | FLOAT_BADC1 | FLOAT_BADC2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=32bit_packets];
rsp_fl_frm3[label="RX:| 01 | 03 | 08 | 00 E4 40 46 | 00 E4 40 46 | 46 D3 |\n", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd4[label= "RX:| UID | FC | LEN | FLOAT_DCAB1 | FLOAT_DCAB2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=32bit_packets];
rsp_fl_frm4[label="RX:| 01 | 03 | 08 | 40 46 00 E4 | 40 46 00 E4 | 32 6B |\n", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
64bit_notes[label="(UINT64, INT64) FLOAT64 VALUE = 0x40c81c8000000000 = 12345.0", width=600, color=lightblue, shape = roundedbox];
req_dbl_hd1[label= "TX:| UID | FC | REG_START | REG_LEN | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=64bit_packets];
req_dbl_frm1[label="TX:| 01 | 03 | 00 28 | 00 08 | C4 04 |", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd1[label= "RX:| UID | FC | LEN | DOUBLE_ABCDEFGH1 | DOUBLE_ABCDEFGH2 | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=64bit_packets];
rsp_dbl_frm1[label="RX:| 01 | 03 | 10 | 00 00 00 00 1C 80 40 C8 | 00 00 00 00 1C 80 40 C8 | 9F 4B |", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd2[label= "RX:| UID | FC | LEN | DOUBLE_HGFEDCBA1 | DOUBLE_HGFEDCBA2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=64bit_packets];
rsp_dbl_frm2[label="RX:| 01 | 03 | 10 | C8 40 80 1C 00 00 00 00 | C8 40 80 1C 00 00 00 00 | DF D3 |\n", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd3[label= "RX:| UID | FC | LEN | DOUBLE_GHEFCDAB1 | DOUBLE_GHEFCDAB2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=64bit_packets];
rsp_dbl_frm3[label="RX:| 01 | 03 | 10 | 40 C8 1C 80 00 00 00 00 | 40 C8 1C 80 00 00 00 00 | B1 9C |\n", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd4[label= "RX:| UID | FC | LEN | DOUBLE_BADCFEHG1 | DOUBLE_BADCFEHG2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=64bit_packets];
rsp_dbl_frm4[label="RX:| 01 | 03 | 10 | 00 00 00 00 80 1C C8 40 | 00 00 00 00 80 1C C8 40 | 86 94 |\n", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
}

View File

@@ -29,5 +29,5 @@ html_static_path = ['../_static']
project_slug = 'esp-modbus'
versions_url = './_static/modbus_docs_versions.js'
idf_targets = ['esp32', 'esp32c2', 'esp32c3', 'esp32c6', 'esp32h2', 'esp32s2', 'esp32s3']
idf_targets = [ 'esp32' ]
languages = ['en']

View File

@@ -66,6 +66,13 @@ Protocol References
API Reference
-------------
.. include-build-file:: inc/esp_modbus_common.inc
.. include-build-file:: inc/esp_modbus_master.inc
.. include-build-file:: inc/esp_modbus_slave.inc
.. _modbus_api_endianness_conversion:
Modbus Endianness Conversion API Reference
------------------------------------------
.. include-build-file:: inc/mb_endianness_utils.inc

View File

@@ -16,10 +16,7 @@ The following overview describes how to setup Modbus master communication. The o
Configuring Master Data Access
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The architectural approach of ESP_Modbus includes one level above standard Modbus IO driver.
The additional layer is called Modbus controller and its goal is to add an abstraction such as CID - characteristic identifier.
The CID is linked to a corresponding Modbus registers through the table called Data Dictionary and represents device physical parameter (such as temperature, humidity, etc.) in specific Modbus slave device.
This approach allows the upper layer (e.g., MESH or MQTT) to be isolated from Modbus specifics thus simplify Modbus integration with other protocols/networks.
The architectural approach of ESP_Modbus includes one level above standard Modbus IO driver. The additional layer is called Modbus controller and its goal is to add an abstraction such as CID - characteristic identifier. The CID is linked to a corresponding Modbus registers through the table called Data Dictionary and represents device physical parameter (such as temperature, humidity, etc.) in specific Modbus slave device. This approach allows the upper layer (e.g., MESH or MQTT) to be isolated from Modbus specifics thus simplify Modbus integration with other protocols/networks.
The Data Dictionary is the list in the Modbus master which shall be defined by user to link each CID to its corresponding Modbus registers representation using Register Mapping table of the Modbus slave being used.
Each element in this data dictionary is of type :cpp:type:`mb_parameter_descriptor_t` and represents the description of one physical characteristic:
@@ -52,20 +49,17 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
- Relative register address of the characteristic in the register area.
* - ``mb_size``
- Modbus Register Size
- Length of characteristic in registers.
- Length of characteristic in registers (two bytes).
* - ``param_offset``
- Instance Offset
- Offset to instance of the characteristic in bytes. It is used to calculate the absolute address to the characteristic in the storage structure.
It is optional field and can be set to zero if the parameter is not used in the application.
* - ``param_type``
- Data Type
- Specifies type of the characteristic.
:cpp:enumerator:`PARAM_TYPE_U8`, :cpp:enumerator:`PARAM_TYPE_U16`, :cpp:enumerator:`PARAM_TYPE_U32` - Unsigned integer 8/16/32 bit type;
:cpp:enumerator:`PARAM_TYPE_FLOAT` - IEEE754 floating point format;
:cpp:enumerator:`PARAM_TYPE_ASCII` - ASCII string or binary data;
- Data Type
- Specifies type of the characteristic. Possible types are described in the section :ref:`modbus_mapping_complex_data_types`.
* - ``param_size``
- Data Size
- The storage size of the characteristic (bytes).
- The storage size of the characteristic (in bytes) describes the size of data to keep into data instance during mapping. For the :ref:`modbus_mapping_complex_data_types` this allows to define the data container of the corresponded type.
* - ``param_opts``
- Parameter Options
- Limits, options of characteristic used during processing of alarm in user application (optional)
@@ -76,6 +70,13 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
.. note:: The ``cid`` and ``param_key`` have to be unique. Please use the prefix to the parameter key if you have several similar parameters in your register map table.
Examples Of Mapping
@@@@@@@@@@@@@@@@@@@
Please refer to section :ref:`modbus_mapping_complex_data_types` for more information about used data types.
Example 1: Configure access to legacy parameter types is described below.
.. list-table:: Table 2 Example Register mapping table of Modbus slave
:widths: 5 5 2 10 5 5 68
:header-rows: 1
@@ -115,6 +116,7 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
- ASCII or binary array
- Not defined
- Device name (16 bytes) ASCII string. The type of `PARAM_TYPE_ASCII` allows to read/write complex parameter (string or binary data) that corresponds to one CID.
.. code:: c
// Enumeration of modbus slave addresses accessed by master device
@@ -160,6 +162,133 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
// Calculate number of parameters in the table
uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0]));
Example 2: Configure access using extended parameter types for third-party devices.
.. list-table:: Table 3 Example Register mapping table of Modbus slave
:widths: 2 4 2 10 3 68
:header-rows: 1
* - CID
- Register
- Length
- Range
- Units
- Description
* - 0
- 40000
- 4
- 0 ... 255
- No units
- :cpp:enumerator:`PARAM_TYPE_U8_A` - unsigned integer 8-bit
* - 1
- 40002
- 4
- 0 ... 65535
- No Units
- :cpp:enumerator:`PARAM_TYPE_U16_AB` uinsigned integer 16-bit
* - 3
- 40004
- 8
- 0 ... Unsigned integer 32-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_U32_ABCD` - unsigned integer 32-bit in ABCD format
* - 4
- 40008
- 8
- 0 ... Unsigned integer 32-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_FLOAT_CDAB` - FLOAT 32-bit value in CDAB format
* - 5
- 400012
- 16
- 0 ... Unsigned integer 64-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_U64_ABCDEFGH` - Unsigned integer 64-bit value in ABCDEFGH format
* - 6
- 400020
- 16
- 0 ... Unsigned integer 64-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_DOUBLE_HGFEDCBA` - Double precision 64-bit value in HGFEDCBA format
.. code:: c
#include "limits.h"
#include "mbcontroller.h"
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
#define HOLD_REG_START(field) (HOLD_OFFSET(field) >> 1)
#define HOLD_REG_SIZE(field) (sizeof(((holding_reg_params_t *)0)->field) >> 1)
#pragma pack(push, 1)
// Example structure that contains parameter arrays of different types
// with different options of endianness.
typedef struct
{
uint16_t holding_u8_a[2];
uint16_t holding_u16_ab[2];
uint32_t holding_uint32_abcd[2];
float holding_float_cdab[2];
double holding_uint64_abcdefgh[2];
double holding_double_hgfedcba[2];
} holding_reg_params_t;
#pragma pack(pop)
// Enumeration of modbus slave addresses accessed by master device
enum {
MB_DEVICE_ADDR1 = 1, // Short address of Modbus slave device
MB_SLAVE_COUNT
};
// Enumeration of all supported CIDs for device (used in parameter definition table)
enum {
CID_HOLD_U8_A = 0,
CID_HOLD_U16_AB,
CID_HOLD_UINT32_ABCD,
CID_HOLD_FLOAT_CDAB,
CID_HOLD_UINT64_ABCDEFGH,
CID_HOLD_DOUBLE_HGFEDCBA,
CID_COUNT
};
// Example Data Dictionary for to address parameters from slaves with different options of endianness
mb_parameter_descriptor_t device_parameters[] = {
// CID, Name, Units, Modbus addr, register type, Modbus Reg Start Addr, Modbus Reg read length,
// Instance offset (NA), Instance type, Instance length (bytes), Options (NA), Permissions
{ CID_HOLD_U8_A, STR("U8_A"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_u8_a), HOLD_REG_SIZE(holding_u8_a),
HOLD_OFFSET(holding_u8_a), PARAM_TYPE_U8_A, (HOLD_REG_SIZE(holding_u8_a) << 1),
OPTS( 0, UCHAR_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_AB, STR("U16_AB"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_u16_ab), HOLD_REG_SIZE(holding_u16_ab),
HOLD_OFFSET(holding_u16_ab), PARAM_TYPE_U16_AB, (HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, USHRT_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_ABCD, STR("UINT32_ABCD"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_uint32_abcd), HOLD_REG_SIZE(holding_uint32_abcd),
HOLD_OFFSET(holding_uint32_abcd), PARAM_TYPE_U32_ABCD, (HOLD_REG_SIZE(holding_uint32_abcd) << 1),
OPTS( 0, ULONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_CDAB, STR("FLOAT_CDAB"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_float_cdab), HOLD_REG_SIZE(holding_float_cdab),
HOLD_OFFSET(holding_float_cdab), PARAM_TYPE_FLOAT_CDAB, (HOLD_REG_SIZE(holding_float_cdab) << 1),
OPTS( 0, ULONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT64_ABCDEFGH, STR("UINT64_ABCDEFGH"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_uint64_abcdefgh), HOLD_REG_SIZE(holding_uint64_abcdefgh),
HOLD_OFFSET(holding_uint64_abcdefgh), PARAM_TYPE_UINT64_ABCDEFGH, (HOLD_REG_SIZE(holding_uint64_abcdefgh) << 1),
OPTS( 0, ULLONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_HGFEDCBA, STR("DOUBLE_HGFEDCBA"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_double_hgfedcba), HOLD_REG_SIZE(holding_double_hgfedcba),
HOLD_OFFSET(holding_double_hgfedcba), PARAM_TYPE_DOUBLE_HGFEDCBA, (HOLD_REG_SIZE(holding_double_hgfedcba) << 1),
OPTS( 0, ULLONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
};
uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0]));
The example above describes the definition of just several extended types. The types described in the :ref:`modbus_mapping_complex_data_types` allow to address the most useful value formats from devices of known third-party vendors.
Once the type of characteristic is defined in data dictionary the stack is responsible for conversion of values to/from the corresponding type option into the format recognizable by compiler.
.. note:: Please refer to your vendor device manual and its mapping table to select the types suitable for your device.
The Modbus stack contains also the :ref:`modbus_api_endianness_conversion` - endianness conversion API functions that allow to convert values from/to each extended type into compiler representation.
During initialization of the Modbus stack, a pointer to the Data Dictionary (called descriptor) must be provided as the parameter of the function below.
:cpp:func:`mbc_master_set_descriptor`:
@@ -174,6 +303,62 @@ Initialization of master descriptor. The descriptor represents an array of type
ESP_ERROR_CHECK(mbc_master_set_descriptor(master_handle, &device_parameters[0], num_device_parameters));
The Data Dictionary can be initialized from SD card, MQTT or other source before start of stack. Once the initialization and setup is done, the Modbus controller allows the reading of complex parameters from any slave included in descriptor table using its CID.
Refer to :ref:`example TCP master <example_mb_tcp_master>`, :ref:`example Serial master <example_mb_master>` for more information.
.. _modbus_api_master_setup_communication_options:
Master Communication Options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Calling the setup function allows for specific communication options to be defined for port.
:cpp:func:`mbc_master_setup`
The communication structure provided as a parameter is different for serial and TCP communication mode.
Example setup for serial port:
.. code:: c
mb_communication_info_t comm_info = {
.port = MB_PORT_NUM, // Serial port number
.mode = MB_MODE_RTU, // Modbus mode of communication (MB_MODE_RTU or MB_MODE_ASCII)
.baudrate = 9600, // Modbus communication baud rate
.parity = MB_PARITY_NONE // parity option for serial port
};
ESP_ERROR_CHECK(mbc_master_setup((void*)&comm_info));
Modbus master TCP port requires additional definition of IP address table where number of addresses should be equal to number of unique slave addresses in master Modbus Data Dictionary:
The order of IP address string corresponds to short slave address in the Data Dictionary.
.. code:: c
#define MB_SLAVE_COUNT 2 // Number of slaves in the segment being accessed (as defined in Data Dictionary)
char* slave_ip_address_table[MB_SLAVE_COUNT] = {
"192.168.1.2", // Address corresponds to UID1 and set to predefined value by user
"192.168.1.3", // corresponds to UID2 in the segment
NULL // end of table
};
mb_communication_info_t comm_info = {
.ip_port = MB_TCP_PORT, // Modbus TCP port number (default = 502)
.ip_addr_type = MB_IPV4, // version of IP protocol
.ip_mode = MB_MODE_TCP, // Port communication mode
.ip_addr = (void*)slave_ip_address_table, // assign table of IP addresses
.ip_netif_ptr = esp_netif_ptr // esp_netif_ptr pointer to the corresponding network interface
};
ESP_ERROR_CHECK(mbc_master_setup((void*)&comm_info));
.. note:: Refer to `esp_netif component <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_netif.html>`__ for more information about network interface initialization.
The slave IP addresses in the table can be assigned automatically using mDNS service as described in the example.
Refer to :ref:`example TCP master <example_mb_tcp_master>` for more information.
.. note:: RS485 communication requires call to UART specific APIs to setup communication mode and pins. Refer to the `UART communication section <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#uart-api-running-uart-communication>`__ in documentation.
.. _modbus_api_master_start_communication:

View File

@@ -39,9 +39,212 @@ The Modbus protocol allows devices to map data to four types of registers (Holdi
Modbus data mapping
The following sections give an overview of how to use the ESP_Modbus component. The sections cover initialization of a Modbus port, and the setup a master or slave device accordingly:
.. _modbus_mapping_complex_data_types:
Mapping Of Complex Data Types
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
As per section 4.2 of Modbus specification, "MODBUS uses a ``big-Endian`` representation for addresses and data items. This means that when a numerical quantity larger than a single byte is transmitted, the most significant byte is sent first". The biggest official structure defined by the Modbus specification is a 16-bit word register, which is 2 bytes. However, vendors sometimes group two or even four 16-bit registers together to be interpretted as 32-bit or 64-bit values, respectively. It is also possible when the Modbus vendors group many registers together for serial numbers, text strings, time/date, etc. Regardless of how the vendor intends the data to be interpreted, the Modbus protocol itself simply transfers 16-bit word registers. These values grouped from registers may use either little-endian or big-endian register order.
.. note:: Each individual 16-bit register, is encoded in big-endian order (assuming the Modbus device abides by the Modbus specification). However, the 32-bit and 64-bit types naming conventions like ABCD or ABCDEFGH, does not take into account the network format byte order of frame. For example: the ABCD prefix for 32-bit values means the common Modbus mapping format and corresponds to the CDAB on network format (order in the frame).
Common Data Types Supported By Modbus Vendors
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.. list-table:: Table 1 basic types used by Modbus vendors
:widths: 8 3 20
:header-rows: 1
* - Type
- Range
- Format description
* - U8, I8 - Unsigned/Signed 8-bit type
- (0 .. 255)/(-128 .. 127)
- Common unsigned 8-bit type that is stored usually in one Modbus register. The value can be stored in HI or LO byte of the register or packed with the next byte into one 16 - bit register.
* - U16 - Unsigned integer 16-bit type
- 0 - 65535
- Stored in one 16-bit register. The values can be stored with AB or BA endianness.
* - I16 - Signed integer 16-bit type
- -32768 to 32767 is allowed.
- Stored in one 16-bit register. The values can be stored with AB or BA forendiannessmat.
* - I32 - Signed long integer 32-bit type
- -2147483648 to 2147483647 is allowed.
- Stored in two consecutive 16-bit register. The values can be stored with ABCD - DCBA endianness (see below).
* - U32 - Unsigned long integer 32-bit type
- 0 to 4294967295 is allowed.
- Stored in two consecutive 16-bit register. The values can be stored with ABCD - DCBA endianness.
* - U64 Unsigned Long long integers (Unsigned integer 64)
- 0 to 18446744073709551615 is allowed.
- Stored in four consecutive 16-bit register. The values can be stored with ABCDEFGH - BADCFEHG endianness.
* - I64 Signed Long long integers (Signed integer 64)
- -9223372036854775808 to 9223372036854775807 is allowed.
- Stored in four consecutive 16-bit register. The values can be stored with ABCDEFGH - BADCFEHG endianness.
* - Floating point single precision 32-bit
- 1.17549435E-38 to 3.40282347E+38 is allowed.
- Stored in two consecutive 16-bit register per IEEE754. The values can be stored with ABCD - DCBA endianness.
* - Floating point double precision 64-bit
- +/-5.0E-324 to +/-1.7E+308 is allowed.
- Stored in four consecutive 16-bit register per IEEE754. The values can be stored with ABCDEFGH - BADCFEHG endianness.
As showed in the table above the float and double types do not fit to the 16-bit register and reguire several consecutive registers be used to store the value. However, different manufacturers store the consecutive bytes in different order (not standardized). For example: The DCBA prefix means inversed Modbus format (BADC order on network format).
.. list-table:: Table 2 Modbus byte order for extended types
:widths: 3 28
:header-rows: 1
* - Postfix
- Format description
* - ABCD
- Big endian, high order byte first
* - CDAB
- Big endian, reversed register order (Little endian with byte swap)
* - BADC
- Little endian, reversed register order (Big endian with byte swap)
* - DCBA
- Little endian (Low order byte first)
The extended data types are used to define all possible combinations of groupped values are represented below and correspond to ``param_type`` field of the data dictionary as described in the table below:
.. list-table:: Table 3 Modbus extended data types of characteristics
:widths: 6 28 10
:header-rows: 1
* - Type
- Format type description (common format)
- Format type (network format)
* - :cpp:enumerator:`PARAM_TYPE_U8`
- compatibility type corresponds to :cpp:enumerator:`PARAM_TYPE_U8_A`
- Unsigned integer 8 bit type
* - :cpp:enumerator:`PARAM_TYPE_U16`
- Unsigned integer 16 bit type, corresponds to :cpp:enumerator:`PARAM_TYPE_U16_AB`
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U32`
- Default unsigned integer 32 bit type, corresponds to :cpp:enumerator:`PARAM_TYPE_U32_ABCD`
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_FLOAT`
- Default unsigned integer 32 bit type, corresponds to :cpp:enumerator:`PARAM_TYPE_FLOAT_ABCD`
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_ASCII`
- Default ASCII string format
- Packed ASCII string data
* - :cpp:enumerator:`PARAM_TYPE_BIN`
- Binary data type
- Default type for binary packed data
* - :cpp:enumerator:`PARAM_TYPE_I8_A`
- I8 signed integer in low byte of register, high byte is zero
- I8 signed integer LO
* - :cpp:enumerator:`PARAM_TYPE_I8_B`
- I8 signed integer in high byte of register, low byte is zero
- I8 signed integer HI
* - :cpp:enumerator:`PARAM_TYPE_U8_A`
- U8 unsigned integer written to low byte of register, high byte is zero
- U8 unsigned integer LO
* - :cpp:enumerator:`PARAM_TYPE_U8_B`
- U8 unsigned integer written to hi byte of register, low byte is zero
- U8 unsigned integer HI
* - :cpp:enumerator:`PARAM_TYPE_I16_AB`
- I16 signed integer, big endian
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_I16_BA`
- I16 signed integer, little endian
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_U16_AB`
- U16 unsigned integer, big endian
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_U16_BA`
- U16 unsigned integer, little endian
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_I32_ABCD`
- I32 ABCD signed integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I32_CDAB`
- I32 CDAB signed integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_I32_BADC`
- I32 BADC signed integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_I32_DCBA`
- I32 DCBA signed integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U32_ABCD`
- U32 ABCD unsigned integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U32_CDAB`
- U32 CDAB unsigned integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_U32_BADC`
- U32 BADC unsigned integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_U32_DCBA`
- U32 DCBA unsigned integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_ABCD`
- Float ABCD floating point, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_CDAB`
- Float CDAB floating point, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_BADC`
- Float BADC floating point, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_DCBA`
- Float DCBA floating point, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I64_ABCDEFGH`
- I64, ABCDEFGH signed integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I64_HGFEDCBA`
- I64, HGFEDCBA signed integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I64_GHEFCDAB`
- I64, GHEFCDAB signed integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_I64_BADCFEHG`
- I64, BADCFEHG signed integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_U64_ABCDEFGH`
- U64, ABCDEFGH unsigned integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U64_HGFEDCBA`
- U64, HGFEDCBA unsigned integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U64_GHEFCDAB`
- U64, GHEFCDAB unsigned integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_U64_BADCFEHG`
- U64, BADCFEHG unsigned integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_ABCDEFGH`
- Double ABCDEFGH floating point, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_HGFEDCBA`
- Double HGFEDCBA floating point, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_GHEFCDAB`
- Double GHEFCDAB floating point, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_BADCFEHG`
- Double BADCFEHG floating point, little endian, reversed register order
- Little endian
.. note:: The support for the extended data types should be enabled using the option ``CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND`` in kconfig menu.
The below diagrams show how the extended data types appear on network layer.
.. blockdiag:: /../_static/diag_frame.diag
:scale: 80%
:caption: Modbus master response with ABCD frame
:align: center
.. blockdiag:: /../_static/modbus_frame_examples.diag
:scale: 80%
:caption: Modbus frame packaging examples (16-bit, 32-bit, 64-bit data)
:align: center
The approach showed above can be used to pack the data into MBAP frames used by Modbus TCP as well as for other types with similar size.
The following sections give an overview of how to use the ESP_Modbus component found under `components/freemodbus`. The sections cover initialization of a Modbus port, and the setup a master or slave device accordingly:
- :ref:`modbus_api_port_initialization`
- :ref:`modbus_api_slave_overview`
- :ref:`modbus_api_master_overview`

View File

@@ -18,6 +18,27 @@ Calling the constructor function allows to create communication object with the
// is used later as a first parameter for each API call
static void *master_handle = NULL;
ESP_ERROR_CHECK(mbc_master_create_serial(&config, &master_handle));
...
.. code:: c
static void *master_handle = NULL;
ESP_ERROR_CHECK(mbc_master_create_tcp(&config, &master_handle));
...
.. code:: c
static void *slave_handle = NULL;
ESP_ERROR_CHECK(mbc_slave_create_tcp(&config, &slave_handle));
...
.. code:: c
static void *slave_handle = NULL;
ESP_ERROR_CHECK(mbc_slave_create_serial(&config, &slave_handle));
...
Refer to :ref:`modbus_api_master_setup_communication_options` and :ref:`modbus_api_slave_setup_communication_options` for more information on how to configure communication options for the master and slave object accordingly.
.. _modbus_api_master_setup_communication_options:

View File

@@ -5,7 +5,7 @@ Modbus Slave API Overview
The sections below represent typical programming workflow for the slave API which should be called in following order:
1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface for the selected port.
1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface using communication options.
2. :ref:`modbus_api_slave_configure_descriptor` - Configure data descriptors to access slave parameters.
3. :ref:`modbus_api_slave_setup_communication_options` - Allows to setup communication options for selected port.
4. :ref:`modbus_api_slave_communication` - Start stack and sending / receiving data. Filter events when master accesses the register areas.
@@ -71,12 +71,37 @@ The function initializes Modbus communication descriptors for each type of Modbu
At least one area descriptor per each Modbus register type must be set in order to provide register access to its area. If the master tries to access an undefined area, the stack will generate a Modbus exception.
The stack supports the extended data types when enabled through the the option ``CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND`` in kconfig menu.
In this case the mapped data values can be initialized to specific format using :ref:`modbus_api_endianness_conversion`.
Please refer to secton :ref:`modbus_mapping_complex_data_types` for more information about data types.
Example initialization of mapped values:
.. code:: c
#include "mbcontroller.h" // for mbcontroller defines and api
val_32_arr holding_float_abcd[2] = {0};
val_64_arr holding_double_ghefcdab[2] = {0};
...
// set the Modbus parameter to specific format
portENTER_CRITICAL(&param_lock); // critical section is required if the stack is active
mb_set_float_abcd(&holding_float_abcd[0], (float)12345.0);
mb_set_float_abcd(&holding_float_abcd[1], (float)12345.0);
mb_set_double_ghefcdab(&holding_double_ghefcdab[0], (double)12345.0);
portEXIT_CRITICAL(&param_lock);
...
// The actual abcd formatted value can be converted to actual float represenatation as below
ESP_LOGI("TEST", "Test value abcd: %f", mb_get_float_abcd(&holding_float_abcd[0]));
ESP_LOGI("TEST", "Test value abcd: %f", mb_get_float_abcd(&holding_float_abcd[1]));
ESP_LOGI("TEST", "Test value ghefcdab: %lf", mb_get_double_ghefcdab(&holding_double_ghefcdab[0]));
...
.. _modbus_api_slave_communication:
Slave Communication
^^^^^^^^^^^^^^^^^^^
The function below is used to start Modbus controller interface and allows communication.
The function below is used to start Modbus controller interface and allows communication.
:cpp:func:`mbc_slave_start`
@@ -130,7 +155,7 @@ Example to get event when holding or input registers accessed in the slave:
const char* rw_str = (reg_info.type & MB_READ_MASK) ? "READ" : "WRITE";
// Filter events and process them accordingly
if (event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
if (reg_info.type & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
rw_str,
(uint32_t)reg_info.time_stamp,
@@ -138,7 +163,7 @@ Example to get event when holding or input registers accessed in the slave:
(uint32_t)reg_info.type,
(uint32_t)reg_info.address,
(uint32_t)reg_info.size);
} else if (event & (MB_EVENT_INPUT_REG_RD)) {
} else if (reg_info.type & (MB_EVENT_INPUT_REG_RD)) {
ESP_LOGI(TAG, "INPUT %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
rw_str,
(uint32_t)reg_info.time_stamp,
@@ -159,9 +184,20 @@ The direct access to slave register area from user application must be protected
static void *slave_handle = NULL; // communication object handle
...
(void)mbc_slave_lock(slave_handle); // ignore the returned error if the object is not actual
holding_reg_area[2] += 10; // the data is part of initialized register area accessed by slave
holding_reg_area[1] += 10; // the data is part of initialized register area accessed by slave
(void)mbc_slave_unlock(slave_handle);
The access to registered area shared between several slave objects from user application must be protected by critical section base on spin lock:
.. code:: c
#include "freertos/FreeRTOS.h"
...
static portMUX_TYPE g_spinlock = portMUX_INITIALIZER_UNLOCKED;
...
portENTER_CRITICAL(&param_lock);
holding_reg_area[2] = 123;
portEXIT_CRITICAL(&param_lock);
.. _modbus_api_slave_destroy:
Modbus Slave Teardown

View File

@@ -1,7 +1,7 @@
#!/bin/bash
rm -rf docs
build-docs --target esp32 --language en
build-docs --target esp32 --language en && true
# Modifes target field of html files
ELEMENT="<script type='text/javascript'>

View File

@@ -1 +1 @@
esp-docs==0.2.4
esp-docs>=1.8,<2.0

View File

@@ -39,7 +39,7 @@ class ModbusTestDut(IdfDut):
TEST_IP_PROMPT = r'Waiting IP([0-9]{1,2}) from stdin:\r\r\n'
TEST_IP_SET_CONFIRM = r'.*IP\([0-9]+\) = \[([0-9a-zA-Z\.\:]+)\] set from stdin.*'
TEST_IP_ADDRESS_REGEXP = r'.*example_[a-z]+: .* IPv4 [a-z]+:.* ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*'
TEST_APP_NAME = r'I \([0-9]+\) cpu_start: Project name: ([_a-z]*)'
TEST_APP_NAME = r'.*Project name: ([_a-z]*)'
TEST_EXPECT_STR_TIMEOUT = 120
TEST_ACK_TIMEOUT = 60

View File

@@ -14,6 +14,7 @@
#define _DEVICE_PARAMS
#include <stdint.h>
#include "sdkconfig.h"
// This file defines structure of modbus parameters which reflect correspond modbus address space
// for each modbus register type (coils, discreet inputs, holding registers, input registers)
@@ -61,6 +62,25 @@ typedef struct
#pragma pack(push, 1)
typedef struct
{
#if CONFIG_FMB_EXT_TYPE_SUPPORT
uint16_t holding_u8_a[2];
uint16_t holding_u8_b[2];
uint16_t holding_u16_ab[2];
uint16_t holding_u16_ba[2];
uint32_t holding_uint32_abcd[2];
uint32_t holding_uint32_cdab[2];
uint32_t holding_uint32_badc[2];
uint32_t holding_uint32_dcba[2];
float holding_float_abcd[2];
float holding_float_cdab[2];
float holding_float_badc[2];
float holding_float_dcba[2];
double holding_double_abcdefgh[2];
double holding_double_hgfedcba[2];
double holding_double_ghefcdab[2];
double holding_double_badcfehg[2];
uint32_t holding_area2_end;
#endif
float holding_data0;
float holding_data1;
float holding_data2;
@@ -70,6 +90,7 @@ typedef struct
float holding_data5;
float holding_data6;
float holding_data7;
uint32_t holding_area1_end;
} holding_reg_params_t;
#pragma pack(pop)

View File

@@ -37,10 +37,22 @@
// Discrete offset macro
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
#define STR(fieldname) ((const char*)( fieldname ))
#define STR(fieldname) ((const char *)( fieldname ))
#define TEST_HOLD_REG_START(field) (HOLD_OFFSET(field) >> 1)
#define TEST_HOLD_REG_SIZE(field) (sizeof(((holding_reg_params_t *)0)->field) >> 1)
#define TEST_INPUT_REG_START(field) (INPUT_OFFSET(field) >> 1)
#define TEST_INPUT_REG_SIZE(field) (sizeof(((input_reg_params_t *)0)->field) >> 1)
#define TEST_VALUE 12345 // default test value
#define TEST_ASCII_BIN 0xAAAAAAAA
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
#define EACH_ITEM(array, length) \
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
static const char *TAG = "MASTER_TEST";
// Enumeration of modbus device addresses accessed by master device
@@ -60,6 +72,24 @@ enum {
CID_RELAY_P1,
CID_RELAY_P2,
CID_DISCR_P1,
#if CONFIG_FMB_EXT_TYPE_SUPPORT
CID_HOLD_U8_A,
CID_HOLD_U8_B,
CID_HOLD_U16_AB,
CID_HOLD_U16_BA,
CID_HOLD_UINT32_ABCD,
CID_HOLD_UINT32_CDAB,
CID_HOLD_UINT32_BADC,
CID_HOLD_UINT32_DCBA,
CID_HOLD_FLOAT_ABCD,
CID_HOLD_FLOAT_CDAB,
CID_HOLD_FLOAT_BADC,
CID_HOLD_FLOAT_DCBA,
CID_HOLD_DOUBLE_ABCDEFGH,
CID_HOLD_DOUBLE_HGFEDCBA,
CID_HOLD_DOUBLE_GHEFCDAB,
CID_HOLD_DOUBLE_BADCFEHG,
#endif
CID_COUNT
};
@@ -74,26 +104,109 @@ enum {
// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
const mb_parameter_descriptor_t device_parameters[] = {
// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2,
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 10, 58,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data0), TEST_INPUT_REG_SIZE(input_data0),
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data0), TEST_HOLD_REG_SIZE(holding_data0),
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data1), TEST_INPUT_REG_SIZE(input_data1),
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data1), TEST_HOLD_REG_SIZE(holding_data1),
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data2), TEST_INPUT_REG_SIZE(input_data2),
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2),
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(test_regs), 58,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116,
OPTS( 0, 100, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 2, 6,
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1, OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 10, 6,
COIL_OFFSET(coils_port1), PARAM_TYPE_U8, 1, OPTS( 0x55, 0x2A, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
COIL_OFFSET(coils_port1), PARAM_TYPE_U8, 1,
OPTS( 0x55, 0x2A, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_DISCR_P1, STR("DiscreteInpP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_DISCRETE, 2, 7,
DISCR_OFFSET(discrete_input_port1), PARAM_TYPE_U8, 1, OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
DISCR_OFFSET(discrete_input_port1), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
#if CONFIG_FMB_EXT_TYPE_SUPPORT
{ CID_HOLD_U8_A, STR("U8_A"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_a), TEST_HOLD_REG_SIZE(holding_u8_a),
HOLD_OFFSET(holding_u8_a), PARAM_TYPE_U8_A, (TEST_HOLD_REG_SIZE(holding_u8_a) << 1),
OPTS( CHAR_MIN, 0x0055, 0x0055 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U8_B, STR("U8_B"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_b), TEST_HOLD_REG_SIZE(holding_u8_b),
HOLD_OFFSET(holding_u8_b), PARAM_TYPE_U8_B, (TEST_HOLD_REG_SIZE(holding_u8_b) << 1),
OPTS( 0, 0x5500, 0x5500 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_AB, STR("U16_AB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ab), TEST_HOLD_REG_SIZE(holding_u16_ab),
HOLD_OFFSET(holding_u16_ab), PARAM_TYPE_U16_AB, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_BA, STR("U16_BA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ba), TEST_HOLD_REG_SIZE(holding_u16_ba),
HOLD_OFFSET(holding_u16_ba), PARAM_TYPE_U16_BA, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_ABCD, STR("UINT32_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_abcd), TEST_HOLD_REG_SIZE(holding_uint32_abcd),
HOLD_OFFSET(holding_uint32_abcd), PARAM_TYPE_U32_ABCD, (TEST_HOLD_REG_SIZE(holding_uint32_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_CDAB, STR("UINT32_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_cdab), TEST_HOLD_REG_SIZE(holding_uint32_cdab),
HOLD_OFFSET(holding_uint32_cdab), PARAM_TYPE_U32_CDAB, (TEST_HOLD_REG_SIZE(holding_uint32_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_BADC, STR("UINT32_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_badc), TEST_HOLD_REG_SIZE(holding_uint32_badc),
HOLD_OFFSET(holding_uint32_badc), PARAM_TYPE_U32_BADC, (TEST_HOLD_REG_SIZE(holding_uint32_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_DCBA, STR("UINT32_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_dcba), TEST_HOLD_REG_SIZE(holding_uint32_dcba),
HOLD_OFFSET(holding_uint32_dcba), PARAM_TYPE_U32_DCBA, (TEST_HOLD_REG_SIZE(holding_uint32_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_ABCD, STR("FLOAT_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_abcd), TEST_HOLD_REG_SIZE(holding_float_abcd),
HOLD_OFFSET(holding_float_abcd), PARAM_TYPE_FLOAT_ABCD, (TEST_HOLD_REG_SIZE(holding_float_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_CDAB, STR("FLOAT_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_cdab), TEST_HOLD_REG_SIZE(holding_float_cdab),
HOLD_OFFSET(holding_float_cdab), PARAM_TYPE_FLOAT_CDAB, (TEST_HOLD_REG_SIZE(holding_float_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_BADC, STR("FLOAT_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_badc), TEST_HOLD_REG_SIZE(holding_float_badc),
HOLD_OFFSET(holding_float_badc), PARAM_TYPE_FLOAT_BADC, (TEST_HOLD_REG_SIZE(holding_float_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_DCBA, STR("FLOAT_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_dcba), TEST_HOLD_REG_SIZE(holding_float_dcba),
HOLD_OFFSET(holding_float_dcba), PARAM_TYPE_FLOAT_DCBA, (TEST_HOLD_REG_SIZE(holding_float_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_ABCDEFGH, STR("DOUBLE_ABCDEFGH"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_abcdefgh), TEST_HOLD_REG_SIZE(holding_double_abcdefgh),
HOLD_OFFSET(holding_double_abcdefgh), PARAM_TYPE_DOUBLE_ABCDEFGH, (TEST_HOLD_REG_SIZE(holding_double_abcdefgh) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_HGFEDCBA, STR("DOUBLE_HGFEDCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_hgfedcba), TEST_HOLD_REG_SIZE(holding_double_hgfedcba),
HOLD_OFFSET(holding_double_hgfedcba), PARAM_TYPE_DOUBLE_HGFEDCBA, (TEST_HOLD_REG_SIZE(holding_double_hgfedcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_GHEFCDAB, STR("DOUBLE_GHEFCDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_ghefcdab), TEST_HOLD_REG_SIZE(holding_double_ghefcdab),
HOLD_OFFSET(holding_double_ghefcdab), PARAM_TYPE_DOUBLE_GHEFCDAB, (TEST_HOLD_REG_SIZE(holding_double_ghefcdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_BADCFEHG, STR("DOUBLE_BADCFEHG"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_badcfehg), TEST_HOLD_REG_SIZE(holding_double_badcfehg),
HOLD_OFFSET(holding_double_badcfehg), PARAM_TYPE_DOUBLE_BADCFEHG, (TEST_HOLD_REG_SIZE(holding_double_badcfehg) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER }
#endif
};
// Calculate number of parameters in the table
@@ -105,21 +218,21 @@ static void *master_handle = NULL;
static void *master_get_param_data(const mb_parameter_descriptor_t *param_descriptor)
{
assert(param_descriptor != NULL);
void* instance_ptr = NULL;
void *instance_ptr = NULL;
if (param_descriptor->param_offset != 0) {
switch(param_descriptor->mb_param_type)
{
case MB_PARAM_HOLDING:
instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_INPUT:
instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&input_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_COIL:
instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&coil_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_DISCRETE:
instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&discrete_reg_params + param_descriptor->param_offset - 1);
break;
default:
instance_ptr = NULL;
@@ -132,112 +245,179 @@ static void *master_get_param_data(const mb_parameter_descriptor_t *param_descri
return instance_ptr;
}
#define TEST_VERIFY_VALUES(handle, pdescr, pinst) (__extension__( \
{ \
assert(pinst); \
assert(pdescr); \
uint8_t type = 0; \
esp_err_t err = ESP_FAIL; \
err = mbc_master_get_parameter(handle, pdescr->cid, \
(uint8_t *)pinst, &type); \
if (err == ESP_OK) { \
bool is_correct = true; \
if (pdescr->param_opts.opt3) { \
for EACH_ITEM(pinst, pdescr->param_size / sizeof(*pitem)) { \
if (*pitem != (typeof(*(pinst)))pdescr->param_opts.opt3) { \
*pitem = (typeof(*(pinst)))pdescr->param_opts.opt3; \
ESP_LOGD(TAG, "Characteristic #%d (%s), initialize to 0x%" PRIx16 ".", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(uint16_t)pdescr->param_opts.opt3); \
is_correct = false; \
} \
} \
} \
if (!is_correct) { \
ESP_LOGE(TAG, "Characteristic #%d (%s), initialize.", \
(int)pdescr->cid, \
(char *)pdescr->param_key); \
err = mbc_master_set_parameter(handle, cid, (uint8_t *)pinst, &type); \
if (err != ESP_OK) { \
ESP_LOGE(TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} else { \
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (..) write successful.", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(char *)pdescr->param_units); \
} \
} \
} else { \
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} \
(err); \
} \
))
// User operation function to read slave values and check alarm
static void master_operation_func(void *arg)
{
esp_err_t err = ESP_OK;
float value = 0;
bool alarm_state = false;
const mb_parameter_descriptor_t* param_descriptor = NULL;
const mb_parameter_descriptor_t *param_descriptor = NULL;
ESP_LOGI(TAG, "Start modbus test...");
for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
// Read all found characteristics from slave(s)
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
{
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) {
// Get data from parameters description table
// and use this information to fill the characteristics description table
// and having all required fields in just one table
err = mbc_master_get_cid_info(master_handle, cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
void* temp_data_ptr = master_get_param_data(param_descriptor);
void *temp_data_ptr = master_get_param_data(param_descriptor);
assert(temp_data_ptr);
uint8_t type = 0;
if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
(param_descriptor->cid == CID_HOLD_TEST_REG)) {
// Check for long array of registers of type PARAM_TYPE_ASCII
err = mbc_master_get_parameter(master_handle, cid, (uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = (0x%" PRIx32 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
// Initialize data of test array and write to slave
if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
*(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
err = mbc_master_set_parameter(master_handle, cid, (uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = (0x%" PRIx32 "), write successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%u (%s) write fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
} else {
ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint32_t *)temp_data_ptr);
}
} else {
err = mbc_master_get_parameter(master_handle, cid, (uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
(param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
value = *(float*)temp_data_ptr;
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %f (0x%" PRIx32 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
value,
*(uint32_t*)temp_data_ptr);
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
#if CONFIG_FMB_EXT_TYPE_SUPPORT
} else if ((param_descriptor->cid >= CID_HOLD_U16_AB)
&& (param_descriptor->cid <= CID_HOLD_U16_BA)) {
// Check the uint16 parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_U8_A)
&& (param_descriptor->cid <= CID_HOLD_U8_B)) {
// Check the uint8 parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_UINT32_ABCD)
&& (param_descriptor->cid <= CID_HOLD_UINT32_DCBA)) {
// Check the uint32 parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %" PRIu32 " (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint32_t *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_FLOAT_ABCD)
&& (param_descriptor->cid <= CID_HOLD_FLOAT_DCBA)) {
// Check the float parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if (param_descriptor->cid >= CID_HOLD_DOUBLE_ABCDEFGH) {
// Check the double parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (double *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %lf (0x%" PRIx64 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(double *)temp_data_ptr,
*(uint64_t *)temp_data_ptr);
}
#endif
} else if (cid <= CID_HOLD_DATA_2) {
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
float value = *(float *)temp_data_ptr;
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
} else if ((cid >= CID_RELAY_P1) && (cid <= CID_DISCR_P1)) {
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint8_t *)temp_data_ptr) == ESP_OK) {
uint8_t state = *(uint8_t *)temp_data_ptr;
const char *rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
} else {
uint8_t state = *(uint8_t*)temp_data_ptr;
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) {
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %s (0x%" PRIx8 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
(const char*)rw_str,
*(uint8_t*)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%u %s (%s) value = %s (0x%" PRIx8 "), unexpected value.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
(const char*)rw_str,
*(uint8_t*)temp_data_ptr);
alarm_state = true;
break;
}
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
ESP_LOGE(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 "), unexpected value.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
alarm_state = true;
break;
}
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
} else {
ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
@@ -247,9 +427,11 @@ static void master_operation_func(void *arg)
}
if (alarm_state) {
ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid);
ESP_LOGI(TAG, "Alarm triggered by cid #%d.",
(int)param_descriptor->cid);
} else {
ESP_LOGE(TAG, "Alarm is not triggered after %u retries.", MASTER_MAX_RETRY);
ESP_LOGE(TAG, "Alarm is not triggered after %d retries.",
MASTER_MAX_RETRY);
}
ESP_LOGI(TAG, "Destroy master...");
ESP_ERROR_CHECK(mbc_master_delete(master_handle));

View File

@@ -4,3 +4,4 @@ CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=400
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@@ -4,3 +4,4 @@ CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=400
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@@ -1,11 +1,9 @@
/*
* SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// FreeModbus Slave Example ESP32
#include <stdio.h>
#include <stdint.h>
#include "esp_err.h"
@@ -29,7 +27,11 @@
#define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0
#define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1
#define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0))
#define MB_REG_HOLDING_START_AREA0_SIZE ((size_t)((HOLD_OFFSET(holding_data4) - HOLD_OFFSET(holding_data0)) << 1))
#define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4))
#define MB_REG_HOLDING_START_AREA1_SIZE ((size_t)((HOLD_OFFSET(holding_area1_end) - HOLD_OFFSET(holding_data4)) << 1))
#define MB_REG_HOLDING_START_AREA2 (HOLD_OFFSET(holding_u8_a))
#define MB_REG_HOLDING_START_AREA2_SIZE ((size_t)((HOLD_OFFSET(holding_area2_end) - HOLD_OFFSET(holding_u8_a)) << 1))
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
#define MB_CHAN_DATA_MAX_VAL (6)
@@ -41,9 +43,11 @@
#define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR \
| MB_EVENT_COILS_WR)
#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)
#define MB_TEST_VALUE 12345.0
static const char *TAG = "SLAVE_TEST";
static void* mbc_slave_handle = NULL;
static void *mbc_slave_handle = NULL;
// Set register values into known state
static void setup_reg_data(void)
@@ -68,6 +72,44 @@ static void setup_reg_data(void)
holding_reg_params.holding_data6 = 7.79;
holding_reg_params.holding_data7 = 8.80;
#if CONFIG_FMB_EXT_TYPE_SUPPORT
mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[0], (uint8_t)0x55);
mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[1], (uint8_t)0x55);
mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[0], (uint8_t)0x55);
mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[1], (uint8_t)0x55);
mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[1], (uint16_t)MB_TEST_VALUE);
mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[0], (uint16_t)MB_TEST_VALUE);
mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[0], (uint16_t)MB_TEST_VALUE);
mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[1], (uint16_t)MB_TEST_VALUE);
mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[0], (float)MB_TEST_VALUE);
mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[1], (float)MB_TEST_VALUE);
mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[0], (float)MB_TEST_VALUE);
mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[1], (float)MB_TEST_VALUE);
mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[0], (float)MB_TEST_VALUE);
mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[1], (float)MB_TEST_VALUE);
mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[0], (float)MB_TEST_VALUE);
mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[1], (float)MB_TEST_VALUE);
mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[1], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[1], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[1], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[1], (uint32_t)MB_TEST_VALUE);
mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[0], (double)MB_TEST_VALUE);
mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[1], (double)MB_TEST_VALUE);
mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[0], (double)MB_TEST_VALUE);
mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[1], (double)MB_TEST_VALUE);
mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[0], (double)MB_TEST_VALUE);
mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[1], (double)MB_TEST_VALUE);
mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[0], (double)MB_TEST_VALUE);
mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[1], (double)MB_TEST_VALUE);
#endif
coil_reg_params.coils_port0 = 0x55;
coil_reg_params.coils_port1 = 0xAA;
@@ -120,15 +162,26 @@ void app_main(void)
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
// Set the size of register storage instance = 150 holding registers
reg_area.size = (size_t)(HOLD_OFFSET(holding_data4) - HOLD_OFFSET(test_regs));
// Set the size of register storage instance in bytes
reg_area.size = MB_REG_HOLDING_START_AREA0_SIZE;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(mbc_slave_handle, reg_area));
// The second register area
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
reg_area.start_offset = MB_REG_HOLDING_START_AREA1;
reg_area.address = (void*)&holding_reg_params.holding_data4;
reg_area.size = MB_REG_HOLDING_START_AREA1_SIZE;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(mbc_slave_handle, reg_area));
#if CONFIG_FMB_EXT_TYPE_SUPPORT
// The extended parameters register area
reg_area.type = MB_PARAM_HOLDING;
reg_area.start_offset = MB_REG_HOLDING_START_AREA2;
reg_area.address = (void*)&holding_reg_params.holding_u8_a;
reg_area.size = MB_REG_HOLDING_START_AREA2_SIZE;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(mbc_slave_handle, reg_area));
#endif
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA0;

View File

@@ -3,3 +3,4 @@ CONFIG_MB_COMM_MODE_RTU=n
CONFIG_MB_SLAVE_ADDR=1
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@@ -3,3 +3,4 @@ CONFIG_MB_COMM_MODE_RTU=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@@ -5,3 +5,4 @@ CONFIG_MB_COMM_MODE_ASCII=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@@ -46,7 +46,16 @@
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
#define STR(fieldname) ((const char*)( fieldname ))
#define STR(fieldname) ((const char *)( fieldname ))
#define TEST_HOLD_REG_START(field) (HOLD_OFFSET(field) >> 1)
#define TEST_HOLD_REG_SIZE(field) (sizeof(((holding_reg_params_t *)0)->field) >> 1)
#define TEST_INPUT_REG_START(field) (INPUT_OFFSET(field) >> 1)
#define TEST_INPUT_REG_SIZE(field) (sizeof(((input_reg_params_t *)0)->field) >> 1)
#define TEST_VALUE 12345 // default test value
#define TEST_ASCII_BIN 0xAAAAAAAA
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
@@ -65,6 +74,10 @@
#endif
#define MB_MDNS_INSTANCE(pref) pref"mb_master_tcp"
#define EACH_ITEM(array, length) \
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
static const char *TAG = "MASTER_TEST";
// Enumeration of modbus device addresses accessed by master device
@@ -87,6 +100,24 @@ enum {
CID_RELAY_P1,
CID_RELAY_P2,
CID_DISCR_P1,
#if CONFIG_FMB_EXT_TYPE_SUPPORT
CID_HOLD_U8_A,
CID_HOLD_U8_B,
CID_HOLD_U16_AB,
CID_HOLD_U16_BA,
CID_HOLD_UINT32_ABCD,
CID_HOLD_UINT32_CDAB,
CID_HOLD_UINT32_BADC,
CID_HOLD_UINT32_DCBA,
CID_HOLD_FLOAT_ABCD,
CID_HOLD_FLOAT_CDAB,
CID_HOLD_FLOAT_BADC,
CID_HOLD_FLOAT_DCBA,
CID_HOLD_DOUBLE_ABCDEFGH,
CID_HOLD_DOUBLE_HGFEDCBA,
CID_HOLD_DOUBLE_GHEFCDAB,
CID_HOLD_DOUBLE_BADCFEHG,
#endif
CID_COUNT
};
@@ -101,26 +132,109 @@ enum {
// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
const mb_parameter_descriptor_t device_parameters[] = {
// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 1000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 1000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 1000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 1000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 1000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2,
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 1000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 10, 30,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 60, OPTS( 0, 1000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data0), TEST_INPUT_REG_SIZE(input_data0),
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data0), TEST_HOLD_REG_SIZE(holding_data0),
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data1), TEST_INPUT_REG_SIZE(input_data1),
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data1), TEST_HOLD_REG_SIZE(holding_data1),
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data2), TEST_INPUT_REG_SIZE(input_data2),
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR3, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2),
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(test_regs), 58,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116,
OPTS( 0, 100, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 2, 6,
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1, OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 10, 6,
COIL_OFFSET(coils_port1), PARAM_TYPE_U8, 1, OPTS( 0x55, 0x2A, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
COIL_OFFSET(coils_port1), PARAM_TYPE_U8, 1,
OPTS( 0x55, 0x2A, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_DISCR_P1, STR("DiscreteInpP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_DISCRETE, 2, 7,
DISCR_OFFSET(discrete_input_port1), PARAM_TYPE_U8, 1, OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
DISCR_OFFSET(discrete_input_port1), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
#if CONFIG_FMB_EXT_TYPE_SUPPORT
{ CID_HOLD_U8_A, STR("U8_A"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_a), TEST_HOLD_REG_SIZE(holding_u8_a),
HOLD_OFFSET(holding_u8_a), PARAM_TYPE_U8_A, (TEST_HOLD_REG_SIZE(holding_u8_a) << 1),
OPTS( CHAR_MIN, 0x0055, 0x0055 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U8_B, STR("U8_B"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_b), TEST_HOLD_REG_SIZE(holding_u8_b),
HOLD_OFFSET(holding_u8_b), PARAM_TYPE_U8_B, (TEST_HOLD_REG_SIZE(holding_u8_b) << 1),
OPTS( 0, 0x5500, 0x5500 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_AB, STR("U16_AB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ab), TEST_HOLD_REG_SIZE(holding_u16_ab),
HOLD_OFFSET(holding_u16_ab), PARAM_TYPE_U16_AB, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_BA, STR("U16_BA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ba), TEST_HOLD_REG_SIZE(holding_u16_ba),
HOLD_OFFSET(holding_u16_ba), PARAM_TYPE_U16_BA, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_ABCD, STR("UINT32_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_abcd), TEST_HOLD_REG_SIZE(holding_uint32_abcd),
HOLD_OFFSET(holding_uint32_abcd), PARAM_TYPE_U32_ABCD, (TEST_HOLD_REG_SIZE(holding_uint32_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_CDAB, STR("UINT32_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_cdab), TEST_HOLD_REG_SIZE(holding_uint32_cdab),
HOLD_OFFSET(holding_uint32_cdab), PARAM_TYPE_U32_CDAB, (TEST_HOLD_REG_SIZE(holding_uint32_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_BADC, STR("UINT32_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_badc), TEST_HOLD_REG_SIZE(holding_uint32_badc),
HOLD_OFFSET(holding_uint32_badc), PARAM_TYPE_U32_BADC, (TEST_HOLD_REG_SIZE(holding_uint32_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_DCBA, STR("UINT32_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_dcba), TEST_HOLD_REG_SIZE(holding_uint32_dcba),
HOLD_OFFSET(holding_uint32_dcba), PARAM_TYPE_U32_DCBA, (TEST_HOLD_REG_SIZE(holding_uint32_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_ABCD, STR("FLOAT_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_abcd), TEST_HOLD_REG_SIZE(holding_float_abcd),
HOLD_OFFSET(holding_float_abcd), PARAM_TYPE_FLOAT_ABCD, (TEST_HOLD_REG_SIZE(holding_float_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_CDAB, STR("FLOAT_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_cdab), TEST_HOLD_REG_SIZE(holding_float_cdab),
HOLD_OFFSET(holding_float_cdab), PARAM_TYPE_FLOAT_CDAB, (TEST_HOLD_REG_SIZE(holding_float_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_BADC, STR("FLOAT_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_badc), TEST_HOLD_REG_SIZE(holding_float_badc),
HOLD_OFFSET(holding_float_badc), PARAM_TYPE_FLOAT_BADC, (TEST_HOLD_REG_SIZE(holding_float_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_DCBA, STR("FLOAT_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_dcba), TEST_HOLD_REG_SIZE(holding_float_dcba),
HOLD_OFFSET(holding_float_dcba), PARAM_TYPE_FLOAT_DCBA, (TEST_HOLD_REG_SIZE(holding_float_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_ABCDEFGH, STR("DOUBLE_ABCDEFGH"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_abcdefgh), TEST_HOLD_REG_SIZE(holding_double_abcdefgh),
HOLD_OFFSET(holding_double_abcdefgh), PARAM_TYPE_DOUBLE_ABCDEFGH, (TEST_HOLD_REG_SIZE(holding_double_abcdefgh) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_HGFEDCBA, STR("DOUBLE_HGFEDCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_hgfedcba), TEST_HOLD_REG_SIZE(holding_double_hgfedcba),
HOLD_OFFSET(holding_double_hgfedcba), PARAM_TYPE_DOUBLE_HGFEDCBA, (TEST_HOLD_REG_SIZE(holding_double_hgfedcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_GHEFCDAB, STR("DOUBLE_GHEFCDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_ghefcdab), TEST_HOLD_REG_SIZE(holding_double_ghefcdab),
HOLD_OFFSET(holding_double_ghefcdab), PARAM_TYPE_DOUBLE_GHEFCDAB, (TEST_HOLD_REG_SIZE(holding_double_ghefcdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_BADCFEHG, STR("DOUBLE_BADCFEHG"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_badcfehg), TEST_HOLD_REG_SIZE(holding_double_badcfehg),
HOLD_OFFSET(holding_double_badcfehg), PARAM_TYPE_DOUBLE_BADCFEHG, (TEST_HOLD_REG_SIZE(holding_double_badcfehg) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER }
#endif
};
// Calculate number of parameters in the table
@@ -134,7 +248,7 @@ const size_t ip_table_sz;
// This table represents slave IP addresses that correspond to the short address field of the slave in device_parameters structure
// Modbus TCP stack shall use these addresses to be able to connect and read parameters from slave
char* slave_ip_address_table[] = {
char *slave_ip_address_table[] = {
"FROM_STDIN", // Address corresponds to MB_DEVICE_ADDR1 and set to predefined value by user
//"FROM_STDIN", // Corresponds to characteristic MB_DEVICE_ADDR2
//"FROM_STDIN", // Corresponds to characteristic MB_DEVICE_ADDR3
@@ -142,9 +256,9 @@ char* slave_ip_address_table[] = {
};
// Scan IP address according to IPV settings
char* master_scan_addr(int* index, char* buffer)
char *master_scan_addr(int *index, char *buffer)
{
char* ip_str = NULL;
char *ip_str = NULL;
int a[8] = {0};
int buf_cnt = 0;
#if !CONFIG_EXAMPLE_CONNECT_IPV6
@@ -165,11 +279,11 @@ char* master_scan_addr(int* index, char* buffer)
return ip_str;
}
static int master_get_slave_ip_stdin(char** addr_table)
static int master_get_slave_ip_stdin(char **addr_table)
{
char buf[128];
int index;
char* ip_str = NULL;
char *ip_str = NULL;
int buf_cnt = 0;
int ip_cnt = 0;
@@ -180,7 +294,7 @@ static int master_get_slave_ip_stdin(char** addr_table)
ESP_ERROR_CHECK(example_configure_stdin_stdout());
while(1) {
if (addr_table[ip_cnt] && strcmp(addr_table[ip_cnt], "FROM_STDIN") == 0) {
printf("Waiting IP%d from stdin:\r\n", ip_cnt);
printf("Waiting IP%d from stdin:\r\n", (int)ip_cnt);
while (fgets(buf, sizeof(buf), stdin) == NULL) {
fputs(buf, stdout);
}
@@ -189,7 +303,7 @@ static int master_get_slave_ip_stdin(char** addr_table)
fputc('\n', stdout);
ip_str = master_scan_addr(&index, buf);
if (ip_str != NULL) {
ESP_LOGI(TAG, "IP(%d) = [%s] set from stdin.", ip_cnt, ip_str);
ESP_LOGI(TAG, "IP(%d) = [%s] set from stdin.", (int)ip_cnt, ip_str);
if ((ip_cnt >= ip_table_sz) || (index != ip_cnt)) {
addr_table[ip_cnt] = NULL;
break;
@@ -202,10 +316,10 @@ static int master_get_slave_ip_stdin(char** addr_table)
}
} else {
if (addr_table[ip_cnt]) {
ESP_LOGI(TAG, "Leave IP(%d) = [%s] set manually.", ip_cnt, addr_table[ip_cnt]);
ESP_LOGI(TAG, "Leave IP(%d) = [%s] set manually.", (int)ip_cnt, addr_table[ip_cnt]);
ip_cnt++;
} else {
ESP_LOGI(TAG, "IP(%d) is not set in the table.", ip_cnt);
ESP_LOGI(TAG, "IP(%d) is not set in the table.", (int)ip_cnt);
break;
}
}
@@ -226,7 +340,7 @@ char *slave_ip_address_table[] = {
const size_t ip_table_sz = (size_t)(sizeof(slave_ip_address_table) / sizeof(slave_ip_address_table[0]));
static void master_destroy_slave_list(char** table, size_t ip_table_size)
static void master_destroy_slave_list(char **table, size_t ip_table_size)
{
for (int i = 0; ((i < ip_table_size) && table[i] != NULL); i++) {
if (table[i]) {
@@ -241,24 +355,24 @@ static void master_destroy_slave_list(char** table, size_t ip_table_size)
}
// The function to get pointer to parameter storage (instance) according to parameter description table
static void* master_get_param_data(const mb_parameter_descriptor_t* param_descriptor)
static void *master_get_param_data(const mb_parameter_descriptor_t *param_descriptor)
{
assert(param_descriptor != NULL);
void* instance_ptr = NULL;
void *instance_ptr = NULL;
if (param_descriptor->param_offset != 0) {
switch(param_descriptor->mb_param_type)
{
case MB_PARAM_HOLDING:
instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_INPUT:
instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&input_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_COIL:
instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&coil_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_DISCRETE:
instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&discrete_reg_params + param_descriptor->param_offset - 1);
break;
default:
instance_ptr = NULL;
@@ -271,112 +385,178 @@ static void* master_get_param_data(const mb_parameter_descriptor_t* param_descri
return instance_ptr;
}
#define TEST_VERIFY_VALUES(handle, pdescr, pinst) (__extension__( \
{ \
assert(pinst); \
assert(pdescr); \
uint8_t type = 0; \
esp_err_t err = ESP_FAIL; \
err = mbc_master_get_parameter(handle, pdescr->cid, (uint8_t *)pinst, &type); \
if (err == ESP_OK) { \
bool is_correct = true; \
if (pdescr->param_opts.opt3) { \
for EACH_ITEM(pinst, pdescr->param_size / sizeof(*pitem)) { \
if (*pitem != (typeof(*(pinst)))pdescr->param_opts.opt3) { \
*pitem = (typeof(*(pinst)))pdescr->param_opts.opt3; \
ESP_LOGD(TAG, "Characteristic #%d (%s), initialize to 0x%" PRIx16 ".", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(uint16_t)pdescr->param_opts.opt3); \
is_correct = false; \
} \
} \
} \
if (!is_correct) { \
ESP_LOGE(TAG, "Characteristic #%d (%s), initialize.", \
(int)pdescr->cid, \
(char *)pdescr->param_key); \
err = mbc_master_set_parameter(handle, cid, (uint8_t *)pinst, &type); \
if (err != ESP_OK) { \
ESP_LOGE(TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} else { \
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (..) write successful.", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(char *)pdescr->param_units); \
} \
} \
} else { \
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} \
(err); \
} \
))
// User operation function to read slave values and check alarm
static void master_operation_func(void *arg)
{
esp_err_t err = ESP_OK;
float value = 0;
bool alarm_state = false;
const mb_parameter_descriptor_t* param_descriptor = NULL;
const mb_parameter_descriptor_t *param_descriptor = NULL;
ESP_LOGI(TAG, "Start modbus test...");
for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
// Read all found characteristics from slave(s)
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
{
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) {
// Get data from parameters description table
// and use this information to fill the characteristics description table
// and having all required fields in just one table
err = mbc_master_get_cid_info(master_handle, cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
void* temp_data_ptr = master_get_param_data(param_descriptor);
void *temp_data_ptr = master_get_param_data(param_descriptor);
assert(temp_data_ptr);
uint8_t type = 0;
if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
(param_descriptor->cid == CID_HOLD_TEST_REG)) {
// Check for long array of registers of type PARAM_TYPE_ASCII
err = mbc_master_get_parameter(master_handle, cid, (uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = (0x%" PRIx32 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
// Initialize data of test array and write to slave
if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
*(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
err = mbc_master_set_parameter(master_handle, cid, (uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = (0x%" PRIx32 "), write successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%u (%s) write fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
} else {
ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint32_t *)temp_data_ptr);
}
} else {
err = mbc_master_get_parameter(master_handle, cid, (uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
(param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
value = *(float*)temp_data_ptr;
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %f (0x%" PRIx32 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
value,
*(uint32_t*)temp_data_ptr);
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
#if CONFIG_FMB_EXT_TYPE_SUPPORT
} else if ((param_descriptor->cid >= CID_HOLD_U16_AB)
&& (param_descriptor->cid <= CID_HOLD_U16_BA)) {
// Check the uint16 parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_U8_A)
&& (param_descriptor->cid <= CID_HOLD_U8_B)) {
// Check the uint8 parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_UINT32_ABCD)
&& (param_descriptor->cid <= CID_HOLD_UINT32_DCBA)) {
// Check the uint32 parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %" PRIu32 " (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint32_t *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_FLOAT_ABCD)
&& (param_descriptor->cid <= CID_HOLD_FLOAT_DCBA)) {
// Check the float parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if (param_descriptor->cid >= CID_HOLD_DOUBLE_ABCDEFGH) {
// Check the double parameters
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (double *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %lf (0x%" PRIx64 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(double *)temp_data_ptr,
*(uint64_t *)temp_data_ptr);
}
#endif
} else if (cid <= CID_HOLD_DATA_2) {
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
float value = *(float *)temp_data_ptr;
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
} else if ((cid >= CID_RELAY_P1) && (cid <= CID_DISCR_P1)) {
if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint8_t *)temp_data_ptr) == ESP_OK) {
uint8_t state = *(uint8_t *)temp_data_ptr;
const char *rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
} else {
uint8_t state = *(uint8_t*)temp_data_ptr;
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) {
ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %s (0x%" PRIx8 ") read successful.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
rw_str,
*(uint8_t*)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%u %s (%s) value = %s (0x%" PRIx8 "), unexpected value.",
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
rw_str,
*(uint8_t*)temp_data_ptr);
//alarm_state = true;
//break;
}
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
ESP_LOGE(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 "), unexpected value.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
alarm_state = true;
break;
}
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
} else {
ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
@@ -416,7 +596,6 @@ static esp_err_t init_services(mb_tcp_addr_type_t ip_addr_type)
TAG,
"esp_event_loop_create_default fail, returns(0x%x).",
(int)result);
// This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
// Read "Establishing Wi-Fi or Ethernet Connection" section in
// examples/protocols/README.md for more information about this function.

View File

@@ -15,6 +15,8 @@ CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y

View File

@@ -13,6 +13,8 @@ CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
CONFIG_EXAMPLE_CONNECT_WIFI=y
CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}"

View File

@@ -14,6 +14,7 @@ CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
CONFIG_EXAMPLE_CONNECT_WIFI=y

View File

@@ -436,3 +436,281 @@ mb_err_enum_t mbc_reg_discrete_master_cb(mb_base_t *inst, uint8_t *reg_buffer, u
}
return status;
}
// 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

@@ -9,6 +9,10 @@
#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

View File

@@ -9,7 +9,6 @@
#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_log.h"
#include "esp_modbus_common.h" // for common types
#ifdef __cplusplus
@@ -40,6 +39,12 @@ extern "C" {
} \
))
/*!
* \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.
*/
@@ -48,7 +53,40 @@ typedef enum {
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_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;
/*!
@@ -56,11 +94,18 @@ typedef enum {
*/
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 size */
PARAM_SIZE_ASCII = 0x08, /*!< ASCII size */
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;
@@ -405,6 +450,22 @@ mb_err_enum_t mbc_reg_discrete_master_cb(mb_base_t *inst, uint8_t *reg_buffer, u
*/
mb_err_enum_t mbc_reg_coils_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_coils, mb_reg_mode_enum_t mode);
/**
* @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);
#ifdef __cplusplus
}
#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,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] = (char)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] = (char)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] = (char)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] = (char)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

@@ -348,7 +348,7 @@ static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_para
// 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(void *ctx, uint8_t cid, mb_param_mode_t mode,
static esp_err_t mbc_serial_master_set_request(void *ctx, uint16_t cid, mb_param_mode_t mode,
mb_param_request_t *request,
mb_parameter_descriptor_t *reg_data)
{
@@ -377,42 +377,51 @@ static esp_err_t mbc_serial_master_set_request(void *ctx, uint8_t cid, mb_param_
}
// Get parameter data for corresponding characteristic
static esp_err_t mbc_serial_master_get_parameter(void *ctx, uint16_t cid,
uint8_t *value_ptr, uint8_t *type)
static esp_err_t mbc_serial_master_get_parameter(void *ctx, uint16_t cid, uint8_t *value, uint8_t *type)
{
MB_RETURN_ON_FALSE((type), ESP_ERR_INVALID_ARG, TAG, "type pointer is incorrect.");
MB_RETURN_ON_FALSE((value_ptr), ESP_ERR_INVALID_ARG, TAG, "value pointer is incorrect.");
MB_RETURN_ON_FALSE((value), ESP_ERR_INVALID_ARG, TAG, "value pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request;
mb_parameter_descriptor_t reg_info = {0};
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t *pdata = NULL;
error = mbc_serial_master_set_request(ctx, cid, MB_PARAM_READ, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid) && (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER))
{
// Send request to read characteristic data
error = mbc_serial_master_send_request(ctx, &request, value_ptr);
if (error == ESP_OK)
{
ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char *)esp_err_to_name(error));
if ((error == ESP_OK) && (cid == reg_info.cid) && (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER)) {
MB_MASTER_ASSERT(xPortGetFreeHeapSize() > (reg_info.mb_size << 1));
// alloc buffer to store parameter data
pdata = calloc(1, (reg_info.mb_size << 1));
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
else
{
error = mbc_serial_master_send_request(ctx, &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) {
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));
__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) address information is not found in the data dictionary.",
__FUNCTION__, reg_info.cid);
} else {
ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.",
__FUNCTION__, (unsigned)reg_info.cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
}
// Get parameter data for corresponding characteristic
static esp_err_t mbc_serial_master_get_parameter_with(void *ctx, uint16_t cid, uint8_t uid,
uint8_t *value_ptr, uint8_t *type)
@@ -422,28 +431,46 @@ static esp_err_t mbc_serial_master_get_parameter_with(void *ctx, uint16_t cid, u
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(ctx, cid, MB_PARAM_READ, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid))
{
if (request.slave_addr == MB_SLAVE_ADDR_PLACEHOLDER)
if (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER)
{
ESP_LOGD(TAG, "%s: override uid %d = %d for cid(%u)",
__FUNCTION__, (int)request.slave_addr, (int)uid, (unsigned)reg_info.cid);
}
request.slave_addr = uid; // override the UID
MB_MASTER_ASSERT(xPortGetFreeHeapSize() > (reg_info.mb_size << 1));
// alloc buffer to store parameter data
pdata = calloc(1, (reg_info.mb_size << 1));
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
// Send request to read characteristic data
error = mbc_serial_master_send_request(ctx, &request, value_ptr);
error = mbc_serial_master_send_request(ctx, &request, pdata);
if (error == ESP_OK)
{
ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
__FUNCTION__, (unsigned)reg_info.cid, (char *)esp_err_to_name(error));
// If data pointer is NULL then we don't need to set value (it is still in the cache of cid)
if (value_ptr) {
error = mbc_master_set_param_data((void *)value_ptr, (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;
}
@@ -456,38 +483,46 @@ static esp_err_t mbc_serial_master_get_parameter_with(void *ctx, uint16_t cid, u
return error;
}
// Set parameter value for characteristic selected by name and cid
static esp_err_t mbc_serial_master_set_parameter(void *ctx, uint16_t cid,
uint8_t *value_ptr, uint8_t *type)
// Set parameter value for characteristic selected by cid
static esp_err_t mbc_serial_master_set_parameter(void *ctx, uint16_t cid, uint8_t *value, uint8_t *type)
{
MB_RETURN_ON_FALSE((value_ptr), ESP_ERR_INVALID_ARG, TAG, "value pointer is incorrect.");
MB_RETURN_ON_FALSE((value), ESP_ERR_INVALID_ARG, TAG, "value pointer is incorrect.");
MB_RETURN_ON_FALSE((type), ESP_ERR_INVALID_ARG, TAG, "type pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request;
mb_parameter_descriptor_t reg_info = {0};
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t *pdata = NULL;
error = mbc_serial_master_set_request(ctx, cid, MB_PARAM_WRITE, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid) && (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER))
{
if ((error == ESP_OK) && (cid == reg_info.cid) && (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER)) {
MB_MASTER_ASSERT(xPortGetFreeHeapSize() > (reg_info.mb_size << 1));
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(ctx, &request, value_ptr);
if (error == ESP_OK)
{
error = mbc_serial_master_send_request(ctx, &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
{
__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));
__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) address information is not found in the data dictionary.",
__FUNCTION__, (unsigned)reg_info.cid);
} 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;
@@ -502,16 +537,29 @@ static esp_err_t mbc_serial_master_set_parameter_with(void *ctx, uint16_t cid, u
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(ctx, cid, MB_PARAM_WRITE, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid))
{
if (request.slave_addr == MB_SLAVE_ADDR_PLACEHOLDER)
if (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER)
{
ESP_LOGD(TAG, "%s: override uid %d = %d for cid(%u)",
__FUNCTION__, (int)request.slave_addr, (int)uid, (unsigned)reg_info.cid);
}
request.slave_addr = uid; // override the UID
MB_MASTER_ASSERT(xPortGetFreeHeapSize() > (reg_info.mb_size << 1));
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_ptr,
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(ctx, &request, value_ptr);
if (error == ESP_OK)
@@ -524,6 +572,7 @@ static esp_err_t mbc_serial_master_set_parameter_with(void *ctx, uint16_t cid, u
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;
}
@@ -680,4 +729,4 @@ error:
return ret;
}
#endif
#endif

View File

@@ -110,7 +110,7 @@ static esp_err_t mbc_tcp_master_stop(void *ctx)
mb_master_options_t *mbm_opts = MB_MASTER_GET_OPTS(ctx);
mb_err_enum_t status = MB_EIO;
mbm_iface->mb_base->descr.parent = ctx;
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupClearBits(mbm_opts->event_group_handle,
(EventBits_t)MB_EVENT_STACK_STARTED);
@@ -170,14 +170,13 @@ static esp_err_t mbc_tcp_master_send_request(void *ctx, mb_param_request_t *requ
mb_err_enum_t mb_error = MB_EBUSY;
esp_err_t error = ESP_FAIL;
if (mb_port_evt_res_take(mbm_controller_iface->mb_base->port_obj, pdMS_TO_TICKS(MB_MAX_RESP_DELAY_MS))) {
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->reg_buffer_ptr = (uint8_t *)data_ptr;
mbm_opts->reg_buffer_size = mb_size;
@@ -353,39 +352,8 @@ static uint8_t mbc_tcp_master_get_command(mb_param_type_t param_type, mb_param_m
return command;
}
// Helper function to set parameter buffer according to its type
static esp_err_t mbc_tcp_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((dest), ESP_ERR_INVALID_ARG, TAG, "incorrect parameter pointer.");
MB_RETURN_ON_FALSE((src), ESP_ERR_INVALID_ARG, TAG, "incorrect parameter pointer.");
// Transfer parameter data into value of characteristic
switch(param_type) {
case PARAM_TYPE_U8:
*((uint8_t *)dest) = *((uint8_t *)src);
break;
case PARAM_TYPE_U16:
*((uint16_t *)dest) = *((uint16_t *)src);
break;
case PARAM_TYPE_U32:
*((uint32_t*)dest) = *((uint32_t*)src);
break;
case PARAM_TYPE_FLOAT:
*((float*)dest) = *(float*)src;
break;
case PARAM_TYPE_ASCII:
memcpy(dest, src, param_size);
break;
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u).",
__FUNCTION__, (unsigned)param_type);
err = ESP_ERR_NOT_SUPPORTED;
break;
}
return err;
}
// Helper to search parameter by name in the parameter description table and fills Modbus request fields accordingly
// Helper to search parameter in the parameter description table and fills Modbus request fields accordingly
static esp_err_t mbc_tcp_master_set_request(void *ctx, uint16_t cid, mb_param_mode_t mode, mb_param_request_t *request,
mb_parameter_descriptor_t *reg_data)
{
@@ -439,7 +407,7 @@ static esp_err_t mbc_tcp_master_get_parameter(void *ctx, uint16_t cid, uint8_t *
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) {
error = mbc_tcp_master_set_param_data((void *)value, (void *)pdata,
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.");
@@ -471,7 +439,7 @@ static esp_err_t mbc_tcp_master_get_parameter_with(void *ctx, uint16_t cid, uint
MB_RETURN_ON_FALSE((value), ESP_ERR_INVALID_ARG, TAG, "value pointer is incorrect.");
mbm_controller_iface_t *mbm_controller_iface = MB_MASTER_GET_IFACE(ctx);
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request ;
mb_param_request_t request;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t *pdata = NULL;
@@ -497,7 +465,7 @@ static esp_err_t mbc_tcp_master_get_parameter_with(void *ctx, uint16_t cid, uint
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) {
error = mbc_tcp_master_set_param_data((void *)value, (void *)pdata,
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.");
@@ -546,7 +514,7 @@ static esp_err_t mbc_tcp_master_set_parameter(void *ctx, uint16_t cid, uint8_t *
return ESP_ERR_INVALID_STATE;
}
// Transfer value of characteristic into parameter buffer
error = mbc_tcp_master_set_param_data((void *)pdata, (void *)value,
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.");
@@ -593,10 +561,10 @@ static esp_err_t mbc_tcp_master_set_parameter_with(void *ctx, uint16_t cid, uint
"mb can not send request for cid #%d with uid=%d.",
(unsigned)reg_info.cid, (int)uid);
if (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER) {
ESP_LOGD(TAG, "%s: override uid %d = %d for cid(%u)",
ESP_LOGW(TAG, "%s: override uid %d = %d for cid(%u)",
__FUNCTION__, (int)request.slave_addr, (int)uid, (unsigned)reg_info.cid);
}
request.slave_addr = uid; // override the UID
request.slave_addr = uid; // override the UID
MB_MASTER_ASSERT(xPortGetFreeHeapSize() > (reg_info.mb_size << 1));
pdata = calloc(1, (reg_info.mb_size << 1)); // alloc parameter buffer
@@ -604,7 +572,7 @@ static esp_err_t mbc_tcp_master_set_parameter_with(void *ctx, uint16_t cid, uint
return ESP_ERR_INVALID_STATE;
}
// Transfer value of characteristic into parameter buffer
error = mbc_tcp_master_set_param_data((void *)pdata, (void *)value,
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.");
@@ -796,4 +764,4 @@ error:
return ret;
}
#endif
#endif

View File

@@ -0,0 +1,8 @@
#This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mb_endianness_utils)

View File

@@ -0,0 +1,3 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -0,0 +1,7 @@
set(srcs "test_mb_endianness_utils.c")
idf_component_register(SRCS ${srcs}
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES esp-modbus test_utils unity)

View File

@@ -0,0 +1,6 @@
dependencies:
idf: ">=4.3"
espressif/esp-modbus:
version: "^2.0"
override_path: "../../../../"

View File

@@ -0,0 +1,149 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdlib.h>
#include <stdbool.h>
#include "unity.h"
#include "test_utils.h"
#include "sdkconfig.h"
#include "mb_endianness_utils.h"
#define TAG "MB_ENDIANNESS_TEST"
// The below is the data used for endianness conversion test
const uint16_t TEST_INT8_A = 0x00F6;
const uint16_t TEST_INT8_B = 0xF600;
const uint16_t TEST_UINT8_A = 0x0037;
const uint16_t TEST_UINT8_B = 0x3700;
const uint16_t TEST_UINT16_AB = 0x3039;
const uint16_t TEST_UINT16_BA = 0x3930;
const uint16_t TEST_INT16_AB = 0x3039;
const uint16_t TEST_INT16_BA = 0x3930;
const uint32_t TEST_FLOAT_ABCD = 0x4640e400;
const uint32_t TEST_FLOAT_DCBA = 0x00e44046;
const uint32_t TEST_FLOAT_BADC = 0x404600e4;
const uint32_t TEST_FLOAT_CDAB = 0xe4004640;
const uint32_t TEST_UINT32_ABCD = 0x11223344;
const uint32_t TEST_UINT32_DCBA = 0x44332211;
const uint32_t TEST_UINT32_BADC = 0x22114433;
const uint32_t TEST_UINT32_CDAB = 0x33441122;
const uint64_t TEST_DOUBLE_ABCDEFGH = 0x40c81c8000000000;
const uint64_t TEST_DOUBLE_HGFEDCBA = 0x00000000801cc840;
const uint64_t TEST_DOUBLE_GHEFCDAB = 0x000000001c8040c8;
const uint64_t TEST_DOUBLE_BADCFEHG = 0xc840801c00000000;
const uint64_t TEST_INT64_ABCDEFGH = 0xffffffffffffcfc7;
const uint64_t TEST_INT64_HGFEDCBA = 0xc7cfffffffffffff;
const uint64_t TEST_INT64_GHEFCDAB = 0xcfc7ffffffffffff;
const uint64_t TEST_INT64_BADCFEHG = 0xffffffffffffc7cf;
const uint64_t TEST_UINT64_ABCDEFGH = 0x1122334455667788;
const uint64_t TEST_UINT64_HGFEDCBA = 0x8877665544332211;
const uint64_t TEST_UINT64_GHEFCDAB = 0x7788556633441122;
const uint64_t TEST_UINT64_BADCFEHG = 0x2211443366558877;
TEST_CASE("Test endianness conversion for all extended Modbus types.", "[MB_ENDIANNESS]")
{
val_16_arr arr_16 = {0};
val_32_arr arr_32 = {0};
val_64_arr arr_64 = {0};
TEST_ASSERT(mb_set_uint8_a(&arr_16, (uint8_t)55) == TEST_UINT8_A);
TEST_ASSERT(mb_get_uint8_a(&arr_16) == (uint8_t)55);
TEST_ASSERT(mb_set_int8_a(&arr_16, (int8_t)-10) == TEST_INT8_A);
TEST_ASSERT(mb_get_int8_a(&arr_16) == (int8_t)-10);
TEST_ASSERT(mb_set_uint8_b(&arr_16, (uint8_t)55) == TEST_UINT8_B);
TEST_ASSERT(mb_get_uint8_b(&arr_16) == (uint8_t)55);
TEST_ASSERT(mb_set_int8_b(&arr_16, (int8_t)-10) == TEST_INT8_B);
TEST_ASSERT(mb_get_int8_b(&arr_16) == (int8_t)-10);
TEST_ASSERT(mb_set_uint16_ab(&arr_16, (uint16_t)12345) == TEST_UINT16_AB);
TEST_ASSERT(mb_get_uint16_ab(&arr_16) == (uint16_t)12345);
TEST_ASSERT(mb_set_int16_ab(&arr_16, (int16_t)12345) == TEST_INT16_AB);
TEST_ASSERT(mb_get_int16_ab(&arr_16) == (int16_t)12345);
TEST_ASSERT(mb_set_uint16_ba(&arr_16, (uint16_t)12345) == TEST_UINT16_BA);
TEST_ASSERT(mb_get_uint16_ba(&arr_16) == (uint16_t)12345);
TEST_ASSERT(mb_set_int16_ba(&arr_16, (int16_t)12345) == TEST_INT16_BA);
TEST_ASSERT(mb_get_int16_ba(&arr_16) == (int16_t)12345);
TEST_ASSERT(mb_set_uint16_ab(&arr_16, (uint16_t)12345) == TEST_UINT16_AB);
TEST_ASSERT(mb_get_uint16_ab(&arr_16) == (uint16_t)12345);
TEST_ASSERT(mb_set_int16_ab(&arr_16, (int16_t)12345) == TEST_INT16_AB);
TEST_ASSERT(mb_get_int16_ab(&arr_16) == (int16_t)12345);
TEST_ASSERT(mb_set_float_abcd(&arr_32, (float)12345.0) == TEST_FLOAT_ABCD);
TEST_ASSERT(mb_get_float_abcd(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_float_badc(&arr_32, (float)12345.0) == TEST_FLOAT_BADC);
TEST_ASSERT(mb_get_float_badc(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_float_cdab(&arr_32, (float)12345.0) == TEST_FLOAT_CDAB);
TEST_ASSERT(mb_get_float_cdab(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_float_dcba(&arr_32, (float)12345.0) == TEST_FLOAT_DCBA);
TEST_ASSERT(mb_get_float_dcba(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_uint32_abcd(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_ABCD);
TEST_ASSERT(mb_get_uint32_abcd(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_abcd(&arr_32, (int32_t)0x11223344) == TEST_UINT32_ABCD);
TEST_ASSERT(mb_get_int32_abcd(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_uint32_badc(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_BADC);
TEST_ASSERT(mb_get_uint32_badc(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_badc(&arr_32, (int32_t)0x11223344) == TEST_UINT32_BADC);
TEST_ASSERT(mb_get_int32_badc(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_uint32_cdab(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_CDAB);
TEST_ASSERT(mb_get_uint32_cdab(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_cdab(&arr_32, (int32_t)0x11223344) == TEST_UINT32_CDAB);
TEST_ASSERT(mb_get_int32_cdab(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_uint32_dcba(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_DCBA);
TEST_ASSERT(mb_get_uint32_dcba(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_dcba(&arr_32, (int32_t)0x11223344) == TEST_UINT32_DCBA);
TEST_ASSERT(mb_get_int32_dcba(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_double_abcdefgh(&arr_64, (double)12345.0) == TEST_DOUBLE_ABCDEFGH);
TEST_ASSERT(mb_get_double_abcdefgh(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_abcdefgh(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_ABCDEFGH);
TEST_ASSERT(mb_get_uint64_abcdefgh(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_abcdefgh(&arr_64, (int64_t)-12345) == TEST_INT64_ABCDEFGH);
TEST_ASSERT(mb_get_int64_abcdefgh(&arr_64) == (int64_t)-12345);
TEST_ASSERT(mb_set_double_hgfedcba(&arr_64, (double)12345.0) == TEST_DOUBLE_HGFEDCBA);
TEST_ASSERT(mb_get_double_hgfedcba(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_hgfedcba(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_HGFEDCBA);
TEST_ASSERT(mb_get_uint64_hgfedcba(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_hgfedcba(&arr_64, (int64_t)-12345) == TEST_INT64_HGFEDCBA);
TEST_ASSERT(mb_get_int64_hgfedcba(&arr_64) == (int64_t)-12345);
TEST_ASSERT(mb_set_double_ghefcdab(&arr_64, (double)12345.0) == TEST_DOUBLE_GHEFCDAB);
TEST_ASSERT(mb_get_double_ghefcdab(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_ghefcdab(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_GHEFCDAB);
TEST_ASSERT(mb_get_uint64_ghefcdab(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_ghefcdab(&arr_64, (int64_t)-12345) == TEST_INT64_GHEFCDAB);
TEST_ASSERT(mb_get_int64_ghefcdab(&arr_64) == (int64_t)-12345);
TEST_ASSERT(mb_set_double_badcfehg(&arr_64, (double)12345.0) == TEST_DOUBLE_BADCFEHG);
TEST_ASSERT(mb_get_double_badcfehg(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_badcfehg(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_BADCFEHG);
TEST_ASSERT(mb_get_uint64_badcfehg(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_badcfehg(&arr_64, (int64_t)-12345) == TEST_INT64_BADCFEHG);
TEST_ASSERT(mb_get_int64_badcfehg(&arr_64) == (int64_t)-12345);
}
void app_main(void)
{
unity_run_menu();
}

View File

@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.multi_dut_modbus_generic
def test_mb_endianness_utils(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@@ -0,0 +1,2 @@
# General options for test
CONFIG_FMB_EXT_TYPE_SUPPORT=y