diff --git a/bacnet-stack/include/bacnet.h b/bacnet-stack/include/bacnet.h index 19cf7b6c..a773db0b 100644 --- a/bacnet-stack/include/bacnet.h +++ b/bacnet-stack/include/bacnet.h @@ -44,10 +44,11 @@ #include "bacapp.h" #include "bacdcode.h" #include "bacint.h" +#include "bacreal.h" +#include "bacstr.h" #include "bacdef.h" #include "bacenum.h" #include "bacerror.h" -#include "bacstr.h" #include "bactext.h" #include "datalink.h" #include "indtext.h" diff --git a/bacnet-stack/include/iam.h b/bacnet-stack/include/iam.h index 3c5674da..074749cb 100644 --- a/bacnet-stack/include/iam.h +++ b/bacnet-stack/include/iam.h @@ -50,6 +50,11 @@ extern "C" { uint32_t * pDevice_id, unsigned *pMax_apdu, int *pSegmentation, uint16_t * pVendor_id); + int iam_encode_pdu( + uint8_t * buffer, + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data); + int iam_send(uint8_t * buffer); #ifdef TEST diff --git a/bacnet-stack/ports/atmega168/ai.c b/bacnet-stack/ports/atmega168/ai.c index efbacc29..399c0264 100644 --- a/bacnet-stack/ports/atmega168/ai.c +++ b/bacnet-stack/ports/atmega168/ai.c @@ -64,6 +64,13 @@ uint32_t Analog_Input_Index_To_Instance(unsigned index) return index; } +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Instance_To_Index(uint32_t object_instance) +{ + return object_instance; +} + + char *Analog_Input_Name(uint32_t object_instance) { static char text_string[5] = "AI-0"; /* okay for single thread */ @@ -87,6 +94,8 @@ int Analog_Input_Encode_Property_APDU(uint8_t * apdu, int apdu_len = 0; /* return value */ BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; + unsigned object_index; + (void) array_index; switch (property) { @@ -108,8 +117,9 @@ int Analog_Input_Encode_Property_APDU(uint8_t * apdu, OBJECT_ANALOG_INPUT); break; case PROP_PRESENT_VALUE: + object_index = Analog_Input_Instance_To_Index(object_instance); apdu_len = encode_application_real(&apdu[0], - Present_Value[object_instance]); + Present_Value[object_index]); break; case PROP_STATUS_FLAGS: bitstring_init(&bit_string); diff --git a/bacnet-stack/ports/atmega168/av.c b/bacnet-stack/ports/atmega168/av.c new file mode 100644 index 00000000..fe318e67 --- /dev/null +++ b/bacnet-stack/ports/atmega168/av.c @@ -0,0 +1,310 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_ANALOG_VALUES 9 +#if (MAX_ANALOG_VALUES > 9) + #error Modify the Analog_Value_Name to handle multiple digits +#endif + +float Present_Value[MAX_ANALOG_VALUES]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count(void) +{ + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + Analog_Value_Init(); + if (object_instance < MAX_ANALOG_VALUES) + index = object_instance; + + return index; +} + +/* note: the object name must be unique within this device */ +char *Analog_Value_Name(uint32_t object_instance) +{ + static char text_string[5] = "AV-0"; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_VALUES) { + text_string[3] = '0' + (uint8_t)object_instance; + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_Encode_Property_APDU(uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index; + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Value_Name(object_instance)); + apdu_len = encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + break; + case PROP_PRESENT_VALUE: + object_index = Analog_Value_Instance_To_Index(object_instance); + apdu_len = encode_application_real(&apdu[0], Present_Value[object_index]); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +#if 0 +/* returns true if successful */ +bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + uint8_t level = ANALOG_LEVEL_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Analog_Value_Init(); + if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Real >= 0.0) && (value.type.Real <= 100.0)) { + level = (uint8_t) value.type.Real; + object_index = + Analog_Value_Instance_To_Index(wp_data-> + object_instance); + priority--; + Present_Value[object_index] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#if 0 + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = ANALOG_LEVEL_NULL; + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Present_Value[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#endif + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#if 0 + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAnalog_Value(Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = Analog_Value_Encode_Property_APDU(&apdu[0], + instance, + PROP_OBJECT_IDENTIFIER, + BACNET_ARRAY_ALL, &error_class, &error_code); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], + (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == OBJECT_ANALOG_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_ANALOG_VALUE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalog_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_VALUE */ +#endif /* TEST */ diff --git a/bacnet-stack/ports/atmega168/dlmstp.c b/bacnet-stack/ports/atmega168/dlmstp.c index a854fe7f..d7034301 100644 --- a/bacnet-stack/ports/atmega168/dlmstp.c +++ b/bacnet-stack/ports/atmega168/dlmstp.c @@ -43,6 +43,8 @@ #include "npdu.h" #include "bits.h" #include "bacaddr.h" +#include "iam.h" +#include "txbuf.h" /* This file has been customized for use with small microprocessors */ /* Assumptions: @@ -270,114 +272,26 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, uint8_t mstp_address) } } -static bool dlmstp_compare_data_expecting_reply( - uint8_t *request_pdu, - uint16_t request_pdu_len, - uint8_t src_address, - uint8_t *reply_pdu, - uint16_t reply_pdu_len, - uint8_t dest_address) -{ - uint16_t offset; - /* One way to check the message is to compare NPDU - src, dest, along with the APDU type, invoke id. - Seems a bit overkill */ - struct DER_compare_t { - BACNET_NPDU_DATA npdu_data; - BACNET_ADDRESS address; - uint8_t pdu_type; - uint8_t invoke_id; - uint8_t service_choice; - }; - struct DER_compare_t request; - struct DER_compare_t reply; +/* I Am from handler */ +extern bool Send_I_Am; - /* decode the request data */ - request.address.mac[0] = src_address; - request.address.mac_len = 1; - offset = npdu_decode(&request_pdu[0], - NULL, &request.address, &request.npdu_data); - if (request.npdu_data.network_layer_message) { - return false; - } - request.pdu_type = request_pdu[offset] & 0xF0; - if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { - return false; - } - request.invoke_id = request_pdu[offset+2]; - /* segmented message? */ - if (request_pdu[offset] & BIT3) - request.service_choice = request_pdu[offset+5]; - else - request.service_choice = request_pdu[offset+3]; - /* decode the reply data */ - reply.address.mac[0] = dest_address; - reply.address.mac_len = 1; - offset = npdu_decode(&reply_pdu[0], - &reply.address, NULL, &reply.npdu_data); - if (reply.npdu_data.network_layer_message) { - return false; - } - /* reply could be a lot of things: - confirmed, simple ack, abort, reject, error */ - reply.pdu_type = reply_pdu[offset] & 0xF0; - switch (reply.pdu_type) { - case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - reply.invoke_id = reply_pdu[offset+2]; - /* segmented message? */ - if (reply_pdu[offset] & BIT3) - reply.service_choice = reply_pdu[offset+5]; - else - reply.service_choice = reply_pdu[offset+3]; - break; - case PDU_TYPE_SIMPLE_ACK: - reply.invoke_id = reply_pdu[offset+1]; - reply.service_choice = reply_pdu[offset+2]; - break; - case PDU_TYPE_COMPLEX_ACK: - reply.invoke_id = reply_pdu[offset+1]; - /* segmented message? */ - if (reply_pdu[offset] & BIT3) - reply.service_choice = reply_pdu[offset+4]; - else - reply.service_choice = reply_pdu[offset+2]; - break; - case PDU_TYPE_ERROR: - reply.invoke_id = reply_pdu[offset+1]; - reply.service_choice = reply_pdu[offset+2]; - break; - case PDU_TYPE_REJECT: - case PDU_TYPE_ABORT: - reply.invoke_id = reply_pdu[offset+1]; - break; - default: - return false; - } - /* these don't have service choice included */ - if ((reply.pdu_type == PDU_TYPE_REJECT) || - (reply.pdu_type == PDU_TYPE_ABORT)) { - if (request.invoke_id != reply.invoke_id) { - return false; - } - } else { - if (request.invoke_id != reply.invoke_id) { - return false; - } - if (request.service_choice != reply.service_choice) { - return false; - } - } - if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { - return false; - } - if (request.npdu_data.priority != reply.npdu_data.priority) { - return false; - } - if (!bacnet_address_same(&request.address, &reply.address)) { - return false; +/* look at any of the unconfirmed message bits and encode if set */ +static uint16_t dlmstp_encode_unconfirmed_frame(void) +{ + BACNET_ADDRESS dest; + BACNET_NPDU_DATA npdu_data; + uint16_t len = 0; + + if (Send_I_Am) { + Send_I_Am = false; + TransmitPacket = Handler_Transmit_Buffer; + len = iam_encode_pdu( + &TransmitPacket[0], + &dest, + &npdu_data); } - return true; + return len; } /* MS/TP Frame Format */ @@ -690,7 +604,6 @@ static bool MSTP_Master_Node_FSM(void) uint8_t next_next_station = 0; /* timeout values */ uint16_t my_timeout = 10, ns_timeout = 0; - bool matched; /* transition immediately to the next state */ bool transition_now = false; @@ -782,49 +695,24 @@ static bool MSTP_Master_Node_FSM(void) /* more data frames. These may be BACnet Data frames or */ /* proprietary frames. */ case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: optimized for minimal server: + we only send unconfirmed frames when we get the token */ /* Note: We could wait for up to Tusage_delay */ - if (!MSTP_Flag.TransmitPacketPending) { - /* NothingToSend */ - FrameCount = Nmax_info_frames; - Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; - transition_now = true; - } else { - uint8_t frame_type; - if (MSTP_Flag.TransmitPacketDER) { - frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; - } else { - frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; - } + TransmitPacketLen = dlmstp_encode_unconfirmed_frame(); + if (TransmitPacketLen) { MSTP_Send_Frame( - frame_type, - TransmitPacketDest, + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY, + MSTP_BROADCAST_ADDRESS, This_Station, (uint8_t *) & TransmitPacket[0], TransmitPacketLen); - MSTP_Flag.TransmitPacketPending = false; FrameCount++; - switch (frame_type) { - case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: - /* SendAndWait */ - if (TransmitPacketDest == MSTP_BROADCAST_ADDRESS) - Master_State = - MSTP_MASTER_STATE_DONE_WITH_TOKEN; - else - Master_State = - MSTP_MASTER_STATE_WAIT_FOR_REPLY; - break; - case FRAME_TYPE_TEST_REQUEST: - Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; - break; - case FRAME_TYPE_TEST_RESPONSE: - case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: - default: - /* SendNoWait */ - Master_State = - MSTP_MASTER_STATE_DONE_WITH_TOKEN; - break; - } + } else { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + transition_now = true; } + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; break; case MSTP_MASTER_STATE_WAIT_FOR_REPLY: /* In the WAIT_FOR_REPLY state, the node waits for */ diff --git a/bacnet-stack/ports/atmega168/h_wp.c b/bacnet-stack/ports/atmega168/h_wp.c index 88cf2f1f..deef0347 100644 --- a/bacnet-stack/ports/atmega168/h_wp.c +++ b/bacnet-stack/ports/atmega168/h_wp.c @@ -78,6 +78,7 @@ void handler_write_property(uint8_t * service_request, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); } else { switch (wp_data.object_type) { +#if 0 case OBJECT_DEVICE: if (Device_Write_Property(&wp_data, &error_class, &error_code)) { len = @@ -92,8 +93,7 @@ void handler_write_property(uint8_t * service_request, error_code); } break; -#if 0 - case OBJECT_ANALOG_INPUT: + case OBJECT_ANALOG_INPUT: case OBJECT_BINARY_INPUT: error_class = ERROR_CLASS_PROPERTY; error_code = ERROR_CODE_WRITE_ACCESS_DENIED; @@ -117,6 +117,7 @@ void handler_write_property(uint8_t * service_request, error_code); } break; +#endif case OBJECT_ANALOG_VALUE: if (Analog_Value_Write_Property(&wp_data, &error_class, &error_code)) { @@ -132,8 +133,7 @@ void handler_write_property(uint8_t * service_request, error_code); } break; -#endif - default: + default: len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY, diff --git a/bacnet-stack/ports/atmega168/main.c b/bacnet-stack/ports/atmega168/main.c index 521eaff0..84af9c70 100644 --- a/bacnet-stack/ports/atmega168/main.c +++ b/bacnet-stack/ports/atmega168/main.c @@ -38,9 +38,6 @@ /* For porting to IAR, see: http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/IarToAvrgcc*/ -/* broadcast an I-Am on startup */ -extern bool Send_I_Am; - /* dummy function */ bool dcc_communication_enabled(void) { return true; @@ -166,10 +163,6 @@ int main(void) #endif datalink_init(NULL); for (;;) { - if (Send_I_Am) { - Send_I_Am = false; - iam_send(&Handler_Transmit_Buffer[0]); - } input_switch_read(); task_milliseconds(); /* other tasks */ diff --git a/bacnet-stack/src/bacdcode.c b/bacnet-stack/src/bacdcode.c index ab561ca6..26ec15ec 100644 --- a/bacnet-stack/src/bacdcode.c +++ b/bacnet-stack/src/bacdcode.c @@ -40,6 +40,7 @@ #include "bits.h" #include "bacstr.h" #include "bacint.h" +#include "bacreal.h" /* NOTE: byte order plays a role in decoding multibyte values */ /* http://www.unixpapa.com/incnote/byteorder.html */ diff --git a/bacnet-stack/src/iam.c b/bacnet-stack/src/iam.c index e1cfe452..f7e3458d 100755 --- a/bacnet-stack/src/iam.c +++ b/bacnet-stack/src/iam.c @@ -135,10 +135,30 @@ int iam_decode_service_request(uint8_t * apdu, return apdu_len; } -int iam_send(uint8_t * buffer) +int iam_encode_pdu(uint8_t * buffer, + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data) { int len = 0; int pdu_len = 0; + + /* I-Am is a global broadcast */ + datalink_get_broadcast_address(dest); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&buffer[0], dest, NULL, npdu_data); + /* encode the APDU portion of the packet */ + len = iam_encode_apdu(&buffer[pdu_len], + Device_Object_Instance_Number(), + MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier()); + pdu_len += len; + + return pdu_len; +} + +int iam_send(uint8_t * buffer) +{ + int pdu_len = 0; BACNET_ADDRESS dest; int bytes_sent = 0; BACNET_NPDU_DATA npdu_data; @@ -146,17 +166,8 @@ int iam_send(uint8_t * buffer) /* if we are forbidden to send, don't send! */ if (!dcc_communication_enabled()) return 0; - - /* I-Am is a global broadcast */ - datalink_get_broadcast_address(&dest); - /* encode the NPDU portion of the packet */ - npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); - pdu_len = npdu_encode_pdu(&buffer[0], &dest, NULL, &npdu_data); - /* encode the APDU portion of the packet */ - len = iam_encode_apdu(&buffer[pdu_len], - Device_Object_Instance_Number(), - MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier()); - pdu_len += len; + /* encode the data */ + pdu_len = iam_encode_pdu(buffer, &dest, &npdu_data); /* send data */ bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);