diff --git a/ports/stm32f10x/device.c b/ports/stm32f10x/device.c index bd43f7cc..efaf8ba4 100644 --- a/ports/stm32f10x/device.c +++ b/ports/stm32f10x/device.c @@ -77,7 +77,7 @@ static struct my_object_functions { properties that are writable or that may change. The properties that are constant can be hard coded 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_CHARACTER_STRING My_Object_Name; static uint32_t Database_Revision; @@ -453,9 +453,8 @@ void Device_Init(object_functions_t *object_table) pObject++; } dcc_set_status_duration(COMMUNICATION_ENABLE, 0); - if (Object_Instance_Number >= BACNET_MAX_INSTANCE) { - Object_Instance_Number = 103; - srand(Object_Instance_Number); + if (Object_Instance_Number > BACNET_MAX_INSTANCE) { + Object_Instance_Number = BACNET_MAX_INSTANCE; } characterstring_init_ansi(&My_Object_Name, "stm32-design-challenge-103"); } diff --git a/ports/stm32f10x/main.c b/ports/stm32f10x/main.c index 927c9d14..fdb1f919 100644 --- a/ports/stm32f10x/main.c +++ b/ports/stm32f10x/main.c @@ -25,11 +25,13 @@ #include #include +#include #include "hardware.h" #include "bacnet/basic/sys/mstimer.h" #include "bacnet/datalink/datalink.h" #include "bacnet/datalink/dlmstp.h" #include "bacnet/datalink/mstp.h" +#include "bacnet/basic/object/device.h" #include "rs485.h" #include "led.h" #include "bacnet.h" @@ -38,7 +40,7 @@ char *BACnet_Version = "1.0"; /* MS/TP port */ static struct mstp_port_struct_t MSTP_Port; -static struct dlmstp_rs485_driver RS485_Driver = { +static struct dlmstp_rs485_driver RS485_Driver = { .init = rs485_init, .send = rs485_bytes_send, .read = rs485_byte_available, @@ -46,7 +48,7 @@ static struct dlmstp_rs485_driver RS485_Driver = { .baud_rate = rs485_baud_rate, .baud_rate_set = rs485_baud_rate_set, .silence_milliseconds = rs485_silence_milliseconds, - .silence_reset = rs485_silence_reset + .silence_reset = rs485_silence_reset }; static struct dlmstp_user_data_t MSTP_User_Data; static uint8_t Input_Buffer[DLMSTP_MPDU_MAX]; @@ -144,6 +146,7 @@ static void mstp_configure(void) /* user data */ MSTP_Port.ZeroConfigEnabled = true; MSTP_Port.SlaveNodeEnabled = false; + MSTP_Zero_Config_UUID_Init(&MSTP_Port); MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_Port.UserData = &MSTP_User_Data; dlmstp_init((char *)&MSTP_Port); @@ -163,6 +166,7 @@ static void mstp_configure(void) int main(void) { struct mstimer Blink_Timer; + uint32_t Object_Instance_Number = 103; /*At this stage the microcontroller clock setting is already configured, this is done through SystemInit() function which is called from startup @@ -178,6 +182,10 @@ int main(void) mstimer_init(); lse_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 */ mstp_configure(); /* initialize application layer*/ diff --git a/ports/stm32f4xx/device.c b/ports/stm32f4xx/device.c index f2056fb8..89ea2dad 100644 --- a/ports/stm32f4xx/device.c +++ b/ports/stm32f4xx/device.c @@ -81,6 +81,7 @@ static uint32_t Database_Revision; static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; static const char *Reinit_Password = "stm32f4xx"; static const char *BACnet_Version = BACNET_VERSION_TEXT; +static uint8_t Device_UUID[16]; /* These three arrays are used by the ReadPropertyMultiple handler */ 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 }; 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 }; @@ -398,6 +400,71 @@ void Device_Inc_Database_Revision(void) 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. * @note Since many network clients depend on the object list * 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 */ BACNET_BIT_STRING bit_string = { 0 }; BACNET_CHARACTER_STRING char_string = { 0 }; + BACNET_OCTET_STRING octet_string = { 0 }; uint32_t i = 0; uint32_t count = 0; uint8_t *apdu = NULL; @@ -713,6 +781,10 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_unsigned(&apdu[0], dlmstp_max_master()); 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: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; diff --git a/ports/stm32f4xx/main.c b/ports/stm32f4xx/main.c index 891c0afb..22be8085 100644 --- a/ports/stm32f4xx/main.c +++ b/ports/stm32f4xx/main.c @@ -44,7 +44,7 @@ /* MS/TP port */ static struct mstp_port_struct_t MSTP_Port; -static struct dlmstp_rs485_driver RS485_Driver = { +static struct dlmstp_rs485_driver RS485_Driver = { .init = rs485_init, .send = rs485_bytes_send, .read = rs485_byte_available, @@ -52,7 +52,7 @@ static struct dlmstp_rs485_driver RS485_Driver = { .baud_rate = rs485_baud_rate, .baud_rate_set = rs485_baud_rate_set, .silence_milliseconds = rs485_silence_milliseconds, - .silence_reset = rs485_silence_reset + .silence_reset = rs485_silence_reset }; static struct dlmstp_user_data_t MSTP_User_Data; static uint8_t Input_Buffer[DLMSTP_MPDU_MAX]; @@ -90,6 +90,24 @@ int main(void) led_init(); rs485_init(); 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 */ MSTP_Port.Nmax_info_frames = DLMSTP_MAX_INFO_FRAMES; MSTP_Port.Nmax_master = DLMSTP_MAX_MASTER; @@ -104,6 +122,7 @@ int main(void) MSTP_Port.UserData = &MSTP_User_Data; dlmstp_init((char *)&MSTP_Port); if (MSTP_Port.ZeroConfigEnabled) { + /* set node to monitor address */ dlmstp_set_mac_address(255); } else { /* 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); /* initialize application layer*/ 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 (;;) { if (mstimer_expired(&Blink_Timer)) { mstimer_reset(&Blink_Timer); diff --git a/ports/xplained/Makefile b/ports/xplained/Makefile index 2dba01f0..1d27cc53 100644 --- a/ports/xplained/Makefile +++ b/ports/xplained/Makefile @@ -48,7 +48,6 @@ CSRC = main.c \ led.c \ mstimer-init.c \ netport.c \ - nvmdata.c \ rs485.c \ stack.c diff --git a/ports/xplained/main.c b/ports/xplained/main.c index d0ebcfd4..2cdf260a 100644 --- a/ports/xplained/main.c +++ b/ports/xplained/main.c @@ -6,6 +6,7 @@ * @copyright SPDX-License-Identifier: MIT */ #include +#include "bacnet/basic/object/device.h" #include "bacnet/basic/sys/mstimer.h" #include "bacnet/datalink/datalink.h" #include "bacnet/datalink/dlmstp.h" @@ -14,12 +15,13 @@ #include "led.h" #include "adc-hdw.h" #include "bacnet.h" +#include "nvmdata.h" static struct mstimer_callback_data_t BACnet_Callback; /* MS/TP port */ static struct mstp_port_struct_t MSTP_Port; -static struct dlmstp_rs485_driver RS485_Driver = { +static struct dlmstp_rs485_driver RS485_Driver = { .init = rs485_init, .send = rs485_bytes_send, .read = rs485_byte_available, @@ -27,17 +29,49 @@ static struct dlmstp_rs485_driver RS485_Driver = { .baud_rate = rs485_baud_rate, .baud_rate_set = rs485_baud_rate_set, .silence_milliseconds = rs485_silence_milliseconds, - .silence_reset = rs485_silence_reset + .silence_reset = rs485_silence_reset }; static struct dlmstp_user_data_t MSTP_User_Data; static uint8_t Input_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 */ static void dlmstp_configure(void) { + uint8_t mac_address = 0; + /* initialize MSTP datalink layer */ MSTP_Port.Nmax_info_frames = DLMSTP_MAX_INFO_FRAMES; MSTP_Port.Nmax_master = DLMSTP_MAX_MASTER; @@ -46,19 +80,21 @@ static void dlmstp_configure(void) MSTP_Port.OutputBuffer = Output_Buffer; MSTP_Port.OutputBufferSize = sizeof(Output_Buffer); /* user data */ - MSTP_Port.ZeroConfigEnabled = true; - MSTP_Port.SlaveNodeEnabled = false; + mac_address = dlmstp_mac_address(); + 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_Port.UserData = &MSTP_User_Data; 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(); /* application initialization */ - rs485_baud_rate_set(38400); + nvm_data_init(); dlmstp_configure(); bacnet_init(); /* run forever - timed tasks */ diff --git a/ports/xplained/nvmdata.c b/ports/xplained/nvmdata.c deleted file mode 100644 index 46420ac7..00000000 --- a/ports/xplained/nvmdata.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file - * @brief Store and retrieve non-volatile data - * @author Steve Karg - * @date 2013 - * @copyright SPDX-License-Identifier: MIT - */ -#include -#include -#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); - } -} diff --git a/ports/xplained/nvmdata.h b/ports/xplained/nvmdata.h index be1ec6a0..e486b20b 100644 --- a/ports/xplained/nvmdata.h +++ b/ports/xplained/nvmdata.h @@ -10,13 +10,13 @@ #include -/* 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) \ eeprom_write_block((uint8_t *)(src),(uint8_t *)(dst), (size_t)(len)) #define nvm_read(src, dst, len) \ eeprom_read_block((uint8_t *)dst, (const uint8_t *)(src),(size_t)(len)) - + /*=============== EEPROM ================*/ /* define EEPROM signature version */ #define NVM_SIGNATURE 0 @@ -54,14 +54,4 @@ /* free space 128..4096 */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -void nvm_data_init(void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ #endif diff --git a/ports/xplained/rs485.c b/ports/xplained/rs485.c index d3eba4bc..9d43edb3 100644 --- a/ports/xplained/rs485.c +++ b/ports/xplained/rs485.c @@ -210,6 +210,44 @@ uint32_t rs485_baud_rate(void) 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 * diff --git a/ports/xplained/rs485.h b/ports/xplained/rs485.h index 362dd8a0..79511cb2 100644 --- a/ports/xplained/rs485.h +++ b/ports/xplained/rs485.h @@ -25,6 +25,9 @@ void rs485_bytes_send(uint8_t *buffer, uint16_t nbytes); uint32_t rs485_baud_rate(void); 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); void rs485_silence_reset(void); diff --git a/src/bacnet/basic/object/device.h b/src/bacnet/basic/object/device.h index eb834e0a..0647f011 100644 --- a/src/bacnet/basic/object/device.h +++ b/src/bacnet/basic/object/device.h @@ -321,7 +321,19 @@ extern "C" { BACNET_STACK_EXPORT bool Device_Valid_Object_Instance_Number( 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 unsigned Device_Object_List_Count( void); @@ -332,8 +344,8 @@ extern "C" { uint32_t * instance); BACNET_STACK_EXPORT int Device_Object_List_Element_Encode( - uint32_t object_instance, - BACNET_ARRAY_INDEX array_index, + uint32_t object_instance, + BACNET_ARRAY_INDEX array_index, uint8_t *apdu); BACNET_STACK_EXPORT diff --git a/src/bacnet/datalink/dlmstp.h b/src/bacnet/datalink/dlmstp.h index 843d5570..fe6aa900 100644 --- a/src/bacnet/datalink/dlmstp.h +++ b/src/bacnet/datalink/dlmstp.h @@ -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_statistics Statistics; @@ -107,6 +107,7 @@ struct dlmstp_user_data_t { struct dlmstp_packet PDU_Buffer[DLMSTP_MAX_INFO_FRAMES]; bool Initialized; bool ReceivePacketPending; + void *Context; }; /* callback to signify the receipt of a preamble */ diff --git a/src/bacnet/datalink/mstp.c b/src/bacnet/datalink/mstp.c index 7e9352a0..63770cae 100644 --- a/src/bacnet/datalink/mstp.c +++ b/src/bacnet/datalink/mstp.c @@ -452,12 +452,19 @@ void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port) } else { if (mstp_port->DataLength == 0) { /* NoData */ - printf_receive_data("%s", - mstptext_frame_type( + if ((mstp_port->DestinationAddress == + 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)); - /* indicate that a frame with no data has been - * received */ - mstp_port->ReceivedValidFrame = true; + /* indicate that a frame with no data has been + * received */ + mstp_port->ReceivedValidFrame = true; + } /* wait for the start of the next frame. */ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; } else { @@ -465,6 +472,8 @@ void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port) if ((mstp_port->DestinationAddress == mstp_port->This_Station) || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) || + (mstp_port->This_Station == MSTP_BROADCAST_ADDRESS)) { if (mstp_port->DataLength <= 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) { /* indicate that the next station is unknown */ 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->Zero_Config_Max_Master + 1); mstp_port->TokenCount = Npoll; + mstp_port->RetryCount = 0; mstp_port->EventCount = 0; mstp_port->SoleMaster = true; 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->ReceivedValidFrame = false; - } else if ((mstp_port->DestinationAddress == - mstp_port->This_Station) || - (mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS)) { + } else { /* destined for me! */ switch (mstp_port->FrameType) { case FRAME_TYPE_TOKEN: /* ReceivedToken */ - /* tokens can't be broadcast */ + /* tokens cannot be broadcast */ if (mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS) { 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); } +/** + * @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 * 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->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 one Tslot after the last master node. Add one Tslot of silence timeout per zero config priority slot */ slots = 128 + mstp_port->Npoll_slot; 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_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) { /* AddressInUse */ /* monitor PFM from the next address */ - mstp_port->Zero_Config_Station++; - if (mstp_port->Zero_Config_Station > Nmax_poll_station) { - /* start again from first */ - mstp_port->Zero_Config_Station = Nmin_poll_station; - } + mstp_port->Zero_Config_Station = + MSTP_Zero_Config_Station_Increment( + mstp_port->Zero_Config_Station); mstp_port->Poll_Count = 0; } else if ((frame == FRAME_TYPE_POLL_FOR_MASTER) && (dst == mstp_port->Zero_Config_Station)) { - mstp_port->Poll_Count++; - /* calculate this node poll count priority */ + /* calculate this node poll count priority number */ count = Nmin_poll + mstp_port->Npoll_slot; if (mstp_port->Poll_Count == count) { - /* ClaimAddress */ + /* PollResponse */ MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, src, mstp_port->Zero_Config_Station, NULL, 0); mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CLAIM; + } else { + /* CountFrame */ + mstp_port->Poll_Count++; } } } 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) { return; } - /* */ if (mstp_port->ReceivedValidFrame) { mstp_port->ReceivedValidFrame = false; 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) { /* ClaimAddressInUse */ /* monitor PFM from the next address */ - mstp_port->Zero_Config_Station++; - if (mstp_port->Zero_Config_Station > Nmax_poll_station) { - /* start again from first */ - mstp_port->Zero_Config_Station = Nmin_poll_station; - } + mstp_port->Zero_Config_Station = + MSTP_Zero_Config_Station_Increment( + mstp_port->Zero_Config_Station); mstp_port->Poll_Count = 0; mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK; } 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 */ mstp_port->ReceivedInvalidFrame = false; } else if (mstp_port->Zero_Config_Silence > 0) { - /* ClaimLostToken */ + /* ClaimTimeout */ if (mstp_port->SilenceTimer((void *)mstp_port) > mstp_port->Zero_Config_Silence) { 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) { /* ConfirmationAddressInUse */ /* monitor PFM from the next address */ - mstp_port->Zero_Config_Station++; - if (mstp_port->Zero_Config_Station > Nmax_poll_station) { - /* start again from first */ - mstp_port->Zero_Config_Station = Nmin_poll_station; - } + mstp_port->Zero_Config_Station = + MSTP_Zero_Config_Station_Increment( + mstp_port->Zero_Config_Station); 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) >= mstp_port->Treply_timeout) { /* 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_timeout = DEFAULT_Treply_timeout; mstp_port->Tusage_timeout = DEFAULT_Tusage_timeout; + mstp_port->SlaveNodeEnabled = false; /* FIXME: point to functions */ mstp_port->SilenceTimer = Timer_Silence; 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 if ((mstp_port->Tframe_abort < 6) || (mstp_port->Tframe_abort > 100)) { mstp_port->Tframe_abort = DEFAULT_Tframe_abort; diff --git a/src/bacnet/datalink/mstp.h b/src/bacnet/datalink/mstp.h index 3ec5ae7a..75dfb755 100644 --- a/src/bacnet/datalink/mstp.h +++ b/src/bacnet/datalink/mstp.h @@ -271,6 +271,9 @@ void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address); BACNET_STACK_EXPORT 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 void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port); diff --git a/test/bacnet/datalink/mstp/CMakeLists.txt b/test/bacnet/datalink/mstp/CMakeLists.txt index b4d63ae4..1cbf7745 100644 --- a/test/bacnet/datalink/mstp/CMakeLists.txt +++ b/test/bacnet/datalink/mstp/CMakeLists.txt @@ -24,6 +24,7 @@ add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 BACDL_MSTP=1 + MSTP_ZERO_CONFIG_STATION_INCREMENT_MODULO ) include_directories( diff --git a/test/bacnet/datalink/mstp/src/main.c b/test/bacnet/datalink/mstp/src/main.c index d8da83d7..dc63b0f0 100644 --- a/test/bacnet/datalink/mstp/src/main.c +++ b/test/bacnet/datalink/mstp/src/main.c @@ -643,6 +643,7 @@ static void testZeroConfigNode_Init(struct mstp_port_struct_t *mstp_port) /* configure for Zero Config */ mstp_port->ZeroConfigEnabled = true; mstp_port->This_Station = 255; + MSTP_Zero_Config_UUID_Init(mstp_port); MSTP_Init(mstp_port); 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) { struct mstp_port_struct_t MSTP_Port = { 0 }; /* port data */ + unsigned station, next_station, test_station; /* test case: timeout event */ testZeroConfigNode_Init(&MSTP_Port); @@ -1117,6 +1119,27 @@ static void testZeroConfigNodeFSM(void) testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); testZeroConfigNode_Test_LURK_Claim(&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); } /**