π Platform Testing Update
Weβre currently putting the Radxa Zero 3e through its paces as a potential platform of choice for this project!
Itβs looking promising, but we want to make sure it meets all our needsβso expect plenty of tinkering and testing before we make anything official.Interested in helping out with testing?
Iβd love some extra hands (and hardware!) on deck. If youβre curious or want to help, please reach out!
My name is Chris Favre, project manager and evangelist of all things HVAC. Over the years I've seen the industry change from pneumatic to DOS and text based, graphical programming, and web based solutions. As time has evolved, we have a great need for easy to use toolsets to keep our technicians and account managers up to date. Who still remembers Hayes commands, ATDT8675309?
This project began as a way to bridge a gap I kept seeing in the field, many HVAC controls technicians and account managers aren't deeply familiar with IT or systems integration. Iβve spent a lot of time trying to make this tool practical, accessible, and field-ready. My hope is that it can make networking and BACnet diagnostics easier for those of us who didnβt come up through the IT side of the house. If it helps you or your team get a clearer picture of whatβs going on in the network, then itβs doing its job.
A web-based dashboard for network scanning, BACnet device discovery (with deep scan), and network configuration.
Built with simplicity in mind, especially for those working in the trenches of HVAC controls and building automation. Itβs meant to run cleanly on a Raspberry Pi with minimal setup.
- Network Scan: Scan your local subnet for devices using ARP.
- BACnet Scan: Discover BACnet devices and automatically perform a deep scan (all objects/points and properties are collected in one scan).
- Adjustable BACnet UDP Port: Default is 47808; change per-scan in the UI or set a default via env var.
- Network Settings: Configure static/DHCP IP for
eth0. - CSV Export: Download scan results as CSV files, with consistent column order.
- Live Device/Network Info: See networks found and device count after each scan.
- Progress Feedback: "Please wait" message or spinner shown during BACnet scan.
- Easy Setup: No background threads or global state; uses a singleton BACnet app.
- Optional Wi-Fi Access Point: Turn your device into an open Wi-Fi AP for direct access.
- Local Hostname Access: Access the dashboard at
http://tttv1.localinstead of an IP address.
- Python 3.7+
- Linux (Raspberry Pi OS Bullseye recommended)
- Python packages:
- flask
- BAC0
- bacpypes
- mac-vendor-lookup (optional)
- netifaces (optional)
- System tools:
- arp-scan
- libcap2-bin (to grant capabilities to arp-scan)
- avahi-daemon (for .local hostname access)
- hostapd and dnsmasq (optional, for WiβFi AP)
# Get code
git clone https://github.com/yourrepo/TTTv1.0.2.git
cd TTTv1.0.2
# System packages
sudo apt-get update
sudo apt-get install -y python3-venv python3-pip arp-scan libcap2-bin avahi-daemon
# Allow arp-scan to run without sudo (raw sockets)
sudo setcap cap_net_raw,cap_net_admin+eip "$(which arp-scan)"
getcap "$(which arp-scan)" # expect: cap_net_admin,cap_net_raw+eip
# (Optional) Python venv
python3 -m venv .venv
. .venv/bin/activate
# Python deps
pip install -r requirements.txt# In project folder
export PORT=8080
# Optional defaults:
# export BACNET_UDP_PORT=47808 # Default BACnet port (change if your site uses a non-standard port)
# export TTT_RESULTS_DIR=/home/makeitworkok/TTTv1.0.2/results
python3 app.pyOpen http://127.0.0.1:8080 (or the device IP on port 8080).
If you must use port 80, run via systemd with CAP_NET_BIND_SERVICE (see below).
This project is configured to be accessible at http://tttv1.local on your network, so you don't need to remember the device's IP address.
How it works:
- The Raspberry Pi runs the
avahi-daemonservice, which advertises its hostname on the local network using mDNS (Bonjour/ZeroConf). - By default, the Pi's hostname is set to
tttv1. You can check or set it with:sudo hostnamectl set-hostname tttv1 sudo systemctl restart avahi-daemon
- Any computer or phone on the same network can access the dashboard at
http://tttv1.local.
Note:
- Most modern OSes (Windows 10+, macOS, Linux, iOS, Android) support
.localhostnames out of the box. - If you have trouble, make sure
avahi-daemonis running on the Pi and your client device supports mDNS.
- Go to Network Scan tab.
- Click Start Scan to scan your subnet.
- Results show IP, MAC, Hostname, Vendor.
- Download results as CSV.
- Go to BACnet Scan tab.
- Set the optional βBACnet UDP Portβ (default 47808). This value applies to the current scan only.
- Click Start Full Scan or Quick Scan.
- After scan, you see networks found, device count, and can download CSV.
Tip:
- To change the default port globally (for all sessions), set environment variable
BACNET_UDP_PORT(see systemd example below). - Ensure firewalls allow the selected UDP port.
- Go to Network Settings tab.
- Set static IP or switch to DHCP for
eth0. - Changes are applied to
/etc/dhcpcd.confand take effect after restarting thedhcpcdservice.
To make your device an open Wi-Fi AP (tttv1):
-
Install required packages:
sudo apt-get update sudo apt-get install hostapd dnsmasq sudo systemctl stop hostapd sudo systemctl stop dnsmasq
-
Configure
/etc/hostapd/hostapd.conf:interface=wlan0 driver=nl80211 ssid=tttv1 hw_mode=g channel=6 auth_algs=1 wmm_enabled=0 ignore_broadcast_ssid=0 # No WPA settings for open networkSet the config file location in
/etc/default/hostapd:DAEMON_CONF="/etc/hostapd/hostapd.conf" -
Configure
/etc/dnsmasq.conffor DHCP:interface=wlan0 dhcp-range=192.168.50.2,192.168.50.20,255.255.255.0,24h -
Set static IP for
wlan0in/etc/dhcpcd.conf:interface wlan0 static ip_address=192.168.50.1/24 nohook wpa_supplicant -
Enable IP forwarding and NAT:
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADETo make IP forwarding permanent, add to
/etc/sysctl.conf:net.ipv4.ip_forward=1 -
Restart services:
sudo systemctl restart dhcpcd sudo systemctl start hostapd sudo systemctl start dnsmasq
Your device will now broadcast an open Wi-Fi network named tttv1.
Connect to it and access the dashboard at http://192.168.50.1 or http://tttv1.local.
- ARP Scan not working: Make sure
arp-scanis installed and run as root. - BACnet Scan errors: Ensure only one BACnet app instance is running.
- Network config not applying: Check
/etc/dhcpcd.confand restartdhcpcd. - Wi-Fi AP not showing: Check hostapd and dnsmasq configs, and use correct interface.
- IP not changing: Make sure no other network manager is controlling
eth0and check for errors indhcpcdstatus. - Can't access
tttv1.local: Ensureavahi-daemonis running and your client supports mDNS. - ARP scan says βarp-scan needs privilegesβ:
- Ensure caps are set: getcap /usr/sbin/arp-scan β cap_net_admin,cap_net_raw+eip
- If you set NoNewPrivileges=true in the service, remove it (blocks file caps).
- Alternative: grant caps via service instead of file caps:
- AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN
- CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN
- Service not starting:
- sudo journalctl -u ttt.service -n 200 --no-pager
- Verify paths and user in the unit file match your system.
- Canβt reach the app:
- sudo ss -lntp | grep -E ':80|:8080'
- curl -I http://127.0.0.1:8080
- arp-scan not found:
- Install: sudo apt-get install -y arp-scan
- Ensure correct path: which arp-scan β /usr/sbin/arp-scan
- BACnet devices not discovered on expected port:
- Verify the deviceβs BACnet/IP port setting.
- Set the port in the BACnet Scan page or via env:
BACNET_UDP_PORT=<port>. - Ensure UDP is permitted on that port (switch/firewall).
TTTv1.0.2/
βββ app.py # Main Flask app and routes
βββ bac0_scan.py # BACnet scan logic (deep scan built-in, uses BAC0)
βββ requirements.txt # Python dependencies
βββ templates/ # HTML templates for Flask
β βββ index.html # Dashboard landing page
β βββ network.html # Network settings/configuration page
β βββ bacnet.html # BACnet scan and results page
β βββ scan.html # ARP network scan page
β βββ ... # (other HTML pages)
βββ results/ # CSV output files from scans
β βββ bacnet_scan_*.csv
β βββ arp_scan_*.csv
βββ static/ # Static files (CSS, JS, images, spinner, etc.)
βββ README.md # This documentation
βββ ... # Other supporting files (legacy code, helpers, etc.)
Pull requests welcome!
MIT