refact 3
This commit is contained in:
@@ -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)
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
// ================================
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
0
managed_components/espressif__cmake_utilities/.component_hash
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/.component_hash
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/CHANGELOG.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/CHANGELOG.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/Kconfig
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/Kconfig
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/README.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/README.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/cmake_utilities.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/cmake_utilities.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/docs/gcc.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/docs/gcc.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/docs/gen_compressed_ota.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/docs/gen_compressed_ota.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/docs/relinker.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/docs/relinker.md
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/gcc.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/gcc.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/gen_compressed_ota.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/gen_compressed_ota.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/gen_single_bin.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/gen_single_bin.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/idf_component.yml
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/idf_component.yml
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/license.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/license.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/package_manager.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/package_manager.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/project_include.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/project_include.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/relinker.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/relinker.cmake
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/gen_custom_ota.py
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/gen_custom_ota.py
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/configuration.py
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/configuration.py
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/examples/esp32c2/function.csv
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/examples/esp32c2/function.csv
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/examples/esp32c2/library.csv
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/examples/esp32c2/library.csv
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/examples/esp32c2/object.csv
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/examples/esp32c2/object.csv
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/relinker.py
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/scripts/relinker/relinker.py
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/idf_component.yml
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/idf_component.yml
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/test_component2.c
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/test_component2.c
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/test_component2.h
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/TEST-component2/test_component2.h
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/idf_component.yml
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/idf_component.yml
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/test_component1.c
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/test_component1.c
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/test_component1.h
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/components/test_component1/test_component1.h
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/main/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/main/CMakeLists.txt
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/main/test_cmake_utilities.c
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/main/test_cmake_utilities.c
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/pytest_cmake_utilities.py
Executable file → Normal file
0
managed_components/espressif__cmake_utilities/test_apps/pytest_cmake_utilities.py
Executable file → Normal file
@@ -1 +0,0 @@
|
||||
2168e6b4cbda4d0281a2a2d1a40a3848e231473b2690d73217e3600fd2c98c12
|
||||
@@ -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/
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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 Espressif’s IoT Development Framework, [ESP_IDF](https://github.com/espressif/esp-idf). The packages from this repository are uploaded to Espressif’s 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.
|
||||
@@ -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}"
|
||||
|
||||
@@ -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) \
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 $
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user