<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://community.element14.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Smart Security and Surveillance</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/</link><description>A community design challenge focused on smart security and surveillance projects, where members design, build, and document practical solutions to real-world monitoring and protection problems.  Participants share their process, hardware choices, and resul</description><dc:language>en-US</dc:language><generator>Telligent Community 12</generator><item><title>Forum Post: It all works!</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56974/it-all-works</link><pubDate>Thu, 21 May 2026 21:37:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:2904b7a2-dadb-4b47-8584-a959deeb8465</guid><dc:creator>Alistair</dc:creator><description>Good news. Yesterday I managed to get everything working and a case tidied. Today I did some more serious testing. In short the hard work is done and I juts need to get the final writeup done. Yay! Thank you to all who have helped or participated. It has ben great working with you. I hope you are as happy with your build as I am. As mine turned out mostly software based around stock hardware I suspect it is not going to be seen as a winner, but it has been enjoyable to create. I have a device that I will actually be using in real life, and perhaps some of my work will carry forward in other people&amp;#39;s projects and give more life to a useful little dev board. I will add that I have really enjoyed the challenge of solving all these problems. Some of the issues were so abstract I am surprised I figured it out, but that made it more rewarding when I found solution. If anyone has any questions that I might be able to help with the feel free to reach out.</description></item><item><title>Forum Post: NetSentinel – Update 3: Network Architecture, Firewall Configuration &amp; Raspberry Pi Core</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56969/netsentinel-update-3-network-architecture-firewall-configuration-raspberry-pi-core</link><pubDate>Wed, 20 May 2026 21:29:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:eaabae9b-69e8-47f8-82a9-66c151eedd26</guid><dc:creator>GustavoMorales</dc:creator><description>The network layer is what separates NetSentinel from a standalone gadget. The Raspberry Pi at the core of the system does not sit on a flat home network — it lives in a properly segmented DMZ, receives syslog from real enterprise firewall and routing equipment, and distributes processed alerts to the field and desktop nodes. This blog covers the full network architecture, the OSPF routing configuration, the firewall policies, and the Flask application running on the Raspberry Pi. Topology Overview The network was built on infrastructure originally designed for an MSc Cybersecurity lab at Universidad Galileo. Three devices form the routing and security backbone. The FortiGate 90D is the exterior firewall. It connects to the simulated internet upstream and provides the first security boundary. Its internal interface connects to the MikroTik RB3011 on a /30 transit link. The MikroTik RB3011 is the central routing node. It has three relevant interfaces: ether1 connecting upstream to the FG-90D, ether4 hosting the DMZ segment where the Raspberry Pi lives, and ether2 connecting downstream to the FG-40F. The FortiGate 40F is the interior firewall protecting the LAN where both MAX nodes reside. OSPF Configuration — Area 0 Dynamic routing runs OSPF Area 0 across all three devices. No static routes were used — every segment is reachable through OSPF redistribution. FortiGate 90D: config router ospf set router-id 10.20.30.1 set default-information-originate enable config area edit 0.0.0.0 next end config network edit 1 set prefix 192.168.10.0 255.255.255.252 next end config redistribute &amp;quot;connected&amp;quot; set status enable end end MikroTik RB3011: /routing ospf instance add name=default router-id=10.20.30.0 /routing ospf network add network=192.168.10.0/30 area=backbone add network=192.168.20.0/30 area=backbone add network=192.168.2.0/24 area=backbone FortiGate 40F: config router ospf set router-id 10.20.30.3 config area edit 0.0.0.0 next end config network edit 1 set prefix 192.168.20.0 255.255.255.252 next edit 2 set prefix 192.168.4.0 255.255.255.0 next end config redistribute &amp;quot;connected&amp;quot; set status enable end end Firewall Policies The policies follow least-privilege: only explicitly required traffic is permitted. The DMZ design principle holds — the Raspberry Pi can receive connections from all segments but cannot initiate connections toward the LAN. FortiGate 90D — Internet to DMZ (syslog and management): config firewall policy edit 1 set name &amp;quot;internet-to-dmz&amp;quot; set srcintf &amp;quot;wan1&amp;quot; set dstintf &amp;quot;internal10&amp;quot; set srcaddr &amp;quot;all&amp;quot; set dstaddr &amp;quot;RPi-DMZ&amp;quot; set action accept set service &amp;quot;HTTPS&amp;quot; &amp;quot;SSH&amp;quot; set schedule &amp;quot;always&amp;quot; set logtraffic all next edit 2 set name &amp;quot;dmz-to-internet&amp;quot; set srcintf &amp;quot;internal10&amp;quot; set dstintf &amp;quot;wan1&amp;quot; set srcaddr &amp;quot;DMZ-Net&amp;quot; set dstaddr &amp;quot;all&amp;quot; set action accept set nat enable set schedule &amp;quot;always&amp;quot; set logtraffic all next end FortiGate 40F — LAN to DMZ (MAX nodes reaching RPi): config firewall policy edit 1 set name &amp;quot;lan-to-dmz&amp;quot; set srcintf &amp;quot;lan&amp;quot; set dstintf &amp;quot;wan1&amp;quot; set srcaddr &amp;quot;LAN-Net&amp;quot; set dstaddr &amp;quot;RPi-DMZ&amp;quot; set action accept set service &amp;quot;HTTP&amp;quot; &amp;quot;UDP-514&amp;quot; set schedule &amp;quot;always&amp;quot; set logtraffic all next edit 2 set name &amp;quot;dmz-to-lan-block&amp;quot; set srcintf &amp;quot;wan1&amp;quot; set dstintf &amp;quot;lan&amp;quot; set srcaddr &amp;quot;DMZ-Net&amp;quot; set dstaddr &amp;quot;LAN-Net&amp;quot; set action deny set logtraffic all next end Syslog forwarding — FortiGate 90D and 40F: config log syslogd setting set status enable set server &amp;quot;192.168.2.200&amp;quot; set port 514 set facility local7 set format default end config log syslogd filter set severity information set forward-traffic enable set local-traffic enable set sniffer-traffic disable set anomaly enable end MikroTik RB3011 syslog /system logging action set remote address=192.168.2.200 remote-port=514 \ name=netsentinel src-address=0.0.0.0 target=remote /system logging add action=netsentinel topics=info add action=netsentinel topics=warning add action=netsentinel topics=error : Raspberry Pi — Flask Application The Flask app on the Raspberry Pi runs two services simultaneously: a UDP syslog listener on port 514 and an HTTP Flask server on port 5000. When a syslog message arrives from the FortiGates or MikroTik, it is parsed, cleaned, and forwarded as an alert to MAX Node 1 over HTTP. When a motion event arrives from the field, the same pipeline triggers the alert display and sends the sweep command to MAX Node 2. from flask import Flask, request, jsonify from datetime import datetime import requests import socketserver import threading import logging # ─── Configuration ──────────────────────────────────────── MAX_ALERT_IP = &amp;quot;192.168.4.XX&amp;quot; # MAX Node 1 — desktop alert panel MAX_TURRET_IP = &amp;quot;192.168.4.YY&amp;quot; # MAX Node 2 — field turret MAX_PORT = 8080 SYSLOG_PORT = 514 app = Flask(__name__) logging.getLogger(&amp;#39;werkzeug&amp;#39;).setLevel(logging.ERROR) # ─── Push alert to MAX Node 1 (desktop panel) ───────────── def push_to_max(message): try: r = requests.post( f&amp;quot;http://{MAX_ALERT_IP}:{MAX_PORT}/alert&amp;quot;, json={&amp;quot;message&amp;quot;: message}, timeout=3 ) print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Push OK → {message}&amp;quot;) except Exception as e: print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Push error: {e}&amp;quot;) # ─── Send sweep trigger to MAX Node 2 (turret) ──────────── def trigger_sweep(): try: requests.post( f&amp;quot;http://{MAX_TURRET_IP}:{MAX_PORT}/sweep&amp;quot;, json={&amp;quot;command&amp;quot;: &amp;quot;SCAN_TRIGGER&amp;quot;}, timeout=3 ) print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Sweep triggered&amp;quot;) except Exception as e: print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Sweep error: {e}&amp;quot;) # ─── UDP Syslog listener ─────────────────────────────────── class SyslogHandler(socketserver.BaseRequestHandler): def handle(self): data = self.request[0].strip() try: message = data.decode(&amp;#39;utf-8&amp;#39;, errors=&amp;#39;ignore&amp;#39;) except: return print(f&amp;quot;[SYSLOG] {message}&amp;quot;) # Strip syslog priority header clean = message if clean.startswith(&amp;#39; &amp;#39;) if end != -1: clean = clean[end+1:].strip() parts = clean.split() if len(parts) &amp;gt; 4: clean = &amp;#39; &amp;#39;.join(parts[3:]) # Forward cleaned message to alert panel push_to_max(clean[:60]) # ─── Flask endpoints ─────────────────────────────────────── @app.route(&amp;#39;/alert&amp;#39;, methods=[&amp;#39;POST&amp;#39;]) def receive_alert(): data = request.get_json() message = data.get(&amp;#39;message&amp;#39;, &amp;#39;&amp;#39;) print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Alert received: {message}&amp;quot;) push_to_max(message) trigger_sweep() return jsonify({&amp;quot;status&amp;quot;: &amp;quot;ok&amp;quot;}), 200 @app.route(&amp;#39;/motion&amp;#39;, methods=[&amp;#39;POST&amp;#39;]) def receive_motion(): print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Motion event from field&amp;quot;) push_to_max(&amp;quot;MOTION DETECTED&amp;quot;) trigger_sweep() return jsonify({&amp;quot;status&amp;quot;: &amp;quot;ok&amp;quot;}), 200 @app.route(&amp;#39;/status&amp;#39;, methods=[&amp;#39;GET&amp;#39;]) def status(): return jsonify({&amp;quot;status&amp;quot;: &amp;quot;NetSentinel online&amp;quot;}), 200 # ─── Startup ─────────────────────────────────────────────── if __name__ == &amp;#39;__main__&amp;#39;: syslog_server = socketserver.UDPServer( (&amp;#39;0.0.0.0&amp;#39;, SYSLOG_PORT), SyslogHandler ) syslog_thread = threading.Thread( target=syslog_server.serve_forever ) syslog_thread.daemon = True syslog_thread.start() print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Syslog UDP listening on port {SYSLOG_PORT}&amp;quot;) print(f&amp;quot;[{datetime.now().strftime(&amp;#39;%H:%M:%S&amp;#39;)}] Flask HTTP on port 5000&amp;quot;) app.run(host=&amp;#39;0.0.0.0&amp;#39;, port=5000, debug=False) Running the application: cd ~/netsentinel source venv/bin/activate sudo python3 app.py Sudo is required for binding to port 514 which is a privileged port below 1024. Event Flow Summary When a FortiGate detects a threat or a link state changes, it sends a syslog UDP packet to 192.168.2.200 port 514. The SyslogHandler parses the message, strips the priority header, and calls push_to_max() which sends an HTTP POST to MAX Node 1. The CharlieWing lights up the alert pattern on the operator&amp;#39;s desk. When the PIR fires in the field, the ESP32-CAM sends an HTTP POST to the /motion endpoint. The RPi simultaneously calls push_to_max() to alert the operator and trigger_sweep() to start the NEMA 17 pan sequence on MAX Node 2. Two completely independent threat vectors — network events and physical intrusion — converging at a single processing node and producing coordinated responses at two physical endpoints. What Comes Next The next blog covers the detection pipeline in detail — the PIR sensor, the ESP32-CAM, and how photographic evidence is captured and logged the moment motion is detected.</description></item><item><title>Forum Post: You can Now Post your Final Round-Up Project Blog</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56970/you-can-now-post-your-final-round-up-project-blog</link><pubDate>Wed, 20 May 2026 16:16:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:4f714742-4ffc-4a1e-875d-17ead259ea9d</guid><dc:creator>cstanton</dc:creator><description>Hey everyone, Projects is available for you to write up your final project overview/summary. Remember Design Challenges encourage you to provide updates here in the forums while you build, and then for your final &amp;#39;presentation&amp;#39; your full write up, videos, images, etc are for the final blog post. The specific requirements being: &amp;quot; at least 5 forum thread updates and 1 project blog before the closing date &amp;quot;. Looking forward to seeing the round up posts!</description><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/design%2bchallenge">design challenge</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/round_2D00_up">round-up</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/project">project</category></item><item><title>Forum Post: RE: Accessing the PAN1326B Bluetooth module with the Arduino IDE (Don't Forget to Set)</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56850/accessing-the-pan1326b-bluetooth-module-with-the-arduino-ide-don-t-forget-to-set/235676</link><pubDate>Wed, 20 May 2026 13:27:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:d552d600-84a6-41db-a29e-d2a0b1b16a20</guid><dc:creator>Alistair</dc:creator><description>Just in case it is useful I have discovered calling NVIC_SystemReset() to reset the MCU, instead of using the patch wire and calling pinMode() and digitalWrite() to reset the module is the way to go. Using the wire proved not to be reliable after a power cycle, but using this ARM reset command works 100% of the time.</description></item><item><title>Forum Post: RE: Anyone else having PAN1326C2 initialisation problems after a power cycle?</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56957/anyone-else-having-pan1326c2-initialisation-problems-after-a-power-cycle/235672</link><pubDate>Wed, 20 May 2026 09:44:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:5bc60fcc-3ca9-4157-9dc0-1157bec6e333</guid><dc:creator>Alistair</dc:creator><description>Thanks for that. It gave me the clue that it was likely in the Arduino core, and although I don&amp;#39;t know where exactly, the second reset needs to happen before the setup() routine is reached. The good news is I have a solution. The bad news is I have not 100% addressed the problem, but it is a reliable functional workaround. The solution appears to detect the failure (that I was doing before) and call the ARM function to do a processor reset ( NVIC_SystemReset(); ) without resetting the rest of the hardware. The list of possibilities for this working are long, and as much as I want to work out why this is the solution I don&amp;#39;t have time before the deadline. As a result I am calling it job done and will add it to my list of puzzles for the future.</description></item><item><title>Forum Post: RE: Anyone else having PAN1326C2 initialisation problems after a power cycle?</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56957/anyone-else-having-pan1326c2-initialisation-problems-after-a-power-cycle/235660</link><pubDate>Tue, 19 May 2026 22:44:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:74f92463-0dcc-496d-adc3-192b2ffd6655</guid><dc:creator>saramic</dc:creator><description>it is all a bit of a blur to me, and I think I will definitely be building my final setup with BTStack, but for what it is worth, I may have hit a similar snag when trying to setup the PAN1326C2 for raw HCI, some random notes in https://github.com/saramic/sentinel-box/blob/main/WORK_LOG.md#the-cts-trap under The CTS Trap. During boot, the PAN1326 holds its RTS output HIGH, which in turn feeds into P0.2 resulting in HCI command sitting in the transmit buffer and not going out. The fix at that time seemed to be to disable hardware CTS checking for the UART connected to the PAN1326. As I said, might be completely different to your setup but hope it helps &amp;#175;\_(ツ)_/&amp;#175;</description></item><item><title>Forum Post: NetSentinel – Update 2: 3D Printed Turret Enclosure &amp; Physical Design</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56967/netsentinel-update-2-3d-printed-turret-enclosure-physical-design</link><pubDate>Tue, 19 May 2026 21:53:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:996e23b6-9f38-4342-92c1-e6710bbbe696</guid><dc:creator>GustavoMorales</dc:creator><description>A distributed embedded security system needs a physical presence that matches its purpose. For NetSentinel&amp;#39;s field node — the turret that houses the MAX32630FTHR, the ESP32-CAM, and the NEMA 17 stepper motor — a custom enclosure was designed in Fusion 360 and printed on a Voxelab Aquila X2. This blog covers the design process, the mechanical concept, and the physical assembly. Two Nodes, Two Physical Forms The two MAX nodes have completely different physical forms reflecting their different roles. MAX Node 1 lives on the operator&amp;#39;s desk. It is built on the Particle Ethernet FeatherWing ProtoBoard with the MAX32630FTHR in slot 2 and the CharlieWing 15x7 LED matrix in slot 3. Its enclosure is compact and oriented so the LED matrix is always visible to the operator. This node does not move — its job is to be seen. MAX Node 2 is the field turret. It needed a mechanical design that could rotate a camera through a surveillance arc, house a full Feather stack, and do it all with components already on hand. The solution came from an unexpected place: a lead screw from a 3D printer Z axis. The Lead Screw Mechanism The pan mechanism is built around a T8 lead screw — the same Tr8x8 trapezoidal ACME thread used in the Z axis of FDM printers. The NEMA 17 stepper motor (17HS19-2004S1, 200 steps/rev, 2A/phase) drives the lead screw directly through the motor&amp;#39;s shaft coupler. Rather than using the lead screw for linear motion as it would in a printer, here it acts as a rotating vertical axis — the NEMA spins the screw and the ESP32-CAM mounted at the top of the screw rotates with it, sweeping the surveillance arc. The base connection is minimal by design: the lead screw couples directly to the NEMA 17 shaft through the red coupler that came with the screw, with no additional printed parts at the base. This kept the mechanical design simple and the motor easily replaceable. ESP32-CAM Mount — Fusion 360 Modification The ESP32-CAM mount at the top of the lead screw was designed in Fusion 360. Rather than modeling from scratch, an existing ESP32-CAM case was downloaded as a base and modified directly. The key addition was a square block on the lateral face of the case with a hole sized for the T8 screw — 8.5mm diameter with a T8 ACME thread profile. This block positions the camera at the correct height on the screw and keeps it oriented with the lens facing outward through the sweep arc. The T8 thread in the printed block acts as the mounting point — the case screws onto the lead screw at the desired height and stays fixed there while the whole assembly rotates with the NEMA. Cable management for the ESP32-CAM was straightforward: since the ESP32-CAM communicates over WiFi, only two wires run up the screw — power and ground. These were left with enough slack to allow the full 180-degree sweep without binding, and the firmware limits rotation to prevent a full 360-degree turn that would tangle the cables. Print Settings — Voxelab Aquila X2 All printed parts were produced on the Voxelab Aquila X2 in PLA. Layer height was 0.2mm for the main enclosure bodies and 0.15mm for the ESP32-CAM mount where the thread accuracy matters most. Infill was 20% gyroid. Print temperature was 200&amp;#176;C with the bed at 60&amp;#176;C. No supports were needed on any of the pieces. Assembly Sequence Assembly starts at the NEMA 17. The motor mounts to the base surface and the T8 lead screw couples to the shaft through the red coupler. The MAX32630FTHR stack — board, Ethernet FeatherWing, and DC+Stepper FeatherWing — sits in its enclosure adjacent to the motor with cables routed to the FeatherWing terminals. The ESP32-CAM case threads onto the top of the lead screw at the desired height. Power cables run up alongside the screw with a small spiral of slack to accommodate the sweep rotation. What Comes Next With the physical build complete, the next blog goes into the network layer — the dual-firewall DMZ topology, OSPF routing across FortiGate 90D, FortiGate 40F, and MikroTik RB3011, syslog forwarding to the Raspberry Pi, and the firewall policies that define what each segment can reach.</description></item><item><title>Forum Post: RE: Updating the PAN1326B Bluetooth module firmware and scanning for BLE devices (Don't Forget to Set)</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56951/updating-the-pan1326b-bluetooth-module-firmware-and-scanning-for-ble-devices-don-t-forget-to-set/235656</link><pubDate>Tue, 19 May 2026 21:21:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:f46d9078-645e-41dc-9b2d-93489210e21d</guid><dc:creator>kmikemoo</dc:creator><description>Alistair Best of luck!</description></item><item><title>Forum Post: NetSentinel – Update 1: Project Overview &amp; System Design</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56966/netsentinel-update-1-project-overview-system-design</link><pubDate>Tue, 19 May 2026 21:18:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:7244ceff-6d32-4ab3-90ed-cca577560a28</guid><dc:creator>GustavoMorales</dc:creator><description>Security monitoring has two dimensions that are rarely combined in a single embedded system: physical intrusion detection and network event awareness. NetSentinel was built for the element14 Smart Security and Surveillance Challenge to address both simultaneously — a distributed embedded SOC (Security Operations Center) node that detects movement in a datacenter environment, captures photographic evidence, performs an automated pan sweep, and delivers real-time visual alerts to an operator at their desk, all while also displaying network-level alerts from syslog sources across the infrastructure. This is not a single-device security camera. It is a multi-node distributed system running on enterprise-grade network infrastructure. The Two Nodes — Two Roles NetSentinel is built around two MAX32630FTHR Feather boards, each with a completely different role and physical location. MAX Node 1 lives on the operator&amp;#39;s desk. It drives a CharlieWing LED matrix that serves as a physical alert panel. When something happens — either a motion detection event in the field or a network change event captured via syslog from FortiGates, MikroTik routers, or switches — the operator sees an immediate visual alert on the display. No screen required, no application to check. The alert comes to the operator physically. MAX Node 2 lives in the field, mounted on a turret enclosure printed on a Voxelab Aquila X2. When the Raspberry Pi sends it a trigger over UDP, it drives a NEMA 17 stepper motor through an Adafruit DC+Stepper FeatherWing (P2927D) to perform a 180-degree surveillance sweep of the area where motion was detected. An ESP32-CAM module captures photographic evidence in parallel the moment the PIR fires, before the sweep even begins. The Brain — Raspberry Pi in the DMZ The Raspberry Pi is the central processing node. It sits in the DMZ of the network topology and has two inputs and two outputs. On the input side it receives syslog from network infrastructure — FortiGate firewalls, MikroTik routers, and switches — and it receives motion detection events from the ESP32-CAM and PIR sensor in the field. On the output side it forwards processed alerts to MAX Node 1 for visual display, and it sends UDP trigger packets to MAX Node 2 to initiate the pan sweep. The full event flow looks like this: FIELD (Datacenter / Turret): [PIR sensor] ──► motion detected [ESP32-CAM] ──► photo captured ──► RPi [MAX Node 2] ◄── UDP trigger ◄── RPi ──► NEMA 17 sweep BRAIN (DMZ): [Raspberry Pi 192.168.2.200] ◄── syslog from FortiGate 90D, FortiGate 40F, MikroTik RB3011, switches ◄── motion events from ESP32-CAM / PIR ──► alert packets to MAX Node 1 ──► sweep trigger to MAX Node 2 OPERATOR DESK: [MAX Node 1 + CharlieWing] ◄── physical intrusion alerts ◄── network change alerts (syslog) ──► visual LED display to operator Network Architecture NetSentinel was deployed on a dual-firewall DMZ architecture originally designed for an MSc Cybersecurity lab at Universidad Galileo. This gave the project a realistic enterprise network context rather than a flat home lab. The topology uses a FortiGate 90D as the exterior firewall, a MikroTik RB3011 as the central routing node, and a FortiGate 40F as the interior firewall protecting the LAN where the MAX nodes reside. Internet | [FortiGate 90D] — 192.168.10.1/30 | [MikroTik RB3011] |— ether4: 192.168.2.1/24 ← DMZ | | | [Raspberry Pi — NetSentinel Core] | 192.168.2.200 | |— ether2: 192.168.20.1/30 | [FortiGate 40F] |— LAN: 192.168.4.0/24 | [MAX Node 1 — Operator Alert Panel] [MAX Node 2 — Field Turret Controller] OSPF Area 0 runs across all three routing devices, providing full dynamic reachability without static routes. The Raspberry Pi in the DMZ can receive syslog and events from all segments but cannot initiate connections toward the LAN — a clean security boundary that reflects real DMZ design principles. The FortiGates and MikroTik forward syslog to the Raspberry Pi at 192.168.2.200. The Pi processes those logs and determines which events warrant a visual alert on the operator panel. Hardware Summary The complete bill of materials for NetSentinel includes the Raspberry Pi 4 as the central node, two MAX32630FTHR boards for the alert panel and turret controller, a Particle Ethernet FeatherWing for wired connectivity on each MAX board, an Adafruit CharlieWing LED matrix for the operator display, an Adafruit DC+Stepper FeatherWing P2927D for stepper motor control, a NEMA 17 stepper motor (17HS19-2004S1, 200 steps/rev, 2A/phase) for the pan mechanism, an ESP32-CAM for photographic evidence capture, a PIR sensor as the primary motion trigger, and a Voxelab Aquila X2 3D printer for the custom turret enclosure. What Comes Next The following blogs will cover each subsystem in detail: the 3D printed turret enclosure, the network configuration and firewall policies, the PIR and ESP32-CAM detection pipeline, the stepper motor pan mechanism, and the full system integration. NetSentinel demonstrates that embedded security systems do not have to be isolated gadgets — with the right network architecture they become nodes in a real security infrastructure.</description></item><item><title>Forum Post: RE: NetSentinel – Blog 1: Project Overview &amp; System Design</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56966/netsentinel-blog-1-project-overview-system-design/235685</link><pubDate>Tue, 19 May 2026 19:07:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:48d63433-297b-44b0-ade4-b71d1806ff4b</guid><dc:creator>DAB</dc:creator><description>Nice plan.</description></item><item><title>Forum Post: RE: NetSentinel – Blog 2: 3D Printed Turret Enclosure &amp; Physical Design</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56967/netsentinel-blog-2-3d-printed-turret-enclosure-physical-design/235686</link><pubDate>Tue, 19 May 2026 19:05:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:36b76dc5-9cec-4546-a8cf-d462da559f36</guid><dc:creator>DAB</dc:creator><description>Nice update.</description></item><item><title>Forum Post: Anyone else having PAN1326C2 initialisation problems after a power cycle?</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56957/anyone-else-having-pan1326c2-initialisation-problems-after-a-power-cycle</link><pubDate>Tue, 19 May 2026 11:06:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:f36dc6ee-b0a4-43fb-b025-893700aa392d</guid><dc:creator>Alistair</dc:creator><description>I noticed this issue before, and I thought I had worked around it, but my hack is not 100% reliable. The problem is the PAN1326C2 does not always appear initialise correctly so does not respond. My solution to this was to detect when this happened by having a timeout on the initial response from the module, and pulling the reset line low if nothing came. The entire platform would reboot and almost all of the time it would spring in to life. Win. The issue is that if I power cycle the entire system it almost never comes to life. If I press the reset button it is more likely to start, but not much more likely. The only way I have found to bring things to life is to press the reset button (or pull the reset line low) twice in quick succession. This is 100% reliable, but because I am resetting the MCU I can not implement this in code. I even did a quick hack with a capacitor to act as a simple bit of nonvolatile memory and did reset twice in code, but even this did not work. Either the pulses are not long enough or by the time it gets to my code it is too late. My original assumption for the need to reset at all was it is related 32768Hz oscillator output on P1.7 breaking the Bluetooth reset on P1.6. My gut reaction is this is still the issue, but there will be a chain of events and states leading to this. Has anyone else experienced this? If so have you managed to work around it? I guess I could set up some kind of test setup to work out what timings work and what do not. A more simple approach may be to hack on another microcontroller to handle the reset, or even reprogram the MAX32625PICO to do it. My actual solution may be to simply show a warning light and require the use to double press the reset button.</description></item><item><title>Forum Post: Updating the PAN1326B Bluetooth module firmware and scanning for BLE devices (Don't Forget to Set)</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56951/updating-the-pan1326b-bluetooth-module-firmware-and-scanning-for-ble-devices-don-t-forget-to-set</link><pubDate>Mon, 18 May 2026 11:37:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:557ffa80-5c57-4634-8237-2350afb04b88</guid><dc:creator>Alistair</dc:creator><description>After a brief holiday I am back and documenting what I was working on literally hours before needing to go. Problem solving can be addictive.:-) Just before I went I thought I would test the scanning for BLE devices, like the BLE keyring I am using in the project, and it did not work. I was getting nothing. The summary is that I needed to upload a firmware update to the CC256xB in the PAN1326B module at boot to access the BLE functions. At first I decided to experiment with the BTStack solution that has the patch pre-rendered in the open source code, and after a little experimentation I was able to send these blocks, but timing was critical and I needed to implement a form of flow control to make this reliable. For reference I am wanting to avoid BTStack as it adds so much complexity to my project when I only need a fraction of its functionality. There is nothing wrong with BTStack, and following other threads here I may be the only person not using it for my project, it is just not the best fit for this project.. In the end I used the library from https://www.ti.com/tool/CC256XB-BT-SP and created code to read each HCI block and send to the module, and then wait for a response before sending the next. To my surprise this worked without any great complication, but it was still not scanning for BLE devices. Then thank to BigG for finding the magic HCI commands . I was getting data. I believe I missed that I needed to activate the old school Bluetooth functionality before activating the Low Energy functionality, but I have not yet tested that. Here is some test code. I should have it tidied by the time the full project is ready. First I downloaded cc256xb_bt_sp_v1.8.zip from the TI link above. I then took BasePatch (including the part in the #ifdef __SUPPORT_LOW_ENERGY__ block) and LowEnergyPatch from CC256XB.h. Do not take the AvprPatch though. I then load that into a const uint8_t. const uint8_t patchBinary[] = { 0x01,0x37,0xfe,0x02,0x07,0x10… Next I add my commands to the bottom to start the scan. I could, and in a different scenario would, send these separately, but for this project this gives me little advantage. // Set Event Mask 0x01, 0x01, 0x0C, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Set LE Event Mask 0x01, 0x01, 0x20, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Set Enable LE 0x01, 0x6D, 0x0C, 0x02, 0x01, 0x01, // Set Scan Parameters 0x01, 0x0B, 0x20, 0x07, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, // Set Scan Enable (aka Start) 0x01, 0x0C, 0x20, 0x02, 0x01, 0x00 The code simply iterates thought the data and waits for a response at the end of each HCI command. uint16_t cursor = 0; while (cursor millis() ) ) { delay(1); } return BT_SERIAL.read(); } void receiveHCIResponse(Stream &amp;amp;inSerial) { uint8_t packetType = streamReadBlocking(inSerial); uint8_t eventCode = streamReadBlocking(inSerial); uint8_t paramLength = streamReadBlocking(inSerial); // Skip what we don&amp;#39;t need streamSkipBlocking(inSerial, paramLength, HCI_TIMEOUT); } There was still one issue, that being I was getting incomplete messages back from the module. I was getting packets of data reporting to have many many messages in it, but in fact only had one to two. My code was timing out and nothing was making any sense. In the end I instigated the scan (aka ran the Set Scan Enable command) with the remove duplicates flag set to 1 (&amp;quot;0x01, 0x0C, 0x20, 0x02, 0x01, 0x01&amp;quot;). This just worked and I now get several properly formed messages a minute. All the local BLE devices are showing up, and are repeated between 1 and 2 minutes, so adequate for my needs. I will note that buffer overruns should be considered with this approach. I am not so worried at the moment as binary data being read from flash is fixed at compile time and not stored in RAM, and I do not trust the data received from the module to use it for checking a buffer size, but it really does need considering. So that is working and I am at the point in starting the final “integration hell” stage of the project. Wish me luck.</description></item><item><title>File: btstack_lescanner</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/m/managed-videos/151325</link><pubDate>Sun, 17 May 2026 22:08:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:f2044b45-47bd-4264-be6a-af5745036b15</guid><dc:creator>BigG</dc:creator><description /></item><item><title>Forum Post: Getting btstack to work via Arduino</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56949/getting-btstack-to-work-via-arduino</link><pubDate>Sun, 17 May 2026 22:06:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:7982c187-284b-4227-9ccd-3991fe500f58</guid><dc:creator>BigG</dc:creator><description>This was purely an agentic AI exercise as I was curious to learn how fast I could get my AI agent to modify an Arduino version of bluekitchen&amp;#39;s btstack I had found online. I had no way of testing this btstack version with other MCU&amp;#39;s so thought to give it a go anyway. Well, it took a couple of hours to crack it, with the majority time spent testing 2 examples. The impressive part is that it started by (re?)creating a &amp;quot;HAL&amp;quot; of sorts for the MAX32630FTHR in seconds once I had pointed it to be previous manual attempt of uploading the service pack and configuring the RX and TX pins correctly. It worked first time without error. Header: hal_max32630fthr.h #ifndef HAL_MAX32630FTHR_H #define HAL_MAX32630FTHR_H #include #if defined __cplusplus extern &amp;quot;C&amp;quot; { #endif void max32630fthr_bluetooth_setup(void); void max32630fthr_bluetooth_set_baud(uint32_t baud); #if defined __cplusplus } #endif #endif Source: hal_max32630fthr.cpp #include &amp;quot;hal_max32630fthr.h&amp;quot; #if defined(ARDUINO_MAX32630FTHR) || defined(TARGET_MAX32630FTHR) || defined(MAX32630FTHR) || defined(MAX32630) #include #include &amp;quot;pwrseq_regs.h&amp;quot; #include &amp;quot;rtc_regs.h&amp;quot; #include &amp;quot;ioman_regs.h&amp;quot; #include &amp;quot;gpio_regs.h&amp;quot; // Pin Definitions for MAX32630FTHR #define BLE_SHUTDOWN_PIN P1_6 // nSHUTD (Active Low) #define M4_RTS_OUT_TO_BLE P0_2 // M4 RTS Output #define BLE_RTS_IN_TO_M4 P0_3 // BLE Module RTS Input (M4 CTS) #define M4_TX_PIN P0_0 // UART0 TX (Mapping B) #define M4_RX_PIN P0_1 // UART0 RX (Mapping B) extern &amp;quot;C&amp;quot; void max32630fthr_bluetooth_setup(void) { // --- 1. SET VOLTAGES --- useVDDIO(BLE_SHUTDOWN_PIN); useVDDIO(M4_TX_PIN); useVDDIO(M4_RX_PIN); useVDDIO(M4_RTS_OUT_TO_BLE); useVDDIO(BLE_RTS_IN_TO_M4); // --- 2. INITIALIZE HARDWARE --- // Configure 32.768 kHz Clock (P1.7) MXC_RTCCFG-&amp;gt;clk_ctrl |= MXC_F_RTC_CLK_CTRL_NANO_EN; MXC_RTCCFG-&amp;gt;osc_ctrl |= MXC_F_RTC_OSC_CTRL_OSC_WARMUP_ENABLE; MXC_RTCTMR-&amp;gt;prescale = MXC_V_RTC_PRESCALE_DIV_2_0; MXC_PWRSEQ-&amp;gt;reg4 |= MXC_F_PWRSEQ_REG4_PWR_PSEQ_32K_EN; // Prepare Pins: Set M4 RTS LOW BEFORE the module wakes up to signal &amp;quot;Ready&amp;quot; pinMode(M4_RTS_OUT_TO_BLE, OUTPUT); digitalWrite(M4_RTS_OUT_TO_BLE, LOW); pinMode(BLE_RTS_IN_TO_M4, INPUT); pinMode(M4_RX_PIN, INPUT); // Pulse Reset (nSHUTD) pinMode(BLE_SHUTDOWN_PIN, OUTPUT); digitalWrite(BLE_SHUTDOWN_PIN, LOW); delay(100); digitalWrite(BLE_SHUTDOWN_PIN, HIGH); // Give the TI Controller time to wake up and stabilize delay(500); } extern &amp;quot;C&amp;quot; void max32630fthr_bluetooth_set_baud(uint32_t baud) { Serial0.begin(baud); // Force IOMAN to Mapping B (Crossover). // Serial0.begin() can revert to Mapping A under the hood. We force it back! MXC_IOMAN-&amp;gt;uart0_req = 0x11; while (MXC_IOMAN-&amp;gt;uart0_ack != 0x11) continue; } #endif // MAX32630FTHR Then it was a case of adding in the service pack info for the CC256x. Finally, it was tweaking a couple of files to align them with the MAX32630FTHR. The main file that was tweaked was bsp_arduino_em9301.cpp . /** * Arduino + Energia Wrapper for BTstack */ #if !defined(ARDUINO) #error &amp;quot;Not compiling for Arduino/Energia&amp;quot; #endif #include #ifdef ENERGIA #include #endif #include #include &amp;quot;btstack/hal_uart_dma.h&amp;quot; #if defined(__arm__) // --- TRUE UART IMPLEMENTATION FOR ARM / MAX32630FTHR --- #include &amp;quot;utility/hal_max32630fthr.h&amp;quot; // Define the hardware serial port connected to your CC256x #if defined(ARDUINO_MAX32630FTHR) || defined(TARGET_MAX32630FTHR) || defined(MAX32630FTHR) || defined(MAX32630) #define BT_SERIAL Serial0 #else #define BT_SERIAL Serial1 #endif static uint16_t bytes_to_read = 0; static uint8_t * rx_buffer_ptr = NULL; static uint16_t bytes_to_write = 0; static uint8_t * tx_buffer_ptr = NULL; static void dummy_handler(void){}; static void (*rx_done_handler)(void) = dummy_handler; static void (*tx_done_handler)(void) = dummy_handler; // Large secondary ring buffer to absorb Arduino loop() / Serial.print() latency #define RX_RING_BUFFER_SIZE 1024 static uint8_t rx_ring_buffer[RX_RING_BUFFER_SIZE]; static volatile uint16_t rx_ring_head = 0; static volatile uint16_t rx_ring_tail = 0; extern &amp;quot;C&amp;quot; __attribute__((weak)) void max32630fthr_bluetooth_setup(void) {} extern &amp;quot;C&amp;quot; __attribute__((weak)) void max32630fthr_bluetooth_set_baud(uint32_t baud) { BT_SERIAL.begin(baud); } extern &amp;quot;C&amp;quot; void hal_uart_dma_init(void){ max32630fthr_bluetooth_setup(); max32630fthr_bluetooth_set_baud(115200); } extern &amp;quot;C&amp;quot; int hal_uart_dma_set_baud(uint32_t baud){ max32630fthr_bluetooth_set_baud(baud); return 0; } extern &amp;quot;C&amp;quot; void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t length){ tx_buffer_ptr = (uint8_t *) buffer; bytes_to_write = length; } extern &amp;quot;C&amp;quot; void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t length){ rx_buffer_ptr = buffer; bytes_to_read = length; } static inline void drain_hardware_buffer(void) { // Drain Hardware UART buffer into our massive Ring Buffer to prevent overflows while (BT_SERIAL.available() &amp;gt; 0) { uint16_t next_head = (rx_ring_head + 1) % RX_RING_BUFFER_SIZE; if (next_head != rx_ring_tail) { rx_ring_buffer[rx_ring_head] = BT_SERIAL.read(); rx_ring_head = next_head; } else { break; // Ring buffer full! } } } extern &amp;quot;C&amp;quot; void hal_uart_dma_process(void){ // 1. Process Transmit (Blocking write for reliability during init script upload) if (bytes_to_write &amp;gt; 0) { BT_SERIAL.write(tx_buffer_ptr, bytes_to_write); bytes_to_write = 0; (*tx_done_handler)(); } // 2. Drain Hardware UART buffer drain_hardware_buffer(); // 3. Process Receive (Feed BTstack from Ring Buffer) while (bytes_to_read &amp;gt; 0 &amp;amp;&amp;amp; rx_ring_head != rx_ring_tail) { *rx_buffer_ptr++ = rx_ring_buffer[rx_ring_tail]; rx_ring_tail = (rx_ring_tail + 1) % RX_RING_BUFFER_SIZE; bytes_to_read--; if (bytes_to_read == 0) { (*rx_done_handler)(); } } } // Override Arduino&amp;#39;s weak yield() to aggressively drain the UART buffer // while the user sketch is blocked on delay() or Serial.print()! extern &amp;quot;C&amp;quot; void yield(void) { drain_hardware_buffer(); } extern &amp;quot;C&amp;quot; void hal_uart_dma_set_sleep(uint8_t sleep){} extern &amp;quot;C&amp;quot; void hal_uart_dma_set_csr_irq_handler( void (*csr_irq_handler)(void)){} extern &amp;quot;C&amp;quot; void hal_uart_dma_set_block_received( void (*block_handler)(void)){ rx_done_handler = block_handler ? block_handler : dummy_handler; } extern &amp;quot;C&amp;quot; void hal_uart_dma_set_block_sent( void (*block_handler)(void)){ tx_done_handler = block_handler ? block_handler : dummy_handler; } extern &amp;quot;C&amp;quot; uint32_t hal_time_ms(void){ return millis(); } #else // --- ORIGINAL EM9301 SPI IMPLEMENTATION (AVR) --- #define HAVE_SHUTDOWN #ifdef ENERGIA // CMM 9301 Configuration for TI Launchpad #define PIN_SPI_SCK 7 #define PIN_CS 8 #define PIN_SHUTDOWN 11 #define PIN_IRQ_DATA 13 #define PIN_SPI_MISO 14 #define PIN_SPI_MOSI 15 #else // ARDUINO // CMM 9301 Configuration for Arduino #define PIN_IRQ_DATA 2 #define PIN_CS 4 #define PIN_SHUTDOWN 5 // -- SPI defines for Arduino Mega #ifndef PIN_SPI_MISO #define PIN_SPI_MISO 50 #endif #ifndef PIN_SPI_MOSI #define PIN_SPI_MOSI 51 #endif #ifndef PIN_SPI_SCK #define PIN_SPI_SCK 52 #endif #endif // rx state static uint16_t bytes_to_read = 0; static uint8_t * rx_buffer_ptr = 0; // tx state static uint16_t bytes_to_write = 0; static uint8_t * tx_buffer_ptr = 0; // handlers static void dummy_handler(void){}; static void (*rx_done_handler)(void) = dummy_handler; static void (*tx_done_handler)(void) = dummy_handler; static void bt_setup(void){ pinMode(PIN_CS, OUTPUT); pinMode(PIN_SPI_MOSI, OUTPUT); pinMode(PIN_SPI_SCK, OUTPUT); pinMode(PIN_SHUTDOWN, OUTPUT); pinMode(PIN_IRQ_DATA, INPUT); digitalWrite(PIN_CS, HIGH); digitalWrite(PIN_SPI_MOSI, LOW); digitalWrite(PIN_SHUTDOWN, HIGH); // SPI settings are reset in SPI.begin() - calls hang on Arduino Zero, too. // SPI.setBitOrder(MSBFIRST); // SPI.setDataMode(SPI_MODE0); // SPI.end(); } #ifdef HAVE_SHUTDOWN static void bt_power_cycle(void){ // power cycle. set CPU outputs to input to not power EM9301 via IOs // pinMode(PIN_SPI_MOSI, INPUT); // pinMode(PIN_CS, INPUT); pinMode(PIN_CS, OUTPUT); pinMode(PIN_SPI_MOSI, OUTPUT); pinMode(PIN_SPI_SCK, OUTPUT); pinMode(PIN_SHUTDOWN, OUTPUT); digitalWrite(PIN_CS, LOW); digitalWrite(PIN_SPI_MOSI, LOW); digitalWrite(PIN_SPI_SCK, LOW); digitalWrite(PIN_SHUTDOWN, HIGH); delay(500); pinMode(PIN_SPI_MOSI, OUTPUT); pinMode(PIN_CS, OUTPUT); digitalWrite(PIN_SPI_MOSI, LOW); digitalWrite(PIN_CS, HIGH); digitalWrite(PIN_SHUTDOWN, LOW); delay(1000); } #endif #ifndef HAVE_SHUTDOWN static void bt_send_illegal(void){ digitalWrite(PIN_SPI_MOSI, HIGH); digitalWrite(PIN_CS, LOW); printf(&amp;quot;Illegal start\n&amp;quot;); SPI.begin(); int i; for (i=0;i 0); SPI.end(); digitalWrite(PIN_CS, HIGH); // printf(&amp;quot;\n&amp;quot;); // notify upper layer (*rx_done_handler)(); return num_bytes_read; } extern &amp;quot;C&amp;quot; void hal_uart_dma_init(void){ bt_setup(); #ifdef HAVE_SHUTDOWN bt_power_cycle(); #else // bring EM9301 into defined state bt_send_illegal(); bt_send_reset(); bt_flush_input(); #endif } extern &amp;quot;C&amp;quot; void hal_uart_dma_set_block_received( void (*block_handler)(void)){ rx_done_handler = block_handler; } extern &amp;quot;C&amp;quot; void hal_uart_dma_set_block_sent( void (*block_handler)(void)){ tx_done_handler = block_handler; } extern &amp;quot;C&amp;quot; void hal_uart_dma_set_csr_irq_handler( void (*csr_irq_handler)(void)){ // only used for eHCILL } extern &amp;quot;C&amp;quot; int hal_uart_dma_set_baud(uint32_t baud){ return 0; } extern &amp;quot;C&amp;quot; void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t length){ // printf(&amp;quot;send_block, bytes %u\n&amp;quot;, length); tx_buffer_ptr = (uint8_t *) buffer; bytes_to_write = length; } extern &amp;quot;C&amp;quot; void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t length){ rx_buffer_ptr = buffer; bytes_to_read = length; } extern &amp;quot;C&amp;quot; void hal_uart_dma_set_sleep(uint8_t sleep){ // not needed for SPI (doesn&amp;#39;t need internal clock to work) } extern &amp;quot;C&amp;quot; void hal_uart_dma_process(void){ int num_bytes_read = bt_try_read(); if (num_bytes_read == 0){ bt_try_send(); } } extern &amp;quot;C&amp;quot; uint32_t hal_time_ms(void){ return millis(); } #endif Once the btstack library compiled it was onto creating two examples. Here I modified one example (LEPeripheral.ino) and created a new example (LEScanner.ino). The other examples in the btstack library were not tested and would need to be modified to get them to work with the MAX32630FTHR board. The modified version of `LEPeripheral.ino` basically creates a LED service which allows you to modify the onboard RGB LED&amp;#39;s colour, brightness and a blinking pattern. It does this through the use of two characteristics. I tried to add in a button service but the CC256b BLE device did not have enough memory. This was because I was using 128-bit UUID&amp;#39;s. It&amp;#39;s a well known problem with old (v4.x) BLE devices. // LE Peripheral Example - Adapted for MAX32630FTHR #include // Bring in the native C BTstack headers extern &amp;quot;C&amp;quot; { #include &amp;quot;btstack_memory.h&amp;quot; #include #include #include &amp;quot;btstack/hal_uart_dma.h&amp;quot; #include &amp;quot;att_server.h&amp;quot; // Expose the TI CC256x controller extern bt_control_t * bt_control_cc256x_instance(void); } // GATT Characteristic Handles uint16_t handle_rgb_color; uint16_t handle_rgb_state; // State Variables uint8_t color_r = 0; uint8_t color_g = 0; uint8_t color_b = 255; // Default to Blue uint8_t intensity = 100; uint8_t state_mode = 0; // 0=Off, 1=Solid, 2=Blink uint16_t state_period = 1000; uint8_t state_duty = 50; // Blink tracking uint32_t last_blink_time = 0; bool blink_state = false; // Update physical RGB LED (Active LOW on MAX32630FTHR) void updateLEDs(bool is_on) { if (!is_on || state_mode == 0) { // Active LOW: 255 turns the LED completely off analogWrite(RED_LED, 255); analogWrite(GREEN_LED, 255); analogWrite(BLUE_LED, 255); } else { // Calculate intensity-scaled values uint8_t r = 255 - ((uint16_t)color_r * intensity / 100); uint8_t g = 255 - ((uint16_t)color_g * intensity / 100); uint8_t b = 255 - ((uint16_t)color_b * intensity / 100); analogWrite(RED_LED, r); analogWrite(GREEN_LED, g); analogWrite(BLUE_LED, b); } } void setup(void){ Serial.begin(115200); while(!Serial) continue; Serial.println(&amp;quot;\r\nStarting LEPeripheral setup for MAX32630FTHR...&amp;quot;); pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); pinMode(BLUE_LED, OUTPUT); updateLEDs(false); // Start OFF // set callbacks BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback); BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback); BTstack.setGATTCharacteristicRead(gattReadCallback); BTstack.setGATTCharacteristicWrite(gattWriteCallback); // --- SETUP GATT DATABASE --- // 1. rgbService BTstack.addGATTService(new UUID(&amp;quot;00000001-1111-2222-3333-444444444444&amp;quot;)); handle_rgb_color = BTstack.addGATTCharacteristicDynamic( new UUID(&amp;quot;00000002-1111-2222-3333-444444444444&amp;quot;), ATT_PROPERTY_READ | ATT_PROPERTY_WRITE_WITHOUT_RESPONSE, 0); handle_rgb_state = BTstack.addGATTCharacteristicDynamic( new UUID(&amp;quot;00000003-1111-2222-3333-444444444444&amp;quot;), ATT_PROPERTY_READ | ATT_PROPERTY_WRITE_WITHOUT_RESPONSE, 0); // Pass the official CC256X controller flag so it loads the TI Service patch! BTstack.setup(BT_CONTROLLER_CC256X); BTstack.startAdvertising(); } void loop(void){ BTstack.loop(); // 1. Non-blocking Blink Logic if (state_mode == 2) { uint32_t now = millis(); uint32_t on_time = (uint32_t)state_period * state_duty / 100; uint32_t off_time = state_period - on_time; if (blink_state &amp;amp;&amp;amp; (now - last_blink_time &amp;gt;= on_time)) { blink_state = false; last_blink_time = now; updateLEDs(false); } else if (!blink_state &amp;amp;&amp;amp; (now - last_blink_time &amp;gt;= off_time)) { blink_state = true; last_blink_time = now; updateLEDs(true); } } } void deviceConnectedCallback(BLEStatus status, BLEDevice *device) { if (status == BLE_STATUS_OK) { Serial.println(&amp;quot;Device connected!&amp;quot;); } } void deviceDisconnectedCallback(BLEDevice * device){ Serial.println(&amp;quot;Disconnected. Scanning resumes automatically.&amp;quot;); } uint16_t gattReadCallback(uint16_t value_handle, uint8_t * buffer, uint16_t buffer_size){ if (value_handle == handle_rgb_color) { if (buffer &amp;amp;&amp;amp; buffer_size &amp;gt;= 4) { buffer[0] = color_r; buffer[1] = color_g; buffer[2] = color_b; buffer[3] = intensity; } return 4; } else if (value_handle == handle_rgb_state) { if (buffer &amp;amp;&amp;amp; buffer_size &amp;gt;= 4) { buffer[0] = state_mode; buffer[1] = (state_period &amp;gt;&amp;gt; 8) &amp;amp; 0xFF; buffer[2] = state_period &amp;amp; 0xFF; buffer[3] = state_duty; } return 4; } return 0; } int gattWriteCallback(uint16_t value_handle, uint8_t *buffer, uint16_t size){ if (value_handle == handle_rgb_color &amp;amp;&amp;amp; size &amp;gt;= 4) { color_r = buffer[0]; color_g = buffer[1]; color_b = buffer[2]; intensity = buffer[3]; Serial.print(&amp;quot;RGB updated: &amp;quot;); Serial.print(color_r); Serial.print(&amp;quot;,&amp;quot;); Serial.print(color_g); Serial.print(&amp;quot;,&amp;quot;); Serial.print(color_b); Serial.print(&amp;quot; @ &amp;quot;); Serial.print(intensity); Serial.println(&amp;quot;%&amp;quot;); if (state_mode == 1 || (state_mode == 2 &amp;amp;&amp;amp; blink_state)) { updateLEDs(true); } } else if (value_handle == handle_rgb_state &amp;amp;&amp;amp; size &amp;gt;= 4) { state_mode = buffer[0]; state_period = (buffer[1] // Bring in the native C BTstack headers extern &amp;quot;C&amp;quot; { #include &amp;quot;btstack_memory.h&amp;quot; #include #include #include &amp;quot;btstack/hal_uart_dma.h&amp;quot; // Expose the TI CC256x controller and the H4 UART transport extern bt_control_t * bt_control_cc256x_instance(void); extern hci_transport_t * hci_transport_h4_dma_instance(void); } UUID targetLuggageTagUUID(&amp;quot;0000FEED-0000-1000-8000-00805F9B34FB&amp;quot;); // Hardware definitions const int LED_PIN = LED_BUILTIN; // Triggers the MAX32630FTHR&amp;#39;s onboard LED uint32_t lastTagSeenTime = 0; // Globals to pass data from the fast callback to the main loop bool targetFound = false; int targetRssi = 0; char targetMac[18] = {0}; void setup(void){ Serial.begin(115200); pinMode(LED_PIN, OUTPUT); // Wait for the native USB serial port to connect before booting BTstack while(!Serial) continue; Serial.println(&amp;quot;\r\nStarting LECentral Scanner&amp;quot;); // Only register the advertisement callback (used for scanning) BTstack.setBLEAdvertisementCallback(advertisementCallback); // 1. Disable packet logging now that everything is working! // (Printing massive raw hex dumps to USB causes the hardware UART buffer to overflow and drop packets) // BTstack.enablePacketLogger(); // BTstack.enableDebugLogger(); // 2. Use the official setup (now that we patched BTstack.cpp to use the CC256x!) BTstack.setup(BT_CONTROLLER_CC256X); BTstack.bleStartScanning(); } void loop(void){ // Must be called continuously to process Bluetooth events BTstack.loop(); // Turn off LED if we haven&amp;#39;t seen the target tag in the last 3 seconds if (millis() - lastTagSeenTime &amp;gt; 3000) { digitalWrite(LED_PIN, HIGH); // Note: Built-in LEDs are often active-LOW on Maxim boards! } // Process the discovery safely outside the callback if (targetFound) { targetFound = false; // clear the flag lastTagSeenTime = millis(); digitalWrite(LED_PIN, LOW); // Turn ON the LED! // Rough distance calculation (assuming Tx Power at 1 meter is ~ -65 dBm) // Distance = 10 ^ ((TxPower - RSSI) / (10 * N)) -&amp;gt; N = Path loss exponent (usually ~2.5) float distance = pow(10.0, (-65.0 - targetRssi) / (10.0 * 2.5))*0.6; static uint32_t lastPrintTime = 0; if (millis() - lastPrintTime &amp;gt; 100) { lastPrintTime = millis(); Serial.print(&amp;quot;\r\n *** Target Tag Found! MAC: &amp;quot;); Serial.print(targetMac); Serial.print(&amp;quot; | RSSI: &amp;quot;); Serial.print(targetRssi); Serial.print(&amp;quot; dBm | Est. Distance: &amp;quot;); Serial.print(distance); Serial.println(&amp;quot; meters&amp;quot;); } } } void advertisementCallback(BLEAdvertisement *bleAdvertisement) { bool isTarget = false; // 1. Check if the advertisement contains our target as a standard GATT Service UUID if (bleAdvertisement-&amp;gt;containsService(&amp;amp;targetLuggageTagUUID)) { isTarget = true; } // 2. Check if the beacon is an iBeacon, which hides the UUID inside Manufacturer Specific Data if (bleAdvertisement-&amp;gt;isIBeacon()) { if (targetLuggageTagUUID.matches((UUID*)bleAdvertisement-&amp;gt;getIBeaconUUID())) { isTarget = true; } } if (isTarget) { // Quickly copy the data to our globals and get out of the callback! targetRssi = bleAdvertisement-&amp;gt;getRssi(); // getAddressString() uses a single static buffer internally in BTstack, // so we copy it to our own safe array using strncpy. strncpy(targetMac, bleAdvertisement-&amp;gt;getBdAddr()-&amp;gt;getAddressString(), sizeof(targetMac)); targetFound = true; } } Here is a video demonstration of this sketch: community.element14.com/.../btstack_5F00_lescanner.mp4 The whole library can be found here should anyone find it useful - I just compressed it as a zip file rather than uploading it onto Github: https://drive.google.com/file/d/18-V3d7NckCjgbqI09-9MtiYFfE9YE3BZ/view?usp=sharing I hope you find this useful. Any question please feel free to ask.</description><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/ble">ble</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/btstack">btstack</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/arduino">arduino</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/MAX32630FTHR_2300_">MAX32630FTHR#</category></item><item><title>Forum Post: RE: Sentinel Box - Part IV - Bluetooth and BTStack</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56942/sentinel-box---part-iv---bluetooth-and-btstack/235582</link><pubDate>Fri, 15 May 2026 19:24:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:79c00655-6e84-4ae0-b17c-10effe6da181</guid><dc:creator>kmikemoo</dc:creator><description>Great post. Good job on getting the Bluetooth going.</description></item><item><title>Forum Post: RE: Sentinel Box - Part IV - Bluetooth and BTStack</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56942/sentinel-box---part-iv---bluetooth-and-btstack/235578</link><pubDate>Fri, 15 May 2026 02:08:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:62071278-4450-4220-94d0-d863e2a3d760</guid><dc:creator>meera_hussien</dc:creator><description>great progress...</description></item><item><title>File: 20260514_BLE_demo</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/m/managed-videos/151321</link><pubDate>Fri, 15 May 2026 01:42:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:e742d24d-4694-40d8-967b-54d162b078c9</guid><dc:creator>saramic</dc:creator><description /></item><item><title>Forum Post: Sentinel Box - Part IV - Bluetooth and BTStack</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/f/forum/56942/sentinel-box---part-iv---bluetooth-and-btstack</link><pubDate>Fri, 15 May 2026 01:40:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:69855a0f-3979-43ac-82a6-57b614d748ac</guid><dc:creator>saramic</dc:creator><description>As the competition end is nearing, I am hoping to connect to some Bluetooth to help speed up some of the configuration options of my Lock Box — Sentinel Box . Recap The idea is to build a smart lock box for digital devices to help control digital addiction, more on the idea can be found in part I and coding with LPSDK and accessing a fingerprint scanner in part II , and mechanical stepper motors and vault lock mechanisms in part III : Sentinel Box - Part I - the plan Sentinel Box - Part II - back to C Sentinel Box - Part III - Stepper Motor and Vault lock mechanism Getting over my fears I have a somewhat scarred history in some parts of microprocessor hobbyist programming. I recall back in the early 2000s making some relatively expensive development board unresponsive, I think it may have been running some version of J2ME Java &amp;#175;_(ツ)_/&amp;#175; , I shoved it under the house and gave up for some 5 years. I thought with the advent of PIC microcontrollers , and when I got around to looking at them around 2005, I would give it a try, but after a few different approaches I failed and gave up again. By the time Arduino came around I was a little hesitant and didn’t really think much of it. Finally, at a coding hackathon in Melbourne Australia, I was lucky enough to get a Particle board — one you had to program through a web browser and by default it was internet connected by WiFi when the implications of an internet connected device sparked something in me. Noting that most Arduinos do not support wireless communications, I quickly switched to ESP32 as my go-to and was relatively happy ever since. This was until a few years ago when I wanted to connect via Bluetooth — and I failed, the project went into a box. So with this background I got myself into this competition, where all of a sudden I would need a DAP link programmer to connect to a device, and the only wireless comms that it came with was Bluetooth. Numerous times when my device was unresponsive, I thought I had fried it somehow and it was time to put it in a box under the house. Numerous times I would touch and check voltages just in case something was heating up, or I had mixed up a wire or a voltage. This was exacerbated by my reliance on AI guiding me in a bunch of ideas like hermetic builds in a new Rust tool chain only to work out I had not thought about setting up the PMIC (power management) and pins were at random voltages. I got this far, touch wood, I can finish this off. When it came to Bluetooth and not really understanding the communications protocol and GATT (Generic Attribute Profile) vs ATT (Bluetooth Attribute Protocol) and what is Bluetooth vs BLE (Bluetooth Low Energy) and all the write ups on bluetooth that came before me in this competition: Guardian Sentinel — Ethernet &amp;amp; BLE meera_hussien BLE scanning and CC256X firmware uploading Alistair Identity Protocol - Part 9 - BLE GATT Challenge/Response with BTstack arvindsa Accessing the PAN1326B Bluetooth module with the Arduino IDE (Don’t Forget to Set) Alistair Accessing the PAN1326B Bluetooth module on the MAX32630fTHR Alistair Identity Protocol - Part 4 - BLE using PAN1326B and BTstack arvindsa github:arvindsa ble_beacon test using LPSDK With things like: CC256X firmware uploading … returned 0x01 (Unknown HCI Command), and that shown new firmware needs uploading. Has anyone done this? and GATT Service Definition … BTstack generates an ATT database at plaintext .gatt file. Refer: tool/compile_gatt.py … If you are new to GATT - I recommend to watch this video … and The first thing I do is to pull the reset pin low (aka active) white we set up some other things. There is an obscure hardware bug in the module design that at times prevents the reset from functioning as it should later #define BT_RST P1_6 digitalWrite ( BT_RST , LOW ); and I figured since BTStack was kind enough to share the LPSDK I thought they would also include the TI ’s Init Script for the CC2564B . and AI no longer having access to reading artilces on Element 14 - seems like that block came in recently? I was scared — was I going to fry my device? Was this ever going to work? Was my reliance on AI going to just burn tokens and not get me anywhere? here goes nothing … Random paths with AI The Element 14 apparent block of AI hit me day one. Given all the great resources of other people’s findings, I thought I would point AI at these articles and have it give me a strategy to go forward. Not paying too much attention, I just kept agreeing to the prompt to come up with a plan; only after hours of going around in circles I worked out it was actually having trouble accessing the articles and nothing to do with coming up with a plan of how to get things going. I was triggered when I saw it trying to install and set up Jina AI Reader . Jina AI is a specialized tool designed to scrape website data and convert it into clean, LLM -friendly text or Markdown format. It is particularly useful for RAG (Retrieval-Augmented Generation) systems, AI agents, and content extraction by removing HTML clutter. A bunch of tokens burnt and the long way round to getting Bluetooth working, as we will see shortly. HCI (Host Control Interface) The MAX32630FTHR has a PAN1326B Bluetooth module soldered directly on it — no external module or extra wiring needed. Inside the PAN1326B is a TI CC2564B dual-mode BT/BLE chip. It talks to the MAX32630 via UART0 , via P0.0 and P0.1 , simple enough, at least I couldn’t get the wiring wrong. As I had somehow gone down the direct HCI (Host Control Interface) method, I seemed to have to start to do a lot of low-level stuff like setting up the 32kHz reference clock on P1.7 needed by the BLE module. So this required starting the crystal oscillator and enabling it MXC_RTCCFG-&amp;gt;clk_ctrl |= MXC_F_RTC_CLK_CTRL_NANO_EN; MXC_RTCCFG-&amp;gt;osc_ctrl |= MXC_F_RTC_OSC_CTRL_OSC_WARMUP_ENABLE; MXC_PWRSEQ-&amp;gt;reg4 |= MXC_F_PWRSEQ_REG4_PWR_PSEQ_32K_EN; TMR_Delay(MXC_TMR0, MSEC(50)); Skipping the first two lines gives you a valid GPIO voltage on P1.7 — just no clock signal. The module boots but never initialises its HCI UART properly. Next there was a CTS trap. The module relies on hardware flow control — specifically RTS (Ready to Send) and CTS (Clear to Send) — to manage data transmission. With hardware flow control enabled ( .cts = 1 in uart_cfg_t ), the UART peripheral refuses to transmit while the CTS input (P0.2) is HIGH. During boot, the PAN1326B holds its RTS output HIGH — which feeds directly into P0.2. Result: every HCI command sits in the transmit buffer and never goes out. The fix is to disable hardware CTS checking and let the MCU transmit freely: const uart_cfg_t ble_cfg = { .parity = UART_PARITY_DISABLE, .size = UART_DATA_SIZE_8_BITS, .extra_stop = 0, .cts = 0, .rts = 0, .baud = 115200, }; Finally, to run BLE (Bluetooth Low Energy), the CC256XB requires a service pack??? The CC2564B chip ships without any LE (Bluetooth Low Energy) subsystem firmware. After a plain HCI Reset, standard BLE commands like HCI_LE_Set_Advertising_Parameters (opcode 0x2006 ) come back as “Unknown HCI Command”. Before BLE works, a service pack of ~150 TI vendor-specific HCI commands must be uploaded. TI distributes this as CC256XB-BT-SP . The Bluetopia variant ( CC256XB.h ) packages all the commands as two C arrays — BasePatch[] and LowEnergyPatch[] — each a flat stream of raw HCI packets. Uploading is straightforward iteration: while (p + 4 &amp;lt;= end) { unsigned int cmd_len = 4 + p[3]; hci_send(p, cmd_len); hci_drain_event(200); p += cmd_len; } The last command in LowEnergyPatch is 0xFD5B (LE enable). It takes noticeably longer than the others and its Command Complete event can still be sitting in the UART buffer when you send the follow-up HCI Reset. The symptom: hci_reset() receives 04 0E 04 01 5B FD 00 — a valid response, but for the wrong command. The fix is to loop in hci_reset() and skip any Command Complete that isn’t for the Reset opcode 0x0C03 . The file is TI proprietary (TSPA licence) TI TSPA License TECHNOLOGY AND SOFTWARE PUBLICLY AVAILABLE so it lives in reference/CC256XB_BT_SP/ which is gitignored. The license to get it reminds me of distributing encryption back in the 90s I certify that the following is true: (a) I understand that this Software/Tool/Document is subject to export controls under the U.S. Commerce Department’s Export Administration Regulations (“EAR”). (b) I am NOT located in Cuba, Iran, North Korea, Sudan or Syria. I understand these are prohibited destination countries under the EAR or U.S. sanctions regulations. (c) I am NOT listed on the Commerce Department’s Denied Persons List, the Commerce Department’s Entity List, the Commerce Department’s General Order No. 3 (in Supp. 1 t o EAR Part 736), or the Treasury Department’s Lists of Specially Designated Nationals. (d) I WILL NOT EXPORT, re-EXPORT or TRANSFER this Software/Tool/Document to any prohibited destination, entity, or individual without the necessary export license(s) or authorization(s) from the U.S. Government. (e) I will NOT USE or TRANSFER this Software/Tool/Document for use in any sensitive NUCLEAR, CHEMICAL or BIOLOGICAL WEAPONS, or MISSILE TECHNOLOGY end-uses unless authorized by the U.S. Government by regulation or specific license. … something was working, so now to connect Chrome Web Bluetooth scanner Chrome (and Edge) support the Web Bluetooth API — a page served from localhost can scan for and connect to nearby BLE peripherals without any native app. The device picker shows all advertising BLE devices: const device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true, optionalServices: [&amp;quot;generic_access&amp;quot;], }); const server = await device.gatt.connect(); const services = await server.getPrimaryServices(); The MAX32630FTHR was advertising and I had connectivity but what now? … BTStack is the way Seems I had gone a roundabout way to try to manually install packages and set oscillators as well as use HCI, but this was a lot of code and was not going to scale well to actually having 2-way comms between a configuration browser page and the BLE device. I re-read some of the above mentioned resources and it seemed BTStack was the way. AI still seemed to take me down some custom “polling” method it claimed src/btstack_port.c uses the same cooperative polling approach — not interrupt-driven DMA — but with a “simpler” single-pass structure: RX drain → TX drain → run loop tick. but I finally corrected it to pretty much use the code from arvindsa Switching from the raw HCI to BTstack — a proper BLE stack — meant the code was simpler, faster and more robust. Following adding BTstack as a git submodule git submodule add https://github.com/bluekitchen/btstack.git third_party/btstack I could now compile a human-readable GATT database — .gatt → .h file into a C byte array. The service definition is trivial: PRIMARY_SERVICE, 0000F001-0000-1000-8000-00805F9B34FB CHARACTERISTIC, 0000F002-..., DYNAMIC | WRITE | WRITE_WITHOUT_RESPONSE, And I could now access this from the browser and by writing data, see changes on the board. shows that changes on the Chrome page change the LED colour: 01 — RED , 02 — GREEN , 03 — BLUE . community.element14.com/.../20260514_5F00_BLE_5F00_demo.mp4 Next Ok, I have some Bluetooth, a dash of stepper and a finger scan or two. I am away down the coast at the moment so I cannot attempt the build of the vault mechanism till I have access to my limited tools. I am feeling bullish and maybe I should also get access to writing some things to the SD card, but maybe it is time to start to bring these things together in a final design of a Bluetooth-controlled setup for capturing fingerprints, locking and unlocking once the required family members have scanned their fingers. Source https://github.com/saramic/sentinel-box</description><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/ble">ble</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/btstack">btstack</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/security_2D00_challenge">security-challenge</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/design_2D00_challenge">design-challenge</category><category domain="https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/tags/MAX32630FTHR_2300_">MAX32630FTHR#</category></item><item><title>Blog Post: Adaptive Sentinel: Security and Surveillance Intelligence Hub</title><link>https://community.element14.com/challenges-projects/design-challenges/smart-security-and-surveillance/b/projects/posts/adaptive-sentinel-security-and-surveillance-intelligence-hub</link><pubDate>Thu, 14 May 2026 16:58:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:dd054701-0588-455b-a399-61952b27797e</guid><dc:creator>skruglewicz</dc:creator><description>Greetings, Community Members, This Project Blog constitutes the final technical documentation for the Adaptive Sentinel system, detailing its architectural evolution and performance validation. Prepared to meet the &amp;quot;Final Project Blog&amp;quot; criteria for the Smart Security and Surveillance – Design Challenge , this report outlines the development process from initial implementation to final outcomes. As required for challenge award eligibility, sponsored participants are submitting this comprehensive blog detailing the build process before the deadline. The Adaptive Sentinel project evolves standard access control into a proactive, distributed intelligence network. By pairing the Analog Devices MAX32630FTHR kit with the AI-capable Arduino UNO Q, the framework achieves a high-reliability infrastructure centered on edge intelligence. Technical breakthroughs within this project include: Dual-Factor Biometric Authentication: The concurrent use of facial recognition and voice passphrase validation to strengthen security. Crowd Sentiment Analysis: Environmental stress monitoring visualized through an ICLED-based &amp;quot;Atmosphere Index&amp;quot;. Active Sentry Response: Automated barrier deployment and sensor tracking managed by motor FeatherWings. The following sections, 1 through 6 , provide a comprehensive technical roadmap detailing the development and validation of the Sentinel framework. This documentation serves a critical dual purpose: Design Challenge Compliance: It chronicles the hardware-level integration of the Analog Devices MAX32630FTHR while mapping the software orchestration required to manage a high-reliability hub-to-node topology. Implementation Blueprint: Beyond documentation, these sections serve as the internal framework for the software architecture , the physical hardware build , and the iterative development of a functional prototype . By organizing this project into distinct functional pillars, the system&amp;#39;s lineage is traced from its foundational telemetry roots to a fully realized autonomous guardian. This roadmap is positioned at the forefront of the documentation to establish the &amp;quot;System-of-Systems&amp;quot; philosophy—ensuring that every technical specification, from UART backbone protocols to ICLED sentiment monitoring, contributes to a unified, responsive intelligence hub. 1. Architectural Framework: Hub-to-Node Topology This section outlines the physical structure of the system, which is divided into two main components: 1.1 Central Hub Architecture (Arduino UNO Q) Primary functions: Network orchestration, telemetry aggregation, and high-level logic management. Dual-Processor System: MPU for Web UI and intensive data logging; MCU for deterministic low-level communication protocols. Database BRICK Integration: Ensuring reliable storage of all streamed telemetry data. 1.2 Edge Node Architecture (Modular FeatherWing Subsystems) Deployment Strategy: Positioning modular edge nodes in designated building zones for localized data collection. Connectivity Strategy: Initial rollout employing a stable UART infrastructure as a performance benchmark. 2. System Integration and Final Evaluation This section documents the critical transition from component-level development to a functional, unified system prototype. The engineering workflow begins by detailing the hardware and firmware integration required to actualize the hub-and-node topology. Specifically, it maps the complex interfacing between the Arduino UNO Q master controller and the distributed Analog Devices MAX32630FTHR sensor nodes, establishing the core data telemetry pipeline and necessary peripheral synchronization. Once this stable communication backbone is established, the focus seamlessly transitions from physical integration to rigorous operational validation. This secondary phase outlines the empirical testing methodologies employed to quantify the system&amp;#39;s overall performance. By evaluating critical metrics—such as hub-to-node communication latency, processing overhead during dual-factor biometric authentication, and the mechanical actuation times of the active sentry barriers—this final analysis verifies the functional stability and technical viability of the complete Adaptive Sentinel architecture under real-world conditions. 2.1 Connectivity and Data Flow UART is employed for the reliable, short-range, point-to-point transfer of aggregated sensor telemetry from the Edge Nodes to the Central Hub, serving as a stable baseline for initial system validation. Logging data from both edge nodes to the central Arduino UNO Q hub. The exploration of a potential shift from a hardwired UART backbone toward a wireless framework—utilizing technologies like BLE or WiFi—stems from a requirement for improved scalability and deployment versatility.Wireless connectivity would allow Edge Nodes to be placed in a much wider range of locations within a facility, unconstrained by the physical limitations and installation costs of cabling. Furthermore, moving to a high-bandwidth wireless protocol addresses critical telemetry aggregation bottlenecks, enabling the Central Hub to process simultaneous data streams from a larger number of nodes without the serial contention or latency associated with a shared or multi-drop UART bus. 2.2 Functional Specifications The Central Hub orchestrates the network, aggregates telemetry, and manages high-level logic. Implementation of dual-factor biometric authentication (face + voice passphrase) for high-security access. Real-time &amp;#39;Crowd Sentiment&amp;#39; monitoring displayed via the ICLED to provide a color-coded &amp;quot;Atmosphere Index.&amp;quot; The system functions as a Remote Security Sentry, using motor wings to deploy physical barriers or actively orient sensors during a breach. 2.3 Technical Specifications Central Hub Architecture: The Arduino UNO Q uses a Dual-Processor System, with the MPU for data logging, and the MCU for deterministic low-level communication protocols. Edge Node Hardware: Deployment relies on modular FeatherWing Subsystems utilizing the MAX32630FTHR with specific logic integration. Connectivity Baseline: Initial reliable data transfer is established using a UART infrastructure. Software: Development includes key snippets of C++ or MicroPython logic, particularly for low-power management and motor control. Data Persistence: Reliable storage of all streamed telemetry is ensured via Database BRICK Integration. 2.4 System Testing Results: Validation &amp;amp; Metrics 2.4.1 Biometric Authentication Test Cases Validation of Dual-Factor Authentication: Successful log-in/access granted only upon correct face recognition AND voice passphrase match. Error Tolerance &amp;amp; Rejection: Documented failure rate for unauthorized access attempts (incorrect face, incorrect passphrase, or both). Speed and Latency: Measurement of authentication time from sensor input to access decision. 2.4.2 Active Sentry Response &amp;amp; Intelligence Hub Validation Break-In Simulation: Successful and timely motor wing deployment/sensor reorientation upon simulated breach trigger. Sentiment Index Accuracy: Correlation of ICLED &amp;quot;Atmosphere Index&amp;quot; (color-coded output) with environmental stress level inputs. Data Logging Reliability: Confirmation that all events (authentication attempts, breaches, and sentiment updates) are accurately logged to the Database BRICK. 3. Implementation: This section details the concrete execution and physical deployment of the Adaptive Sentinel architecture. Building upon the verified integration parameters, this phase documents the step-by-step realization of the system&amp;#39;s hardware and firmware infrastructure. The focus here is on the precise engineering methodologies required to operationalize the hub-and-node topology. Specifically, it outlines the deployment of the primary coordination logic across the Arduino UNO Q&amp;#39;s dual-processor environment, alongside the localized sensor initialization protocols for the distributed Analog Devices MAX32630FTHR edge nodes. By detailing the complete development workflow—spanning from low-level, interrupt-driven UART communication to the high-level management of the Linux-based Web UI and persistent data storage—this section serves as the definitive technical blueprint for constructing the functional prototype. 3.1 Central Hub Implementation The Arduino UNO Q serves as the primary coordinator, utilizing its dual-processor architecture to ensure deterministic performance. The MCU handles low-level interrupt-driven UART communication from the edge nodes, while the MPU manages the Linux-based Web UI and Database BRICK integration for persistent storage. The Central Hub implementation on the Arduino UNO Q leverages its dual-processor architecture to ensure deterministic, low-latency control on the Microcontroller Unit (MCU) side and robust high-level services on the Microprocessor Unit (MPU) side. The following pseudocode demonstrates the technical functionality for handling interrupt-driven UART communication from the Edge Nodes (on the MCU) and subsequent data logging and Web UI management (on the MPU) via the Remote Procedure Call (RPC) Bridge.The MCU (STM32U585) is responsible for the real-time task of receiving data packets from the Edge Nodes over UART and immediately forwarding them to the MPU. 3.1.1 MCU Pseudocode (C++: sketch.ino ) // ========================================================= // MCU Pseudocode (C++ - sketch.ino) // Role: Handle real-time, interrupt-driven UART comms and forward to MPU // ========================================================= #include #include // RPC Bridge library #include // For UART/Serial comms (Edge Node connection) // Assume Serial1 is the dedicated UART port connecting to Edge Nodes HardwareSerial&amp;amp; EdgeNodeSerial = Serial1; const int BAUDRATE = 115200; void setup() { // 1. Initialize UART for Edge Nodes // In a real implementation, a low-level interrupt handler would be registered // for byte-by-byte collection to minimize latency. EdgeNodeSerial.begin(BAUDRATE); // 2. Initialize the RPC Bridge for MPU communication Bridge.begin(); // 3. Define an optional function the MPU can call (e.g., to check MCU health) Bridge.provide(&amp;quot;get_mcu_status&amp;quot;, []() { return &amp;quot;REAL_TIME_OK&amp;quot;; }); } void loop() { // Check for incoming data from the Edge Node UART line if (EdgeNodeSerial.available()) { // Read a complete, delimited data string (telemetry packet) String data_packet = EdgeNodeSerial.readStringUntil(&amp;#39;\n&amp;#39;); if (data_packet.length() &amp;gt; 0) { // Forward the received data asynchronously to the MPU using &amp;#39;Notify&amp;#39;. // Notify is non-blocking and ideal for high-speed, one-way data pushes. Bridge.notify(&amp;quot;telemetry_data_in&amp;quot;, data_packet.c_str()); } } // Keep the loop running fast for deterministic behavior delay(10); } 3.1.2 MPU Pseudocode (Python: main.py ) The MPU (Qualcomm QRB2210) provides high-level orchestration, leveraging the Database BRICK for robust forensic data logging. This integration ensures that all security events, biometric attempts, and environmental telemetry are preserved with high integrity, providing a chronological audit trail critical for post-incident analysis and system auditing Python CODE # ========================================================= # MPU Pseudocode (Python - main.py) # Role: High-level orchestration, Web UI, Data Logging (Database BRICK) // The App Lab environment automatically handles Bricks and RPC communication # ========================================================= import arduino import logging # Simulate import of the Database BRICK for persistent storage # from bricks.database import DatabaseBrick # db_logger = DatabaseBrick() logging.basicConfig(level=logging.INFO, format=&amp;#39;[MPU] %(levelname)s: %(message)s&amp;#39;) # --- RPC Bridge Callback Function --- # This function is automatically called when the MCU executes Bridge.notify(&amp;quot;telemetry_data_in&amp;quot;, ...) def process_mcu_telemetry(data_string): &amp;quot;&amp;quot;&amp;quot;Processes incoming data packet from the MCU, logs it, and updates the UI.&amp;quot;&amp;quot;&amp;quot; try: # Assuming the data string contains node ID and value (e.g., &amp;quot;NODE1:BIOMETRIC_FAIL&amp;quot;) # In a real system, you would parse the string into structured data (e.g., JSON) logging.info(f&amp;quot;Received from MCU: {data_string}&amp;quot;) # 1. Database BRICK Integration (Persistent Storage) # The MPU handles the heavy I/O of writing to the Database BRICK. print(f&amp;quot;DATABASE BRICK: Logging forensic event: {data_string}&amp;quot;) # db_logger.log_event({&amp;#39;raw_telemetry&amp;#39;: data_string, &amp;#39;timestamp&amp;#39;: arduino.time()}) # 2. Web UI Management # Update the Linux-based Web UI dashboard with new data # arduino.webui.update_display(data_string) except Exception as e: logging.error(f&amp;quot;Error in data processing pipeline: {e}&amp;quot;) def main(): &amp;quot;&amp;quot;&amp;quot;Entry point for the MPU application running in App Lab.&amp;quot;&amp;quot;&amp;quot; # 1. Initialize the App Lab environment and RPC Bridge arduino.init() logging.info(&amp;quot;Arduino UNO Q MPU Services Initialized.&amp;quot;) # 2. Register the callback function to listen for MCU notifications # This links the MPU function (process_mcu_telemetry) to the MCU&amp;#39;s push mechanism. arduino.on_notify(&amp;quot;telemetry_data_in&amp;quot;, process_mcu_telemetry) # 3. Start the main execution loop for the MPU # This command launches the Web UI, initializes Bricks, and keeps the RPC bridge active. logging.info(&amp;quot;Starting Web UI and waiting for MCU events...&amp;quot;) arduino.run() # CRITICAL: MUST be the final line. if __name__ == &amp;quot;__main__&amp;quot;: main() 3.1.2.1 Database BRICK Schema &amp;amp; Logging Pseudocode The Database BRICK manages the complexities of persistent storage, allowing the application logic to focus on security orchestration. The following pseudocode illustrates the data schema and the primary logging function used to record events. # ========================================================= # Database BRICK Schema &amp;amp; Logging Pseudocode # ========================================================= # Schema: { timestamp: ISO8601, event_type: STRING, source_node: STRING, payload: JSON } def log_forensic_event(event_type, source, details): &amp;quot;&amp;quot;&amp;quot;Formats and writes security telemetry to the Database BRICK.&amp;quot;&amp;quot;&amp;quot; record = { &amp;quot;timestamp&amp;quot;: get_network_time(), &amp;quot;event_type&amp;quot;: event_type, &amp;quot;source_node&amp;quot;: source, &amp;quot;payload&amp;quot;: details } # Synchronous write to ensure data persistence before proceeding try: db_brick.insert(record) print(f&amp;quot;Logged {event_type} from {source}&amp;quot;) except StorageError as e: critical_alert(f&amp;quot;Forensic Logging Failed: {e}&amp;quot;) 3.2 Edge Nodes Implementation 3.2.1 Edge Node UART Pseudocode (MAX32630FTHR) The Edge Nodes, built on the MAX32630FTHR, will run an Arduino-based C++ sketch to handle localized sensor readings, packetize the data with a unique NODEID, and transmit this telemetry over a hardware UART port to the Central Hub. // ========================================================= // Edge Node Pseudocode (MAX32630FTHR) // Role: Collect data, packetize, and transmit over UART to the Central Hub // ========================================================= #include #include // For hardware UART communication // --- Configuration --- // Define a unique identifier for this Edge Node instance // Node 1 (Biometrics &amp;amp; Sentiment) and Node 2 (Active Response) const String NODE_ID = &amp;quot;NODE_1&amp;quot;; // Define the hardware UART port used for the connection // NOTE: This assumes &amp;#39;Serial2&amp;#39; on the MAX32630FTHR is mapped to the physical pins // connected to the UNO Q&amp;#39;s Serial1 (TX -&amp;gt; RX, RX -&amp;gt; TX). HardwareSerial&amp;amp; CentralHubSerial = Serial2; const int BAUDRATE = 115200; // Must match the UNO Q&amp;#39;s receiving baud rate const char PACKET_DELIMITER = &amp;#39;\n&amp;#39;; // Newline is used to delimit messages // --- Core Logic Functions (SEND) --- /** * @brief Constructs a telemetry packet and transmits it over the UART backbone. * @param event_type: The type of event (e.g., BIOMETRIC, SENTRY, SENTIMENT). * @param payload: The data associated with the event. */ void SEND(String event_type, String payload) { // Packet Format: NODE_ID:EVENT_TYPE:PAYLOAD\n String data_packet = NODE_ID + &amp;quot;:&amp;quot; + event_type + &amp;quot;:&amp;quot; + payload; CentralHubSerial.print(data_packet); CentralHubSerial.write(PACKET_DELIMITER); // Send the newline delimiter // Optional: Echo to the MAX32630FTHR USB Serial for debugging Serial.println(&amp;quot;SENT: &amp;quot; + data_packet); } // --- Setup --- void setup() { // 1. Initialize USB Serial for local debugging/console output Serial.begin(115200); Serial.println(NODE_ID + &amp;quot; Initializing...&amp;quot;); // 2. Initialize the hardware UART port for the Central Hub connection CentralHubSerial.begin(BAUDRATE); Serial.println(&amp;quot;UART configured at &amp;quot; + String(BAUDRATE)); } // --- Main Loop (Data Generation and Transmission) --- void loop() { // --- Simulate Sensor Reading and Telemetry Generation --- // Example 1: Biometric Authentication Attempt (e.g., from Edge Node 1) if (millis() % 10000 512) ? &amp;quot;SUCCESS&amp;quot; : &amp;quot;FAIL&amp;quot;; // Simulated sensor check // This is the data string that the UNO Q MPU will log SEND(&amp;quot;BIOMETRIC_AUTH&amp;quot;, auth_status); } // Example 2: Sentiment Monitoring Update (e.g., from Edge Node 1) if (millis() % 5000 #include #include // Simulate ICLED control // --- Configuration --- const String NODE_ID = &amp;quot;NODE_1&amp;quot;; HardwareSerial&amp;amp; CentralHubSerial = Serial2; const int BAUDRATE = 115200; const char PACKET_DELIMITER = &amp;#39;\n&amp;#39;; const int SENTIMENT_PIN = A1; // Analog pin for stress/sentiment sensor const int ICLED_PIN = 6; // Pin for ICLED Display (NeoPixel) Adafruit_NeoPixel icled = Adafruit_NeoPixel(1, ICLED_PIN, NEO_GRB + NEO_KHZ800); // Helper function to transmit data void SEND(String event_type, String payload) { String data_packet = NODE_ID + &amp;quot;:&amp;quot; + event_type + &amp;quot;:&amp;quot; + payload; CentralHubSerial.print(data_packet); CentralHubSerial.write(PACKET_DELIMITER); } // --- Biometric Authentication Logic --- bool run_biometric_auth() { // 1. Simulate Face Recognition Check (e.g., via camera module) bool face_match = (analogRead(A0) &amp;gt; 800); // HIGH = Match delay(500); // Processing time // 2. Simulate Voice Passphrase Check (e.g., via microphone module) bool voice_match = (digitalRead(D7) == HIGH); // HIGH = Correct Passphrase delay(500); // Processing time // Dual-Factor Requirement: Both must pass bool access_granted = face_match &amp;amp;&amp;amp; voice_match; // Send telemetry to Central Hub if (access_granted) { SEND(&amp;quot;ACCESS_CONTROL&amp;quot;, &amp;quot;GRANTED&amp;quot;); } else { SEND(&amp;quot;ACCESS_CONTROL&amp;quot;, &amp;quot;DENIED_FACE:&amp;quot; + String(face_match) + &amp;quot;_VOICE:&amp;quot; + String(voice_match)); } return access_granted; } // --- Sentiment Monitoring Logic --- void update_sentiment_index() { int stress_level = analogRead(SENTIMENT_PIN); // Read simulated environmental stress String atmosphere_status; uint32_t icled_color; // Determine Atmosphere Index and corresponding ICLED color if (stress_level &amp;gt; 800) { atmosphere_status = &amp;quot;CRITICAL_RED&amp;quot;; icled_color = icled.Color(255, 0, 0); // Red } else if (stress_level &amp;gt; 400) { atmosphere_status = &amp;quot;ELEVATED_YELLOW&amp;quot;; icled_color = icled.Color(255, 255, 0); // Yellow } else { atmosphere_status = &amp;quot;NORMAL_GREEN&amp;quot;; icled_color = icled.Color(0, 255, 0); // Green } // Update ICLED Display icled.setPixelColor(0, icled_color); icled.show(); // Send telemetry to Central Hub SEND(&amp;quot;SENTIMENT_INDEX&amp;quot;, atmosphere_status); } void setup() { Serial.begin(115200); CentralHubSerial.begin(BAUDRATE); icled.begin(); // Initialize ICLED icled.show(); // Turn off LED initially } void loop() { // 1. Check for access attempt trigger (e.g., button press or proximity sensor) if (digitalRead(D5) == HIGH) { run_biometric_auth(); } // 2. Continuously update sentiment analysis every 2 seconds static unsigned long last_sentiment_update = 0; if (millis() - last_sentiment_update &amp;gt; 2000) { update_sentiment_index(); last_sentiment_update = millis(); } delay(10); } 3.2.3 Edge Node 2: Active Response &amp;amp; Surveillance Control Technical Implementation (MAX32630FTHR C++): This node is dedicated to automated threat mitigation, leveraging FeatherWings for motor and relay control. It continuously monitors for a breach trigger and, upon detection, executes a sequence to deploy a physical barrier and reorient the surveillance sensor, transmitting real-time status updates to the Central Hub. // ========================================================= // EDGE NODE 2 Pseudocode (MAX32630FTHR) // Role: Active Response &amp;amp; Surveillance Control // ========================================================= #include #include #include // Simulate FeatherWing for motor control // --- Configuration --- const String NODE_ID = &amp;quot;NODE_2&amp;quot;; HardwareSerial&amp;amp; CentralHubSerial = Serial2; const int BAUDRATE = 115200; const char PACKET_DELIMITER = &amp;#39;\n&amp;#39;; const int BREACH_SENSOR_PIN = D8; // Digital pin for breach detection // Initialize the motor shield and get motor objects Adafruit_MotorShield AFMS = Adafruit_MotorShield(); Adafruit_DCMotor *sentry_barrier_motor = AFMS.getMotor(1); Adafruit_StepperMotor *sensor_orient_stepper = AFMS.getStepper(200, 2); // Helper function to transmit data void SEND(String event_type, String payload) { String data_packet = NODE_ID + &amp;quot;:&amp;quot; + event_type + &amp;quot;:&amp;quot; + payload; CentralHubSerial.print(data_packet); CentralHubSerial.write(PACKET_DELIMITER); } // --- Active Response Logic --- void execute_sentry_response() { SEND(&amp;quot;SENTRY_STATUS&amp;quot;, &amp;quot;BREACH_DETECTED_INITIATING_RESPONSE&amp;quot;); sentry_barrier_motor-&amp;gt;setSpeed(255); sentry_barrier_motor-&amp;gt;run(FORWARD); delay(1500); sentry_barrier_motor-&amp;gt;run(RELEASE); sensor_orient_stepper-&amp;gt;setSpeed(50); sensor_orient_stepper-&amp;gt;step(50, FORWARD, SINGLE); SEND(&amp;quot;SENTRY_STATUS&amp;quot;, &amp;quot;RESPONSE_COMPLETE:BARRIER_DEPLOYED_SENSOR_ORIENTED&amp;quot;); } void setup() { Serial.begin(115200); CentralHubSerial.begin(BAUDRATE); AFMS.begin(); pinMode(BREACH_SENSOR_PIN, INPUT_PULLUP); Serial.println(NODE_ID + &amp;quot; Active Response Sentry Initialized.&amp;quot;); } void loop() { if (digitalRead(BREACH_SENSOR_PIN) == LOW) { static bool is_breach_active = false; if (!is_breach_active) { is_breach_active = true; execute_sentry_response(); } } else { // Breach cleared retraction logic... SEND(&amp;quot;SENTRY_STATUS&amp;quot;, &amp;quot;ALL_CLEAR_SYSTEM_RESET&amp;quot;); } delay(50); } 4. UART Interconnect The physical layer of the Adaptive Sentinel utilizes a wired UART backbone to ensure high reliability and deterministic communication between the distributed Edge Nodes and the Central Hub. This infrastructure bypasses the potential latency and interference issues of wireless environments, providing a stable foundation for real-time security telemetry. The connectivity is established via point-to-point UART links. Specifically, the TX pins of Edge Node 1 and Edge Node 2 are connected to the RX pins of the Arduino UNO Q&amp;#39;s MCU (Serial1), operating at a baud rate of 115200 to facilitate unidirectional data streaming from the frontline sensors to the primary coordinator. UART Topology Diagram (Unidirectional Flow) [ Edge Node 1: MAX32630FTHR ] --(TX)---&amp;gt; [ Serial1: RX ] ^ | [ Central Hub ] [ Arduino UNO Q ] ^ | [ Edge Node 2: MAX32630FTHR ] --(TX)----------| 4.1 implementation and test The physical layer establishes a reliable data uplink by connecting the Arduino UNO Q MCU Serial1 RX pin to the MAX32630FTHR ( TX ) pin. Integration is further enhanced via the Particle Ethernet FeatherWing , enabling deterministic network orchestration and high-reliability communication for the MAX32630FTHR edge node. The Code MCU Telemetry Receiver (C++: sketch.ino) The Arduino UNO Q features a dual-processor architecture where the MCU (STM32U585) is specifically tasked with handling real-time, interrupt-driven communication. In your Adaptive Sentinel architecture, the MCU acts as the primary receiver for telemetry data streamed from the Edge Nodes via a wired UART backbone. The following code is a simple RX (Receive) example designed for the Arduino UNO Q MCU to listen for incoming data on its dedicated UART port ( Serial1 ) and forward that information to the Linux-based MPU for logging. MCU Telemetry Receiver (C++: sketch.ino) // ========================================================= // MCU RX Example (C++ - sketch.ino) // Role: Receive UART telemetry and forward to MPU via Bridge // ========================================================= #include #include // RPC Bridge library for MPU comms #include // Dedicated UART port connecting to Edge Node TX pins HardwareSerial&amp;amp; EdgeNodeSerial = Serial1; const int BAUDRATE = 115200; void setup() { // 1. Initialize UART for Edge Nodes (RX logic) // Connect Edge Node TX to UNO Q MCU Serial1 RX pin EdgeNodeSerial.begin(BAUDRATE); // 2. Initialize the RPC Bridge for MPU communication Bridge.begin(); // 3. Status indicator for health checks Bridge.provide(&amp;quot;get_mcu_status&amp;quot;, []() { return &amp;quot;RX_READY&amp;quot;; }); } void loop() { // 1. Check if data is available on the UART RX line if (EdgeNodeSerial.available()) { // 2. Read the telemetry packet until the newline delimiter (&amp;#39;\n&amp;#39;) // Standard format used by Edge Nodes: &amp;quot;NODE_ID:EVENT_TYPE:PAYLOAD&amp;quot; String data_packet = EdgeNodeSerial.readStringUntil(&amp;#39;\n&amp;#39;); if (data_packet.length() &amp;gt; 0) { // 3. Forward the data to the MPU using &amp;#39;Notify&amp;#39; // This is non-blocking, ensuring the MCU stays responsive for real-time tasks. Bridge.notify(&amp;quot;telemetry_data_in&amp;quot;, data_packet.c_str()); } } // Small delay to maintain deterministic behavior and prevent buffer flooding delay(10); } Critical Integration Notes Hardware Mapping : The TX pins of your MAX32630FTHR Edge Nodes must be physically connected to the Serial1 RX pin on the Arduino UNO Q MCU. Baud Rate : Ensure the baud rate is set to 115200 on both the Edge Node and the MCU to prevent data corruption. MPU Requirement : For this code to be useful, the MPU must have a corresponding listener registered (e.g., arduino.on_notify(&amp;quot;telemetry_data_in&amp;quot;, callback) ) to process the incoming strings. Edge Node UART Transmitter (C++: sketch.ino) The following C++ code is a simple TX (Transmit) example for the MAX32630FTHR Edge Node. It is designed to packetize data and stream it over the hardware UART backbone to the Arduino UNO Q Central Hub. In your Adaptive Sentinel architecture, this code assumes the Serial2 port on the MAX32630FTHR is physically connected to the Serial1 RX pin on the Arduino UNO Q. Edge Node UART Transmitter (C++: sketch.ino) // ========================================================= // Edge Node TX Example (C++ - sketch.ino) // Role: Collect data and transmit over UART to the Central Hub // ========================================================= #include #include // --- Configuration --- // Define a unique identifier for this Edge Node instance const String NODE_ID = &amp;quot;NODE_1&amp;quot;; // Mapping Serial2 to the physical TX pins connected to the UNO Q Hub HardwareSerial&amp;amp; CentralHubSerial = Serial2; const int BAUDRATE = 115200; const char PACKET_DELIMITER = &amp;#39;\n&amp;#39;; /** * @brief Constructs a telemetry packet and transmits it over the UART backbone. */ void SEND(String event_type, String payload) { // Packet Format: NODE_ID:EVENT_TYPE:PAYLOAD\n String data_packet = NODE_ID + &amp;quot;:&amp;quot; + event_type + &amp;quot;:&amp;quot; + payload; CentralHubSerial.print(data_packet); CentralHubSerial.write(PACKET_DELIMITER); // Debug output to local USB console Serial.println(&amp;quot;SENT: &amp;quot; + data_packet); } void setup() { // 1. Initialize USB Serial for local monitoring Serial.begin(115200); // 2. Initialize the hardware UART port for the Central Hub connection CentralHubSerial.begin(BAUDRATE); Serial.println(NODE_ID + &amp;quot; UART Transmitter Initialized.&amp;quot;); } void loop() { // Example: Periodically simulate and send telemetry every 5 seconds static unsigned long last_send = 0; if (millis() - last_send &amp;gt; 5000) { // Simulate a sensor reading (e.g., Biometric status) String sensor_data = (analogRead(A0) &amp;gt; 512) ? &amp;quot;SUCCESS&amp;quot; : &amp;quot;FAIL&amp;quot;; // Transmit the packetized data SEND(&amp;quot;BIOMETRIC_AUTH&amp;quot;, sensor_data); last_send = millis(); } } Essential Setup Details Hardware Interface: Ensure the TX pin (UART 2) of your MAX32630FTHR is cross-connected to the RX pin (Serial1) of the Arduino UNO Q. Baud Rate Alignment: The baud rate is strictly set to 115200 to match the Central Hub&amp;#39;s receiver configuration. Power Management: If running on a LiPo battery, you must press the onboard power/reset button for a half-second to wake the PMIC. 5. Prototype Detailed Build and Demonstration This section transitions the engineering workflow from component implementation to the final physical assembly and operational validation of the Adaptive Sentinel prototype. Building upon the established firmware and hardware infrastructures, this phase documents the comprehensive mechanical and electrical integration of the central Arduino UNO Q intelligence hub with the distributed Analog Devices MAX32630FTHR edge nodes. Moving beyond static construction, this section also details the live demonstration protocols executed to verify the system&amp;#39;s real-time efficacy. By observing the fully assembled hub-and-node topology under simulated deployment conditions, we empirically validate the system&amp;#39;s core capabilities—including uninterrupted data telemetry, responsive active barrier actuation, and reliable environmental monitoring—confirming the framework&amp;#39;s readiness as a cohesive, field-deployable security solution. 5.1 Central HUB Build: Hardware Interconnect The Central Coordination Hub, centered on the Arduino UNO Q , acts as the data aggregator and high-level logic controller. Its primary physical interfaces are the two UART connections receiving telemetry from the Edge Nodes. The dual-processor architecture means the MCU handles the low-level UART interrupts, while the MPU (Linux side) manages the Database BRICK. The Arduino UNO Q interfaces with the following components: Edge Node 1 UART Data Uplink: Receives one-way telemetry stream. Connection: MCU Serial1 RX Pin. Edge Node 2 UART Data Uplink: Receives one-way telemetry stream. Connection: MCU Serial1 RX Pin (in a multi-point topology via the UART backbone). Database Brick: Used for logging and high-level data aggregation. Central Hub Wiring Diagram (Conceptual Interconnect) [ Arduino UNO Q ] /-------------------\ Edge Node 1 TX ---- | MCU Serial1 RX | | | Edge Node 2 TX ---- | MCU Serial1 RX | | | | | Database BRICK &amp;lt;---| MPU (Internal) | \-------------------/ 5.4 Prototype Demonstration: Final Assembly TODO This section documents the final physical integration of the Adaptive Sentinel prototype, which comprises three major subsystems: the Arduino UNO Q Central Hub, and two distributed Analog Devices MAX32630FTHR edge nodes (Biometric Access and Active Response). To ensure system reliability, a modular assembly methodology was utilized. Each of the three subsystems was first constructed and functionally verified in strict isolation. Following successful standalone validation, the UART communication backbone connecting the hub to the edge nodes was established and tested for stability. Only after confirming uninterrupted UART telemetry were the discrete components synthesized into the final, unified assembly demonstrated below. 5.4.1 Central Intelligence Hub Validation (Insert) [Photo: Adaptive Sentinel Central Hub (Arduino UNO Q) Final Build] Hardware Integration &amp;amp; Enclosure: Detail the physical mounting of the Arduino UNO Q. Document the power distribution and regulation strategy utilized for the main hub. Highlight the physical wiring harness mapping the UART communication lines out to the distributed edge nodes. Dual-Processor Verification: MCU Operation: Describe the real-time processing of incoming telemetry and interrupt handling from Nodes 1 and 2. MPU Operation: Detail the status of the Linux-hosted Web UI and Database BRICK during the demonstration (e.g., confirming active data logging and dashboard updates). Empirical Observation: Note the baseline system stability, boot times, and resting current draw of the hub. 5.4.2 Edge Node 1: Biometric Access &amp;amp; Authentication (Insert) [Photo: Edge Node 1 Biometric Verification Test] Peripheral Integration: Detail the wiring and interface protocols (I2C/SPI) connecting the biometric sensor payload to the Analog Devices MAX32630FTHR. Document any specific power considerations for the biometric sensors. Authentication Pipeline Demonstration: Describe the sequence of events during a live authentication test: Sensor activation $\rightarrow$ Data acquisition $\rightarrow$ Local processing on the MAX32630FTHR. Performance Metrics: Quantify the algorithmic processing overhead on the edge node. Measure and report the transmission latency—the time it takes from a successful/failed local verification to the state update registering on the UNO Q Central Hub. 5.4.3 Edge Node 2: Environmental Monitoring and Active Actuation (Insert) [Photo: Edge Node 2 (Active Response): ADI MAX32630FTHR, DC Motor (Sentry Barrier), Stepper Motor (Sensor Orientation), and Breach Detection sensors.] Electro-Mechanical Integration: Detail the interfacing of the high-current motor drivers with the MAX32630FTHR logic pins. Explain the power isolation strategy preventing DC/Stepper motor noise from interfering with the microcontroller and sensitive breach sensors. Demonstration of Sequential Logic: State 1 (Scanning): Describe the Stepper Motor sweeping protocol for sensor orientation. State 2 (Detection): Explain the exact trigger conditions registered by the Breach Detection sensors. State 3 (Actuation): Document the subsequent firing of the DC Motor to deploy the Sentry Barrier. Performance Metrics: Record the mechanical actuation response time (latency from breach detection to full barrier deployment). Confirm the successful transmission of the &amp;quot;Breach Event&amp;quot; interrupt signal back to the UNO Q Central Hub. 5.2 Edge Node 1 Build: Hardware Interconnect Edge Node 1 is the primary biometric and sentiment hub, utilizing the MAX32630FTHR to run the core logic and interface with the peripheral sensors. Since the kit components often lack stackable headers, this connection strategy assumes the use of a breadboard and jumper wires (as discussed in the documentation) to establish connections based on the defined pseudocode pin assignments. The MAX32630FTHR interfaces with the following components: ICLED Display (Wurth Elektronik 150015): Used for the color-coded &amp;quot;Atmosphere Index&amp;quot;. This featherwing display is controlled by a single digital pin. Connection: Digital Pin D6 Camera Module (Simulated Face Recognition): This is a conceptual module for face data input, simulated by reading an analog value for authentication success. Connection: Analog Pin A0 Microphone Module (Simulated Voice Passphrase): This module is required for the dual-factor authentication process, simulated via a digital input. Connection: Digital Pin D7 UART Data Uplink: The primary data link for streaming telemetry to the Central Hub. Connection: Dedicated UART 2 ( Serial2 ) TX Pin. Edge Node 1 Wiring Diagram (Conceptual Interconnect) [ MAX32630FTHR ] /---------------\ ICLED Display ---| D6 |--- GND (IC2/NeoPixel) | | | D7 -----------|--- Microphone (Digital Input) | A0 -----------|--- Camera (Analog Input) | | | VCC/3.3V -----|--- All VCC/3.3V Lines | GND ----------|--- All GND Lines | | To Central Hub &amp;lt;--| TX2 (UART 2) | \---------------/ 5.3 Edge Node 2 Build: Hardware Interconnect Edge Node 2 is the dedicated Active Response subsystem, utilizing the MAX32630FTHR to implement the threat mitigation logic. Since a dedicated physical breach sensor is unavailable, a Simulated Breach Trigger (Push Button) is used to initiate the active response sequence. When the push button on digital pin D8 is pressed, it triggers the automated deployment of the DC Motor (Sentry Barrier) and the orientation of the Stepper Motor. The MAX32630FTHR interfaces with the following components: Simulated Breach Trigger (Push Button): Digital Pin D8 (Input_Pullup), used to simulate a breach event as defined in the pseudocode. Adafruit ADA2927 DC Motor + Stepper FeatherWing (I2C): This required motor shield connects to the MAX32630FTHR via the I2C interface (SDA/SCL) and power (VCC/GND) using jumper wires and a breadboard, as direct stacking is not possible. DC Motor (Sentry Barrier): Connects to Motor Port M1 on the shield. Stepper Motor (Sensor Orientation): Connects to Motor Port M2 on the shield. UART Data Uplink: The primary data link for streaming telemetry to the Central Hub. Connection: Dedicated UART 2 ( Serial2 ) TX Pin. Edge Node 2 Wiring Diagram (Conceptual Interconnect) [ MAX32630FTHR ] /---------------\ Breach Sensor----| D8 |--- GND (Input) | | Jumper Wires ---| SDA/SCL ------|--- I2C (To ADA2927) Jumper Wires ---| VCC/3.3V -----|--- Power (To ADA2927) Jumper Wires ---| GND ----------|--- Ground (To ADA2927) | | To Central Hub &amp;lt;--| TX2 (UART 2) | \---------------/ | [ Jumper Wires / Breadboard ] | [ Adafruit ADA2927 FeatherWing ] /---------------\ DC Motor (M1) &amp;lt;----| Motor Port 1 | (Sentry Barrier) Stepper Motor (M2) &amp;lt;-| Motor Port 2 | (Sensor Orientation) \---------------/ 6.. Conclusion and Future Work 6.1 Final Outcome and Evaluation The Adaptive Sentinel successfully integrates multi-modal biometrics and active sentry response into a cohesive hub-to-node security framework. The system demonstrated reliable edge-based intelligence with minimal latency during localized threat detection and biometric verification. The integration of the Database BRICK proved critical for preserving forensic telemetry during simulated network disruptions, confirming system resilience. User evaluation of the ICLED Display’s &amp;quot;Atmosphere Index&amp;quot; indicated high intuitive value for security personnel in assessing environmental stress levels at a glance. 6.2 Future Improvements and Scalability Wireless Connectivity Upgrade: Transitioning from the current wired UART infrastructure to high-bandwidth WiFi connectivity using the AirLift FeatherWing or ESP32-based nodes to enable true distributed deployment without physical tethering. Cloud Integration: Implementing MQTT protocols for real-time remote monitoring and secondary cloud-based data backup for historical sentiment analysis. Advanced Power Management: Optimizing the MAX32630FTHR low-power modes for battery-operated wireless nodes to extend operational lifespan in remote zones.</description></item></channel></rss>