| /* |
| * Copyright (c) 2020 PHYTEC Messtechnik GmbH |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "test_modbus.h" |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(mbs_test, LOG_LEVEL_INF); |
| |
| const static uint16_t fp_offset = MB_TEST_FP_OFFSET; |
| static uint16_t coils; |
| static uint16_t holding_reg[8]; |
| static float holding_fp[4]; |
| |
| uint8_t server_iface; |
| |
| uint8_t test_get_server_iface(void) |
| { |
| return server_iface; |
| } |
| |
| static int coil_rd(uint16_t addr, bool *state) |
| { |
| if (addr >= (sizeof(coils) * 8)) { |
| return -ENOTSUP; |
| } |
| |
| if (coils & BIT(addr)) { |
| *state = true; |
| } else { |
| *state = false; |
| } |
| |
| LOG_DBG("Coil read, addr %u, %d", addr, (int)*state); |
| |
| return 0; |
| } |
| |
| static int coil_wr(uint16_t addr, bool state) |
| { |
| if (addr >= (sizeof(coils) * 8)) { |
| return -ENOTSUP; |
| } |
| |
| if (state == true) { |
| coils |= BIT(addr); |
| } else { |
| coils &= ~BIT(addr); |
| } |
| |
| LOG_DBG("Coil write, addr %u, %d", addr, (int)state); |
| |
| return 0; |
| } |
| |
| static int discrete_input_rd(uint16_t addr, bool *state) |
| { |
| if (addr >= (sizeof(coils) * 8)) { |
| return -ENOTSUP; |
| } |
| |
| if (coils & BIT(addr)) { |
| *state = true; |
| } else { |
| *state = false; |
| } |
| |
| LOG_DBG("Discrete input read, addr %u, %d", addr, (int)*state); |
| |
| return 0; |
| } |
| |
| static int input_reg_rd(uint16_t addr, uint16_t *reg) |
| { |
| if (addr >= ARRAY_SIZE(holding_reg)) { |
| return -ENOTSUP; |
| } |
| |
| *reg = holding_reg[addr]; |
| |
| LOG_DBG("Input register read, addr %u, 0x%04x", addr, *reg); |
| |
| return 0; |
| } |
| |
| static int input_reg_rd_fp(uint16_t addr, float *reg) |
| { |
| if ((addr < fp_offset) || |
| (addr >= (ARRAY_SIZE(holding_fp) + fp_offset))) { |
| return -ENOTSUP; |
| } |
| |
| *reg = holding_fp[addr - fp_offset]; |
| |
| LOG_DBG("FP input register read, addr %u", addr); |
| |
| return 0; |
| } |
| |
| static int holding_reg_rd(uint16_t addr, uint16_t *reg) |
| { |
| if (addr >= ARRAY_SIZE(holding_reg)) { |
| return -ENOTSUP; |
| } |
| |
| *reg = holding_reg[addr]; |
| |
| LOG_DBG("Holding register read, addr %u", addr); |
| |
| return 0; |
| } |
| |
| static int holding_reg_wr(uint16_t addr, uint16_t reg) |
| { |
| if (addr >= ARRAY_SIZE(holding_reg)) { |
| return -ENOTSUP; |
| } |
| |
| holding_reg[addr] = reg; |
| |
| LOG_DBG("Holding register write, addr %u", addr); |
| |
| return 0; |
| } |
| |
| static int holding_reg_rd_fp(uint16_t addr, float *reg) |
| { |
| if ((addr < fp_offset) || |
| (addr >= (ARRAY_SIZE(holding_fp) + fp_offset))) { |
| return -ENOTSUP; |
| } |
| |
| *reg = holding_fp[addr - fp_offset]; |
| |
| LOG_DBG("FP holding register read, addr %u", addr); |
| |
| return 0; |
| } |
| |
| static int holding_reg_wr_fp(uint16_t addr, float reg) |
| { |
| if ((addr < fp_offset) || |
| (addr >= (ARRAY_SIZE(holding_fp) + fp_offset))) { |
| return -ENOTSUP; |
| } |
| |
| holding_fp[addr - fp_offset] = reg; |
| |
| LOG_DBG("FP holding register write, addr %u", addr); |
| |
| return 0; |
| } |
| |
| static struct modbus_user_callbacks mbs_cbs = { |
| /** Coil read/write callback */ |
| .coil_rd = coil_rd, |
| .coil_wr = coil_wr, |
| /* Discrete Input read callback */ |
| .discrete_input_rd = discrete_input_rd, |
| /* Input Register read callback */ |
| .input_reg_rd = input_reg_rd, |
| /* Floating Point Input Register read callback */ |
| .input_reg_rd_fp = input_reg_rd_fp, |
| /* Holding Register read/write callback */ |
| .holding_reg_rd = holding_reg_rd, |
| .holding_reg_wr = holding_reg_wr, |
| /* Floating Point Holding Register read/write callback */ |
| .holding_reg_rd_fp = holding_reg_rd_fp, |
| .holding_reg_wr_fp = holding_reg_wr_fp, |
| }; |
| |
| static struct modbus_iface_param server_param = { |
| .mode = MODBUS_MODE_RTU, |
| .server = { |
| .user_cb = &mbs_cbs, |
| .unit_id = MB_TEST_NODE_ADDR, |
| }, |
| .serial = { |
| .baud = MB_TEST_BAUDRATE_LOW, |
| .parity = UART_CFG_PARITY_ODD, |
| }, |
| }; |
| |
| /* |
| * This test performed on hardware requires two UART controllers |
| * on the board (with RX/TX lines connected crosswise). |
| * The exact mapping is not required, we assume that both controllers |
| * have similar capabilities and use the instance with index 1 |
| * as interface for the server. |
| */ |
| #if DT_NODE_EXISTS(DT_INST(1, zephyr_modbus_serial)) |
| static const char rtu_iface_name[] = {DEVICE_DT_NAME(DT_INST(1, zephyr_modbus_serial))}; |
| #else |
| static const char rtu_iface_name[] = ""; |
| #endif |
| |
| void test_server_setup_low_odd(void) |
| { |
| int err; |
| |
| server_iface = modbus_iface_get_by_name(rtu_iface_name); |
| server_param.mode = MODBUS_MODE_RTU; |
| server_param.serial.baud = MB_TEST_BAUDRATE_LOW; |
| server_param.serial.parity = UART_CFG_PARITY_ODD; |
| |
| if (IS_ENABLED(CONFIG_MODBUS_SERVER)) { |
| err = modbus_init_server(server_iface, server_param); |
| zassert_equal(err, 0, "Failed to configure RTU server"); |
| } else { |
| ztest_test_skip(); |
| } |
| } |
| |
| void test_server_setup_low_none(void) |
| { |
| int err; |
| |
| server_iface = modbus_iface_get_by_name(rtu_iface_name); |
| server_param.mode = MODBUS_MODE_RTU; |
| server_param.serial.baud = MB_TEST_BAUDRATE_LOW; |
| server_param.serial.parity = UART_CFG_PARITY_NONE; |
| |
| if (IS_ENABLED(CONFIG_MODBUS_SERVER)) { |
| err = modbus_init_server(server_iface, server_param); |
| zassert_equal(err, 0, "Failed to configure RTU server"); |
| } else { |
| ztest_test_skip(); |
| } |
| } |
| |
| void test_server_setup_high_even(void) |
| { |
| int err; |
| |
| server_iface = modbus_iface_get_by_name(rtu_iface_name); |
| server_param.mode = MODBUS_MODE_RTU; |
| server_param.serial.baud = MB_TEST_BAUDRATE_HIGH; |
| server_param.serial.parity = UART_CFG_PARITY_EVEN; |
| |
| if (IS_ENABLED(CONFIG_MODBUS_SERVER)) { |
| err = modbus_init_server(server_iface, server_param); |
| zassert_equal(err, 0, "Failed to configure RTU server"); |
| } else { |
| ztest_test_skip(); |
| } |
| } |
| |
| void test_server_setup_ascii(void) |
| { |
| int err; |
| |
| server_iface = modbus_iface_get_by_name(rtu_iface_name); |
| server_param.mode = MODBUS_MODE_ASCII; |
| server_param.serial.baud = MB_TEST_BAUDRATE_HIGH; |
| server_param.serial.parity = UART_CFG_PARITY_EVEN; |
| |
| if (IS_ENABLED(CONFIG_MODBUS_SERVER)) { |
| err = modbus_init_server(server_iface, server_param); |
| zassert_equal(err, 0, "Failed to configure RTU server"); |
| } else { |
| ztest_test_skip(); |
| } |
| } |
| |
| void test_server_setup_raw(void) |
| { |
| char iface_name[] = "RAW_1"; |
| int err; |
| |
| server_iface = modbus_iface_get_by_name(iface_name); |
| server_param.mode = MODBUS_MODE_RAW; |
| server_param.rawcb.raw_tx_cb = server_raw_cb; |
| |
| if (IS_ENABLED(CONFIG_MODBUS_SERVER)) { |
| err = modbus_init_server(server_iface, server_param); |
| zassert_equal(err, 0, "Failed to configure RAW server"); |
| } else { |
| ztest_test_skip(); |
| } |
| } |
| |
| void test_server_disable(void) |
| { |
| int err; |
| |
| if (IS_ENABLED(CONFIG_MODBUS_SERVER)) { |
| err = modbus_disable(server_iface); |
| zassert_equal(err, 0, "Failed to disable RTU server"); |
| } else { |
| ztest_test_skip(); |
| } |
| } |