Files
Testimony 17e94092a1 Add Raspberry Pi Pico port (#1232)
* Add Raspberry Pi Pico port
- Adds BACnet/IP with abstracted network layer
- Adds BACnet MS/TP support using RS485/UART
- Includes README with examples

* Add Pico 2 MS/TP demo, CI, and docs
2026-03-25 08:22:09 -05:00

163 lines
4.5 KiB
Markdown

# BACnet Stack Port for Raspberry Pi Pico
This directory contains the Raspberry Pi Pico port of the BACnet stack, supporting both BACnet/IP and BACnet MS/TP.
## Overview
This port provides:
- **BACnet/IP** support with abstracted network layer (Developer must provide Ethernet implementation)
- **BACnet MS/TP** support using UART (RS485)
## Hardware Used
The port is written against the Raspberry Pi Pico SDK. The same code is intended to build for boards that the Pico SDK exposes with compatible UART/GPIO support, such as:
- `pico`
- `pico_w`
- `pico2`
- `pico2_w`
The current `make pico-cmake` flow defaults to the Pico SDK board `pico2` for this port unless `-DPICO_BOARD=...` is passed to CMake.
For the MS/TP side, the code currently assumes:
- UART1 on GPIO 8 (`TX`) and GPIO 9 (`RX`)
- GPIO 10 as the RS485 transceiver `DE`/`RE` control pin
- An external 3.3 V RS485 transceiver module, for example a MAX3485- or SN65HVD-based module
Typical wiring is:
- Pico/Pico 2 GPIO 8 -> transceiver `DI`
- Pico/Pico 2 GPIO 9 -> transceiver `RO`
- Pico/Pico 2 GPIO 10 -> transceiver `DE` and `RE`
- Pico/Pico 2 `3V3` -> transceiver `VCC`
- Pico/Pico 2 `GND` -> transceiver `GND`
- Transceiver `A/B` -> BACnet MS/TP trunk
No external hardware is needed for the automated CI build because the CI job only verifies that the Pico MS/TP target compiles.
## Software Dependencies
The external dependency required for this port is cloned by `ports/pico/configure.sh` into `ports/pico/external/`:
- Raspberry Pi Pico SDK 2.2.0
URL: <https://github.com/raspberrypi/pico-sdk>
The script checks out that SDK tag and initializes the SDK submodules needed by the build.
## Build
From the repository root:
```sh
make pico-cmake
```
That target runs `ports/pico/configure.sh`, configures the CMake build in `ports/pico/build`, and builds the Pico port for `pico2`.
To select another supported board, configure manually and pass `PICO_BOARD`:
```sh
cd ports/pico
./configure.sh
cmake -S . -B build -DPICO_SDK_PATH="$PWD/external/pico-sdk" -DPICO_BOARD=pico2
cmake --build build -- -j"$(nproc)"
```
## Using This Port
### Implement Network Functions
Developer must implement the following functions declared in `bip.h`:
```c
bool bip_socket_init(uint16_t port);
int bip_socket_send(
const uint8_t *dest_addr,
uint16_t dest_port,
const uint8_t *mtu,
uint16_t mtu_len);
int bip_socket_receive(
uint8_t *buf,
uint16_t buf_len,
uint8_t *src_addr,
uint16_t *src_port);
void bip_socket_cleanup(void);
bool bip_get_local_network_info(uint8_t *local_addr, uint8_t *netmask);
```
These BACnet/IP hooks are examples of the interface you need to provide. The repository does not yet include a complete in-tree Pico Ethernet or Wi-Fi transport implementation, so BACnet/IP still requires board-specific network code behind `bip.h`.
### Example Usage
```c
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"
#include "mstimer_init.h"
#include "bip.h"
/* BACnet Stack Includes */
#include "bacnet/datalink/datalink.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/dcc.h"
static struct mstimer DCC_Timer;
int main() {
// Initialize Pico
stdio_init_all();
systimer_init();
// Initialize BIP datalink
Device_Set_Object_Instance_Number(12345);
Device_Init(NULL);
address_init();
// Initialize BACnet/IP on port 0xBAC0 (47808)
if (!bip_init(0xBAC0)) {
return -1;
}
/* set up confirmed service unrecognized service handler - required! */
Send_I_Am(&Handler_Transmit_Buffer[0]);
mstimer_set(&DCC_Timer, 1000); // 1 second timer
printf("BACnet BIP Initialized");
BACNET_ADDRESS src = {0};
uint8_t rx_buf[480];
const uint16_t max_pdu = sizeof(rx_buf);
while (1)
{
if (mstimer_expired(&DCC_Timer))
{
mstimer_reset(&DCC_Timer);
dcc_timer_seconds(1);
}
uint16_t pdu_len = bip_receive(&src, rx_buf, max_pdu, 0);
if (pdu_len > 0)
{
npdu_handler(&src, rx_buf, pdu_len);
printf("%u bytes received from MAC %u\r\n", pdu_len, src.mac[0]);
for (int i=0;i<pdu_len;i++) printf("%02X ", rx_buf[i]);
printf("\n");
}
}
return 0;
}
```
## BACnet MS/TP (RS485)
For a minimal MS/TP example for this port, see `main.c`.
The RS485 driver is ready to use without modification. It uses the Pico SDK UART functions.