This commit is contained in:
2025-06-08 16:55:50 +01:00
parent b457a39997
commit 03de00b93f
249 changed files with 93 additions and 19015 deletions

View File

@@ -9,6 +9,6 @@ idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" "lib/cAT/src"
PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash app_update json driver esp_http_client esp_netif esp_wifi esp_timer esp_hw_support
REQUIRES network config evse peripherals protocols meter_orno_modbus ocpp)
REQUIRES network config evse peripherals protocols meter_orno ocpp)
set_source_files_properties(lib/cAT/src/cat.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized)

View File

@@ -15,7 +15,6 @@
#include "evse_api.h"
#include "ocpp.h"
#include "board_config.h"
#include "meter.h"
#include "socket_lock.h"
#include "proximity.h"
//#include "modbus.h"
@@ -58,9 +57,9 @@ cJSON *json_get_evse_config(void)
cJSON_AddBoolToObject(root, "socketLockDetectionHigh", socket_lock_is_detection_high());
cJSON_AddNumberToObject(root, "socketLockRetryCount", socket_lock_get_retry_count());
cJSON_AddStringToObject(root, "modelMeter", meter_model_to_str(meter_get_model()));
cJSON_AddNumberToObject(root, "maxGridCurrent", grid_get_max_current());
cJSON_AddStringToObject(root, "stateMeter", meter_state_to_str(meter_get_state()));
//cJSON_AddStringToObject(root, "modelMeter", meter_model_to_str(meter_get_model()));
//cJSON_AddNumberToObject(root, "maxGridCurrent", grid_get_max_current());
//cJSON_AddStringToObject(root, "stateMeter", meter_state_to_str(meter_get_state()));
char str[64];
cJSON_AddBoolToObject(root, "enabledocpp", ocpp_get_enabled());
@@ -144,12 +143,12 @@ esp_err_t json_set_evse_config(cJSON *root)
socket_lock_set_retry_count(cJSON_GetObjectItem(root, "socketLockRetryCount")->valuedouble);
}
/*
if (cJSON_IsString(cJSON_GetObjectItem(root, "modelMeter")))
{
RETURN_ON_ERROR(meter_set_model(meter_str_to_model(cJSON_GetObjectItem(root, "modelMeter")->valuestring)));
}
/*
if (cJSON_IsString(cJSON_GetObjectItem(root, "stateMeter")))
{
RETURN_ON_ERROR(meter_set_state(meter_str_to_state(cJSON_GetObjectItem(root, "stateMeter")->valuestring)));
@@ -379,6 +378,7 @@ cJSON *json_get_state(void)
cJSON_AddItemToObject(root, "errors", errors);
}
/*
cJSON_AddNumberToObject(root, "sessionTime", energy_meter_get_session_time());
cJSON_AddNumberToObject(root, "chargingTime", energy_meter_get_charging_time());
cJSON_AddNumberToObject(root, "consumption", energy_meter_get_consumption());
@@ -388,6 +388,7 @@ cJSON *json_get_state(void)
cJSON_AddItemToObject(root, "voltage", cJSON_CreateFloatArray(values, 3));
energy_meter_get_current(values);
cJSON_AddItemToObject(root, "current", cJSON_CreateFloatArray(values, 3));
*/
return root;
}
@@ -456,6 +457,8 @@ cJSON *json_get_board_config(void)
cJSON_AddNumberToObject(root, "socketLockMinBreakTime", board_config.socket_lock_min_break_time);
cJSON_AddBoolToObject(root, "rcm", board_config.rcm);
cJSON_AddBoolToObject(root, "temperatureSensor", board_config.onewire && board_config.onewire_temp_sensor);
/*
switch (board_config.energy_meter)
{
case BOARD_CONFIG_ENERGY_METER_CUR:
@@ -467,7 +470,8 @@ cJSON *json_get_board_config(void)
default:
cJSON_AddStringToObject(root, "energyMeter", "none");
}
cJSON_AddBoolToObject(root, "energyMeterThreePhases", board_config.energy_meter_three_phases);
//cJSON_AddBoolToObject(root, "energyMeterThreePhases", board_config.energy_meter_three_phases);
*/
cJSON_AddStringToObject(root, "serial1", serial_to_str(board_config.serial_1));
cJSON_AddStringToObject(root, "serial1Name", board_config.serial_1_name);

View File

@@ -6,7 +6,6 @@
#include "evse_config.h"
#include "evse_api.h"
#include "pilot.h"
#include "meter.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
@@ -69,11 +68,6 @@ void evse_process(void) {
}
xSemaphoreGive(mutex);
energy_meter_process(
evse_state_is_charging(evse_get_state()),
evse_get_charging_current()
);
}
// ================================

View File

@@ -7,7 +7,6 @@
#include "ac_relay.h"
#include "board_config.h"
#include "socket_lock.h"
#include "meter.h"
#include "proximity.h"
#include "rcm.h"
#include "evse_state.h"
@@ -37,7 +36,7 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
if (board_config.socket_lock && socket_outlet) {
socket_lock_set_locked(false);
}
energy_meter_stop_session();
//energy_meter_stop_session();
break;
case EVSE_STATE_B1:
@@ -57,7 +56,7 @@ static void update_outputs(evse_state_t state, uint16_t charging_current, uint8_
cable_max_current = proximity_get_max_current();
}
energy_meter_start_session();
//energy_meter_start_session();
break;
case EVSE_STATE_B2:

View File

@@ -3,7 +3,6 @@
#include "ac_relay.h"
#include "socket_lock.h"
#include "proximity.h"
#include "meter.h"
static const char *TAG = "evse_hardware";
@@ -27,16 +26,9 @@ bool evse_hardware_is_vehicle_connected(void) {
}
bool evse_hardware_is_energy_detected(void) {
if (!meter_is_running()) {
return false;
}
MeterData data = meter_getData();
const float THRESHOLD = 0.1f; // Corrente mínima para considerar consumo
return (data.irmsA > THRESHOLD) || (data.irmsB > THRESHOLD) ||
(data.irmsC > THRESHOLD);
}
void evse_hardware_relay_on(void) {
ac_relay_set_state(true);
}

View File

@@ -6,4 +6,4 @@ set(srcs
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
REQUIRES esp_event meter_orno_modbus ade7758)
REQUIRES esp_event meter_orno meter_ade7758)

View File

@@ -5,4 +5,4 @@ set(srcs
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
REQUIRES esp_event meter_orno_modbus)
REQUIRES esp_event meter_orno meter_zigbee)

View File

@@ -6,5 +6,5 @@ idf_component_register(
INCLUDE_DIRS
"include"
REQUIRES
driver evse nvs_flash esp_timer meter_orno_modbus
driver evse nvs_flash esp_timer
)

View File

@@ -4,5 +4,4 @@ set(srcs
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
PRIV_REQUIRES driver
REQUIRES config evse loadbalancer serial_sync)
PRIV_REQUIRES driver)

View File

@@ -1,4 +1,3 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
@@ -7,171 +6,104 @@
#include "string.h"
#include "driver/gpio.h"
#include "meter_zigbee.h"
#include "evse_api.h"
#include "loadbalancer.h"
#include "meter.h"
//#include "app_main.h"
//#include "sync_master.h"
#include <math.h>
#define BUF_SIZE 128
#define TAG "meter_zigbee"
#define TXD_PIN (GPIO_NUM_17)
#define RXD_PIN (GPIO_NUM_16)
#define BUF_SIZE 128
#define RX_BUF_SIZE 14
#define VOLTAGE_CURRENT1_ATTR 0x0006
#define VOLTAGE_CURRENT2_ATTR 0x0007
#define VOLTAGE_CURRENT3_ATTR 0x0008
// static uint8_t msg[128];
static const char *TAG = "meter_zigbee";
static uart_port_t port = -1;
static TaskHandle_t meter_zigbee_task = NULL;
static float l1_current = 0, l2_current = 0, l3_current = 0;
static const int RX_BUF_SIZE = 14;
static void meter_zigbee_task_func(void *param)
{
ESP_LOGI(TAG, "meter_zigbee_task_func");
uint8_t *buff = (uint8_t *)malloc(RX_BUF_SIZE + 1);
unsigned long currentMillis = pdTICKS_TO_MS(xTaskGetTickCount()) - 120000;
while (1)
{
const int rxBytes = uart_read_bytes(UART_NUM_1, buff, RX_BUF_SIZE, 1000 / portTICK_PERIOD_MS);
if (rxBytes >= 10)
{
//ESP_LOGI(TAG, "Read %d bytes: '%s'", rxBytes, buff);
//ESP_LOG_BUFFER_HEXDUMP(TAG, buff, rxBytes, ESP_LOG_INFO);
uint8_t idmsg = buff[0];
uint16_t code = buff[1] + (buff[2] << 8);
uint8_t size = buff[4];
uint32_t power = 0;
uint32_t current = 0;
uint32_t volt = 0;
float maxcurrent = 0;
float l1current = 0;
float l2current = 0;
float l3current = 0;
//ESP_LOGI(TAG, "Msg id: %d code 0x%04hx size %d ", idmsg, code, size);
if (size == 8)
{
power = buff[12] + (buff[11] << 8) + (buff[10] << 16); // + (buff[9] << 24);
ESP_LOGI(TAG, "VOLTAGE_CURRENT_ATTR Power value %" PRIu32 " ", power);
current = buff[9] + (buff[8] << 8) + (buff[7] << 16); // + (buff[9] << 24);
ESP_LOGI(TAG, "VOLTAGE_CURRENT_ATTR Current value %" PRIu32 " ", current);
volt = buff[6] + (buff[5] << 8); // + (buff[4] << 16);// + (buff[9] << 24);
ESP_LOGI(TAG, "VOLTAGE_CURRENT_ATTR Voltage value %" PRIu32 " ", volt);
if (code == VOLTAGE_CURRENT1_ATTR)
{
l1current = current / 100.0f;
}
else if (code == VOLTAGE_CURRENT2_ATTR)
{
l2current = current / 100.0f;
}
else if (code == VOLTAGE_CURRENT3_ATTR)
{
l3current = current / 100.0f;
}
else
{
ESP_LOGW(TAG, "Error code %d ", code);
// continue;
}
maxcurrent = (l1current > l2current) ? l1current : l2current;
maxcurrent = (maxcurrent > l3current) ? maxcurrent : l3current;
ESP_LOGI(TAG, "maxcurrent value %f ", maxcurrent);
//send_grid_current(maxcurrent);
if (evse_state_is_charging(evse_get_state()))
{
setMaxGridCurrent(grid_get_max_current() * 10);
setLiveGridCurrent((int)maxcurrent);
/*
if (pdTICKS_TO_MS(xTaskGetTickCount()) - currentMillis > 120000)
{
push_grid_power(power / 1000.0f);
push_grid_current(l1current / 10.0f);
push_grid_volt(volt / 10.0f);
currentMillis = pdTICKS_TO_MS(xTaskGetTickCount());
}*/
}
}
}
}
free(buff);
static float decode_current(const uint8_t *buf) {
return (buf[9] + (buf[8] << 8) + (buf[7] << 16)) / 100.0f;
}
int meter_zigbee_send_data(const char *data)
{
const int len = strlen(data);
const int txBytes = uart_write_bytes(UART_NUM_1, data, len);
ESP_LOGI(TAG, "Wrote %d bytes", txBytes);
return txBytes;
static void decode_frame(const uint8_t *buf) {
uint16_t attr_code = buf[1] | (buf[2] << 8);
uint8_t size = buf[4];
if (size != 8) {
ESP_LOGW(TAG, "Unexpected data size: %d", size);
return;
}
void meter_zigbee_start()
{
float current = decode_current(buf);
ESP_LOGI(TAG, "Attr 0x%04X - Current: %.2f A", attr_code, current);
ESP_LOGI(TAG, "Starting MT Serial");
switch (attr_code) {
case VOLTAGE_CURRENT1_ATTR: l1_current = current; break;
case VOLTAGE_CURRENT2_ATTR: l2_current = current; break;
case VOLTAGE_CURRENT3_ATTR: l3_current = current; break;
default:
ESP_LOGW(TAG, "Unknown attribute code: 0x%04X", attr_code);
return;
}
const uart_config_t uart_config = {
float max_current = fmaxf(fmaxf(l1_current, l2_current), l3_current);
ESP_LOGI(TAG, "Max current: %.2f A", max_current);
}
static void meter_zigbee_task_func(void *param) {
ESP_LOGI(TAG, "Zigbee meter task started");
uint8_t *buf = malloc(RX_BUF_SIZE);
if (!buf) {
ESP_LOGE(TAG, "Memory allocation failed");
vTaskDelete(NULL);
return;
}
while (1) {
int len = uart_read_bytes(UART_NUM_1, buf, RX_BUF_SIZE, pdMS_TO_TICKS(1000));
if (len >= 10) {
decode_frame(buf);
}
}
free(buf);
vTaskDelete(NULL);
}
int meter_zigbee_send_data(const char *data) {
int len = strlen(data);
int sent = uart_write_bytes(UART_NUM_1, data, len);
ESP_LOGI(TAG, "Sent %d bytes", sent);
return sent;
}
void meter_zigbee_start(void) {
ESP_LOGI(TAG, "Starting Zigbee UART");
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
.source_clk = UART_SCLK_DEFAULT
};
esp_err_t err = uart_param_config(UART_NUM_1, &uart_config);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "uart_param_config() returned 0x%x", err);
return;
}
err = uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "uart_driver_install() returned 0x%x", err);
return;
}
ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL,0));
// xTaskCreate(rx_task, "uart_rx_task", 1024 * 4, NULL, configMAX_PRIORITIES, NULL);
xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4 * 1024, NULL, 5, &meter_zigbee_task);
xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4096, NULL, 5, &meter_zigbee_task);
}
void meter_zigbee_stop(void)
{
ESP_LOGI(TAG, "Stopping");
void meter_zigbee_stop(void) {
ESP_LOGI(TAG, "Stopping Zigbee UART");
if (meter_zigbee_task)
{
if (meter_zigbee_task) {
vTaskDelete(meter_zigbee_task);
meter_zigbee_task = NULL;
}
if (port != -1)
{
uart_driver_delete(port);
port = -1;
}
uart_driver_delete(UART_NUM_1);
}

View File

@@ -1,8 +0,0 @@
set(srcs
"src/modbus.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash app_update driver esp_timer
REQUIRES config evse peripherals)

View File

@@ -1,17 +0,0 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp-modbus: "^1.0.16"
## Required IDF version
idf:
version: ">=4.1.0"
# # Put list of dependencies here
# # For components maintained by Espressif:
# component: "~1.0.0"
# # For 3rd party components:
# username/component: ">=1.0.0,<2.0.0"
# username2/component2:
# version: "~1.0.0"
# # For transient dependencies `public` flag can be set.
# # `public` flag doesn't have an effect dependencies of the `main` component.
# # All dependencies of `main` are public by default.
# public: true

View File

@@ -1,42 +0,0 @@
#ifndef MODBUS_H_
#define MODBUS_H_
#define MODBUS_PACKET_SIZE 256
#define MODBUS_READ_UINT16(buf, offset) ((uint16_t)(buf[offset] << 8 | buf[offset + 1]))
#define MODBUS_WRITE_UINT16(buf, offset, value) \
buf[offset] = value >> 8; \
buf[offset + 1] = value & 0xFF; \
#include "esp_err.h"
/**
* @brief Initialize modbus
*
*/
void modbus_init(void);
/**
* @brief Process modbus request
*
* @param buf Request/response data
* @param len Length of request data
* @return uint16_t Length of response data, 0 if no response
*/
uint16_t modbus_request_exec(uint8_t *buf, uint16_t len);
/**
* @brief Get modbus unit id
*
* @return uint8_t
*/
uint8_t modbus_get_unit_id(void);
/**
* @brief Set modbus unit id
*
* @param unit_id
*/
esp_err_t modbus_set_unit_id(uint8_t unit_id);
#endif /* MODBUS_H_ */

View File

@@ -1,508 +0,0 @@
#include <string.h>
#include <math.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_ota_ops.h"
#include "nvs.h"
#include "modbus.h"
#include "evse_api.h"
#include "evse_error.h"
#include "meter.h"
#include "socket_lock.h"
#include "temp_sensor.h"
#define MODBUS_REG_STATE 100
#define MODBUS_REG_ERROR 101 // 2 word
#define MODBUS_REG_ENABLED 103
#define MODBUS_REG_AVAILABLE 104
#define MODBUS_REG_PENDING_AUTH 105
#define MODBUS_REG_CHR_CURRENT 106
#define MODBUS_REG_CONSUMPTION_LIM 107 // 2 word
#define MODBUS_REG_CHR_TIME_LIM 109 // 2 word
#define MODBUS_REG_UNDER_POWER_LIM 111
#define MODBUS_REG_AUTHORISE 112
#define MODBUS_REG_EMETER_POWER 200
#define MODBUS_REG_EMETER_SES_TIME 201 // 2 word
#define MODBUS_REG_EMETER_CHR_TIME 203 // 2 word
#define MODBUS_REG_EMETER_CONSUMPTION 205 // 2 word
#define MODBUS_REG_EMETER_L1_VTL 207 // 2 word
#define MODBUS_REG_EMETER_L2_VTL 209 // 2 word
#define MODBUS_REG_EMETER_L3_VTL 211 // 2 word
#define MODBUS_REG_EMETER_L1_CUR 213 // 2 word
#define MODBUS_REG_EMETER_L2_CUR 215 // 2 word
#define MODBUS_REG_EMETER_L3_CUR 217 // 2 word
#define MODBUS_REG_SOCKET_OUTLET 300
#define MODBUS_REG_RCM 301
#define MODBUS_REG_TEMP_THRESHOLD 302
#define MODBUS_REG_REQ_AUTH 303
#define MODBUS_REG_MAX_CHR_CURRENT 304
#define MODBUS_REG_DEF_CHR_CURRENT 305
#define MODBUS_REG_DEF_CONSUMPTION_LIM 306 //2 word
#define MODBUS_REG_DEF_CHR_TIME_LIM 308 //2 word
#define MODBUS_REG_DEF_UNDER_POWER_LIM 310
#define MODBUS_REG_LOCK_OPERATING_TIME 311
#define MODBUS_REG_LOCK_BRAKE_TIME 312
#define MODBUS_REG_LOCK_DET_HI 313
#define MODBUS_REG_LOCK_RET_COUNT 314
#define MODBUS_REG_EMETER_MODE 315
#define MODBUS_REG_EMETER_AC_VLT 316
#define MODBUS_REG_UPTIME 400 //2 word
#define MODBUS_REG_TEMP_LOW 402
#define MODBUS_REG_TEMP_HIGH 403
#define MODBUS_REG_TEMP_SENSOR_COUNT 404
#define MODBUS_REG_APP_VERSION 405 //16 word
#define MODBUS_REG_RESTART 421
#define MODBUS_EX_NONE 0x00
#define MODBUS_EX_ILLEGAL_FUNCTION 0x01
#define MODBUS_EX_ILLEGAL_DATA_ADDRESS 0x02
#define MODBUS_EX_ILLEGAL_DATA_VALUE 0x03
#define MODBUS_EX_SLAVE_DEVICE_FAILURE 0x04
#define MODBUS_EX_ACKNOWLEDGE 0x05
#define MODBUS_EX_SLAVE_BUSY 0x06
#define MODBUS_EX_MEMORY_PARITY_ERROR 0x08
#define UINT32_GET_HI(value) ((uint16_t)(((uint32_t) (value)) >> 16))
#define UINT32_GET_LO(value) ((uint16_t)(((uint32_t) (value)) & 0xFFFF))
#define NVS_NAMESPACE "modbus"
#define NVS_UNIT_ID "unit_id"
static const char* TAG = "modbus";
static nvs_handle nvs;
static uint8_t unit_id = 1;
static void restart_func(void* arg)
{
vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart();
vTaskDelete(NULL);
}
static void timeout_restart()
{
xTaskCreate(restart_func, "restart_task", 2 * 1024, NULL, 10, NULL);
}
void modbus_init(void)
{
ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs));
nvs_get_u8(nvs, NVS_UNIT_ID, &unit_id);
}
static uint32_t get_uptime(void)
{
return esp_timer_get_time() / 1000000;
}
static bool read_holding_register(uint16_t addr, uint16_t* value)
{
ESP_LOGD(TAG, "HR read %d", addr);
switch (addr) {
case MODBUS_REG_STATE:
const char* state_str = evse_state_to_str(evse_get_state());
*value = state_str[0] << 8 | state_str[1];
break;
case MODBUS_REG_ERROR:
*value = UINT32_GET_HI(evse_get_error());
break;
case MODBUS_REG_ERROR + 1:
*value = UINT32_GET_LO(evse_get_error());
break;
case MODBUS_REG_ENABLED:
*value = evse_is_enabled();
break;
case MODBUS_REG_AVAILABLE:
*value = evse_is_enabled();
break;
case MODBUS_REG_PENDING_AUTH:
*value = evse_is_pending_auth();
break;
case MODBUS_REG_CHR_CURRENT:
*value = evse_get_charging_current();
break;
case MODBUS_REG_CONSUMPTION_LIM:
*value = UINT32_GET_HI(evse_get_consumption_limit());
break;
case MODBUS_REG_CONSUMPTION_LIM + 1:
*value = UINT32_GET_LO(evse_get_consumption_limit());
break;
case MODBUS_REG_CHR_TIME_LIM:
*value = UINT32_GET_HI(evse_get_charging_time_limit());
break;
case MODBUS_REG_CHR_TIME_LIM + 1:
*value = UINT32_GET_LO(evse_get_charging_time_limit());
break;
case MODBUS_REG_UNDER_POWER_LIM:
*value = evse_get_under_power_limit();
break;
case MODBUS_REG_EMETER_POWER:
*value = energy_meter_get_power();
break;
case MODBUS_REG_EMETER_SES_TIME:
*value = UINT32_GET_HI(energy_meter_get_session_time());
break;
case MODBUS_REG_EMETER_SES_TIME + 1:
*value = UINT32_GET_LO(energy_meter_get_session_time());
break;
case MODBUS_REG_EMETER_CHR_TIME:
*value = UINT32_GET_HI(energy_meter_get_charging_time());
break;
case MODBUS_REG_EMETER_CHR_TIME + 1:
*value = UINT32_GET_LO(energy_meter_get_charging_time());
break;
case MODBUS_REG_EMETER_CONSUMPTION:
*value = UINT32_GET_HI(energy_meter_get_consumption());
break;
case MODBUS_REG_EMETER_CONSUMPTION + 1:
*value = UINT32_GET_LO(energy_meter_get_consumption());
break;
case MODBUS_REG_EMETER_L1_VTL:
*value = UINT32_GET_HI(energy_meter_get_l1_voltage() * 1000);
break;
case MODBUS_REG_EMETER_L1_VTL + 1:
*value = UINT32_GET_LO(energy_meter_get_l1_voltage() * 1000);
break;
case MODBUS_REG_EMETER_L2_VTL:
*value = UINT32_GET_HI(energy_meter_get_l2_voltage() * 1000);
break;
case MODBUS_REG_EMETER_L2_VTL + 1:
*value = UINT32_GET_LO(energy_meter_get_l2_voltage() * 1000);
break;
case MODBUS_REG_EMETER_L3_VTL:
*value = UINT32_GET_HI(energy_meter_get_l3_voltage() * 1000);
break;
case MODBUS_REG_EMETER_L3_VTL + 1:
*value = UINT32_GET_LO(energy_meter_get_l3_voltage() * 1000);
break;
case MODBUS_REG_EMETER_L1_CUR:
*value = UINT32_GET_HI(energy_meter_get_l1_current() * 1000);
break;
case MODBUS_REG_EMETER_L1_CUR + 1:
*value = UINT32_GET_LO(energy_meter_get_l1_current() * 1000);
break;
case MODBUS_REG_EMETER_L2_CUR:
*value = UINT32_GET_HI(energy_meter_get_l2_current() * 1000);
break;
case MODBUS_REG_EMETER_L2_CUR + 1:
*value = UINT32_GET_LO(energy_meter_get_l2_current() * 1000);
break;
case MODBUS_REG_EMETER_L3_CUR:
*value = UINT32_GET_HI(energy_meter_get_l3_current() * 1000);
break;
case MODBUS_REG_EMETER_L3_CUR + 1:
*value = UINT32_GET_LO(energy_meter_get_l3_current() * 1000);
break;
case MODBUS_REG_SOCKET_OUTLET:
*value = evse_get_socket_outlet();
break;
case MODBUS_REG_RCM:
*value = evse_is_rcm();
break;
case MODBUS_REG_TEMP_THRESHOLD:
*value = evse_get_temp_threshold();
break;
case MODBUS_REG_REQ_AUTH:
*value = evse_is_require_auth();
break;
case MODBUS_REG_MAX_CHR_CURRENT:
*value = evse_get_max_charging_current();
break;
case MODBUS_REG_DEF_CHR_CURRENT:
*value = evse_get_default_charging_current();
break;
case MODBUS_REG_DEF_CONSUMPTION_LIM:
*value = UINT32_GET_HI(evse_get_default_consumption_limit());
break;
case MODBUS_REG_DEF_CONSUMPTION_LIM + 1:
*value = UINT32_GET_LO(evse_get_default_consumption_limit());
break;
case MODBUS_REG_DEF_CHR_TIME_LIM:
*value = UINT32_GET_HI(evse_get_default_charging_time_limit());
break;
case MODBUS_REG_DEF_CHR_TIME_LIM + 1:
*value = UINT32_GET_LO(evse_get_default_charging_time_limit());
break;
case MODBUS_REG_DEF_UNDER_POWER_LIM:
*value = evse_get_default_under_power_limit();
break;
case MODBUS_REG_LOCK_OPERATING_TIME:
*value = socket_lock_get_operating_time();
break;
case MODBUS_REG_LOCK_BRAKE_TIME:
*value = socket_lock_get_break_time();
break;
case MODBUS_REG_LOCK_DET_HI:
*value = socket_lock_is_detection_high();
break;
case MODBUS_REG_LOCK_RET_COUNT:
*value = socket_lock_get_retry_count();
break;
//case MODBUS_REG_EMETER_MODE:
// *value = energy_meter_get_mode();
// break;
//case MODBUS_REG_EMETER_AC_VLT:
// *value = energy_meter_get_ac_voltage();
// break;
case MODBUS_REG_UPTIME:
*value = UINT32_GET_HI(get_uptime());
break;
case MODBUS_REG_UPTIME + 1:
*value = UINT32_GET_LO(get_uptime());
break;
case MODBUS_REG_TEMP_LOW:
*value = temp_sensor_get_low();
break;
case MODBUS_REG_TEMP_HIGH:
*value = temp_sensor_get_high();
break;
case MODBUS_REG_TEMP_SENSOR_COUNT:
*value = temp_sensor_get_count();
break;
default:
//string registers
if (addr >= MODBUS_REG_APP_VERSION && addr <= MODBUS_REG_APP_VERSION + 16) {
const esp_app_desc_t* app_desc = esp_app_get_description();
*value = app_desc->version[(addr - MODBUS_REG_APP_VERSION) * 2] << 8 | app_desc->version[(addr - MODBUS_REG_APP_VERSION) * 2 + 1];
} else {
return MODBUS_EX_ILLEGAL_DATA_ADDRESS;
}
}
return MODBUS_EX_NONE;
}
static bool write_holding_register(uint16_t addr, uint8_t* buffer, uint16_t left)
{
uint16_t value = MODBUS_READ_UINT16(buffer, 0);
ESP_LOGD(TAG, "HR write %d = %d", addr, value);
switch (addr) {
case MODBUS_REG_ENABLED:
if (value > 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
evse_set_enabled(value);
break;
case MODBUS_REG_AVAILABLE:
if (value > 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
evse_set_available(value);
break;
case MODBUS_REG_CHR_CURRENT:
if (evse_set_charging_current(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_CONSUMPTION_LIM:
if (left > 0) {
evse_set_consumption_limit(value << 16 | MODBUS_READ_UINT16(buffer, 2));
} else {
return MODBUS_EX_ILLEGAL_DATA_ADDRESS;
}
break;
case MODBUS_REG_CONSUMPTION_LIM + 1:
break;
case MODBUS_REG_CHR_TIME_LIM:
if (left > 0) {
evse_set_charging_time_limit(value << 16 | MODBUS_READ_UINT16(buffer, 2));
} else {
return MODBUS_EX_ILLEGAL_DATA_ADDRESS;
}
break;
case MODBUS_REG_CHR_TIME_LIM + 1:
break;
case MODBUS_REG_UNDER_POWER_LIM:
evse_set_under_power_limit(value);
break;
case MODBUS_REG_AUTHORISE:
if (value != 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
evse_authorize();
break;
case MODBUS_REG_SOCKET_OUTLET:
if (value != 0 || value != 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
if (evse_set_socket_outlet(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_RCM:
if (value != 0 || value != 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
if (evse_set_rcm(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_TEMP_THRESHOLD:
if (evse_set_temp_threshold(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_REQ_AUTH:
if (value != 0 || value != 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
evse_set_require_auth(value);
break;
case MODBUS_REG_MAX_CHR_CURRENT:
if (evse_set_max_charging_current(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_DEF_CHR_CURRENT:
if (evse_set_default_charging_current(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_DEF_CONSUMPTION_LIM:
if (left > 0) {
evse_set_default_consumption_limit(value << 16 | MODBUS_READ_UINT16(buffer, 2));
} else {
return MODBUS_EX_ILLEGAL_DATA_ADDRESS;
}
break;
case MODBUS_REG_DEF_CONSUMPTION_LIM + 1:
break;
case MODBUS_REG_DEF_CHR_TIME_LIM:
if (left > 0) {
evse_set_default_charging_time_limit(value << 16 | MODBUS_READ_UINT16(buffer, 2));
} else {
return MODBUS_EX_ILLEGAL_DATA_ADDRESS;
}
break;
case MODBUS_REG_DEF_CHR_TIME_LIM + 1:
break;
case MODBUS_REG_DEF_UNDER_POWER_LIM:
evse_set_default_under_power_limit(value);
break;
case MODBUS_REG_LOCK_OPERATING_TIME:
if (socket_lock_set_operating_time(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_LOCK_BRAKE_TIME:
if (socket_lock_set_break_time(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_LOCK_DET_HI:
if (value != 0 || value != 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
socket_lock_set_detection_high(value);
break;
case MODBUS_REG_LOCK_RET_COUNT:
if (value > UINT8_MAX) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
socket_lock_set_retry_count(value);
break;
/*
case MODBUS_REG_EMETER_MODE:
if (energy_meter_set_mode(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
case MODBUS_REG_EMETER_AC_VLT:
if (energy_meter_set_ac_voltage(value) != ESP_OK) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
break;
*/
case MODBUS_REG_RESTART:
if (value != 1) {
return MODBUS_EX_ILLEGAL_DATA_VALUE;
}
timeout_restart();
break;
default:
return MODBUS_EX_ILLEGAL_DATA_ADDRESS;
}
return MODBUS_EX_NONE;
}
uint16_t modbus_request_exec(uint8_t* data, uint16_t len)
{
uint16_t resp_len = 0;
if (unit_id == data[0]) {
uint8_t fc = data[1];
uint16_t addr;
uint16_t count;
uint16_t value;
uint8_t ex = MODBUS_EX_NONE;
if (fc == 3) {
addr = MODBUS_READ_UINT16(data, 2);
count = MODBUS_READ_UINT16(data, 4);
data[2] = count * 2;
resp_len = 3 + count * 2;
for (uint16_t i = 0; i < count; i++) {
if ((ex = read_holding_register(addr + i, &value)) != MODBUS_EX_NONE) {
break;
}
MODBUS_WRITE_UINT16(data, 3 + 2 * i, value);
}
} else if (fc == 6) {
addr = MODBUS_READ_UINT16(data, 2);
resp_len = 6;
ex = write_holding_register(addr, &data[4], 0);
} else if (fc == 16) {
addr = MODBUS_READ_UINT16(data, 2);
count = MODBUS_READ_UINT16(data, 4);
resp_len = 6;
for (uint16_t i = 0; i < count; i++) {
if ((ex = write_holding_register(addr + i, &data[7 + 2 * i], count - i)) != MODBUS_EX_NONE) {
break;
}
}
} else {
ex = MODBUS_EX_ILLEGAL_FUNCTION;
}
if (ex != MODBUS_EX_NONE) {
data[1] = 0x8 | fc;
data[2] = ex;
resp_len = 3;
}
}
return resp_len;
}
uint8_t modbus_get_unit_id(void)
{
return unit_id;
}
esp_err_t modbus_set_unit_id(uint8_t _unit_id)
{
if (_unit_id == 0) {
ESP_LOGE(TAG, "Unit id cant be 0");
return ESP_ERR_INVALID_ARG;
}
unit_id = _unit_id;
nvs_set_u8(nvs, NVS_UNIT_ID, unit_id);
return ESP_OK;
}

View File

@@ -5,4 +5,4 @@ set(srcs
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
PRIV_REQUIRES nvs_flash
REQUIRES config esp_wifi evse ade7758 mongoose MicroOcpp MicroOcppMongoose)
REQUIRES config esp_wifi evse mongoose MicroOcpp MicroOcppMongoose)

View File

@@ -6,7 +6,6 @@
#include "evse_state.h"
#include "esp_wifi.h"
#include "meter.h"
#include "orno_modbus.h"
#include "nvs.h"
@@ -149,24 +148,14 @@ bool setEvseReadyInput()
// return false;
}
#include "meter.h"
#include "esp_log.h"
#define TAG "ocpp_input" // ou o nome adequado do seu módulo
float setPowerMeterInput()
{
ESP_LOGI(TAG, "PowerMeterInput");
if (!meter_is_running()) {
ESP_LOGW(TAG, "Meter not running, returning fallback value.");
//MeterData data = meter_getData();
//return data.wattA + data.wattB + data.wattC;
return 0.0f;
}
MeterData data = meter_getData();
return data.wattA + data.wattB + data.wattC;
}
float setEnergyMeterInput()
{
ESP_LOGI(TAG, "EnergyMeterInput");
@@ -187,6 +176,7 @@ float setCurrentInput()
{
ESP_LOGI(TAG, "CurrentInput");
/*
if (!meter_is_running()) {
ESP_LOGW(TAG, "Meter not running, returning fallback.");
return 0.0f;
@@ -194,12 +184,15 @@ float setCurrentInput()
MeterData data = meter_getData();
return data.irmsA;
*/
return 0;
}
float setVoltageInput()
{
ESP_LOGI(TAG, "VoltageInput");
/*
if (!meter_is_running()) {
ESP_LOGW(TAG, "Meter not running, returning fallback.");
return 0.0f;
@@ -207,6 +200,8 @@ float setVoltageInput()
MeterData data = meter_getData();
return data.vrmsA;
*/
return 0.0f;
}
float setTemperatureInput()
@@ -219,7 +214,8 @@ float setPowerInput()
{
ESP_LOGI(TAG, "PowerInput");
// return (float)orno_modbus_get_meter_state().activepower;
return meter_getData().wattA + meter_getData().wattB + meter_getData().wattC;
//return meter_getData().wattA + meter_getData().wattB + meter_getData().wattC;
return 0;
}
void setSmartChargingCurrentOutput(float limit)

View File

@@ -19,4 +19,4 @@ set(srcs
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
PRIV_REQUIRES nvs_flash driver esp_adc esp_timer
REQUIRES config evse api ade7758 ntc_driver)
REQUIRES config evse api ntc_driver)

View File

@@ -7,7 +7,6 @@
#include "ac_relay.h"
#include "socket_lock.h"
#include "rcm.h"
#include "meter.h"
#include "aux_io.h"
#include "ntc_sensor.h"
@@ -21,7 +20,7 @@ void peripherals_init(void)
proximity_init();
// socket_lock_init();
// rcm_init();
energy_meter_init();
//energy_meter_init();
// aux_init();
ntc_sensor_init();
}

View File

@@ -5,12 +5,6 @@ dependencies:
service_url: https://api.components.espressif.com/
type: service
version: 0.5.3
espressif/esp-modbus:
component_hash: 2168e6b4cbda4d0281a2a2d1a40a3848e231473b2690d73217e3600fd2c98c12
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.0.16
espressif/mdns:
component_hash: 3ec0af5f6bce310512e90f482388d21cc7c0e99668172d2f895356165fc6f7c5
source:
@@ -28,6 +22,6 @@ dependencies:
source:
type: idf
version: 5.3.0
manifest_hash: c3d3273b8d8a591808187df8795265e85b441d0c189d0c969b6afb32d9166bac
manifest_hash: 182b2719a9746e2efb089e041f2387b8dedb900344831b6f5b6ce215a485c67f
target: esp32
version: 1.0.0

View File

View File

View File

0
managed_components/espressif__cmake_utilities/Kconfig Executable file → Normal file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

@@ -1 +0,0 @@
2168e6b4cbda4d0281a2a2d1a40a3848e231473b2690d73217e3600fd2c98c12

View File

@@ -1,49 +0,0 @@
.config
*.o
*.pyc
# gtags
GTAGS
GRTAGS
GPATH
# emacs
.dir-locals.el
# emacs temp file suffixes
*~
.#*
\#*#
# eclipse setting
.settings
# MacOS directory files
.DS_Store
# Test files
test/build
test/sdkconfig
test/sdkconfig.old
# Doc build artifacts
docs/_build/
docs/doxygen-warning-log.txt
docs/sphinx-warning-log.txt
docs/sphinx-warning-log-sanitized.txt
docs/xml/
docs/xml_in/
docs/man/
docs/doxygen_sqlite3.db
TEST_LOGS
# gcov coverage reports
*.gcda
*.gcno
coverage.info
coverage_report/
# VS Code Settings
.vscode/

View File

@@ -1,78 +0,0 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
set(srcs
"common/esp_modbus_master.c"
"common/esp_modbus_slave.c"
"modbus/mb.c"
"modbus/mb_m.c"
"modbus/ascii/mbascii.c"
"modbus/ascii/mbascii_m.c"
"modbus/rtu/mbrtu_m.c"
"modbus/rtu/mbrtu.c"
"modbus/rtu/mbcrc.c"
"modbus/tcp/mbtcp.c"
"modbus/tcp/mbtcp_m.c"
"port/port.c"
"port/portevent.c"
"port/portevent_m.c"
"port/portother.c"
"port/portother_m.c"
"port/portserial.c"
"port/portserial_m.c"
"port/porttimer.c"
"port/porttimer_m.c"
"modbus/functions/mbfunccoils.c"
"modbus/functions/mbfunccoils_m.c"
"modbus/functions/mbfuncdiag.c"
"modbus/functions/mbfuncdisc.c"
"modbus/functions/mbfuncdisc_m.c"
"modbus/functions/mbfuncholding.c"
"modbus/functions/mbfuncholding_m.c"
"modbus/functions/mbfuncinput.c"
"modbus/functions/mbfuncinput_m.c"
"modbus/functions/mbfuncother.c"
"modbus/functions/mbutils.c"
"serial_slave/modbus_controller/mbc_serial_slave.c"
"serial_master/modbus_controller/mbc_serial_master.c"
"tcp_slave/port/port_tcp_slave.c"
"tcp_slave/modbus_controller/mbc_tcp_slave.c"
"tcp_master/modbus_controller/mbc_tcp_master.c"
"tcp_master/port/port_tcp_master.c"
"common/esp_modbus_master_tcp.c"
"common/esp_modbus_slave_tcp.c"
"common/esp_modbus_master_serial.c"
"common/esp_modbus_slave_serial.c")
set(include_dirs common/include)
set(priv_include_dirs common port modbus modbus/ascii modbus/functions
modbus/rtu modbus/tcp modbus/include)
list(APPEND priv_include_dirs serial_slave/port serial_slave/modbus_controller
serial_master/port serial_master/modbus_controller
tcp_slave/port tcp_slave/modbus_controller
tcp_master/port tcp_master/modbus_controller)
if(CONFIG_FMB_EXT_TYPE_SUPPORT)
list(APPEND srcs "common/mb_endianness_utils.c")
endif()
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${srcs})
add_prefix(include_dirs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${include_dirs})
add_prefix(priv_include_dirs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${priv_include_dirs})
message(STATUS "DEBUG: Use esp-modbus component folder: ${CMAKE_CURRENT_LIST_DIR}.")
set(requires driver lwip)
# esp_timer component was introduced in v4.2
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
list(APPEND requires esp_timer)
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
REQUIRES ${requires}
PRIV_REQUIRES esp_netif)

View File

@@ -1,233 +0,0 @@
menu "Modbus configuration"
config FMB_COMM_MODE_TCP_EN
bool "Enable Modbus stack support for TCP communication mode"
default y
help
Enable Modbus TCP option for stack.
config FMB_TCP_PORT_DEFAULT
int "Modbus TCP port number"
range 0 65535
default 502
depends on FMB_COMM_MODE_TCP_EN
help
Modbus default port number used by Modbus TCP stack
config FMB_TCP_PORT_MAX_CONN
int "Maximum allowed connections for TCP stack"
range 1 8
default 5
depends on FMB_COMM_MODE_TCP_EN
help
Maximum allowed connections number for Modbus TCP stack.
This is used by Modbus master and slave port layer to establish connections.
This parameter may decrease performance of Modbus stack and can cause
increasing of processing time (increase only if absolutely necessary).
config FMB_TCP_CONNECTION_TOUT_SEC
int "Modbus TCP connection timeout"
range 1 3600
default 20
depends on FMB_COMM_MODE_TCP_EN
help
Modbus TCP connection timeout in seconds.
Once expired the current connection with the client will be closed
and Modbus slave will be waiting for new connection to accept.
config FMB_TCP_UID_ENABLED
bool "Modbus TCP enable UID (Unit Identifier) support"
default n
depends on FMB_COMM_MODE_TCP_EN
help
If this option is set the Modbus stack uses UID (Unit Identifier) field in MBAP frame.
Else the UID is ignored by master and slave.
config FMB_COMM_MODE_RTU_EN
bool "Enable Modbus stack support for RTU mode"
default y
help
Enable RTU Modbus communication mode option for Modbus serial stack.
config FMB_COMM_MODE_ASCII_EN
bool "Enable Modbus stack support for ASCII mode"
default y
help
Enable ASCII Modbus communication mode option for Modbus serial stack.
config FMB_MASTER_TIMEOUT_MS_RESPOND
int "Slave respond timeout (Milliseconds)"
default 3000
range 150 15000
help
If master sends a frame which is not broadcast, it has to wait sometime for slave response.
if slave is not respond in this time, the master will process timeout error.
config FMB_MASTER_DELAY_MS_CONVERT
int "Slave conversion delay (Milliseconds)"
default 200
range 150 2000
help
If master sends a broadcast frame, it has to wait conversion time to delay,
then master can send next frame.
config FMB_QUEUE_LENGTH
int "Modbus serial task queue length"
range 0 200
default 20
help
Modbus serial driver queue length. It is used by event queue task.
See the serial driver API for more information.
config FMB_PORT_TASK_STACK_SIZE
int "Modbus port task stack size"
range 2048 8192
default 4096
help
Modbus port task stack size for rx/tx event processing.
It may be adjusted when debugging is enabled (for example).
config FMB_SERIAL_BUF_SIZE
int "Modbus serial task RX/TX buffer size"
range 0 2048
default 256
help
Modbus serial task RX and TX buffer size for UART driver initialization.
This buffer is used for modbus frame transfer. The Modbus protocol maximum
frame size is 256 bytes. Bigger size can be used for non standard implementations.
config FMB_SERIAL_ASCII_BITS_PER_SYMB
int "Number of data bits per ASCII character"
default 8
range 7 8
depends on FMB_COMM_MODE_ASCII_EN
help
This option defines the number of data bits per ASCII character.
config FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS
int "Wait before send for ASCII communication mode (ms)"
default 0
range 0 1000
depends on FMB_COMM_MODE_ASCII_EN
help
This option defines timeout before slave sends the response in ASCII communication mode.
This allows to work with slow masters. Zero means delay before send is disabled.
config FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS
int "Response timeout for ASCII communication mode (ms)"
default 1000
range 200 5000
depends on FMB_COMM_MODE_ASCII_EN
help
This option defines response timeout of slave in milliseconds for ASCII communication mode.
Thus the timeout will expire and allow the master program to handle the error.
config FMB_PORT_TASK_PRIO
int "Modbus port task priority"
range 3 23
default 10
help
Modbus port data processing task priority.
The priority of Modbus controller task is equal to (CONFIG_FMB_PORT_TASK_PRIO - 1).
choice FMB_PORT_TASK_AFFINITY
prompt "Modbus task affinity"
default FMB_PORT_TASK_AFFINITY_CPU0
depends on !FREERTOS_UNICORE
help
Allows setting the core affinity of the Modbus controller task, i.e. whether the task is pinned to
particular CPU, or allowed to run on any CPU.
config FMB_PORT_TASK_AFFINITY_NO_AFFINITY
bool "No affinity"
config FMB_PORT_TASK_AFFINITY_CPU0
bool "CPU0"
config FMB_PORT_TASK_AFFINITY_CPU1
bool "CPU1"
endchoice
config FMB_PORT_TASK_AFFINITY
hex
default FREERTOS_NO_AFFINITY if FMB_PORT_TASK_AFFINITY_NO_AFFINITY || FREERTOS_UNICORE
default 0x0 if FMB_PORT_TASK_AFFINITY_CPU0
default 0x1 if FMB_PORT_TASK_AFFINITY_CPU1
config FMB_CONTROLLER_SLAVE_ID_SUPPORT
bool "Modbus controller slave ID support"
default y
help
Modbus slave ID support enable.
When enabled the Modbus <Report Slave ID> command is supported by stack.
config FMB_CONTROLLER_SLAVE_ID
hex "Modbus controller slave ID"
range 0 4294967295
default 0x00112233
depends on FMB_CONTROLLER_SLAVE_ID_SUPPORT
help
Modbus slave ID value to identify modbus device
in the network using <Report Slave ID> command.
Most significant byte of ID is used as short device ID and
other three bytes used as long ID.
config FMB_CONTROLLER_NOTIFY_TIMEOUT
int "Modbus controller notification timeout (ms)"
range 0 200
default 20
help
Modbus controller notification timeout in milliseconds.
This timeout is used to send notification about accessed parameters.
config FMB_CONTROLLER_NOTIFY_QUEUE_SIZE
int "Modbus controller notification queue size"
range 0 200
default 20
help
Modbus controller notification queue size.
The notification queue is used to get information about accessed parameters.
config FMB_CONTROLLER_STACK_SIZE
int "Modbus controller stack size"
range 0 8192
default 4096
help
Modbus controller task stack size. The Stack size may be adjusted when
debug mode is used which requires more stack size (for example).
config FMB_EVENT_QUEUE_TIMEOUT
int "Modbus stack event queue timeout (ms)"
range 0 500
default 20
help
Modbus stack event queue timeout in milliseconds. This may help to optimize
Modbus stack event processing time.
config FMB_TIMER_PORT_ENABLED
bool "Modbus stack use timer for 3.5T symbol time measurement"
default n
help
If this option is set the Modbus stack uses timer for T3.5 time measurement.
Else the internal UART TOUT timeout is used for 3.5T symbol time measurement.
config FMB_TIMER_USE_ISR_DISPATCH_METHOD
bool "Modbus timer uses ISR dispatch method"
default n
select ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
select UART_ISR_IN_IRAM
help
If this option is set the Modbus stack uses ISR dispatch method
to send timeout events from the callback function called from ISR.
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

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,65 +0,0 @@
# ESP-Modbus Library
## Overview
An Espressif ESP-Modbus Library (esp-modbus) is a library to support Modbus communication in the networks based on RS485, WiFi, Ethernet interfaces. The Modbus is a data communications protocol originally published by Modicon (now Schneider Electric) in 1979 for use with its programmable logic controllers (PLCs).
* [ESP-Modbus component on GitHub](https://www.github.com/espressif/esp-modbus)
This library is to be used with Espressifs IoT Development Framework, [ESP_IDF](https://github.com/espressif/esp-idf). The packages from this repository are uploaded to Espressifs component repository.
* [esp-modbus component in component repository](https://components.espressif.com/component/espressif/esp-modbus)
You can add the component to your project via `idf.py add-dependency`. More information about idf-component-manager can be found in [Espressif API guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html) or [PyPi registry](https://pypi.org/project/idf-component-manager).
The ESP-Modbus library can be used with ESP-IDF v4.1 and later. ESP-IDF v4.x releases include an earlier version of ESP-Modbus library inside freemodbus component. To use ESP-Modbus with these releases, users need to exclude the built-in freemodbus component from the build process, and update application components to depend on esp-modbus component instead. To exclude freemodbus component from compilation, add the following line to the project CMakeLists.txt file:
```
set(EXCLUDE_COMPONENTS freemodbus)
```
ESP-IDF v5.x and later releases do not include freemodbus component, so no extra steps are necessary when adding esp-modbus component.
## Documentation
The documentation can be found on the link below:
* [ESP-Modbus documentation (English)](https://docs.espressif.com/projects/esp-modbus)
## Application Examples
The examples below demonstrate the ESP-Modbus library of serial, TCP ports for slave and master implementations accordingly.
- [Modbus serial slave example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/serial/mb_slave)
- [Modbus serial master example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/serial/mb_master)
- [Modbus TCP master example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/tcp/mb_tcp_master)
- [Modbus TCP slave example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/tcp/mb_tcp_slave)
Please refer to the specific example README.md for details.
## Protocol References
- [Modbus Organization with protocol specifications](https://modbus.org/specs.php)
## Contributing
We welcome contributions to this project in the form of bug reports, feature requests and pull requests.
Issue reports and feature requests can be submitted using Github Issues: https://github.com/espressif/esp-modbus/issues. Please check if the issue has already been reported before opening a new one.
Contributions in the form of pull requests should follow ESP-IDF project's [contribution guidelines](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/contribute/index.html). We kindly ask developers to start a discussion on an issue before proposing large changes to the project.
See the beta version of stack v2.0.0_beta introduced [here](https://github.com/espressif/esp-modbus/discussions/45)
## Licence
ESP-Modbus project is based on [FreeMODBUS library](https://github.com/cwalter-at/freemodbus), Copyright (c) 2006 Christian Walter and licensed under the BSD 3-clause license.
Modbus Master related code is Copyright (c) 2013 Armink and licensed under BSD 3-clause license.
All original code in this repository is Copyright (c) 2016-2022 Espressif Systems (Shanghai) Co. Ltd.
The project is distributed under Apache 2.0 license. See the accompanying [LICENSE file](https://github.com/espressif/esp-modbus/blob/master/LICENSE) for a copy.

View File

@@ -1,97 +0,0 @@
#!/bin/bash
#
# Build the test app and all examples from the examples directory.
# Expects TEST_TARGETS environment variables to be set.
# Each variable is the list of IDF_TARGET values to build the examples and
# the test app for, respectively.
#
# -----------------------------------------------------------------------------
# Safety settings (see https://gist.github.com/ilg-ul/383869cbb01f61a51c4d).
if [[ -n "${DEBUG_SHELL}" ]]
then
set -x # Activate the expand mode if DEBUG is anything but empty.
fi
if [[ -z "${EXAMPLE_TARGETS}" || -z "${TEST_TARGETS}" ]]
then
echo "EXAMPLE_TARGETS and TEST_TARGETS environment variables must be set before calling this script"
exit 1
fi
if [[ -z "${SKIP_GNU_MAKE_BUILD}" ]]
then
echo "SKIP_GNU_MAKE_BUILD not set, will build with GNU Make based build system as well."
export SKIP_GNU_MAKE_BUILD=0
fi
set -o errexit # Exit if command failed.
set -o pipefail # Exit if pipe failed.
set -o nounset # Exit if variable not set.
STARS='***************************************************'
# -----------------------------------------------------------------------------
die() {
echo "${1:-"Unknown Error"}" 1>&2
exit 1
}
# build_for_targets <target list>
# call this in the project directory
function build_for_targets
{
target_list="$1"
for IDF_TARGET in ${target_list}
do
export IDF_TARGET
if [[ "${IDF_TARGET}" = "esp32" ]] && [[ "${SKIP_GNU_MAKE_BUILD}" = "0" ]]
then
echo "${STARS}"
echo "Building in $PWD with Make"
# -j option will be set via MAKEFLAGS in .gitlab-ci.yml
# shellcheck disable=SC2015
make defconfig && make || die "Make build in ${PWD} has failed"
rm -rf build
fi
echo "${STARS}"
echo "Building in $PWD with CMake for ${IDF_TARGET}"
preview_target=
if [[ ${IDF_TARGET} == "esp32c6" ]]
then
preview_target="--preview"
fi
if [[ ${IDF_TARGET} != "esp32" ]]
then
# IDF 4.0 doesn't support idf.py set-target, and only supports esp32.
idf.py ${preview_target} set-target "${IDF_TARGET}"
fi
idf.py build || die "CMake build in ${PWD} has failed for ${IDF_TARGET}"
idf.py fullclean
done
}
function build_folders
{
pushd "$1"
EXAMPLES=$(find . -maxdepth 1 -mindepth 1 -type d | cut -d '/' -f 2)
for NAME in ${EXAMPLES}
do
cd "${NAME}"
build_for_targets "$2"
cd ..
done
popd
}
echo "${STARS}"
# Build the tests
build_folders test/serial "${TEST_TARGETS}"
echo "${STARS}"
# Build the tests
build_folders test/tcp "${TEST_TARGETS}"
echo "${STARS}"

View File

@@ -1,29 +0,0 @@
INCLUDEDIRS := common/include
PRIV_INCLUDEDIRS := common port modbus modbus/ascii modbus/functions
PRIV_INCLUDEDIRS += modbus/rtu modbus/tcp modbus/include
PRIV_INCLUDEDIRS += serial_slave/port serial_slave/modbus_controller
PRIV_INCLUDEDIRS += serial_master/port serial_master/modbus_controller
PRIV_INCLUDEDIRS += tcp_slave/port tcp_slave/modbus_controller
PRIV_INCLUDEDIRS += tcp_master/port tcp_master/modbus_controller
SRCDIRS := common
SRCDIRS += modbus modbus/ascii modbus/functions modbus/rtu modbus/tcp
SRCDIRS += serial_slave/port serial_slave/modbus_controller
SRCDIRS += serial_master/port serial_master/modbus_controller
SRCDIRS += tcp_slave/port tcp_slave/modbus_controller
SRCDIRS += tcp_master/port tcp_master/modbus_controller
SRCDIRS += port
COMPONENT_PRIV_INCLUDEDIRS = $(addprefix freemodbus/, \
$(PRIV_INCLUDEDIRS) \
)
COMPONENT_SRCDIRS = $(addprefix freemodbus/, \
$(SRCDIRS) \
)
COMPONENT_ADD_INCLUDEDIRS = $(addprefix freemodbus/, \
$(INCLUDEDIRS) \
)

View File

@@ -1,20 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// Stack callback functions prototypes
#ifndef _ESP_MODBUS_CALLBACKS_H_
#define _ESP_MODBUS_CALLBACKS_H_
#include "mb.h"
#include "mb_m.h"
typedef eMBErrorCode (*reg_input_cb)(UCHAR*, USHORT, USHORT);
typedef eMBErrorCode (*reg_holding_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_coils_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_discrete_cb)(UCHAR*, USHORT, USHORT);
#endif /* _ESP_MODBUS_CALLBACKS_H_ */

View File

@@ -1,532 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "mbc_master.h" // for master interface define
#include "esp_modbus_master.h" // for public interface defines
#include "esp_modbus_callbacks.h" // for callback functions
#include "sdkconfig.h"
static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_MASTER";
// This file implements public API for Modbus master controller.
// These functions are wrappers for interface functions of the controller
static mb_master_interface_t* master_interface_ptr = NULL;
void mbc_master_init_iface(void* handler)
{
master_interface_ptr = (mb_master_interface_t*) handler;
}
/**
* Modbus controller destroy function
*/
esp_err_t mbc_master_destroy(void)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->destroy != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->destroy();
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master destroy failure, error=(0x%x).",
(int)error);
return error;
}
esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_info)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->get_cid_info != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->get_cid_info(cid, param_info);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master get cid info failure, error=(0x%x).",
(int)error);
return error;
}
/**
* Get parameter data for corresponding characteristic
*/
esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->get_parameter != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->get_parameter(cid, name, value, type);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master get parameter failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return error;
}
/**
* Send custom Modbus request defined as mb_param_request_t structure
*/
esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->send_request != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->send_request(request, data_ptr);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master send request failure error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Set Modbus parameter description table
*/
esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor,
const uint16_t num_elements)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->set_descriptor != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->set_descriptor(descriptor, num_elements);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master set descriptor failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Set parameter value for characteristic selected by name and cid
*/
esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->set_parameter != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->set_parameter(cid, name, value, type);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master set parameter failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Setup Modbus controller parameters
*/
esp_err_t mbc_master_setup(void* comm_info)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->setup != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->setup(comm_info);
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master setup failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
/**
* Modbus controller stack start function
*/
esp_err_t mbc_master_start(void)
{
esp_err_t error = ESP_OK;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->start != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->start();
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master start failure, error=(0x%x) (%s).",
(int)error, esp_err_to_name(error));
return ESP_OK;
}
eMBErrorCode eMBMasterRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_discrete != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
return error;
}
eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_coils != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_coils(pucRegBuffer, usAddress,
usNCoils, eMode);
return error;
}
eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_holding != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_holding(pucRegBuffer, usAddress,
usNRegs, eMode);
return error;
}
eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_input != NULL),
ESP_ERR_INVALID_STATE,
"Master interface is not correctly initialized.");
error = master_interface_ptr->master_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
return error;
}
/**
* Helper function to get current transaction info
*/
esp_err_t mbc_master_get_transaction_info(mb_trans_info_t *ptinfo)
{
MB_MASTER_CHECK((ptinfo),
ESP_ERR_INVALID_ARG,
"Wrong argument.");
MB_MASTER_CHECK(xMBMasterGetLastTransactionInfo(&ptinfo->trans_id, &ptinfo->dest_addr,
&ptinfo->func_code, &ptinfo->exception,
&ptinfo->err_type),
ESP_ERR_INVALID_STATE,
"Master can not get transaction info.");
return ESP_OK;
}
// 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

@@ -1,32 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "mbc_master.h" // for master interface define
#include "esp_modbus_master.h" // for public slave defines
#include "mbc_serial_master.h" // for public interface defines
/**
* Initialization of Modbus master serial
*/
esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler)
{
void* port_handler = NULL;
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
switch(port_type)
{
case MB_PORT_SERIAL_MASTER:
error = mbc_serial_master_create(&port_handler);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
}
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_master_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_master.h" // for public interface defines
#include "mbc_tcp_master.h" // for public interface defines
/**
* Initialization of Modbus TCP Master controller interface
*/
esp_err_t mbc_master_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_master_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_master_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -1,530 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_timer.h" // for esp_timer_get_time()
#include "sdkconfig.h" // for KConfig defines
#include "mbc_slave.h" // for slave private type definitions
#include "mbutils.h" // for stack bit setting utilities
#include "esp_modbus_common.h" // for common defines
#include "esp_modbus_slave.h" // for public slave defines
#include "esp_modbus_callbacks.h" // for modbus callbacks function pointers declaration
#ifdef CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT
#define MB_ID_BYTE0(id) ((uint8_t)(id))
#define MB_ID_BYTE1(id) ((uint8_t)(((uint16_t)(id) >> 8) & 0xFF))
#define MB_ID_BYTE2(id) ((uint8_t)(((uint32_t)(id) >> 16) & 0xFF))
#define MB_ID_BYTE3(id) ((uint8_t)(((uint32_t)(id) >> 24) & 0xFF))
#define MB_CONTROLLER_SLAVE_ID (CONFIG_FMB_CONTROLLER_SLAVE_ID)
#define MB_SLAVE_ID_SHORT (MB_ID_BYTE3(MB_CONTROLLER_SLAVE_ID))
// Slave ID constant
static uint8_t mb_slave_id[] = { MB_ID_BYTE0(MB_CONTROLLER_SLAVE_ID),
MB_ID_BYTE1(MB_CONTROLLER_SLAVE_ID),
MB_ID_BYTE2(MB_CONTROLLER_SLAVE_ID) };
#endif
#define REG_SIZE(type, nregs) ((type == MB_PARAM_INPUT) || (type == MB_PARAM_HOLDING)) ? (nregs >> 1) : (nregs << 3)
// Common interface pointer for slave port
static mb_slave_interface_t* slave_interface_ptr = NULL;
static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_SLAVE";
// Searches the register in the area specified by type, returns descriptor if found, else NULL
static mb_descr_entry_t* mbc_slave_find_reg_descriptor(mb_param_type_t type, uint16_t addr, size_t regs)
{
mb_descr_entry_t* it;
uint16_t reg_size = 0;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
if (LIST_EMPTY(&mbs_opts->mbs_area_descriptors[type])) {
return NULL;
}
// search for the register in each area
for (it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[type]); it != NULL; it = LIST_NEXT(it, entries)) {
reg_size = REG_SIZE(type, it->size);
if ((addr >= it->start_offset)
&& (it->p_data)
&& (regs >= 1)
&& ((addr + regs) <= (it->start_offset + reg_size))
&& (reg_size >= 1)) {
return it;
}
}
return NULL;
}
static void mbc_slave_free_descriptors(void) {
mb_descr_entry_t* it;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
for (int descr_type = 0; descr_type < MB_PARAM_COUNT; descr_type++) {
while ((it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[descr_type]))) {
LIST_REMOVE(it, entries);
free(it);
}
}
}
void mbc_slave_init_iface(void* handler)
{
slave_interface_ptr = (mb_slave_interface_t*) handler;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
// Initialize list head for register areas
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_COIL]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE]);
}
/**
* Modbus controller destroy function
*/
esp_err_t mbc_slave_destroy(void)
{
esp_err_t error = ESP_OK;
// Is initialization done?
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
// Check if interface has been initialized
MB_SLAVE_CHECK((slave_interface_ptr->destroy != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
// Call the slave port destroy function
error = slave_interface_ptr->destroy();
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave destroy failure error=(0x%x).",
(int)error);
// Destroy all opened descriptors
mbc_slave_free_descriptors();
free(slave_interface_ptr);
slave_interface_ptr = NULL;
return error;
}
/**
* Setup Modbus controller parameters
*/
esp_err_t mbc_slave_setup(void* comm_info)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->setup != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->setup(comm_info);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave setup failure error=(0x%x).",
(int)error);
return error;
}
/**
* Start Modbus controller start function
*/
esp_err_t mbc_slave_start(void)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->start != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
#ifdef CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT
// Set the slave ID if the KConfig option is selected
eMBErrorCode status = eMBSetSlaveID(MB_SLAVE_ID_SHORT, TRUE, (UCHAR*)mb_slave_id, sizeof(mb_slave_id));
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack set slave ID failure.");
#endif
error = slave_interface_ptr->start();
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave start failure error=(0x%x).",
(int)error);
return error;
}
/**
* Blocking function to get event on parameter group change for application task
*/
mb_event_group_t mbc_slave_check_event(mb_event_group_t group)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EVENT_NO_EVENTS,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->check_event != NULL),
MB_EVENT_NO_EVENTS,
"Slave interface is not correctly initialized.");
mb_event_group_t event = slave_interface_ptr->check_event(group);
return event;
}
/**
* Function to get notification about parameter change from application task
*/
esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->get_param_info != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->get_param_info(reg_info, timeout);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave get parameter info failure error=(0x%x).",
(int)error);
return error;
}
/**
* Function to set area descriptors for modbus parameters
*/
esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data)
{
esp_err_t error = ESP_OK;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->set_descriptor != NULL) {
error = slave_interface_ptr->set_descriptor(descr_data);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave set descriptor failure error=(0x%x).",
(int)error);
} else {
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
// Check if the address is already in the descriptor list
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(descr_data.type, descr_data.start_offset, 1);
MB_SLAVE_CHECK((it == NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor or already defined.");
mb_descr_entry_t* new_descr = (mb_descr_entry_t*) heap_caps_malloc(sizeof(mb_descr_entry_t),
MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
MB_SLAVE_CHECK((new_descr != NULL), ESP_ERR_NO_MEM, "mb can not allocate memory for descriptor.");
new_descr->start_offset = descr_data.start_offset;
new_descr->type = descr_data.type;
new_descr->p_data = descr_data.address;
new_descr->size = descr_data.size;
LIST_INSERT_HEAD(&mbs_opts->mbs_area_descriptors[descr_data.type], new_descr, entries);
error = ESP_OK;
}
return error;
}
// The helper function to get time stamp in microseconds
static uint64_t mbc_slave_get_time_stamp(void)
{
uint64_t time_stamp = esp_timer_get_time();
return time_stamp;
}
// Helper function to send parameter information to application task
static esp_err_t mbc_slave_send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
uint8_t* par_address, uint16_t par_size)
{
MB_SLAVE_ASSERT(slave_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
esp_err_t error = ESP_FAIL;
mb_param_info_t par_info;
// Check if queue is not full the send parameter information
par_info.type = par_type;
par_info.size = par_size;
par_info.address = par_address;
par_info.time_stamp = mbc_slave_get_time_stamp();
par_info.mb_offset = mb_offset;
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle, &par_info, MB_PAR_INFO_TOUT);
if (pdTRUE == status) {
ESP_LOGD(TAG, "Queue send parameter info (type, address, size): %d, 0x%" PRIx32 ", %u",
(int)par_type, (uint32_t)par_address, (unsigned)par_size);
error = ESP_OK;
} else if (errQUEUE_FULL == status) {
ESP_LOGD(TAG, "Parameter queue is overflowed.");
}
return error;
}
// Helper function to send notification
static esp_err_t mbc_slave_send_param_access_notification(mb_event_group_t event)
{
MB_SLAVE_ASSERT(slave_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
esp_err_t err = ESP_FAIL;
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group, (EventBits_t)event);
if (bits & event) {
ESP_LOGD(TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (int)event);
err = ESP_OK;
}
return err;
}
/*
* Below are the common slave read/write register callback functions
* The concrete slave port can override them using interface function pointers
*/
// Callback function for reading of MB Input Registers
eMBErrorCode mbc_reg_input_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
address--; // address of register is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_INPUT, address, n_regs);
if (it != NULL) {
uint16_t input_reg_start = (uint16_t)it->start_offset; // Get Modbus start address
uint8_t* input_buffer = (uint8_t*)it->p_data; // Get instance address
uint16_t regs = n_regs;
uint16_t reg_index;
// If input or configuration parameters are incorrect then return an error to stack layer
reg_index = (uint16_t)(address - input_reg_start);
reg_index <<= 1; // register Address to byte address
input_buffer += reg_index;
uint8_t* buffer_start = input_buffer;
while (regs > 0) {
_XFER_2_RD(reg_buffer, input_buffer);
reg_index += 2;
regs -= 1;
}
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_INPUT_REG_RD);
// Send parameter info to application task
(void)mbc_slave_send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
} else {
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Holding Registers
// Executed by stack when request to read/write holding registers is received
eMBErrorCode mbc_reg_holding_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs, eMBRegisterMode mode)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
address--; // address of register is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_HOLDING, address, n_regs);
if (it != NULL) {
uint16_t reg_holding_start = (uint16_t)it->start_offset; // Get Modbus start address
uint8_t* holding_buffer = (uint8_t*)it->p_data; // Get instance address
uint16_t regs = n_regs;
reg_index = (uint16_t) (address - reg_holding_start);
reg_index <<= 1; // register Address to byte address
holding_buffer += reg_index;
uint8_t* buffer_start = holding_buffer;
switch (mode) {
case MB_REG_READ:
while (regs > 0) {
_XFER_2_RD(reg_buffer, holding_buffer);
reg_index += 2;
regs -= 1;
};
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
// Send parameter info
(void)mbc_slave_send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
break;
case MB_REG_WRITE:
while (regs > 0) {
_XFER_2_WR(holding_buffer, reg_buffer);
holding_buffer += 2;
reg_index += 2;
regs -= 1;
};
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
// Send parameter info
(void)mbc_slave_send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
break;
}
} else {
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Coils Registers
eMBErrorCode mbc_reg_coils_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_coils, eMBRegisterMode mode)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
uint16_t coils = n_coils;
address--; // The address is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_COIL, address, n_coils);
if (it != NULL) {
uint16_t reg_coils_start = (uint16_t)it->start_offset; // MB offset of coils
uint8_t* reg_coils_buf = (uint8_t*)it->p_data;
reg_index = (uint16_t) (address - it->start_offset);
CHAR* coils_data_buf = (CHAR*)(reg_coils_buf + (reg_index >> 3));
switch (mode) {
case MB_REG_READ:
while (coils > 0) {
uint8_t result = xMBUtilGetBits((uint8_t*)reg_coils_buf, reg_index, 1);
xMBUtilSetBits(reg_buffer, reg_index - (address - reg_coils_start), 1, result);
reg_index++;
coils--;
}
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_COILS_RD);
(void)mbc_slave_send_param_info(MB_EVENT_COILS_RD, (uint16_t)address,
(uint8_t*)(coils_data_buf), (uint16_t)n_coils);
break;
case MB_REG_WRITE:
while (coils > 0) {
uint8_t result = xMBUtilGetBits(reg_buffer,
reg_index - (address - reg_coils_start), 1);
xMBUtilSetBits((uint8_t*)reg_coils_buf, reg_index, 1, result);
reg_index++;
coils--;
}
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_COILS_WR);
(void)mbc_slave_send_param_info(MB_EVENT_COILS_WR, (uint16_t)address,
(uint8_t*)coils_data_buf, (uint16_t)n_coils);
break;
} // switch ( eMode )
} else {
// If the configuration or input parameters are incorrect then return error to stack
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Discrete Input Registers
eMBErrorCode mbc_reg_discrete_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_discrete)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
uint16_t reg_bit_index;
uint16_t n_reg;
uint8_t* discrete_input_buf;
// It already plus one in modbus function method.
address--;
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_DISCRETE, address, n_discrete);
if (it != NULL) {
uint16_t reg_discrete_start = (uint16_t)it->start_offset; // MB offset of registers
n_reg = (n_discrete >> 3) + 1;
discrete_input_buf = (uint8_t*)it->p_data; // the storage address
reg_index = (uint16_t) (address - reg_discrete_start) / 8; // Get register index in the buffer for bit number
reg_bit_index = (uint16_t)(address - reg_discrete_start) % 8; // Get bit index
uint8_t* temp_buf = &discrete_input_buf[reg_index];
while (n_reg > 0) {
*reg_buffer++ = xMBUtilGetBits(&discrete_input_buf[reg_index++], reg_bit_index, 8);
n_reg--;
}
reg_buffer--;
// Last discrete
n_discrete = n_discrete % 8;
// Filling zero to high bit
*reg_buffer = *reg_buffer << (8 - n_discrete);
*reg_buffer = *reg_buffer >> (8 - n_discrete);
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_DISCRETE_RD);
(void)mbc_slave_send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)address,
(uint8_t*)temp_buf, (uint16_t)n_discrete);
} else {
status = MB_ENOREG;
}
return status;
}
/**
* Below are the stack callback functions to read/write registers
*/
eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
// Check if the callback is overridden in concrete port
if (slave_interface_ptr->slave_reg_cb_discrete) {
error = slave_interface_ptr->slave_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
} else {
error = mbc_reg_discrete_slave_cb(pucRegBuffer, usAddress, usNDiscrete);
}
return error;
}
eMBErrorCode eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->slave_reg_cb_coils) {
error = slave_interface_ptr->slave_reg_cb_coils(pucRegBuffer, usAddress, usNCoils, eMode);
} else {
error = mbc_reg_coils_slave_cb(pucRegBuffer, usAddress, usNCoils, eMode);
}
return error;
}
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->slave_reg_cb_holding) {
error = slave_interface_ptr->slave_reg_cb_holding(pucRegBuffer, usAddress, usNRegs, eMode);
} else {
error = mbc_reg_holding_slave_cb(pucRegBuffer, usAddress, usNRegs, eMode);
}
return error;
}
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
eMBErrorCode error = ESP_ERR_INVALID_STATE;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
if (slave_interface_ptr->slave_reg_cb_input) {
error = slave_interface_ptr->slave_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
} else {
error = mbc_reg_input_slave_cb(pucRegBuffer, usAddress, usNRegs);
}
return error;
}

View File

@@ -1,34 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "sdkconfig.h" // for KConfig defines
#include "mbc_slave.h" // for slave interface define
#include "esp_modbus_slave.h" // for public slave defines
#include "mbc_serial_slave.h" // for public interface defines
/**
* Initialization of Modbus Serial slave controller
*/
esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler)
{
void* port_handler = NULL;
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
switch(port_type)
{
case MB_PORT_SERIAL_SLAVE:
// Call constructor function of actual port implementation
error = mbc_serial_slave_create(&port_handler);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
}
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_slave_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_slave.h" // for public slave defines
#include "mbc_tcp_slave.h" // for public interface defines
/**
* Initialization of Modbus TCP Slave controller
*/
esp_err_t mbc_slave_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_slave_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_slave_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@@ -1,157 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_IFACE_COMMON_H
#define _MB_IFACE_COMMON_H
#include <inttypes.h> // needs to be included for default system types (such as PRIxx)
#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
#if __has_include("esp_check.h")
#include "esp_check.h"
#include "esp_log.h"
#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) ESP_RETURN_ON_FALSE(a, err_code, tag, format __VA_OPT__(,) __VA_ARGS__)
#else
// if cannot include esp_check then use custom check macro
#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) do { \
if (!(a)) { \
ESP_LOGE(tag, "%s(%" PRIu32 "): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \
return err_code; \
} \
} while(0)
#endif
#define MB_CONTROLLER_STACK_SIZE (CONFIG_FMB_CONTROLLER_STACK_SIZE) // Stack size for Modbus controller
#define MB_CONTROLLER_PRIORITY (CONFIG_FMB_PORT_TASK_PRIO - 1) // priority of MB controller task
// Default port defines
#define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus
#define MB_DEVICE_SPEED (115200) // Default Modbus speed for now hard defined
#define MB_UART_PORT (UART_NUM_MAX - 1) // Default UART port number
#define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info
#define MB_PARITY_NONE (UART_PARITY_DISABLE)
// The Macros below handle the endianness while transfer N byte data into buffer (convert from network byte order)
#define _XFER_2_RD(dst, src) { \
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 1); \
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 0); \
(src) += 2; \
}
#define _XFER_2_WR(dst, src) { \
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
}
/**
* @brief Types of actual Modbus implementation
*/
typedef enum
{
MB_PORT_SERIAL_MASTER = 0x00, /*!< Modbus port type serial master. */
MB_PORT_SERIAL_SLAVE, /*!< Modbus port type serial slave. */
MB_PORT_TCP_MASTER, /*!< Modbus port type TCP master. */
MB_PORT_TCP_SLAVE, /*!< Modbus port type TCP slave. */
MB_PORT_COUNT, /*!< Modbus port count. */
MB_PORT_INACTIVE = 0xFF
} mb_port_type_t;
/**
* @brief Event group for parameters notification
*/
typedef enum
{
MB_EVENT_NO_EVENTS = 0x00,
MB_EVENT_HOLDING_REG_WR = BIT0, /*!< Modbus Event Write Holding registers. */
MB_EVENT_HOLDING_REG_RD = BIT1, /*!< Modbus Event Read Holding registers. */
MB_EVENT_INPUT_REG_RD = BIT3, /*!< Modbus Event Read Input registers. */
MB_EVENT_COILS_WR = BIT4, /*!< Modbus Event Write Coils. */
MB_EVENT_COILS_RD = BIT5, /*!< Modbus Event Read Coils. */
MB_EVENT_DISCRETE_RD = BIT6, /*!< Modbus Event Read Discrete bits. */
MB_EVENT_STACK_STARTED = BIT7 /*!< Modbus Event Stack started */
} mb_event_group_t;
/**
* @brief Type of Modbus parameter
*/
typedef enum {
MB_PARAM_HOLDING = 0x00, /*!< Modbus Holding register. */
MB_PARAM_INPUT, /*!< Modbus Input register. */
MB_PARAM_COIL, /*!< Modbus Coils. */
MB_PARAM_DISCRETE, /*!< Modbus Discrete bits. */
MB_PARAM_COUNT,
MB_PARAM_UNKNOWN = 0xFF
} mb_param_type_t;
/*!
* \brief Modbus serial transmission modes (RTU/ASCII).
*/
typedef enum {
MB_MODE_RTU, /*!< RTU transmission mode. */
MB_MODE_ASCII, /*!< ASCII transmission mode. */
MB_MODE_TCP, /*!< TCP communication mode. */
MB_MODE_UDP /*!< UDP communication mode. */
} mb_mode_type_t;
/*!
* \brief Modbus TCP type of address.
*/
typedef enum {
MB_IPV4 = 0, /*!< TCP IPV4 addressing */
MB_IPV6 = 1 /*!< TCP IPV6 addressing */
} mb_tcp_addr_type_t;
/**
* @brief Device communication structure to setup Modbus controller
*/
typedef union {
// Serial communication structure
struct {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint8_t slave_addr; /*!< Modbus slave address field (dummy for master) */
uart_port_t port; /*!< Modbus communication port (UART) number */
uint32_t baudrate; /*!< Modbus baudrate */
uart_parity_t parity; /*!< Modbus UART parity settings */
uint16_t dummy_port; /*!< Dummy field, unused */
};
// TCP/UDP communication structure
struct {
mb_mode_type_t ip_mode; /*!< Modbus communication mode */
uint8_t slave_uid; /*!< Modbus slave address field for UID */
uint16_t ip_port; /*!< Modbus port */
mb_tcp_addr_type_t ip_addr_type; /*!< Modbus address type */
void* ip_addr; /*!< Modbus address table for connection */
void* ip_netif_ptr; /*!< Modbus network interface */
};
} mb_communication_info_t;
/**
* common interface method types
*/
typedef esp_err_t (*iface_init)(void**); /*!< Interface method init */
typedef esp_err_t (*iface_destroy)(void); /*!< Interface method destroy */
typedef esp_err_t (*iface_setup)(void*); /*!< Interface method setup */
typedef esp_err_t (*iface_start)(void); /*!< Interface method start */
#ifdef __cplusplus
}
#endif
#endif // _MB_IFACE_COMMON_H

View File

@@ -1,351 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_MB_MASTER_INTERFACE_H
#define _ESP_MB_MASTER_INTERFACE_H
#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_modbus_common.h" // for common types
#ifdef __cplusplus
extern "C" {
#endif
#define MB_MASTER_CHECK(a, err_code, format, ...) MB_RETURN_ON_FALSE(a, err_code, TAG, format __VA_OPT__(,) __VA_ARGS__)
#define MB_MASTER_ASSERT(con) do { \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%u, errno_str: !(%s)", (unsigned)errno, strerror(errno)); assert(0 && #con); } \
} while (0)
/*!
* \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.
*/
typedef enum {
PARAM_TYPE_U8 = 0x00, /*!< Unsigned 8 */
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_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;
/*!
* \brief Modbus descriptor table parameter size in bytes.
*/
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 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;
/*!
* \brief Modbus parameter options for description table
*/
typedef union {
struct {
int opt1; /*!< Parameter option1 */
int opt2; /*!< Parameter option2 */
int opt3; /*!< Parameter option3 */
};
struct {
int min; /*!< Parameter minimum value */
int max; /*!< Parameter maximum value */
int step; /*!< Step of parameter change tracking */
};
} mb_parameter_opt_t;
/**
* @brief Permissions for the characteristics
*/
typedef enum {
PAR_PERMS_READ = 1 << BIT0, /**< the characteristic of the device are readable */
PAR_PERMS_WRITE = 1 << BIT1, /**< the characteristic of the device are writable*/
PAR_PERMS_TRIGGER = 1 << BIT2, /**< the characteristic of the device are triggerable */
PAR_PERMS_READ_WRITE = PAR_PERMS_READ | PAR_PERMS_WRITE, /**< the characteristic of the device are readable & writable */
PAR_PERMS_READ_TRIGGER = PAR_PERMS_READ | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & triggerable */
PAR_PERMS_WRITE_TRIGGER = PAR_PERMS_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are writable & triggerable */
PAR_PERMS_READ_WRITE_TRIGGER = PAR_PERMS_READ_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & writable & triggerable */
} mb_param_perms_t;
/**
* @brief Characteristics descriptor type is used to describe characteristic and
* link it with Modbus parameters that reflect its data.
*/
typedef struct {
uint16_t cid; /*!< Characteristic cid */
const char* param_key; /*!< The key (name) of the parameter */
const char* param_units; /*!< The physical units of the parameter */
uint8_t mb_slave_addr; /*!< Slave address of device in the Modbus segment */
mb_param_type_t mb_param_type; /*!< Type of modbus parameter */
uint16_t mb_reg_start; /*!< This is the Modbus register address. This is the 0 based value. */
uint16_t mb_size; /*!< Size of mb parameter in registers */
uint16_t param_offset; /*!< Parameter name (OFFSET in the parameter structure) */
mb_descr_type_t param_type; /*!< Float, U8, U16, U32, ASCII, etc. */
mb_descr_size_t param_size; /*!< Number of bytes in the parameter. */
mb_parameter_opt_t param_opts; /*!< Parameter options used to check limits and etc. */
mb_param_perms_t access; /*!< Access permissions based on mode */
} mb_parameter_descriptor_t;
/**
* @brief Modbus register request type structure
*/
typedef struct {
uint8_t slave_addr; /*!< Modbus slave address */
uint8_t command; /*!< Modbus command to send */
uint16_t reg_start; /*!< Modbus start register */
uint16_t reg_size; /*!< Modbus number of registers */
} mb_param_request_t;
/**
* @brief Modbus transacion info structure
*/
typedef struct {
uint64_t trans_id; /*!< Modbus unique transaction identificator */
uint16_t err_type; /*!< Modbus last transaction error type */
uint8_t dest_addr; /*!< Modbus destination short address (or UID) */
uint8_t func_code; /*!< Modbus last transaction function code */
uint8_t exception; /*!< Modbus last transaction exception code returned by slave */
} mb_trans_info_t;
/**
* @brief Initialize Modbus controller and stack for TCP port
*
* @param[out] handler handler(pointer) to master data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_master_init_tcp(void** handler);
/**
* @brief Initialize Modbus Master controller and stack for Serial port
*
* @param[out] handler handler(pointer) to master data structure
* @param[in] port_type type of stack
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler);
/**
* @brief Initialize Modbus Master controller interface handle
*
* @param[in] handler - pointer to master data structure
*/
void mbc_master_init_iface(void* handler);
/**
* @brief Destroy Modbus controller and stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Parameter error
*/
esp_err_t mbc_master_destroy(void);
/**
* @brief Start Modbus communication stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Modbus stack start error
*/
esp_err_t mbc_master_start(void);
/**
* @brief Set Modbus communication parameters for the controller
*
* @param comm_info Communication parameters structure.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Incorrect parameter data
*/
esp_err_t mbc_master_setup(void* comm_info);
/***************************** Specific interface functions ********************************************
* Interface functions below provide basic methods to read/write access to slave devices in Modbus
* segment as well as API to read specific supported characteristics linked to Modbus parameters
* of devices in Modbus network.
*******************************************************************************************************/
/**
* @brief Assign parameter description table for Modbus controller interface.
*
* @param[in] descriptor pointer to parameter description table
* @param num_elements number of elements in the table
*
* @return
* - esp_err_t ESP_OK - set descriptor successfully
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument in function call
*/
esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements);
/**
* @brief Send data request as defined in parameter request, waits response
* from slave and returns status of command execution. This function provides standard way
* for read/write access to Modbus devices in the network.
*
* @param[in] request pointer to request structure of type mb_param_request_t
* @param[in] data_ptr pointer to data buffer to send or received data (dependent of command field in request)
*
* @return
* - esp_err_t ESP_OK - request was successful
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave
* - esp_err_t ESP_ERR_TIMEOUT - operation timeout or no response from slave
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
*/
esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr);
/**
* @brief Get information about supported characteristic defined as cid. Uses parameter description table to get
* this information. The function will check if characteristic defined as a cid parameter is supported
* and returns its description in param_info. Returns ESP_ERR_NOT_FOUND if characteristic is not supported.
*
* @param[in] cid characteristic id
* @param param_info pointer to pointer of characteristic data.
*
* @return
* - esp_err_t ESP_OK - request was successful and buffer contains the supported characteristic name
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
* - esp_err_t ESP_ERR_NOT_FOUND - the characteristic (cid) not found
* - esp_err_t ESP_FAIL - unknown error during lookup table processing
*/
esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_info);
/**
* @brief Read parameter from modbus slave device whose name is defined by name and has cid.
* The additional data for request is taken from parameter description (lookup) table.
*
* @param[in] cid id of the characteristic for parameter
* @param[in] name pointer into string name (key) of parameter (null terminated)
* @param[out] value pointer to data buffer of parameter
* @param[out] type parameter type associated with the name returned from parameter description table.
*
* @return
* - esp_err_t ESP_OK - request was successful and value buffer contains
* representation of actual parameter data from slave
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
* - esp_err_t ESP_ERR_TIMEOUT - operation timed out and no response from slave
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
* - esp_err_t ESP_ERR_NOT_FOUND - the parameter is not found in the parameter description table
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
*/
esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type);
/**
* @brief Set characteristic's value defined as a name and cid parameter.
* The additional data for cid parameter request is taken from master parameter lookup table.
*
* @param[in] cid id of the characteristic for parameter
* @param[in] name pointer into string name (key) of parameter (null terminated)
* @param[out] value pointer to data buffer of parameter (actual representation of json value field in binary form)
* @param[out] type pointer to parameter type associated with the name returned from parameter lookup table.
*
* @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_INVALID_RESPONSE - an invalid response from slave during processing of parameter
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
* - esp_err_t ESP_ERR_TIMEOUT - operation timed out and no response from slave
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
*/
esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type);
/**
* @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);
/**
* @brief The helper function to expose transaction info from modbus layer
*
* @param[in] ptinfo the pointer to transaction info structure
*
* @return
* - esp_err_t ESP_OK - the transaction info is saved in the appropriate parameter structure
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
*/
esp_err_t mbc_master_get_transaction_info(mb_trans_info_t *ptinfo);
#ifdef __cplusplus
}
#endif
#endif // _ESP_MB_MASTER_INTERFACE_H

View File

@@ -1,148 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_MB_SLAVE_INTERFACE_H
#define _ESP_MB_SLAVE_INTERFACE_H
// Public interface header for slave
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "soc/soc.h" // for BITN definitions
#include "freertos/FreeRTOS.h" // for task creation and queues access
#include "freertos/event_groups.h" // for event groups
#include "esp_modbus_common.h" // for common types
#ifdef __cplusplus
extern "C" {
#endif
#define MB_SLAVE_CHECK(a, err_code, format, ...) MB_RETURN_ON_FALSE(a, err_code, TAG, format __VA_OPT__(,) __VA_ARGS__)
#define MB_SLAVE_ASSERT(con) do { \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%u, errno_str: !(%s)", (unsigned)errno, strerror(errno)); assert(0 && #con); } \
} while (0)
/**
* @brief Parameter access event information type
*/
typedef struct {
uint32_t time_stamp; /*!< Timestamp of Modbus Event (uS)*/
uint16_t mb_offset; /*!< Modbus register offset */
mb_event_group_t type; /*!< Modbus event type */
uint8_t* address; /*!< Modbus data storage address */
size_t size; /*!< Modbus event register size (number of registers)*/
} mb_param_info_t;
/**
* @brief Parameter storage area descriptor
*/
typedef struct {
uint16_t start_offset; /*!< Modbus start address for area descriptor */
mb_param_type_t type; /*!< Type of storage area descriptor */
void* address; /*!< Instance address for storage area descriptor */
size_t size; /*!< Instance size for area descriptor (bytes) */
} mb_register_area_descriptor_t;
/**
* @brief Initialize Modbus Slave controller and stack for TCP port
*
* @param[out] handler handler(pointer) to master data structure
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_slave_init_tcp(void** handler);
/**
* @brief Initialize Modbus Slave controller and stack for Serial port
*
* @param[out] handler handler(pointer) to master data structure
* @param[in] port_type the type of port
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM Parameter error
* - ESP_ERR_NOT_SUPPORTED Port type not supported
* - ESP_ERR_INVALID_STATE Initialization failure
*/
esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler);
/**
* @brief Initialize Modbus Slave controller interface handle
*
* @param[in] handler - pointer to slave interface data structure
*/
void mbc_slave_init_iface(void* handler);
/**
* @brief Destroy Modbus controller and stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Parameter error
*/
esp_err_t mbc_slave_destroy(void);
/**
* @brief Start Modbus communication stack
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Modbus stack start error
*/
esp_err_t mbc_slave_start(void);
/**
* @brief Set Modbus communication parameters for the controller
*
* @param comm_info Communication parameters structure.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Incorrect parameter data
*/
esp_err_t mbc_slave_setup(void* comm_info);
/**
* @brief Wait for specific event on parameter change.
*
* @param group Group event bit mask to wait for change
*
* @return
* - mb_event_group_t event bits triggered
*/
mb_event_group_t mbc_slave_check_event(mb_event_group_t group);
/**
* @brief Get parameter information
*
* @param[out] reg_info parameter info structure
* @param timeout Timeout in milliseconds to read information from
* parameter queue
* @return
* - ESP_OK Success
* - ESP_ERR_TIMEOUT Can not get data from parameter queue
* or queue overflow
*/
esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout);
/**
* @brief Set Modbus area descriptor
*
* @param descr_data Modbus registers area descriptor structure
*
* @return
* - ESP_OK: The appropriate descriptor is set
* - ESP_ERR_INVALID_ARG: The argument is incorrect
*/
esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,556 +0,0 @@
/*
* 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

@@ -1,23 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbcontroller.h
// mbcontroller - common Modbus controller header file
#ifndef _MODBUS_CONTROLLER_COMMON
#define _MODBUS_CONTROLLER_COMMON
#include <stdint.h> // for standard int types definition
#include <stddef.h> // for NULL and std defines
#include "string.h" // for strerror()
#include "errno.h" // for errno
#include "esp_err.h" // for error handling
#include "driver/uart.h" // for uart port number defines
#include "sdkconfig.h" // for KConfig options
#include "esp_modbus_master.h"
#include "esp_modbus_slave.h"
#endif

View File

@@ -1,683 +0,0 @@
/*
* 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] = (uint8_t)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] = (int8_t)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] = (uint8_t)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] = (uint8_t)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

@@ -1,105 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_CONTROLLER_MASTER_H
#define _MB_CONTROLLER_MASTER_H
#include <sys/queue.h> // for list
#include "freertos/FreeRTOS.h" // for task creation and queue access
#include "freertos/task.h" // for task api access
#include "freertos/event_groups.h" // for event groups
#include "driver/uart.h" // for UART types
#include "errno.h" // for errno
#include "esp_log.h" // for log write
#include "string.h" // for strerror()
#include "esp_modbus_common.h" // for common types
#include "esp_modbus_master.h" // for public master types
#include "esp_modbus_callbacks.h"
#include "mb_m.h" // this is required to expose current transaction info
/* ----------------------- Defines ------------------------------------------*/
/**
* @brief Request mode for parameter to use in data dictionary
*/
typedef enum {
MB_PARAM_READ, /*!< Read parameter values. */
MB_PARAM_WRITE /*!< Write parameter values. */
} mb_param_mode_t;
/**
* @brief Device communication parameters for master
*/
typedef struct {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint8_t dummy; /*!< Dummy field */
uart_port_t port; /*!< Modbus communication port (UART) number */
uint32_t baudrate; /*!< Modbus baudrate */
uart_parity_t parity; /*!< Modbus UART parity settings */
} mb_master_comm_info_t;
#if MB_MASTER_TCP_ENABLED
/**
* @brief Modbus slave addr list item for the master
*/
typedef struct mb_slave_addr_entry_s{
uint16_t index; /*!< Index of the slave address */
const char* ip_address; /*!< IP address string of the slave */
uint8_t slave_addr; /*!< Short slave address */
void* p_data; /*!< pointer to data structure */
LIST_ENTRY(mb_slave_addr_entry_s) entries; /*!< The slave address entry */
} mb_slave_addr_entry_t;
#endif
/**
* @brief Modbus controller handler structure
*/
typedef struct {
mb_port_type_t port_type; /*!< Modbus port type */
mb_communication_info_t mbm_comm; /*!< Modbus communication info */
uint8_t* mbm_reg_buffer_ptr; /*!< Modbus data buffer pointer */
uint16_t mbm_reg_buffer_size; /*!< Modbus data buffer size */
TaskHandle_t mbm_task_handle; /*!< Modbus task handle */
EventGroupHandle_t mbm_event_group; /*!< Modbus controller event group */
const mb_parameter_descriptor_t* mbm_param_descriptor_table; /*!< Modbus controller parameter description table */
size_t mbm_param_descriptor_size; /*!< Modbus controller parameter description table size*/
#if MB_MASTER_TCP_ENABLED
LIST_HEAD(mbm_slave_addr_info_, mb_slave_addr_entry_s) mbm_slave_list; /*!< Slave address information list */
uint16_t mbm_slave_list_count;
#endif
} mb_master_options_t;
typedef esp_err_t (*iface_get_cid_info)(uint16_t, const mb_parameter_descriptor_t**); /*!< Interface get_cid_info method */
typedef esp_err_t (*iface_get_parameter)(uint16_t, char*, uint8_t*, uint8_t*); /*!< Interface get_parameter method */
typedef esp_err_t (*iface_send_request)(mb_param_request_t*, void*); /*!< Interface send_request method */
typedef esp_err_t (*iface_set_descriptor)(const mb_parameter_descriptor_t*, const uint16_t); /*!< Interface set_descriptor method */
typedef esp_err_t (*iface_set_parameter)(uint16_t, char*, uint8_t*, uint8_t*); /*!< Interface set_parameter method */
/**
* @brief Modbus controller interface structure
*/
typedef struct {
// Master object interface options
mb_master_options_t opts;
// Public interface methods
iface_init init; /*!< Interface method init */
iface_destroy destroy; /*!< Interface method destroy */
iface_setup setup; /*!< Interface method setup */
iface_start start; /*!< Interface method start */
iface_get_cid_info get_cid_info; /*!< Interface get_cid_info method */
iface_get_parameter get_parameter; /*!< Interface get_parameter method */
iface_send_request send_request; /*!< Interface send_request method */
iface_set_descriptor set_descriptor; /*!< Interface set_descriptor method */
iface_set_parameter set_parameter; /*!< Interface set_parameter method */
// Modbus register calback function pointers
reg_discrete_cb master_reg_cb_discrete; /*!< Stack callback discrete rw method */
reg_input_cb master_reg_cb_input; /*!< Stack callback input rw method */
reg_holding_cb master_reg_cb_holding; /*!< Stack callback holding rw method */
reg_coils_cb master_reg_cb_coils; /*!< Stack callback coils rw method */
} mb_master_interface_t;
#endif //_MB_CONTROLLER_MASTER_H

View File

@@ -1,87 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_CONTROLLER_SLAVE_H
#define _MB_CONTROLLER_SLAVE_H
#include "driver/uart.h" // for uart defines
#include "errno.h" // for errno
#include "sys/queue.h" // for list
#include "esp_log.h" // for log write
#include "string.h" // for strerror()
#include "esp_modbus_slave.h" // for public type defines
#include "esp_modbus_callbacks.h" // for callback functions
/* ----------------------- Defines ------------------------------------------*/
#define MB_INST_MIN_SIZE (2) // The minimal size of Modbus registers area in bytes
#define MB_INST_MAX_SIZE (65535 * 2) // The maximum size of Modbus area in bytes
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
/**
* @brief Device communication parameters for master
*/
typedef struct {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint8_t slave_addr; /*!< Slave address field */
uart_port_t port; /*!< Modbus communication port (UART) number */
uint32_t baudrate; /*!< Modbus baudrate */
uart_parity_t parity; /*!< Modbus UART parity settings */
} mb_slave_comm_info_t;
/**
* @brief Modbus area descriptor list item
*/
typedef struct mb_descr_entry_s{
uint16_t start_offset; /*!< Modbus start address for area descriptor */
mb_param_type_t type; /*!< Type of storage area descriptor */
void* p_data; /*!< Instance address for storage area descriptor */
size_t size; /*!< Instance size for area descriptor (bytes) */
LIST_ENTRY(mb_descr_entry_s) entries; /*!< The Modbus area descriptor entry */
} mb_descr_entry_t;
/**
* @brief Modbus controller handler structure
*/
typedef struct {
mb_port_type_t port_type; /*!< port type */
mb_communication_info_t mbs_comm; /*!< communication info */
TaskHandle_t mbs_task_handle; /*!< task handle */
EventGroupHandle_t mbs_event_group; /*!< controller event group */
QueueHandle_t mbs_notification_queue_handle; /*!< controller notification queue */
LIST_HEAD(mbs_area_descriptors_, mb_descr_entry_s) mbs_area_descriptors[MB_PARAM_COUNT]; /*!< register area descriptors */
} mb_slave_options_t;
typedef mb_event_group_t (*iface_check_event)(mb_event_group_t); /*!< Interface method check_event */
typedef esp_err_t (*iface_get_param_info)(mb_param_info_t*, uint32_t); /*!< Interface method get_param_info */
typedef esp_err_t (*iface_set_descriptor)(mb_register_area_descriptor_t); /*!< Interface method set_descriptor */
/**
* @brief Request mode for parameter to use in data dictionary
*/
typedef struct
{
mb_slave_options_t opts; /*!< Modbus slave options */
// Functional pointers to internal static functions of the implementation (public interface methods)
iface_init init; /*!< Interface method init */
iface_destroy destroy; /*!< Interface method destroy */
iface_setup setup; /*!< Interface method setup */
iface_start start; /*!< Interface method start */
iface_check_event check_event; /*!< Interface method check_event */
iface_get_param_info get_param_info; /*!< Interface method get_param_info */
iface_set_descriptor set_descriptor; /*!< Interface method set_descriptor */
// Modbus register calback function pointers
reg_discrete_cb slave_reg_cb_discrete; /*!< Stack callback discrete rw method */
reg_input_cb slave_reg_cb_input; /*!< Stack callback input rw method */
reg_holding_cb slave_reg_cb_holding; /*!< Stack callback holding rw method */
reg_coils_cb slave_reg_cb_coils; /*!< Stack callback coils rw method */
} mb_slave_interface_t;
#endif

View File

@@ -1,495 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbascii.c,v 1.17 2010/06/06 13:47:07 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbconfig.h"
#include "mbascii.h"
#include "mbframe.h"
#include "mbcrc.h"
#include "mbport.h"
#if MB_SLAVE_ASCII_ENABLED > 0
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
STATE_RX_IDLE, /*!< Receiver is in idle state. */
STATE_RX_RCV, /*!< Frame is beeing received. */
STATE_RX_WAIT_EOF /*!< Wait for End of Frame. */
} eMBRcvState;
typedef enum
{
STATE_TX_IDLE, /*!< Transmitter is in idle state. */
STATE_TX_START, /*!< Starting transmission (':' sent). */
STATE_TX_DATA, /*!< Sending of data (Address, Data, LRC). */
STATE_TX_END, /*!< End of transmission. */
STATE_TX_NOTIFY /*!< Notify sender that the frame has been sent. */
} eMBSndState;
typedef enum
{
BYTE_HIGH_NIBBLE, /*!< Character for high nibble of byte. */
BYTE_LOW_NIBBLE /*!< Character for low nibble of byte. */
} eMBBytePos;
/* ----------------------- Shared variables ---------------------------------*/
/* We reuse the Modbus RTU buffer because only one driver is active */
extern volatile UCHAR ucMbSlaveBuf[];
/* ----------------------- Static functions ---------------------------------*/
static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter );
static UCHAR prvucMBBIN2CHAR( UCHAR ucByte );
static UCHAR prvucMBLRC( UCHAR * pucFrame, USHORT usLen );
/* ----------------------- Static variables ---------------------------------*/
static volatile eMBSndState eSndState;
static volatile eMBRcvState eRcvState;
static volatile UCHAR *ucASCIIBuf = ucMbSlaveBuf;
static volatile USHORT usRcvBufferPos;
static volatile eMBBytePos eBytePos;
static volatile UCHAR *pucSndBufferCur;
static volatile USHORT usSndBufferCount;
static volatile UCHAR ucLRC;
static volatile UCHAR ucMBLFCharacter;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBASCIIInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
( void )ucSlaveAddress;
ENTER_CRITICAL_SECTION( );
ucMBLFCharacter = MB_ASCII_DEFAULT_LF;
if( xMBPortSerialInit( ucPort, ulBaudRate, MB_ASCII_BITS_PER_SYMB, eParity ) != TRUE )
{
eStatus = MB_EPORTERR;
}
else if( xMBPortTimersInit( MB_ASCII_TIMEOUT_MS * 20UL ) != TRUE )
{
eStatus = MB_EPORTERR;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
void
eMBASCIIStart( void )
{
ENTER_CRITICAL_SECTION( );
vMBPortSerialEnable( TRUE, FALSE );
eRcvState = STATE_RX_IDLE;
EXIT_CRITICAL_SECTION( );
/* No special startup required for ASCII. */
( void )xMBPortEventPost( EV_READY );
}
void
eMBASCIIStop( void )
{
ENTER_CRITICAL_SECTION( );
vMBPortSerialEnable( FALSE, FALSE );
vMBPortTimersDisable( );
EXIT_CRITICAL_SECTION( );
}
eMBErrorCode
eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucASCIIBuf;
USHORT usFrameLength = usRcvBufferPos;
if( xMBPortSerialGetRequest( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
ENTER_CRITICAL_SECTION( );
assert( usFrameLength < MB_SER_PDU_SIZE_MAX );
/* Length and CRC check */
if( ( usFrameLength >= MB_ASCII_SER_PDU_SIZE_MIN )
&& ( prvucMBLRC( ( UCHAR * ) pucMBASCIIFrame, usFrameLength ) == 0 ) )
{
/* Save the address field. All frames are passed to the upper layed
* and the decision if a frame is used is done there.
*/
*pucRcvAddress = pucMBASCIIFrame[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
*pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & pucMBASCIIFrame[MB_SER_PDU_PDU_OFF];
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
eMBErrorCode
eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR usLRC;
/* Check if the receiver is still in idle state. If not we where too
* slow with processing the received frame and the master sent another
* frame on the network. We have to abort sending the frame.
*/
if( eRcvState == STATE_RX_IDLE )
{
ENTER_CRITICAL_SECTION( );
/* First byte before the Modbus-PDU is the slave address. */
pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
usSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usSndBufferCount += usLength;
/* Calculate LRC checksum for Modbus-Serial-Line-PDU. */
usLRC = prvucMBLRC( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
ucASCIIBuf[usSndBufferCount++] = usLRC;
/* Activate the transmitter. */
eSndState = STATE_TX_START;
EXIT_CRITICAL_SECTION( );
if ( xMBPortSerialSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
BOOL
xMBASCIIReceiveFSM( void )
{
BOOL xNeedPoll = FALSE;
UCHAR ucByte;
UCHAR ucResult;
assert( eSndState == STATE_TX_IDLE );
xNeedPoll = xMBPortSerialGetByte( ( CHAR * ) & ucByte );
switch ( eRcvState )
{
/* A new character is received. If the character is a ':' the input
* buffer is cleared. A CR-character signals the end of the data
* block. Other characters are part of the data block and their
* ASCII value is converted back to a binary representation.
*/
case STATE_RX_RCV:
/* Enable timer for character timeout. */
vMBPortTimersEnable( );
if( ucByte == ':' )
{
/* Empty receive buffer. */
eBytePos = BYTE_HIGH_NIBBLE;
usRcvBufferPos = 0;
}
else if( ucByte == MB_ASCII_DEFAULT_CR )
{
eRcvState = STATE_RX_WAIT_EOF;
}
else
{
ucResult = prvucMBCHAR2BIN( ucByte );
switch ( eBytePos )
{
/* High nibble of the byte comes first. We check for
* a buffer overflow here. */
case BYTE_HIGH_NIBBLE:
if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
ucASCIIBuf[usRcvBufferPos] = ( UCHAR )( ucResult << 4 );
eBytePos = BYTE_LOW_NIBBLE;
break;
}
else
{
/* not handled in Modbus specification but seems
* a resonable implementation. */
eRcvState = STATE_RX_IDLE;
/* Disable previously activated timer because of error state. */
vMBPortTimersDisable( );
}
break;
case BYTE_LOW_NIBBLE:
ucASCIIBuf[usRcvBufferPos] |= ucResult;
usRcvBufferPos++;
eBytePos = BYTE_HIGH_NIBBLE;
break;
}
}
break;
case STATE_RX_WAIT_EOF:
if( ucByte == ucMBLFCharacter )
{
/* Disable character timeout timer because all characters are
* received. */
vMBPortTimersDisable( );
/* Receiver is again in idle state. */
eRcvState = STATE_RX_IDLE;
/* Notify the caller of eMBASCIIReceive that a new frame
* was received. */
(void)xMBPortEventPost( EV_FRAME_RECEIVED );
}
else if( ucByte == ':' )
{
/* Empty receive buffer and back to receive state. */
eBytePos = BYTE_HIGH_NIBBLE;
usRcvBufferPos = 0;
eRcvState = STATE_RX_RCV;
/* Enable timer for character timeout. */
vMBPortTimersEnable( );
}
else
{
/* Frame is not okay. Delete entire frame. */
eRcvState = STATE_RX_IDLE;
}
break;
case STATE_RX_IDLE:
if( ucByte == ':' )
{
/* Enable timer for character timeout. */
vMBPortTimersEnable( );
/* Reset the input buffers to store the frame. */
usRcvBufferPos = 0;
eBytePos = BYTE_HIGH_NIBBLE;
eRcvState = STATE_RX_RCV;
}
break;
}
return xNeedPoll;
}
BOOL
xMBASCIITransmitFSM( void )
{
BOOL xNeedPoll = TRUE;
UCHAR ucByte;
assert( eRcvState == STATE_RX_IDLE );
switch ( eSndState )
{
/* Start of transmission. The start of a frame is defined by sending
* the character ':'. */
case STATE_TX_START:
ucByte = ':';
xMBPortSerialPutByte( ( CHAR )ucByte );
eSndState = STATE_TX_DATA;
eBytePos = BYTE_HIGH_NIBBLE;
break;
/* Send the data block. Each data byte is encoded as a character hex
* stream with the high nibble sent first and the low nibble sent
* last. If all data bytes are exhausted we send a '\r' character
* to end the transmission. */
case STATE_TX_DATA:
if( usSndBufferCount > 0 )
{
switch ( eBytePos )
{
case BYTE_HIGH_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucSndBufferCur >> 4 ) );
xMBPortSerialPutByte( ( CHAR ) ucByte );
eBytePos = BYTE_LOW_NIBBLE;
break;
case BYTE_LOW_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucSndBufferCur & 0x0F ) );
xMBPortSerialPutByte( ( CHAR )ucByte );
pucSndBufferCur++;
eBytePos = BYTE_HIGH_NIBBLE;
usSndBufferCount--;
break;
}
}
else
{
xMBPortSerialPutByte( MB_ASCII_DEFAULT_CR );
eSndState = STATE_TX_END;
}
break;
/* Finish the frame by sending a LF character. */
case STATE_TX_END:
xMBPortSerialPutByte( ( CHAR )ucMBLFCharacter );
/* We need another state to make sure that the CR character has
* been sent. */
eSndState = STATE_TX_NOTIFY;
break;
/* Notify the task which called eMBASCIISend that the frame has
* been sent. */
case STATE_TX_NOTIFY:
eSndState = STATE_TX_IDLE;
xMBPortEventPost( EV_FRAME_TRANSMIT );
xNeedPoll = FALSE;
break;
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_TX_IDLE:
break;
}
return xNeedPoll;
}
BOOL MB_PORT_ISR_ATTR
xMBASCIITimerT1SExpired( void )
{
switch ( eRcvState )
{
/* If we have a timeout we go back to the idle state and wait for
* the next frame.
*/
case STATE_RX_RCV:
case STATE_RX_WAIT_EOF:
eRcvState = STATE_RX_IDLE;
break;
default:
assert( ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_WAIT_EOF )
|| (eRcvState == STATE_RX_IDLE ));
break;
}
vMBPortTimersDisable( );
/* no context switch required. */
return FALSE;
}
static UCHAR
prvucMBCHAR2BIN( UCHAR ucCharacter )
{
if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )
{
return ( UCHAR )( ucCharacter - '0' );
}
else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )
{
return ( UCHAR )( ucCharacter - 'A' + 0x0A );
}
else
{
return 0xFF;
}
}
static UCHAR
prvucMBBIN2CHAR( UCHAR ucByte )
{
if( ucByte <= 0x09 )
{
return ( UCHAR )( '0' + ucByte );
}
else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )
{
return ( UCHAR )( ucByte - 0x0A + 'A' );
}
else
{
/* Programming error. */
assert( 0 );
}
return '0';
}
static UCHAR
prvucMBLRC( UCHAR * pucFrame, USHORT usLen )
{
UCHAR ucLRC = 0; /* LRC char initialized */
while( usLen-- )
{
ucLRC += *pucFrame++; /* Add buffer byte without carry */
}
/* Return twos complement */
ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );
return ucLRC;
}
#endif

View File

@@ -1,85 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbascii.h,v 1.8 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_ASCII_H
#define _MB_ASCII_H
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
/* ----------------------- Defines ------------------------------------------*/
#define MB_ASCII_DEFAULT_CR '\r' /*!< Default CR character for Modbus ASCII. */
#define MB_ASCII_DEFAULT_LF '\n' /*!< Default LF character for Modbus ASCII. */
#define MB_ASCII_SER_PDU_SIZE_MIN 3 /*!< Minimum size of a Modbus ASCII frame. */
/* ----------------------- Function declaration -----------------------------*/
#if MB_SLAVE_ASCII_ENABLED > 0
eMBErrorCode eMBASCIIInit( UCHAR slaveAddress, UCHAR ucPort,
ULONG ulBaudRate, eMBParity eParity );
void eMBASCIIStart( void );
void eMBASCIIStop( void );
eMBErrorCode eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
USHORT * pusLength );
eMBErrorCode eMBASCIISend( UCHAR slaveAddress, const UCHAR * pucFrame,
USHORT usLength );
BOOL xMBASCIIReceiveFSM( void );
BOOL xMBASCIITransmitFSM( void );
BOOL xMBASCIITimerT1SExpired( void );
#endif
#if MB_MASTER_ASCII_ENABLED > 0
eMBErrorCode eMBMasterASCIIInit( UCHAR ucPort,
ULONG ulBaudRate, eMBParity eParity );
void eMBMasterASCIIStart( void );
void eMBMasterASCIIStop( void );
eMBErrorCode eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
USHORT * pusLength );
eMBErrorCode eMBMasterASCIISend( UCHAR slaveAddress, const UCHAR * pucFrame,
USHORT usLength );
BOOL xMBMasterASCIIReceiveFSM( void );
BOOL xMBMasterASCIITransmitFSM( void );
BOOL xMBMasterASCIITimerT1SExpired( void );
#endif
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

View File

@@ -1,587 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbascii.c,v 1.17 2010/06/06 13:47:07 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbconfig.h"
#include "mbascii.h"
#include "mbframe.h"
#include "mbcrc.h"
#include "mbport.h"
#if MB_MASTER_ASCII_ENABLED > 0
/* ----------------------- Defines ------------------------------------------*/
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
STATE_M_RX_INIT, /*!< Receiver is in initial state. */
STATE_M_RX_IDLE, /*!< Receiver is in idle state. */
STATE_M_RX_RCV, /*!< Frame is beeing received. */
STATE_M_RX_WAIT_EOF, /*!< Wait for End of Frame. */
STATE_M_RX_ERROR, /*!< If the frame is invalid. */
} eMBMasterAsciiRcvState;
typedef enum
{
STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */
STATE_M_TX_START, /*!< Starting transmission (':' sent). */
STATE_M_TX_DATA, /*!< Sending of data (Address, Data, LRC). */
STATE_M_TX_END, /*!< End of transmission. */
STATE_M_TX_NOTIFY, /*!< Notify sender that the frame has been sent. */
STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */
} eMBMasterAsciiSndState;
typedef enum
{
BYTE_HIGH_NIBBLE, /*!< Character for high nibble of byte. */
BYTE_LOW_NIBBLE /*!< Character for low nibble of byte. */
} eMBBytePos;
/* ----------------------- Shared values -----------------------------------*/
/* These Modbus values are shared in ASCII mode*/
extern volatile UCHAR ucMasterRcvBuf[];
extern volatile UCHAR ucMasterSndBuf[];
/* ----------------------- Static functions ---------------------------------*/
static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter );
static UCHAR prvucMBBIN2CHAR( UCHAR ucByte );
static UCHAR prvucMBLRC( UCHAR * pucFrame, USHORT usLen );
/* ----------------------- Static variables ---------------------------------*/
static volatile eMBMasterAsciiSndState eSndState;
static volatile eMBMasterAsciiRcvState eRcvState;
static volatile UCHAR *ucMasterASCIIRcvBuf = ucMasterRcvBuf;
static volatile UCHAR *ucMasterASCIISndBuf = ucMasterSndBuf;
static volatile USHORT usMasterRcvBufferPos;
static volatile eMBBytePos eBytePos;
static volatile UCHAR *pucMasterSndBufferCur;
static volatile USHORT usMasterSndBufferCount;
static volatile UCHAR ucLRC;
static volatile UCHAR ucMBLFCharacter;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBMasterASCIIInit( UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
ENTER_CRITICAL_SECTION( );
ucMBLFCharacter = MB_ASCII_DEFAULT_LF;
if( xMBMasterPortSerialInit( ucPort, ulBaudRate, MB_ASCII_BITS_PER_SYMB, eParity ) != TRUE )
{
eStatus = MB_EPORTERR;
}
else if( xMBMasterPortTimersInit( MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS ) != TRUE )
{
eStatus = MB_EPORTERR;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
void
eMBMasterASCIIStart( void )
{
ENTER_CRITICAL_SECTION( );
eRcvState = STATE_M_RX_IDLE;
vMBMasterPortSerialEnable( TRUE, FALSE );
xMBMasterPortEventPost(EV_MASTER_READY);
EXIT_CRITICAL_SECTION( );
}
void
eMBMasterASCIIStop( void )
{
ENTER_CRITICAL_SECTION( );
vMBMasterPortSerialEnable( FALSE, FALSE );
vMBMasterPortTimersDisable( );
EXIT_CRITICAL_SECTION( );
}
eMBErrorCode
eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucMasterASCIIRcvBuf;
USHORT usFrameLength = usMasterRcvBufferPos;
if( xMBMasterPortSerialGetResponse( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
ENTER_CRITICAL_SECTION( );
assert( usFrameLength < MB_SER_PDU_SIZE_MAX );
assert( pucMBASCIIFrame );
/* Length and CRC check */
if( ( usFrameLength >= MB_ASCII_SER_PDU_SIZE_MIN )
&& ( prvucMBLRC( ( UCHAR * ) pucMBASCIIFrame, usFrameLength ) == 0 ) )
{
/* Save the address field. All frames are passed to the upper layed
* and the decision if a frame is used is done there.
*/
*pucRcvAddress = pucMBASCIIFrame[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
*pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & pucMBASCIIFrame[MB_SER_PDU_PDU_OFF];
} else {
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
eMBErrorCode
eMBMasterASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR usLRC;
if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
/* Check if the receiver is still in idle state. If not we where too
* slow with processing the received frame and the master sent another
* frame on the network. We have to abort sending the frame.
*/
if(eRcvState == STATE_M_RX_IDLE)
{
ENTER_CRITICAL_SECTION( );
/* First byte before the Modbus-PDU is the slave address. */
pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
usMasterSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usMasterSndBufferCount += usLength;
/* Calculate LRC checksum for Modbus-Serial-Line-PDU. */
usLRC = prvucMBLRC( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
pucMasterSndBufferCur[usMasterSndBufferCount++] = usLRC;
/* Activate the transmitter. */
eSndState = STATE_M_TX_START;
EXIT_CRITICAL_SECTION( );
if ( xMBMasterPortSerialSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBMasterPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
return eStatus;
}
BOOL
xMBMasterASCIIReceiveFSM( void )
{
BOOL xNeedPoll = FALSE;
UCHAR ucByte;
UCHAR ucResult;
assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
/* Always read the character. */
xNeedPoll = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
switch ( eRcvState )
{
/* If we have received a character in the init state we have to
* wait until the frame is finished.
*/
case STATE_M_RX_INIT:
vMBMasterPortTimersT35Enable( );
break;
/* In the error state we wait until all characters in the
* damaged frame are transmitted.
*/
case STATE_M_RX_ERROR:
vMBMasterPortTimersRespondTimeoutEnable( );
break;
/* In the idle state we wait for a new character. If a character
* is received the t1.5 and t3.5 timers are started and the
* receiver is in the state STATE_RX_RECEIVE and disable early
* the timer of respond timeout .
*/
case STATE_M_RX_IDLE:
/* Waiting for the start of frame character during respond timeout */
vMBMasterPortTimersRespondTimeoutEnable( );
if( ucByte == ':' )
{
/* Reset the input buffers to store the frame in receive state. */
usMasterRcvBufferPos = 0;
eBytePos = BYTE_HIGH_NIBBLE;
eRcvState = STATE_M_RX_RCV;
eSndState = STATE_M_TX_IDLE;
}
break;
/* A new character is received. If the character is a ':' the input
* buffer is cleared. A CR-character signals the end of the data
* block. Other characters are part of the data block and their
* ASCII value is converted back to a binary representation.
*/
case STATE_M_RX_RCV:
/* Enable timer timeout. */
vMBMasterPortTimersT35Enable( );
if( ucByte == ':' )
{
/* Empty receive buffer. */
eBytePos = BYTE_HIGH_NIBBLE;
usMasterRcvBufferPos = 0;
}
else if( ucByte == MB_ASCII_DEFAULT_CR )
{
eRcvState = STATE_M_RX_WAIT_EOF;
}
else
{
ucResult = prvucMBCHAR2BIN( ucByte );
switch ( eBytePos )
{
/* High nibble of the byte comes first. We check for
* a buffer overflow here. */
case BYTE_HIGH_NIBBLE:
if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
ucMasterASCIIRcvBuf[usMasterRcvBufferPos] = ( UCHAR )( ucResult << 4 );
eBytePos = BYTE_LOW_NIBBLE;
break;
}
else
{
/* not handled in Modbus specification but seems
* a resonable implementation. */
eRcvState = STATE_M_RX_ERROR;
/* Disable previously activated timer because of error state. */
vMBPortTimersDisable( );
}
break;
case BYTE_LOW_NIBBLE:
ucMasterASCIIRcvBuf[usMasterRcvBufferPos] |= ucResult;
usMasterRcvBufferPos++;
eBytePos = BYTE_HIGH_NIBBLE;
break;
}
}
break;
case STATE_M_RX_WAIT_EOF:
if( ucByte == ucMBLFCharacter )
{
/* Disable character timeout timer because all characters are
* received. */
vMBMasterPortTimersDisable( );
/* Receiver is again in idle state. */
eRcvState = STATE_M_RX_IDLE;
/* Notify the caller of eMBMasterASCIIReceive that a new frame
* was received. */
(void)xMBMasterPortEventPost( EV_MASTER_FRAME_RECEIVED );
xNeedPoll = FALSE;
}
else if( ucByte == ':' )
{
/* Start of frame character received but last message is not completed.
* Empty receive buffer and back to receive state. */
eBytePos = BYTE_HIGH_NIBBLE;
usMasterRcvBufferPos = 0;
eRcvState = STATE_M_RX_IDLE;
/* Enable timer for respond timeout and wait for next frame. */
vMBMasterPortTimersRespondTimeoutEnable( );
}
else
{
/* Frame is not okay. Delete entire frame. */
eRcvState = STATE_M_RX_IDLE;
}
break;
}
return xNeedPoll;
}
BOOL
xMBMasterASCIITransmitFSM( void )
{
BOOL xNeedPoll = TRUE;
UCHAR ucByte;
BOOL xFrameIsBroadcast = FALSE;
assert( eRcvState == STATE_M_RX_IDLE );
switch ( eSndState )
{
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_M_TX_XFWR:
break;
/* We should not get a transmitter event if the transmitter is in
* idle state. */
case STATE_M_TX_IDLE:
break;
/* Start of transmission. The start of a frame is defined by sending
* the character ':'. */
case STATE_M_TX_START:
ucByte = ':';
xMBMasterPortSerialPutByte( ( CHAR )ucByte );
eSndState = STATE_M_TX_DATA;
eBytePos = BYTE_HIGH_NIBBLE;
break;
/* Send the data block. Each data byte is encoded as a character hex
* stream with the high nibble sent first and the low nibble sent
* last. If all data bytes are exhausted we send a '\r' character
* to end the transmission. */
case STATE_M_TX_DATA:
if( usMasterSndBufferCount > 0 )
{
switch ( eBytePos )
{
case BYTE_HIGH_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur >> 4 ) );
xMBMasterPortSerialPutByte( ( CHAR ) ucByte );
eBytePos = BYTE_LOW_NIBBLE;
break;
case BYTE_LOW_NIBBLE:
ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur & 0x0F ) );
xMBMasterPortSerialPutByte( ( CHAR )ucByte );
pucMasterSndBufferCur++;
eBytePos = BYTE_HIGH_NIBBLE;
usMasterSndBufferCount--;
break;
}
}
else
{
xMBMasterPortSerialPutByte( MB_ASCII_DEFAULT_CR );
eSndState = STATE_M_TX_END;
}
break;
/* Finish the frame by sending a LF character. */
case STATE_M_TX_END:
xMBMasterPortSerialPutByte( ( CHAR )ucMBLFCharacter );
/* We need another state to make sure that the CR character has
* been sent. */
eSndState = STATE_M_TX_NOTIFY;
break;
/* Notify the task which called eMBMasterASCIISend that the frame has
* been sent. */
case STATE_M_TX_NOTIFY:
xFrameIsBroadcast = ( ucMasterASCIISndBuf[MB_SEND_BUF_PDU_OFF - MB_SER_PDU_PDU_OFF]
== MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
vMBMasterRequestSetType( xFrameIsBroadcast );
eSndState = STATE_M_TX_XFWR;
/* If the frame is broadcast ,master will enable timer of convert delay,
* else master will enable timer of respond timeout. */
if ( xFrameIsBroadcast == TRUE )
{
vMBMasterPortTimersConvertDelayEnable( );
}
else
{
vMBMasterPortTimersRespondTimeoutEnable( );
}
xNeedPoll = FALSE;
break;
}
return xNeedPoll;
}
BOOL MB_PORT_ISR_ATTR
xMBMasterASCIITimerT1SExpired( void )
{
BOOL xNeedPoll = FALSE;
switch ( eRcvState )
{
/* Timer t35 expired. Startup phase is finished. */
case STATE_M_RX_INIT:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
break;
/* Start of message is not received during respond timeout.
* Process error. */
case STATE_M_RX_IDLE:
eRcvState = STATE_M_RX_ERROR;
break;
/* A recieve timeout expired and no any new character received.
* Wait for respond time and go to error state to inform listener about error */
case STATE_M_RX_RCV:
eRcvState = STATE_M_RX_ERROR;
break;
/* An error occured while receiving the frame. */
case STATE_M_RX_ERROR:
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
xNeedPoll = xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
break;
/* If we have a timeout we go back to the idle state and wait for
* the next frame.
*/
case STATE_M_RX_WAIT_EOF:
eRcvState = STATE_M_RX_IDLE;
break;
default:
assert( 0 );
break;
}
eRcvState = STATE_M_RX_IDLE;
switch (eSndState)
{
/* A frame was send finish and convert delay or respond timeout expired.
* If the frame is broadcast,The master will idle,and if the frame is not
* broadcast.*/
case STATE_M_TX_XFWR:
if ( xMBMasterRequestIsBroadcast( ) == FALSE ) {
vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
}
break;
/* Function called in an illegal state. */
default:
assert( ( eSndState == STATE_M_TX_START ) || ( eSndState == STATE_M_TX_IDLE )
|| ( eSndState == STATE_M_TX_DATA ) || ( eSndState == STATE_M_TX_END )
|| ( eSndState == STATE_M_TX_NOTIFY ) );
break;
}
eSndState = STATE_M_TX_IDLE;
vMBMasterPortTimersDisable( );
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
if (xMBMasterGetCurTimerMode() == MB_TMODE_CONVERT_DELAY) {
xNeedPoll = xMBMasterPortEventPost( EV_MASTER_EXECUTE );
}
vMBMasterPortTimersDisable( );
/* no context switch required. */
return xNeedPoll;
}
static UCHAR
prvucMBCHAR2BIN( UCHAR ucCharacter )
{
if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )
{
return ( UCHAR )( ucCharacter - '0' );
}
else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )
{
return ( UCHAR )( ucCharacter - 'A' + 0x0A );
}
else
{
return 0xFF;
}
}
static UCHAR
prvucMBBIN2CHAR( UCHAR ucByte )
{
if( ucByte <= 0x09 )
{
return ( UCHAR )( '0' + ucByte );
}
else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )
{
return ( UCHAR )( ucByte - 0x0A + 'A' );
}
else
{
/* Programming error. */
assert( 0 );
}
return '0';
}
static UCHAR
prvucMBLRC( UCHAR * pucFrame, USHORT usLen )
{
UCHAR ucLRC = 0; /* LRC char initialized */
while( usLen-- )
{
ucLRC += *pucFrame++; /* Add buffer byte without carry */
}
/* Return twos complement */
ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );
return ucLRC;
}
#endif

View File

@@ -1,280 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfunccoils.c,v 1.8 2007/02/18 23:47:16 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_COILCNT_MAX ( 0x07D0 )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_FUNC_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX ( 0x07B0 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_READ_COILS_ENABLED
eMBException
eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usCoilCount;
UCHAR ucNBytes;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usCoilCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8 );
usCoilCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usCoilCount >= 1 ) &&
( usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_COILS;
*usLen += 1;
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usCoilCount & 0x0007 ) != 0 )
{
ucNBytes = ( UCHAR )( usCoilCount / 8 + 1 );
}
else
{
ucNBytes = ( UCHAR )( usCoilCount / 8 );
}
*pucFrameCur++ = ucNBytes;
*usLen += 1;
eRegStatus =
eMBRegCoilsCB( pucFrameCur, usRegAddress, usCoilCount,
MB_REG_READ );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen += ucNBytes;;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#if MB_FUNC_WRITE_COIL_ENABLED > 0
eMBException
eMBFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
UCHAR ucBuf[2];
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) &&
( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) ||
( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) )
{
ucBuf[1] = 0;
if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF )
{
ucBuf[0] = 1;
}
else
{
ucBuf[0] = 0;
}
eRegStatus =
eMBRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
eMBException
eMBFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usCoilCnt;
UCHAR ucByteCount;
UCHAR ucByteCountVerify;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen > ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 );
usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] );
ucByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF];
/* Compute the number of expected bytes in the request. */
if( ( usCoilCnt & 0x0007 ) != 0 )
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 );
}
else
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 );
}
if( ( usCoilCnt >= 1 ) &&
( usCoilCnt <= MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX ) &&
( ucByteCountVerify == ucByteCount ) )
{
eRegStatus =
eMBRegCoilsCB( &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF],
usRegAddress, usCoilCnt, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif
#endif

View File

@@ -1,398 +0,0 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfunccoils_m.c,v 1.60 2013/10/12 15:10:12 Armink Add Master Functions
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
#include "mbutils.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
#define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_REQ_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ( 0x07B0 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE ( 5 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_READ_COILS_ENABLED
/**
* This function will request read coil.
*
* @param ucSndAddr salve address
* @param usCoilAddr coil start address
* @param usNCoils coil total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_COILS;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usCoilAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usCoilAddr;
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF ] = usNCoils >> 8;
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] = usNCoils;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usCoilCount;
UCHAR ucByteCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if ( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usCoilCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF] << 8 );
usCoilCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] );
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usCoilCount & 0x0007 ) != 0 )
{
ucByteCount = ( UCHAR )( usCoilCount / 8 + 1 );
}
else
{
ucByteCount = ( UCHAR )( usCoilCount / 8 );
}
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usCoilCount >= 1 ) &&
( ucByteCount == pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] ) )
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegCoilsCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usCoilCount, MB_REG_READ );
/* If an error occurred convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
/**
* This function will request write one coil.
*
* @param ucSndAddr salve address
* @param usCoilAddr coil start address
* @param usCoilData data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*
* @see eMBMasterReqWriteMultipleCoils
*/
eMBMasterReqErrCode
eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( ( usCoilData != 0xFF00 ) && ( usCoilData != 0x0000 ) ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_SINGLE_COIL;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usCoilAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usCoilAddr;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF ] = usCoilData >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usCoilData;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
UCHAR ucBuf[2];
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) &&
( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) ||
( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) )
{
ucBuf[1] = 0;
if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF )
{
ucBuf[0] = 1;
}
else
{
ucBuf[0] = 0;
}
eRegStatus =
eMBMasterRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif // #if MB_FUNC_WRITE_COIL_ENABLED > 0
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
/**
* This function will request write multiple coils.
*
* @param ucSndAddr salve address
* @param usCoilAddr coil start address
* @param usNCoils coil total number
* @param usCoilData data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*
* @see eMBMasterReqWriteCoil
*/
eMBMasterReqErrCode
eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut)
{
UCHAR *ucMBFrame;
USHORT usRegIndex = 0;
UCHAR ucByteCount;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( usNCoils > MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_COILS;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usCoilAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usCoilAddr;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF] = usNCoils >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF + 1] = usNCoils ;
if( ( usNCoils & 0x0007 ) != 0 )
{
ucByteCount = ( UCHAR )( usNCoils / 8 + 1 );
}
else
{
ucByteCount = ( UCHAR )( usNCoils / 8 );
}
ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = ucByteCount;
ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF;
while( ucByteCount > usRegIndex)
{
*ucMBFrame++ = pucDataBuffer[usRegIndex++];
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + ucByteCount );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usCoilCnt;
UCHAR ucByteCount;
UCHAR ucByteCountVerify;
UCHAR *ucMBFrame;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, the *usLen is not need check. */
if( ( *usLen == MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 );
usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] );
ucByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF];
/* Compute the number of expected bytes in the request. */
if( ( usCoilCnt & 0x0007 ) != 0 )
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 );
}
else
{
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 );
}
if( ( usCoilCnt >= 1 ) && ( ucByteCountVerify == ucByteCount ) )
{
eRegStatus =
eMBMasterRegCoilsCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF],
usRegAddress, usCoilCnt, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
#endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0

View File

@@ -1,36 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncdiag.c,v 1.3 2006/12/07 22:10:34 wolti Exp $
*/

View File

@@ -1,143 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeRTOS Modbus Libary: A Modbus serial implementation for FreeRTOS
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncdisc.c,v 1.10 2007/09/12 10:15:56 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_DISCCNT_MAX ( 0x07D0 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_READ_COILS_ENABLED
eMBException
eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usDiscreteCnt;
UCHAR ucNBytes;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usDiscreteCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF] << 8 );
usDiscreteCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usDiscreteCnt >= 1 ) &&
( usDiscreteCnt < MB_PDU_FUNC_READ_DISCCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_DISCRETE_INPUTS;
*usLen += 1;
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usDiscreteCnt & 0x0007 ) != 0 )
{
ucNBytes = ( UCHAR ) ( usDiscreteCnt / 8 + 1 );
}
else
{
ucNBytes = ( UCHAR ) ( usDiscreteCnt / 8 );
}
*pucFrameCur++ = ucNBytes;
*usLen += 1;
eRegStatus =
eMBRegDiscreteCB( pucFrameCur, usRegAddress, usDiscreteCnt );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen += ucNBytes;;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif

View File

@@ -1,167 +0,0 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncdisc_m.c,v 1.60 2013/10/15 8:48:20 Armink Add Master Functions Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
/**
* This function will request read discrete inputs.
*
* @param ucSndAddr salve address
* @param usDiscreteAddr discrete start address
* @param usNDiscreteIn discrete total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_DISCRETE_INPUTS;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usDiscreteAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usDiscreteAddr;
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF ] = usNDiscreteIn >> 8;
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF + 1] = usNDiscreteIn;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usDiscreteCnt;
UCHAR ucNBytes;
UCHAR *ucMBFrame;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usDiscreteCnt = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF] << 8 );
usDiscreteCnt |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF + 1] );
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if( ( usDiscreteCnt & 0x0007 ) != 0 )
{
ucNBytes = ( UCHAR )( usDiscreteCnt / 8 + 1 );
}
else
{
ucNBytes = ( UCHAR )( usDiscreteCnt / 8 );
}
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if ((usDiscreteCnt >= 1) && ucNBytes == pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF])
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegDiscreteCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usDiscreteCnt );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif // #if MB_SERIAL_MASTER_RTU_ENABLED || MB_SERIAL_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@@ -1,318 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncholding.c,v 1.12 2007/02/18 23:48:22 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_FUNC_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_REGCNT_MAX ( 0x007D )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_FUNC_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX ( 0x0078 )
#define MB_PDU_FUNC_READWRITE_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF ( MB_PDU_DATA_OFF + 6 )
#define MB_PDU_FUNC_READWRITE_BYTECNT_OFF ( MB_PDU_DATA_OFF + 8 )
#define MB_PDU_FUNC_READWRITE_WRITE_VALUES_OFF ( MB_PDU_DATA_OFF + 9 )
#define MB_PDU_FUNC_READWRITE_SIZE_MIN ( 9 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_WRITE_HOLDING_ENABLED
eMBException
eMBFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
/* Make callback to update the value. */
eRegStatus = eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF],
usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
eMBException
eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usRegCount;
UCHAR ucRegByteCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen >= ( MB_PDU_FUNC_WRITE_MUL_SIZE_MIN + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF + 1] );
ucRegByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF];
if( ( usRegCount >= 1 ) &&
( usRegCount <= MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX ) &&
( ucRegByteCount == ( UCHAR ) ( 2 * usRegCount ) ) )
{
/* Make callback to update the register values. */
eRegStatus =
eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF],
usRegAddress, usRegCount, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
/* The response contains the function code, the starting
* address and the quantity of registers. We reuse the
* old values in the buffer because they are still valid.
*/
*usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
eMBException
eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usRegCount;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 ) && ( usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_HOLDING_REGISTER;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = ( UCHAR ) ( usRegCount * 2 );
*usLen += 1;
/* Make callback to fill the buffer. */
eRegStatus = eMBRegHoldingCB( pucFrameCur, usRegAddress, usRegCount, MB_REG_READ );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
*usLen += usRegCount * 2;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
eMBException
eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegReadAddress;
USHORT usRegReadCount;
USHORT usRegWriteAddress;
USHORT usRegWriteCount;
UCHAR ucRegWriteByteCount;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen >= ( MB_PDU_FUNC_READWRITE_SIZE_MIN + MB_PDU_SIZE_MIN ) )
{
usRegReadAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_ADDR_OFF] << 8U );
usRegReadAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_ADDR_OFF + 1] );
usRegReadAddress++;
usRegReadCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF] << 8U );
usRegReadCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF + 1] );
usRegWriteAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF] << 8U );
usRegWriteAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF + 1] );
usRegWriteAddress++;
usRegWriteCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF] << 8U );
usRegWriteCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF + 1] );
ucRegWriteByteCount = pucFrame[MB_PDU_FUNC_READWRITE_BYTECNT_OFF];
if( ( usRegReadCount >= 1 ) && ( usRegReadCount <= 0x7D ) &&
( usRegWriteCount >= 1 ) && ( usRegWriteCount <= 0x79 ) &&
( ( 2 * usRegWriteCount ) == ucRegWriteByteCount ) )
{
/* Make callback to update the register values. */
eRegStatus = eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_READWRITE_WRITE_VALUES_OFF],
usRegWriteAddress, usRegWriteCount, MB_REG_WRITE );
if( eRegStatus == MB_ENOERR )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READWRITE_MULTIPLE_REGISTERS;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = ( UCHAR ) ( usRegReadCount * 2 );
*usLen += 1;
/* Make the read callback. */
eRegStatus =
eMBRegHoldingCB( pucFrameCur, usRegReadAddress, usRegReadCount, MB_REG_READ );
if( eRegStatus == MB_ENOERR )
{
*usLen += 2 * usRegReadCount;
}
}
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
return eStatus;
}
#endif
#endif

View File

@@ -1,461 +0,0 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncholding_m.c,v 1.60 2013/09/02 14:13:40 Armink Add Master Functions Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
//#include "mb.h"
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_REGCNT_MAX ( 0x007D )
#define MB_PDU_FUNC_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
#define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_SIZE ( 4 )
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
#define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
#define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 )
#define MB_PDU_REQ_WRITE_MUL_REGCNT_MAX ( 0x0078 )
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_WRITE_MUL_SIZE ( 4 )
#define MB_PDU_REQ_READWRITE_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READWRITE_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 4 )
#define MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF ( MB_PDU_DATA_OFF + 6 )
#define MB_PDU_REQ_READWRITE_WRITE_BYTECNT_OFF ( MB_PDU_DATA_OFF + 8 )
#define MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF ( MB_PDU_DATA_OFF + 9 )
#define MB_PDU_REQ_READWRITE_SIZE_MIN ( 9 )
#define MB_PDU_FUNC_READWRITE_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READWRITE_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READWRITE_SIZE_MIN ( 1 )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_WRITE_HOLDING_ENABLED
/**
* This function will request write holding register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usRegData register data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_REGISTER;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF] = usRegData >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usRegData ;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_SIZE_MIN + MB_PDU_FUNC_WRITE_SIZE ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
usRegAddress++;
/* Make callback to update the value. */
eRegStatus = eMBMasterRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF],
usRegAddress, 1, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
/**
* This function will request write multiple holding register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usNRegs register total number
* @param pusDataBuffer data to be written
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr,
USHORT usRegAddr, USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut )
{
UCHAR *ucMBFrame;
USHORT usRegIndex = 0;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_REGISTERS;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] = usNRegs ;
ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = usNRegs * 2;
ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF;
while( usNRegs > usRegIndex)
{
*ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8;
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + 2*usNRegs );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usRegCount;
UCHAR ucRegByteCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, the *usLen is not need check. */
if( ( *usLen == MB_PDU_SIZE_MIN + MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] );
ucRegByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF];
if( ucRegByteCount == 2 * usRegCount )
{
/* Make callback to update the register values. */
eRegStatus = eMBMasterRegHoldingCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF],
usRegAddress, usRegCount, MB_REG_WRITE );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
/**
* This function will request read holding register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usNRegs register total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_HOLDING_REGISTER;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usRegCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 ) && ( 2 * usRegCount == pucFrame[MB_PDU_FUNC_READ_BYTECNT_OFF] ) )
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegHoldingCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usRegCount, MB_REG_READ );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
/**
* This function will request read and write holding register.
*
* @param ucSndAddr salve address
* @param usReadRegAddr read register start address
* @param usNReadRegs read register total number
* @param pusDataBuffer data to be written
* @param usWriteRegAddr write register start address
* @param usNWriteRegs write register total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr,
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut )
{
UCHAR *ucMBFrame;
USHORT usRegIndex = 0;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READWRITE_MULTIPLE_REGISTERS;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF] = usReadRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF + 1] = usReadRegAddr;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF] = usNReadRegs >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] = usNReadRegs ;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF] = usWriteRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF + 1] = usWriteRegAddr;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF] = usNWriteRegs >> 8;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] = usNWriteRegs ;
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_BYTECNT_OFF] = usNWriteRegs * 2;
ucMBFrame += MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF;
while( usNWriteRegs > usRegIndex)
{
*ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8;
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READWRITE_SIZE_MIN + 2*usNWriteRegs );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegReadAddress;
USHORT usRegReadCount;
USHORT usRegWriteAddress;
USHORT usRegWriteCount;
UCHAR *ucMBFrame;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READWRITE_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegReadAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF] << 8U );
usRegReadAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF + 1] );
usRegReadAddress++;
usRegReadCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF] << 8U );
usRegReadCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] );
usRegWriteAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF] << 8U );
usRegWriteAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF + 1] );
usRegWriteAddress++;
usRegWriteCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF] << 8U );
usRegWriteCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] );
if( ( 2 * usRegReadCount ) == pucFrame[MB_PDU_FUNC_READWRITE_READ_BYTECNT_OFF] )
{
/* Make callback to update the register values. */
eRegStatus = eMBMasterRegHoldingCB( &ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF],
usRegWriteAddress, usRegWriteCount, MB_REG_WRITE );
if( eRegStatus == MB_ENOERR )
{
/* Make the read callback. */
eRegStatus = eMBMasterRegHoldingCB(&pucFrame[MB_PDU_FUNC_READWRITE_READ_VALUES_OFF],
usRegReadAddress, usRegReadCount, MB_REG_READ);
}
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
return eStatus;
}
#endif
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@@ -1,133 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncinput.c,v 1.10 2007/09/12 10:15:56 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_FUNC_READ_ADDR_OFF ( MB_PDU_DATA_OFF )
#define MB_PDU_FUNC_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_FUNC_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_REGCNT_MAX ( 0x007D )
#define MB_PDU_FUNC_READ_RSP_BYTECNT_OFF ( MB_PDU_DATA_OFF )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_READ_INPUT_ENABLED
eMBException
eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usRegCount;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 )
&& ( usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = ( UCHAR )( usRegCount * 2 );
*usLen += 1;
eRegStatus =
eMBRegInputCB( pucFrameCur, usRegAddress, usRegCount );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
*usLen += usRegCount * 2;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read input register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif

View File

@@ -1,154 +0,0 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncinput_m.c,v 1.60 2013/10/12 14:23:40 Armink Add Master Functions Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_REQ_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
#define MB_PDU_REQ_READ_SIZE ( 4 )
#define MB_PDU_FUNC_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
#define MB_PDU_FUNC_READ_RSP_BYTECNT_OFF ( MB_PDU_DATA_OFF )
/* ----------------------- Static functions ---------------------------------*/
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
/* ----------------------- Start implementation -----------------------------*/
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
#if MB_FUNC_READ_INPUT_ENABLED
/**
* This function will request read input register.
*
* @param ucSndAddr salve address
* @param usRegAddr register start address
* @param usNRegs register total number
* @param lTimeOut timeout (-1 will waiting forever)
*
* @return error code
*/
eMBMasterReqErrCode
eMBMasterReqReadInputRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut )
{
UCHAR *ucMBFrame;
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
else
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
vMBMasterSetDestAddress(ucSndAddr);
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_INPUT_REGISTER;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usRegAddr >> 8;
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usRegAddr;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
}
eMBException
eMBMasterFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
{
UCHAR *ucMBFrame;
USHORT usRegAddress;
USHORT usRegCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
/* If this request is broadcast, and it's read mode. This request don't need execute. */
if ( xMBMasterRequestIsBroadcast() )
{
eStatus = MB_EX_NONE;
}
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
{
vMBMasterGetPDUSndBuf(&ucMBFrame);
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
usRegAddress++;
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 ) && ( 2 * usRegCount == pucFrame[MB_PDU_FUNC_READ_BYTECNT_OFF] ) )
{
/* Make callback to fill the buffer. */
eRegStatus = eMBMasterRegInputCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usRegCount );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
#endif
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@@ -1,99 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbfuncother.c,v 1.8 2006/12/07 22:10:34 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbconfig.h"
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
/* ----------------------- Static variables ---------------------------------*/
static UCHAR ucMBSlaveID[MB_FUNC_OTHER_REP_SLAVEID_BUF];
static USHORT usMBSlaveIDLen;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning,
UCHAR const *pucAdditional, USHORT usAdditionalLen )
{
eMBErrorCode eStatus = MB_ENOERR;
/* the first byte and second byte in the buffer is reserved for
* the parameter ucSlaveID and the running flag. The rest of
* the buffer is available for additional data. */
if( usAdditionalLen + 2 < MB_FUNC_OTHER_REP_SLAVEID_BUF )
{
usMBSlaveIDLen = 0;
ucMBSlaveID[usMBSlaveIDLen++] = ucSlaveID;
ucMBSlaveID[usMBSlaveIDLen++] = ( UCHAR )( xIsRunning ? 0xFF : 0x00 );
if( usAdditionalLen > 0 )
{
memcpy( &ucMBSlaveID[usMBSlaveIDLen], pucAdditional,
( size_t )usAdditionalLen );
usMBSlaveIDLen += usAdditionalLen;
}
}
else
{
eStatus = MB_ENORES;
}
return eStatus;
}
eMBException
eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen )
{
memcpy( &pucFrame[MB_PDU_DATA_OFF], &ucMBSlaveID[0], ( size_t )usMBSlaveIDLen );
*usLen = ( USHORT )( MB_PDU_DATA_OFF + usMBSlaveIDLen );
return MB_EX_NONE;
}
#endif
#endif

View File

@@ -1,148 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbutils.c,v 1.6 2007/02/18 23:49:07 wolti Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbproto.h"
/* ----------------------- Defines ------------------------------------------*/
#define BITS_UCHAR 8U
/* ----------------------- Start implementation -----------------------------*/
void
xMBUtilSetBits( UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits,
UCHAR ucValue )
{
USHORT usWordBuf;
USHORT usMask;
USHORT usByteOffset;
USHORT usNPreBits;
USHORT usValue = ucValue;
assert( ucNBits <= 8 );
assert( ( size_t )BITS_UCHAR == sizeof( UCHAR ) * 8 );
/* Calculate byte offset for first byte containing the bit values starting
* at usBitOffset. */
usByteOffset = ( USHORT )( ( usBitOffset ) / BITS_UCHAR );
/* How many bits precede our bits to set. */
usNPreBits = ( USHORT )( usBitOffset - usByteOffset * BITS_UCHAR );
/* Move bit field into position over bits to set */
usValue <<= usNPreBits;
/* Prepare a mask for setting the new bits. */
usMask = ( USHORT )( ( 1 << ( USHORT ) ucNBits ) - 1 );
usMask <<= usBitOffset - usByteOffset * BITS_UCHAR;
/* copy bits into temporary storage. */
usWordBuf = ucByteBuf[usByteOffset];
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR;
/* Zero out bit field bits and then or value bits into them. */
usWordBuf = ( USHORT )( ( usWordBuf & ( ~usMask ) ) | usValue );
/* move bits back into storage */
ucByteBuf[usByteOffset] = ( UCHAR )( usWordBuf & 0xFF );
ucByteBuf[usByteOffset + 1] = ( UCHAR )( usWordBuf >> BITS_UCHAR );
}
UCHAR
xMBUtilGetBits( UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits )
{
USHORT usWordBuf;
USHORT usMask;
USHORT usByteOffset;
USHORT usNPreBits;
/* Calculate byte offset for first byte containing the bit values starting
* at usBitOffset. */
usByteOffset = ( USHORT )( ( usBitOffset ) / BITS_UCHAR );
/* How many bits precede our bits to set. */
usNPreBits = ( USHORT )( usBitOffset - usByteOffset * BITS_UCHAR );
/* Prepare a mask for setting the new bits. */
usMask = ( USHORT )( ( 1 << ( USHORT ) ucNBits ) - 1 );
/* copy bits into temporary storage. */
usWordBuf = ucByteBuf[usByteOffset];
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR;
/* throw away unneeded bits. */
usWordBuf >>= usNPreBits;
/* mask away bits above the requested bitfield. */
usWordBuf &= usMask;
return ( UCHAR ) usWordBuf;
}
eMBException
prveMBError2Exception( eMBErrorCode eErrorCode )
{
eMBException eStatus;
switch ( eErrorCode )
{
case MB_ENOERR:
eStatus = MB_EX_NONE;
break;
case MB_ENOREG:
eStatus = MB_EX_ILLEGAL_DATA_ADDRESS;
break;
case MB_ETIMEDOUT:
eStatus = MB_EX_SLAVE_BUSY;
break;
default:
eStatus = MB_EX_SLAVE_DEVICE_FAILURE;
break;
}
return eStatus;
}

View File

@@ -1,425 +0,0 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mb.h,v 1.17 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MB_H
#define _MB_H
#include "port.h"
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
#include "mbport.h"
#include "mbproto.h"
/*! \defgroup modbus Modbus
* \code #include "mb.h" \endcode
*
* This module defines the interface for the application. It contains
* the basic functions and types required to use the Modbus protocol stack.
* A typical application will want to call eMBInit() first. If the device
* is ready to answer network requests it must then call eMBEnable() to activate
* the protocol stack. In the main loop the function eMBPoll() must be called
* periodically. The time interval between pooling depends on the configured
* Modbus timeout. If an RTOS is available a separate task should be created
* and the task should always call the function eMBPoll().
*
* \code
* // Initialize protocol stack in RTU mode for a slave with address 10 = 0x0A
* eMBInit( MB_RTU, 0x0A, 38400, MB_PAR_EVEN );
* // Enable the Modbus Protocol Stack.
* eMBEnable( );
* for( ;; )
* {
* // Call the main polling loop of the Modbus protocol stack.
* eMBPoll( );
* ...
* }
* \endcode
*/
/* ----------------------- Defines ------------------------------------------*/
/*! \ingroup modbus
* \brief Use the default Modbus TCP port (502)
*/
#define MB_TCP_PORT_USE_DEFAULT 0
#define MB_FUNC_CODE_MAX 127
/* ----------------------- Type definitions ---------------------------------*/
/*! \ingroup modbus
* \brief Modbus serial transmission modes (RTU/ASCII).
*
* Modbus serial supports two transmission modes. Either ASCII or RTU. RTU
* is faster but has more hardware requirements and requires a network with
* a low jitter. ASCII is slower and more reliable on slower links (E.g. modems)
*/
typedef enum
{
MB_RTU, /*!< RTU transmission mode. */
MB_ASCII, /*!< ASCII transmission mode. */
MB_TCP /*!< TCP mode. */
} eMBMode;
/*! \ingroup modbus
* \brief If register should be written or read.
*
* This value is passed to the callback functions which support either
* reading or writing register values. Writing means that the application
* registers should be updated and reading means that the modbus protocol
* stack needs to know the current register values.
*
* \see eMBRegHoldingCB( ), eMBRegCoilsCB( ), eMBRegDiscreteCB( ) and
* eMBRegInputCB( ).
*/
typedef enum
{
MB_REG_READ, /*!< Read register values and pass to protocol stack. */
MB_REG_WRITE /*!< Update register values. */
} eMBRegisterMode;
/*! \ingroup modbus
* \brief Errorcodes used by all function in the protocol stack.
*/
typedef enum
{
MB_ENOERR, /*!< no error. */
MB_ENOREG, /*!< illegal register address. */
MB_EINVAL, /*!< illegal argument. */
MB_EPORTERR, /*!< porting layer error. */
MB_ENORES, /*!< insufficient resources. */
MB_EIO, /*!< I/O error. */
MB_EILLSTATE, /*!< protocol stack in illegal state. */
MB_ETIMEDOUT /*!< timeout error occurred. */
} eMBErrorCode;
/* ----------------------- Function prototypes ------------------------------*/
/*! \ingroup modbus
* \brief Initialize the Modbus protocol stack.
*
* This functions initializes the ASCII or RTU module and calls the
* init functions of the porting layer to prepare the hardware. Please
* note that the receiver is still disabled and no Modbus frames are
* processed until eMBEnable( ) has been called.
*
* \param eMode If ASCII or RTU mode should be used.
* \param ucSlaveAddress The slave address. Only frames sent to this
* address or to the broadcast address are processed.
* \param ucPort The port to use. E.g. 1 for COM1 on windows. This value
* is platform dependent and some ports simply choose to ignore it.
* \param ulBaudRate The baudrate. E.g. 19200. Supported baudrates depend
* on the porting layer.
* \param eParity Parity used for serial transmission.
*
* \return If no error occurs the function returns eMBErrorCode::MB_ENOERR.
* The protocol is then in the disabled state and ready for activation
* by calling eMBEnable( ). Otherwise one of the following error codes
* is returned:
* - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid
* slave addresses are in the range 1 - 247.
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress,
UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity );
/*! \ingroup modbus
* \brief Initialize the Modbus protocol stack for Modbus TCP.
*
* This function initializes the Modbus TCP Module. Please note that
* frame processing is still disabled until eMBEnable( ) is called.
*
* \param usTCPPort The TCP port to listen on.
* \param ucSlaveUid The UID field for slave to listen on.
* \return If the protocol stack has been initialized correctly the function
* returns eMBErrorCode::MB_ENOERR. Otherwise one of the following error
* codes is returned:
* - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid
* slave addresses are in the range 1 - 247.
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBTCPInit( UCHAR ucSlaveUid, USHORT usTCPPort );
/*! \ingroup modbus
* \brief Release resources used by the protocol stack.
*
* This function disables the Modbus protocol stack and release all
* hardware resources. It must only be called when the protocol stack
* is disabled.
*
* \note Note all ports implement this function. A port which wants to
* get an callback must define the macro MB_PORT_HAS_CLOSE to 1.
*
* \return If the resources where released it return eMBErrorCode::MB_ENOERR.
* If the protocol stack is not in the disabled state it returns
* eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBClose( void );
/*! \ingroup modbus
* \brief Enable the Modbus protocol stack.
*
* This function enables processing of Modbus frames. Enabling the protocol
* stack is only possible if it is in the disabled state.
*
* \return If the protocol stack is now in the state enabled it returns
* eMBErrorCode::MB_ENOERR. If it was not in the disabled state it
* return eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBEnable( void );
/*! \ingroup modbus
* \brief Disable the Modbus protocol stack.
*
* This function disables processing of Modbus frames.
*
* \return If the protocol stack has been disabled it returns
* eMBErrorCode::MB_ENOERR. If it was not in the enabled state it returns
* eMBErrorCode::MB_EILLSTATE.
*/
eMBErrorCode eMBDisable( void );
/*! \ingroup modbus
* \brief The main pooling loop of the Modbus protocol stack.
*
* This function must be called periodically. The timer interval required
* is given by the application dependent Modbus slave timeout. Internally the
* function calls xMBPortEventGet() and waits for an event from the receiver or
* transmitter state machines.
*
* \return If the protocol stack is not in the enabled state the function
* returns eMBErrorCode::MB_EILLSTATE. Otherwise it returns
* eMBErrorCode::MB_ENOERR.
*/
eMBErrorCode eMBPoll( void );
/*! \ingroup modbus
* \brief Configure the slave id of the device.
*
* This function should be called when the Modbus function <em>Report Slave ID</em>
* is enabled ( By defining MB_FUNC_OTHER_REP_SLAVEID_ENABLED in mbconfig.h ).
*
* \param ucSlaveID Values is returned in the <em>Slave ID</em> byte of the
* <em>Report Slave ID</em> response.
* \param xIsRunning If TRUE the <em>Run Indicator Status</em> byte is set to 0xFF.
* otherwise the <em>Run Indicator Status</em> is 0x00.
* \param pucAdditional Values which should be returned in the <em>Additional</em>
* bytes of the <em> Report Slave ID</em> response.
* \param usAdditionalLen Length of the buffer <code>pucAdditonal</code>.
*
* \return If the static buffer defined by MB_FUNC_OTHER_REP_SLAVEID_BUF in
* mbconfig.h is to small it returns eMBErrorCode::MB_ENORES. Otherwise
* it returns eMBErrorCode::MB_ENOERR.
*/
eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning,
UCHAR const *pucAdditional,
USHORT usAdditionalLen );
/*! \ingroup modbus
* \brief Registers a callback handler for a given function code.
*
* This function registers a new callback handler for a given function code.
* The callback handler supplied is responsible for interpreting the Modbus PDU and
* the creation of an appropriate response. In case of an error it should return
* one of the possible Modbus exceptions which results in a Modbus exception frame
* sent by the protocol stack.
*
* \param ucFunctionCode The Modbus function code for which this handler should
* be registers. Valid function codes are in the range 1 to 127.
* \param pxHandler The function handler which should be called in case
* such a frame is received. If \c NULL a previously registered function handler
* for this function code is removed.
*
* \return eMBErrorCode::MB_ENOERR if the handler has been installed. If no
* more resources are available it returns eMBErrorCode::MB_ENORES. In this
* case the values in mbconfig.h should be adjusted. If the argument was not
* valid it returns eMBErrorCode::MB_EINVAL.
*/
eMBErrorCode eMBRegisterCB( UCHAR ucFunctionCode,
pxMBFunctionHandler pxHandler );
/* ----------------------- Callback -----------------------------------------*/
/*! \defgroup modbus_registers Modbus Registers
* \code #include "mb.h" \endcode
* The protocol stack does not internally allocate any memory for the
* registers. This makes the protocol stack very small and also usable on
* low end targets. In addition the values don't have to be in the memory
* and could for example be stored in a flash.<br>
* Whenever the protocol stack requires a value it calls one of the callback
* function with the register address and the number of registers to read
* as an argument. The application should then read the actual register values
* (for example the ADC voltage) and should store the result in the supplied
* buffer.<br>
* If the protocol stack wants to update a register value because a write
* register function was received a buffer with the new register values is
* passed to the callback function. The function should then use these values
* to update the application register values.
*/
/*! \ingroup modbus_registers
* \brief Callback function used if the value of a <em>Input Register</em>
* is required by the protocol stack. The starting register address is given
* by \c usAddress and the last register is given by <tt>usAddress +
* usNRegs - 1</tt>.
*
* \param pucRegBuffer A buffer where the callback function should write
* the current value of the modbus registers to.
* \param usAddress The starting address of the register. Input registers
* are in the range 1 - 65535.
* \param usNRegs Number of registers the callback function must supply.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application can not supply values
* for registers within this range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> exception frame is sent as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Holding Register</em> value is
* read or written by the protocol stack. The starting register address
* is given by \c usAddress and the last register is given by
* <tt>usAddress + usNRegs - 1</tt>.
*
* \param pucRegBuffer If the application registers values should be updated the
* buffer points to the new registers values. If the protocol stack needs
* to now the current values the callback function should write them into
* this buffer.
* \param usAddress The starting address of the register.
* \param usNRegs Number of registers to read or write.
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application register
* values should be updated from the values in the buffer. For example
* this would be the case when the Modbus master has issued an
* <b>WRITE SINGLE REGISTER</b> command.
* If the value eMBRegisterMode::MB_REG_READ the application should copy
* the current values into the buffer \c pucRegBuffer.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application can not supply values
* for registers within this range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> exception frame is sent as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Coil Register</em> value is
* read or written by the protocol stack. If you are going to use
* this function you might use the functions xMBUtilSetBits( ) and
* xMBUtilGetBits( ) for working with bitfields.
*
* \param pucRegBuffer The bits are packed in bytes where the first coil
* starting at address \c usAddress is stored in the LSB of the
* first byte in the buffer <code>pucRegBuffer</code>.
* If the buffer should be written by the callback function unused
* coil values (I.e. if not a multiple of eight coils is used) should be set
* to zero.
* \param usAddress The first coil number.
* \param usNCoils Number of coil values requested.
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application values should
* be updated from the values supplied in the buffer \c pucRegBuffer.
* If eMBRegisterMode::MB_REG_READ the application should store the current
* values in the buffer \c pucRegBuffer.
*
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
* within the requested address range. In this case a
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Input Discrete Register</em> value is
* read by the protocol stack.
*
* If you are going to use his function you might use the functions
* xMBUtilSetBits( ) and xMBUtilGetBits( ) for working with bitfields.
*
* \param pucRegBuffer The buffer should be updated with the current
* coil values. The first discrete input starting at \c usAddress must be
* stored at the LSB of the first byte in the buffer. If the requested number
* is not a multiple of eight the remaining bits should be set to zero.
* \param usAddress The starting address of the first discrete input.
* \param usNDiscrete Number of discrete input values.
* \return The function must return one of the following error codes:
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
* Modbus response is sent.
* - eMBErrorCode::MB_ENOREG If no such discrete inputs exists.
* In this case a <b>ILLEGAL DATA ADDRESS</b> exception frame is sent
* as a response.
* - eMBErrorCode::MB_ETIMEDOUT If the requested register block is
* currently not available and the application dependent response
* timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b>
* exception is sent as a response.
* - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case
* a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response.
*/
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete );
#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More