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:
Steve Karg
2023-11-12 09:49:37 -06:00
committed by GitHub
parent 1372e52aa7
commit 3c94580bee
16 changed files with 391 additions and 84 deletions
+4 -4
View File
@@ -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
+32
View File
@@ -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
View File
@@ -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 */
+7
View File
@@ -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 */