Added MSTP extended frame transmit. (#531)
* Added MSTP extended frame transmit to bacnet/datalink/mstp.c and ports/stm32f4xx/dlmstp.c modules. * Changed RS485_Send_Frame to MSTP_Send_Frame in mstp.c --------- Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
+24
-11
@@ -18,13 +18,18 @@ The git repositories are hosted at the following sites:
|
||||
|
||||
### Added
|
||||
|
||||
- Added MSTP extended frames to src/datalink/mstp.c module used by mstpcap (#529)
|
||||
- Added MSTP extended frames transmit to src/datalink/mstp.c
|
||||
and ports/stm32f4xx/dlmstp.c modules (#531)
|
||||
- Added MSTP extended frames to src/datalink/mstp.c module
|
||||
used by mstpcap (#529)
|
||||
- Added menu to release script (#506)
|
||||
|
||||
### Changed
|
||||
|
||||
- Change SubscribeCOV Cancellations to always reply with Result+ (#526)
|
||||
- Allow processing of Who-Has when DCC initiation is disabled
|
||||
- Changed SubscribeCOV Cancellations to always reply with Result+ (#526)
|
||||
- Changed Who-Has to process when DCC initiation is disabled
|
||||
- Changed mstp.c external API to remove rs485.h dependency
|
||||
for send frame. (#531)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -55,30 +60,38 @@ The git repositories are hosted at the following sites:
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed WriteProperty string property checker to ignore length check with zero option.(#504)
|
||||
- Changed WriteProperty string property checker to ignore length
|
||||
check with zero option.(#504)
|
||||
|
||||
## [1.3.0] - 2023-09-28
|
||||
|
||||
### Added
|
||||
|
||||
- Added [feature#14] EventTimeStamp decoding from ReadPropertyMultiple app. (#503)
|
||||
- Added Channel, Color, Color Temperature, & Lighting Output demo app with Blinkt! (#503)
|
||||
- Added pipeline build of piface and blinkt apps with Raspberry Pi OS image. (#503)
|
||||
- Added Channel, Color, Color Temperature, & Lighting Output demo
|
||||
app with Blinkt! (#503)
|
||||
- Added pipeline build of piface and blinkt apps with Raspberry Pi
|
||||
OS image. (#503)
|
||||
- Added linear interpolation library functions used in fading and ramping. (#503)
|
||||
|
||||
### Changed
|
||||
|
||||
- Added Device timer API to feed elapsed milliseconds to children objects. (#503)
|
||||
- Changed gitignore to ease the maintainenance of source files in app folder. (#503)
|
||||
- Changed example server app device simulator to use mstimer instead of OS time. (#503)
|
||||
- Added Device timer API to feed elapsed milliseconds to children
|
||||
objects. (#503)
|
||||
- Changed gitignore to ease the maintainenance of source files in
|
||||
app folder. (#503)
|
||||
- Changed example server app device simulator to use mstimer instead
|
||||
of OS time. (#503)
|
||||
- Changed example channel object to be dynamically created or deleted. (#503)
|
||||
- Changed example channel object to handle color & color temperature objects. (#503)
|
||||
- Changed example channel object to handle color & color temperature
|
||||
objects. (#503)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed datetime decode of invalid application tag. (#495)
|
||||
- Fixed extraneous SO_BINDTODEVICE error message in Linux BIP. (#498)
|
||||
- Fixed example Color, Color Temperature, and Lighting object fade, ramp, and step. (#503)
|
||||
- Fixed example Color, Color Temperature, and Lighting object fade,
|
||||
ramp, and step. (#503)
|
||||
- Fixed and secured BACnetXYcolor and ColorCommand codecs. (#503)
|
||||
|
||||
## [1.2.0] - 2023-09-11
|
||||
|
||||
@@ -430,6 +430,22 @@ uint16_t MSTP_Get_Send(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
(void)mstp_port;
|
||||
(void)buffer;
|
||||
(void)nbytes;
|
||||
}
|
||||
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
|
||||
@@ -164,6 +164,7 @@ static uint32_t Timer_Silence(void *pArg)
|
||||
struct timespec now, diff;
|
||||
int32_t res;
|
||||
|
||||
(void)pArg;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
timespec_subtract(&diff, &now, &start);
|
||||
res = ((diff.tv_sec) * 1000 + (diff.tv_nsec) / 1000000);
|
||||
@@ -173,6 +174,7 @@ static uint32_t Timer_Silence(void *pArg)
|
||||
|
||||
static void Timer_Silence_Reset(void *pArg)
|
||||
{
|
||||
(void)pArg;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
}
|
||||
|
||||
@@ -412,6 +414,20 @@ uint16_t MSTP_Get_Send(
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
RS485_Send_Frame(mstp_port, buffer, nbytes);
|
||||
}
|
||||
|
||||
static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
uint16_t request_pdu_len,
|
||||
uint8_t src_address,
|
||||
|
||||
@@ -384,6 +384,20 @@ uint16_t MSTP_Get_Send(
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
RS485_Send_Frame(mstp_port, buffer, nbytes);
|
||||
}
|
||||
|
||||
bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
uint16_t request_pdu_len,
|
||||
uint8_t src_address,
|
||||
|
||||
@@ -100,6 +100,22 @@ uint16_t MSTP_Get_Send(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
(void)mstp_port;
|
||||
(void)buffer;
|
||||
(void)nbytes;
|
||||
}
|
||||
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
|
||||
@@ -125,6 +125,22 @@ uint16_t MSTP_Get_Send(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
(void)mstp_port;
|
||||
(void)buffer;
|
||||
(void)nbytes;
|
||||
}
|
||||
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
|
||||
@@ -60,6 +60,7 @@ BACNET_SRC = \
|
||||
$(BACNET_CORE)/bacint.c \
|
||||
$(BACNET_CORE)/bacreal.c \
|
||||
$(BACNET_CORE)/bacstr.c \
|
||||
$(BACNET_CORE)/datalink/cobs.c \
|
||||
$(BACNET_CORE)/datalink/crc.c \
|
||||
$(BACNET_CORE)/datetime.c \
|
||||
$(BACNET_CORE)/dcc.c \
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
</group>
|
||||
<group>
|
||||
<name>BACnet Datalink</name>
|
||||
<file>
|
||||
<name>$PROJ_DIR$\..\..\src\bacnet\datalink\cobs.c</name>
|
||||
</file>
|
||||
<file>
|
||||
<name>$PROJ_DIR$\..\..\src\bacnet\datalink\crc.c</name>
|
||||
</file>
|
||||
|
||||
+83
-22
@@ -37,10 +37,11 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/datalink/cobs.h"
|
||||
#include "bacnet/datalink/crc.h"
|
||||
#include "bacnet/datalink/dlmstp.h"
|
||||
#include "bacnet/datalink/mstpdef.h"
|
||||
#include "rs485.h"
|
||||
#include "bacnet/datalink/crc.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/bytes.h"
|
||||
@@ -363,6 +364,10 @@ static uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
uint16_t crc16 = 0xFFFF;
|
||||
/* used to load the data portion of the frame */
|
||||
uint16_t index = 0;
|
||||
/* length of the COBS encoded frame */
|
||||
uint16_t cobs_len;
|
||||
/* true for COBS BACnet frames */
|
||||
bool cobs_bacnet_frame = false;
|
||||
|
||||
/* not enough to do a header */
|
||||
if (buffer_size < 8)
|
||||
@@ -381,25 +386,58 @@ static uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
buffer[6] = data_len & 0xFF;
|
||||
crc8 = CRC_Calc_Header(buffer[6], crc8);
|
||||
buffer[7] = ~crc8;
|
||||
|
||||
index = 8;
|
||||
while (data_len && data && (index < buffer_size)) {
|
||||
buffer[index] = *data;
|
||||
crc16 = CRC_Calc_Data(buffer[index], crc16);
|
||||
data++;
|
||||
index++;
|
||||
data_len--;
|
||||
}
|
||||
/* append the data CRC if necessary */
|
||||
if (index > 8) {
|
||||
if ((index + 2) <= buffer_size) {
|
||||
crc16 = ~crc16;
|
||||
buffer[index] = crc16 & 0xFF; /* LSB first */
|
||||
index++;
|
||||
buffer[index] = crc16 >> 8;
|
||||
index++;
|
||||
} else
|
||||
|
||||
if ((data_len > 501) || ((frame_type >= Nmin_COBS_type) &&
|
||||
(frame_type <= Nmax_COBS_type))) {
|
||||
/* COBS encoded frame */
|
||||
if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) {
|
||||
frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY;
|
||||
cobs_bacnet_frame = true;
|
||||
} else if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) {
|
||||
frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY;
|
||||
cobs_bacnet_frame = true;
|
||||
} else if ((frame_type < Nmin_COBS_type) ||
|
||||
(frame_type > Nmax_COBS_type)) {
|
||||
/* I'm sorry, Dave, I'm afraid I can't do that. */
|
||||
return 0;
|
||||
}
|
||||
cobs_len = cobs_frame_encode(buffer, buffer_size, data, data_len);
|
||||
/* check the results of COBs encoding for validity */
|
||||
if (cobs_bacnet_frame) {
|
||||
if (cobs_len < Nmin_COBS_length_BACnet) {
|
||||
return 0;
|
||||
} else if (cobs_len > Nmax_COBS_length_BACnet) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (cobs_len < Nmin_COBS_length) {
|
||||
return 0;
|
||||
} else if (cobs_len > Nmax_COBS_length) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* for COBS, we must subtract two before use as the
|
||||
MS/TP frame length field since CRC32 is 2 bytes longer
|
||||
than CRC16 in original MSTP and non-COBS devices need
|
||||
to be able to ingest the entire frame */
|
||||
index = index + cobs_len - 2;
|
||||
} else if (data_len > 0) {
|
||||
while (data_len && data && (index < buffer_size)) {
|
||||
buffer[index] = *data;
|
||||
crc16 = CRC_Calc_Data(buffer[index], crc16);
|
||||
data++;
|
||||
index++;
|
||||
data_len--;
|
||||
}
|
||||
if ((index + 2) > buffer_size) {
|
||||
return 0;
|
||||
}
|
||||
crc16 = ~crc16;
|
||||
buffer[index] = crc16 & 0xFF; /* LSB first */
|
||||
index++;
|
||||
buffer[index] = crc16 >> 8;
|
||||
index++;
|
||||
}
|
||||
|
||||
return index; /* returns the frame length */
|
||||
@@ -653,13 +691,37 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
} else if (Index == DataLength) {
|
||||
/* CRC1 */
|
||||
DataCRC = CRC_Calc_Data(DataRegister, DataCRC);
|
||||
if (Index < InputBufferSize) {
|
||||
InputBuffer[Index] = DataRegister;
|
||||
}
|
||||
Index++;
|
||||
} else if (Index == (DataLength + 1)) {
|
||||
/* CRC2 */
|
||||
DataCRC = CRC_Calc_Data(DataRegister, DataCRC);
|
||||
/* STATE DATA CRC - no need for new state */
|
||||
/* indicate the complete reception of a valid frame */
|
||||
if (DataCRC == 0xF0B8) {
|
||||
if (Index < InputBufferSize) {
|
||||
InputBuffer[Index] = DataRegister;
|
||||
}
|
||||
if ((FrameType >= Nmin_COBS_type) &&
|
||||
(FrameType <= Nmax_COBS_type)) {
|
||||
if (((Index+1) < InputBufferSize) &&
|
||||
cobs_frame_decode(
|
||||
&InputBuffer[Index + 1],
|
||||
InputBufferSize,
|
||||
InputBuffer, Index + 1)) {
|
||||
if (Receive_State ==
|
||||
MSTP_RECEIVE_STATE_DATA) {
|
||||
/* ForUs */
|
||||
MSTP_Flag.ReceivedValidFrame = true;
|
||||
} else {
|
||||
/* NotForUs */
|
||||
MSTP_Flag.ReceivedValidFrameNotForUs = true;
|
||||
}
|
||||
} else {
|
||||
MSTP_Flag.ReceivedInvalidFrame = true;
|
||||
}
|
||||
} else if (DataCRC == 0xF0B8) {
|
||||
/* STATE DATA CRC - no need for new state */
|
||||
/* indicate the complete reception of a valid frame */
|
||||
if (Receive_State == MSTP_RECEIVE_STATE_DATA) {
|
||||
/* ForUs */
|
||||
MSTP_Flag.ReceivedValidFrame = true;
|
||||
@@ -678,7 +740,6 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
Frame_Rx_Callback(source, destination, frame,
|
||||
InputBuffer, DataLength);
|
||||
}
|
||||
|
||||
} else {
|
||||
MSTP_Flag.ReceivedInvalidFrame = true;
|
||||
}
|
||||
|
||||
@@ -296,6 +296,20 @@ uint16_t MSTP_Get_Send(
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
RS485_Send_Frame(mstp_port, buffer, nbytes);
|
||||
}
|
||||
|
||||
bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
uint16_t request_pdu_len,
|
||||
uint8_t src_address,
|
||||
|
||||
@@ -281,6 +281,20 @@ uint16_t MSTP_Get_Send(
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
RS485_Send_Frame(mstp_port, buffer, nbytes);
|
||||
}
|
||||
|
||||
static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
uint16_t request_pdu_len,
|
||||
uint8_t src_address,
|
||||
|
||||
@@ -130,6 +130,22 @@ uint16_t MSTP_Get_Send(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MSTP frame
|
||||
* @param mstp_port - port specific data
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
(void)mstp_port;
|
||||
(void)buffer;
|
||||
(void)nbytes;
|
||||
}
|
||||
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
|
||||
@@ -77,7 +77,7 @@ uint32_t cobs_crc32k(uint8_t dataValue, uint32_t crc32kValue)
|
||||
|
||||
/**
|
||||
* @brief Encodes 'length' octets of data located at 'from' and
|
||||
* writes one or more COBS code blocks at 'to', removing
|
||||
* writes one or more COBS code blocks at 'buffer', removing
|
||||
* any 0x55 octets that may present be in the encoded data.
|
||||
* @param buffer - encoded buffer
|
||||
* @param buffer_size - encoded buffer size
|
||||
@@ -154,7 +154,7 @@ size_t cobs_encode(uint8_t *buffer,
|
||||
}
|
||||
/**
|
||||
* @brief Encodes 'length' octets of client data located at 'from' and writes
|
||||
* the COBS-encoded Encoded Data and Encoded CRC-32K fields at 'to'.
|
||||
* the COBS-encoded Encoded Data and Encoded CRC-32K fields at 'buffer'.
|
||||
* @param buffer - encoded buffer
|
||||
* @param buffer_size - encoded buffer size
|
||||
* @param from - buffer to encode
|
||||
@@ -206,7 +206,7 @@ size_t cobs_frame_encode(
|
||||
|
||||
/**
|
||||
* @brief Decodes 'length' octets of data located at 'from' and
|
||||
* writes the original client data at 'to', restoring any
|
||||
* writes the original client data at 'buffer', restoring any
|
||||
* 'mask' octets that may present in the encoded data.
|
||||
* @param buffer - decoded buffer
|
||||
* @param buffer_size - decoded buffer size
|
||||
@@ -266,7 +266,7 @@ size_t cobs_decode(uint8_t *buffer,
|
||||
|
||||
/**
|
||||
* Decodes Encoded Data and Encoded CRC-32K fields at 'from' and
|
||||
* writes the decoded client data at 'to'. Assumes 'length' contains
|
||||
* writes the decoded client data at 'buffer'. Assumes 'length' contains
|
||||
* the actual combined length of these fields in octets (that is, the
|
||||
* MS/TP header Length field plus two).
|
||||
* @param buffer - decoded buffer
|
||||
|
||||
@@ -33,6 +33,38 @@
|
||||
/* inclusive extra bytes needed for APDU */
|
||||
#define COBS_ENCODED_SIZE(a) ((a)+((a)/254)+1)
|
||||
|
||||
|
||||
/* The first COBS-encoded Frame Type value: 32. */
|
||||
#define Nmin_COBS_type 32
|
||||
/* The last COBS-encoded Frame Type value: 127. */
|
||||
#define Nmax_COBS_type 127
|
||||
/* The minimum valid Length value of any COBS-encoded frame: 5.
|
||||
The theoretical minimum Length is calculated as follows:
|
||||
COBS-encoded frames must contain at least one data octet.
|
||||
The minimum COBS encoding overhead for the Encoded Data
|
||||
field is one octet. The size of the Encoded CRC-32K field
|
||||
is always five octets. Adding the lengths of these fields
|
||||
and subtracting two (for backward compatibility) results
|
||||
in a minimum Length value of five (1 + 1 + 5 - 2). In practice,
|
||||
the minimum Length value is determined by the network-layer
|
||||
client and is likely to be larger (e.g., for BACnet the minimum
|
||||
Length is 502 + 1 + 3 = 506. */
|
||||
#define Nmin_COBS_length 5
|
||||
#define Nmin_COBS_length_BACnet 506
|
||||
/* The maximum valid Length value of any COBS-encoded frame: 2043.
|
||||
The theoretical maximum Length is calculated as follows:
|
||||
the largest data parameter that any future network client
|
||||
may specify is 2032 octets (this is near the limit of the
|
||||
CRC-32K's maximum error-detection capability). The worst-case
|
||||
COBS encoding overhead for the Encoded Data field would be
|
||||
2032 / 254 = 8 octets. Adding in the size adjustment for the
|
||||
Encoded CRC-32K results in a maximum Length value of
|
||||
2032 + 8 + 3 = 2043. In practice, the maximum Length value is
|
||||
determined by the network-layer client and is likely to be
|
||||
smaller (e.g., for BACnet the maximum Length is 1497 + 6 + 3 = 1506. */
|
||||
#define Nmax_COBS_length 2043
|
||||
#define Nmax_COBS_length_BACnet 1506
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
+115
-47
@@ -51,12 +51,11 @@
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include "bacnet/datalink/cobs.h"
|
||||
#include "bacnet/datalink/crc.h"
|
||||
#include "bacnet/datalink/mstp.h"
|
||||
#include "crc.h"
|
||||
#include "rs485.h"
|
||||
#include "bacnet/datalink/mstptext.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/datalink/cobs.h"
|
||||
|
||||
#ifndef DEBUG_ENABLED
|
||||
#define DEBUG_ENABLED 0
|
||||
@@ -199,23 +198,48 @@ void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t MSTP_Create_Frame(uint8_t *buffer, /* where frame is loaded */
|
||||
uint16_t buffer_len, /* amount of space available */
|
||||
uint8_t frame_type, /* type of frame to send - see defines */
|
||||
uint8_t destination, /* destination address */
|
||||
uint8_t source, /* source address */
|
||||
uint8_t *data, /* any data to be sent - may be null */
|
||||
/**
|
||||
* @brief Create an MS/TP Frame
|
||||
*
|
||||
* All MS/TP frames are of the following format:
|
||||
* Preamble: two octet preamble: X`55', X`FF'
|
||||
* Frame Type: one octet
|
||||
* Destination Address: one octet address
|
||||
* Source Address: one octet address
|
||||
* Length: two octets, most significant octet first, of the Data field
|
||||
* Header CRC: one octet
|
||||
* Data: (present only if Length is non-zero)
|
||||
* Data CRC: (present only if Length is non-zero) two octets,
|
||||
* least significant octet first
|
||||
* (pad): (optional) at most one octet of padding: X'FF'
|
||||
*
|
||||
* @param buffer - where frame is loaded
|
||||
* @param buffer_size - amount of space available in the buffer
|
||||
* @param frame_type - type of frame to send - see defines
|
||||
* @param destination - destination address
|
||||
* @param source - source address
|
||||
* @param data - any data to be sent - may be null
|
||||
* @param data_len - number of bytes of data (up to 501)
|
||||
* @return number of bytes encoded, or 0 on error
|
||||
*/
|
||||
uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
uint16_t buffer_size,
|
||||
uint8_t frame_type,
|
||||
uint8_t destination,
|
||||
uint8_t source,
|
||||
uint8_t *data,
|
||||
uint16_t data_len)
|
||||
{ /* number of bytes of data (up to 501) */
|
||||
{
|
||||
uint8_t crc8 = 0xFF; /* used to calculate the crc value */
|
||||
uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */
|
||||
uint16_t index = 0; /* used to load the data portion of the frame */
|
||||
uint16_t cobs_len; /* length of the COBS encoded frame */
|
||||
bool cobs_bacnet_frame = false; /* true for COBS BACnet frames */
|
||||
|
||||
/* not enough to do a header */
|
||||
if (buffer_len < 8) {
|
||||
if (buffer_size < 8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer[0] = 0x55;
|
||||
buffer[1] = 0xFF;
|
||||
buffer[2] = frame_type;
|
||||
@@ -229,46 +253,87 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer, /* where frame is loaded */
|
||||
buffer[6] = data_len & 0xFF;
|
||||
crc8 = CRC_Calc_Header(buffer[6], crc8);
|
||||
buffer[7] = ~crc8;
|
||||
|
||||
index = 8;
|
||||
while (data_len && data && (index < buffer_len)) {
|
||||
buffer[index] = *data;
|
||||
crc16 = CRC_Calc_Data(buffer[index], crc16);
|
||||
data++;
|
||||
index++;
|
||||
data_len--;
|
||||
}
|
||||
/* append the data CRC if necessary */
|
||||
if (index > 8) {
|
||||
if ((index + 2) <= buffer_len) {
|
||||
crc16 = ~crc16;
|
||||
buffer[index] = crc16 & 0xFF; /* LSB first */
|
||||
index++;
|
||||
buffer[index] = crc16 >> 8;
|
||||
index++;
|
||||
} else {
|
||||
|
||||
if ((data_len > 501) || ((frame_type >= Nmin_COBS_type) &&
|
||||
(frame_type <= Nmax_COBS_type))) {
|
||||
/* COBS encoded frame */
|
||||
if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) {
|
||||
frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY;
|
||||
cobs_bacnet_frame = true;
|
||||
} else if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) {
|
||||
frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY;
|
||||
cobs_bacnet_frame = true;
|
||||
} else if ((frame_type < Nmin_COBS_type) ||
|
||||
(frame_type > Nmax_COBS_type)) {
|
||||
/* I'm sorry, Dave, I'm afraid I can't do that. */
|
||||
return 0;
|
||||
}
|
||||
cobs_len = cobs_frame_encode(buffer, buffer_size, data, data_len);
|
||||
/* check the results of COBs encoding for validity */
|
||||
if (cobs_bacnet_frame) {
|
||||
if (cobs_len < Nmin_COBS_length_BACnet) {
|
||||
return 0;
|
||||
} else if (cobs_len > Nmax_COBS_length_BACnet) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (cobs_len < Nmin_COBS_length) {
|
||||
return 0;
|
||||
} else if (cobs_len > Nmax_COBS_length) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* for COBS, we must subtract two before use as the
|
||||
MS/TP frame length field since CRC32 is 2 bytes longer
|
||||
than CRC16 in original MSTP and non-COBS devices need
|
||||
to be able to ingest the entire frame */
|
||||
index = index + cobs_len - 2;
|
||||
} else if (data_len > 0) {
|
||||
while (data_len && data && (index < buffer_size)) {
|
||||
buffer[index] = *data;
|
||||
crc16 = CRC_Calc_Data(buffer[index], crc16);
|
||||
data++;
|
||||
index++;
|
||||
data_len--;
|
||||
}
|
||||
if ((index + 2) > buffer_size) {
|
||||
return 0;
|
||||
}
|
||||
crc16 = ~crc16;
|
||||
buffer[index] = crc16 & 0xFF; /* LSB first */
|
||||
index++;
|
||||
buffer[index] = crc16 >> 8;
|
||||
index++;
|
||||
}
|
||||
|
||||
return index; /* returns the frame length */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an MS/TP Frame
|
||||
* @param mstp_port - port to send from
|
||||
* @param frame_type - type of frame to send - see defines
|
||||
* @param destination - destination address
|
||||
* @param source - source address
|
||||
* @param data - any data to be sent - may be null
|
||||
* @param data_len - number of bytes of data (up to 501)
|
||||
*/
|
||||
void MSTP_Create_And_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port to send from */
|
||||
uint8_t frame_type, /* type of frame to send - see defines */
|
||||
uint8_t destination, /* destination address */
|
||||
uint8_t source, /* source address */
|
||||
uint8_t *data, /* any data to be sent - may be null */
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t frame_type,
|
||||
uint8_t destination,
|
||||
uint8_t source,
|
||||
uint8_t *data,
|
||||
uint16_t data_len)
|
||||
{ /* number of bytes of data (up to 501) */
|
||||
{
|
||||
uint16_t len = 0; /* number of bytes to send */
|
||||
|
||||
len = MSTP_Create_Frame((uint8_t *)&mstp_port->OutputBuffer[0],
|
||||
mstp_port->OutputBufferSize, frame_type, destination, source, data,
|
||||
data_len);
|
||||
|
||||
RS485_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], len);
|
||||
MSTP_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], len);
|
||||
/* FIXME: be sure to reset SilenceTimer() after each octet is sent! */
|
||||
}
|
||||
|
||||
@@ -533,23 +598,26 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->DataCRC = CRC_Calc_Data(
|
||||
mstp_port->DataRegister, mstp_port->DataCRC);
|
||||
mstp_port->DataCRCActualMSB = mstp_port->DataRegister;
|
||||
mstp_port->InputBuffer[mstp_port->Index] =
|
||||
mstp_port->DataRegister;
|
||||
if (mstp_port->Index < mstp_port->InputBufferSize) {
|
||||
mstp_port->InputBuffer[mstp_port->Index] =
|
||||
mstp_port->DataRegister;
|
||||
}
|
||||
mstp_port->Index++;
|
||||
/* SKIP_DATA or DATA - no change in state */
|
||||
} else if (mstp_port->Index == (mstp_port->DataLength + 1)) {
|
||||
/* CRC2 */
|
||||
mstp_port->InputBuffer[mstp_port->Index] =
|
||||
mstp_port->DataRegister;
|
||||
if (mstp_port->Index < mstp_port->InputBufferSize) {
|
||||
mstp_port->InputBuffer[mstp_port->Index] =
|
||||
mstp_port->DataRegister;
|
||||
}
|
||||
mstp_port->DataCRC = CRC_Calc_Data(
|
||||
mstp_port->DataRegister, mstp_port->DataCRC);
|
||||
mstp_port->DataCRCActualLSB = mstp_port->DataRegister;
|
||||
printf_receive_data("%s",
|
||||
mstptext_frame_type((unsigned)mstp_port->FrameType));
|
||||
if ((mstp_port->FrameType ==
|
||||
FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY) ||
|
||||
(mstp_port->FrameType ==
|
||||
FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY)) {
|
||||
if (((mstp_port->Index+1) < mstp_port->InputBufferSize) &&
|
||||
(mstp_port->FrameType >= Nmin_COBS_type) &&
|
||||
(mstp_port->FrameType <= Nmax_COBS_type)) {
|
||||
if (cobs_frame_decode(
|
||||
&mstp_port->InputBuffer[mstp_port->Index + 1],
|
||||
mstp_port->InputBufferSize,
|
||||
@@ -756,7 +824,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
} else {
|
||||
uint8_t frame_type = mstp_port->OutputBuffer[2];
|
||||
uint8_t destination = mstp_port->OutputBuffer[3];
|
||||
RS485_Send_Frame(mstp_port,
|
||||
MSTP_Send_Frame(mstp_port,
|
||||
(uint8_t *)&mstp_port->OutputBuffer[0], (uint16_t)length);
|
||||
mstp_port->FrameCount++;
|
||||
switch (frame_type) {
|
||||
@@ -1135,7 +1203,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* then call MSTP_Create_And_Send_Frame to transmit the reply
|
||||
* frame */
|
||||
/* and enter the IDLE state to wait for the next frame. */
|
||||
RS485_Send_Frame(mstp_port,
|
||||
MSTP_Send_Frame(mstp_port,
|
||||
(uint8_t *)&mstp_port->OutputBuffer[0], (uint16_t)length);
|
||||
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
||||
/* clear our flag we were holding for comparison */
|
||||
@@ -1207,7 +1275,7 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
* reply frame */
|
||||
/* and enter the IDLE state to wait for the next frame.
|
||||
*/
|
||||
RS485_Send_Frame(mstp_port,
|
||||
MSTP_Send_Frame(mstp_port,
|
||||
(uint8_t *)&mstp_port->OutputBuffer[0],
|
||||
(uint16_t)length);
|
||||
/* clear our flag we were holding for comparison */
|
||||
|
||||
@@ -220,6 +220,7 @@ extern "C" {
|
||||
|
||||
/* functions used by the MS/TP state machine to put or get data */
|
||||
/* FIXME: developer must implement these in their DLMSTP module */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint16_t MSTP_Put_Receive(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
@@ -238,6 +239,12 @@ extern "C" {
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
Reference in New Issue
Block a user