/* * Copyright (c) 2020 PHYTEC Messtechnik GmbH * * SPDX-License-Identifier: Apache-2.0 */ #include "test_modbus.h" #include #include 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 (!IN_RANGE(addr, fp_offset, sizeof(holding_fp) / 2 + fp_offset)) { return -ENOTSUP; } *reg = holding_fp[(addr - fp_offset) / 2]; 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 (!IN_RANGE(addr, fp_offset, sizeof(holding_fp) / 2 + fp_offset)) { return -ENOTSUP; } *reg = holding_fp[(addr - fp_offset) / 2]; LOG_DBG("FP holding register read, addr %u", addr); return 0; } static int holding_reg_wr_fp(uint16_t addr, float reg) { if (!IN_RANGE(addr, fp_offset, sizeof(holding_fp) / 2 + fp_offset)) { return -ENOTSUP; } holding_fp[(addr - fp_offset) / 2] = 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(); } }