← All Projects
Parametric Retrofit of Lab Equipment for Remote Control
April 2026
ESP32
Ubuntu
KiCad
SolidWorks
MATLAB
Simulink
Raspberry Pi
MQTT
Django
WebRTC
SRT
Python
C++
nginx
Oracle Cloud
University control systems labs depend on expensive, specialized equipment that students can only access during scheduled hours, on campus, in person. This project set out to change that by taking a physical Feedback Instruments inverted pendulum cart apparatus at Florida Atlantic University and making it fully operable from anywhere in the world through a web browser.
The original system relied on a proprietary analog control box connected to a Windows PC via a PCI-1711 DAQ card and 62-pin SCSI ribbon cables. The control box had failed, and replacement parts were no longer available. Rather than sourcing another aging proprietary system, the team designed and built a completely new control architecture from scratch using modern, affordable, open-source-friendly components.
The new system replaces the entire original control box with two ESP32 microcontrollers, two Raspberry Pi single-board computers, a custom PCB, a laser-cut enclosure, and a cloud-hosted web application. The first ESP32 reads quadrature encoders from the motor and pendulum at nearly 1,000 Hz using hardware pulse counters and transmits the data to a Raspberry Pi 5 over SPI. The second ESP32 drives the DC motor through a BTS7960 H-bridge, manages physical limit switches, and executes a multi-step homing routine to calibrate the cart's position at startup. A Raspberry Pi 5 runs a standalone Simulink executable that implements the control algorithm, reads encoder data via SPI, and outputs motor voltage through an I2C DAC. A second Raspberry Pi streams live video of the apparatus to the web interface using hardware-accelerated encoding and WebRTC for low-latency playback.
The web interface is a Django application hosted on an Oracle Cloud virtual machine. Users log in, start an experiment, and watch the pendulum respond in real time through an embedded video feed and live data graphs. All commands travel between the browser and the lab hardware over MQTT, a lightweight publish-subscribe messaging protocol. The entire pipeline, from a user clicking "Start" in their browser to the motor physically moving, is designed to be as close to real-time as the network allows.
The result is a working proof of concept that a single piece of legacy lab equipment can be transformed into a shared, remotely accessible resource using off-the-shelf hardware and open-source software, at a fraction of the cost of purpose-built remote lab platforms.
My Role
Hardware and software co-design lead. Responsible for all firmware development across both ESP32 microcontrollers, the custom PCB schematic and layout in KiCad, the SolidWorks enclosure design, and full system integration between the embedded hardware and the Raspberry Pi control stack. Diagnosed and resolved the original control box failure, led the iterative architecture redesign from the initial parallel bus approach through to the final SPI-based pipeline, and coordinated hardware-software interfaces across all four team members' subsystems.
Team
Skylar Gutman : Hardware/software co-design, ESP32 firmware, PCB design, enclosure, system integration
Bruce Fong : Raspberry Pi integration, Simulink Coder deployment, SPI encoder pipeline, MQTT connectivity
Evan King : Web interface, Django application, Oracle Cloud infrastructure, nginx/gunicorn deployment
Angel Diaz-Ramirez : Video streaming, camera hardware, FFmpeg/WebRTC pipeline
Institution
Florida Atlantic University, Department of Electrical Engineering and Computer Science (Senior Design, 2025-2026)
Tech Stack
Microcontrollers and Firmware
ESP32 NodeMCU-32S (x2): Dual microcontroller architecture with dedicated roles
Arduino IDE with ESP-IDF v5 Core: Firmware development environment
ESP32 PCNT (Pulse Counter) Hardware: Hardware-level quadrature decoding for both motor and pendulum encoders
ESP-IDF SPI Slave Driver: Raw IDF v5 SPI slave implementation with DMA-aligned 12-byte packets, FreeRTOS semaphore synchronization from post-transaction ISR, running at ~984 Hz
IBT-2 / BTS7960 H-Bridge: Dual half-bridge motor driver for bidirectional DC motor control
UART2: Inter-ESP32 communication for homing coordination (0xAA completion signal)
Single-Board Computers
Raspberry Pi 5: Primary control computer running Simulink standalone executables, DAC daemon, and MQTT client
Raspberry Pi 4 (Model B): Dedicated camera streaming unit with OV5647 sensor module
Raspberry Pi 2 (Model B): Backup camera unit with USB webcam fallback
Control System
MATLAB/Simulink R2023b: Control algorithm development environment
Simulink Coder: Cross-compilation of Simulink models to standalone ARM Linux executables (.elf)
Raspberry Pi Support Package for Simulink: Hardware target configuration and deployment
MCP4725 DAC: 12-bit I2C digital-to-analog converter for motor voltage output (bus 1, address 0x60, calibrated Vref 4.83V)
ADS1115 ADC: 16-bit I2C analog-to-digital converter for sensor feedback
Custom PCB
KiCad: Full schematic capture and PCB layout
Components on board: Two ESP32 NodeMCU-32S sockets, MCP4725 DAC, ADS1115 ADC, TCA9555/MCP23017 GPIO expanders, BTS7960 H-bridge driver, TXS0108E bidirectional level shifter, passive components (pull-ups, decoupling capacitors, debounce capacitors)
Enclosure
SolidWorks: 3D CAD modeling with box joint panel design
Boxes.py: Parametric finger joint DXF generation
Deepnest: Automated nesting of DXF panels for laser cutting
LightBurn: Laser cutter toolpath and job control software
Web and Cloud
Django (Python): Web application framework serving the user interface
nginx: Reverse proxy and static file server
gunicorn: WSGI application server for Django
Oracle Cloud Free Tier: Always-on virtual machine (tenancy kingevan311, region us-ashburn-1)
Mosquitto MQTT Broker: Lightweight publish-subscribe messaging (sciencelabtoyou.com:1885)
MySQL: Database backend for user authentication and experiment logging
Video Streaming
OV5647 Camera Module: 5MP camera sensor on Raspberry Pi 4
FFmpeg: Hardware-accelerated video encoding pipeline
mediamtx v1.17.0: Media server handling SRT ingest (port 8890) and WebRTC output (ports 8889/8189)
WebRTC via WHEP: Browser-native low-latency video playback (~1 second latency on Pi 4)
SRT (Secure Reliable Transport): Ingest protocol from Pi to cloud server
Networking and Remote Access
MQTT Protocol: Command and telemetry transport (topics: pendulum/cmd, pendulum/status)
autossh Reverse SSH Tunnels: Persistent remote access through FAU's firewall (Pi 4 on port 2222, control Pi on port 2223)
systemd Services: Auto-start and watchdog restart for streaming, tunnels, and DAC daemon
Development and Test Equipment
Tektronix MDO3024 Oscilloscope: Signal verification and timing analysis
P6316 Logic Analyzer Probe: Multi-channel digital protocol debugging
GitHub: Version control (skylargutman/picontrol, skylargutman/ST7735_PI4)
SSH (ed25519 keys): Secure access to all Pi units and Oracle Cloud server
Key Technical Challenges
Encoder Signal Corruption ("The 255 Glitch")
The original architecture attempted to read encoder data through MCP23017 GPIO expanders connected to the PCI-1711 DAQ card's parallel bus. This approach suffered from persistent data corruption: encoder values would spike to 255 due to pull-up resistors on the original control board, reversed port wiring, and byte-order mismatches. Months of debugging, including oscilloscope measurements and physical isolation tests, eventually traced the root cause to electrical interference on the legacy board. The solution was to bypass the original control board entirely and route encoder signals directly to the ESP32's hardware pulse counters, eliminating the parallel bus architecture altogether.
ESP32 SPI Slave on IDF v5
Implementing SPI slave communication on the ESP32 required navigating breaking API changes between ESP-IDF v4 and v5. The commonly referenced VSPI_HOST constant was deprecated, the hideakitai ESP32SPISlave library's async trigger/wait pattern caused watchdog crashes, and strapping pin GPIO 12 caused boot failures when used for MISO. The final working implementation uses the raw IDF v5 SPI slave driver on SPI2_HOST, custom pin mapping (MISO on GPIO 25), SPI mode 1 for DMA compatibility, and a FreeRTOS semaphore signaled from a post-transaction ISR callback. The 12-byte packet size was chosen specifically to satisfy the ESP32's 4-byte DMA alignment requirement.
Simulink Code Generation Constraints
Deploying Simulink models as standalone executables for the Raspberry Pi revealed several functions that are unavailable for code generation. The get_param function, commonly used in desktop Simulink, has no Coder equivalent and had to be replaced with input ports fed by Constant blocks. The typecast function was also unsupported, requiring bitshift operations with int32 casting to unpack signed encoder values from the SPI byte stream. The MCP4725 DAC integration required a Rate Transition block to establish an execution rate, since a Constant block feeding an I2C write with no explicit sample time never fires at runtime.
Video Streaming Through a University Firewall
FAU's network firewall blocks inbound connections, making direct camera streaming impossible. The solution uses reverse SSH tunnels via autossh to maintain persistent outbound connections from the Pi to the Oracle Cloud server. The initial RTSP-based streaming approach added 6 seconds of latency due to the tunnel overhead. Switching to SRT ingest with mediamtx as the relay server and WebRTC (via WHEP) for browser playback brought latency down to approximately 1 second.
CS Toggling and SPI Display Issues
An early attempt to drive an ST7735R LCD display from the Raspberry Pi's kernel SPI driver failed because the driver toggles the chip select line between every transfer, which resets the display controller's GRAM write pointer. The fix required fully bit-banged SPI with manual chip select control, along with the discovery that the ST7735R expects BGR565 pixel format rather than RGB565.
System Architecture
Browser (ScienceLabToYou.com)
|
| HTTPS / WebRTC (WHEP)
v
Oracle Cloud VM (Django + nginx + gunicorn + mediamtx + Mosquitto)
|
| MQTT (port 1885) SRT (port 8890)
| Reverse SSH (2222, 2223)
v v
Raspberry Pi 5 (Control) Raspberry Pi 4 (Camera)
| |
| SPI (~984 Hz) OV5647 Camera Module
| I2C (MCP4725 DAC) + FFmpeg HW Encoding
| GPIO (homing, stop)
v
ESP32 #1 (Encoder) <--UART2--> ESP32 #2 (Motor)
| |
| PCNT Hardware | BTS7960 H-Bridge
v v
Motor Encoder DC Motor + Limit Switches
Pendulum Encoder
Links
Live Site: ScienceLabToYou.com
Source Code: github.com/skylargutman/picontrol
Status
Senior Design capstone project, Florida Atlantic University, 2025-2026. Core hardware and firmware stack is built and functional. End-to-end integration testing, web UI polish, and final user experience validation are in progress.