Refactor/mstp zero config state machine (#676)

* Changed MS/TP master node self destination checks to be located in receive FSM

* Changed MSTP zero configuration: modified comments for state transition names; modified next station increment; refactored the UUID rand() to not be required by common zero config implementation; added more unit tests.

* Added another context to MS/TP user data to allow additional user data
This commit is contained in:
Steve Karg
2024-06-26 07:43:25 -05:00
committed by GitHub
parent 9e0751f8c9
commit ddb2b43125
16 changed files with 313 additions and 126 deletions
+3 -4
View File
@@ -77,7 +77,7 @@ static struct my_object_functions {
properties that are writable or that may change. properties that are writable or that may change.
The properties that are constant can be hard coded The properties that are constant can be hard coded
into the read-property encoding. */ into the read-property encoding. */
static uint32_t Object_Instance_Number = 103; static uint32_t Object_Instance_Number = BACNET_MAX_INSTANCE;
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
static BACNET_CHARACTER_STRING My_Object_Name; static BACNET_CHARACTER_STRING My_Object_Name;
static uint32_t Database_Revision; static uint32_t Database_Revision;
@@ -453,9 +453,8 @@ void Device_Init(object_functions_t *object_table)
pObject++; pObject++;
} }
dcc_set_status_duration(COMMUNICATION_ENABLE, 0); dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
if (Object_Instance_Number >= BACNET_MAX_INSTANCE) { if (Object_Instance_Number > BACNET_MAX_INSTANCE) {
Object_Instance_Number = 103; Object_Instance_Number = BACNET_MAX_INSTANCE;
srand(Object_Instance_Number);
} }
characterstring_init_ansi(&My_Object_Name, "stm32-design-challenge-103"); characterstring_init_ansi(&My_Object_Name, "stm32-design-challenge-103");
} }
+8
View File
@@ -25,11 +25,13 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include "hardware.h" #include "hardware.h"
#include "bacnet/basic/sys/mstimer.h" #include "bacnet/basic/sys/mstimer.h"
#include "bacnet/datalink/datalink.h" #include "bacnet/datalink/datalink.h"
#include "bacnet/datalink/dlmstp.h" #include "bacnet/datalink/dlmstp.h"
#include "bacnet/datalink/mstp.h" #include "bacnet/datalink/mstp.h"
#include "bacnet/basic/object/device.h"
#include "rs485.h" #include "rs485.h"
#include "led.h" #include "led.h"
#include "bacnet.h" #include "bacnet.h"
@@ -144,6 +146,7 @@ static void mstp_configure(void)
/* user data */ /* user data */
MSTP_Port.ZeroConfigEnabled = true; MSTP_Port.ZeroConfigEnabled = true;
MSTP_Port.SlaveNodeEnabled = false; MSTP_Port.SlaveNodeEnabled = false;
MSTP_Zero_Config_UUID_Init(&MSTP_Port);
MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_User_Data.RS485_Driver = &RS485_Driver;
MSTP_Port.UserData = &MSTP_User_Data; MSTP_Port.UserData = &MSTP_User_Data;
dlmstp_init((char *)&MSTP_Port); dlmstp_init((char *)&MSTP_Port);
@@ -163,6 +166,7 @@ static void mstp_configure(void)
int main(void) int main(void)
{ {
struct mstimer Blink_Timer; struct mstimer Blink_Timer;
uint32_t Object_Instance_Number = 103;
/*At this stage the microcontroller clock setting is already configured, /*At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup this is done through SystemInit() function which is called from startup
@@ -178,6 +182,10 @@ int main(void)
mstimer_init(); mstimer_init();
lse_init(); lse_init();
led_init(); led_init();
/* FIXME: get device instance from EEPROM */
(void)Device_Set_Object_Instance_Number(Object_Instance_Number);
/* seed libc random number generator */
srand(Object_Instance_Number);
/* initialize MSTP datalink layer */ /* initialize MSTP datalink layer */
mstp_configure(); mstp_configure();
/* initialize application layer*/ /* initialize application layer*/
+73 -1
View File
@@ -81,6 +81,7 @@ static uint32_t Database_Revision;
static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE;
static const char *Reinit_Password = "stm32f4xx"; static const char *Reinit_Password = "stm32f4xx";
static const char *BACnet_Version = BACNET_VERSION_TEXT; static const char *BACnet_Version = BACNET_VERSION_TEXT;
static uint8_t Device_UUID[16];
/* These three arrays are used by the ReadPropertyMultiple handler */ /* These three arrays are used by the ReadPropertyMultiple handler */
static const int Device_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, static const int Device_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
@@ -94,7 +95,8 @@ static const int Device_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_DATABASE_REVISION, -1 }; PROP_DATABASE_REVISION, -1 };
static const int Device_Properties_Optional[] = { PROP_DESCRIPTION, static const int Device_Properties_Optional[] = { PROP_DESCRIPTION,
PROP_LOCATION, PROP_MAX_MASTER, PROP_MAX_INFO_FRAMES, -1 }; PROP_LOCATION, PROP_MAX_MASTER, PROP_MAX_INFO_FRAMES, PROP_DEVICE_UUID,
-1 };
static const int Device_Properties_Proprietary[] = { -1 }; static const int Device_Properties_Proprietary[] = { -1 };
@@ -398,6 +400,71 @@ void Device_Inc_Database_Revision(void)
Database_Revision++; Database_Revision++;
} }
/**
* @brief Initialize a UUID for storing the unique identifier of this device
* @note A Universally Unique IDentifier (UUID) - also called a
* Global Unique IDentifier (GUID) - is a 128-bit value, see RFC 4122.
*
* 4.4. Algorithms for Creating a UUID from Truly Random or
* Pseudo-Random Numbers
*
* The version 4 UUID is meant for generating UUIDs from truly-random or
* pseudo-random numbers.
*
* The algorithm is as follows:
*
* o Set the two most significant bits (bits 6 and 7) of the
* clock_seq_hi_and_reserved to zero and one, respectively.
*
* o Set the four most significant bits (bits 12 through 15) of the
* time_hi_and_version field to the 4-bit version number from
* Section 4.1.3.
*
* o Set all the other bits to randomly (or pseudo-randomly) chosen
* values.
*/
void Device_UUID_Init(void)
{
unsigned i = 0;
/* 1. Generate 16 random bytes = 128 bits */
for (i = 0; i < sizeof(Device_UUID); i++) {
Device_UUID[i] = rand() % 255;
}
/* 2. Adjust certain bits according to RFC 4122 section 4.4.
This just means do the following
(a) set the high nibble of the 7th byte equal to 4 and
(b) set the two most significant bits of the 9th byte to 10'B,
so the high nibble will be one of {8,9,A,B}.
From http://www.cryptosys.net/pki/Uuid.c.html */
Device_UUID[6] = 0x40 | (Device_UUID[6] & 0x0f);
Device_UUID[8] = 0x80 | (Device_UUID[8] & 0x3f);
}
/**
* @brief Set the UUID for this device
* @param new_uuid [in] The new UUID to set
* @param length [in] The length of the new UUID
*/
void Device_UUID_Set(uint8_t *new_uuid, size_t length)
{
if (new_uuid && (length == sizeof(Device_UUID))) {
memcpy(Device_UUID, new_uuid, sizeof(Device_UUID));
}
}
/**
* @brief Get the UUID for this device
* @param uuid [out] The UUID of this device
* @param length [in] The length of the UUID
*/
void Device_UUID_Get(uint8_t *uuid, size_t length)
{
if (uuid && (length == sizeof(Device_UUID))) {
memcpy(uuid, Device_UUID, sizeof(Device_UUID));
}
}
/** Get the total count of objects supported by this Device Object. /** Get the total count of objects supported by this Device Object.
* @note Since many network clients depend on the object list * @note Since many network clients depend on the object list
* for discovery, it must be consistent! * for discovery, it must be consistent!
@@ -586,6 +653,7 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
int apdu_len = 0; /* return value */ int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string = { 0 }; BACNET_BIT_STRING bit_string = { 0 };
BACNET_CHARACTER_STRING char_string = { 0 }; BACNET_CHARACTER_STRING char_string = { 0 };
BACNET_OCTET_STRING octet_string = { 0 };
uint32_t i = 0; uint32_t i = 0;
uint32_t count = 0; uint32_t count = 0;
uint8_t *apdu = NULL; uint8_t *apdu = NULL;
@@ -713,6 +781,10 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
apdu_len = apdu_len =
encode_application_unsigned(&apdu[0], dlmstp_max_master()); encode_application_unsigned(&apdu[0], dlmstp_max_master());
break; break;
case PROP_DEVICE_UUID:
octetstring_init(&octet_string, Device_UUID, sizeof(Device_UUID));
apdu_len = encode_application_octet_string(&apdu[0], &octet_string);
break;
default: default:
rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+19 -15
View File
@@ -90,6 +90,24 @@ int main(void)
led_init(); led_init();
rs485_init(); rs485_init();
mstimer_set(&Blink_Timer, 500); mstimer_set(&Blink_Timer, 500);
/* FIXME: get the device ID from EEPROM */
Device_Set_Object_Instance_Number(103);
/* seed stdlib rand() with device-id to get pseudo consistent
zero-config poll slot, or use hardware RNG to get a more random slot */
#ifdef BACNET_ZERO_CONFIG_RNG_HARDWARE
/* enable the random number generator hardware */
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
RNG_Cmd(ENABLE);
while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET) {
/* wait for 32-bit random number to generate */
}
srand(RNG_GetRandomNumber());
#else
srand(Device_Object_Instance_Number());
#endif
/* initialize the Device UUID from rand() */
Device_UUID_Init();
Device_UUID_Get(MSTP_Port.UUID, sizeof(MSTP_Port.UUID));
/* initialize MSTP datalink layer */ /* initialize MSTP datalink layer */
MSTP_Port.Nmax_info_frames = DLMSTP_MAX_INFO_FRAMES; MSTP_Port.Nmax_info_frames = DLMSTP_MAX_INFO_FRAMES;
MSTP_Port.Nmax_master = DLMSTP_MAX_MASTER; MSTP_Port.Nmax_master = DLMSTP_MAX_MASTER;
@@ -104,6 +122,7 @@ int main(void)
MSTP_Port.UserData = &MSTP_User_Data; MSTP_Port.UserData = &MSTP_User_Data;
dlmstp_init((char *)&MSTP_Port); dlmstp_init((char *)&MSTP_Port);
if (MSTP_Port.ZeroConfigEnabled) { if (MSTP_Port.ZeroConfigEnabled) {
/* set node to monitor address */
dlmstp_set_mac_address(255); dlmstp_set_mac_address(255);
} else { } else {
/* FIXME: get the address from hardware DIP or from EEPROM */ /* FIXME: get the address from hardware DIP or from EEPROM */
@@ -113,21 +132,6 @@ int main(void)
dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT); dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT);
/* initialize application layer*/ /* initialize application layer*/
bacnet_init(); bacnet_init();
/* FIXME: get the device ID from EEPROM */
Device_Set_Object_Instance_Number(103);
/* seed stdlib rand() with device-id to get pweudo consisten
zero-config poll slot, or use hardware RNG to get a more random slot */
#ifdef BACNET_ZERO_CONFIG_RNG_HARDWARE
/* enable the random number generator hardware */
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
RNG_Cmd(ENABLE);
while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET) {
/* wait for 32-bit random number to generate */
}
srand(RNG_GetRandomNumber());
#else
srand(Device_Object_Instance_Number());
#endif
for (;;) { for (;;) {
if (mstimer_expired(&Blink_Timer)) { if (mstimer_expired(&Blink_Timer)) {
mstimer_reset(&Blink_Timer); mstimer_reset(&Blink_Timer);
-1
View File
@@ -48,7 +48,6 @@ CSRC = main.c \
led.c \ led.c \
mstimer-init.c \ mstimer-init.c \
netport.c \ netport.c \
nvmdata.c \
rs485.c \ rs485.c \
stack.c stack.c
+47 -11
View File
@@ -6,6 +6,7 @@
* @copyright SPDX-License-Identifier: MIT * @copyright SPDX-License-Identifier: MIT
*/ */
#include <asf.h> #include <asf.h>
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/sys/mstimer.h" #include "bacnet/basic/sys/mstimer.h"
#include "bacnet/datalink/datalink.h" #include "bacnet/datalink/datalink.h"
#include "bacnet/datalink/dlmstp.h" #include "bacnet/datalink/dlmstp.h"
@@ -14,6 +15,7 @@
#include "led.h" #include "led.h"
#include "adc-hdw.h" #include "adc-hdw.h"
#include "bacnet.h" #include "bacnet.h"
#include "nvmdata.h"
static struct mstimer_callback_data_t BACnet_Callback; static struct mstimer_callback_data_t BACnet_Callback;
@@ -33,11 +35,43 @@ static struct dlmstp_user_data_t MSTP_User_Data;
static uint8_t Input_Buffer[DLMSTP_MPDU_MAX]; static uint8_t Input_Buffer[DLMSTP_MPDU_MAX];
static uint8_t Output_Buffer[DLMSTP_MPDU_MAX]; static uint8_t Output_Buffer[DLMSTP_MPDU_MAX];
/**
* Initializes some data from non-volatile memory module
*/
static void nvm_data_init(void)
{
uint32_t device_id = 127;
uint8_t max_master = 127;
uint8_t mac_address = 127;
uint8_t kbaud_rate = 38;
nvm_read(NVM_BAUD_K, &kbaud_rate, 1);
rs485_kbaud_rate_set(kbaud_rate);
nvm_read(NVM_MAC_ADDRESS, &mac_address, 1);
dlmstp_set_mac_address(mac_address);
nvm_read(NVM_MAX_MASTER, &max_master, 1);
if (max_master > 127) {
max_master = 127;
}
dlmstp_set_max_master(max_master);
/* Get the device ID from the EEPROM */
nvm_read(NVM_DEVICE_0, (uint8_t *)&device_id, sizeof(device_id));
if (device_id < BACNET_MAX_INSTANCE) {
Device_Set_Object_Instance_Number(device_id);
} else {
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
}
}
/** /**
* @brief MS/TP configuraiton * @brief MS/TP configuraiton
*/ */
static void dlmstp_configure(void) static void dlmstp_configure(void)
{ {
uint8_t mac_address = 0;
/* initialize MSTP datalink layer */ /* initialize MSTP datalink layer */
MSTP_Port.Nmax_info_frames = DLMSTP_MAX_INFO_FRAMES; MSTP_Port.Nmax_info_frames = DLMSTP_MAX_INFO_FRAMES;
MSTP_Port.Nmax_master = DLMSTP_MAX_MASTER; MSTP_Port.Nmax_master = DLMSTP_MAX_MASTER;
@@ -46,19 +80,21 @@ static void dlmstp_configure(void)
MSTP_Port.OutputBuffer = Output_Buffer; MSTP_Port.OutputBuffer = Output_Buffer;
MSTP_Port.OutputBufferSize = sizeof(Output_Buffer); MSTP_Port.OutputBufferSize = sizeof(Output_Buffer);
/* user data */ /* user data */
MSTP_Port.ZeroConfigEnabled = true; mac_address = dlmstp_mac_address();
MSTP_Port.SlaveNodeEnabled = false; if (mac_address == 255) {
MSTP_Port.ZeroConfigEnabled = true;
} else {
MSTP_Port.ZeroConfigEnabled = false;
}
if (mac_address <= 127) {
MSTP_Port.SlaveNodeEnabled = false;
} else {
MSTP_Port.SlaveNodeEnabled = true;
}
MSTP_Zero_Config_UUID_Init(&MSTP_Port);
MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_User_Data.RS485_Driver = &RS485_Driver;
MSTP_Port.UserData = &MSTP_User_Data; MSTP_Port.UserData = &MSTP_User_Data;
dlmstp_init((char *)&MSTP_Port); dlmstp_init((char *)&MSTP_Port);
if (MSTP_Port.ZeroConfigEnabled) {
dlmstp_set_mac_address(255);
} else {
/* FIXME: get the address from hardware DIP or from EEPROM */
dlmstp_set_mac_address(1);
}
/* FIXME: get the baud rate from hardware DIP or from EEPROM */
dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT);
} }
/** /**
@@ -87,7 +123,7 @@ int main(void)
} }
cpu_irq_enable(); cpu_irq_enable();
/* application initialization */ /* application initialization */
rs485_baud_rate_set(38400); nvm_data_init();
dlmstp_configure(); dlmstp_configure();
bacnet_init(); bacnet_init();
/* run forever - timed tasks */ /* run forever - timed tasks */
-41
View File
@@ -1,41 +0,0 @@
/**
* @file
* @brief Store and retrieve non-volatile data
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2013
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include <stdbool.h>
#include "nvmdata.h"
#include "bacnet/datalink/dlmstp.h"
#include "bacnet/basic/object/device.h"
/**
* Initializes the non-volatile memory module
*/
void nvm_data_init(void)
{
uint32_t device_id = 127;
uint8_t max_master = 127;
uint8_t mac_address = 127;
nvm_read(NVM_MAC_ADDRESS, &mac_address, 1);
if (mac_address == 255) {
/* uninitialized */
mac_address = 123;
}
dlmstp_set_mac_address(mac_address);
nvm_read(NVM_MAX_MASTER, &max_master, 1);
if (max_master > 127) {
max_master = 127;
}
dlmstp_set_max_master(max_master);
/* Get the device ID from the EEPROM */
nvm_read(NVM_DEVICE_0, (uint8_t *)&device_id, sizeof(device_id));
if (device_id < BACNET_MAX_INSTANCE) {
Device_Set_Object_Instance_Number(device_id);
} else {
Device_Set_Object_Instance_Number(mac_address);
}
}
+1 -11
View File
@@ -10,7 +10,7 @@
#include <avr/eeprom.h> #include <avr/eeprom.h>
/* compatible functions could put in nvm.h to abstract more */ /* compatible functions could put in header to abstract more */
#define nvm_write(dst, src, len) \ #define nvm_write(dst, src, len) \
eeprom_write_block((uint8_t *)(src),(uint8_t *)(dst), (size_t)(len)) eeprom_write_block((uint8_t *)(src),(uint8_t *)(dst), (size_t)(len))
@@ -54,14 +54,4 @@
/* free space 128..4096 */ /* free space 128..4096 */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void nvm_data_init(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif #endif
+38
View File
@@ -210,6 +210,44 @@ uint32_t rs485_baud_rate(void)
return Baud_Rate; return Baud_Rate;
} }
/**
* @brief Set the baud in kili-baud
* @param baud_k baud rate in approximate kilobaud
*/
bool rs485_kbaud_rate_set(uint8_t baud_k)
{
uint32_t baud = 38400;
if (baud_k == 255) {
baud = 38400;
} else if (baud_k >= 115) {
baud = 115200;
} else if (baud_k >= 76) {
baud = 76800;
} else if (baud_k >= 57) {
baud = 57600;
} else if (baud_k >= 38) {
baud = 38400;
} else if (baud_k >= 19) {
baud = 19200;
} else if (baud_k >= 9) {
baud = 9600;
}
return rs485_baud_rate_set(baud);
}
/**
* Converts baud in bps to kili-baud
*
* @param baud - baud rate in bps
* @return: baud rate in approximate kilo-baud
*/
uint8_t rs485_kbaud_rate(void)
{
return Baud_Rate/1000;
}
/** /**
* Initialize the RS-485 baud rate * Initialize the RS-485 baud rate
* *
+3
View File
@@ -25,6 +25,9 @@ void rs485_bytes_send(uint8_t *buffer, uint16_t nbytes);
uint32_t rs485_baud_rate(void); uint32_t rs485_baud_rate(void);
bool rs485_baud_rate_set(uint32_t baud); bool rs485_baud_rate_set(uint32_t baud);
bool rs485_kbaud_rate_set(uint8_t baud_k);
uint8_t rs485_kbaud_rate(void);
uint32_t rs485_silence_milliseconds(void); uint32_t rs485_silence_milliseconds(void);
void rs485_silence_reset(void); void rs485_silence_reset(void);
+12
View File
@@ -322,6 +322,18 @@ extern "C" {
bool Device_Valid_Object_Instance_Number( bool Device_Valid_Object_Instance_Number(
uint32_t object_id); uint32_t object_id);
BACNET_STACK_EXPORT
void Device_UUID_Init(
void);
BACNET_STACK_EXPORT
void Device_UUID_Set(
uint8_t *new_uuid,
size_t length);
BACNET_STACK_EXPORT
void Device_UUID_Get(
uint8_t *uuid,
size_t length);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
unsigned Device_Object_List_Count( unsigned Device_Object_List_Count(
void); void);
+2 -1
View File
@@ -97,7 +97,7 @@ struct dlmstp_rs485_driver {
}; };
/** /**
* A structure of BACnet Port Data for BACnet MS/TP * An example structure of user data for BACnet MS/TP
*/ */
struct dlmstp_user_data_t { struct dlmstp_user_data_t {
struct dlmstp_statistics Statistics; struct dlmstp_statistics Statistics;
@@ -107,6 +107,7 @@ struct dlmstp_user_data_t {
struct dlmstp_packet PDU_Buffer[DLMSTP_MAX_INFO_FRAMES]; struct dlmstp_packet PDU_Buffer[DLMSTP_MAX_INFO_FRAMES];
bool Initialized; bool Initialized;
bool ReceivePacketPending; bool ReceivePacketPending;
void *Context;
}; };
/* callback to signify the receipt of a preamble */ /* callback to signify the receipt of a preamble */
+70 -31
View File
@@ -452,12 +452,19 @@ void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port)
} else { } else {
if (mstp_port->DataLength == 0) { if (mstp_port->DataLength == 0) {
/* NoData */ /* NoData */
printf_receive_data("%s", if ((mstp_port->DestinationAddress ==
mstptext_frame_type( mstp_port->This_Station) ||
(mstp_port->DestinationAddress ==
MSTP_BROADCAST_ADDRESS) ||
(mstp_port->This_Station ==
MSTP_BROADCAST_ADDRESS)) {
printf_receive_data("%s",
mstptext_frame_type(
(unsigned)mstp_port->FrameType)); (unsigned)mstp_port->FrameType));
/* indicate that a frame with no data has been /* indicate that a frame with no data has been
* received */ * received */
mstp_port->ReceivedValidFrame = true; mstp_port->ReceivedValidFrame = true;
}
/* wait for the start of the next frame. */ /* wait for the start of the next frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
} else { } else {
@@ -465,6 +472,8 @@ void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port)
if ((mstp_port->DestinationAddress == if ((mstp_port->DestinationAddress ==
mstp_port->This_Station) || mstp_port->This_Station) ||
(mstp_port->DestinationAddress == (mstp_port->DestinationAddress ==
MSTP_BROADCAST_ADDRESS) ||
(mstp_port->This_Station ==
MSTP_BROADCAST_ADDRESS)) { MSTP_BROADCAST_ADDRESS)) {
if (mstp_port->DataLength <= if (mstp_port->DataLength <=
mstp_port->InputBufferSize) { mstp_port->InputBufferSize) {
@@ -641,9 +650,12 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port)
if (mstp_port->This_Station != 255) { if (mstp_port->This_Station != 255) {
/* indicate that the next station is unknown */ /* indicate that the next station is unknown */
mstp_port->Next_Station = mstp_port->This_Station; mstp_port->Next_Station = mstp_port->This_Station;
/* Send a Poll For Master since we just received
the token */
mstp_port->Poll_Station = (mstp_port->Next_Station + 1) % mstp_port->Poll_Station = (mstp_port->Next_Station + 1) %
(mstp_port->Zero_Config_Max_Master + 1); (mstp_port->Zero_Config_Max_Master + 1);
mstp_port->TokenCount = Npoll; mstp_port->TokenCount = Npoll;
mstp_port->RetryCount = 0;
mstp_port->EventCount = 0; mstp_port->EventCount = 0;
mstp_port->SoleMaster = true; mstp_port->SoleMaster = true;
MSTP_Create_And_Send_Frame(mstp_port, MSTP_Create_And_Send_Frame(mstp_port,
@@ -689,14 +701,12 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port)
mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE;
} }
mstp_port->ReceivedValidFrame = false; mstp_port->ReceivedValidFrame = false;
} else if ((mstp_port->DestinationAddress == } else {
mstp_port->This_Station) ||
(mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS)) {
/* destined for me! */ /* destined for me! */
switch (mstp_port->FrameType) { switch (mstp_port->FrameType) {
case FRAME_TYPE_TOKEN: case FRAME_TYPE_TOKEN:
/* ReceivedToken */ /* ReceivedToken */
/* tokens can't be broadcast */ /* tokens cannot be broadcast */
if (mstp_port->DestinationAddress == if (mstp_port->DestinationAddress ==
MSTP_BROADCAST_ADDRESS) { MSTP_BROADCAST_ADDRESS) {
break; break;
@@ -1336,6 +1346,33 @@ void MSTP_Zero_Config_UUID_Init(struct mstp_port_struct_t *mstp_port)
mstp_port->UUID[8] = 0x80 | (mstp_port->UUID[8] & 0x3f); mstp_port->UUID[8] = 0x80 | (mstp_port->UUID[8] & 0x3f);
} }
/**
* @brief Increment the Zero Configuration Station address
* @param station the current station address in the range of min..max
* @return the next station address
*/
unsigned MSTP_Zero_Config_Station_Increment(unsigned station)
{
unsigned next_station;
if (station < Nmin_poll_station) {
next_station = Nmin_poll_station;
} else {
#ifdef MSTP_ZERO_CONFIG_STATION_INCREMENT_MODULO
/* as defined by specification language */
next_station = Nmin_poll_station +
((station + 1) % ((Nmax_poll_station - Nmin_poll_station) + 1));
#else
next_station = station + 1;
if (next_station > Nmax_poll_station) {
next_station = Nmin_poll_station;
}
#endif
}
return next_station;
}
/** /**
* @brief The ZERO_CONFIGURATION_INIT state is entered when * @brief The ZERO_CONFIGURATION_INIT state is entered when
* ZeroConfigurationMode is TRUE * ZeroConfigurationMode is TRUE
@@ -1350,13 +1387,12 @@ static void MSTP_Zero_Config_State_Init(struct mstp_port_struct_t *mstp_port)
} }
mstp_port->Poll_Count = 0; mstp_port->Poll_Count = 0;
mstp_port->Zero_Config_Station = Nmin_poll_station; mstp_port->Zero_Config_Station = Nmin_poll_station;
mstp_port->Npoll_slot = 1 + (rand() % Nmax_poll_slot); mstp_port->Npoll_slot = 1 + (mstp_port->UUID[0] % Nmax_poll_slot);
/* basic silence timeout is the dropped token time plus /* basic silence timeout is the dropped token time plus
one Tslot after the last master node. Add one Tslot of one Tslot after the last master node. Add one Tslot of
silence timeout per zero config priority slot */ silence timeout per zero config priority slot */
slots = 128 + mstp_port->Npoll_slot; slots = 128 + mstp_port->Npoll_slot;
mstp_port->Zero_Config_Silence = Tno_token + Tslot * slots; mstp_port->Zero_Config_Silence = Tno_token + Tslot * slots;
MSTP_Zero_Config_UUID_Init(mstp_port);
mstp_port->Zero_Config_Max_Master = 0; mstp_port->Zero_Config_Max_Master = 0;
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE; mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
} }
@@ -1426,23 +1462,23 @@ static void MSTP_Zero_Config_State_Lurk(struct mstp_port_struct_t *mstp_port)
if (src == mstp_port->Zero_Config_Station) { if (src == mstp_port->Zero_Config_Station) {
/* AddressInUse */ /* AddressInUse */
/* monitor PFM from the next address */ /* monitor PFM from the next address */
mstp_port->Zero_Config_Station++; mstp_port->Zero_Config_Station =
if (mstp_port->Zero_Config_Station > Nmax_poll_station) { MSTP_Zero_Config_Station_Increment(
/* start again from first */ mstp_port->Zero_Config_Station);
mstp_port->Zero_Config_Station = Nmin_poll_station;
}
mstp_port->Poll_Count = 0; mstp_port->Poll_Count = 0;
} else if ((frame == FRAME_TYPE_POLL_FOR_MASTER) && } else if ((frame == FRAME_TYPE_POLL_FOR_MASTER) &&
(dst == mstp_port->Zero_Config_Station)) { (dst == mstp_port->Zero_Config_Station)) {
mstp_port->Poll_Count++; /* calculate this node poll count priority number */
/* calculate this node poll count priority */
count = Nmin_poll + mstp_port->Npoll_slot; count = Nmin_poll + mstp_port->Npoll_slot;
if (mstp_port->Poll_Count == count) { if (mstp_port->Poll_Count == count) {
/* ClaimAddress */ /* PollResponse */
MSTP_Create_And_Send_Frame(mstp_port, MSTP_Create_And_Send_Frame(mstp_port,
FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, src, FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, src,
mstp_port->Zero_Config_Station, NULL, 0); mstp_port->Zero_Config_Station, NULL, 0);
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CLAIM; mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CLAIM;
} else {
/* CountFrame */
mstp_port->Poll_Count++;
} }
} }
} else if (mstp_port->ReceivedInvalidFrame) { } else if (mstp_port->ReceivedInvalidFrame) {
@@ -1470,7 +1506,6 @@ static void MSTP_Zero_Config_State_Claim(struct mstp_port_struct_t *mstp_port)
if (!mstp_port) { if (!mstp_port) {
return; return;
} }
/* */
if (mstp_port->ReceivedValidFrame) { if (mstp_port->ReceivedValidFrame) {
mstp_port->ReceivedValidFrame = false; mstp_port->ReceivedValidFrame = false;
dst = mstp_port->DestinationAddress; dst = mstp_port->DestinationAddress;
@@ -1479,11 +1514,9 @@ static void MSTP_Zero_Config_State_Claim(struct mstp_port_struct_t *mstp_port)
if (src == mstp_port->Zero_Config_Station) { if (src == mstp_port->Zero_Config_Station) {
/* ClaimAddressInUse */ /* ClaimAddressInUse */
/* monitor PFM from the next address */ /* monitor PFM from the next address */
mstp_port->Zero_Config_Station++; mstp_port->Zero_Config_Station =
if (mstp_port->Zero_Config_Station > Nmax_poll_station) { MSTP_Zero_Config_Station_Increment(
/* start again from first */ mstp_port->Zero_Config_Station);
mstp_port->Zero_Config_Station = Nmin_poll_station;
}
mstp_port->Poll_Count = 0; mstp_port->Poll_Count = 0;
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK; mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
} else if (frame == FRAME_TYPE_TOKEN) { } else if (frame == FRAME_TYPE_TOKEN) {
@@ -1499,7 +1532,7 @@ static void MSTP_Zero_Config_State_Claim(struct mstp_port_struct_t *mstp_port)
/* ClaimInvalidFrame */ /* ClaimInvalidFrame */
mstp_port->ReceivedInvalidFrame = false; mstp_port->ReceivedInvalidFrame = false;
} else if (mstp_port->Zero_Config_Silence > 0) { } else if (mstp_port->Zero_Config_Silence > 0) {
/* ClaimLostToken */ /* ClaimTimeout */
if (mstp_port->SilenceTimer((void *)mstp_port) > if (mstp_port->SilenceTimer((void *)mstp_port) >
mstp_port->Zero_Config_Silence) { mstp_port->Zero_Config_Silence) {
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE; mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
@@ -1550,13 +1583,14 @@ static void MSTP_Zero_Config_State_Confirm(struct mstp_port_struct_t *mstp_port)
} else if (src == mstp_port->Zero_Config_Station) { } else if (src == mstp_port->Zero_Config_Station) {
/* ConfirmationAddressInUse */ /* ConfirmationAddressInUse */
/* monitor PFM from the next address */ /* monitor PFM from the next address */
mstp_port->Zero_Config_Station++; mstp_port->Zero_Config_Station =
if (mstp_port->Zero_Config_Station > Nmax_poll_station) { MSTP_Zero_Config_Station_Increment(
/* start again from first */ mstp_port->Zero_Config_Station);
mstp_port->Zero_Config_Station = Nmin_poll_station;
}
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK; mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
} }
} else if (mstp_port->ReceivedInvalidFrame) {
/* ConfirmationInvalidFrame */
mstp_port->ReceivedInvalidFrame = false;
} else if (mstp_port->SilenceTimer((void *)mstp_port) >= } else if (mstp_port->SilenceTimer((void *)mstp_port) >=
mstp_port->Treply_timeout) { mstp_port->Treply_timeout) {
/* ConfirmationTimeout */ /* ConfirmationTimeout */
@@ -1625,9 +1659,14 @@ void MSTP_Init(struct mstp_port_struct_t *mstp_port)
mstp_port->Treply_delay = DEFAULT_Treply_delay; mstp_port->Treply_delay = DEFAULT_Treply_delay;
mstp_port->Treply_timeout = DEFAULT_Treply_timeout; mstp_port->Treply_timeout = DEFAULT_Treply_timeout;
mstp_port->Tusage_timeout = DEFAULT_Tusage_timeout; mstp_port->Tusage_timeout = DEFAULT_Tusage_timeout;
mstp_port->SlaveNodeEnabled = false;
/* FIXME: point to functions */ /* FIXME: point to functions */
mstp_port->SilenceTimer = Timer_Silence; mstp_port->SilenceTimer = Timer_Silence;
mstp_port->SilenceTimerReset = Timer_Silence_Reset; mstp_port->SilenceTimerReset = Timer_Silence_Reset;
/* FIXME: set these in your dlmstp if you are zero-config */
mstp_port->ZeroConfigEnabled = true;
/* use the libc srand() and rand() generated random number*/
MSTP_Zero_Config_UUID_Init(&MSTP_Port);
#endif #endif
if ((mstp_port->Tframe_abort < 6) || (mstp_port->Tframe_abort > 100)) { if ((mstp_port->Tframe_abort < 6) || (mstp_port->Tframe_abort > 100)) {
mstp_port->Tframe_abort = DEFAULT_Tframe_abort; mstp_port->Tframe_abort = DEFAULT_Tframe_abort;
+3
View File
@@ -271,6 +271,9 @@ void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void MSTP_Zero_Config_UUID_Init(struct mstp_port_struct_t *mstp_port); void MSTP_Zero_Config_UUID_Init(struct mstp_port_struct_t *mstp_port);
BACNET_STACK_EXPORT
unsigned MSTP_Zero_Config_Station_Increment(unsigned station);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port); void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port);
+1
View File
@@ -24,6 +24,7 @@ add_compile_definitions(
BIG_ENDIAN=0 BIG_ENDIAN=0
CONFIG_ZTEST=1 CONFIG_ZTEST=1
BACDL_MSTP=1 BACDL_MSTP=1
MSTP_ZERO_CONFIG_STATION_INCREMENT_MODULO
) )
include_directories( include_directories(
+23
View File
@@ -643,6 +643,7 @@ static void testZeroConfigNode_Init(struct mstp_port_struct_t *mstp_port)
/* configure for Zero Config */ /* configure for Zero Config */
mstp_port->ZeroConfigEnabled = true; mstp_port->ZeroConfigEnabled = true;
mstp_port->This_Station = 255; mstp_port->This_Station = 255;
MSTP_Zero_Config_UUID_Init(mstp_port);
MSTP_Init(mstp_port); MSTP_Init(mstp_port);
zassert_true(mstp_port->master_state == MSTP_MASTER_STATE_INITIALIZE, NULL); zassert_true(mstp_port->master_state == MSTP_MASTER_STATE_INITIALIZE, NULL);
@@ -1055,6 +1056,7 @@ static void testZeroConfigNode_Test_LURK_ClaimLostToken(
static void testZeroConfigNodeFSM(void) static void testZeroConfigNodeFSM(void)
{ {
struct mstp_port_struct_t MSTP_Port = { 0 }; /* port data */ struct mstp_port_struct_t MSTP_Port = { 0 }; /* port data */
unsigned station, next_station, test_station;
/* test case: timeout event */ /* test case: timeout event */
testZeroConfigNode_Init(&MSTP_Port); testZeroConfigNode_Init(&MSTP_Port);
@@ -1117,6 +1119,27 @@ static void testZeroConfigNodeFSM(void)
testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port);
testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); testZeroConfigNode_Test_LURK_Claim(&MSTP_Port);
testZeroConfigNode_Test_LURK_ClaimLostToken(&MSTP_Port); testZeroConfigNode_Test_LURK_ClaimLostToken(&MSTP_Port);
/* test next station rollover */
station = 0;
test_station = Nmin_poll_station;
next_station = MSTP_Zero_Config_Station_Increment(station);
zassert_equal(next_station, test_station, "station=%u next_station=%u",
station, next_station);
station = Nmin_poll_station;
test_station = Nmin_poll_station + 1;
next_station = MSTP_Zero_Config_Station_Increment(station);
zassert_equal(next_station, test_station, "station=%u next_station=%u",
station, next_station);
station = Nmax_poll_station - 1;
test_station = Nmax_poll_station;
next_station = MSTP_Zero_Config_Station_Increment(station);
zassert_equal(next_station, test_station,"station=%u next_station=%u",
station, next_station);
station = Nmax_poll_station;
test_station = Nmin_poll_station;
next_station = MSTP_Zero_Config_Station_Increment(station);
zassert_equal(next_station, test_station, "station=%u next_station=%u",
station, next_station);
} }
/** /**