BACnet4J + Android Things
-
We're trying to implement BACnet on our Android Things device and we're wondering if/how we can use the BACnet4J library to accomplish this. Our device will act as a thermostat/heat pump controller.
Our setup:
- MSTP network
- Using UART/RS-485 to communicate from Android Things device (master) to a BACnet-ready heat pump (another master)
- Will be adding more devices to the MSTP network in the future
I'm not exactly sure how to connect the way Android reads in bytes from UART to the BACnet4J library. Here is the documentation for Android Things UART: https://developer.android.com/things/sdk/pio/uart. With the setup described in the documentation (using the callback), I am able to read in bytes from the heat pump. Right now I can see by looking at the byte arrays coming in that it is polling for master, i.e.
55 ff 1 0 18 0 0 91 55 ff 1 1 18 0 0 a2 55 ff 1 2 18 0 0 3a ...
My question is how do I convert the incoming bytes from the heat pump (and other devices on the network) into service requests/objects that my device can understand and respond to if necessary.
Also, I understand that I need to set up my Android Things device as a LocalDevice. However the localDevice needs a Transport, which needs a (Mstp)Network, which needs a (Mstp)Node. I don't know how to set all this up. If someone could describe how to implement this hierarchy it would be greatly appreciated. Specifically
- How do I set up the MstpNetwork? How do I choose the
localNetworkNumber
? - MstpNetwork needs an MstpNode. How does a Node differ from a local/remote device? Why is there only one Node per network? What should I provide for the
portId
parameter?
I think it's probably obvious that I am new to BACnet and the BACnet4J library. I would be super grateful for any help!
Thank you
Joshua -
Hi Joshua, welcome to the forum!
I think it's probably obvious that I am new to BACnet and the BACnet4J library. I would be super grateful for any help!
No worries! The support here for BACnet4J here is not as zealous as Mango at large, but I will try to help when I think I can.
I'm not exactly sure how to connect the way Android reads in bytes from UART to the BACnet4J library.
I would consider opening the input and ouput streams, and then using the MstpNode constructor that takes input and output streams. This will enable the portId variable you asked about to be any string (I think it's only used to make the information available, not used). The constructors require either the streams are already open or that you pass in a SerialPortWrapper in. So, you can just pass in the UART stream. If you are looking to bridge the two networks, then you'll need a LocalDevice on each, and your code will be reading from one of them, and setting the object values on the other (so that it can be polled by the other devices on the 485 bus (or they could have registered for COV on those objects, or you can set them out to the devices)
Also, I understand that I need to set up my Android Things device as a LocalDevice. However the localDevice needs a Transport, which needs a (Mstp)Network, which needs a (Mstp)Node. I don't know how to set all this up.
Well you're essentially correct! I would think you'd be looking for something like....
//pseudocode byte stationId = 1; int networkNumber = 0; int localDeviceId = 123; InputStream in = getUartInputStream(); OutputStream out = getUartOutputStream(); MstpNode mstpNode = new MasterNode("UART", in, out, stationId); //Must be unique station ID on the UART //Make calls on mstpNode to set max frames, usage timeout, whatever MstpNetwork network = new MstpNetwork(mstpNode, networkNumber); Transport transport = new DefaultTransport(network); //Set transport settings like timeout, segment window, whatever LocalDevice localDevice = new LocalDevice(localDeviceId, transport); //Set up the device object, add any other objects you want available, localDevice.getDeviceObject()
How do I choose the localNetworkNumber?
Is determined if there is some topology where one BACnet device may be a router to another network. If you don't have to worry about that, you can just set your network number to 0 which is the local network number. But, if the devices it is communicating with have a network number set (and there aren't any gateways or IP to serial type devices) just use that device's network number. It only comes in to play in more complicated networks where a device will announce it routes requests to a particular network on behalf of devices on the network it advertises that on.
My question is how do I convert the incoming bytes from the heat pump (and other devices on the network) into service requests/objects that my device can understand and respond to if necessary.
It's talking BACnet over the UART, yes? You can send a COV subscription and handle incoming COV notifications to write properties on objects you've addObject'ed to on the second local device, the local device on the rs-485 network.
MstpNetwork needs an MstpNode. How does a Node differ from a local/remote device? Why is there only one Node per network?
I think those are semantic questions, but I am not the original author and only know so-much about the BACnet specification and the decisions made in implementing it in BACnet4J. I would say a distinction between an MstpNode and a LocalDevice is that a LocalDevice is a higher level abstraction of BACnet workings. It's just some separation of protocol and application.
-
Hi Phil,
Thank you very much for your detailed reply.
My main confusion is how the InputStream works. An InputStream does not represent a continuous stream of data, it only contains a fixed number of bytes, and there's no way to 'update' the input stream to add more bytes, without creating a new one. So I don't know how the library keeps on reading the continuous data from the device via an InputStream.
-
An InputStream does not represent a continuous stream of data, it only contains a fixed number of bytes, and there's no way to 'update' the input stream to add more bytes,
I don't think that's correct. An input stream is an abstract class and the underlying implementation may or may not have additional incoming bytes. Take for instances a JsscSerialPortInputStream from the Mango core: It receives incoming serial information from the JSSC library, which has native code included in it to handle the interface with the serial port. It puts that in a buffer, and then it's readable. Not having bytes available at a given moment definitely doesn't mean an input stream won't have bytes in the future. https://github.com/infiniteautomation/ma-core-public/blob/main/Core/src/com/infiniteautomation/mango/io/serial/JsscSerialPortInputStream.java
You can see in the MasterNode class the doCycle method, which is invoked in the run() method of MstpNode, which is a runnable and can start its own thread (see MstpNode initialize(final boolean runInThread) method).
-
Thanks Phil, I wasn't understanding the input stream concept correctly. I understand now I will have to provide my own implementation of InputStream/OutputStream that uses the Android Uart API.
Thanks for clearing up my confusion!
Joshua -
If you don't have to worry about that, you can just set your network number to 0 which is the local network number.
Great, I will use 0 for the network number.
If you are looking to bridge the two networks, then you'll need a LocalDevice on each, and your code will be reading from one of them, and setting the object values on the other (so that it can be polled by the other devices on the 485 bus (or they could have registered for COV on those objects, or you can set them out to the devices)
Could you explain this a little more further? I thought in our situation there's only one network, so I'm not quite sure what you mean by bridging the two networks.
As I understand it, I need to set up my Android device as a LocalDevice and I will send commands to/subscribe to COVs on the heat pump, which will be a RemoteDevice. Do I need a second LocalDevice somewhere?
Thanks for your help, I appreciate it.
-
Sure. I meant that as an aside to anyone who may stumble onto this thread and was in need of something like connecting a mstp network and an ip network; to say there is not to my knowledge any prebuilt MSTP-IP converter functionality in the LocalDevice code. Could also have been a useful inclusion if I didn't fully understand your ambition. The sentence does being with 'if' :D