Skip to content

Instantly share code, notes, and snippets.

@bbartling
Created February 18, 2025 20:18
Show Gist options
  • Save bbartling/334b01026dcf74f78ab60f1d914f76d2 to your computer and use it in GitHub Desktop.
Save bbartling/334b01026dcf74f78ab60f1d914f76d2 to your computer and use it in GitHub Desktop.
Minimal BACnet Server Attempt
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* BACnet Stack includes */
#include "bacnet/bacdef.h"
#include "bacnet/apdu.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bactext.h"
#include "bacnet/dcc.h"
#include "bacnet/getevent.h"
#include "bacnet/iam.h"
#include "bacnet/npdu.h"
#include "bacnet/version.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/datalink/dlenv.h"
#include "bacnet/basic/binding/address.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/ai.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/bacstr.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/service/h_whois.h"
#include "bacnet/basic/service/h_rp.h"
#include "bacnet/basic/service/h_wp.h"
#include "bacnet/basic/service/h_apdu.h"
#include "bacnet/basic/service/s_iam.h"
/* Buffers */
static uint8_t Rx_Buf[MAX_MPDU] = {0};
/**
* @brief Initializes the BACnet objects (AI-0 and BV-0).
*/
static void Init_Service_Handlers(void)
{
BACNET_CHARACTER_STRING ai_name, bv_name;
/* Initialize device and objects */
Device_Init(NULL);
Analog_Input_Init();
Binary_Value_Init();
/* Create Analog Input 0 */
Analog_Input_Create(0);
Analog_Input_Present_Value_Set(0, 25.5);
characterstring_init_ansi(&ai_name, "Room Temperature");
Analog_Input_Object_Name(0, &ai_name);
printf("Created Analog Input AI-0: Room Temperature\n");
/* Create Binary Value 0 */
Binary_Value_Create(0);
Binary_Value_Present_Value_Set(0, 0);
characterstring_init_ansi(&bv_name, "Fan Control Relay");
Binary_Value_Object_Name(0, &bv_name);
printf("Created Binary Value BV-0: Fan Control Relay\n");
printf("Testing 123 \n");
/* Configure required BACnet service handlers */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
}
/**
* @brief Main entry point for the BACnet server.
*/
int main()
{
BACNET_ADDRESS src = {0}; /* Source address */
uint16_t pdu_len = 0;
unsigned timeout = 1000; /* 1 second */
printf("Starting Minimal BACnet Server...\n");
/* Initialize BACnet stack */
dlenv_init();
Init_Service_Handlers();
atexit(datalink_cleanup);
/* Announce device */
Send_I_Am(&Rx_Buf[0]);
/* Main loop */
for (;;)
{
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
if (pdu_len)
{
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
}
return 0;
}
@bbartling
Copy link
Author

Yes thanks for the tip on the out-of-service. I may start this process over with your fork : )

To me that seems odd why the bacnet stack doesn't have that feature for commandable points out of the box. To me it makes sense not to have for an AI, BI or hardware inputs by why not BV?

@nazaryev-cool
Copy link

From: Steve Karg
Sent: Tuesday, August 22, 2023 12:32 PM
To: Discussion for developers of the BACnet stack
Subject: Re: [Bacnet-developers] relinquish-default, priority-array

Hello Andreas,

The debate about priority-array in Value objects has been around since the inception of the BACnet standard (so I've been told). After lots of discussion in the BACnet committee related to HOA (hand-off-auto) in Input/Output/Value objects, I am convinced that there should not be a priority-array in value objects. If a priority-array (commandability) is needed, there exist output objects for that specific purpose, and therefore, I don't encourage using priority-array in the value objects in the examples in the library. That doesn't preclude someone from doing that - the examples are just that - examples.

In 2017, David Fisher proposed a Prioritization object (DMF-083). "While various object types support the concept of commandability, there is no standard mechanism for synchronizing access to non-commandable object types, or non-Present_Value properties." After working its way through working groups, in 2022 it was voted by SSPC for inclusion in a future addendum (subject to public review); not sure when it will become part of the BACnet standard.

Best Regards,
Steve

https://sourceforge.net/p/bacnet/mailman/message/37887217/

@bbartling
Copy link
Author

Okay so this works... I recompiled with your fork.

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* BACnet Stack includes */
#include "bacnet/bacdef.h"
#include "bacnet/apdu.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bactext.h"
#include "bacnet/dcc.h"
#include "bacnet/getevent.h"
#include "bacnet/iam.h"
#include "bacnet/npdu.h"
#include "bacnet/version.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/datalink/dlenv.h"
#include "bacnet/basic/binding/address.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/ai.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/basic/services.h"

#include "bacnet/basic/service/h_whois.h"
#include "bacnet/basic/service/h_rp.h"
#include "bacnet/basic/service/h_wp.h"
#include "bacnet/basic/service/h_apdu.h"
#include "bacnet/basic/service/s_iam.h"

/* Buffers */
static uint8_t Rx_Buf[MAX_MPDU] = {0};

/* Custom Object Table */
static object_functions_t My_Object_Table[] = {
    { OBJECT_DEVICE, NULL /* Init - don't init Device or it will recurse! */,
        Device_Count, Device_Index_To_Instance,
        Device_Valid_Object_Instance_Number, Device_Object_Name,
        Device_Read_Property_Local, Device_Write_Property_Local,
        Device_Property_Lists, DeviceGetRRInfo, NULL /* Iterator */,
        NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
        NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
        NULL /* Remove_List_Element */, NULL /* Create */, NULL /* Delete */,
        NULL /* Timer */ },

    { OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
        Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
        Analog_Input_Object_Name, Analog_Input_Read_Property,
        Analog_Input_Write_Property, Analog_Input_Property_Lists,
        NULL /* ReadRangeInfo */, NULL /* Iterator */,
        Analog_Input_Encode_Value_List, Analog_Input_Change_Of_Value,
        Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting,
        NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
        Analog_Input_Create, Analog_Input_Delete, NULL /* Timer */ },

    { OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count,
        Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance,
        Binary_Value_Object_Name, Binary_Value_Read_Property,
        Binary_Value_Write_Property, Binary_Value_Property_Lists,
        NULL /* ReadRangeInfo */, NULL /* Iterator */,
        Binary_Value_Encode_Value_List, Binary_Value_Change_Of_Value,
        Binary_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
        NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
        Binary_Value_Create, Binary_Value_Delete, NULL /* Timer */ },

    { MAX_BACNET_OBJECT_TYPE, NULL /* End of list marker */ }
};

/**
 * @brief Initializes the BACnet objects (AI-0 and BV-0).
 */
static void Init_Service_Handlers(void)
{
    BACNET_CHARACTER_STRING ai_name, bv_name;

    /* Initialize device and objects */
    Device_Init(My_Object_Table);

    /* Create Analog Input 0 */
    uint32_t room_temp_obj_instance = Analog_Input_Create(0);
    Analog_Input_Name_Set(room_temp_obj_instance, "Room Temperature");
    Analog_Input_Units_Set(room_temp_obj_instance, UNITS_DEGREES_CELSIUS);
    Analog_Input_Present_Value_Set(room_temp_obj_instance, 24.2f);
    printf("Created Analog Input AI-0: Room Temperature\n");

    /* Create Binary Value 0 (Fan Control Relay) */
    uint32_t fan_control_obj_instance = Binary_Value_Create(0);
    Binary_Value_Name_Set(fan_control_obj_instance, "Fan Control Relay");
    printf("Created Binary Value BV-0: Fan Control Relay\n");

    /* Configure required BACnet service handlers */
    apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
    apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
    apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
    apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
}

/**
 * @brief Main entry point for the BACnet server.
 */
int main()
{
    BACNET_ADDRESS src = {0}; /* Source address */
    uint16_t pdu_len = 0;
    unsigned timeout = 1000; /* 1 second */

    printf("Starting Minimal BACnet Server...\n");

    /* Initialize BACnet stack */
    dlenv_init();
    Init_Service_Handlers();
    atexit(datalink_cleanup);

    /* Announce device */
    Send_I_Am(&Rx_Buf[0]);

    /* Main loop */
    for (;;)
    {
        pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
        if (pdu_len)
        {
            npdu_handler(&src, &Rx_Buf[0], pdu_len);
        }
    }
    return 0;
}

image

Is it possible @nazaryev-cool to reiterate once more on commandable points in the bacnet-stack? I know BV being commandable is a custom feature of your fork ... If you had the time could you bullet point out some steps wheater its an AO, AV, BV, BO one creating an additional point and making it writeable/commandable? Thank you so much for just getting me here : )

@bbartling
Copy link
Author

Priority arr not working but there is a server app with 2 AVs one writeable and one BV.

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* BACnet Stack includes */
#include "bacnet/bacdef.h"
#include "bacnet/apdu.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bactext.h"
#include "bacnet/dcc.h"
#include "bacnet/getevent.h"
#include "bacnet/iam.h"
#include "bacnet/npdu.h"
#include "bacnet/version.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/datalink/dlenv.h"
#include "bacnet/basic/binding/address.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/ai.h"
#include "bacnet/basic/object/av.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/basic/services.h"

#include "bacnet/basic/service/h_whois.h"
#include "bacnet/basic/service/h_rp.h"
#include "bacnet/basic/service/h_wp.h"
#include "bacnet/basic/service/h_apdu.h"
#include "bacnet/basic/service/s_iam.h"

/* Buffers */
static uint8_t Rx_Buf[MAX_MPDU] = {0};

/* Custom Object Table */
static object_functions_t My_Object_Table[] = {
    { OBJECT_DEVICE, NULL /* Init - don't init Device or it will recurse! */,
        Device_Count, Device_Index_To_Instance,
        Device_Valid_Object_Instance_Number, Device_Object_Name,
        Device_Read_Property_Local, Device_Write_Property_Local,
        Device_Property_Lists, DeviceGetRRInfo, NULL /* Iterator */,
        NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
        NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
        NULL /* Remove_List_Element */, NULL /* Create */, NULL /* Delete */,
        NULL /* Timer */ },

    { OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
        Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
        Analog_Input_Object_Name, Analog_Input_Read_Property,
        Analog_Input_Write_Property, Analog_Input_Property_Lists,
        NULL /* ReadRangeInfo */, NULL /* Iterator */,
        Analog_Input_Encode_Value_List, Analog_Input_Change_Of_Value,
        Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting,
        NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
        Analog_Input_Create, Analog_Input_Delete, NULL /* Timer */ },

    { OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count,
        Analog_Value_Index_To_Instance, Analog_Value_Valid_Instance,
        Analog_Value_Object_Name, Analog_Value_Read_Property,
        Analog_Value_Write_Property, Analog_Value_Property_Lists,
        NULL /* ReadRangeInfo */, NULL /* Iterator */,
        Analog_Value_Encode_Value_List, NULL /* COV */, NULL /* COV Clear */, 
        NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, 
        NULL /* Remove_List_Element */, Analog_Value_Create, Analog_Value_Delete, NULL /* Timer */ },

    { OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count,
        Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance,
        Binary_Value_Object_Name, Binary_Value_Read_Property,
        Binary_Value_Write_Property, Binary_Value_Property_Lists,
        NULL /* ReadRangeInfo */, NULL /* Iterator */,
        Binary_Value_Encode_Value_List, Binary_Value_Change_Of_Value,
        Binary_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
        NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
        Binary_Value_Create, Binary_Value_Delete, NULL /* Timer */ },

    { MAX_BACNET_OBJECT_TYPE, NULL /* End of list marker */ }
};

/**
 * @brief Initializes the BACnet objects (AI-0, AV-0, and BV-0).
 */
static void Init_Service_Handlers(void)
{
    BACNET_CHARACTER_STRING ai_name, av_name, bv_name;

    /* Initialize device and objects */
    Device_Init(My_Object_Table);

    /* Create Analog Input 0 (Read-Only) */
    uint32_t room_temp_obj_instance = Analog_Input_Create(0);
    Analog_Input_Name_Set(room_temp_obj_instance, "Room Temperature");
    Analog_Input_Units_Set(room_temp_obj_instance, UNITS_DEGREES_CELSIUS);
    Analog_Input_Present_Value_Set(room_temp_obj_instance, 24.2f);
    printf("Created Analog Input AI-0: Room Temperature\n");

    /* Create Analog Value 0 (Commandable Setpoint) */
    uint32_t setpoint_obj_instance = Analog_Value_Create(0);
    Analog_Value_Name_Set(setpoint_obj_instance, "Commandable Setpoint");
    Analog_Value_Units_Set(setpoint_obj_instance, UNITS_DEGREES_CELSIUS);
    Analog_Value_Present_Value_Set(setpoint_obj_instance, 22.5, 8); // Priority 8
    printf("Created Analog Value AV-0: Commandable Setpoint\n");

    /* Create Binary Value 0 (Fan Control Relay) */
    uint32_t fan_control_obj_instance = Binary_Value_Create(0);
    Binary_Value_Name_Set(fan_control_obj_instance, "Fan Control Relay");
    printf("Created Binary Value BV-0: Fan Control Relay\n");

    /* Configure required BACnet service handlers */
    apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
    apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
    apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
    apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
}

/**
 * @brief Main entry point for the BACnet server.
 */
int main()
{
    BACNET_ADDRESS src = {0}; /* Source address */
    uint16_t pdu_len = 0;
    unsigned timeout = 1000; /* 1 second */

    printf("Starting Minimal BACnet Server...\n");

    /* Initialize BACnet stack */
    dlenv_init();
    Init_Service_Handlers();
    atexit(datalink_cleanup);

    /* Announce device */
    Send_I_Am(&Rx_Buf[0]);

    /* Main loop */
    for (;;)
    {
        pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
        if (pdu_len)
        {
            npdu_handler(&src, &Rx_Buf[0], pdu_len);
        }
    }
    return 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment