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:
@@ -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