make simple read request app
Brought to you by:
skarg
Im in the src
directory of the project. Am a bit of a begineer here...
Could someone give me a tip? This is my main.c
where I am experimenting with creating a read request app with some hard coded values to start with. This is my main.c:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/apdu.h"
#include "bacnet/npdu.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/whois.h"
#include "bacnet/iam.h"
#include "bacnet/rpm.h"
#define TARGET_DEVICE_INSTANCE 201201
#define TARGET_OBJECT_TYPE OBJECT_ANALOG_INPUT
#define TARGET_OBJECT_INSTANCE 1
#define TARGET_PROPERTY_ID PROP_PRESENT_VALUE
// Make Error, Abort, and Reject Handlers here...
// Callback function for Read Property Acknowledgement
void MyReadPropertyCallback(
uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data) {
printf("Read Property Callback\n");
}
int main() {
BACNET_ADDRESS target;
uint32_t device_instance;
unsigned max_apdu = 0;
unsigned timeout = 100; // ms
unsigned retries = 3;
uint8_t invoke_id = 0;
// Initialize the BACnet stack
Device_Init(NULL);
dlenv_init();
// Register the Read Property callback
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyReadPropertyCallback);
// Send a WhoIs to discover devices on the network
Send_WhoIs(-1, -1);
// Wait for responses or a timeout
for (int i = 0; i < 100; i++) {
dlenv_maintenance();
}
// Assuming we know the device instance of the target
device_instance = TARGET_DEVICE_INSTANCE;
// Send the read property request
invoke_id = Send_Read_Property_Request(
device_instance,
TARGET_OBJECT_TYPE,
TARGET_OBJECT_INSTANCE,
TARGET_PROPERTY_ID,
BACNET_ARRAY_ALL
);
if (invoke_id > 0) {
// Wait for the response or a timeout
for (int i = 0; i < 100; i++) {
dlenv_maintenance();
}
} else {
// Error handling if sending the request fails
fprintf(stderr, "Failed to send Read Property Request!\n");
}
return 0;
}
And Makefile:
# Makefile to build your BACnet Application using GCC
# Compiler
CC := gcc
# Compiler flags
CFLAGS := -g -I./ -I./bacnet -I./bacnet/basic -I./bacnet/basic/service -I./bacnet/basic/bbmd -I./bacnet/datalink
# Executable file name
EXECUTABLE := bacnet_app
# BACnet source directory
BACNET_SRC_DIR := ./bacnet
# Source files
SRCS := $(wildcard *.c) \
$(wildcard $(BACNET_SRC_DIR)/*.c) \
$(wildcard $(BACNET_SRC_DIR)/basic/*.c) \
$(wildcard $(BACNET_SRC_DIR)/basic/service/*.c) \
$(wildcard $(BACNET_SRC_DIR)/basic/bbmd/*.c) \
$(wildcard $(BACNET_SRC_DIR)/datalink/*.c)
# Object files
OBJS := $(SRCS:.c=.o)
# Default target
all: $(EXECUTABLE)
# Linking the executable
$(EXECUTABLE): $(OBJS)
$(CC) -o $@ $^
# Compiling source files
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Clean target
clean:
rm -f $(OBJS) $(EXECUTABLE)
.PHONY: all clean
But when I do a make clean
and then run make
it does appear to compile for a while but then appears to error out on alot of undefined reference
errors before it exists early. Its a lot of these types of errors below, any tips appreciated!
/home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:582: undefined reference to `bip_debug_enable'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:588: undefined reference to `bip_set_port'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:596: undefined reference to `bip_get_port'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:597: undefined reference to `bip_set_port'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:602: undefined reference to `bip_set_broadcast_binding'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:606: undefined reference to `bip_get_addr_by_name'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:654: undefined reference to `bip_init'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/dlenv.c:660: undefined reference to `tsm_invokeID_set'
/usr/bin/ld: bacnet/datalink/mstp.o: in function `MSTP_Create_And_Send_Frame':
/home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:336: undefined reference to `MSTP_Send_Frame'
/usr/bin/ld: bacnet/datalink/mstp.o: in function `MSTP_Master_Node_FSM':
/home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:767: undefined reference to `MSTP_Put_Receive'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:777: undefined reference to `MSTP_Put_Receive'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:818: undefined reference to `MSTP_Get_Send'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:827: undefined reference to `MSTP_Send_Frame'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:898: undefined reference to `MSTP_Put_Receive'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:1196: undefined reference to `MSTP_Get_Reply'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:1206: undefined reference to `MSTP_Send_Frame'
/usr/bin/ld: bacnet/datalink/mstp.o: in function `MSTP_Slave_Node_FSM':
/home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:1266: undefined reference to `MSTP_Get_Reply'
/usr/bin/ld: /home/bbartling/bacnet-stack/src/bacnet/datalink/mstp.c:1278: undefined reference to `MSTP_Send_Frame'
Anonymous
Typical usage of the library uses some defines to choose a single datalink: BACDL_BIP is usually the default, and this will then - via the apps/Makefile - choose a datalink to include in the library - apps/lib/Makefile. Either the apps/lib/libbacnet.a doesn't exist, or was not built for a datalink your code is referencing. The BACDL_BIP would also get used by datalink.h file, which then uses a macro to configure which datalink_xyz() functions are used in your build.
Note: there is an example project of a BACnet server/client reading values from other devices. See apps/server-client.
Hi Steve,
When you send out a
who_is
can you print/debug the response? IE.,I am working with a function like this below for learning purposes... where in Wireshark I can see the request was successful which confirms its working, and I was just curious to see if there is an easy way to print the MAC address or processed contents of the
i_am
request from the device? Or would I need create my own handler for this and loop through the contents?Last edit: Ben Bartling 2024-01-03
The simplest method is to use a custom handler. The apps/whois/main.c does that with my_i_am_handler() function. The custom handler is configured by setting the unconfirmed handler:
Hi Steve,
On a
My_Read_Property_Ack_Handler
is it possible to return the processed/formatted data Vs just print it?Thanks!
Ben
As I mentioned earlier: there is an example project of a BACnet server/client reading values from other devices. See apps/server-client/main.c module. The application uses a helper task and state machine in src/bacnet/basic/client/bac-rw.c module to perform a ReadProperty or WriteProperty to a remote device. The module includes a callback function which includes all the parameters of the ReadPropertyACK when the data is received, and there are also hooks for errors or timeouts.
Hi @skarg,
Thanks for all the info and tips so far... when I use the
bacnet-stack/apps/whois $ ./bacwi 201201
for a device on my test bench it returns:Would this MAC in hex be what would be required when using the
apps/readprop
when using the arg-dnet
to perform a read request using a the MAC address of the device?Any chance could you give me a tip on how to perform a read prop request with using the MAC address and not using the BACnet instance ID? For example I have been using the app like this:
$ ./bacrp 201201 analog-input 2 present-value
Thank you!
Ben
There are two methods to bind without specifically using the Device ID:
The demo apps compile the src/bacnet/basic/binding/address.c module to include the initialization from a file named "address_cache" which is formatted as the output of "bacwi" Who-Is application. This imports the data into the address.c address binding table and when the demo app looks up the device it returns the address listed. For example:
The ReadProperty app also allows 3 additional arguments:
[--dnet][--dadr][--mac]
which correspond to the values returned by Who-Is:The ReadProperty app include more info about each parameter in the detailed `--help'
Hi Steve,
Could I ever get a tip for how to pass in a
--mac
address arg to the readprop app?In wireshark my 201201 device is this on an Iam response can a person get the MAC address from this?
Thanks much!
Is it the
00:50:db:00:d6:c2
inside theEthernet II, Src: Contemporary_00:d6:c2 (00:50:db:00:d6:c2), Dst: Broadcast (ff:ff:ff:ff:ff:ff)
that represents the source address orsrc
?The address inside the Ethernet II frame is the Ethernet MAC address, which is resolved to IPv4 addresses using ARP (address resolution protocol). You can usually list the binding table of ARP using
arp -a
from the command line.For BACnet/IP, the IP address and UDP port number are used for addressing, not the Ethernet MAC address.
When using
--mac
option, the tools still need a device-id for pseudo-binding. Note that 4194303 is a wildcard number for the device object in newer protocol revisions:If you want the device downstream of the router, you'll need to use
--dnet
and--dadr