esp32: replace port with PlatformIO implementation and add CI build (#1292)
This commit is contained in:
@@ -328,3 +328,15 @@ jobs:
|
|||||||
gcc --version
|
gcc --version
|
||||||
make clean
|
make clean
|
||||||
make gtk-discover
|
make gtk-discover
|
||||||
|
|
||||||
|
ports-esp32:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install PlatformIO
|
||||||
|
run: |
|
||||||
|
python3 -m pip install --upgrade pip
|
||||||
|
pip3 install platformio
|
||||||
|
- name: Build ESP32 Port
|
||||||
|
run: |
|
||||||
|
pio run -d ports/esp32
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
# ESP32 Port
|
||||||
|
|
||||||
|
Date: 2026-04-04
|
||||||
|
Author: Kato Gangstad
|
||||||
|
|
||||||
|
This folder contains the ESP32 BACnet port built with PlatformIO.
|
||||||
|
It targets multiple ESP32-family boards and covers BACnet MS/TP, BACnet/IP, and BACnet Gateway (based on apps/gateway2) profiles.
|
||||||
|
It is based on the current lwIP port and Pico port patterns in this repository.
|
||||||
|
|
||||||
|
## Current Scope
|
||||||
|
|
||||||
|
- BACnet MS/TP transport for ESP32 boards with RS485
|
||||||
|
- BACnet/IP transport for compact WiFi and Ethernet ESP32 targets
|
||||||
|
- BACnet routing between BACnet/IP and BACnet MS/TP
|
||||||
|
|
||||||
|
## Port Identity
|
||||||
|
|
||||||
|
It is the ESP32 BACnet port for the current PlatformIO-based targets in this repository.
|
||||||
|
|
||||||
|
## Supported Board Profiles
|
||||||
|
|
||||||
|
The current board coverage includes:
|
||||||
|
|
||||||
|
- Seeed Studio XIAO ESP32C3 for ultra-compact BACnet/IP
|
||||||
|
- M5StamPLC for BACnet MS/TP and BACnet router builds
|
||||||
|
- Olimex ESP32-POE for BACnet/IP
|
||||||
|
|
||||||
|
The upstream BACnet stack has a broad source set and multiple app profiles.
|
||||||
|
This port selects the ESP32-specific transport, board, and application wiring needed for the environments documented below.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- src/main.cpp: Application loop, BACnet object table, and PLC I/O sync
|
||||||
|
- src/mstimer_init.c: millisecond timer hook used by mstimer
|
||||||
|
- src/rs485.c: ESP32 RS485 low-level driver for MS/TP
|
||||||
|
- src/dlenv.c: MS/TP datalink environment setup and port wiring
|
||||||
|
- src/bip_socket.cpp: BACnet/IP UDP socket bridge for WiFi and Ethernet targets
|
||||||
|
- extra_script.py: Injects BACnet core/basic sources into the PlatformIO build
|
||||||
|
|
||||||
|
## Build Notes
|
||||||
|
|
||||||
|
1. Open this folder as a PlatformIO project root, or run PlatformIO with this folder as cwd.
|
||||||
|
2. Start with the environment that matches your board and transport profile.
|
||||||
|
3. The `platformio.ini` file defines all tested board environments for this port.
|
||||||
|
4. Ensure PlatformIO CLI is available in your shell (`pio --version`).
|
||||||
|
5. Build with `pio run -e <environment-name>`.
|
||||||
|
|
||||||
|
## Tested Environments
|
||||||
|
|
||||||
|
The following PlatformIO environments have been built and uploaded successfully:
|
||||||
|
|
||||||
|
- `xiao-esp32c3-wifi-bip` on Seeed Studio XIAO ESP32C3
|
||||||
|
- `m5stamplc-mstp` on M5StamPLC
|
||||||
|
- `m5stamplc-gateway-bip-mstp` on M5StamPLC
|
||||||
|
- `esp32-poe-wifi-bip` on Olimex ESP32-POE
|
||||||
|
|
||||||
|
## Validation Tools
|
||||||
|
|
||||||
|
Interoperability and functional verification have been tested with:
|
||||||
|
|
||||||
|
- YABE
|
||||||
|
- Honeywell Eaglehawk 4.15
|
||||||
|
- Tridium Niagara 4.15
|
||||||
|
|
||||||
|
## Board Pictures
|
||||||
|
|
||||||
|
The following board photos document the hardware used for the tested environments.
|
||||||
|
|
||||||
|
### Seeed Studio XIAO ESP32C3
|
||||||
|
|
||||||
|
Used for:
|
||||||
|
|
||||||
|
- `xiao-esp32c3-wifi-bip`
|
||||||
|
|
||||||
|
Why this board stands out:
|
||||||
|
|
||||||
|
- Extremely compact form factor for a BACnet/IP target
|
||||||
|
- Physical size: 21 x 17.8 mm
|
||||||
|
- Strong candidate for one of the smallest BACnet/IP device ever !
|
||||||
|
|
||||||
|
Hardware summary:
|
||||||
|
|
||||||
|
- MCU: ESP32-C3
|
||||||
|
- CPU type: 32-bit single-core RISC-V
|
||||||
|
- CPU clock: 160 MHz
|
||||||
|
- RAM: 320 KB
|
||||||
|
- Flash: 4 MB
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### M5StamPLC
|
||||||
|
|
||||||
|
- BACnet server object model with Device + Binary Input + Binary Output objects
|
||||||
|
- Runtime mapping:
|
||||||
|
- BI 0..7 <- PLC inputs 1..8
|
||||||
|
- BO 0..3 -> PLC relays 1..4
|
||||||
|
- RS485 pin mapping aligned with the M5StamPLC hardware profile:
|
||||||
|
- TX: GPIO0
|
||||||
|
- RX: GPIO39
|
||||||
|
- DIR: GPIO46
|
||||||
|
|
||||||
|
Used for:
|
||||||
|
|
||||||
|
- `m5stamplc-mstp`
|
||||||
|
- `m5stamplc-bip`
|
||||||
|
- `m5stamplc-gateway-bip-mstp`
|
||||||
|
- soon to come `m5stamplc-poe-bip`
|
||||||
|
|
||||||
|
Hardware summary:
|
||||||
|
|
||||||
|
- MCU: ESP32-S3
|
||||||
|
- CPU clock: 240 MHz
|
||||||
|
- RAM: 320 KB
|
||||||
|
- Flash: 8 MB
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Olimex ESP32-POE
|
||||||
|
|
||||||
|
Used for:
|
||||||
|
|
||||||
|
- `esp32-poe-wifi-bip`
|
||||||
|
- `esp32-poe-eth-bip`
|
||||||
|
|
||||||
|
Hardware summary:
|
||||||
|
|
||||||
|
- MCU: ESP32
|
||||||
|
- CPU clock: 240 MHz
|
||||||
|
- RAM: 320 KB
|
||||||
|
- Flash: 4 MB
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"core": "esp32",
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "esp32s3",
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "dio"
|
||||||
|
},
|
||||||
|
"frameworks": [
|
||||||
|
"arduino"
|
||||||
|
],
|
||||||
|
"name": "M5Stack StamPLC",
|
||||||
|
"url": "https://m5stack.com/products/m5stamplc",
|
||||||
|
"vendor": "M5Stack",
|
||||||
|
"upload": {
|
||||||
|
"speed": 460800,
|
||||||
|
"maximum_size": 8388608,
|
||||||
|
"maximum_ram_size": 327680
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 372 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,120 @@
|
|||||||
|
# @file
|
||||||
|
# @brief Build-time BACnet source wiring for PlatformIO environments
|
||||||
|
# @author Kato Gangstad
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
PIOENV = env.get("PIOENV", "")
|
||||||
|
if PIOENV not in (
|
||||||
|
"m5stamplc-mstp",
|
||||||
|
"m5stamplc-bip",
|
||||||
|
"esp32-poe-wifi-bip",
|
||||||
|
"esp32-poe-eth-bip",
|
||||||
|
"xiao-esp32c3-wifi-bip",
|
||||||
|
"m5stamplc-gateway-bip-mstp",
|
||||||
|
):
|
||||||
|
Return()
|
||||||
|
|
||||||
|
project_dir = Path(env.subst("$PROJECT_DIR")).resolve()
|
||||||
|
bacnet_src_root = (project_dir / ".." / ".." / "src").resolve()
|
||||||
|
|
||||||
|
bacnet_common_sources = [
|
||||||
|
"bacnet/basic/binding/address.c",
|
||||||
|
"bacnet/basic/server/bacnet_device.c",
|
||||||
|
"bacnet/basic/object/ai.c",
|
||||||
|
"bacnet/basic/object/bi.c",
|
||||||
|
"bacnet/basic/object/bo.c",
|
||||||
|
"bacnet/basic/npdu/h_npdu.c",
|
||||||
|
"bacnet/basic/service/h_apdu.c",
|
||||||
|
"bacnet/basic/service/h_dcc.c",
|
||||||
|
"bacnet/basic/service/h_noserv.c",
|
||||||
|
"bacnet/basic/service/h_rd.c",
|
||||||
|
"bacnet/basic/service/h_rp.c",
|
||||||
|
"bacnet/basic/service/h_rpm.c",
|
||||||
|
"bacnet/basic/service/h_whohas.c",
|
||||||
|
"bacnet/basic/service/h_whois.c",
|
||||||
|
"bacnet/basic/service/h_wp.c",
|
||||||
|
"bacnet/basic/service/s_iam.c",
|
||||||
|
"bacnet/basic/service/s_ihave.c",
|
||||||
|
"bacnet/basic/sys/datetime_mstimer.c",
|
||||||
|
"bacnet/basic/sys/days.c",
|
||||||
|
"bacnet/basic/sys/dst.c",
|
||||||
|
"bacnet/basic/sys/debug.c",
|
||||||
|
"bacnet/basic/sys/ringbuf.c",
|
||||||
|
"bacnet/basic/sys/fifo.c",
|
||||||
|
"bacnet/basic/sys/mstimer.c",
|
||||||
|
"bacnet/basic/tsm/tsm.c",
|
||||||
|
"bacnet/abort.c",
|
||||||
|
"bacnet/bacaction.c",
|
||||||
|
"bacnet/bacaddr.c",
|
||||||
|
"bacnet/bacapp.c",
|
||||||
|
"bacnet/bacdcode.c",
|
||||||
|
"bacnet/bacdest.c",
|
||||||
|
"bacnet/bacdevobjpropref.c",
|
||||||
|
"bacnet/bacerror.c",
|
||||||
|
"bacnet/bacint.c",
|
||||||
|
"bacnet/bacreal.c",
|
||||||
|
"bacnet/bacstr.c",
|
||||||
|
"bacnet/datetime.c",
|
||||||
|
"bacnet/dcc.c",
|
||||||
|
"bacnet/hostnport.c",
|
||||||
|
"bacnet/iam.c",
|
||||||
|
"bacnet/ihave.c",
|
||||||
|
"bacnet/indtext.c",
|
||||||
|
"bacnet/memcopy.c",
|
||||||
|
"bacnet/npdu.c",
|
||||||
|
"bacnet/proplist.c",
|
||||||
|
"bacnet/rd.c",
|
||||||
|
"bacnet/reject.c",
|
||||||
|
"bacnet/rp.c",
|
||||||
|
"bacnet/rpm.c",
|
||||||
|
"bacnet/timestamp.c",
|
||||||
|
"bacnet/whohas.c",
|
||||||
|
"bacnet/whois.c",
|
||||||
|
"bacnet/wp.c",
|
||||||
|
"bacnet/cov.c",
|
||||||
|
"bacnet/basic/sys/keylist.c",
|
||||||
|
]
|
||||||
|
|
||||||
|
bacnet_transport_sources = {
|
||||||
|
"m5stamplc-mstp": [
|
||||||
|
"bacnet/datalink/cobs.c",
|
||||||
|
"bacnet/datalink/crc.c",
|
||||||
|
"bacnet/datalink/dlmstp.c",
|
||||||
|
"bacnet/datalink/mstp.c",
|
||||||
|
"bacnet/datalink/mstptext.c",
|
||||||
|
],
|
||||||
|
"m5stamplc-bip": [
|
||||||
|
"bacnet/datalink/bvlc.c",
|
||||||
|
],
|
||||||
|
"esp32-poe-wifi-bip": [
|
||||||
|
"bacnet/datalink/bvlc.c",
|
||||||
|
],
|
||||||
|
"esp32-poe-eth-bip": [
|
||||||
|
"bacnet/datalink/bvlc.c",
|
||||||
|
],
|
||||||
|
"xiao-esp32c3-wifi-bip": [
|
||||||
|
"bacnet/datalink/bvlc.c",
|
||||||
|
],
|
||||||
|
"m5stamplc-gateway-bip-mstp": [
|
||||||
|
"bacnet/datalink/bvlc.c",
|
||||||
|
"bacnet/datalink/cobs.c",
|
||||||
|
"bacnet/datalink/crc.c",
|
||||||
|
"bacnet/datalink/dlmstp.c",
|
||||||
|
"bacnet/datalink/mstp.c",
|
||||||
|
"bacnet/datalink/mstptext.c",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
bacnet_sources = bacnet_common_sources + bacnet_transport_sources.get(PIOENV, [])
|
||||||
|
|
||||||
|
env.Append(CPPPATH=[str(project_dir / "src"), str(bacnet_src_root)])
|
||||||
|
|
||||||
|
for rel in bacnet_sources:
|
||||||
|
env.BuildSources(
|
||||||
|
str(Path("$BUILD_DIR") / "bacnet"),
|
||||||
|
str(bacnet_src_root),
|
||||||
|
src_filter=f"+<{rel}>",
|
||||||
|
)
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for the project specific (private) libraries.
|
|
||||||
PlatformIO will compile them to static libraries and link to executable file.
|
|
||||||
|
|
||||||
The source code of each library should be placed in separate directory, like
|
|
||||||
"lib/private_lib/[here are source files]".
|
|
||||||
|
|
||||||
For example, see how can be organized `Foo` and `Bar` libraries:
|
|
||||||
|
|
||||||
|--lib
|
|
||||||
| |--Bar
|
|
||||||
| | |--docs
|
|
||||||
| | |--examples
|
|
||||||
| | |--src
|
|
||||||
| | |- Bar.c
|
|
||||||
| | |- Bar.h
|
|
||||||
| |--Foo
|
|
||||||
| | |- Foo.c
|
|
||||||
| | |- Foo.h
|
|
||||||
| |- readme.txt --> THIS FILE
|
|
||||||
|- platformio.ini
|
|
||||||
|--src
|
|
||||||
|- main.c
|
|
||||||
|
|
||||||
Then in `src/main.c` you should use:
|
|
||||||
|
|
||||||
#include <Foo.h>
|
|
||||||
#include <Bar.h>
|
|
||||||
|
|
||||||
// rest H/C/CPP code
|
|
||||||
|
|
||||||
PlatformIO will find your libraries automatically, configure preprocessor's
|
|
||||||
include paths and build them.
|
|
||||||
|
|
||||||
More information about PlatformIO Library Dependency Finder
|
|
||||||
- http://docs.platformio.org/page/librarymanager/ldf.html
|
|
||||||
+148
-7
@@ -6,14 +6,155 @@
|
|||||||
; Advanced options: extra scripting
|
; Advanced options: extra scripting
|
||||||
;
|
;
|
||||||
; Please visit documentation for the other options and examples
|
; Please visit documentation for the other options and examples
|
||||||
; http://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
[env:esp32thing]
|
[platformio]
|
||||||
|
default_envs = m5stamplc-mstp
|
||||||
|
|
||||||
|
[wifi_common]
|
||||||
|
build_flags =
|
||||||
|
-DWIFI_SSID=\"REPLACE_WITH_WIFI_SSID\"
|
||||||
|
-DWIFI_PASS=\"REPLACE_WITH_WIFI_PASSWORD\"
|
||||||
|
|
||||||
|
[bip_common]
|
||||||
|
build_flags =
|
||||||
|
-UBACDL_MSTP
|
||||||
|
-DBACDL_BIP
|
||||||
|
-DBACNET_IP_PORT=47808
|
||||||
|
-DMAX_APDU=1476
|
||||||
|
|
||||||
|
[env]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = esp32thing
|
framework = arduino
|
||||||
framework = espidf
|
monitor_speed = 115200
|
||||||
|
upload_speed = 921600
|
||||||
|
extra_scripts = pre:extra_script.py
|
||||||
|
lib_deps =
|
||||||
|
m5stack/M5StamPLC@^1.2.0
|
||||||
|
build_unflags =
|
||||||
|
-std=gnu++11
|
||||||
|
build_flags =
|
||||||
|
-std=gnu11
|
||||||
|
-std=gnu++17
|
||||||
|
-I src
|
||||||
|
-I ../../src
|
||||||
|
-DBACDL_MSTP
|
||||||
|
-DMAX_TSM_TRANSACTIONS=1
|
||||||
|
-DPRINT_ENABLED=0
|
||||||
|
-DBACNET_BIG_ENDIAN=0
|
||||||
|
-DCRC_USE_TABLE
|
||||||
|
-DBACAPP_MINIMAL
|
||||||
|
-DBACAPP_TIMESTAMP
|
||||||
|
-DBACNET_STACK_DEPRECATED_DISABLE
|
||||||
|
-DBACNET_PROTOCOL_REVISION=16
|
||||||
|
-DCONFIG_BACNET_BASIC_OBJECT_ANALOG_INPUT
|
||||||
|
-DCONFIG_BACNET_BASIC_OBJECT_BINARY_INPUT
|
||||||
|
-DCONFIG_BACNET_BASIC_OBJECT_BINARY_OUTPUT
|
||||||
|
|
||||||
upload_port = COM7
|
[env:m5stamplc-mstp]
|
||||||
upload_speed = 1152000
|
board = m5stamplc
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-DMAX_APDU=480
|
||||||
|
build_src_filter =
|
||||||
|
+<main.cpp>
|
||||||
|
+<bacnet_app.c>
|
||||||
|
+<mstimer_init.c>
|
||||||
|
+<rs485.c>
|
||||||
|
+<dlenv.c>
|
||||||
|
lib_deps = m5stack/M5StamPLC@^1.2.0
|
||||||
|
|
||||||
monitor_baud = 115200
|
[env:m5stamplc-bip]
|
||||||
|
board = m5stamplc
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
${bip_common.build_flags}
|
||||||
|
${wifi_common.build_flags}
|
||||||
|
-DUSE_M5STAMPLC_IO=1
|
||||||
|
build_src_filter =
|
||||||
|
+<main_bip.cpp>
|
||||||
|
+<bacnet_app.c>
|
||||||
|
+<bip.c>
|
||||||
|
+<bip_init.c>
|
||||||
|
+<bvlc.c>
|
||||||
|
+<bip_socket.cpp>
|
||||||
|
+<mstimer_init.c>
|
||||||
|
lib_deps = m5stack/M5StamPLC@^1.2.0
|
||||||
|
|
||||||
|
[env:esp32-poe-wifi-bip]
|
||||||
|
board = esp32-poe
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
${bip_common.build_flags}
|
||||||
|
${wifi_common.build_flags}
|
||||||
|
-DUSE_M5STAMPLC_IO=0
|
||||||
|
-DUSE_ETH_INTERFACE=0
|
||||||
|
build_src_filter =
|
||||||
|
+<main_bip.cpp>
|
||||||
|
+<bacnet_app.c>
|
||||||
|
+<bip.c>
|
||||||
|
+<bip_init.c>
|
||||||
|
+<bvlc.c>
|
||||||
|
+<bip_socket.cpp>
|
||||||
|
+<mstimer_init.c>
|
||||||
|
lib_deps =
|
||||||
|
|
||||||
|
[env:esp32-poe-eth-bip]
|
||||||
|
board = esp32-poe
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
${bip_common.build_flags}
|
||||||
|
-DUSE_M5STAMPLC_IO=0
|
||||||
|
-DUSE_ETH_INTERFACE=1
|
||||||
|
build_src_filter =
|
||||||
|
+<main_bip.cpp>
|
||||||
|
+<bacnet_app.c>
|
||||||
|
+<bip.c>
|
||||||
|
+<bip_init.c>
|
||||||
|
+<bvlc.c>
|
||||||
|
+<bip_socket.cpp>
|
||||||
|
+<mstimer_init.c>
|
||||||
|
lib_deps =
|
||||||
|
|
||||||
|
[env:xiao-esp32c3-wifi-bip]
|
||||||
|
board = seeed_xiao_esp32c3
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
${bip_common.build_flags}
|
||||||
|
${wifi_common.build_flags}
|
||||||
|
-DUSE_M5STAMPLC_IO=0
|
||||||
|
-DUSE_ETH_INTERFACE=0
|
||||||
|
build_src_filter =
|
||||||
|
+<main_bip.cpp>
|
||||||
|
+<bacnet_app.c>
|
||||||
|
+<bip.c>
|
||||||
|
+<bip_init.c>
|
||||||
|
+<bvlc.c>
|
||||||
|
+<bip_socket.cpp>
|
||||||
|
+<mstimer_init.c>
|
||||||
|
lib_deps =
|
||||||
|
|
||||||
|
[env:m5stamplc-gateway-bip-mstp]
|
||||||
|
board = m5stamplc
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
${wifi_common.build_flags}
|
||||||
|
-DMAX_APDU=480
|
||||||
|
-UBACDL_MSTP
|
||||||
|
-DBACDL_MSTP
|
||||||
|
-DBACDL_BIP
|
||||||
|
-DBACNET_IP_PORT=47808
|
||||||
|
-DROUTER_BIP_NET=1
|
||||||
|
-DROUTER_MSTP_NET=200
|
||||||
|
-DROUTER_MSTP_MAC=2
|
||||||
|
build_src_filter =
|
||||||
|
+<main_gateway_router.cpp>
|
||||||
|
+<bacnet_app.c>
|
||||||
|
+<bip.c>
|
||||||
|
+<bip_init.c>
|
||||||
|
+<bvlc.c>
|
||||||
|
+<bip_socket.cpp>
|
||||||
|
+<mstimer_init.c>
|
||||||
|
+<rs485.c>
|
||||||
|
+<dlenv.c>
|
||||||
|
lib_deps = m5stack/M5StamPLC@^1.2.0
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
Bacnet Server for Espressif ESP32
|
|
||||||
Steve Karg Bacnet stack using PlatformIO open source ecosystem for IoT development on VSCode or Atom
|
|
||||||
F. Chaxel 2017
|
|
||||||
|
|
||||||
TODO list :
|
|
||||||
|
|
||||||
(Install VSCode or Atom and add the PlatformIO extension)
|
|
||||||
|
|
||||||
Edit platformio.ini to adjust board, Com Port, ...
|
|
||||||
|
|
||||||
Goto lib/stack and copy the requested files from Steve code :
|
|
||||||
|
|
||||||
all .h from include directory (not all required by it's simple)
|
|
||||||
|
|
||||||
these .c files from src or demo/handlers
|
|
||||||
abort.c
|
|
||||||
address.c
|
|
||||||
apdu.c
|
|
||||||
bacaddr.c
|
|
||||||
bacapp.c
|
|
||||||
bacdcode.c
|
|
||||||
bacerror.c
|
|
||||||
bacint.c
|
|
||||||
bacreal.c
|
|
||||||
bacstr.c
|
|
||||||
bip.c
|
|
||||||
bvlc.c
|
|
||||||
cov.c
|
|
||||||
datetime.c
|
|
||||||
bacdevobjpropref.c
|
|
||||||
dcc.c
|
|
||||||
debug.c
|
|
||||||
h_cov.c
|
|
||||||
h_ucov.c
|
|
||||||
h_npdu.c
|
|
||||||
h_rp.c
|
|
||||||
h_rpm.c
|
|
||||||
h_whois.c
|
|
||||||
h_wp.c
|
|
||||||
iam.c
|
|
||||||
hostnport.c
|
|
||||||
lighting.c
|
|
||||||
memcopy.c
|
|
||||||
noserv.c
|
|
||||||
npdu.c
|
|
||||||
proplist.c
|
|
||||||
reject.c
|
|
||||||
rp.c
|
|
||||||
rpm.c
|
|
||||||
s_iam.c
|
|
||||||
tsm.c
|
|
||||||
whois.c
|
|
||||||
wp.c
|
|
||||||
|
|
||||||
Modify
|
|
||||||
in config.h
|
|
||||||
MAX_TSM_TRANSACTIONS 255, set the value to 10 for instances
|
|
||||||
in main.c
|
|
||||||
wifi_config to fit your wifi network
|
|
||||||
BACNET_LED 5, set another IO number depending of your board
|
|
||||||
|
|
||||||
A lot of Warning will be issued at compile time due to the redefinition of BIT macros.
|
|
||||||
Could be removes by placing a #ifndef #BIT0 .. #endif arround the BIT macro in bits.h,
|
|
||||||
and moving to the top of include list
|
|
||||||
#include "bacnet/datalink/datalink.h" in tsm.c, s_iam and in device.c
|
|
||||||
#include "bacport.h" in bip.c and in bip.h (redondant include in bip.c)
|
|
||||||
#include "bacnet/datalink/bvlc.h" in bvlc.c
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
# Disable formatting for now as there is external code. We should move external
|
|
||||||
# code to a separate directory and enable formatting for our code.
|
|
||||||
DisableFormat: true
|
|
||||||
|
|
||||||
# DisableFormat will not disable include sorting with some versions.
|
|
||||||
SortIncludes: Never
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,151 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
|
||||||
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
*********************************************************************/
|
|
||||||
#ifndef AI_H
|
|
||||||
#define AI_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "bacnet/bacdef.h"
|
|
||||||
#include "bacnet/rp.h"
|
|
||||||
#include "bacnet/wp.h"
|
|
||||||
#if defined(INTRINSIC_REPORTING)
|
|
||||||
#include "bacnet/basic/object/nc.h"
|
|
||||||
#include "bacnet/getevent.h"
|
|
||||||
#include "bacnet/alarm_ack.h"
|
|
||||||
#include "bacnet/get_alarm_sum.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
typedef struct analog_input_descr {
|
|
||||||
unsigned Event_State:3;
|
|
||||||
float Present_Value;
|
|
||||||
BACNET_RELIABILITY Reliability;
|
|
||||||
bool Out_Of_Service;
|
|
||||||
uint8_t Units;
|
|
||||||
float Prior_Value;
|
|
||||||
float COV_Increment;
|
|
||||||
bool Changed;
|
|
||||||
#if defined(INTRINSIC_REPORTING)
|
|
||||||
uint32_t Time_Delay;
|
|
||||||
uint32_t Notification_Class;
|
|
||||||
float High_Limit;
|
|
||||||
float Low_Limit;
|
|
||||||
float Deadband;
|
|
||||||
unsigned Limit_Enable:2;
|
|
||||||
unsigned Event_Enable:3;
|
|
||||||
unsigned Notify_Type:1;
|
|
||||||
ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION];
|
|
||||||
BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION];
|
|
||||||
/* time to generate event notification */
|
|
||||||
uint32_t Remaining_Time_Delay;
|
|
||||||
/* AckNotification informations */
|
|
||||||
ACK_NOTIFICATION Ack_notify_data;
|
|
||||||
#endif
|
|
||||||
} ANALOG_INPUT_DESCR;
|
|
||||||
|
|
||||||
void Analog_Input_Property_Lists(
|
|
||||||
const int32_t **pRequired,
|
|
||||||
const int32_t **pOptional,
|
|
||||||
const int32_t **pProprietary);
|
|
||||||
|
|
||||||
bool Analog_Input_Valid_Instance(
|
|
||||||
uint32_t object_instance);
|
|
||||||
unsigned Analog_Input_Count(
|
|
||||||
void);
|
|
||||||
uint32_t Analog_Input_Index_To_Instance(
|
|
||||||
unsigned index);
|
|
||||||
unsigned Analog_Input_Instance_To_Index(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Analog_Input_Object_Instance_Add(
|
|
||||||
uint32_t instance);
|
|
||||||
|
|
||||||
bool Analog_Input_Object_Name(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_CHARACTER_STRING * object_name);
|
|
||||||
bool Analog_Input_Name_Set(
|
|
||||||
uint32_t object_instance,
|
|
||||||
const char *new_name);
|
|
||||||
|
|
||||||
const char *Analog_Input_Description(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Analog_Input_Description_Set(
|
|
||||||
uint32_t instance,
|
|
||||||
const char *new_name);
|
|
||||||
|
|
||||||
bool Analog_Input_Units_Set(
|
|
||||||
uint32_t instance,
|
|
||||||
uint16_t units);
|
|
||||||
uint16_t Analog_Input_Units(
|
|
||||||
uint32_t instance);
|
|
||||||
|
|
||||||
int Analog_Input_Read_Property(
|
|
||||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
|
||||||
bool Analog_Input_Write_Property(
|
|
||||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
|
||||||
|
|
||||||
float Analog_Input_Present_Value(
|
|
||||||
uint32_t object_instance);
|
|
||||||
void Analog_Input_Present_Value_Set(
|
|
||||||
uint32_t object_instance,
|
|
||||||
float value);
|
|
||||||
|
|
||||||
bool Analog_Input_Out_Of_Service(
|
|
||||||
uint32_t object_instance);
|
|
||||||
void Analog_Input_Out_Of_Service_Set(
|
|
||||||
uint32_t object_instance,
|
|
||||||
bool oos_flag);
|
|
||||||
|
|
||||||
bool Analog_Input_Change_Of_Value(
|
|
||||||
uint32_t instance);
|
|
||||||
void Analog_Input_Change_Of_Value_Clear(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Analog_Input_Encode_Value_List(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_PROPERTY_VALUE * value_list);
|
|
||||||
float Analog_Input_COV_Increment(
|
|
||||||
uint32_t instance);
|
|
||||||
void Analog_Input_COV_Increment_Set(
|
|
||||||
uint32_t instance,
|
|
||||||
float value);
|
|
||||||
|
|
||||||
/* note: header of Intrinsic_Reporting function is required
|
|
||||||
even when INTRINSIC_REPORTING is not defined */
|
|
||||||
void Analog_Input_Intrinsic_Reporting(
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
#if defined(INTRINSIC_REPORTING)
|
|
||||||
int Analog_Input_Event_Information(
|
|
||||||
unsigned index,
|
|
||||||
BACNET_GET_EVENT_INFORMATION_DATA * getevent_data);
|
|
||||||
|
|
||||||
int Analog_Input_Alarm_Ack(
|
|
||||||
BACNET_ALARM_ACK_DATA * alarmack_data,
|
|
||||||
BACNET_ERROR_CODE * error_code);
|
|
||||||
|
|
||||||
int Analog_Input_Alarm_Summary(
|
|
||||||
unsigned index,
|
|
||||||
BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint32_t Analog_Input_Create(
|
|
||||||
uint32_t object_instance);
|
|
||||||
bool Analog_Input_Delete(
|
|
||||||
uint32_t object_instance);
|
|
||||||
void Analog_Input_Cleanup(
|
|
||||||
void);
|
|
||||||
void Analog_Input_Init(
|
|
||||||
void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,365 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet application glue for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "bacnet_app.h"
|
||||||
|
#include "mstimer_init.h"
|
||||||
|
|
||||||
|
#if defined(BACDL_MSTP)
|
||||||
|
#include "dlenv.h"
|
||||||
|
#include "bacnet/datalink/datalink.h"
|
||||||
|
#elif defined(BACDL_BIP)
|
||||||
|
#include "bip.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "bacnet/basic/binding/address.h"
|
||||||
|
#include "bacnet/basic/object/ai.h"
|
||||||
|
#include "bacnet/basic/object/bi.h"
|
||||||
|
#include "bacnet/basic/object/bo.h"
|
||||||
|
#include "bacnet/basic/object/device.h"
|
||||||
|
#include "bacnet/basic/services.h"
|
||||||
|
#include "bacnet/basic/tsm/tsm.h"
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
|
||||||
|
#ifndef BACNET_IP_PORT
|
||||||
|
#define BACNET_IP_PORT 47808
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BACDL_BIP)
|
||||||
|
static uint8_t PDUBuffer[BIP_MPDU_MAX];
|
||||||
|
#else
|
||||||
|
static uint8_t PDUBuffer[MAX_MPDU + 16];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PLC_INPUT_COUNT 8
|
||||||
|
#define PLC_RELAY_COUNT 4
|
||||||
|
#define PLC_ANALOG_COUNT 2
|
||||||
|
|
||||||
|
#define PLC_AI_INSTANCE_TEMPERATURE 100
|
||||||
|
#define PLC_AI_INSTANCE_FREE_HEAP_KB 101
|
||||||
|
|
||||||
|
static uint32_t Plc_Input_Instances[PLC_INPUT_COUNT];
|
||||||
|
static uint32_t Plc_Relay_Instances[PLC_RELAY_COUNT];
|
||||||
|
static uint32_t Plc_Analog_Instances[PLC_ANALOG_COUNT];
|
||||||
|
|
||||||
|
static const char *const Plc_Input_Names[PLC_INPUT_COUNT] = {
|
||||||
|
"PLC Input 1", "PLC Input 2", "PLC Input 3", "PLC Input 4",
|
||||||
|
"PLC Input 5", "PLC Input 6", "PLC Input 7", "PLC Input 8",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const Plc_Input_Descriptions[PLC_INPUT_COUNT] = {
|
||||||
|
"PLC Input 1", "PLC Input 2", "PLC Input 3", "PLC Input 4",
|
||||||
|
"PLC Input 5", "PLC Input 6", "PLC Input 7", "PLC Input 8",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const Plc_Relay_Names[PLC_RELAY_COUNT] = {
|
||||||
|
"PLC Relay 1",
|
||||||
|
"PLC Relay 2",
|
||||||
|
"PLC Relay 3",
|
||||||
|
"PLC Relay 4",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const Plc_Relay_Descriptions[PLC_RELAY_COUNT] = {
|
||||||
|
"PLC Relay 1",
|
||||||
|
"PLC Relay 2",
|
||||||
|
"PLC Relay 3",
|
||||||
|
"PLC Relay 4",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const Plc_Analog_Names[PLC_ANALOG_COUNT] = {
|
||||||
|
"PLC Temperature",
|
||||||
|
"Free Heap KB",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const Plc_Analog_Descriptions[PLC_ANALOG_COUNT] = {
|
||||||
|
"Internal PLC temperature in C",
|
||||||
|
"Free heap memory in kilobytes",
|
||||||
|
};
|
||||||
|
|
||||||
|
static object_functions_t My_Object_Table[] = {
|
||||||
|
{ OBJECT_DEVICE,
|
||||||
|
NULL,
|
||||||
|
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,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
Device_Writable_Property_List },
|
||||||
|
|
||||||
|
{ OBJECT_BINARY_INPUT,
|
||||||
|
Binary_Input_Init,
|
||||||
|
Binary_Input_Count,
|
||||||
|
Binary_Input_Index_To_Instance,
|
||||||
|
Binary_Input_Valid_Instance,
|
||||||
|
Binary_Input_Object_Name,
|
||||||
|
Binary_Input_Read_Property,
|
||||||
|
NULL,
|
||||||
|
Binary_Input_Property_Lists,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
Binary_Input_Encode_Value_List,
|
||||||
|
Binary_Input_Change_Of_Value,
|
||||||
|
Binary_Input_Change_Of_Value_Clear,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
Binary_Input_Create,
|
||||||
|
Binary_Input_Delete,
|
||||||
|
NULL,
|
||||||
|
Binary_Input_Writable_Property_List },
|
||||||
|
|
||||||
|
{ 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,
|
||||||
|
NULL,
|
||||||
|
Analog_Input_Encode_Value_List,
|
||||||
|
Analog_Input_Change_Of_Value,
|
||||||
|
Analog_Input_Change_Of_Value_Clear,
|
||||||
|
Analog_Input_Intrinsic_Reporting,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
Analog_Input_Create,
|
||||||
|
Analog_Input_Delete,
|
||||||
|
NULL,
|
||||||
|
Analog_Input_Writable_Property_List },
|
||||||
|
|
||||||
|
{ OBJECT_BINARY_OUTPUT,
|
||||||
|
Binary_Output_Init,
|
||||||
|
Binary_Output_Count,
|
||||||
|
Binary_Output_Index_To_Instance,
|
||||||
|
Binary_Output_Valid_Instance,
|
||||||
|
Binary_Output_Object_Name,
|
||||||
|
Binary_Output_Read_Property,
|
||||||
|
Binary_Output_Write_Property,
|
||||||
|
Binary_Output_Property_Lists,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
Binary_Output_Encode_Value_List,
|
||||||
|
Binary_Output_Change_Of_Value,
|
||||||
|
Binary_Output_Change_Of_Value_Clear,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
Binary_Output_Create,
|
||||||
|
Binary_Output_Delete,
|
||||||
|
NULL,
|
||||||
|
Binary_Output_Writable_Property_List },
|
||||||
|
|
||||||
|
{ MAX_BACNET_OBJECT_TYPE,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create and initialize the BACnet objects exposed by this port
|
||||||
|
*/
|
||||||
|
static void init_server_objects(void)
|
||||||
|
{
|
||||||
|
uint32_t instance;
|
||||||
|
|
||||||
|
Device_Init(My_Object_Table);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < PLC_INPUT_COUNT; i++) {
|
||||||
|
instance = Binary_Input_Create(i);
|
||||||
|
Plc_Input_Instances[i] = instance;
|
||||||
|
(void)Binary_Input_Name_Set(instance, Plc_Input_Names[i]);
|
||||||
|
(void)Binary_Input_Description_Set(instance, Plc_Input_Descriptions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < PLC_RELAY_COUNT; i++) {
|
||||||
|
instance = Binary_Output_Create(i);
|
||||||
|
Plc_Relay_Instances[i] = instance;
|
||||||
|
(void)Binary_Output_Name_Set(instance, Plc_Relay_Names[i]);
|
||||||
|
(void)Binary_Output_Description_Set(
|
||||||
|
instance, Plc_Relay_Descriptions[i]);
|
||||||
|
(void)Binary_Output_Present_Value_Set(
|
||||||
|
instance, BINARY_INACTIVE, BACNET_MAX_PRIORITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
Plc_Analog_Instances[0] = Analog_Input_Create(PLC_AI_INSTANCE_TEMPERATURE);
|
||||||
|
Plc_Analog_Instances[1] = Analog_Input_Create(PLC_AI_INSTANCE_FREE_HEAP_KB);
|
||||||
|
|
||||||
|
(void)Analog_Input_Name_Set(Plc_Analog_Instances[0], Plc_Analog_Names[0]);
|
||||||
|
(void)Analog_Input_Description_Set(
|
||||||
|
Plc_Analog_Instances[0], Plc_Analog_Descriptions[0]);
|
||||||
|
(void)Analog_Input_Units_Set(
|
||||||
|
Plc_Analog_Instances[0], UNITS_DEGREES_CELSIUS);
|
||||||
|
|
||||||
|
(void)Analog_Input_Name_Set(Plc_Analog_Instances[1], Plc_Analog_Names[1]);
|
||||||
|
(void)Analog_Input_Description_Set(
|
||||||
|
Plc_Analog_Instances[1], Plc_Analog_Descriptions[1]);
|
||||||
|
(void)Analog_Input_Units_Set(Plc_Analog_Instances[1], UNITS_NO_UNITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet application and selected datalink
|
||||||
|
*/
|
||||||
|
void bacnet_app_init(void)
|
||||||
|
{
|
||||||
|
systimer_init();
|
||||||
|
|
||||||
|
address_init();
|
||||||
|
Device_Set_Object_Instance_Number(12345);
|
||||||
|
init_server_objects();
|
||||||
|
|
||||||
|
#if defined(BACDL_MSTP)
|
||||||
|
if (!m5_dlenv_init(2)) {
|
||||||
|
for (;;) { }
|
||||||
|
}
|
||||||
|
#elif defined(BACDL_BIP)
|
||||||
|
if (!bip_init((uint16_t)BACNET_IP_PORT)) {
|
||||||
|
for (;;) { }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
|
||||||
|
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||||
|
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
|
||||||
|
apdu_set_confirmed_handler(
|
||||||
|
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||||
|
handler_device_communication_control);
|
||||||
|
|
||||||
|
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process one BACnet polling cycle and advance timers
|
||||||
|
*/
|
||||||
|
void bacnet_app_tick(void)
|
||||||
|
{
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
uint16_t pdu_len = 0;
|
||||||
|
|
||||||
|
#if defined(BACDL_BIP)
|
||||||
|
pdu_len = bip_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0);
|
||||||
|
#else
|
||||||
|
pdu_len = datalink_receive(&src, &PDUBuffer[0], MAX_MPDU, 0);
|
||||||
|
#endif
|
||||||
|
if (pdu_len) {
|
||||||
|
npdu_handler(&src, &PDUBuffer[0], pdu_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
tsm_timer_milliseconds(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of exposed PLC binary inputs
|
||||||
|
* @return number of binary inputs
|
||||||
|
*/
|
||||||
|
uint8_t bacnet_app_input_count(void)
|
||||||
|
{
|
||||||
|
return PLC_INPUT_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of exposed PLC relay outputs
|
||||||
|
* @return number of binary outputs
|
||||||
|
*/
|
||||||
|
uint8_t bacnet_app_relay_count(void)
|
||||||
|
{
|
||||||
|
return PLC_RELAY_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update a BACnet binary input from PLC state
|
||||||
|
* @param index zero-based PLC input index
|
||||||
|
* @param active input state to publish
|
||||||
|
*/
|
||||||
|
void bacnet_app_input_set(uint8_t index, bool active)
|
||||||
|
{
|
||||||
|
uint32_t instance;
|
||||||
|
|
||||||
|
if (index >= PLC_INPUT_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = Plc_Input_Instances[index];
|
||||||
|
|
||||||
|
(void)Binary_Input_Present_Value_Set(
|
||||||
|
instance, active ? BINARY_ACTIVE : BINARY_INACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read the commanded state of a BACnet relay output
|
||||||
|
* @param index zero-based relay index
|
||||||
|
* @return true if the relay should be active
|
||||||
|
*/
|
||||||
|
bool bacnet_app_relay_get(uint8_t index)
|
||||||
|
{
|
||||||
|
uint32_t instance;
|
||||||
|
|
||||||
|
if (index >= PLC_RELAY_COUNT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = Plc_Relay_Instances[index];
|
||||||
|
|
||||||
|
return (Binary_Output_Present_Value(instance) == BINARY_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the analog input that reports PLC temperature
|
||||||
|
* @param value_celsius temperature in degrees Celsius
|
||||||
|
*/
|
||||||
|
void bacnet_app_temperature_set(float value_celsius)
|
||||||
|
{
|
||||||
|
Analog_Input_Present_Value_Set(Plc_Analog_Instances[0], value_celsius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the analog input that reports free heap memory
|
||||||
|
* @param value_kb free heap in kilobytes
|
||||||
|
*/
|
||||||
|
void bacnet_app_free_heap_kb_set(float value_kb)
|
||||||
|
{
|
||||||
|
Analog_Input_Present_Value_Set(Plc_Analog_Instances[1], value_kb);
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Public BACnet application hooks for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M5STAMPLC_BACNET_APP_H
|
||||||
|
#define M5STAMPLC_BACNET_APP_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet application and datalink bindings
|
||||||
|
*/
|
||||||
|
void bacnet_app_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run one BACnet processing cycle
|
||||||
|
*/
|
||||||
|
void bacnet_app_tick(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of published PLC inputs
|
||||||
|
* @return number of PLC inputs
|
||||||
|
*/
|
||||||
|
uint8_t bacnet_app_input_count(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of published PLC relays
|
||||||
|
* @return number of PLC relays
|
||||||
|
*/
|
||||||
|
uint8_t bacnet_app_relay_count(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push a PLC input state into the BACnet object model
|
||||||
|
* @param index zero-based input index
|
||||||
|
* @param active input state
|
||||||
|
*/
|
||||||
|
void bacnet_app_input_set(uint8_t index, bool active);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read the requested state for a PLC relay
|
||||||
|
* @param index zero-based relay index
|
||||||
|
* @return true if the relay should be active
|
||||||
|
*/
|
||||||
|
bool bacnet_app_relay_get(uint8_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the published PLC temperature value
|
||||||
|
* @param value_celsius temperature in degrees Celsius
|
||||||
|
*/
|
||||||
|
void bacnet_app_temperature_set(float value_celsius);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the published free heap value
|
||||||
|
* @param value_kb free heap in kilobytes
|
||||||
|
*/
|
||||||
|
void bacnet_app_free_heap_kb_set(float value_kb);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,350 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet/IP datalink implementation for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bacnet/bacdcode.h"
|
||||||
|
#include "bacnet/bacint.h"
|
||||||
|
#include "bacnet/datalink/bvlc.h"
|
||||||
|
|
||||||
|
#include "bip.h"
|
||||||
|
#include "bvlc.h"
|
||||||
|
|
||||||
|
#if PRINT_ENABLED || DEBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_SOCK_NUM 8
|
||||||
|
|
||||||
|
static uint8_t BIP_Socket = MAX_SOCK_NUM;
|
||||||
|
static uint16_t BIP_Port = 0;
|
||||||
|
static uint8_t BIP_Address[4] = { 0, 0, 0, 0 };
|
||||||
|
static uint8_t BIP_Broadcast_Address[4] = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a 4-byte IP address into a uint32_t value
|
||||||
|
* @param bip_address pointer to the 4-byte address
|
||||||
|
* @return 32-bit packed address
|
||||||
|
*/
|
||||||
|
uint32_t convertBIP_Address2uint32(const uint8_t *bip_address)
|
||||||
|
{
|
||||||
|
return ((uint32_t)bip_address[0] << 24) | ((uint32_t)bip_address[1] << 16) |
|
||||||
|
((uint32_t)bip_address[2] << 8) | ((uint32_t)bip_address[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a uint32_t IP address into a 4-byte array
|
||||||
|
* @param ip packed IP address
|
||||||
|
* @param address destination buffer for the 4-byte address
|
||||||
|
*/
|
||||||
|
void convertUint32Address_2_uint8Address(uint32_t ip, uint8_t *address)
|
||||||
|
{
|
||||||
|
address[0] = (uint8_t)(ip >> 24);
|
||||||
|
address[1] = (uint8_t)(ip >> 16);
|
||||||
|
address[2] = (uint8_t)(ip >> 8);
|
||||||
|
address[3] = (uint8_t)(ip >> 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the active socket identifier used by the BACnet/IP layer
|
||||||
|
* @param sock_fd socket identifier
|
||||||
|
*/
|
||||||
|
void bip_set_socket(uint8_t sock_fd)
|
||||||
|
{
|
||||||
|
BIP_Socket = sock_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the stored BACnet/IP socket identifier
|
||||||
|
* @return socket identifier
|
||||||
|
*/
|
||||||
|
uint8_t bip_socket(void)
|
||||||
|
{
|
||||||
|
return BIP_Socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the BACnet/IP socket state is valid
|
||||||
|
* @return true if the stored socket identifier is valid
|
||||||
|
*/
|
||||||
|
bool bip_valid(void)
|
||||||
|
{
|
||||||
|
return (BIP_Socket < MAX_SOCK_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the local IPv4 address used by BACnet/IP
|
||||||
|
* @param net_address pointer to the 4-byte local address
|
||||||
|
*/
|
||||||
|
void bip_set_addr(const uint8_t *net_address)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
BIP_Address[i] = net_address[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the stored local IPv4 address
|
||||||
|
* @return pointer to the 4-byte local address
|
||||||
|
*/
|
||||||
|
uint8_t *bip_get_addr(void)
|
||||||
|
{
|
||||||
|
return BIP_Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the broadcast IPv4 address used by BACnet/IP
|
||||||
|
* @param net_address pointer to the 4-byte broadcast address
|
||||||
|
*/
|
||||||
|
void bip_set_broadcast_addr(const uint8_t *net_address)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
BIP_Broadcast_Address[i] = net_address[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the stored broadcast IPv4 address
|
||||||
|
* @return pointer to the 4-byte broadcast address
|
||||||
|
*/
|
||||||
|
uint8_t *bip_get_broadcast_addr(void)
|
||||||
|
{
|
||||||
|
return BIP_Broadcast_Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the UDP port used by BACnet/IP
|
||||||
|
* @param port UDP port number
|
||||||
|
*/
|
||||||
|
void bip_set_port(uint16_t port)
|
||||||
|
{
|
||||||
|
BIP_Port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the UDP port used by BACnet/IP
|
||||||
|
* @return UDP port number
|
||||||
|
*/
|
||||||
|
uint16_t bip_get_port(void)
|
||||||
|
{
|
||||||
|
return BIP_Port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode a BACnet/IP MAC address into IPv4 address and port parts
|
||||||
|
* @param bac_addr source BACnet address
|
||||||
|
* @param address destination IPv4 address buffer
|
||||||
|
* @param port destination UDP port pointer
|
||||||
|
* @return number of bytes decoded from the MAC address
|
||||||
|
*/
|
||||||
|
static int bip_decode_bip_address(
|
||||||
|
const BACNET_ADDRESS *bac_addr, uint8_t *address, uint16_t *port)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (bac_addr) {
|
||||||
|
memcpy(address, &bac_addr->mac[0], 4);
|
||||||
|
memcpy(port, &bac_addr->mac[4], 2);
|
||||||
|
len = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a BACnet NPDU over BACnet/IP
|
||||||
|
* @param dest destination BACnet address
|
||||||
|
* @param npdu_data NPDU metadata
|
||||||
|
* @param pdu payload buffer
|
||||||
|
* @param pdu_len payload length in bytes
|
||||||
|
* @return number of bytes sent, or -1 on error
|
||||||
|
*/
|
||||||
|
int bip_send_pdu(
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
BACNET_NPDU_DATA *npdu_data,
|
||||||
|
uint8_t *pdu,
|
||||||
|
unsigned pdu_len)
|
||||||
|
{
|
||||||
|
uint8_t mtu[BIP_MPDU_MAX] = { 0 };
|
||||||
|
int mtu_len = 0;
|
||||||
|
int bytes_sent = 0;
|
||||||
|
uint8_t address[] = { 0, 0, 0, 0 };
|
||||||
|
uint16_t port = 0;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
(void)npdu_data;
|
||||||
|
|
||||||
|
if (BIP_Socket >= MAX_SOCK_NUM) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtu[0] = BVLL_TYPE_BACNET_IP;
|
||||||
|
if ((dest->net == BACNET_BROADCAST_NETWORK) ||
|
||||||
|
((dest->net > 0) && (dest->len == 0)) || (dest->mac_len == 0)) {
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
address[i] = BIP_Broadcast_Address[i];
|
||||||
|
}
|
||||||
|
port = BIP_Port;
|
||||||
|
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
|
||||||
|
} else if (dest->mac_len == 6) {
|
||||||
|
bip_decode_bip_address(dest, address, &port);
|
||||||
|
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtu_len = 2;
|
||||||
|
mtu_len += encode_unsigned16(&mtu[mtu_len], (uint16_t)(pdu_len + 4));
|
||||||
|
memcpy(&mtu[mtu_len], pdu, pdu_len);
|
||||||
|
mtu_len += (int)pdu_len;
|
||||||
|
|
||||||
|
bytes_sent = bip_socket_send(address, port, mtu, (uint16_t)mtu_len);
|
||||||
|
|
||||||
|
return bytes_sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Receive a BACnet/IP NPDU and strip BVLC framing
|
||||||
|
* @param src source BACnet address
|
||||||
|
* @param pdu receive buffer for NPDU data
|
||||||
|
* @param max_pdu receive buffer size
|
||||||
|
* @param timeout receive timeout in milliseconds
|
||||||
|
* @return NPDU length in bytes, or 0 if no valid frame was received
|
||||||
|
*/
|
||||||
|
uint16_t bip_receive(
|
||||||
|
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout)
|
||||||
|
{
|
||||||
|
int received_bytes = 0;
|
||||||
|
int max = 0;
|
||||||
|
uint16_t pdu_len = 0;
|
||||||
|
uint8_t src_addr[] = { 0, 0, 0, 0 };
|
||||||
|
uint16_t src_port = 0;
|
||||||
|
uint16_t i = 0;
|
||||||
|
int function = 0;
|
||||||
|
|
||||||
|
(void)timeout;
|
||||||
|
|
||||||
|
if (BIP_Socket >= MAX_SOCK_NUM) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
received_bytes = bip_socket_receive(pdu, max_pdu, src_addr, &src_port);
|
||||||
|
if (received_bytes <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdu[0] != BVLL_TYPE_BACNET_IP) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
max = (int)max_pdu - received_bytes;
|
||||||
|
if (max > 0) {
|
||||||
|
if (max > 16) {
|
||||||
|
max = 16;
|
||||||
|
}
|
||||||
|
memset(&pdu[received_bytes], 0, (size_t)max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bvlc_for_non_bbmd(src_addr, &src_port, pdu, (uint16_t)received_bytes) >
|
||||||
|
0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function = pico_bvlc_get_function_code();
|
||||||
|
if ((function == BVLC_ORIGINAL_UNICAST_NPDU) ||
|
||||||
|
(function == BVLC_ORIGINAL_BROADCAST_NPDU)) {
|
||||||
|
if ((convertBIP_Address2uint32(src_addr) ==
|
||||||
|
convertBIP_Address2uint32(BIP_Address)) &&
|
||||||
|
(src_port == BIP_Port)) {
|
||||||
|
pdu_len = 0;
|
||||||
|
} else {
|
||||||
|
src->mac_len = 6;
|
||||||
|
memcpy(&src->mac[0], &src_addr, 4);
|
||||||
|
memcpy(&src->mac[4], &src_port, 2);
|
||||||
|
(void)decode_unsigned16(&pdu[2], &pdu_len);
|
||||||
|
pdu_len -= 4;
|
||||||
|
if (pdu_len < max_pdu) {
|
||||||
|
for (i = 0; i < pdu_len; i++) {
|
||||||
|
pdu[i] = pdu[4 + i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pdu_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (function == BVLC_FORWARDED_NPDU) {
|
||||||
|
memcpy(&src_addr, &pdu[4], 4);
|
||||||
|
memcpy(&src_port, &pdu[8], 2);
|
||||||
|
if ((convertBIP_Address2uint32(src_addr) ==
|
||||||
|
convertBIP_Address2uint32(BIP_Address)) &&
|
||||||
|
(src_port == BIP_Port)) {
|
||||||
|
pdu_len = 0;
|
||||||
|
} else {
|
||||||
|
src->mac_len = 6;
|
||||||
|
memcpy(&src->mac[0], &src_addr, 4);
|
||||||
|
memcpy(&src->mac[4], &src_port, 2);
|
||||||
|
(void)decode_unsigned16(&pdu[2], &pdu_len);
|
||||||
|
pdu_len -= 10;
|
||||||
|
if (pdu_len < max_pdu) {
|
||||||
|
for (i = 0; i < pdu_len; i++) {
|
||||||
|
pdu[i] = pdu[10 + i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pdu_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdu_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build the local BACnet/IP address structure
|
||||||
|
* @param my_address destination BACnet address
|
||||||
|
*/
|
||||||
|
void bip_get_my_address(BACNET_ADDRESS *my_address)
|
||||||
|
{
|
||||||
|
if (my_address) {
|
||||||
|
my_address->mac_len = 6;
|
||||||
|
memcpy(&my_address->mac[0], &BIP_Address[0], 4);
|
||||||
|
memcpy(&my_address->mac[4], &BIP_Port, 2);
|
||||||
|
my_address->net = 0;
|
||||||
|
my_address->len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build the BACnet/IP broadcast address structure
|
||||||
|
* @param dest destination BACnet address
|
||||||
|
*/
|
||||||
|
void bip_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||||
|
{
|
||||||
|
if (dest) {
|
||||||
|
dest->mac_len = 6;
|
||||||
|
memcpy(&dest->mac[0], &BIP_Broadcast_Address[0], 4);
|
||||||
|
memcpy(&dest->mac[4], &BIP_Port, 2);
|
||||||
|
dest->net = BACNET_BROADCAST_NETWORK;
|
||||||
|
dest->len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a raw BACnet/IP MPDU to a specific host and port
|
||||||
|
* @param dest destination IPv4 address and port
|
||||||
|
* @param mtu raw BVLC/NPDU buffer
|
||||||
|
* @param mtu_len buffer length in bytes
|
||||||
|
* @return number of bytes sent, or -1 on error
|
||||||
|
*/
|
||||||
|
int bip_send_mpdu(
|
||||||
|
const BACNET_IP_ADDRESS *dest, const uint8_t *mtu, uint16_t mtu_len)
|
||||||
|
{
|
||||||
|
return bip_socket_send(dest->address, dest->port, mtu, mtu_len);
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet/IP datalink interface for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M5STAMPLC_BIP_H
|
||||||
|
#define M5STAMPLC_BIP_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
#include "bacnet/datalink/bvlc.h"
|
||||||
|
|
||||||
|
#define BIP_HEADER_MAX (1 + 1 + 2)
|
||||||
|
#ifndef BIP_MPDU_MAX
|
||||||
|
#define BIP_MPDU_MAX (BIP_HEADER_MAX + MAX_PDU)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BVLL_TYPE_BACNET_IP (0x81)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the UDP socket backend used by BACnet/IP
|
||||||
|
* @param port UDP port to bind
|
||||||
|
* @return true if the socket backend was initialized
|
||||||
|
*/
|
||||||
|
bool bip_socket_init(uint16_t port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a raw UDP payload for BACnet/IP
|
||||||
|
* @param dest_addr destination IPv4 address
|
||||||
|
* @param dest_port destination UDP port
|
||||||
|
* @param mtu payload buffer
|
||||||
|
* @param mtu_len payload length
|
||||||
|
* @return number of bytes sent, or -1 on error
|
||||||
|
*/
|
||||||
|
int bip_socket_send(
|
||||||
|
const uint8_t *dest_addr,
|
||||||
|
uint16_t dest_port,
|
||||||
|
const uint8_t *mtu,
|
||||||
|
uint16_t mtu_len);
|
||||||
|
/**
|
||||||
|
* @brief Receive a raw UDP payload for BACnet/IP
|
||||||
|
* @param buf receive buffer
|
||||||
|
* @param buf_len receive buffer size
|
||||||
|
* @param src_addr source IPv4 address
|
||||||
|
* @param src_port source UDP port
|
||||||
|
* @return number of bytes received
|
||||||
|
*/
|
||||||
|
int bip_socket_receive(
|
||||||
|
uint8_t *buf, uint16_t buf_len, uint8_t *src_addr, uint16_t *src_port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close the UDP socket backend used by BACnet/IP
|
||||||
|
*/
|
||||||
|
void bip_socket_cleanup(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read the current local IP address and subnet mask
|
||||||
|
* @param local_addr destination for the local IPv4 address
|
||||||
|
* @param netmask destination for the subnet mask
|
||||||
|
* @return true if the network information was available
|
||||||
|
*/
|
||||||
|
bool bip_get_local_network_info(uint8_t *local_addr, uint8_t *netmask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet/IP datalink
|
||||||
|
* @param port UDP port to use
|
||||||
|
* @return true if initialization succeeded
|
||||||
|
*/
|
||||||
|
bool bip_init(uint16_t port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Refresh the local and broadcast interface addresses
|
||||||
|
*/
|
||||||
|
void bip_set_interface(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shut down the BACnet/IP datalink
|
||||||
|
*/
|
||||||
|
void bip_cleanup(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a 4-byte address into a 32-bit value
|
||||||
|
* @param bip_address pointer to IPv4 address bytes
|
||||||
|
* @return packed address value
|
||||||
|
*/
|
||||||
|
uint32_t convertBIP_Address2uint32(const uint8_t *bip_address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a 32-bit address into a 4-byte array
|
||||||
|
* @param ip packed address value
|
||||||
|
* @param address destination buffer
|
||||||
|
*/
|
||||||
|
void convertUint32Address_2_uint8Address(uint32_t ip, uint8_t *address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the current socket identifier
|
||||||
|
* @param sock_fd socket identifier
|
||||||
|
*/
|
||||||
|
void bip_set_socket(uint8_t sock_fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current socket identifier
|
||||||
|
* @return socket identifier
|
||||||
|
*/
|
||||||
|
uint8_t bip_socket(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the BACnet/IP datalink is valid
|
||||||
|
* @return true if the datalink is ready to use
|
||||||
|
*/
|
||||||
|
bool bip_valid(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build the broadcast BACnet/IP address
|
||||||
|
* @param dest destination address structure
|
||||||
|
*/
|
||||||
|
void bip_get_broadcast_address(BACNET_ADDRESS *dest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build the local BACnet/IP address
|
||||||
|
* @param my_address destination address structure
|
||||||
|
*/
|
||||||
|
void bip_get_my_address(BACNET_ADDRESS *my_address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a BACnet NPDU over BACnet/IP
|
||||||
|
* @param dest destination BACnet address
|
||||||
|
* @param npdu_data NPDU metadata
|
||||||
|
* @param pdu payload buffer
|
||||||
|
* @param pdu_len payload length in bytes
|
||||||
|
* @return number of bytes sent, or -1 on error
|
||||||
|
*/
|
||||||
|
int bip_send_pdu(
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
BACNET_NPDU_DATA *npdu_data,
|
||||||
|
uint8_t *pdu,
|
||||||
|
unsigned pdu_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a raw BACnet/IP MPDU
|
||||||
|
* @param dest destination IP address and port
|
||||||
|
* @param mtu raw buffer to send
|
||||||
|
* @param mtu_len buffer length in bytes
|
||||||
|
* @return number of bytes sent, or -1 on error
|
||||||
|
*/
|
||||||
|
int bip_send_mpdu(
|
||||||
|
const BACNET_IP_ADDRESS *dest, const uint8_t *mtu, uint16_t mtu_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Receive a BACnet/IP NPDU
|
||||||
|
* @param src source BACnet address
|
||||||
|
* @param pdu receive buffer
|
||||||
|
* @param max_pdu receive buffer size
|
||||||
|
* @param timeout receive timeout in milliseconds
|
||||||
|
* @return number of NPDU bytes received
|
||||||
|
*/
|
||||||
|
uint16_t bip_receive(
|
||||||
|
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the UDP port used by BACnet/IP
|
||||||
|
* @param port UDP port number
|
||||||
|
*/
|
||||||
|
void bip_set_port(uint16_t port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the UDP port used by BACnet/IP
|
||||||
|
* @return UDP port number
|
||||||
|
*/
|
||||||
|
uint16_t bip_get_port(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the local IPv4 address
|
||||||
|
* @param net_address pointer to the 4-byte address
|
||||||
|
*/
|
||||||
|
void bip_set_addr(const uint8_t *net_address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the local IPv4 address
|
||||||
|
* @return pointer to the stored address
|
||||||
|
*/
|
||||||
|
uint8_t *bip_get_addr(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store the IPv4 broadcast address
|
||||||
|
* @param net_address pointer to the 4-byte broadcast address
|
||||||
|
*/
|
||||||
|
void bip_set_broadcast_addr(const uint8_t *net_address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the stored IPv4 broadcast address
|
||||||
|
* @return pointer to the stored broadcast address
|
||||||
|
*/
|
||||||
|
uint8_t *bip_get_broadcast_addr(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resolve a host name into an address when supported
|
||||||
|
* @param host_name host name to resolve
|
||||||
|
* @return resolved address value, or 0 when unsupported
|
||||||
|
*/
|
||||||
|
long bip_getaddrbyname(const char *host_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable BACnet/IP debug behavior
|
||||||
|
*/
|
||||||
|
void bip_debug_enable(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable BACnet/IP debug behavior
|
||||||
|
*/
|
||||||
|
void bip_debug_disable(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+76
-36
@@ -1,56 +1,96 @@
|
|||||||
//
|
/**
|
||||||
// Copyleft F.Chaxel 2017
|
* @file
|
||||||
//
|
* @brief BACnet/IP initialization helpers for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include <stdbool.h>
|
||||||
#include "esp_wifi.h"
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "lwip/sockets.h"
|
#include "bip.h"
|
||||||
#include "lwip/netdb.h"
|
|
||||||
|
|
||||||
#include "bacnet/datalink/bip.h"
|
static bool BIP_Debug = false;
|
||||||
|
|
||||||
long bip_get_addr_by_name(const char *host_name)
|
/**
|
||||||
|
* @brief Enable BACnet/IP debug mode
|
||||||
|
*/
|
||||||
|
void bip_debug_enable(void)
|
||||||
{
|
{
|
||||||
|
BIP_Debug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable BACnet/IP debug mode
|
||||||
|
*/
|
||||||
|
void bip_debug_disable(void)
|
||||||
|
{
|
||||||
|
BIP_Debug = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resolve a host name into an address when supported
|
||||||
|
* @param host_name host name to resolve
|
||||||
|
* @return resolved address, or 0 when unsupported by this port
|
||||||
|
*/
|
||||||
|
long bip_getaddrbyname(const char *host_name)
|
||||||
|
{
|
||||||
|
(void)host_name;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bip_set_interface(const char *ifname)
|
/**
|
||||||
|
* @brief Refresh the local and broadcast BACnet/IP addresses
|
||||||
|
*/
|
||||||
|
void bip_set_interface(void)
|
||||||
{
|
{
|
||||||
|
uint8_t local_address[] = { 0, 0, 0, 0 };
|
||||||
|
uint8_t broadcast_address[] = { 0, 0, 0, 0 };
|
||||||
|
uint8_t netmask[] = { 0, 0, 0, 0 };
|
||||||
|
uint8_t inverted_netmask[] = { 0, 0, 0, 0 };
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!bip_get_local_network_info(local_address, netmask)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bip_cleanup(void)
|
bip_set_addr(local_address);
|
||||||
{
|
|
||||||
close(bip_socket());
|
for (i = 0; i < 4; i++) {
|
||||||
bip_set_socket(-1);
|
inverted_netmask[i] = (uint8_t)~netmask[i];
|
||||||
|
broadcast_address[i] =
|
||||||
|
(uint8_t)(local_address[i] | inverted_netmask[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bip_init(char *ifname)
|
bip_set_broadcast_addr(broadcast_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet/IP datalink for the selected UDP port
|
||||||
|
* @param port UDP port number
|
||||||
|
* @return true if initialization succeeded
|
||||||
|
*/
|
||||||
|
bool bip_init(uint16_t port)
|
||||||
{
|
{
|
||||||
tcpip_adapter_ip_info_t ip_info = { 0 };
|
(void)BIP_Debug;
|
||||||
|
|
||||||
int value = 1;
|
if (!bip_socket_init(port)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
|
bip_set_interface();
|
||||||
|
bip_set_port(port);
|
||||||
|
|
||||||
bip_set_interface(ifname);
|
bip_set_socket(0);
|
||||||
bip_set_port(0xBAC0U);
|
|
||||||
bip_set_addr(ip_info.ip.addr);
|
|
||||||
bip_set_broadcast_addr(
|
|
||||||
(ip_info.ip.addr & ip_info.netmask.addr) | (~ip_info.netmask.addr));
|
|
||||||
|
|
||||||
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
|
||||||
struct sockaddr_in saddr = { 0 };
|
|
||||||
|
|
||||||
saddr.sin_family = PF_INET;
|
|
||||||
saddr.sin_port = htons(0xBAC0U);
|
|
||||||
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
|
|
||||||
|
|
||||||
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&value, sizeof(value));
|
|
||||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(value));
|
|
||||||
|
|
||||||
bip_set_socket(sock);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shut down the BACnet/IP datalink
|
||||||
|
*/
|
||||||
|
void bip_cleanup(void)
|
||||||
|
{
|
||||||
|
if (bip_valid()) {
|
||||||
|
bip_socket_cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief UDP socket bridge used by BACnet/IP on the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#if defined(USE_ETH_INTERFACE) && (USE_ETH_INTERFACE)
|
||||||
|
#include <ETH.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "bip.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
static WiFiUDP BipUdp;
|
||||||
|
static bool BipSocketOpen = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the selected network interface is up
|
||||||
|
* @return true if the interface is connected
|
||||||
|
*/
|
||||||
|
static bool network_connected(void)
|
||||||
|
{
|
||||||
|
#if defined(USE_ETH_INTERFACE) && (USE_ETH_INTERFACE)
|
||||||
|
return ETH.linkUp();
|
||||||
|
#else
|
||||||
|
return (WiFi.status() == WL_CONNECTED);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the local IP address for the selected network interface
|
||||||
|
* @return local IP address
|
||||||
|
*/
|
||||||
|
static IPAddress network_local_ip(void)
|
||||||
|
{
|
||||||
|
#if defined(USE_ETH_INTERFACE) && (USE_ETH_INTERFACE)
|
||||||
|
return ETH.localIP();
|
||||||
|
#else
|
||||||
|
return WiFi.localIP();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the subnet mask for the selected network interface
|
||||||
|
* @return subnet mask
|
||||||
|
*/
|
||||||
|
static IPAddress network_subnet_mask(void)
|
||||||
|
{
|
||||||
|
#if defined(USE_ETH_INTERFACE) && (USE_ETH_INTERFACE)
|
||||||
|
return ETH.subnetMask();
|
||||||
|
#else
|
||||||
|
return WiFi.subnetMask();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the UDP socket used by BACnet/IP
|
||||||
|
* @param port UDP port to bind
|
||||||
|
* @return true if the socket was opened successfully
|
||||||
|
*/
|
||||||
|
extern "C" bool bip_socket_init(uint16_t port)
|
||||||
|
{
|
||||||
|
if (!network_connected()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BipUdp.stop();
|
||||||
|
BipSocketOpen = (BipUdp.begin(port) == 1);
|
||||||
|
return BipSocketOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a UDP packet for BACnet/IP
|
||||||
|
* @param dest_addr destination IPv4 address
|
||||||
|
* @param dest_port destination UDP port
|
||||||
|
* @param mtu payload buffer
|
||||||
|
* @param mtu_len payload length in bytes
|
||||||
|
* @return number of bytes sent, or -1 on error
|
||||||
|
*/
|
||||||
|
extern "C" int bip_socket_send(
|
||||||
|
const uint8_t *dest_addr,
|
||||||
|
uint16_t dest_port,
|
||||||
|
const uint8_t *mtu,
|
||||||
|
uint16_t mtu_len)
|
||||||
|
{
|
||||||
|
if (!BipSocketOpen || !dest_addr || !mtu || (mtu_len == 0U)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress ip(dest_addr[0], dest_addr[1], dest_addr[2], dest_addr[3]);
|
||||||
|
if (!BipUdp.beginPacket(ip, dest_port)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t written = BipUdp.write(mtu, mtu_len);
|
||||||
|
if (!BipUdp.endPacket()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (written == mtu_len) ? (int)written : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Receive a UDP packet for BACnet/IP
|
||||||
|
* @param buf receive buffer
|
||||||
|
* @param buf_len receive buffer size
|
||||||
|
* @param src_addr source IPv4 address
|
||||||
|
* @param src_port source UDP port
|
||||||
|
* @return number of bytes received
|
||||||
|
*/
|
||||||
|
extern "C" int bip_socket_receive(
|
||||||
|
uint8_t *buf, uint16_t buf_len, uint8_t *src_addr, uint16_t *src_port)
|
||||||
|
{
|
||||||
|
if (!BipSocketOpen || !buf || !src_addr || !src_port || (buf_len == 0U)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int packet_size = BipUdp.parsePacket();
|
||||||
|
if (packet_size <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress remote = BipUdp.remoteIP();
|
||||||
|
src_addr[0] = remote[0];
|
||||||
|
src_addr[1] = remote[1];
|
||||||
|
src_addr[2] = remote[2];
|
||||||
|
src_addr[3] = remote[3];
|
||||||
|
*src_port = BipUdp.remotePort();
|
||||||
|
|
||||||
|
int to_read = packet_size;
|
||||||
|
if (to_read > (int)buf_len) {
|
||||||
|
to_read = (int)buf_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = BipUdp.read(buf, to_read);
|
||||||
|
return (len > 0) ? len : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close the UDP socket used by BACnet/IP
|
||||||
|
*/
|
||||||
|
extern "C" void bip_socket_cleanup(void)
|
||||||
|
{
|
||||||
|
BipUdp.stop();
|
||||||
|
BipSocketOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the local network address and subnet mask
|
||||||
|
* @param local_addr destination IPv4 address buffer
|
||||||
|
* @param netmask destination subnet mask buffer
|
||||||
|
* @return true if network information was available
|
||||||
|
*/
|
||||||
|
extern "C" bool
|
||||||
|
bip_get_local_network_info(uint8_t *local_addr, uint8_t *netmask)
|
||||||
|
{
|
||||||
|
if (!local_addr || !netmask || !network_connected()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress ip = network_local_ip();
|
||||||
|
IPAddress mask = network_subnet_mask();
|
||||||
|
local_addr[0] = ip[0];
|
||||||
|
local_addr[1] = ip[1];
|
||||||
|
local_addr[2] = ip[2];
|
||||||
|
local_addr[3] = ip[3];
|
||||||
|
|
||||||
|
netmask[0] = mask[0];
|
||||||
|
netmask[1] = mask[1];
|
||||||
|
netmask[2] = mask[2];
|
||||||
|
netmask[3] = mask[3];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,433 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
*********************************************************************/
|
|
||||||
|
|
||||||
/* Binary Output Objects - customize for your use */
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "bacnet/bacdef.h"
|
|
||||||
#include "bacnet/bacdcode.h"
|
|
||||||
#include "bacnet/bacenum.h"
|
|
||||||
#include "bacnet/bacapp.h"
|
|
||||||
#include "bacnet/config.h" /* the custom stuff */
|
|
||||||
#include "bacnet/rp.h"
|
|
||||||
#include "bacnet/wp.h"
|
|
||||||
#include "bacnet/basic/object/bo.h"
|
|
||||||
#include "bacnet/basic/services.h"
|
|
||||||
|
|
||||||
#ifndef MAX_BINARY_OUTPUTS
|
|
||||||
#define MAX_BINARY_OUTPUTS 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* When all the priorities are level null, the present value returns */
|
|
||||||
/* the Relinquish Default value */
|
|
||||||
#define RELINQUISH_DEFAULT BINARY_INACTIVE
|
|
||||||
/* Here is our Priority Array.*/
|
|
||||||
static BACNET_BINARY_PV Binary_Output_Level[MAX_BINARY_OUTPUTS]
|
|
||||||
[BACNET_MAX_PRIORITY];
|
|
||||||
/* Writable out-of-service allows others to play with our Present Value */
|
|
||||||
/* without changing the physical output */
|
|
||||||
static bool Out_Of_Service[MAX_BINARY_OUTPUTS];
|
|
||||||
|
|
||||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
|
||||||
static const int32_t Binary_Output_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
|
||||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
|
|
||||||
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, PROP_PRIORITY_ARRAY,
|
|
||||||
PROP_RELINQUISH_DEFAULT, -1 };
|
|
||||||
|
|
||||||
static const int32_t Binary_Output_Properties_Optional[] = { PROP_DESCRIPTION,
|
|
||||||
PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 };
|
|
||||||
|
|
||||||
static const int32_t Binary_Output_Properties_Proprietary[] = { -1 };
|
|
||||||
|
|
||||||
void Binary_Output_Property_Lists(
|
|
||||||
const int32_t **pRequired, const int32_t **pOptional, const int32_t **pProprietary)
|
|
||||||
{
|
|
||||||
if (pRequired)
|
|
||||||
*pRequired = Binary_Output_Properties_Required;
|
|
||||||
if (pOptional)
|
|
||||||
*pOptional = Binary_Output_Properties_Optional;
|
|
||||||
if (pProprietary)
|
|
||||||
*pProprietary = Binary_Output_Properties_Proprietary;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Binary_Output_Init(void)
|
|
||||||
{
|
|
||||||
unsigned i, j;
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
/* initialize all the analog output priority arrays to NULL */
|
|
||||||
for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
|
|
||||||
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
|
|
||||||
Binary_Output_Level[i][j] = BINARY_NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we simply have 0-n object instances. Yours might be */
|
|
||||||
/* more complex, and then you need validate that the */
|
|
||||||
/* given instance exists */
|
|
||||||
bool Binary_Output_Valid_Instance(uint32_t object_instance)
|
|
||||||
{
|
|
||||||
if (object_instance < MAX_BINARY_OUTPUTS)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we simply have 0-n object instances. Yours might be */
|
|
||||||
/* more complex, and then count how many you have */
|
|
||||||
unsigned Binary_Output_Count(void)
|
|
||||||
{
|
|
||||||
return MAX_BINARY_OUTPUTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 Binary_Output_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 Binary_Output_Instance_To_Index(uint32_t object_instance)
|
|
||||||
{
|
|
||||||
unsigned index = MAX_BINARY_OUTPUTS;
|
|
||||||
|
|
||||||
if (object_instance < MAX_BINARY_OUTPUTS)
|
|
||||||
index = object_instance;
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
BACNET_BINARY_PV Binary_Output_Present_Value(uint32_t object_instance)
|
|
||||||
{
|
|
||||||
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
|
|
||||||
unsigned index = 0;
|
|
||||||
unsigned i = 0;
|
|
||||||
|
|
||||||
index = Binary_Output_Instance_To_Index(object_instance);
|
|
||||||
if (index < MAX_BINARY_OUTPUTS) {
|
|
||||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
|
||||||
if (Binary_Output_Level[index][i] != BINARY_NULL) {
|
|
||||||
value = Binary_Output_Level[index][i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Binary_Output_Out_Of_Service(uint32_t object_instance)
|
|
||||||
{
|
|
||||||
bool value = false;
|
|
||||||
unsigned index = 0;
|
|
||||||
|
|
||||||
index = Binary_Output_Instance_To_Index(object_instance);
|
|
||||||
if (index < MAX_BINARY_OUTPUTS) {
|
|
||||||
value = Out_Of_Service[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* note: the object name must be unique within this device */
|
|
||||||
|
|
||||||
bool Binary_Output_Object_Name(
|
|
||||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
|
||||||
{
|
|
||||||
static char text[32] = "";
|
|
||||||
bool status = false;
|
|
||||||
|
|
||||||
if (object_instance == 0)
|
|
||||||
status = characterstring_init_ansi(object_name, "Led");
|
|
||||||
else {
|
|
||||||
if (object_instance < MAX_BINARY_OUTPUTS) {
|
|
||||||
snprintf(text, sizeof(text), "BINARY OUTPUT %lu",
|
|
||||||
(unsigned long)object_instance);
|
|
||||||
status = characterstring_init_ansi(object_name, text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
|
||||||
int Binary_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
|
||||||
{
|
|
||||||
int len = 0;
|
|
||||||
int apdu_len = 0; /* return value */
|
|
||||||
BACNET_BIT_STRING bit_string;
|
|
||||||
BACNET_CHARACTER_STRING char_string;
|
|
||||||
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
|
|
||||||
BACNET_POLARITY polarity = POLARITY_NORMAL;
|
|
||||||
unsigned object_index = 0;
|
|
||||||
unsigned i = 0;
|
|
||||||
bool state = false;
|
|
||||||
uint8_t *apdu = NULL;
|
|
||||||
|
|
||||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
|
||||||
(rpdata->application_data_len == 0)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
apdu = rpdata->application_data;
|
|
||||||
switch (rpdata->object_property) {
|
|
||||||
case PROP_OBJECT_IDENTIFIER:
|
|
||||||
apdu_len = encode_application_object_id(
|
|
||||||
&apdu[0], OBJECT_BINARY_OUTPUT, rpdata->object_instance);
|
|
||||||
break;
|
|
||||||
/* note: Name and Description don't have to be the same.
|
|
||||||
You could make Description writable and different */
|
|
||||||
case PROP_OBJECT_NAME:
|
|
||||||
case PROP_DESCRIPTION:
|
|
||||||
Binary_Output_Object_Name(rpdata->object_instance, &char_string);
|
|
||||||
apdu_len =
|
|
||||||
encode_application_character_string(&apdu[0], &char_string);
|
|
||||||
break;
|
|
||||||
case PROP_OBJECT_TYPE:
|
|
||||||
apdu_len =
|
|
||||||
encode_application_enumerated(&apdu[0], OBJECT_BINARY_OUTPUT);
|
|
||||||
break;
|
|
||||||
case PROP_PRESENT_VALUE:
|
|
||||||
present_value =
|
|
||||||
Binary_Output_Present_Value(rpdata->object_instance);
|
|
||||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
|
||||||
break;
|
|
||||||
case PROP_STATUS_FLAGS:
|
|
||||||
/* note: see the details in the standard on how to use these */
|
|
||||||
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:
|
|
||||||
/* note: see the details in the standard on how to use this */
|
|
||||||
apdu_len =
|
|
||||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
|
||||||
break;
|
|
||||||
case PROP_OUT_OF_SERVICE:
|
|
||||||
object_index =
|
|
||||||
Binary_Output_Instance_To_Index(rpdata->object_instance);
|
|
||||||
state = Out_Of_Service[object_index];
|
|
||||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
|
||||||
break;
|
|
||||||
case PROP_POLARITY:
|
|
||||||
apdu_len = encode_application_enumerated(&apdu[0], polarity);
|
|
||||||
break;
|
|
||||||
case PROP_PRIORITY_ARRAY:
|
|
||||||
/* Array element zero is the number of elements in the array */
|
|
||||||
if (rpdata->array_index == 0)
|
|
||||||
apdu_len =
|
|
||||||
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
|
|
||||||
/* if no index was specified, then try to encode the entire list */
|
|
||||||
/* into one packet. */
|
|
||||||
else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
|
||||||
object_index =
|
|
||||||
Binary_Output_Instance_To_Index(rpdata->object_instance);
|
|
||||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
|
||||||
/* FIXME: check if we have room before adding it to APDU */
|
|
||||||
if (Binary_Output_Level[object_index][i] == BINARY_NULL)
|
|
||||||
len = encode_application_null(&apdu[apdu_len]);
|
|
||||||
else {
|
|
||||||
present_value = Binary_Output_Level[object_index][i];
|
|
||||||
len = encode_application_enumerated(
|
|
||||||
&apdu[apdu_len], present_value);
|
|
||||||
}
|
|
||||||
/* add it if we have room */
|
|
||||||
if ((apdu_len + len) < MAX_APDU)
|
|
||||||
apdu_len += len;
|
|
||||||
else {
|
|
||||||
rpdata->error_code =
|
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
|
||||||
apdu_len = BACNET_STATUS_ABORT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
object_index =
|
|
||||||
Binary_Output_Instance_To_Index(rpdata->object_instance);
|
|
||||||
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
|
|
||||||
if (Binary_Output_Level[object_index][rpdata->array_index -
|
|
||||||
1] == BINARY_NULL)
|
|
||||||
apdu_len = encode_application_null(&apdu[apdu_len]);
|
|
||||||
else {
|
|
||||||
present_value =
|
|
||||||
Binary_Output_Level[object_index]
|
|
||||||
[rpdata->array_index - 1];
|
|
||||||
apdu_len = encode_application_enumerated(
|
|
||||||
&apdu[apdu_len], present_value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
|
||||||
apdu_len = BACNET_STATUS_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case PROP_RELINQUISH_DEFAULT:
|
|
||||||
present_value = RELINQUISH_DEFAULT;
|
|
||||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
|
||||||
break;
|
|
||||||
case PROP_ACTIVE_TEXT:
|
|
||||||
characterstring_init_ansi(&char_string, "on");
|
|
||||||
apdu_len =
|
|
||||||
encode_application_character_string(&apdu[0], &char_string);
|
|
||||||
break;
|
|
||||||
case PROP_INACTIVE_TEXT:
|
|
||||||
characterstring_init_ansi(&char_string, "off");
|
|
||||||
apdu_len =
|
|
||||||
encode_application_character_string(&apdu[0], &char_string);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
|
||||||
apdu_len = BACNET_STATUS_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* only array properties can have array options */
|
|
||||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
|
|
||||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
|
||||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
|
||||||
apdu_len = BACNET_STATUS_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return apdu_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* returns true if successful */
|
|
||||||
bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
|
||||||
{
|
|
||||||
bool status = false; /* return value */
|
|
||||||
unsigned int object_index = 0;
|
|
||||||
unsigned int priority = 0;
|
|
||||||
BACNET_BINARY_PV level = BINARY_NULL;
|
|
||||||
int len = 0;
|
|
||||||
BACNET_APPLICATION_DATA_VALUE value = { 0 };
|
|
||||||
|
|
||||||
/* 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? */
|
|
||||||
if (len < 0) {
|
|
||||||
/* error while decoding - a value larger than we can handle */
|
|
||||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
|
||||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
|
||||||
/* only array properties can have array options */
|
|
||||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (wp_data->object_property) {
|
|
||||||
case PROP_PRESENT_VALUE:
|
|
||||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
|
||||||
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.Enumerated <= MAX_BINARY_PV)) {
|
|
||||||
level = (BACNET_BINARY_PV)value.type.Enumerated;
|
|
||||||
object_index = Binary_Output_Instance_To_Index(
|
|
||||||
wp_data->object_instance);
|
|
||||||
priority--;
|
|
||||||
Binary_Output_Level[object_index][priority] = 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. */
|
|
||||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
|
||||||
} else {
|
|
||||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
status = write_property_type_valid(wp_data, &value,
|
|
||||||
BACNET_APPLICATION_TAG_NULL);
|
|
||||||
if (status) {
|
|
||||||
level = BINARY_NULL;
|
|
||||||
object_index = Binary_Output_Instance_To_Index(
|
|
||||||
wp_data->object_instance);
|
|
||||||
priority = wp_data->priority;
|
|
||||||
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
|
|
||||||
priority--;
|
|
||||||
Binary_Output_Level[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) */
|
|
||||||
} else {
|
|
||||||
status = false;
|
|
||||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PROP_OUT_OF_SERVICE:
|
|
||||||
status = write_property_type_valid(wp_data, &value,
|
|
||||||
BACNET_APPLICATION_TAG_BOOLEAN);
|
|
||||||
if (status) {
|
|
||||||
object_index =
|
|
||||||
Binary_Output_Instance_To_Index(wp_data->object_instance);
|
|
||||||
Out_Of_Service[object_index] = value.type.Boolean;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PROP_OBJECT_IDENTIFIER:
|
|
||||||
case PROP_OBJECT_NAME:
|
|
||||||
case PROP_OBJECT_TYPE:
|
|
||||||
case PROP_STATUS_FLAGS:
|
|
||||||
case PROP_RELIABILITY:
|
|
||||||
case PROP_EVENT_STATE:
|
|
||||||
case PROP_POLARITY:
|
|
||||||
case PROP_PRIORITY_ARRAY:
|
|
||||||
case PROP_RELINQUISH_DEFAULT:
|
|
||||||
case PROP_ACTIVE_TEXT:
|
|
||||||
case PROP_INACTIVE_TEXT:
|
|
||||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
||||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
*********************************************************************/
|
|
||||||
#ifndef BO_H
|
|
||||||
#define BO_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "bacnet/bacdef.h"
|
|
||||||
#include "bacnet/bacerror.h"
|
|
||||||
#include "bacnet/rp.h"
|
|
||||||
#include "bacnet/wp.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
void Binary_Output_Init(
|
|
||||||
void);
|
|
||||||
|
|
||||||
void Binary_Output_Property_Lists(
|
|
||||||
const int32_t **pRequired,
|
|
||||||
const int32_t **pOptional,
|
|
||||||
const int32_t **pProprietary);
|
|
||||||
|
|
||||||
bool Binary_Output_Valid_Instance(
|
|
||||||
uint32_t object_instance);
|
|
||||||
unsigned Binary_Output_Count(
|
|
||||||
void);
|
|
||||||
uint32_t Binary_Output_Index_To_Instance(
|
|
||||||
unsigned index);
|
|
||||||
unsigned Binary_Output_Instance_To_Index(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Binary_Output_Object_Instance_Add(
|
|
||||||
uint32_t instance);
|
|
||||||
|
|
||||||
bool Binary_Output_Object_Name(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_CHARACTER_STRING * object_name);
|
|
||||||
bool Binary_Output_Name_Set(
|
|
||||||
uint32_t object_instance,
|
|
||||||
char *new_name);
|
|
||||||
|
|
||||||
const char *Binary_Output_Description(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Binary_Output_Description_Set(
|
|
||||||
uint32_t instance,
|
|
||||||
const char *new_name);
|
|
||||||
|
|
||||||
const char *Binary_Output_Inactive_Text(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Binary_Output_Inactive_Text_Set(
|
|
||||||
uint32_t instance,
|
|
||||||
char *new_name);
|
|
||||||
char *Binary_Output_Active_Text(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Binary_Output_Active_Text_Set(
|
|
||||||
uint32_t instance,
|
|
||||||
char *new_name);
|
|
||||||
|
|
||||||
BACNET_BINARY_PV Binary_Output_Present_Value(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Binary_Output_Present_Value_Set(
|
|
||||||
uint32_t instance,
|
|
||||||
BACNET_BINARY_PV binary_value,
|
|
||||||
unsigned priority);
|
|
||||||
bool Binary_Output_Present_Value_Relinquish(
|
|
||||||
uint32_t instance,
|
|
||||||
unsigned priority);
|
|
||||||
unsigned Binary_Output_Present_Value_Priority(
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
BACNET_POLARITY Binary_Output_Polarity(
|
|
||||||
uint32_t instance);
|
|
||||||
bool Binary_Output_Polarity_Set(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_POLARITY polarity);
|
|
||||||
|
|
||||||
bool Binary_Output_Out_Of_Service(
|
|
||||||
uint32_t instance);
|
|
||||||
void Binary_Output_Out_Of_Service_Set(
|
|
||||||
uint32_t object_instance,
|
|
||||||
bool value);
|
|
||||||
|
|
||||||
BACNET_BINARY_PV Binary_Output_Relinquish_Default(
|
|
||||||
uint32_t object_instance);
|
|
||||||
bool Binary_Output_Relinquish_Default_Set(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_BINARY_PV value);
|
|
||||||
|
|
||||||
bool Binary_Output_Encode_Value_List(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_PROPERTY_VALUE * value_list);
|
|
||||||
bool Binary_Output_Change_Of_Value(
|
|
||||||
uint32_t instance);
|
|
||||||
void Binary_Output_Change_Of_Value_Clear(
|
|
||||||
uint32_t instance);
|
|
||||||
|
|
||||||
int Binary_Output_Read_Property(
|
|
||||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
|
||||||
bool Binary_Output_Write_Property(
|
|
||||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
|
||||||
|
|
||||||
uint32_t Binary_Output_Create(
|
|
||||||
uint32_t object_instance);
|
|
||||||
bool Binary_Output_Delete(
|
|
||||||
uint32_t object_instance);
|
|
||||||
void Binary_Output_Cleanup(
|
|
||||||
void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet Virtual Link Control implementation for the PlatformIO ESP32
|
||||||
|
* port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bip.h"
|
||||||
|
#include "bvlc.h"
|
||||||
|
#include "bacnet/bacint.h"
|
||||||
|
|
||||||
|
#define BVLC_RESULT 0
|
||||||
|
#define BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE 1
|
||||||
|
#define BVLC_READ_BROADCAST_DIST_TABLE 2
|
||||||
|
#define BVLC_READ_BROADCAST_DIST_TABLE_ACK 3
|
||||||
|
#define BVLC_FORWARDED_NPDU 4
|
||||||
|
#define BVLC_REGISTER_FOREIGN_DEVICE 5
|
||||||
|
#define BVLC_READ_FOREIGN_DEVICE_TABLE 6
|
||||||
|
#define BVLC_READ_FOREIGN_DEVICE_TABLE_ACK 7
|
||||||
|
#define BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY 8
|
||||||
|
#define BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK 9
|
||||||
|
#define BVLC_ORIGINAL_UNICAST_NPDU 10
|
||||||
|
#define BVLC_ORIGINAL_BROADCAST_NPDU 11
|
||||||
|
#define BVLC_SECURE_BVLL 12
|
||||||
|
|
||||||
|
#define BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK 0x0010
|
||||||
|
#define BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK 0x0020
|
||||||
|
#define BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK 0X0030
|
||||||
|
#define BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK 0x0040
|
||||||
|
#define BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK 0x0050
|
||||||
|
#define BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK 0x0060
|
||||||
|
|
||||||
|
static BACNET_BVLC_RESULT BVLC_Result_Code = BVLC_RESULT_SUCCESSFUL_COMPLETION;
|
||||||
|
static BACNET_BVLC_FUNCTION BVLC_Function_Code = BVLC_RESULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encode a BVLC result message
|
||||||
|
* @param pdu destination buffer, or NULL for length-only use
|
||||||
|
* @param result_code BVLC result code to encode
|
||||||
|
* @return encoded BVLC message length
|
||||||
|
*/
|
||||||
|
static int bvlc_encode_bvlc_result(uint8_t *pdu, BACNET_BVLC_RESULT result_code)
|
||||||
|
{
|
||||||
|
if (pdu) {
|
||||||
|
pdu[0] = BVLL_TYPE_BACNET_IP;
|
||||||
|
pdu[1] = BVLC_RESULT;
|
||||||
|
encode_unsigned16(&pdu[2], 6);
|
||||||
|
encode_unsigned16(&pdu[4], (uint16_t)result_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a raw BVLC frame to the destination host
|
||||||
|
* @param dest_addr destination IPv4 address
|
||||||
|
* @param dest_port destination UDP port
|
||||||
|
* @param mtu raw BVLC frame buffer
|
||||||
|
* @param mtu_len frame length in bytes
|
||||||
|
* @return number of bytes sent, or -1 on error
|
||||||
|
*/
|
||||||
|
static int bvlc_send_mpdu(
|
||||||
|
const uint8_t *dest_addr,
|
||||||
|
const uint16_t *dest_port,
|
||||||
|
uint8_t *mtu,
|
||||||
|
uint16_t mtu_len)
|
||||||
|
{
|
||||||
|
int bytes_sent = 0;
|
||||||
|
|
||||||
|
if (!bip_valid()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_sent = bip_socket_send(dest_addr, *dest_port, mtu, mtu_len);
|
||||||
|
|
||||||
|
return bytes_sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a BVLC result response
|
||||||
|
* @param dest_addr destination IPv4 address
|
||||||
|
* @param dest_port destination UDP port
|
||||||
|
* @param result_code BVLC result code to report
|
||||||
|
*/
|
||||||
|
static void bvlc_send_result(
|
||||||
|
const uint8_t *dest_addr,
|
||||||
|
const uint16_t *dest_port,
|
||||||
|
BACNET_BVLC_RESULT result_code)
|
||||||
|
{
|
||||||
|
uint8_t mtu[BIP_MPDU_MAX] = { 0 };
|
||||||
|
uint16_t mtu_len = 0;
|
||||||
|
|
||||||
|
mtu_len = (uint16_t)bvlc_encode_bvlc_result(&mtu[0], result_code);
|
||||||
|
bvlc_send_mpdu(dest_addr, dest_port, mtu, mtu_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle non-BBMD BVLC functions for this port
|
||||||
|
* @param addr source IPv4 address
|
||||||
|
* @param port source UDP port
|
||||||
|
* @param npdu received BVLC frame buffer
|
||||||
|
* @param received_bytes frame length in bytes
|
||||||
|
* @return non-zero when the frame was consumed by BVLC handling
|
||||||
|
*/
|
||||||
|
uint16_t bvlc_for_non_bbmd(
|
||||||
|
uint8_t *addr, uint16_t *port, uint8_t *npdu, uint16_t received_bytes)
|
||||||
|
{
|
||||||
|
uint16_t result_code = 0;
|
||||||
|
|
||||||
|
if (received_bytes >= 1) {
|
||||||
|
BVLC_Function_Code = npdu[1];
|
||||||
|
switch (BVLC_Function_Code) {
|
||||||
|
case BVLC_RESULT:
|
||||||
|
if (received_bytes >= 6) {
|
||||||
|
(void)decode_unsigned16(&npdu[4], &result_code);
|
||||||
|
BVLC_Result_Code = (BACNET_BVLC_RESULT)result_code;
|
||||||
|
fprintf(stderr, "BVLC: Result Code=%d\n", BVLC_Result_Code);
|
||||||
|
result_code = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE:
|
||||||
|
result_code =
|
||||||
|
BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK;
|
||||||
|
break;
|
||||||
|
case BVLC_READ_BROADCAST_DIST_TABLE:
|
||||||
|
result_code = BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK;
|
||||||
|
break;
|
||||||
|
case BVLC_REGISTER_FOREIGN_DEVICE:
|
||||||
|
result_code = BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK;
|
||||||
|
break;
|
||||||
|
case BVLC_READ_FOREIGN_DEVICE_TABLE:
|
||||||
|
result_code = BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK;
|
||||||
|
break;
|
||||||
|
case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY:
|
||||||
|
result_code = BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK;
|
||||||
|
break;
|
||||||
|
case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK:
|
||||||
|
result_code = BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result_code > 0) {
|
||||||
|
bvlc_send_result(addr, port, result_code);
|
||||||
|
fprintf(stderr, "BVLC: NAK code=%d\n", result_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the last decoded BVLC function code
|
||||||
|
* @return BVLC function code
|
||||||
|
*/
|
||||||
|
BACNET_BVLC_FUNCTION pico_bvlc_get_function_code(void)
|
||||||
|
{
|
||||||
|
return BVLC_Function_Code;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet Virtual Link Control interfaces for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M5STAMPLC_BVLC_H
|
||||||
|
#define M5STAMPLC_BVLC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "bacnet/datalink/bvlc.h"
|
||||||
|
|
||||||
|
#define BACNET_BVLC_RESULT uint8_t
|
||||||
|
#define BACNET_BVLC_FUNCTION uint8_t
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BIP_MPDU_MAX
|
||||||
|
#define BIP_MPDU_MAX 1506
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle non-BBMD BVLC functions for a received frame
|
||||||
|
* @param addr source IPv4 address
|
||||||
|
* @param port source UDP port
|
||||||
|
* @param npdu received frame buffer
|
||||||
|
* @param received_bytes frame length in bytes
|
||||||
|
* @return non-zero when the frame was consumed by BVLC handling
|
||||||
|
*/
|
||||||
|
uint16_t bvlc_for_non_bbmd(
|
||||||
|
uint8_t *addr, uint16_t *port, uint8_t *npdu, uint16_t received_bytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the most recently decoded BVLC function code
|
||||||
|
* @return BVLC function code
|
||||||
|
*/
|
||||||
|
BACNET_BVLC_FUNCTION pico_bvlc_get_function_code(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,461 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
*********************************************************************/
|
|
||||||
|
|
||||||
/** @file device.h Defines functions for handling all BACnet objects belonging
|
|
||||||
* to a BACnet device, as well as Device-specific properties. */
|
|
||||||
|
|
||||||
#ifndef DEVICE_H
|
|
||||||
#define DEVICE_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "bacnet/bacdef.h"
|
|
||||||
#include "bacnet/bacenum.h"
|
|
||||||
#include "bacnet/wp.h"
|
|
||||||
#include "bacnet/rd.h"
|
|
||||||
#include "bacnet/rp.h"
|
|
||||||
#include "bacnet/rpm.h"
|
|
||||||
#include "bacnet/readrange.h"
|
|
||||||
|
|
||||||
/** Called so a BACnet object can perform any necessary initialization.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
*/
|
|
||||||
typedef void (
|
|
||||||
*object_init_function) (
|
|
||||||
void);
|
|
||||||
|
|
||||||
/** Counts the number of objects of this type.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @return Count of implemented objects of this type.
|
|
||||||
*/
|
|
||||||
typedef unsigned (
|
|
||||||
*object_count_function) (
|
|
||||||
void);
|
|
||||||
|
|
||||||
/** Maps an object index position to its corresponding BACnet object instance number.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param index [in] The index of the object, in the array of objects of its type.
|
|
||||||
* @return The BACnet object instance number to be used in a BACNET_OBJECT_ID.
|
|
||||||
*/
|
|
||||||
typedef uint32_t(
|
|
||||||
*object_index_to_instance_function)
|
|
||||||
(
|
|
||||||
unsigned index);
|
|
||||||
|
|
||||||
/** Provides the BACnet Object_Name for a given object instance of this type.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param object_instance [in] The object instance number to be looked up.
|
|
||||||
* @param object_name [in,out] Pointer to a character_string structure that
|
|
||||||
* will hold a copy of the object name if this is a valid object_instance.
|
|
||||||
* @return True if the object_instance is valid and object_name has been
|
|
||||||
* filled with a copy of the Object's name.
|
|
||||||
*/
|
|
||||||
typedef bool(
|
|
||||||
*object_name_function)
|
|
||||||
(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_CHARACTER_STRING * object_name);
|
|
||||||
|
|
||||||
/** Look in the table of objects of this type, and see if this is a valid
|
|
||||||
* instance number.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param [in] The object instance number to be looked up.
|
|
||||||
* @return True if the object instance refers to a valid object of this type.
|
|
||||||
*/
|
|
||||||
typedef bool(
|
|
||||||
*object_valid_instance_function) (
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
/** Helper function to step through an array of objects and find either the
|
|
||||||
* first one or the next one of a given type. Used to step through an array
|
|
||||||
* of objects which is not necessarily contiguious for each type i.e. the
|
|
||||||
* index for the 'n'th object of a given type is not necessarily 'n'.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param [in] The index of the current object or a value of ~0 to indicate
|
|
||||||
* start at the beginning.
|
|
||||||
* @return The index of the next object of the required type or ~0 (all bits
|
|
||||||
* == 1) to indicate no more objects found.
|
|
||||||
*/
|
|
||||||
typedef unsigned (
|
|
||||||
*object_iterate_function) (
|
|
||||||
unsigned current_index);
|
|
||||||
|
|
||||||
/** Look in the table of objects of this type, and get the COV Value List.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param [in] The object instance number to be looked up.
|
|
||||||
* @param [out] The value list
|
|
||||||
* @return True if the object instance supports this feature, and has changed.
|
|
||||||
*/
|
|
||||||
typedef bool(
|
|
||||||
*object_value_list_function) (
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_PROPERTY_VALUE * value_list);
|
|
||||||
|
|
||||||
/** Look in the table of objects for this instance to see if value changed.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param [in] The object instance number to be looked up.
|
|
||||||
* @return True if the object instance has changed.
|
|
||||||
*/
|
|
||||||
typedef bool(
|
|
||||||
*object_cov_function) (
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
/** Look in the table of objects for this instance to clear the changed flag.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param [in] The object instance number to be looked up.
|
|
||||||
*/
|
|
||||||
typedef void (
|
|
||||||
*object_cov_clear_function) (
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
/** Intrinsic Reporting funcionality.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* @param [in] Object instance.
|
|
||||||
*/
|
|
||||||
typedef void (
|
|
||||||
*object_intrinsic_reporting_function) (
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
|
|
||||||
/** Defines the group of object helper functions for any supported Object.
|
|
||||||
* @ingroup ObjHelpers
|
|
||||||
* Each Object must provide some implementation of each of these helpers
|
|
||||||
* in order to properly support the handlers. Eg, the ReadProperty handler
|
|
||||||
* handler_read_property() relies on the instance of Object_Read_Property
|
|
||||||
* for each Object type, or configure the function as NULL.
|
|
||||||
* In both appearance and operation, this group of functions acts like
|
|
||||||
* they are member functions of a C++ Object base class.
|
|
||||||
*/
|
|
||||||
typedef struct object_functions {
|
|
||||||
BACNET_OBJECT_TYPE Object_Type;
|
|
||||||
object_init_function Object_Init;
|
|
||||||
object_count_function Object_Count;
|
|
||||||
object_index_to_instance_function Object_Index_To_Instance;
|
|
||||||
object_valid_instance_function Object_Valid_Instance;
|
|
||||||
object_name_function Object_Name;
|
|
||||||
read_property_function Object_Read_Property;
|
|
||||||
write_property_function Object_Write_Property;
|
|
||||||
rpm_property_lists_function Object_RPM_List;
|
|
||||||
rr_info_function Object_RR_Info;
|
|
||||||
object_iterate_function Object_Iterator;
|
|
||||||
object_value_list_function Object_Value_List;
|
|
||||||
object_cov_function Object_COV;
|
|
||||||
object_cov_clear_function Object_COV_Clear;
|
|
||||||
object_intrinsic_reporting_function Object_Intrinsic_Reporting;
|
|
||||||
} object_functions_t;
|
|
||||||
|
|
||||||
/* String Lengths - excluding any nul terminator */
|
|
||||||
#define MAX_DEV_NAME_LEN 32
|
|
||||||
#define MAX_DEV_LOC_LEN 64
|
|
||||||
#define MAX_DEV_MOD_LEN 32
|
|
||||||
#define MAX_DEV_VER_LEN 16
|
|
||||||
#define MAX_DEV_DESC_LEN 64
|
|
||||||
|
|
||||||
/** Structure to define the Object Properties common to all Objects. */
|
|
||||||
typedef struct commonBacObj_s {
|
|
||||||
|
|
||||||
/** The BACnet type of this object (ie, what class is this object from?).
|
|
||||||
* This property, of type BACnetObjectType, indicates membership in a
|
|
||||||
* particular object type class. Each inherited class will be of one type.
|
|
||||||
*/
|
|
||||||
BACNET_OBJECT_TYPE mObject_Type;
|
|
||||||
|
|
||||||
/** The instance number for this class instance. */
|
|
||||||
uint32_t Object_Instance_Number;
|
|
||||||
|
|
||||||
/** Object Name; must be unique.
|
|
||||||
* This property, of type CharacterString, shall represent a name for
|
|
||||||
* the object that is unique within the BACnet Device that maintains it.
|
|
||||||
*/
|
|
||||||
char Object_Name[MAX_DEV_NAME_LEN];
|
|
||||||
|
|
||||||
} COMMON_BAC_OBJECT;
|
|
||||||
|
|
||||||
|
|
||||||
/** Structure to define the Properties of Device Objects which distinguish
|
|
||||||
* one instance from another.
|
|
||||||
* This structure only defines fields for properties that are unique to
|
|
||||||
* a given Device object. The rest may be fixed in device.c or hard-coded
|
|
||||||
* into the read-property encoding.
|
|
||||||
* This may be useful for implementations which manage multiple Devices,
|
|
||||||
* eg, a Gateway.
|
|
||||||
*/
|
|
||||||
typedef struct devObj_s {
|
|
||||||
/** The BACnet Device Address for this device; ->len depends on DLL type. */
|
|
||||||
BACNET_ADDRESS bacDevAddr;
|
|
||||||
|
|
||||||
/** Structure for the Object Properties common to all Objects. */
|
|
||||||
COMMON_BAC_OBJECT bacObj;
|
|
||||||
|
|
||||||
/** Device Description. */
|
|
||||||
char Description[MAX_DEV_DESC_LEN];
|
|
||||||
|
|
||||||
/** The upcounter that shows if the Device ID or object structure has changed. */
|
|
||||||
uint32_t Database_Revision;
|
|
||||||
} DEVICE_OBJECT_DATA;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
void Device_Init(
|
|
||||||
object_functions_t * object_table);
|
|
||||||
|
|
||||||
bool Device_Reinitialize(
|
|
||||||
BACNET_REINITIALIZE_DEVICE_DATA * rd_data);
|
|
||||||
bool Device_Reinitialize_State_Set(BACNET_REINITIALIZED_STATE state);
|
|
||||||
BACNET_REINITIALIZED_STATE Device_Reinitialized_State(
|
|
||||||
void);
|
|
||||||
|
|
||||||
rr_info_function Device_Objects_RR_Info(
|
|
||||||
BACNET_OBJECT_TYPE object_type);
|
|
||||||
|
|
||||||
void Device_getCurrentDateTime(
|
|
||||||
BACNET_DATE_TIME * DateTime);
|
|
||||||
|
|
||||||
int32_t Device_UTC_Offset(void);
|
|
||||||
void Device_UTC_Offset_Set(int16_t offset);
|
|
||||||
|
|
||||||
bool Device_Daylight_Savings_Status(void);
|
|
||||||
bool Device_Align_Intervals(void);
|
|
||||||
bool Device_Align_Intervals_Set(bool flag);
|
|
||||||
uint32_t Device_Time_Sync_Interval(void);
|
|
||||||
bool Device_Time_Sync_Interval_Set(uint32_t value);
|
|
||||||
uint32_t Device_Interval_Offset(void);
|
|
||||||
bool Device_Interval_Offset_Set(uint32_t value);
|
|
||||||
|
|
||||||
void Device_Property_Lists(
|
|
||||||
const int32_t **pRequired,
|
|
||||||
const int32_t **pOptional,
|
|
||||||
const int32_t **pProprietary);
|
|
||||||
void Device_Objects_Property_List(
|
|
||||||
BACNET_OBJECT_TYPE object_type,
|
|
||||||
uint32_t object_instance,
|
|
||||||
struct special_property_list_t *pPropertyList);
|
|
||||||
/* functions to support COV */
|
|
||||||
bool Device_Encode_Value_List(
|
|
||||||
BACNET_OBJECT_TYPE object_type,
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_PROPERTY_VALUE * value_list);
|
|
||||||
bool Device_Value_List_Supported(
|
|
||||||
BACNET_OBJECT_TYPE object_type);
|
|
||||||
bool Device_COV(
|
|
||||||
BACNET_OBJECT_TYPE object_type,
|
|
||||||
uint32_t object_instance);
|
|
||||||
void Device_COV_Clear(
|
|
||||||
BACNET_OBJECT_TYPE object_type,
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
uint32_t Device_Object_Instance_Number(
|
|
||||||
void);
|
|
||||||
bool Device_Set_Object_Instance_Number(
|
|
||||||
uint32_t object_id);
|
|
||||||
bool Device_Valid_Object_Instance_Number(
|
|
||||||
uint32_t object_id);
|
|
||||||
unsigned Device_Object_List_Count(
|
|
||||||
void);
|
|
||||||
bool Device_Object_List_Identifier(
|
|
||||||
uint32_t array_index,
|
|
||||||
BACNET_OBJECT_TYPE *object_type,
|
|
||||||
uint32_t * instance);
|
|
||||||
|
|
||||||
unsigned Device_Count(
|
|
||||||
void);
|
|
||||||
uint32_t Device_Index_To_Instance(
|
|
||||||
unsigned index);
|
|
||||||
|
|
||||||
bool Device_Object_Name(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_CHARACTER_STRING * object_name);
|
|
||||||
bool Device_Set_Object_Name(
|
|
||||||
const BACNET_CHARACTER_STRING * object_name);
|
|
||||||
/* Copy a child object name, given its ID. */
|
|
||||||
bool Device_Object_Name_Copy(
|
|
||||||
BACNET_OBJECT_TYPE object_type,
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_CHARACTER_STRING * object_name);
|
|
||||||
bool Device_Object_Name_ANSI_Init(const char * object_name);
|
|
||||||
char * Device_Object_Name_ANSI(void);
|
|
||||||
|
|
||||||
BACNET_DEVICE_STATUS Device_System_Status(
|
|
||||||
void);
|
|
||||||
int Device_Set_System_Status(
|
|
||||||
BACNET_DEVICE_STATUS status,
|
|
||||||
bool local);
|
|
||||||
|
|
||||||
const char *Device_Vendor_Name(
|
|
||||||
void);
|
|
||||||
|
|
||||||
uint16_t Device_Vendor_Identifier(
|
|
||||||
void);
|
|
||||||
void Device_Set_Vendor_Identifier(
|
|
||||||
uint16_t vendor_id);
|
|
||||||
|
|
||||||
const char *Device_Model_Name(
|
|
||||||
void);
|
|
||||||
bool Device_Set_Model_Name(
|
|
||||||
const char *name,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
const char *Device_Firmware_Revision(
|
|
||||||
void);
|
|
||||||
|
|
||||||
const char *Device_Application_Software_Version(
|
|
||||||
void);
|
|
||||||
bool Device_Set_Application_Software_Version(
|
|
||||||
const char *name,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
const char *Device_Description(
|
|
||||||
void);
|
|
||||||
bool Device_Set_Description(
|
|
||||||
const char *name,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
const char *Device_Location(
|
|
||||||
void);
|
|
||||||
bool Device_Set_Location(
|
|
||||||
const char *name,
|
|
||||||
size_t length);
|
|
||||||
|
|
||||||
/* some stack-centric constant values - no set methods */
|
|
||||||
uint8_t Device_Protocol_Version(
|
|
||||||
void);
|
|
||||||
uint8_t Device_Protocol_Revision(
|
|
||||||
void);
|
|
||||||
BACNET_SEGMENTATION Device_Segmentation_Supported(
|
|
||||||
void);
|
|
||||||
|
|
||||||
uint32_t Device_Database_Revision(
|
|
||||||
void);
|
|
||||||
void Device_Set_Database_Revision(
|
|
||||||
uint32_t revision);
|
|
||||||
void Device_Inc_Database_Revision(
|
|
||||||
void);
|
|
||||||
|
|
||||||
bool Device_Valid_Object_Name(
|
|
||||||
const BACNET_CHARACTER_STRING * object_name,
|
|
||||||
BACNET_OBJECT_TYPE *object_type,
|
|
||||||
uint32_t * object_instance);
|
|
||||||
bool Device_Valid_Object_Id(
|
|
||||||
BACNET_OBJECT_TYPE object_type,
|
|
||||||
uint32_t object_instance);
|
|
||||||
|
|
||||||
int Device_Read_Property(
|
|
||||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
|
||||||
bool Device_Write_Property(
|
|
||||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
|
||||||
|
|
||||||
bool DeviceGetRRInfo(
|
|
||||||
BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
|
|
||||||
RR_PROP_INFO * pInfo); /* Where to put the information */
|
|
||||||
|
|
||||||
int Device_Read_Property_Local(
|
|
||||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
|
||||||
bool Device_Write_Property_Local(
|
|
||||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
|
||||||
|
|
||||||
#if defined(INTRINSIC_REPORTING)
|
|
||||||
void Device_local_reporting(
|
|
||||||
void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Prototypes for Routing functionality in the Device Object.
|
|
||||||
* Enable by defining BAC_ROUTING in config.h and including gw_device.c
|
|
||||||
* in the build (lib/Makefile).
|
|
||||||
*/
|
|
||||||
void Routing_Device_Init(
|
|
||||||
uint32_t first_object_instance);
|
|
||||||
|
|
||||||
uint16_t Add_Routed_Device(
|
|
||||||
uint32_t Object_Instance,
|
|
||||||
const BACNET_CHARACTER_STRING * Object_Name,
|
|
||||||
const char *Description);
|
|
||||||
DEVICE_OBJECT_DATA *Get_Routed_Device_Object(
|
|
||||||
int idx);
|
|
||||||
BACNET_ADDRESS *Get_Routed_Device_Address(
|
|
||||||
int idx);
|
|
||||||
|
|
||||||
void routed_get_my_address(
|
|
||||||
BACNET_ADDRESS * my_address);
|
|
||||||
|
|
||||||
bool Routed_Device_Address_Lookup(
|
|
||||||
int idx,
|
|
||||||
uint8_t address_len,
|
|
||||||
const uint8_t * mac_adress);
|
|
||||||
bool Routed_Device_GetNext(
|
|
||||||
const BACNET_ADDRESS * dest,
|
|
||||||
const int32_t *DNET_list,
|
|
||||||
int *cursor);
|
|
||||||
bool Routed_Device_Is_Valid_Network(
|
|
||||||
uint16_t dest_net,
|
|
||||||
const int32_t *DNET_list);
|
|
||||||
|
|
||||||
uint32_t Routed_Device_Index_To_Instance(
|
|
||||||
unsigned index);
|
|
||||||
bool Routed_Device_Valid_Object_Instance_Number(
|
|
||||||
uint32_t object_id);
|
|
||||||
bool Routed_Device_Name(
|
|
||||||
uint32_t object_instance,
|
|
||||||
BACNET_CHARACTER_STRING * object_name);
|
|
||||||
uint32_t Routed_Device_Object_Instance_Number(
|
|
||||||
void);
|
|
||||||
bool Routed_Device_Set_Object_Instance_Number(
|
|
||||||
uint32_t object_id);
|
|
||||||
bool Routed_Device_Set_Object_Name(
|
|
||||||
uint8_t encoding,
|
|
||||||
const char *value,
|
|
||||||
size_t length);
|
|
||||||
bool Routed_Device_Set_Description(
|
|
||||||
const char *name,
|
|
||||||
size_t length);
|
|
||||||
void Routed_Device_Inc_Database_Revision(
|
|
||||||
void);
|
|
||||||
int Routed_Device_Service_Approval(
|
|
||||||
BACNET_CONFIRMED_SERVICE service,
|
|
||||||
int service_argument,
|
|
||||||
uint8_t * apdu_buff,
|
|
||||||
uint8_t invoke_id);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
/** @defgroup ObjFrmwk Object Framework
|
|
||||||
* The modules in this section describe the BACnet-stack's framework for
|
|
||||||
* BACnet-defined Objects (Device, Analog Input, etc). There are two submodules
|
|
||||||
* to describe this arrangement:
|
|
||||||
* - The "object helper functions" which provide C++-like common functionality
|
|
||||||
* to all supported object types.
|
|
||||||
* - The interface between the implemented Objects and the BAC-stack services,
|
|
||||||
* specifically the handlers, which are mediated through function calls to
|
|
||||||
* the Device object.
|
|
||||||
*/
|
|
||||||
/** @defgroup ObjHelpers Object Helper Functions
|
|
||||||
* @ingroup ObjFrmwk
|
|
||||||
* This section describes the function templates for the helper functions that
|
|
||||||
* provide common object support.
|
|
||||||
*/
|
|
||||||
/** @defgroup ObjIntf Handler-to-Object Interface Functions
|
|
||||||
* @ingroup ObjFrmwk
|
|
||||||
* This section describes the fairly limited set of functions that link the
|
|
||||||
* BAC-stack handlers to the BACnet Object instances. All of these calls are
|
|
||||||
* situated in the Device Object, which "knows" how to reach its child Objects.
|
|
||||||
*
|
|
||||||
* Most of these calls have a common operation:
|
|
||||||
* -# Call Device_Objects_Find_Functions( for the desired Object_Type )
|
|
||||||
* - Gets a pointer to the object_functions for this Type of Object.
|
|
||||||
* -# Call the Object's Object_Valid_Instance( for the desired object_instance )
|
|
||||||
* to make sure there is such an instance.
|
|
||||||
* -# Call the Object helper function needed by the handler,
|
|
||||||
* eg Object_Read_Property() for the RP handler.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet MS/TP datalink environment setup for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dlenv.h"
|
||||||
|
|
||||||
|
#include "bacnet/datalink/dlmstp.h"
|
||||||
|
#include "rs485.h"
|
||||||
|
|
||||||
|
volatile struct mstp_port_struct_t MSTP_Port;
|
||||||
|
|
||||||
|
static struct dlmstp_user_data_t MSTP_User_Data;
|
||||||
|
static uint8_t Input_Buffer[DLMSTP_MPDU_MAX];
|
||||||
|
static uint8_t Output_Buffer[DLMSTP_MPDU_MAX];
|
||||||
|
|
||||||
|
static struct dlmstp_rs485_driver RS485_Driver = {
|
||||||
|
.init = rs485_init,
|
||||||
|
.send = rs485_bytes_send,
|
||||||
|
.read = rs485_byte_available,
|
||||||
|
.transmitting = rs485_rts_enabled,
|
||||||
|
.baud_rate = rs485_baud_rate,
|
||||||
|
.baud_rate_set = rs485_baud_rate_set,
|
||||||
|
.silence_milliseconds = rs485_silence_milliseconds,
|
||||||
|
.silence_reset = rs485_silence_reset
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet MS/TP datalink environment
|
||||||
|
* @param mac_address local MS/TP MAC address
|
||||||
|
* @return true if the datalink initialized successfully
|
||||||
|
*/
|
||||||
|
bool m5_dlenv_init(uint8_t mac_address)
|
||||||
|
{
|
||||||
|
RS485_Driver.init();
|
||||||
|
|
||||||
|
MSTP_Port.Nmax_info_frames = BACNET_MSTP_MAX_INFO_FRAMES;
|
||||||
|
MSTP_Port.Nmax_master = BACNET_MSTP_MAX_MASTER;
|
||||||
|
|
||||||
|
MSTP_Port.InputBuffer = Input_Buffer;
|
||||||
|
MSTP_Port.InputBufferSize = sizeof(Input_Buffer);
|
||||||
|
MSTP_Port.OutputBuffer = Output_Buffer;
|
||||||
|
MSTP_Port.OutputBufferSize = sizeof(Output_Buffer);
|
||||||
|
|
||||||
|
MSTP_Port.ZeroConfigEnabled = false;
|
||||||
|
MSTP_Port.SlaveNodeEnabled = false;
|
||||||
|
MSTP_Port.CheckAutoBaud = false;
|
||||||
|
|
||||||
|
MSTP_Zero_Config_UUID_Init((struct mstp_port_struct_t *)&MSTP_Port);
|
||||||
|
|
||||||
|
MSTP_User_Data.RS485_Driver = &RS485_Driver;
|
||||||
|
MSTP_Port.UserData = &MSTP_User_Data;
|
||||||
|
|
||||||
|
if (!dlmstp_init((char *)&MSTP_Port)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dlmstp_set_mac_address(mac_address);
|
||||||
|
dlmstp_set_baud_rate(RS485_BAUD_RATE);
|
||||||
|
dlmstp_set_max_master(BACNET_MSTP_MAX_MASTER);
|
||||||
|
dlmstp_set_max_info_frames(BACNET_MSTP_MAX_INFO_FRAMES);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run any periodic MS/TP maintenance hooks
|
||||||
|
* @param seconds elapsed seconds since the last call
|
||||||
|
*/
|
||||||
|
void dlenv_maintenance_timer(uint16_t seconds)
|
||||||
|
{
|
||||||
|
(void)seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shut down the MS/TP datalink environment
|
||||||
|
*/
|
||||||
|
void dlenv_cleanup(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet MS/TP datalink environment declarations for the PlatformIO
|
||||||
|
* ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M5STAMPLC_DLENV_H
|
||||||
|
#define M5STAMPLC_DLENV_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "bacnet/bacdef.h"
|
||||||
|
#include "bacnet/datalink/dlenv.h"
|
||||||
|
#include "bacnet/datalink/dlmstp.h"
|
||||||
|
#include "bacnet/datalink/mstp.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BACNET_MSTP_MAX_MASTER 127
|
||||||
|
#define BACNET_MSTP_MAX_INFO_FRAMES 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet MS/TP datalink environment
|
||||||
|
* @param mac_address local MS/TP MAC address
|
||||||
|
* @return true if initialization succeeded
|
||||||
|
*/
|
||||||
|
bool m5_dlenv_init(uint8_t mac_address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run periodic MS/TP maintenance work
|
||||||
|
* @param seconds elapsed seconds since the last call
|
||||||
|
*/
|
||||||
|
void dlenv_maintenance_timer(uint16_t seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shut down the MS/TP datalink environment
|
||||||
|
*/
|
||||||
|
void dlenv_cleanup(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyleft F.Chaxel 2017
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "bacnet/config.h"
|
|
||||||
#include "bacnet/basic/tsm/tsm.h"
|
|
||||||
#include "bacnet/basic/services.h"
|
|
||||||
|
|
||||||
#include "bacnet/basic/services.h"
|
|
||||||
#include "bacnet/datalink/datalink.h"
|
|
||||||
#include "bacnet/dcc.h"
|
|
||||||
#include "bacnet/basic/tsm/tsm.h"
|
|
||||||
// conflict filename address.h with another file in default include paths
|
|
||||||
#include "../lib/stack/address.h"
|
|
||||||
#include "bacnet/datalink/bip.h"
|
|
||||||
|
|
||||||
#include "bacnet/basic/object/device.h"
|
|
||||||
#include "bacnet/basic/object/ai.h"
|
|
||||||
#include "bacnet/basic/object/bo.h"
|
|
||||||
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_wifi.h"
|
|
||||||
#include "esp_event_loop.h"
|
|
||||||
#include "nvs_flash.h"
|
|
||||||
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
|
|
||||||
#include "lwip/sockets.h"
|
|
||||||
#include "lwip/netdb.h"
|
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "freertos/event_groups.h"
|
|
||||||
|
|
||||||
// hidden function not in any .h files
|
|
||||||
extern uint8_t temprature_sens_read();
|
|
||||||
extern uint32_t hall_sens_read();
|
|
||||||
|
|
||||||
// Wifi params
|
|
||||||
wifi_config_t wifi_config = {
|
|
||||||
.sta = {
|
|
||||||
.ssid = "myWifi",
|
|
||||||
.password = "myPass",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// GPIO 5 has a Led on Sparkfun ESP32 board
|
|
||||||
#define BACNET_LED 5
|
|
||||||
|
|
||||||
uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 };
|
|
||||||
|
|
||||||
/** Static receive buffer, initialized with zeros by the C Library Startup Code. */
|
|
||||||
|
|
||||||
uint8_t Rx_Buf[MAX_MPDU + 16 /* Add a little safety margin to the buffer,
|
|
||||||
* so that in the rare case, the message
|
|
||||||
* would be filled up to MAX_MPDU and some
|
|
||||||
* decoding functions would overrun, these
|
|
||||||
* decoding functions will just end up in
|
|
||||||
* a safe field of static zeros. */] = { 0 };
|
|
||||||
|
|
||||||
EventGroupHandle_t wifi_event_group;
|
|
||||||
const static int CONNECTED_BIT = BIT0;
|
|
||||||
|
|
||||||
/* BACnet handler, stack init, IAm */
|
|
||||||
void StartBACnet()
|
|
||||||
{
|
|
||||||
/* we need to handle who-is to support dynamic device binding */
|
|
||||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
|
||||||
|
|
||||||
/* set the handler for all the services we don't implement */
|
|
||||||
/* It is required to send the proper reject message... */
|
|
||||||
apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
|
|
||||||
/* Set the handlers for any confirmed services that we support. */
|
|
||||||
/* We must implement read property - it's required! */
|
|
||||||
apdu_set_confirmed_handler(
|
|
||||||
SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
|
|
||||||
apdu_set_confirmed_handler(
|
|
||||||
SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple);
|
|
||||||
|
|
||||||
apdu_set_confirmed_handler(
|
|
||||||
SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
|
|
||||||
apdu_set_confirmed_handler(
|
|
||||||
SERVICE_CONFIRMED_SUBSCRIBE_COV, handler_cov_subscribe);
|
|
||||||
|
|
||||||
address_init();
|
|
||||||
bip_init(NULL);
|
|
||||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wifi events handler : start & stop bacnet with an event */
|
|
||||||
esp_err_t wifi_event_handler(void *ctx, system_event_t *event)
|
|
||||||
{
|
|
||||||
switch (event->event_id) {
|
|
||||||
case SYSTEM_EVENT_STA_START:
|
|
||||||
esp_wifi_connect();
|
|
||||||
break;
|
|
||||||
case SYSTEM_EVENT_STA_CONNECTED:
|
|
||||||
break;
|
|
||||||
case SYSTEM_EVENT_STA_GOT_IP:
|
|
||||||
if (xEventGroupGetBits(wifi_event_group) != CONNECTED_BIT) {
|
|
||||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
|
||||||
StartBACnet();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
|
||||||
/* This is a workaround as ESP32 WiFi libs don't currently
|
|
||||||
auto-reassociate. */
|
|
||||||
esp_wifi_connect();
|
|
||||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
|
||||||
bip_cleanup();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tcpip & wifi station start */
|
|
||||||
|
|
||||||
void wifi_init_station(void)
|
|
||||||
{
|
|
||||||
tcpip_adapter_init();
|
|
||||||
wifi_event_group = xEventGroupCreate();
|
|
||||||
esp_event_loop_init(wifi_event_handler, NULL);
|
|
||||||
|
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
esp_wifi_init(&cfg);
|
|
||||||
|
|
||||||
esp_wifi_set_storage(WIFI_STORAGE_RAM);
|
|
||||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
|
||||||
|
|
||||||
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
|
|
||||||
|
|
||||||
esp_wifi_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup gpio & nv flash, call wifi init code */
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
gpio_pad_select_gpio(BACNET_LED);
|
|
||||||
gpio_set_direction(BACNET_LED, GPIO_MODE_OUTPUT);
|
|
||||||
|
|
||||||
gpio_set_level(BACNET_LED, 0);
|
|
||||||
|
|
||||||
esp_err_t ret = nvs_flash_init();
|
|
||||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
|
|
||||||
nvs_flash_erase();
|
|
||||||
ret = nvs_flash_init();
|
|
||||||
}
|
|
||||||
wifi_init_station();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bacnet Task */
|
|
||||||
void BACnetTask(void *pvParameters)
|
|
||||||
{
|
|
||||||
uint16_t pdu_len = 0;
|
|
||||||
BACNET_ADDRESS src = { 0 };
|
|
||||||
unsigned timeout = 1;
|
|
||||||
|
|
||||||
// Init Bacnet objets dictionnary
|
|
||||||
Device_Init(NULL);
|
|
||||||
Device_Set_Object_Instance_Number(12);
|
|
||||||
|
|
||||||
setup();
|
|
||||||
|
|
||||||
uint32_t tickcount = xTaskGetTickCount();
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
vTaskDelay(
|
|
||||||
10 / portTICK_PERIOD_MS); // could be remove to speed the code
|
|
||||||
|
|
||||||
// do nothing if not connected to wifi
|
|
||||||
xEventGroupWaitBits(
|
|
||||||
wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
|
|
||||||
{
|
|
||||||
uint32_t newtick = xTaskGetTickCount();
|
|
||||||
|
|
||||||
// one second elapse at least (maybe much more if Wifi was
|
|
||||||
// deconnected for a long)
|
|
||||||
if ((newtick < tickcount) ||
|
|
||||||
((newtick - tickcount) >= configTICK_RATE_HZ)) {
|
|
||||||
tickcount = newtick;
|
|
||||||
dcc_timer_seconds(1);
|
|
||||||
bvlc_maintenance_timer(1);
|
|
||||||
handler_cov_timer_seconds(1);
|
|
||||||
tsm_timer_milliseconds(1000);
|
|
||||||
|
|
||||||
// Read analog values from internal sensors
|
|
||||||
Analog_Input_Present_Value_Set(0, temprature_sens_read());
|
|
||||||
Analog_Input_Present_Value_Set(1, hall_sens_read());
|
|
||||||
}
|
|
||||||
|
|
||||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
|
||||||
if (pdu_len) {
|
|
||||||
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
|
||||||
|
|
||||||
if (Binary_Output_Present_Value(0) == BINARY_ACTIVE)
|
|
||||||
gpio_set_level(BACNET_LED, 1);
|
|
||||||
else
|
|
||||||
gpio_set_level(BACNET_LED, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
handler_cov_task();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Entry point */
|
|
||||||
void app_main()
|
|
||||||
{
|
|
||||||
// Cannot run BACnet code here, the default stack size is to small : 4096
|
|
||||||
// byte
|
|
||||||
xTaskCreate(BACnetTask, /* Function to implement the task */
|
|
||||||
"BACnetTask", /* Name of the task */
|
|
||||||
10000, /* Stack size in words */
|
|
||||||
NULL, /* Task input parameter */
|
|
||||||
20, /* Priority of the task */
|
|
||||||
NULL); /* Task handle. */
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet MS/TP application entry point for M5StamPLC
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <M5StamPLC.h>
|
||||||
|
|
||||||
|
#ifndef PLC_INPUT_ACTIVE_LOW
|
||||||
|
#define PLC_INPUT_ACTIVE_LOW 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "bacnet_app.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the MS/TP application and PLC hardware
|
||||||
|
*/
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(100);
|
||||||
|
Serial.println("[BOOT] Mode: BACnet MS/TP");
|
||||||
|
Serial.println("[BOOT] Init M5StamPLC...");
|
||||||
|
M5StamPLC.begin();
|
||||||
|
Serial.println("[BOOT] Init BACnet app...");
|
||||||
|
bacnet_app_init();
|
||||||
|
Serial.println("[BOOT] BACnet MS/TP ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run the main MS/TP application loop
|
||||||
|
*/
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
M5StamPLC.update();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < bacnet_app_input_count(); i++) {
|
||||||
|
bool state = M5StamPLC.readPlcInput(i);
|
||||||
|
#if PLC_INPUT_ACTIVE_LOW
|
||||||
|
state = !state;
|
||||||
|
#endif
|
||||||
|
bacnet_app_input_set(i, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < bacnet_app_relay_count(); i++) {
|
||||||
|
M5StamPLC.writePlcRelay(i, bacnet_app_relay_get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
bacnet_app_temperature_set(M5StamPLC.getTemp());
|
||||||
|
bacnet_app_free_heap_kb_set((float)ESP.getFreeHeap() / 1024.0f);
|
||||||
|
|
||||||
|
bacnet_app_tick();
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet/IP application entry point for ESP32 PlatformIO environments
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <cstring>
|
||||||
|
#if !defined(USE_M5STAMPLC_IO) || (USE_M5STAMPLC_IO)
|
||||||
|
#include <M5StamPLC.h>
|
||||||
|
#endif
|
||||||
|
#include <WiFi.h>
|
||||||
|
#if defined(USE_ETH_INTERFACE) && (USE_ETH_INTERFACE)
|
||||||
|
#include <ETH.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PLC_INPUT_ACTIVE_LOW
|
||||||
|
#define PLC_INPUT_ACTIVE_LOW 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_M5STAMPLC_IO
|
||||||
|
#define USE_M5STAMPLC_IO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_ETH_INTERFACE
|
||||||
|
#define USE_ETH_INTERFACE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIFI_SSID
|
||||||
|
#define WIFI_SSID ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIFI_PASS
|
||||||
|
#define WIFI_PASS ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BACNET_IP_PORT
|
||||||
|
#define BACNET_IP_PORT 47808
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "bacnet_app.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bring up the selected WiFi or Ethernet interface
|
||||||
|
*/
|
||||||
|
static void wifi_connect(void)
|
||||||
|
{
|
||||||
|
#if USE_ETH_INTERFACE
|
||||||
|
Serial.println("[ETH] Starting Ethernet...");
|
||||||
|
if (!ETH.begin()) {
|
||||||
|
Serial.println("[ETH] ETH.begin() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long start = millis();
|
||||||
|
while (!ETH.linkUp() && ((millis() - start) < 15000UL)) {
|
||||||
|
delay(200);
|
||||||
|
Serial.print('.');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
if (ETH.linkUp()) {
|
||||||
|
Serial.printf(
|
||||||
|
"[ETH] Link up. IP: %s\n", ETH.localIP().toString().c_str());
|
||||||
|
} else {
|
||||||
|
Serial.println("[ETH] Link timeout");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (strlen(WIFI_SSID) == 0) {
|
||||||
|
Serial.println("[WIFI] SSID not configured; skipping WiFi connect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("[WIFI] Connecting to SSID: %s\n", WIFI_SSID);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||||
|
|
||||||
|
unsigned long start = millis();
|
||||||
|
while ((WiFi.status() != WL_CONNECTED) && ((millis() - start) < 15000UL)) {
|
||||||
|
delay(200);
|
||||||
|
Serial.print('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Serial.printf(
|
||||||
|
"[WIFI] Connected. IP: %s\n", WiFi.localIP().toString().c_str());
|
||||||
|
} else {
|
||||||
|
Serial.printf(
|
||||||
|
"[WIFI] Connect timeout. status=%d\n", (int)WiFi.status());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet/IP application and selected board profile
|
||||||
|
*/
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(100);
|
||||||
|
Serial.println("[BOOT] Mode: BACnet/IP");
|
||||||
|
Serial.printf("[BOOT] BACnet UDP Port: %u\n", (unsigned)BACNET_IP_PORT);
|
||||||
|
#if USE_ETH_INTERFACE
|
||||||
|
Serial.println("[BOOT] Network: Ethernet");
|
||||||
|
#else
|
||||||
|
Serial.println("[BOOT] Network: WiFi");
|
||||||
|
#endif
|
||||||
|
#if USE_M5STAMPLC_IO
|
||||||
|
Serial.println("[BOOT] Board profile: M5StamPLC I/O enabled");
|
||||||
|
M5StamPLC.begin();
|
||||||
|
#else
|
||||||
|
Serial.println("[BOOT] Board profile: Generic/ESP32-PoE (M5 I/O disabled)");
|
||||||
|
#endif
|
||||||
|
wifi_connect();
|
||||||
|
Serial.println("[BOOT] Init BACnet app...");
|
||||||
|
bacnet_app_init();
|
||||||
|
Serial.println("[BOOT] BACnet/IP ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run the main BACnet/IP application loop
|
||||||
|
*/
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
#if USE_M5STAMPLC_IO
|
||||||
|
M5StamPLC.update();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < bacnet_app_input_count(); i++) {
|
||||||
|
bool state = M5StamPLC.readPlcInput(i);
|
||||||
|
#if PLC_INPUT_ACTIVE_LOW
|
||||||
|
state = !state;
|
||||||
|
#endif
|
||||||
|
bacnet_app_input_set(i, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < bacnet_app_relay_count(); i++) {
|
||||||
|
M5StamPLC.writePlcRelay(i, bacnet_app_relay_get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
bacnet_app_temperature_set(M5StamPLC.getTemp());
|
||||||
|
bacnet_app_free_heap_kb_set((float)ESP.getFreeHeap() / 1024.0f);
|
||||||
|
#else
|
||||||
|
bacnet_app_temperature_set(0.0f);
|
||||||
|
bacnet_app_free_heap_kb_set((float)ESP.getFreeHeap() / 1024.0f);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bacnet_app_tick();
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet router entry point bridging BACnet/IP and BACnet MS/TP
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <M5StamPLC.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "bacnet_app.h"
|
||||||
|
#include "bip.h"
|
||||||
|
#include "dlenv.h"
|
||||||
|
#include "bacnet/bacdcode.h"
|
||||||
|
#include "bacnet/npdu.h"
|
||||||
|
#include "bacnet/apdu.h"
|
||||||
|
#include "bacnet/datalink/dlmstp.h"
|
||||||
|
#include "bacnet/basic/npdu/h_npdu.h"
|
||||||
|
#include "bacnet/basic/tsm/tsm.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef WIFI_SSID
|
||||||
|
#define WIFI_SSID ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIFI_PASS
|
||||||
|
#define WIFI_PASS ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BACNET_IP_PORT
|
||||||
|
#define BACNET_IP_PORT 47808
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ROUTER_BIP_NET
|
||||||
|
#define ROUTER_BIP_NET 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ROUTER_MSTP_NET
|
||||||
|
#define ROUTER_MSTP_NET 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ROUTER_MSTP_MAC
|
||||||
|
#define ROUTER_MSTP_MAC 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PLC_INPUT_ACTIVE_LOW
|
||||||
|
#define PLC_INPUT_ACTIVE_LOW 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef GATEWAY_MAX
|
||||||
|
#define GATEWAY_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint8_t BipRxBuffer[BIP_MPDU_MAX];
|
||||||
|
static uint8_t MstpRxBuffer[DLMSTP_MPDU_MAX];
|
||||||
|
static uint8_t TxBuffer[GATEWAY_MAX(BIP_MPDU_MAX, DLMSTP_MPDU_MAX)];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connect the BACnet/IP side of the router to WiFi
|
||||||
|
*/
|
||||||
|
static void wifi_connect(void)
|
||||||
|
{
|
||||||
|
if (strlen(WIFI_SSID) == 0) {
|
||||||
|
Serial.println("[WIFI] SSID not configured; skipping WiFi connect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("[WIFI] Connecting to SSID: %s\n", WIFI_SSID);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||||
|
|
||||||
|
unsigned long start = millis();
|
||||||
|
while ((WiFi.status() != WL_CONNECTED) && ((millis() - start) < 15000UL)) {
|
||||||
|
delay(200);
|
||||||
|
Serial.print('.');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Serial.printf(
|
||||||
|
"[WIFI] Connected. IP: %s\n", WiFi.localIP().toString().c_str());
|
||||||
|
} else {
|
||||||
|
Serial.printf(
|
||||||
|
"[WIFI] Connect timeout. status=%d\n", (int)WiFi.status());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a BACnet router network message on the MS/TP segment
|
||||||
|
* @param type network layer message type
|
||||||
|
* @param dnet destination network number, or negative when omitted
|
||||||
|
* @return number of bytes sent, or a negative value on error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
send_router_message_on_mstp(BACNET_NETWORK_MESSAGE_TYPE type, int dnet)
|
||||||
|
{
|
||||||
|
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||||
|
BACNET_ADDRESS dest = { 0 };
|
||||||
|
int pdu_len = 0;
|
||||||
|
|
||||||
|
dest.mac_len = 0;
|
||||||
|
dest.net = BACNET_BROADCAST_NETWORK;
|
||||||
|
dest.len = 0;
|
||||||
|
|
||||||
|
npdu_encode_npdu_network(&npdu_data, type, false, MESSAGE_PRIORITY_NORMAL);
|
||||||
|
pdu_len = npdu_encode_pdu(TxBuffer, &dest, NULL, &npdu_data);
|
||||||
|
if (dnet >= 0) {
|
||||||
|
pdu_len += encode_unsigned16(&TxBuffer[pdu_len], (uint16_t)dnet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dlmstp_send_pdu(&dest, &npdu_data, TxBuffer, (unsigned)pdu_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a BACnet router network message on the BACnet/IP segment
|
||||||
|
* @param type network layer message type
|
||||||
|
* @param dnet destination network number, or negative when omitted
|
||||||
|
* @return number of bytes sent, or a negative value on error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
send_router_message_on_bip(BACNET_NETWORK_MESSAGE_TYPE type, int dnet)
|
||||||
|
{
|
||||||
|
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||||
|
BACNET_ADDRESS dest = { 0 };
|
||||||
|
int pdu_len = 0;
|
||||||
|
|
||||||
|
bip_get_broadcast_address(&dest);
|
||||||
|
|
||||||
|
npdu_encode_npdu_network(&npdu_data, type, false, MESSAGE_PRIORITY_NORMAL);
|
||||||
|
pdu_len = npdu_encode_pdu(TxBuffer, &dest, NULL, &npdu_data);
|
||||||
|
if (dnet >= 0) {
|
||||||
|
pdu_len += encode_unsigned16(&TxBuffer[pdu_len], (uint16_t)dnet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bip_send_pdu(&dest, &npdu_data, TxBuffer, (unsigned)pdu_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether a received NPDU should be processed by the local device
|
||||||
|
* @param dest decoded destination address
|
||||||
|
* @return true if the packet targets the local device or broadcast
|
||||||
|
*/
|
||||||
|
static bool should_process_locally(const BACNET_ADDRESS *dest)
|
||||||
|
{
|
||||||
|
return (dest->net == 0U) || (dest->net == BACNET_BROADCAST_NETWORK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Route an NPDU between BACnet/IP and MS/TP segments
|
||||||
|
* @param from_bip true when the frame arrived from BACnet/IP
|
||||||
|
* @param src decoded source address
|
||||||
|
* @param pdu NPDU buffer to route
|
||||||
|
* @param pdu_len NPDU length in bytes
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
forward_pdu(bool from_bip, BACNET_ADDRESS *src, uint8_t *pdu, uint16_t pdu_len)
|
||||||
|
{
|
||||||
|
BACNET_ADDRESS dest = { 0 };
|
||||||
|
BACNET_ADDRESS routed_src = { 0 };
|
||||||
|
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||||
|
int apdu_offset = bacnet_npdu_decode(pdu, pdu_len, &dest, src, &npdu_data);
|
||||||
|
if (apdu_offset <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t src_net = from_bip ? ROUTER_BIP_NET : ROUTER_MSTP_NET;
|
||||||
|
const uint16_t dst_net = from_bip ? ROUTER_MSTP_NET : ROUTER_BIP_NET;
|
||||||
|
|
||||||
|
if (npdu_data.network_layer_message) {
|
||||||
|
if (npdu_data.network_message_type ==
|
||||||
|
NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK) {
|
||||||
|
uint16_t requested = 0;
|
||||||
|
bool has_dnet = (pdu_len - apdu_offset) >= 2;
|
||||||
|
if (has_dnet) {
|
||||||
|
(void)decode_unsigned16(&pdu[apdu_offset], &requested);
|
||||||
|
}
|
||||||
|
if (!has_dnet || (requested == dst_net)) {
|
||||||
|
if (from_bip) {
|
||||||
|
send_router_message_on_bip(
|
||||||
|
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, dst_net);
|
||||||
|
} else {
|
||||||
|
send_router_message_on_mstp(
|
||||||
|
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, dst_net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dest.net != 0U) && (dest.net != BACNET_BROADCAST_NETWORK) &&
|
||||||
|
(dest.net != dst_net)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (npdu_data.hop_count > 0) {
|
||||||
|
npdu_data.hop_count--;
|
||||||
|
if (npdu_data.hop_count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src->net != 0U) {
|
||||||
|
routed_src = *src;
|
||||||
|
} else {
|
||||||
|
memset(&routed_src, 0, sizeof(routed_src));
|
||||||
|
routed_src.net = src_net;
|
||||||
|
routed_src.len = src->mac_len;
|
||||||
|
for (uint8_t i = 0; i < src->mac_len && i < MAX_MAC_LEN; i++) {
|
||||||
|
routed_src.adr[i] = src->mac[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BACNET_ADDRESS out_dest = { 0 };
|
||||||
|
if (dest.net == BACNET_BROADCAST_NETWORK ||
|
||||||
|
((dest.net == dst_net) && (dest.len == 0))) {
|
||||||
|
out_dest.mac_len = 0;
|
||||||
|
out_dest.net = BACNET_BROADCAST_NETWORK;
|
||||||
|
out_dest.len = 0;
|
||||||
|
} else if ((dest.net == dst_net) && (dest.len > 0)) {
|
||||||
|
out_dest.mac_len = dest.len;
|
||||||
|
for (uint8_t i = 0; i < dest.len && i < MAX_MAC_LEN; i++) {
|
||||||
|
out_dest.mac[i] = dest.adr[i];
|
||||||
|
}
|
||||||
|
out_dest.net = 0;
|
||||||
|
out_dest.len = 0;
|
||||||
|
} else {
|
||||||
|
out_dest = dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
int out_len = npdu_encode_pdu(TxBuffer, &out_dest, &routed_src, &npdu_data);
|
||||||
|
if (out_len <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t apdu_len = (uint16_t)(pdu_len - apdu_offset);
|
||||||
|
if ((out_len + (int)apdu_len) > (int)sizeof(TxBuffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(&TxBuffer[out_len], &pdu[apdu_offset], apdu_len);
|
||||||
|
out_len += apdu_len;
|
||||||
|
|
||||||
|
if (from_bip) {
|
||||||
|
(void)dlmstp_send_pdu(
|
||||||
|
&out_dest, &npdu_data, TxBuffer, (unsigned)out_len);
|
||||||
|
} else {
|
||||||
|
(void)bip_send_pdu(&out_dest, &npdu_data, TxBuffer, (unsigned)out_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the BACnet router and local device services
|
||||||
|
*/
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(100);
|
||||||
|
Serial.println("[BOOT] Mode: BACnet Gateway (Router + Local Device)");
|
||||||
|
Serial.printf(
|
||||||
|
"[GATEWAY] BIP NET=%u MSTP NET=%u MSTP MAC=%u\n",
|
||||||
|
(unsigned)ROUTER_BIP_NET, (unsigned)ROUTER_MSTP_NET,
|
||||||
|
(unsigned)ROUTER_MSTP_MAC);
|
||||||
|
|
||||||
|
M5StamPLC.begin();
|
||||||
|
wifi_connect();
|
||||||
|
|
||||||
|
/* Local device + objects run on B/IP side (same logic as existing M5 app).
|
||||||
|
*/
|
||||||
|
bacnet_app_init();
|
||||||
|
|
||||||
|
if (!m5_dlenv_init((uint8_t)ROUTER_MSTP_MAC)) {
|
||||||
|
Serial.println("[GATEWAY] ERROR: MSTP init failed");
|
||||||
|
for (;;) {
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)send_router_message_on_bip(
|
||||||
|
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, ROUTER_MSTP_NET);
|
||||||
|
(void)send_router_message_on_mstp(
|
||||||
|
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, ROUTER_BIP_NET);
|
||||||
|
|
||||||
|
Serial.println("[GATEWAY] Ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run the BACnet router main loop
|
||||||
|
*/
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
BACNET_ADDRESS dest = { 0 };
|
||||||
|
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||||
|
uint16_t pdu_len = 0;
|
||||||
|
|
||||||
|
M5StamPLC.update();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < bacnet_app_input_count(); i++) {
|
||||||
|
bool state = M5StamPLC.readPlcInput(i);
|
||||||
|
#if PLC_INPUT_ACTIVE_LOW
|
||||||
|
state = !state;
|
||||||
|
#endif
|
||||||
|
bacnet_app_input_set(i, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < bacnet_app_relay_count(); i++) {
|
||||||
|
M5StamPLC.writePlcRelay(i, bacnet_app_relay_get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
bacnet_app_temperature_set(M5StamPLC.getTemp());
|
||||||
|
bacnet_app_free_heap_kb_set((float)ESP.getFreeHeap() / 1024.0f);
|
||||||
|
|
||||||
|
pdu_len = bip_receive(&src, BipRxBuffer, sizeof(BipRxBuffer), 0);
|
||||||
|
if (pdu_len > 0) {
|
||||||
|
int apdu_offset =
|
||||||
|
bacnet_npdu_decode(BipRxBuffer, pdu_len, &dest, &src, &npdu_data);
|
||||||
|
if ((apdu_offset > 0) && should_process_locally(&dest)) {
|
||||||
|
npdu_handler(&src, BipRxBuffer, pdu_len);
|
||||||
|
}
|
||||||
|
forward_pdu(true, &src, BipRxBuffer, pdu_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdu_len = dlmstp_receive(&src, MstpRxBuffer, sizeof(MstpRxBuffer), 0);
|
||||||
|
if (pdu_len > 0) {
|
||||||
|
forward_pdu(false, &src, MstpRxBuffer, pdu_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
tsm_timer_milliseconds(1);
|
||||||
|
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet millisecond timer hooks for the PlatformIO ESP32 port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mstimer_init.h"
|
||||||
|
|
||||||
|
#include "esp_timer.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the system timer services used by BACnet
|
||||||
|
*/
|
||||||
|
void systimer_init(void)
|
||||||
|
{
|
||||||
|
/* esp_timer is available after runtime startup on ESP32. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current millisecond tick count
|
||||||
|
* @return milliseconds since startup
|
||||||
|
*/
|
||||||
|
unsigned long mstimer_now(void)
|
||||||
|
{
|
||||||
|
return (unsigned long)(esp_timer_get_time() / 1000ULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BACnet millisecond timer hook declarations for the PlatformIO ESP32
|
||||||
|
* port
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M5STAMPLC_MSTIMER_INIT_H
|
||||||
|
#define M5STAMPLC_MSTIMER_INIT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the system timer services used by BACnet
|
||||||
|
*/
|
||||||
|
void systimer_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current millisecond tick count
|
||||||
|
* @return milliseconds since startup
|
||||||
|
*/
|
||||||
|
unsigned long mstimer_now(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief RS485 UART driver used by BACnet MS/TP on ESP32
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rs485.h"
|
||||||
|
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <driver/uart.h>
|
||||||
|
|
||||||
|
/* UART_SCLK_DEFAULT was introduced in ESP-IDF v5.0.
|
||||||
|
Fall back to UART_SCLK_APB for older IDF/Arduino-ESP32 toolchains. */
|
||||||
|
#ifndef UART_SCLK_DEFAULT
|
||||||
|
#define UART_SCLK_DEFAULT UART_SCLK_APB
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "bacnet/basic/sys/mstimer.h"
|
||||||
|
|
||||||
|
static bool Rs485_RTS_Enabled;
|
||||||
|
static uint32_t Rs485_Baud_Rate = RS485_BAUD_RATE;
|
||||||
|
static volatile uint32_t Rs485_Bytes_Tx;
|
||||||
|
static volatile uint32_t Rs485_Bytes_Rx;
|
||||||
|
static struct mstimer Silence_Timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the RS485 UART peripheral for BACnet MS/TP
|
||||||
|
*/
|
||||||
|
void rs485_init(void)
|
||||||
|
{
|
||||||
|
uart_config_t cfg = { .baud_rate = (int)Rs485_Baud_Rate,
|
||||||
|
.data_bits = UART_DATA_8_BITS,
|
||||||
|
.parity = UART_PARITY_DISABLE,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||||
|
.source_clk = UART_SCLK_DEFAULT };
|
||||||
|
|
||||||
|
(void)uart_driver_delete(RS485_UART_NUM);
|
||||||
|
(void)uart_param_config(RS485_UART_NUM, &cfg);
|
||||||
|
(void)uart_set_pin(
|
||||||
|
RS485_UART_NUM, RS485_TX_PIN, RS485_RX_PIN, RS485_DIR_PIN,
|
||||||
|
UART_PIN_NO_CHANGE);
|
||||||
|
(void)uart_driver_install(RS485_UART_NUM, 1024, 0, 0, NULL, 0);
|
||||||
|
(void)uart_set_mode(RS485_UART_NUM, UART_MODE_RS485_HALF_DUPLEX);
|
||||||
|
|
||||||
|
Rs485_RTS_Enabled = false;
|
||||||
|
Rs485_Bytes_Tx = 0;
|
||||||
|
Rs485_Bytes_Rx = 0;
|
||||||
|
rs485_silence_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the cached RS485 transmit-enable state
|
||||||
|
* @param enable true when transmit is active
|
||||||
|
*/
|
||||||
|
void rs485_rts_enable(bool enable)
|
||||||
|
{
|
||||||
|
/* Hardware RS485 half-duplex mode drives DIR. Keep state for stack queries.
|
||||||
|
*/
|
||||||
|
Rs485_RTS_Enabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the RS485 transmit-enable state is active
|
||||||
|
* @return true if transmit is active
|
||||||
|
*/
|
||||||
|
bool rs485_rts_enabled(void)
|
||||||
|
{
|
||||||
|
return Rs485_RTS_Enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read one byte from the RS485 receive FIFO when available
|
||||||
|
* @param data_register destination for the received byte, or NULL to probe only
|
||||||
|
* @return true if at least one byte is available
|
||||||
|
*/
|
||||||
|
bool rs485_byte_available(uint8_t *data_register)
|
||||||
|
{
|
||||||
|
size_t buffered = 0;
|
||||||
|
(void)uart_get_buffered_data_len(RS485_UART_NUM, &buffered);
|
||||||
|
if (buffered == 0U) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!data_register) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uart_read_bytes(RS485_UART_NUM, data_register, 1, 0) == 1) {
|
||||||
|
Rs485_Bytes_Rx++;
|
||||||
|
rs485_silence_reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Report whether an RS485 receive error is pending
|
||||||
|
* @return true if a receive error is detected
|
||||||
|
*/
|
||||||
|
bool rs485_receive_error(void)
|
||||||
|
{
|
||||||
|
/* In Arduino-ESP32 builds, low-level UART error queues are not always
|
||||||
|
* exposed. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a block of RS485 bytes
|
||||||
|
* @param buffer transmit buffer
|
||||||
|
* @param nbytes number of bytes to send
|
||||||
|
*/
|
||||||
|
void rs485_bytes_send(const uint8_t *buffer, uint16_t nbytes)
|
||||||
|
{
|
||||||
|
if (!buffer || (nbytes == 0U)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs485_rts_enable(true);
|
||||||
|
(void)uart_write_bytes(RS485_UART_NUM, (const char *)buffer, nbytes);
|
||||||
|
(void)uart_wait_tx_done(RS485_UART_NUM, pdMS_TO_TICKS(20));
|
||||||
|
Rs485_Bytes_Tx += nbytes;
|
||||||
|
rs485_rts_enable(false);
|
||||||
|
rs485_silence_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the configured RS485 baud rate
|
||||||
|
* @return baud rate in bits per second
|
||||||
|
*/
|
||||||
|
uint32_t rs485_baud_rate(void)
|
||||||
|
{
|
||||||
|
return Rs485_Baud_Rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the RS485 baud rate
|
||||||
|
* @param baud baud rate in bits per second
|
||||||
|
* @return true if the baud rate was applied
|
||||||
|
*/
|
||||||
|
bool rs485_baud_rate_set(uint32_t baud)
|
||||||
|
{
|
||||||
|
if (baud == 0U) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rs485_Baud_Rate = baud;
|
||||||
|
return (uart_set_baudrate(RS485_UART_NUM, (int)Rs485_Baud_Rate) == ESP_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the elapsed silent time on the RS485 bus
|
||||||
|
* @return milliseconds since the last bus activity
|
||||||
|
*/
|
||||||
|
uint32_t rs485_silence_milliseconds(void)
|
||||||
|
{
|
||||||
|
return mstimer_elapsed(&Silence_Timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the RS485 silence timer
|
||||||
|
*/
|
||||||
|
void rs485_silence_reset(void)
|
||||||
|
{
|
||||||
|
mstimer_set(&Silence_Timer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of transmitted RS485 bytes
|
||||||
|
* @return transmitted byte count
|
||||||
|
*/
|
||||||
|
uint32_t rs485_bytes_transmitted(void)
|
||||||
|
{
|
||||||
|
return Rs485_Bytes_Tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of received RS485 bytes
|
||||||
|
* @return received byte count
|
||||||
|
*/
|
||||||
|
uint32_t rs485_bytes_received(void)
|
||||||
|
{
|
||||||
|
return Rs485_Bytes_Rx;
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief RS485 UART driver declarations used by BACnet MS/TP on ESP32
|
||||||
|
* @author Kato Gangstad
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M5STAMPLC_RS485_H
|
||||||
|
#define M5STAMPLC_RS485_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RS485_UART_NUM UART_NUM_1
|
||||||
|
#define RS485_BAUD_RATE 38400
|
||||||
|
#define RS485_TX_PIN 0
|
||||||
|
#define RS485_RX_PIN 39
|
||||||
|
#define RS485_DIR_PIN 46
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the RS485 UART peripheral
|
||||||
|
*/
|
||||||
|
void rs485_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the cached RS485 transmit-enable state
|
||||||
|
* @param enable true when transmit is active
|
||||||
|
*/
|
||||||
|
void rs485_rts_enable(bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the RS485 transmit-enable state is active
|
||||||
|
* @return true if transmit is active
|
||||||
|
*/
|
||||||
|
bool rs485_rts_enabled(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read one byte from the RS485 receive FIFO when available
|
||||||
|
* @param data_register destination for the received byte, or NULL to probe only
|
||||||
|
* @return true if a byte is available
|
||||||
|
*/
|
||||||
|
bool rs485_byte_available(uint8_t *data_register);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Report whether an RS485 receive error is pending
|
||||||
|
* @return true if a receive error is detected
|
||||||
|
*/
|
||||||
|
bool rs485_receive_error(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a block of RS485 bytes
|
||||||
|
* @param buffer transmit buffer
|
||||||
|
* @param nbytes number of bytes to send
|
||||||
|
*/
|
||||||
|
void rs485_bytes_send(const uint8_t *buffer, uint16_t nbytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the configured RS485 baud rate
|
||||||
|
* @return baud rate in bits per second
|
||||||
|
*/
|
||||||
|
uint32_t rs485_baud_rate(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the RS485 baud rate
|
||||||
|
* @param baud baud rate in bits per second
|
||||||
|
* @return true if the baud rate was applied
|
||||||
|
*/
|
||||||
|
bool rs485_baud_rate_set(uint32_t baud);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the elapsed silent time on the RS485 bus
|
||||||
|
* @return milliseconds since the last bus activity
|
||||||
|
*/
|
||||||
|
uint32_t rs485_silence_milliseconds(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the RS485 silence timer
|
||||||
|
*/
|
||||||
|
void rs485_silence_reset(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of transmitted RS485 bytes
|
||||||
|
* @return transmitted byte count
|
||||||
|
*/
|
||||||
|
uint32_t rs485_bytes_transmitted(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of received RS485 bytes
|
||||||
|
* @return received byte count
|
||||||
|
*/
|
||||||
|
uint32_t rs485_bytes_received(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Automatically generated file; DO NOT EDIT.
|
|
||||||
* Espressif IoT Development Framework Configuration
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define CONFIG_GATTC_ENABLE 1
|
|
||||||
#define CONFIG_ESP32_PHY_MAX_TX_POWER 20
|
|
||||||
#define CONFIG_PHY_ENABLED 1
|
|
||||||
#define CONFIG_TRACEMEM_RESERVE_DRAM 0x0
|
|
||||||
#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16
|
|
||||||
#define CONFIG_BLE_SMP_ENABLE 1
|
|
||||||
#define CONFIG_IPC_TASK_STACK_SIZE 1024
|
|
||||||
#define CONFIG_ESPTOOLPY_FLASHFREQ "40m"
|
|
||||||
#define CONFIG_NEWLIB_STDOUT_ADDCR 1
|
|
||||||
#define CONFIG_TASK_WDT_CHECK_IDLE_TASK 1
|
|
||||||
#define CONFIG_ESPTOOLPY_FLASHSIZE "2MB"
|
|
||||||
#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1
|
|
||||||
#define CONFIG_ETHERNET 1
|
|
||||||
#define CONFIG_INT_WDT 1
|
|
||||||
#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1
|
|
||||||
#define CONFIG_LOG_BOOTLOADER_LEVEL_INFO 1
|
|
||||||
#define CONFIG_ESPTOOLPY_FLASHSIZE_2MB 1
|
|
||||||
#define CONFIG_AWS_IOT_MQTT_PORT 8883
|
|
||||||
#define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 1
|
|
||||||
#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10
|
|
||||||
#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1
|
|
||||||
#define CONFIG_BT_RESERVE_DRAM 0x10000
|
|
||||||
#define CONFIG_ESP32_PANIC_PRINT_REBOOT 1
|
|
||||||
#define CONFIG_CONSOLE_UART_BAUDRATE 115200
|
|
||||||
#define CONFIG_LWIP_MAX_SOCKETS 10
|
|
||||||
#define CONFIG_EMAC_TASK_PRIORITY 20
|
|
||||||
#define CONFIG_TIMER_TASK_STACK_DEPTH 2048
|
|
||||||
#define CONFIG_FATFS_CODEPAGE 1
|
|
||||||
#define CONFIG_ESP32_DEFAULT_CPU_FREQ_160 1
|
|
||||||
#define CONFIG_ULP_COPROC_RESERVE_MEM 0
|
|
||||||
#define CONFIG_ESPTOOLPY_BAUD 115200
|
|
||||||
#define CONFIG_INT_WDT_CHECK_CPU1 1
|
|
||||||
#define CONFIG_FLASHMODE_DIO 1
|
|
||||||
#define CONFIG_ESPTOOLPY_AFTER_RESET 1
|
|
||||||
#define CONFIG_TOOLPREFIX "xtensa-esp32-elf-"
|
|
||||||
#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1024
|
|
||||||
#define CONFIG_ESP32_WIFI_AMPDU_ENABLED 1
|
|
||||||
#define CONFIG_CONSOLE_UART_NUM 0
|
|
||||||
#define CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC 1
|
|
||||||
#define CONFIG_ESPTOOLPY_BAUD_115200B 1
|
|
||||||
#define CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX 0
|
|
||||||
#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1
|
|
||||||
#define CONFIG_CONSOLE_UART_DEFAULT 1
|
|
||||||
#define CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384
|
|
||||||
#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4
|
|
||||||
#define CONFIG_ESPTOOLPY_FLASHSIZE_DETECT 1
|
|
||||||
#define CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE 1
|
|
||||||
#define CONFIG_BTDM_CONTROLLER_RUN_CPU 0
|
|
||||||
#define CONFIG_TCPIP_TASK_STACK_SIZE 2560
|
|
||||||
#define CONFIG_TASK_WDT 1
|
|
||||||
#define CONFIG_MAIN_TASK_STACK_SIZE 4096
|
|
||||||
#define CONFIG_TASK_WDT_TIMEOUT_S 5
|
|
||||||
#define CONFIG_INT_WDT_TIMEOUT_MS 300
|
|
||||||
#define CONFIG_ESPTOOLPY_FLASHMODE "dio"
|
|
||||||
#define CONFIG_BTC_TASK_STACK_SIZE 3072
|
|
||||||
#define CONFIG_BLUEDROID_ENABLED 1
|
|
||||||
#define CONFIG_ESPTOOLPY_BEFORE "default_reset"
|
|
||||||
#define CONFIG_LOG_DEFAULT_LEVEL 3
|
|
||||||
#define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1
|
|
||||||
#define CONFIG_TIMER_QUEUE_LENGTH 10
|
|
||||||
#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 32
|
|
||||||
#define CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER 20
|
|
||||||
#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024
|
|
||||||
#define CONFIG_ESP32_WIFI_NVS_ENABLED 1
|
|
||||||
#define CONFIG_AWS_IOT_SDK 1
|
|
||||||
#define CONFIG_DMA_RX_BUF_NUM 10
|
|
||||||
#define CONFIG_TCP_SYNMAXRTX 6
|
|
||||||
#define CONFIG_PYTHON "python"
|
|
||||||
#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1
|
|
||||||
#define CONFIG_ESPTOOLPY_COMPRESSED 1
|
|
||||||
#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv"
|
|
||||||
#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1
|
|
||||||
#define CONFIG_PARTITION_TABLE_SINGLE_APP 1
|
|
||||||
#define CONFIG_WIFI_ENABLED 1
|
|
||||||
#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1
|
|
||||||
#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 4096
|
|
||||||
#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 2000
|
|
||||||
#define CONFIG_ESP32_APPTRACE_DEST_NONE 1
|
|
||||||
#define CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET 0x10000
|
|
||||||
#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32
|
|
||||||
#define CONFIG_FATFS_CODEPAGE_ASCII 1
|
|
||||||
#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 1
|
|
||||||
#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 160
|
|
||||||
#define CONFIG_FREERTOS_HZ 100
|
|
||||||
#define CONFIG_LOG_COLORS 1
|
|
||||||
#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1
|
|
||||||
#define CONFIG_FREERTOS_ASSERT_FAIL_ABORT 1
|
|
||||||
#define CONFIG_ESP32_XTAL_FREQ 0
|
|
||||||
#define CONFIG_MONITOR_BAUD_115200B 1
|
|
||||||
#define CONFIG_LOG_BOOTLOADER_LEVEL 3
|
|
||||||
#define CONFIG_SMP_ENABLE 1
|
|
||||||
#define CONFIG_ESPTOOLPY_BEFORE_RESET 1
|
|
||||||
#define CONFIG_ESPTOOLPY_BAUD_OTHER_VAL 115200
|
|
||||||
#define CONFIG_ESP32_XTAL_FREQ_AUTO 1
|
|
||||||
#define CONFIG_TCP_MAXRTX 12
|
|
||||||
#define CONFIG_ESPTOOLPY_AFTER "hard_reset"
|
|
||||||
#define CONFIG_DMA_TX_BUF_NUM 10
|
|
||||||
#define CONFIG_ESP32_DEBUG_OCDAWARE 1
|
|
||||||
#define CONFIG_TIMER_TASK_PRIORITY 1
|
|
||||||
#define CONFIG_BT_ENABLED 1
|
|
||||||
#define CONFIG_MONITOR_BAUD 115200
|
|
||||||
#define CONFIG_FREERTOS_CORETIMER_0 1
|
|
||||||
#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv"
|
|
||||||
#define CONFIG_MBEDTLS_HAVE_TIME 1
|
|
||||||
#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1
|
|
||||||
#define CONFIG_GATTS_ENABLE 1
|
|
||||||
#define CONFIG_FREERTOS_ISR_STACKSIZE 1536
|
|
||||||
#define CONFIG_OPENSSL_ASSERT_DO_NOTHING 1
|
|
||||||
#define CONFIG_AWS_IOT_MQTT_HOST ""
|
|
||||||
#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32
|
|
||||||
#define CONFIG_BT_ACL_CONNECTIONS 4
|
|
||||||
#define CONFIG_FATFS_MAX_LFN 255
|
|
||||||
#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1
|
|
||||||
#define CONFIG_APP_OFFSET 0x10000
|
|
||||||
#define CONFIG_MEMMAP_SMP 1
|
|
||||||
#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1
|
|
||||||
#define CONFIG_MONITOR_BAUD_OTHER_VAL 115200
|
|
||||||
#define CONFIG_ESPTOOLPY_PORT "/dev/ttyUSB0"
|
|
||||||
#define CONFIG_OPTIMIZATION_LEVEL_RELEASE 1
|
|
||||||
Reference in New Issue
Block a user