rm cached
This commit is contained in:
parent
003e11c96a
commit
2dd35d3c74
@ -1 +0,0 @@
|
|||||||
Subproject commit d50ec7a85ffa5c42ec23b9a69b7df3b1a75fcb7f
|
|
||||||
236
etk/src/algorithm/trend/src/et_trend.c
Normal file
236
etk/src/algorithm/trend/src/et_trend.c
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
#include "et_trend.h"
|
||||||
|
#include "et_log.h"
|
||||||
|
#include <float.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the index of minimum value in array
|
||||||
|
*
|
||||||
|
* @param data Input data array
|
||||||
|
* @param len Length of the array
|
||||||
|
* @return uint8_t Index of the minimum value
|
||||||
|
*/
|
||||||
|
static uint8_t find_min_index(const float *data, uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t min_idx = 0;
|
||||||
|
for (uint8_t i = 1; i < len; i++)
|
||||||
|
{
|
||||||
|
if (data[i] < data[min_idx])
|
||||||
|
{
|
||||||
|
min_idx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the index of maximum value in array
|
||||||
|
*
|
||||||
|
* @param data Input data array
|
||||||
|
* @param len Length of the array
|
||||||
|
* @return uint8_t Index of the maximum value
|
||||||
|
*/
|
||||||
|
static uint8_t find_max_index(const float *data, uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t max_idx = 0;
|
||||||
|
for (uint8_t i = 1; i < len; i++)
|
||||||
|
{
|
||||||
|
if (data[i] > data[max_idx])
|
||||||
|
{
|
||||||
|
max_idx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
ET_TrendResult et_trend_detect(const et_trend_t *trend, float *out_avg_diff)
|
||||||
|
{
|
||||||
|
// Input parameter validation
|
||||||
|
if (!trend || !trend->data || trend->length < trend->config.window_size)
|
||||||
|
{
|
||||||
|
ET_ERR("Invalid input: trend=%p, data=%p, window_size=%d, length=%d", trend, trend ? trend->data : NULL,
|
||||||
|
trend ? trend->config.window_size : 0, trend ? trend->length : 0);
|
||||||
|
return TREND_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position window data (original time series, maintain order)
|
||||||
|
const float *input = trend->data + (trend->length - trend->config.window_size);
|
||||||
|
uint8_t window_size = trend->config.window_size;
|
||||||
|
|
||||||
|
// Record index of outlier to exclude
|
||||||
|
uint8_t exclude_idx = 0xFF; // Initial invalid index
|
||||||
|
uint8_t valid_count = window_size;
|
||||||
|
|
||||||
|
switch (trend->config.outlier_mode)
|
||||||
|
{
|
||||||
|
case OUTLIER_MIN:
|
||||||
|
{
|
||||||
|
exclude_idx = find_min_index(input, window_size);
|
||||||
|
valid_count--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OUTLIER_MAX:
|
||||||
|
{
|
||||||
|
exclude_idx = find_max_index(input, window_size);
|
||||||
|
valid_count--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OUTLIER_BOTH:
|
||||||
|
{
|
||||||
|
// First exclude minimum, then exclude maximum from remaining data
|
||||||
|
uint8_t min_idx = find_min_index(input, window_size);
|
||||||
|
// Construct temporary array excluding minimum
|
||||||
|
float temp[ET_TREND_WINDOW_MAX];
|
||||||
|
uint8_t temp_len = 0;
|
||||||
|
for (uint8_t i = 0; i < window_size; i++)
|
||||||
|
{
|
||||||
|
if (i != min_idx)
|
||||||
|
{
|
||||||
|
temp[temp_len++] = input[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Find index of maximum in temporary array and map back to original index
|
||||||
|
uint8_t max_temp_idx = find_max_index(temp, temp_len);
|
||||||
|
// Map back to original index (skip min_idx)
|
||||||
|
exclude_idx = 0;
|
||||||
|
for (uint8_t i = 0; i < window_size; i++)
|
||||||
|
{
|
||||||
|
if (i == min_idx)
|
||||||
|
continue;
|
||||||
|
if (exclude_idx == max_temp_idx)
|
||||||
|
{
|
||||||
|
exclude_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
exclude_idx++;
|
||||||
|
}
|
||||||
|
valid_count -= 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // OUTLIER_NONE
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insufficient valid data after filtering
|
||||||
|
if (valid_count < 2)
|
||||||
|
{
|
||||||
|
ET_DBG("Insufficient valid data after outlier exclusion: %d", valid_count);
|
||||||
|
return TREND_NO_VALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect valid data after filtering (maintain original order)
|
||||||
|
float valid_data[ET_TREND_WINDOW_MAX];
|
||||||
|
uint8_t valid_idx = 0;
|
||||||
|
for (uint8_t i = 0; i < window_size; i++)
|
||||||
|
{
|
||||||
|
if (i != exclude_idx)
|
||||||
|
{ // Skip outlier
|
||||||
|
valid_data[valid_idx++] = input[i];
|
||||||
|
ET_DBG("valid_data[%d]=%.3f", valid_idx - 1, valid_data[valid_idx - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate average values of first and second halves (based on original time order)
|
||||||
|
float avg_start = 0.0f, avg_end = 0.0f;
|
||||||
|
float diff = 0.0f;
|
||||||
|
uint8_t half = valid_count / 2;
|
||||||
|
|
||||||
|
// First half (early data)
|
||||||
|
for (uint8_t i = 0; i < half; i++)
|
||||||
|
{
|
||||||
|
avg_start += valid_data[i];
|
||||||
|
}
|
||||||
|
avg_start /= half;
|
||||||
|
|
||||||
|
// Second half (later data)
|
||||||
|
uint8_t second_half_count = valid_count - half;
|
||||||
|
for (uint8_t i = half; i < valid_count; i++)
|
||||||
|
{
|
||||||
|
avg_end += valid_data[i];
|
||||||
|
}
|
||||||
|
avg_end /= second_half_count;
|
||||||
|
|
||||||
|
diff = avg_end - avg_start;
|
||||||
|
|
||||||
|
// Fallback logic for OUTLIER_MIN mode (only when filtered data cannot show trend)
|
||||||
|
if (trend->config.outlier_mode == OUTLIER_MIN && diff > -trend->config.threshold)
|
||||||
|
{
|
||||||
|
ET_DBG("OUTLIER_MIN adjustment: diff=%.3f too small, using original input.", diff);
|
||||||
|
// Re-calculate using original data
|
||||||
|
uint8_t original_half = window_size / 2;
|
||||||
|
avg_start = 0.0f;
|
||||||
|
for (uint8_t i = 0; i < original_half; i++)
|
||||||
|
{
|
||||||
|
avg_start += input[i];
|
||||||
|
}
|
||||||
|
avg_start /= original_half;
|
||||||
|
|
||||||
|
avg_end = 0.0f;
|
||||||
|
uint8_t original_second_half = window_size - original_half;
|
||||||
|
for (uint8_t i = original_half; i < window_size; i++)
|
||||||
|
{
|
||||||
|
avg_end += input[i];
|
||||||
|
}
|
||||||
|
avg_end /= original_second_half;
|
||||||
|
|
||||||
|
diff = avg_end - avg_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output difference value
|
||||||
|
if (out_avg_diff)
|
||||||
|
{
|
||||||
|
*out_avg_diff = diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
ET_DBG("avg_start=%.3f, avg_end=%.3f, diff=%.3f, threshold=%.3f", avg_start, avg_end, diff,
|
||||||
|
trend->config.threshold);
|
||||||
|
|
||||||
|
// 增加数据窗口信息输出
|
||||||
|
ET_DBG("[TREND] Window data (%d samples):", window_size);
|
||||||
|
for (uint8_t i = 0; i < window_size; i++)
|
||||||
|
{
|
||||||
|
ET_DBG("[%d]: %.3f", i, input[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出过滤后的有效数据
|
||||||
|
ET_DBG("[TREND] Valid data after filtering (%d samples):", valid_count);
|
||||||
|
for (uint8_t i = 0; i < valid_count; i++)
|
||||||
|
{
|
||||||
|
ET_DBG("[%d]: %.3f", i, valid_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ET_INFO("[TREND] Result: %d (avg_start=%.3f, avg_end=%.3f, diff=%.3f)",
|
||||||
|
(diff > trend->config.threshold) ? TREND_INCREASING
|
||||||
|
: (diff < -trend->config.threshold) ? TREND_DECREASING
|
||||||
|
: TREND_STABLE,
|
||||||
|
avg_start, avg_end, diff);
|
||||||
|
|
||||||
|
// Determine trend
|
||||||
|
if (diff > trend->config.threshold)
|
||||||
|
{
|
||||||
|
return TREND_INCREASING;
|
||||||
|
}
|
||||||
|
else if (diff < -trend->config.threshold)
|
||||||
|
{
|
||||||
|
return TREND_DECREASING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TREND_STABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int et_trend_init(et_trend_t *trend, et_trend_config_t *cfg)
|
||||||
|
{
|
||||||
|
if (!trend || !cfg || cfg->window_size < ET_TREND_WINDOW_MIN || cfg->window_size > ET_TREND_WINDOW_MAX)
|
||||||
|
{
|
||||||
|
ET_ERR("Invalid input: cfg=%p, window_size=%d", cfg, cfg ? cfg->window_size : 0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Initialize configuration
|
||||||
|
trend->config.window_size = cfg->window_size;
|
||||||
|
trend->config.threshold = cfg->threshold;
|
||||||
|
trend->config.outlier_mode = cfg->outlier_mode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
74
etk/src/algorithm/trend/src/et_trend.h
Normal file
74
etk/src/algorithm/trend/src/et_trend.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* @Author: mypx
|
||||||
|
* @Date: 2025-08-07 13:14:49
|
||||||
|
* @LastEditTime: 2025-08-14 11:15:00
|
||||||
|
* @LastEditors: mypx mypx_coder@163.com
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
#ifndef __ET_TREND_H__
|
||||||
|
#define __ET_TREND_H__
|
||||||
|
#include "etk_utils.h"
|
||||||
|
|
||||||
|
#define ET_TREND_WINDOW_MIN 3
|
||||||
|
#define ET_TREND_WINDOW_MAX 64
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
TREND_INVALID = -1, // Invalid input parameters
|
||||||
|
TREND_STABLE = 0, // Stable trend
|
||||||
|
TREND_INCREASING, // Increasing trend
|
||||||
|
TREND_DECREASING, // Decreasing trend
|
||||||
|
TREND_NO_VALID_DATA // Insufficient valid data (all outliers)
|
||||||
|
} ET_TrendResult;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
OUTLIER_NONE = 0, // No outlier filtering
|
||||||
|
OUTLIER_MAX, // Filter only maximum values
|
||||||
|
OUTLIER_MIN, // Filter only minimum values
|
||||||
|
OUTLIER_BOTH // Filter both maximum and minimum values
|
||||||
|
} ET_OutlierMode;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t window_size; // Window size for trend determination
|
||||||
|
float threshold; // Trend determination threshold
|
||||||
|
ET_OutlierMode outlier_mode; // Outlier processing mode
|
||||||
|
}et_trend_config_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const float *data; // Input data array
|
||||||
|
uint16_t length; // Total data length
|
||||||
|
et_trend_config_t config; // Trend configuration parameters
|
||||||
|
} et_trend_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Detect trend in time series data
|
||||||
|
*
|
||||||
|
* This function analyzes the trend of input data by comparing the average values
|
||||||
|
* of the first and second halves of the data within a specified window.
|
||||||
|
* It can optionally filter out outliers before performing the trend analysis.
|
||||||
|
*
|
||||||
|
* @param cfg Pointer to the trend detection configuration structure
|
||||||
|
* @param out_avg_diff Pointer to store the average difference (can be NULL)
|
||||||
|
* @return ET_TrendResult Trend detection result
|
||||||
|
* - TREND_INVALID: Invalid input parameters
|
||||||
|
* - TREND_STABLE: Stable trend (difference within threshold)
|
||||||
|
* - TREND_INCREASING: Increasing trend (difference > threshold)
|
||||||
|
* - TREND_DECREASING: Decreasing trend (difference < -threshold)
|
||||||
|
* - TREND_NO_VALID_DATA: Insufficient valid data after outlier filtering
|
||||||
|
*/
|
||||||
|
ET_TrendResult et_trend_detect(const et_trend_t *cfg, float *out_avg_diff);
|
||||||
|
|
||||||
|
int et_trend_init(et_trend_t *trend, et_trend_config_t *cfg);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
@ -1 +0,0 @@
|
|||||||
Subproject commit 7df6f11fb0e07dc5d5101f43f1a0c372147f137c
|
|
||||||
2461
nanoMODBUS/nanomodbus.c
Normal file
2461
nanoMODBUS/nanomodbus.c
Normal file
File diff suppressed because it is too large
Load Diff
556
nanoMODBUS/nanomodbus.h
Normal file
556
nanoMODBUS/nanomodbus.h
Normal file
@ -0,0 +1,556 @@
|
|||||||
|
/*
|
||||||
|
nanoMODBUS - A compact MODBUS RTU/TCP C library for microcontrollers
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Valerio De Benedetto (@debevv)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
|
||||||
|
/*! \mainpage nanoMODBUS - A compact MODBUS RTU/TCP C library for microcontrollers
|
||||||
|
* nanoMODBUS is a small C library that implements the Modbus protocol. It is especially useful in resource-constrained
|
||||||
|
* system like microcontrollers.
|
||||||
|
*
|
||||||
|
* GtiHub: <a href="https://github.com/debevv/nanoMODBUS">https://github.com/debevv/nanoMODBUS</a>
|
||||||
|
*
|
||||||
|
* API reference: \link nanomodbus.h \endlink
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NANOMODBUS_H
|
||||||
|
#define NANOMODBUS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanoMODBUS errors.
|
||||||
|
* Values <= 0 are library errors, > 0 are modbus exceptions.
|
||||||
|
*/
|
||||||
|
typedef enum nmbs_error {
|
||||||
|
// Library errors
|
||||||
|
NMBS_ERROR_INVALID_REQUEST = -8, /**< Received invalid request from client */
|
||||||
|
NMBS_ERROR_INVALID_UNIT_ID = -7, /**< Received invalid unit ID in response from server */
|
||||||
|
NMBS_ERROR_INVALID_TCP_MBAP = -6, /**< Received invalid TCP MBAP */
|
||||||
|
NMBS_ERROR_CRC = -5, /**< Received invalid CRC */
|
||||||
|
NMBS_ERROR_TRANSPORT = -4, /**< Transport error */
|
||||||
|
NMBS_ERROR_TIMEOUT = -3, /**< Read/write timeout occurred */
|
||||||
|
NMBS_ERROR_INVALID_RESPONSE = -2, /**< Received invalid response from server */
|
||||||
|
NMBS_ERROR_INVALID_ARGUMENT = -1, /**< Invalid argument provided */
|
||||||
|
NMBS_ERROR_NONE = 0, /**< No error */
|
||||||
|
|
||||||
|
// Modbus exceptions
|
||||||
|
NMBS_EXCEPTION_ILLEGAL_FUNCTION = 1, /**< Modbus exception 1 */
|
||||||
|
NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 2, /**< Modbus exception 2 */
|
||||||
|
NMBS_EXCEPTION_ILLEGAL_DATA_VALUE = 3, /**< Modbus exception 3 */
|
||||||
|
NMBS_EXCEPTION_SERVER_DEVICE_FAILURE = 4, /**< Modbus exception 4 */
|
||||||
|
} nmbs_error;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the nmbs_error is a modbus exception
|
||||||
|
* @e nmbs_error to check
|
||||||
|
*/
|
||||||
|
#define nmbs_error_is_exception(e) ((e) > 0 && (e) < 5)
|
||||||
|
|
||||||
|
#ifndef NMBS_BITFIELD_MAX
|
||||||
|
#define NMBS_BITFIELD_MAX 2000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* check coil count divisible by 8 */
|
||||||
|
#if ((NMBS_BITFIELD_MAX & 7) > 0)
|
||||||
|
#error "NMBS_BITFIELD_MAX must be divisible by 8"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NMBS_BITFIELD_BYTES_MAX (NMBS_BITFIELD_MAX / 8)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitfield consisting of 2000 coils/discrete inputs
|
||||||
|
*/
|
||||||
|
typedef uint8_t nmbs_bitfield[NMBS_BITFIELD_BYTES_MAX];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitfield consisting of 256 values
|
||||||
|
*/
|
||||||
|
typedef uint8_t nmbs_bitfield_256[32];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a bit from the nmbs_bitfield bf at position b
|
||||||
|
*/
|
||||||
|
#define nmbs_bitfield_read(bf, b) ((bool) ((bf)[(b) >> 3] & (0x1 << ((b) & (8 - 1)))))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a bit of the nmbs_bitfield bf at position b
|
||||||
|
*/
|
||||||
|
#define nmbs_bitfield_set(bf, b) (((bf)[(b) >> 3]) = (((bf)[(b) >> 3]) | (0x1 << ((b) & (8 - 1)))))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset a bit of the nmbs_bitfield bf at position b
|
||||||
|
*/
|
||||||
|
#define nmbs_bitfield_unset(bf, b) (((bf)[(b) >> 3]) = (((bf)[(b) >> 3]) & ~(0x1 << ((b) & (8 - 1)))))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write value v to the nmbs_bitfield bf at position b
|
||||||
|
*/
|
||||||
|
#define nmbs_bitfield_write(bf, b, v) ((bf)[(b) >> 3] = ((bf)[(b) >> 3] & ~(1 << ((b) & 7))) | ((v) << ((b) & 7)))
|
||||||
|
/**
|
||||||
|
* Reset (zero) the whole bitfield
|
||||||
|
*/
|
||||||
|
#define nmbs_bitfield_reset(bf) memset(bf, 0, sizeof(bf))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modbus transport type.
|
||||||
|
*/
|
||||||
|
typedef enum nmbs_transport {
|
||||||
|
NMBS_TRANSPORT_RTU = 1,
|
||||||
|
NMBS_TRANSPORT_TCP = 2,
|
||||||
|
} nmbs_transport;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanoMODBUS platform configuration struct.
|
||||||
|
* Passed to nmbs_server_create() and nmbs_client_create().
|
||||||
|
*
|
||||||
|
* read() and write() are the platform-specific methods that read/write data to/from a serial port or a TCP connection.
|
||||||
|
*
|
||||||
|
* Both methods should block until either:
|
||||||
|
* - `count` bytes of data are read/written
|
||||||
|
* - the byte timeout, with `byte_timeout_ms >= 0`, expires
|
||||||
|
*
|
||||||
|
* A value `< 0` for `byte_timeout_ms` means infinite timeout.
|
||||||
|
* With a value `== 0` for `byte_timeout_ms`, the method should read/write once in a non-blocking fashion and return immediately.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Their return value should be the number of bytes actually read/written, or `< 0` in case of error.
|
||||||
|
* A return value between `0` and `count - 1` will be treated as if a timeout occurred on the transport side. All other
|
||||||
|
* values will be treated as transport errors.
|
||||||
|
*
|
||||||
|
* Additionally, an optional crc_calc() function can be defined to override the default nanoMODBUS CRC calculation function.
|
||||||
|
*
|
||||||
|
* These methods accept a pointer to arbitrary user-data, which is the arg member of this struct.
|
||||||
|
* After the creation of an instance it can be changed with nmbs_set_platform_arg().
|
||||||
|
*/
|
||||||
|
typedef struct nmbs_platform_conf {
|
||||||
|
nmbs_transport transport; /*!< Transport type */
|
||||||
|
int32_t (*read)(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms,
|
||||||
|
void* arg); /*!< Bytes read transport function pointer */
|
||||||
|
int32_t (*write)(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms,
|
||||||
|
void* arg); /*!< Bytes write transport function pointer */
|
||||||
|
uint16_t (*crc_calc)(const uint8_t* data, uint32_t length,
|
||||||
|
void* arg); /*!< CRC calculation function pointer. Optional */
|
||||||
|
void* arg; /*!< User data, will be passed to functions above */
|
||||||
|
uint32_t initialized; /*!< Reserved, workaround for older user code not calling nmbs_platform_conf_create() */
|
||||||
|
} nmbs_platform_conf;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modbus server request callbacks. Passed to nmbs_server_create().
|
||||||
|
*
|
||||||
|
* These methods accept a pointer to arbitrary user data, which is the arg member of the nmbs_platform_conf that was passed
|
||||||
|
* to nmbs_server_create together with this struct.
|
||||||
|
*
|
||||||
|
* `unit_id` is the RTU unit ID of the request sender. It is always 0 on TCP.
|
||||||
|
*/
|
||||||
|
typedef struct nmbs_callbacks {
|
||||||
|
#ifndef NMBS_SERVER_DISABLED
|
||||||
|
#ifndef NMBS_SERVER_READ_COILS_DISABLED
|
||||||
|
nmbs_error (*read_coils)(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
|
||||||
|
nmbs_error (*read_discrete_inputs)(uint16_t address, uint16_t quantity, nmbs_bitfield inputs_out, uint8_t unit_id,
|
||||||
|
void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED) || !defined(NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED)
|
||||||
|
nmbs_error (*read_holding_registers)(uint16_t address, uint16_t quantity, uint16_t* registers_out, uint8_t unit_id,
|
||||||
|
void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
|
||||||
|
nmbs_error (*read_input_registers)(uint16_t address, uint16_t quantity, uint16_t* registers_out, uint8_t unit_id,
|
||||||
|
void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
|
||||||
|
nmbs_error (*write_single_coil)(uint16_t address, bool value, uint8_t unit_id, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
|
||||||
|
nmbs_error (*write_single_register)(uint16_t address, uint16_t value, uint8_t unit_id, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
|
||||||
|
nmbs_error (*write_multiple_coils)(uint16_t address, uint16_t quantity, const nmbs_bitfield coils, uint8_t unit_id,
|
||||||
|
void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED) || !defined(NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED)
|
||||||
|
nmbs_error (*write_multiple_registers)(uint16_t address, uint16_t quantity, const uint16_t* registers,
|
||||||
|
uint8_t unit_id, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_READ_FILE_RECORD_DISABLED
|
||||||
|
nmbs_error (*read_file_record)(uint16_t file_number, uint16_t record_number, uint16_t* registers, uint16_t count,
|
||||||
|
uint8_t unit_id, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
|
||||||
|
nmbs_error (*write_file_record)(uint16_t file_number, uint16_t record_number, const uint16_t* registers,
|
||||||
|
uint16_t count, uint8_t unit_id, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
|
||||||
|
#define NMBS_DEVICE_IDENTIFICATION_STRING_LENGTH 128
|
||||||
|
nmbs_error (*read_device_identification)(uint8_t object_id, char buffer[NMBS_DEVICE_IDENTIFICATION_STRING_LENGTH]);
|
||||||
|
nmbs_error (*read_device_identification_map)(nmbs_bitfield_256 map);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void* arg; // User data, will be passed to functions above
|
||||||
|
uint32_t initialized; // Reserved, workaround for older user code not calling nmbs_callbacks_create()
|
||||||
|
} nmbs_callbacks;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanoMODBUS client/server instance type. All struct members are to be considered private,
|
||||||
|
* it is not advisable to read/write them directly.
|
||||||
|
*/
|
||||||
|
typedef struct nmbs_t {
|
||||||
|
struct {
|
||||||
|
uint8_t buf[260];
|
||||||
|
uint16_t buf_idx;
|
||||||
|
|
||||||
|
uint8_t unit_id;
|
||||||
|
uint8_t fc;
|
||||||
|
uint16_t transaction_id;
|
||||||
|
bool broadcast;
|
||||||
|
bool ignored;
|
||||||
|
bool complete;
|
||||||
|
} msg;
|
||||||
|
|
||||||
|
nmbs_callbacks callbacks;
|
||||||
|
|
||||||
|
int32_t byte_timeout_ms;
|
||||||
|
int32_t read_timeout_ms;
|
||||||
|
|
||||||
|
nmbs_platform_conf platform;
|
||||||
|
|
||||||
|
uint8_t address_rtu;
|
||||||
|
uint8_t dest_address_rtu;
|
||||||
|
uint16_t current_tid;
|
||||||
|
} nmbs_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modbus broadcast address. Can be passed to nmbs_set_destination_rtu_address().
|
||||||
|
*/
|
||||||
|
static const uint8_t NMBS_BROADCAST_ADDRESS = 0;
|
||||||
|
|
||||||
|
/** Set the request/response timeout.
|
||||||
|
* If the target instance is a server, sets the timeout of the nmbs_server_poll() function.
|
||||||
|
* If the target instance is a client, sets the response timeout after sending a request. In case of timeout,
|
||||||
|
* the called method will return NMBS_ERROR_TIMEOUT.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param timeout_ms timeout in milliseconds. If < 0, the timeout is disabled.
|
||||||
|
*/
|
||||||
|
void nmbs_set_read_timeout(nmbs_t* nmbs, int32_t timeout_ms);
|
||||||
|
|
||||||
|
/** Set the timeout between the reception/transmission of two consecutive bytes.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param timeout_ms timeout in milliseconds. If < 0, the timeout is disabled.
|
||||||
|
*/
|
||||||
|
void nmbs_set_byte_timeout(nmbs_t* nmbs, int32_t timeout_ms);
|
||||||
|
|
||||||
|
/** Create a new nmbs_platform_conf struct.
|
||||||
|
* @param platform_conf pointer to the nmbs_platform_conf instance
|
||||||
|
*/
|
||||||
|
void nmbs_platform_conf_create(nmbs_platform_conf* platform_conf);
|
||||||
|
|
||||||
|
/** Set the pointer to user data argument passed to platform functions.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param arg user data argument
|
||||||
|
*/
|
||||||
|
void nmbs_set_platform_arg(nmbs_t* nmbs, void* arg);
|
||||||
|
|
||||||
|
#ifndef NMBS_SERVER_DISABLED
|
||||||
|
/** Create a new nmbs_callbacks struct.
|
||||||
|
* @param callbacks pointer to the nmbs_callbacks instance
|
||||||
|
*/
|
||||||
|
void nmbs_callbacks_create(nmbs_callbacks* callbacks);
|
||||||
|
|
||||||
|
/** Create a new Modbus server.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance where the client will be created.
|
||||||
|
* @param address_rtu RTU address of this server. Can be 0 if transport is not RTU.
|
||||||
|
* @param platform_conf nmbs_platform_conf struct with platform configuration. It may be discarded after calling this method.
|
||||||
|
* @param callbacks nmbs_callbacks struct with server request callbacks. It may be discarded after calling this method.
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, NMBS_ERROR_INVALID_ARGUMENT otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_server_create(nmbs_t* nmbs, uint8_t address_rtu, const nmbs_platform_conf* platform_conf,
|
||||||
|
const nmbs_callbacks* callbacks);
|
||||||
|
|
||||||
|
/** Handle incoming requests to the server.
|
||||||
|
* This function should be called in a loop in order to serve any incoming request. Its maximum duration, in case of no
|
||||||
|
* received request, is the value set with nmbs_set_read_timeout() (unless set to < 0).
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_server_poll(nmbs_t* nmbs);
|
||||||
|
|
||||||
|
/** Set the pointer to user data argument passed to server request callbacks.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param arg user data argument
|
||||||
|
*/
|
||||||
|
void nmbs_set_callbacks_arg(nmbs_t* nmbs, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NMBS_CLIENT_DISABLED
|
||||||
|
/** Create a new Modbus client.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance where the client will be created.
|
||||||
|
* @param platform_conf nmbs_platform_conf struct with platform configuration. It may be discarded after calling this method.
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, NMBS_ERROR_INVALID_ARGUMENT otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_client_create(nmbs_t* nmbs, const nmbs_platform_conf* platform_conf);
|
||||||
|
|
||||||
|
/** Set the recipient server address of the next request on RTU transport.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address server address
|
||||||
|
*/
|
||||||
|
void nmbs_set_destination_rtu_address(nmbs_t* nmbs, uint8_t address);
|
||||||
|
|
||||||
|
/** Send a FC 01 (0x01) Read Coils request
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address starting address
|
||||||
|
* @param quantity quantity of coils
|
||||||
|
* @param coils_out nmbs_bitfield where the coils will be stored
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_coils(nmbs_t* nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield coils_out);
|
||||||
|
|
||||||
|
/** Send a FC 02 (0x02) Read Discrete Inputs request
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address starting address
|
||||||
|
* @param quantity quantity of inputs
|
||||||
|
* @param inputs_out nmbs_bitfield where the discrete inputs will be stored
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_discrete_inputs(nmbs_t* nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield inputs_out);
|
||||||
|
|
||||||
|
/** Send a FC 03 (0x03) Read Holding Registers request
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address starting address
|
||||||
|
* @param quantity quantity of registers
|
||||||
|
* @param registers_out array where the registers will be stored
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_holding_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, uint16_t* registers_out);
|
||||||
|
|
||||||
|
/** Send a FC 04 (0x04) Read Input Registers request
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address starting address
|
||||||
|
* @param quantity quantity of registers
|
||||||
|
* @param registers_out array where the registers will be stored
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_input_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, uint16_t* registers_out);
|
||||||
|
|
||||||
|
/** Send a FC 05 (0x05) Write Single Coil request
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address coil address
|
||||||
|
* @param value coil value
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_write_single_coil(nmbs_t* nmbs, uint16_t address, bool value);
|
||||||
|
|
||||||
|
/** Send a FC 06 (0x06) Write Single Register request
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address register address
|
||||||
|
* @param value register value
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_write_single_register(nmbs_t* nmbs, uint16_t address, uint16_t value);
|
||||||
|
|
||||||
|
/** Send a FC 15 (0x0F) Write Multiple Coils
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address starting address
|
||||||
|
* @param quantity quantity of coils
|
||||||
|
* @param coils bitfield of coils values
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_write_multiple_coils(nmbs_t* nmbs, uint16_t address, uint16_t quantity, const nmbs_bitfield coils);
|
||||||
|
|
||||||
|
/** Send a FC 16 (0x10) Write Multiple Registers
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param address starting address
|
||||||
|
* @param quantity quantity of registers
|
||||||
|
* @param registers array of registers values
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_write_multiple_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, const uint16_t* registers);
|
||||||
|
|
||||||
|
/** Send a FC 20 (0x14) Read File Record
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param file_number file number (1 to 65535)
|
||||||
|
* @param record_number record number from file (0000 to 9999)
|
||||||
|
* @param registers array of registers to read
|
||||||
|
* @param count count of registers
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_file_record(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number, uint16_t* registers,
|
||||||
|
uint16_t count);
|
||||||
|
|
||||||
|
/** Send a FC 21 (0x15) Write File Record
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param file_number file number (1 to 65535)
|
||||||
|
* @param record_number record number from file (0000 to 9999)
|
||||||
|
* @param registers array of registers to write
|
||||||
|
* @param count count of registers
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_write_file_record(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number, const uint16_t* registers,
|
||||||
|
uint16_t count);
|
||||||
|
|
||||||
|
/** Send a FC 23 (0x17) Read Write Multiple registers
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param read_address starting read address
|
||||||
|
* @param read_quantity quantity of registers to read
|
||||||
|
* @param registers_out array where the read registers will be stored
|
||||||
|
* @param write_address starting write address
|
||||||
|
* @param write_quantity quantity of registers to write
|
||||||
|
* @param registers array of registers values to write
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_write_registers(nmbs_t* nmbs, uint16_t read_address, uint16_t read_quantity,
|
||||||
|
uint16_t* registers_out, uint16_t write_address, uint16_t write_quantity,
|
||||||
|
const uint16_t* registers);
|
||||||
|
|
||||||
|
/** Send a FC 43 / 14 (0x2B / 0x0E) Read Device Identification to read all Basic Object Id values (Read Device ID code 1)
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param vendor_name char array where the read VendorName value will be stored
|
||||||
|
* @param product_code char array where the read ProductCode value will be stored
|
||||||
|
* @param major_minor_revision char array where the read MajorMinorRevision value will be stored
|
||||||
|
* @param buffers_length length of every char array
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_device_identification_basic(nmbs_t* nmbs, char* vendor_name, char* product_code,
|
||||||
|
char* major_minor_revision, uint8_t buffers_length);
|
||||||
|
|
||||||
|
/** Send a FC 43 / 14 (0x2B / 0x0E) Read Device Identification to read all Regular Object Id values (Read Device ID code 2)
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param vendor_url char array where the read VendorUrl value will be stored
|
||||||
|
* @param product_name char array where the read ProductName value will be stored
|
||||||
|
* @param model_name char array where the read ModelName value will be stored
|
||||||
|
* @param user_application_name char array where the read UserApplicationName value will be stored
|
||||||
|
* @param buffers_length length of every char array
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_device_identification_regular(nmbs_t* nmbs, char* vendor_url, char* product_name, char* model_name,
|
||||||
|
char* user_application_name, uint8_t buffers_length);
|
||||||
|
|
||||||
|
/** Send a FC 43 / 14 (0x2B / 0x0E) Read Device Identification to read all Extended Object Id values (Read Device ID code 3)
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param object_id_start Object Id to start reading from
|
||||||
|
* @param ids array where the read Object Ids will be stored
|
||||||
|
* @param buffers array of char arrays where the read values will be stored
|
||||||
|
* @param ids_length length of the ids array and buffers array
|
||||||
|
* @param buffer_length length of each char array
|
||||||
|
* @param objects_count_out retrieved Object Ids count
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, NMBS_INVALID_ARGUMENT if buffers_count is less than retrieved Object Ids count,
|
||||||
|
* other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_device_identification_extended(nmbs_t* nmbs, uint8_t object_id_start, uint8_t* ids, char** buffers,
|
||||||
|
uint8_t ids_length, uint8_t buffer_length,
|
||||||
|
uint8_t* objects_count_out);
|
||||||
|
|
||||||
|
/** Send a FC 43 / 14 (0x2B / 0x0E) Read Device Identification to retrieve a single Object Id value (Read Device ID code 4)
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param object_id requested Object Id
|
||||||
|
* @param buffer char array where the resulting value will be stored
|
||||||
|
* @param buffer_length length of the char array
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_read_device_identification(nmbs_t* nmbs, uint8_t object_id, char* buffer, uint8_t buffer_length);
|
||||||
|
|
||||||
|
/** Send a raw Modbus PDU.
|
||||||
|
* CRC on RTU will be calculated and sent by this function.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param fc request function code
|
||||||
|
* @param data request data. It's up to the caller to convert this data to network byte order
|
||||||
|
* @param data_len length of the data parameter
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_send_raw_pdu(nmbs_t* nmbs, uint8_t fc, const uint8_t* data, uint16_t data_len);
|
||||||
|
|
||||||
|
/** Receive a raw response Modbus PDU.
|
||||||
|
* @param nmbs pointer to the nmbs_t instance
|
||||||
|
* @param data_out response data. It's up to the caller to convert this data to host byte order. Can be NULL.
|
||||||
|
* @param data_out_len number of bytes to receive
|
||||||
|
*
|
||||||
|
* @return NMBS_ERROR_NONE if successful, other errors otherwise.
|
||||||
|
*/
|
||||||
|
nmbs_error nmbs_receive_raw_pdu_response(nmbs_t* nmbs, uint8_t* data_out, uint8_t data_out_len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Calculate the Modbus CRC of some data.
|
||||||
|
* @param data Data
|
||||||
|
* @param length Length of the data
|
||||||
|
*/
|
||||||
|
uint16_t nmbs_crc_calc(const uint8_t* data, uint32_t length, void* arg);
|
||||||
|
|
||||||
|
#ifndef NMBS_STRERROR_DISABLED
|
||||||
|
/** Convert a nmbs_error to string
|
||||||
|
* @param error error to be converted
|
||||||
|
*
|
||||||
|
* @return string representation of the error
|
||||||
|
*/
|
||||||
|
const char* nmbs_strerror(nmbs_error error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //NANOMODBUS_H
|
||||||
Loading…
Reference in New Issue
Block a user