| /* |
| * Copyright (c) 2020 PHYTEC Messtechnik GmbH |
| * Copyright (c) 2021 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /* |
| * Client API in this file is based on mbm_core.c from uC/Modbus Stack. |
| * |
| * uC/Modbus |
| * The Embedded Modbus Stack |
| * |
| * Copyright 2003-2020 Silicon Laboratories Inc. www.silabs.com |
| * |
| * SPDX-License-Identifier: APACHE-2.0 |
| * |
| * This software is subject to an open source license and is distributed by |
| * Silicon Laboratories Inc. pursuant to the terms of the Apache License, |
| * Version 2.0 available at www.apache.org/licenses/LICENSE-2.0. |
| */ |
| |
| /** |
| * @brief MODBUS transport protocol API |
| * @defgroup modbus MODBUS |
| * @ingroup io_interfaces |
| * @{ |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_MODBUS_H_ |
| #define ZEPHYR_INCLUDE_MODBUS_H_ |
| |
| #include <drivers/uart.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** Length of MBAP Header */ |
| #define MODBUS_MBAP_LENGTH 7 |
| /** Length of MBAP Header plus function code */ |
| #define MODBUS_MBAP_AND_FC_LENGTH (MODBUS_MBAP_LENGTH + 1) |
| |
| /** |
| * @brief Frame struct used internally and for raw ADU support. |
| */ |
| struct modbus_adu { |
| /** Transaction Identifier */ |
| uint16_t trans_id; |
| /** Protocol Identifier */ |
| uint16_t proto_id; |
| /** Length of the data only (not the length of unit ID + PDU) */ |
| uint16_t length; |
| /** Unit Identifier */ |
| uint8_t unit_id; |
| /** Function Code */ |
| uint8_t fc; |
| /** Transaction Data */ |
| uint8_t data[CONFIG_MODBUS_BUFFER_SIZE - 4]; |
| /** RTU CRC */ |
| uint16_t crc; |
| }; |
| |
| /** |
| * @brief Coil read (FC01) |
| * |
| * Sends a Modbus message to read the status of coils from a server. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Coil starting address |
| * @param coil_tbl Pointer to an array of bytes containing the value |
| * of the coils read. |
| * The format is: |
| * |
| * MSB LSB |
| * B7 B6 B5 B4 B3 B2 B1 B0 |
| * ------------------------------------- |
| * coil_tbl[0] #8 #7 #1 |
| * coil_tbl[1] #16 #15 #9 |
| * : |
| * : |
| * |
| * Note that the array that will be receiving the coil |
| * values must be greater than or equal to: |
| * (num_coils - 1) / 8 + 1 |
| * @param num_coils Quantity of coils to read |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_read_coils(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| uint8_t *const coil_tbl, |
| const uint16_t num_coils); |
| |
| /** |
| * @brief Read discrete inputs (FC02) |
| * |
| * Sends a Modbus message to read the status of discrete inputs from |
| * a server. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Discrete input starting address |
| * @param di_tbl Pointer to an array that will receive the state |
| * of the discrete inputs. |
| * The format of the array is as follows: |
| * |
| * MSB LSB |
| * B7 B6 B5 B4 B3 B2 B1 B0 |
| * ------------------------------------- |
| * di_tbl[0] #8 #7 #1 |
| * di_tbl[1] #16 #15 #9 |
| * : |
| * : |
| * |
| * Note that the array that will be receiving the discrete |
| * input values must be greater than or equal to: |
| * (num_di - 1) / 8 + 1 |
| * @param num_di Quantity of discrete inputs to read |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_read_dinputs(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| uint8_t *const di_tbl, |
| const uint16_t num_di); |
| |
| /** |
| * @brief Read holding registers (FC03) |
| * |
| * Sends a Modbus message to read the value of holding registers |
| * from a server. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Register starting address |
| * @param reg_buf Is a pointer to an array that will receive |
| * the current values of the holding registers from |
| * the server. The array pointed to by 'reg_buf' needs |
| * to be able to hold at least 'num_regs' entries. |
| * @param num_regs Quantity of registers to read |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_read_holding_regs(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| uint16_t *const reg_buf, |
| const uint16_t num_regs); |
| |
| /** |
| * @brief Read input registers (FC04) |
| * |
| * Sends a Modbus message to read the value of input registers from |
| * a server. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Register starting address |
| * @param reg_buf Is a pointer to an array that will receive |
| * the current value of the holding registers |
| * from the server. The array pointed to by 'reg_buf' |
| * needs to be able to hold at least 'num_regs' entries. |
| * @param num_regs Quantity of registers to read |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_read_input_regs(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| uint16_t *const reg_buf, |
| const uint16_t num_regs); |
| |
| /** |
| * @brief Write single coil (FC05) |
| * |
| * Sends a Modbus message to write the value of single coil to a server. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param coil_addr Coils starting address |
| * @param coil_state Is the desired state of the coil |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_write_coil(const int iface, |
| const uint8_t unit_id, |
| const uint16_t coil_addr, |
| const bool coil_state); |
| |
| /** |
| * @brief Write single holding register (FC06) |
| * |
| * Sends a Modbus message to write the value of single holding register |
| * to a server unit. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Coils starting address |
| * @param reg_val Desired value of the holding register |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_write_holding_reg(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| const uint16_t reg_val); |
| |
| /** |
| * @brief Read diagnostic (FC08) |
| * |
| * Sends a Modbus message to perform a diagnostic function of a server unit. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param sfunc Diagnostic sub-function code |
| * @param data Sub-function data |
| * @param data_out Pointer to the data value |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_request_diagnostic(const int iface, |
| const uint8_t unit_id, |
| const uint16_t sfunc, |
| const uint16_t data, |
| uint16_t *const data_out); |
| |
| /** |
| * @brief Write coils (FC15) |
| * |
| * Sends a Modbus message to write to coils on a server unit. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Coils starting address |
| * @param coil_tbl Pointer to an array of bytes containing the value |
| * of the coils to write. |
| * The format is: |
| * |
| * MSB LSB |
| * B7 B6 B5 B4 B3 B2 B1 B0 |
| * ------------------------------------- |
| * coil_tbl[0] #8 #7 #1 |
| * coil_tbl[1] #16 #15 #9 |
| * : |
| * : |
| * |
| * Note that the array that will be receiving the coil |
| * values must be greater than or equal to: |
| * (num_coils - 1) / 8 + 1 |
| * @param num_coils Quantity of coils to write |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_write_coils(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| uint8_t *const coil_tbl, |
| const uint16_t num_coils); |
| |
| /** |
| * @brief Write holding registers (FC16) |
| * |
| * Sends a Modbus message to write to integer holding registers |
| * to a server unit. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Register starting address |
| * @param reg_buf Is a pointer to an array containing |
| * the value of the holding registers to write. |
| * Note that the array containing the register values must |
| * be greater than or equal to 'num_regs' |
| * @param num_regs Quantity of registers to write |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_write_holding_regs(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| uint16_t *const reg_buf, |
| const uint16_t num_regs); |
| |
| /** |
| * @brief Read floating-point holding registers (FC03) |
| * |
| * Sends a Modbus message to read the value of floating-point |
| * holding registers from a server unit. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Register starting address |
| * @param reg_buf Is a pointer to an array that will receive |
| * the current values of the holding registers from |
| * the server. The array pointed to by 'reg_buf' needs |
| * to be able to hold at least 'num_regs' entries. |
| * @param num_regs Quantity of registers to read |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_read_holding_regs_fp(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| float *const reg_buf, |
| const uint16_t num_regs); |
| |
| /** |
| * @brief Write floating-point holding registers (FC16) |
| * |
| * Sends a Modbus message to write to floating-point holding registers |
| * to a server unit. |
| * |
| * @param iface Modbus interface index |
| * @param unit_id Modbus unit ID of the server |
| * @param start_addr Register starting address |
| * @param reg_buf Is a pointer to an array containing |
| * the value of the holding registers to write. |
| * Note that the array containing the register values must |
| * be greater than or equal to 'num_regs' |
| * @param num_regs Quantity of registers to write |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_write_holding_regs_fp(const int iface, |
| const uint8_t unit_id, |
| const uint16_t start_addr, |
| float *const reg_buf, |
| const uint16_t num_regs); |
| |
| /** Modbus Server User Callback structure */ |
| struct modbus_user_callbacks { |
| /** Coil read callback */ |
| int (*coil_rd)(uint16_t addr, bool *state); |
| |
| /** Coil write callback */ |
| int (*coil_wr)(uint16_t addr, bool state); |
| |
| /** Discrete Input read callback */ |
| int (*discrete_input_rd)(uint16_t addr, bool *state); |
| |
| /** Input Register read callback */ |
| int (*input_reg_rd)(uint16_t addr, uint16_t *reg); |
| |
| /** Floating Point Input Register read callback */ |
| int (*input_reg_rd_fp)(uint16_t addr, float *reg); |
| |
| /** Holding Register read callback */ |
| int (*holding_reg_rd)(uint16_t addr, uint16_t *reg); |
| |
| /** Holding Register write callback */ |
| int (*holding_reg_wr)(uint16_t addr, uint16_t reg); |
| |
| /** Floating Point Holding Register read callback */ |
| int (*holding_reg_rd_fp)(uint16_t addr, float *reg); |
| |
| /** Floating Point Holding Register write callback */ |
| int (*holding_reg_wr_fp)(uint16_t addr, float reg); |
| }; |
| |
| /** |
| * @brief Get Modbus interface index according to interface name |
| * |
| * If there is more than one interface, it can be used to clearly |
| * identify interfaces in the application. |
| * |
| * @param iface_name Modbus interface name |
| * |
| * @retval Modbus interface index or negative error value. |
| */ |
| int modbus_iface_get_by_name(const char *iface_name); |
| |
| /** |
| * @brief ADU raw callback function signature |
| * |
| * @param iface Modbus RTU interface index |
| * @param adu Pointer to the RAW ADU struct to send |
| * |
| * @retval 0 If transfer was successful |
| */ |
| typedef int (*modbus_raw_cb_t)(const int iface, const struct modbus_adu *adu); |
| |
| /** |
| * @brief Modbus interface mode |
| */ |
| enum modbus_mode { |
| /** Modbus over serial line RTU mode */ |
| MODBUS_MODE_RTU, |
| /** Modbus over serial line ASCII mode */ |
| MODBUS_MODE_ASCII, |
| /** Modbus raw ADU mode */ |
| MODBUS_MODE_RAW, |
| }; |
| |
| /** |
| * @brief Modbus serial line parameter |
| */ |
| struct modbus_serial_param { |
| /** Baudrate of the serial line */ |
| uint32_t baud; |
| /** parity UART's parity setting: |
| * UART_CFG_PARITY_NONE, |
| * UART_CFG_PARITY_EVEN, |
| * UART_CFG_PARITY_ODD |
| */ |
| enum uart_config_parity parity; |
| }; |
| |
| /** |
| * @brief Modbus server parameter |
| */ |
| struct modbus_server_param { |
| /** Pointer to the User Callback structure */ |
| struct modbus_user_callbacks *user_cb; |
| /** Modbus unit ID of the server */ |
| uint8_t unit_id; |
| }; |
| |
| /** |
| * @brief User parameter structure to configure Modbus interfase |
| * as client or server. |
| */ |
| struct modbus_iface_param { |
| /** Mode of the interface */ |
| enum modbus_mode mode; |
| union { |
| struct modbus_server_param server; |
| /** Amount of time client will wait for |
| * a response from the server. |
| */ |
| uint32_t rx_timeout; |
| }; |
| union { |
| /** Serial support parameter of the interface */ |
| struct modbus_serial_param serial; |
| /** Pointer to raw ADU callback function */ |
| modbus_raw_cb_t raw_tx_cb; |
| }; |
| }; |
| |
| /** |
| * @brief Configure Modbus Interface as raw ADU server |
| * |
| * @param iface Modbus RTU interface index |
| * @param param Configuration parameter of the server interface |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_init_server(const int iface, struct modbus_iface_param param); |
| |
| /** |
| * @brief Configure Modbus Interface as raw ADU client |
| * |
| * @param iface Modbus RTU interface index |
| * @param param Configuration parameter of the client interface |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_init_client(const int iface, struct modbus_iface_param param); |
| |
| /** |
| * @brief Disable Modbus Interface |
| * |
| * This function is called to disable Modbus interface. |
| * |
| * @param iface Modbus interface index |
| * |
| * @retval 0 If the function was successful |
| */ |
| int modbus_disable(const uint8_t iface); |
| |
| /** |
| * @brief Submit raw ADU |
| * |
| * @param iface Modbus RTU interface index |
| * @param adu Pointer to the RAW ADU struct that is received |
| * |
| * @retval 0 If transfer was successful |
| */ |
| int modbus_raw_submit_rx(const int iface, const struct modbus_adu *adu); |
| |
| /** |
| * @brief Put MBAP header into a buffer |
| * |
| * @param adu Pointer to the RAW ADU struct |
| * @param header Pointer to the buffer in which MBAP header |
| * will be placed. |
| * |
| * @retval 0 If transfer was successful |
| */ |
| void modbus_raw_put_header(const struct modbus_adu *adu, uint8_t *header); |
| |
| /** |
| * @brief Get MBAP header from a buffer |
| * |
| * @param adu Pointer to the RAW ADU struct |
| * @param header Pointer to the buffer containing MBAP header |
| * |
| * @retval 0 If transfer was successful |
| */ |
| void modbus_raw_get_header(struct modbus_adu *adu, const uint8_t *header); |
| |
| /** |
| * @brief Set Server Device Failure exception |
| * |
| * This function modifies ADU passed by the pointer. |
| * |
| * @param adu Pointer to the RAW ADU struct |
| */ |
| void modbus_raw_set_server_failure(struct modbus_adu *adu); |
| |
| /** |
| * @brief Use interface as backend to send and receive ADU |
| * |
| * This function overwrites ADU passed by the pointer and generates |
| * exception responses if backend interface is misconfigured or |
| * target device is unreachable. |
| * |
| * @param iface Modbus client interface index |
| * @param adu Pointer to the RAW ADU struct |
| * |
| * @retval 0 If transfer was successful |
| */ |
| int modbus_raw_backend_txn(const int iface, struct modbus_adu *adu); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| /** |
| * @} |
| */ |
| |
| #endif /* ZEPHYR_INCLUDE_MODBUS_H_ */ |