Files
esp-modbus/test_apps/test_common/test_common.c
T

753 lines
27 KiB
C

/*
* SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/queue.h"
#include "port_adapter.h"
#include "mb_common.h"
#include "mbc_slave.h"
#include "mbc_master.h"
#include "esp_timer.h" // for esp_timer_get_time()
#include "test_common.h"
#include "esp_heap_caps.h"
#include "sdkconfig.h"
#ifdef CONFIG_HEAP_TRACING
#include "esp_heap_trace.h"
#endif
#if CONFIG_MB_CONSOLE_HELPER_ENABLED
#include "mb_console.h"
#endif
#define TEST_TASK_CYCLE_COUNTER (CONFIG_MB_TEST_COMM_CYCLE_COUNTER)
#define TEST_BUSY_TASK_PRIO (20)
#define TEST_REG_START_AREA0 (0x0000)
#define TEST_READ_MASK (MB_EVENT_HOLDING_REG_RD |\
MB_EVENT_INPUT_REG_RD |\
MB_EVENT_DISCRETE_RD |\
MB_EVENT_COILS_RD)
#define TEST_WRITE_MASK (MB_EVENT_HOLDING_REG_WR | MB_EVENT_COILS_WR)
#define TEST_READ_WRITE_MASK (TEST_WRITE_MASK | TEST_READ_MASK)
#define TEST_BUSY_COUNT (150000)
#define TEST_PAR_INFO_GET_TOUT (10)
#define TEST_SEND_TIMEOUT (200 / portTICK_PERIOD_MS)
#define TEST_TASK_START_TIMEOUT (10000 / portTICK_PERIOD_MS)
#define TEST_NOTIF_SEND_TOUT (400 / portTICK_PERIOD_MS)
#define TEST_NOTIF_SIZE (20)
#define TEST_ALLOW_PROC_FAIL (5) // percentage of allowed failures due to desynchronization
#define TEST_TASK_TICK_TIME (50 / portTICK_PERIOD_MS)
#define TEST_DESTROY_MESSAGE_TOUT (500)
#define TEST_NOTIFY_DONE_TOUT (200 / portTICK_PERIOD_MS)
#define TAG "TEST_COMMON"
#define MSG_DESTROY "mb stop instances\n\0"
typedef enum {
RT_HOLDING_RD,
RT_HOLDING_WR
} mb_access_t;
static uint16_t holding_registers[16] = {0};
static uint16_t input_registers[8] = {0};
static uint16_t coil_registers[10] = {0};
const uint16_t holding_registers_counter = (sizeof(holding_registers) / sizeof(holding_registers[0]));
const uint16_t input_registers_counter = (sizeof(input_registers) / sizeof(input_registers[0]));
const uint16_t coil_registers_counter = (sizeof(coil_registers) / sizeof(coil_registers[0]));
static int test_error_counter = 0;
static int test_good_counter = 0;
static size_t before_free_8bit = 0;
static size_t before_free_32bit = 0;
// Heap memory leak traicing
#ifdef CONFIG_HEAP_TRACING
#define NUM_RECORDS 500
static heap_trace_record_t trace_record[NUM_RECORDS];
#endif
#define CHECK_PAR_VALUE(par, err, value, expected) \
do \
{ \
if ((err != ESP_OK) || (((uint16_t)value) != ((uint16_t)expected))) \
{ \
ESP_LOGE(TAG, "CHAR #%u, value: 0x%" PRIx16 ", expected: 0x%" PRIx16 ", error = %d.", \
(unsigned)par, ((uint16_t)value), ((uint16_t)expected), (int)err); \
TEST_ASSERT((++test_error_counter * 100 / (TEST_TASK_CYCLE_COUNTER * CID_DEV_REG_COUNT)) <= TEST_ALLOW_PROC_FAIL); \
} \
else \
{ \
ESP_LOGI(TAG, "CHAR #%u, value is ok.", (unsigned)par); \
test_good_counter++; \
} \
} while (0)
// The linked list of test tasks instances
LIST_HEAD(task_entry, task_entry_s) s_task_list;
static portMUX_TYPE s_list_spinlock = portMUX_INITIALIZER_UNLOCKED;
static void task_entry_remove(task_entry_t *task_entry)
{
portENTER_CRITICAL(&s_list_spinlock);
LIST_REMOVE(task_entry, entries);
portEXIT_CRITICAL(&s_list_spinlock);
ESP_LOGD(TAG, "Delete task 0x%" PRIx32, (uint32_t)task_entry->task_handle);
vTaskDelete(task_entry->task_handle);
vSemaphoreDelete(task_entry->task_sema_handle);
free(task_entry);
}
static bool task_wait_done_and_remove(task_entry_t *task_entry, TickType_t tout_ticks)
{
bool is_done = false;
if (task_entry && task_entry->task_handle && task_entry->task_sema_handle) {
if ((xSemaphoreTake(task_entry->task_sema_handle, tout_ticks) == pdTRUE)) {
ESP_LOGI(TAG, "Test task 0x%" PRIx32 ", done successfully.", (uint32_t)task_entry->task_handle);
is_done = true;
task_entry_remove(task_entry);
}
// Else statement removed from here.
//This is a workaround on how to handle the finishing of tasks without increasing timeout of
//test_common_task_wait_done_delete_all() and task_wait_done_and_remove()
vTaskDelay(1); // Let the lower priority task to suspend or delete itself
}
return (is_done);
}
void test_task_add_entry(TaskHandle_t task_handle, void *inst)
{
TEST_ASSERT_TRUE(task_handle);
task_entry_t *new_entry = (task_entry_t *) calloc(1, sizeof(task_entry_t));
TEST_ASSERT_TRUE(new_entry);
portENTER_CRITICAL(&s_list_spinlock);
new_entry->task_handle = task_handle;
new_entry->task_sema_handle = xSemaphoreCreateBinary();
new_entry->inst_handle = inst;
LIST_INSERT_HEAD(&s_task_list, new_entry, entries);
portEXIT_CRITICAL(&s_list_spinlock);
xSemaphoreTake(new_entry->task_sema_handle, 1);
}
static task_entry_t *test_task_find_entry(TaskHandle_t task_handle)
{
TEST_ASSERT_NOT_NULL(task_handle);
task_entry_t *it, *pfound = NULL;
if (LIST_EMPTY(&s_task_list)) {
return NULL;
}
portENTER_CRITICAL(&s_list_spinlock);
LIST_FOREACH(it, &s_task_list, entries) {
if (it->task_handle == task_handle) {
pfound = it;
break;
}
}
portEXIT_CRITICAL(&s_list_spinlock);
return pfound;
}
void test_common_task_notify_stop_all()
{
task_entry_t *it = NULL;
if (LIST_EMPTY(&s_task_list)) {
return;
}
LIST_FOREACH(it, &s_task_list, entries) {
ESP_LOGD(TAG, "Notify task stop, inst: %p.", it->task_handle);
test_common_task_notify_start_and_stop(it->task_handle, TASK_STOP);
}
return;
}
bool test_common_wait_check_destroy_message(char *message, uint32_t timeout_ms)
{
bool result = false;
/* Read line from console, non-blocking function, timeout in ms */
#if CONFIG_MB_CONSOLE_HELPER_ENABLED
if (mb_console_event_check(MB_CMD_STOP, MB_PAR_INFO_TOUT) == MB_CMD_STOP) {
ESP_LOGD(TAG, "Destroy message matched, notifying to destroy instances.");
result = true;
}
#else
// If console helper is not enabled, use standard input to read the message.
char buffer[64] = {0}; //fixed size buffer to store the input from stdin
ESP_LOGD(TAG, "Waiting for destroy message: \"%s\" (timeout: %lu ms)", message, timeout_ms);
vTaskDelay(pdMS_TO_TICKS(timeout_ms));
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
ESP_LOGD(TAG, "%s: received: %s", __func__, buffer);
}
if (strcmp(buffer, message) == 0) {
ESP_LOGD(TAG, "Destroy message matched, notifying to destroy instances.");
result = true;
} else {
ESP_LOGD(TAG, "Timeout waiting for destroy message.");
}
#endif
return result;
}
void test_common_task_notify_done(TaskHandle_t task_handle)
{
task_entry_t *it = test_task_find_entry(task_handle);
if (it) {
xSemaphoreGive(it->task_sema_handle);
}
}
static void test_busy_task(void *phandle)
{
spinlock_t spin_lock;
SPIN_LOCK_INIT(spin_lock);
ESP_EARLY_LOGW(TAG, "test task");
while (1) {
SPIN_LOCK_ENTER(spin_lock);
for (int i = 0; i < TEST_BUSY_COUNT; i++) {
;
}
SPIN_LOCK_EXIT(spin_lock);
vTaskDelay(1);
}
}
void test_common_task_start(TaskHandle_t task_handle, uint32_t value)
{
// Directly notify the task waiting to start loop
test_common_task_notify_start_and_stop(task_handle, value);
}
int test_common_task_start_all()
{
task_entry_t *it = NULL;
if (LIST_EMPTY(&s_task_list)) {
return 0;
}
int task_count = 0;
LIST_FOREACH(it, &s_task_list, entries) {
test_common_task_notify_start_and_stop(it->task_handle, TASK_START);
task_count++;
}
return task_count;
}
bool test_common_task_wait_done(TaskHandle_t task_handle, TickType_t timeout_ticks)
{
task_entry_t *it = test_task_find_entry(task_handle);
if (it && (xSemaphoreTake(it->task_sema_handle, timeout_ticks) == pdTRUE)) {
return true;
}
return false;
}
bool test_common_task_wait_done_delete(TaskHandle_t task_handle, TickType_t task_timeout_ticks)
{
task_entry_t *it = test_task_find_entry(task_handle);
return task_wait_done_and_remove(it, task_timeout_ticks);
}
int test_common_task_wait_done_delete_all(TickType_t task_timeout_tick)
{
task_entry_t *it, *ptmp = NULL;
int task_count = 0;
int64_t start_time = esp_timer_get_time(); //getting the reference system time to compare
if (LIST_EMPTY(&s_task_list)) {
return 0;
}
while ((esp_timer_get_time() - start_time) <= (task_timeout_tick * portTICK_PERIOD_MS * 1000)) {
if (LIST_EMPTY(&s_task_list)) {
return task_count;
}
if (test_common_wait_check_destroy_message(MSG_DESTROY, TEST_DESTROY_MESSAGE_TOUT)) {
test_common_task_notify_stop_all();
}
LIST_FOREACH_SAFE(it, &s_task_list, entries, ptmp) {
if (task_wait_done_and_remove(it, TEST_NOTIFY_DONE_TOUT)) {
task_count++;
}
}
vTaskDelay(TEST_TASK_TICK_TIME);
}
task_count = test_common_task_delete_all();
return task_count;
}
void test_common_task_delete(TaskHandle_t task_handle)
{
task_entry_t *it = test_task_find_entry(task_handle);
if (it) {
task_entry_remove(it);
}
}
int test_common_task_delete_all()
{
task_entry_t *it = NULL;
int task_count = 0;
while ((it = LIST_FIRST(&s_task_list))) {
task_entry_remove(it);
task_count++;
}
return task_count;
}
void *test_common_task_get_instance(TaskHandle_t task_handle)
{
task_entry_t *it = test_task_find_entry(task_handle);
if (it) {
return it->inst_handle;
}
return NULL;
}
// Start the high priority task to mimic the case when the modbus
// tasks do not get time quota from RTOS.
TaskHandle_t test_common_start_busy_task(uint32_t priority)
{
TaskHandle_t busy_task_handle = NULL;
if (!priority) {
priority = TEST_BUSY_TASK_PRIO;
}
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_busy_task, "busy_task",
TEST_TASK_STACK_SIZE,
NULL, priority,
&busy_task_handle, MB_PORT_TASK_AFFINITY));
test_task_add_entry(busy_task_handle, NULL);
return busy_task_handle;
}
void test_common_start()
{
#ifdef CONFIG_HEAP_TRACING
ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
#endif
#if CONFIG_MB_CONSOLE_HELPER_ENABLED
mb_console_init();
#endif
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
#ifdef CONFIG_HEAP_TRACING
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
#endif
LIST_INIT(&s_task_list);
}
void test_common_stop()
{
//vQueueDelete(tasks_done_queue);
holding_registers[CID_DEV_REG_COUNT] = 0;
test_error_counter = 0;
test_good_counter = 0;
/* check if unit test has caused heap corruption in any heap */
TEST_ASSERT_MESSAGE(heap_caps_check_integrity(MALLOC_CAP_INVALID, true), "The test has corrupted the heap");
#ifdef CONFIG_HEAP_TRACING
ESP_ERROR_CHECK( heap_trace_stop() );
heap_trace_dump();
#endif
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
test_common_check_leak(before_free_8bit, after_free_8bit, "8BIT",
CONFIG_MB_TEST_LEAK_WARN_LEVEL, CONFIG_MB_TEST_LEAK_CRITICAL_LEVEL);
test_common_check_leak(before_free_32bit, after_free_32bit, "32BIT",
CONFIG_MB_TEST_LEAK_WARN_LEVEL, CONFIG_MB_TEST_LEAK_CRITICAL_LEVEL);
}
uint32_t test_common_task_wait_start_and_stop(TickType_t timeout_ticks)
{
static uint32_t notify_value = 0;
if (xTaskNotifyWait(0, 0, &notify_value, timeout_ticks) == pdTRUE) {
ESP_LOGD(TAG, "Task: 0x%" PRIx32 ", get notify value = %u",
(uint32_t)xTaskGetCurrentTaskHandle(), (unsigned)notify_value);
return pdTRUE;
}
return 0;
}
void test_common_task_notify_start_and_stop(TaskHandle_t task_handle, uint32_t value)
{
char *task_state = (value == TASK_START) ? "start" : "stop";
ESP_LOGD(TAG, "Notify task %s 0x%" PRIx32, task_state, (uint32_t)task_handle);
TEST_ASSERT_EQUAL_INT(xTaskNotify(task_handle, value, eSetValueWithOverwrite), pdTRUE);
}
void test_common_check_leak(size_t before_free,
size_t after_free,
const char *type,
size_t warn_threshold,
size_t critical_threshold)
{
int free_delta = (int)after_free - (int)before_free;
printf("MALLOC_CAP_%s usage: Free memory delta: %d Leak threshold: -%u \n",
type,
free_delta,
critical_threshold);
if (free_delta > 0) {
return; // free memory went up somehow
}
size_t leaked = (size_t)(free_delta * -1);
if (leaked <= warn_threshold) {
return;
}
printf("MALLOC_CAP_%s %s leak: Before %u bytes free, After %u bytes free (delta %u)\n",
type,
leaked <= critical_threshold ? "potential" : "critical",
before_free, after_free, leaked);
fflush(stdout);
TEST_ASSERT_MESSAGE(leaked <= critical_threshold, "The test leaked too much memory");
}
// Helper function to read one characteristic from slave
esp_err_t test_common_read_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data)
{
const mb_parameter_descriptor_t *param_descriptor = NULL;
esp_err_t err = mbc_master_get_cid_info(handle, cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
uint8_t type = 0;
err = mbc_master_get_parameter(handle, cid, (uint8_t *)par_data, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "%p, CHAR #%u %s (%s) value = (0x%04x) parameter read successful.",
handle,
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
*(uint16_t *)par_data);
} else {
ESP_LOGE(TAG, "%p, CHAR #%u (%s) read fail, err = 0x%x (%s).",
handle,
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char *)esp_err_to_name(err));
}
}
return err;
}
// Helper function to write one characteristic to slave
esp_err_t write_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data)
{
const mb_parameter_descriptor_t *param_descriptor = NULL;
esp_err_t err = mbc_master_get_cid_info(handle, cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
uint8_t type = 0; // type of parameter from dictionary
err = mbc_master_set_parameter(handle, cid, (uint8_t *)par_data, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "%p, CHAR #%u %s (%s) value = (0x%04x), write successful.",
handle,
param_descriptor->cid,
param_descriptor->param_key,
param_descriptor->param_units,
*(uint16_t *)par_data);
} else {
ESP_LOGE(TAG, "%p, CHAR #%u (%s) write fail, err = 0x%x (%s).",
handle,
param_descriptor->cid,
param_descriptor->param_key,
(int)err,
(char *)esp_err_to_name(err));
}
}
return err;
}
// This is user function to read and write modbus holding registers
static void test_master_task(void *arg)
{
void *mbm_handle = arg;
//mbm_controller_iface_t *pctrl_obj = ((mbm_controller_iface_t *)mbm_handle);
static mb_access_t req_type = RT_HOLDING_RD;
esp_err_t err = ESP_FAIL;
uint16_t cycle_counter = 0;
// Wait task start notification during timeout
test_common_task_wait_start_and_stop(TEST_TASK_START_TIMEOUT);
holding_registers[CID_DEV_REG0] = TEST_REG_VAL1;
holding_registers[CID_DEV_REG1] = TEST_REG_VAL2;
holding_registers[CID_DEV_REG2] = TEST_REG_VAL3;
holding_registers[CID_DEV_REG3] = TEST_REG_VAL4;
holding_registers[CID_DEV_REG_COUNT] = cycle_counter;
write_modbus_parameter(mbm_handle, CID_DEV_REG0, &holding_registers[CID_DEV_REG0]);
write_modbus_parameter(mbm_handle, CID_DEV_REG1, &holding_registers[CID_DEV_REG1]);
write_modbus_parameter(mbm_handle, CID_DEV_REG2, &holding_registers[CID_DEV_REG2]);
write_modbus_parameter(mbm_handle, CID_DEV_REG3, &holding_registers[CID_DEV_REG3]);
for (cycle_counter = 0; cycle_counter <= TEST_TASK_CYCLE_COUNTER; cycle_counter++) {
switch (req_type) {
case RT_HOLDING_RD:
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG0, &holding_registers[CID_DEV_REG0]);
CHECK_PAR_VALUE(CID_DEV_REG0, err, holding_registers[CID_DEV_REG0], TEST_REG_VAL1);
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG1, &holding_registers[CID_DEV_REG1]);
CHECK_PAR_VALUE(CID_DEV_REG1, err, holding_registers[CID_DEV_REG1], TEST_REG_VAL2);
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG2, &holding_registers[CID_DEV_REG2]);
CHECK_PAR_VALUE(CID_DEV_REG2, err, holding_registers[CID_DEV_REG2], TEST_REG_VAL3);
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG3, &holding_registers[CID_DEV_REG3]);
CHECK_PAR_VALUE(CID_DEV_REG3, err, holding_registers[CID_DEV_REG3], TEST_REG_VAL4);
req_type = RT_HOLDING_WR;
break;
case RT_HOLDING_WR:
err = write_modbus_parameter(mbm_handle, CID_DEV_REG0, &holding_registers[CID_DEV_REG0]);
CHECK_PAR_VALUE(CID_DEV_REG0, err, holding_registers[CID_DEV_REG0], TEST_REG_VAL1);
err = write_modbus_parameter(mbm_handle, CID_DEV_REG1, &holding_registers[CID_DEV_REG1]);
CHECK_PAR_VALUE(CID_DEV_REG1, err, holding_registers[CID_DEV_REG1], TEST_REG_VAL2);
err = write_modbus_parameter(mbm_handle, CID_DEV_REG2, &holding_registers[CID_DEV_REG2]);
CHECK_PAR_VALUE(CID_DEV_REG2, err, holding_registers[CID_DEV_REG2], TEST_REG_VAL3);
err = write_modbus_parameter(mbm_handle, CID_DEV_REG3, &holding_registers[CID_DEV_REG3]);
CHECK_PAR_VALUE(CID_DEV_REG3, err, holding_registers[CID_DEV_REG3], TEST_REG_VAL4);
req_type = RT_HOLDING_RD;
break;
default:
break;
}
if (holding_registers[CID_DEV_REG_COUNT] >= TEST_TASK_CYCLE_COUNTER) {
ESP_LOGI(TAG, "Stop master: %p.", mbm_handle);
break;
} else {
write_modbus_parameter(mbm_handle, CID_DEV_REG_COUNT, &cycle_counter);
vTaskDelay(TEST_TASK_TICK_TIME); // Let the IDLE task to trigger
}
}
ESP_LOGI(TAG, "Destroy master, inst: %p.", mbm_handle);
TEST_ESP_OK(mbc_master_delete(mbm_handle));
test_common_task_notify_done(xTaskGetCurrentTaskHandle());
vTaskSuspend(NULL);
}
static void test_slave_task(void *arg)
{
void *mbs_handle = arg;
mbs_controller_iface_t *pctrl_obj = ((mbs_controller_iface_t *)mbs_handle);
mb_param_info_t reg_info; // keeps the Modbus registers access information
test_common_task_wait_start_and_stop(TEST_TASK_START_TIMEOUT);
while (1) {
// Get parameter information from parameter queue
esp_err_t err = mbc_slave_get_param_info(mbs_handle, &reg_info, TEST_PAR_INFO_GET_TOUT);
const char *rw_str = (reg_info.type & TEST_READ_MASK) ? "READ" : "WRITE";
// Filter events and process them accordingly
if ((err != ESP_ERR_TIMEOUT) && (reg_info.type & TEST_READ_WRITE_MASK)) {
// Get parameter information from parameter queue
ESP_LOGI("SLAVE", "OBJ %p, %s (%" PRIu32 " us), SL: %u, REG:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 "(0x%" PRIx16 "), SIZE:%u",
(void *)pctrl_obj->mb_base->descr.parent,
rw_str,
(uint32_t)reg_info.time_stamp,
(unsigned)pctrl_obj->opts.comm_opts.common_opts.uid,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(uint32_t)reg_info.address,
*(uint16_t *)reg_info.address,
(unsigned)reg_info.size);
}
vTaskDelay(TEST_TASK_TICK_TIME); // Let IDLE task to trigger
if (holding_registers[CID_DEV_REG_COUNT] >= TEST_TASK_CYCLE_COUNTER) {
ESP_LOGD(TAG, "Stop slave: %p.", mbs_handle);
vTaskDelay(TEST_TASK_TICK_TIME); // Let master to get response from slave prior to close
break;
}
}
ESP_LOGI(TAG, "Destroy slave, inst: %p.", mbs_handle);
TEST_ESP_OK(mbc_slave_delete(mbs_handle));
ESP_LOGD(TAG, "Notify task done, inst: %p.", xTaskGetCurrentTaskHandle());
test_common_task_notify_done(xTaskGetCurrentTaskHandle());
vTaskDelay(10);
vTaskSuspend(NULL);
}
void test_common_slave_setup_start(void *mbs_handle)
{
TEST_ASSERT_TRUE(mbs_handle);
mb_register_area_descriptor_t reg_area;
reg_area.type = MB_PARAM_HOLDING;
reg_area.start_offset = TEST_REG_START_AREA0;
reg_area.address = (void *)&holding_registers[CID_DEV_REG0];
reg_area.size = holding_registers_counter << 1;
TEST_ESP_OK(mbc_slave_set_descriptor(mbs_handle, reg_area));
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = TEST_REG_START_AREA0;
reg_area.address = (void *)&input_registers[CID_DEV_REG0];
reg_area.size = input_registers_counter << 1;
TEST_ESP_OK(mbc_slave_set_descriptor(mbs_handle, reg_area));
reg_area.type = MB_PARAM_COIL;
reg_area.start_offset = TEST_REG_START_AREA0;
reg_area.address = (void *)&coil_registers[CID_DEV_REG0];
reg_area.size = coil_registers_counter;
TEST_ESP_OK(mbc_slave_set_descriptor(mbs_handle, reg_area));
TEST_ESP_OK(mbc_slave_start(mbs_handle));
}
#if (CONFIG_FMB_COMM_MODE_RTU_EN || CONFIG_FMB_COMM_MODE_ASCII_EN)
TaskHandle_t test_common_master_serial_create(mb_communication_info_t *pconfig,
uint32_t priority,
const mb_parameter_descriptor_t *descr,
uint16_t descr_size)
{
if (!pconfig || !descr) {
ESP_LOGI(TAG, "invalid master configuration.");
}
void *mbm_handle = NULL;
TaskHandle_t master_task_handle = NULL;
TEST_ESP_OK(mbc_master_create_serial(pconfig, &mbm_handle));
mbm_controller_iface_t *pbase = mbm_handle;
TEST_ESP_OK(mbc_master_set_descriptor(mbm_handle, descr, descr_size));
ESP_LOGI(TAG, "%p, modbus master stack is initialized", mbm_handle);
TEST_ESP_OK(mbc_master_start(mbm_handle));
ESP_LOGI(TAG, "%p, modbus master start...", mbm_handle) ;
if (priority) {
priority = TEST_TASK_PRIO_MASTER;
}
char *port_name = pbase->mb_base->descr.parent_name;
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_master_task, port_name,
TEST_TASK_STACK_SIZE,
mbm_handle, priority,
&master_task_handle, MB_PORT_TASK_AFFINITY));
test_task_add_entry(master_task_handle, mbm_handle);
return master_task_handle;
}
TaskHandle_t test_common_slave_serial_create(mb_communication_info_t *pconfig, uint32_t priority)
{
if (!pconfig) {
ESP_LOGI(TAG, "invalid slave configuration.");
}
void *mbs_handle = NULL;
TaskHandle_t slave_task_handle = NULL;
TEST_ESP_OK(mbc_slave_create_serial(pconfig, &mbs_handle));
mbs_controller_iface_t *pbase = mbs_handle;
test_common_slave_setup_start(mbs_handle);
if (priority) {
priority = TEST_TASK_PRIO_SLAVE;
}
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_slave_task, pbase->mb_base->descr.parent_name,
TEST_TASK_STACK_SIZE,
mbs_handle, priority,
&slave_task_handle, MB_PORT_TASK_AFFINITY));
test_task_add_entry(slave_task_handle, mbs_handle);
return slave_task_handle;
}
#endif
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
TaskHandle_t test_common_master_tcp_create(mb_communication_info_t *pconfig, uint32_t priority, const mb_parameter_descriptor_t *descr, uint16_t descr_size)
{
if (!pconfig || !descr) {
ESP_LOGI(TAG, "invalid master configuration.");
}
void *mbm_handle = NULL;
TaskHandle_t master_task_handle = NULL;
TEST_ESP_OK(mbc_master_create_tcp(pconfig, &mbm_handle));
mbm_controller_iface_t *pbase = mbm_handle;
TEST_ESP_OK(mbc_master_set_descriptor(mbm_handle, descr, descr_size));
ESP_LOGI(TAG, "%p, modbus master stack is initialized", mbm_handle);
TEST_ESP_OK(mbc_master_start(mbm_handle));
ESP_LOGI(TAG, "%p, modbus master start...", mbm_handle) ;
if (priority) {
priority = TEST_TASK_PRIO_MASTER;
}
char *port_name = pbase->mb_base->descr.parent_name;
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_master_task, port_name,
TEST_TASK_STACK_SIZE,
mbm_handle, priority,
&master_task_handle, MB_PORT_TASK_AFFINITY));
test_task_add_entry(master_task_handle, mbm_handle);
return master_task_handle;
}
TaskHandle_t test_common_slave_tcp_create(mb_communication_info_t *pconfig, uint32_t priority)
{
if (!pconfig) {
ESP_LOGI(TAG, "invalid slave configuration.");
}
void *mbs_handle = NULL;
TaskHandle_t slave_task_handle = NULL;
TEST_ESP_OK(mbc_slave_create_tcp(pconfig, &mbs_handle));
mbs_controller_iface_t *pbase = mbs_handle;
test_common_slave_setup_start(mbs_handle);
if (priority) {
priority = TEST_TASK_PRIO_SLAVE;
}
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_slave_task, pbase->mb_base->descr.parent_name,
TEST_TASK_STACK_SIZE,
mbs_handle, priority,
&slave_task_handle, MB_PORT_TASK_AFFINITY));
test_task_add_entry(slave_task_handle, mbs_handle);
return slave_task_handle;
}
#endif