From 24c345e1514fba3b601ac48093c41b7510e5a813 Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 7 Mar 2026 15:00:01 -0800 Subject: [PATCH 1/3] Added README.md --- README.md | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..c7764259 --- /dev/null +++ b/README.md @@ -0,0 +1,133 @@ +# Panorama + +A telemetry visualization client for robotics sensor data, developed by [UBC Open Robotics](https://ubcopenrobotics.ca). Panorama receives a live JSON stream from an ESP32 (or a mock Python server), and displays it in a GUI with real-time graphs and data logging. + +## Table of Contents + +- [Architecture](#architecture) +- [Prerequisites](#prerequisites) +- [Build & Run](#build--run) +- [JSON Data Format](#json-data-format) +- [Firmware](#firmware) +- [Project Structure](#project-structure) +- [CI](#ci) + +## Architecture + +``` +ESP32 Firmware Panorama Client (C++ / wxWidgets) +───────────── ────────────────────────────────── +Sensor data → TCP Client → Message Model → GUI / Graphs +(JSON over TCP) (port 3000) DataBuffer Data Logger +``` + +- **Client** (`client/`) — C++20 desktop app built with wxWidgets and wxMathPlot +- **Firmware** (`firmware/`) — ESP32 Arduino firmware that streams NDJSON sensor data over TCP (port 9000) +- **Tools** (`tools/`) — Python mock server and test utilities for development without hardware + +## Prerequisites + +### Client + +| Platform | Requirements | +|----------|-------------| +| Linux | `cmake >= 3.20`, `g++ (C++20)`, wxWidgets (`wx-config` on PATH) | +| macOS | `cmake >= 3.20`, `clang (C++20)`, wxWidgets (`wx-config` on PATH) | +| Windows | `cmake >= 3.20`, MSVC or MinGW, wxWidgets (set `WXWIN` environment variable) | + +**Install wxWidgets (Linux/macOS):** +```bash +# Via the provided build script (recommended): +bash scripts/wxbuild.sh + +# ** Other Install Methods *** +# Via Homebrew (macOS): +brew install wxwidgets + +# Via apt (Ubuntu/Debian): +sudo apt-get install libwxgtk3.2-dev +``` + +### Firmware + +- [PlatformIO](https://platformio.org/) CLI or IDE extension +- ESP32 dev board + +## Build & Run + +Source the environment script once per shell session to enable shorthand commands: + +```bash +source scripts/env.sh +``` + +| Command | Description | +|------------|------------------------------------------| +| `build` | Configure and build the project | +| `run` | Build (if needed) and launch the client | +| `clean` | Remove the `build/` directory | +| `runtests` | Build in Release mode and run CTest | + +Or use the scripts directly: + +```bash +# Build +bash scripts/build.sh [Debug|Release] + +# Run (connects to ESP32 at 127.0.0.1:3000) +bash scripts/run.sh + +# Run with Python mock server instead of ESP32 +bash scripts/run.sh -noesp # OR: -pserver + +# Run in headless/console mode +bash scripts/run.sh -nogui + +# Run tests +bash scripts/runtests.sh +``` + +## JSON Data Format + +The client expects newline-delimited JSON (NDJSON). Each line is one sensor reading: + +```json +{"datatype": "temperature", "data": 29.5, "dataunit": "celsius", "timestamp": 1770504589} +{"datatype": "light", "data": 0.2, "dataunit": "nm", "timestamp": 1770504589} +``` + +## Firmware + +The ESP32 firmware creates a WiFi Access Point and streams sensor data over TCP. + +**Flash with PlatformIO:** +```bash +cd firmware/panorama +pio run --target upload +pio device monitor +``` + +Update `upload_port` and `monitor_port` in `firmware/panorama/platformio.ini` to match your serial port (e.g., `/dev/ttyUSB0` on Linux, `COM3` on Windows). + +## Project Structure + +``` +panorama/ +├── client/ +│ ├── src/ # C++ source files +│ ├── include/ +│ │ ├── client/ # Client-specific headers +│ │ └── common/ # Shared headers (panorama_defines, panorama_utils) +│ └── external/ # wxMathPlot (bundled) +├── firmware/ +│ └── panorama/ # PlatformIO ESP32 project +├── scripts/ # Build, run, test, and wxWidgets install scripts +├── tests/ # CTest unit tests +├── tools/ # Python mock server and dev utilities +├── rundir/ # Runtime data directory (config, logs, data) +└── doc/ # Documentation +``` + +## CI + +CI runs on pull requests to `main` via GitHub Actions (Ubuntu). Pull requests prefixed with `WIP` skip CI automatically. From 8bcbaf740156ff89ab9cb942d674b26b204ba107 Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 7 Mar 2026 15:05:38 -0800 Subject: [PATCH 2/3] Removed Windows build options --- client/CMakeLists.txt | 90 +----------------- scripts/wxbuild_msvc.ps1 | 195 --------------------------------------- 2 files changed, 4 insertions(+), 281 deletions(-) delete mode 100644 scripts/wxbuild_msvc.ps1 diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index f687f60e..2c27a0eb 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -9,9 +9,7 @@ set(WXMATHPLOT_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) add_subdirectory(external/wxMathPlot_0.2.0/mathplot) # Executable settings -if(WIN32) - add_executable(panorama-client WIN32 ${CLIENT_SOURCES} ${COMMON_HEADERS} ${CLIENT_HEADERS}) -elseif(APPLE) +if(APPLE) add_executable(panorama-client MACOSX_BUNDLE ${CLIENT_SOURCES} ${COMMON_HEADERS} ${CLIENT_HEADERS}) else() add_executable(panorama-client ${CLIENT_SOURCES} ${COMMON_HEADERS} ${CLIENT_HEADERS}) @@ -22,15 +20,9 @@ target_include_directories(panorama-client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ) -if(WIN32) - target_link_libraries(panorama-client PRIVATE ws2_32) -endif() - # wxWidgets Configuration -if(WIN32) - set(WXWIDGETS_PREBUILT_PATH "$ENV{WXWIN}") -elseif(APPLE) +if(APPLE) set(WXWIDGETS_PREBUILT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/third_party/wxwidgets-prebuilt/macos-arm64") else() set(WXWIDGETS_PREBUILT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/third_party/wxwidgets-prebuilt/linux-x64") @@ -40,77 +32,7 @@ set(wxWidgets_ROOT_DIR "${WXWIDGETS_PREBUILT_PATH}") set(wxWidgets_LIB_DIR "${WXWIDGETS_PREBUILT_PATH}/lib") set(wxWidgets_INCLUDE_DIR "${WXWIDGETS_PREBUILT_PATH}/include") -if(WIN32) - # Detect if using MinGW or MSVC - if(MINGW) - message(STATUS "Configuring for MinGW build") - - target_include_directories(panorama-client PRIVATE - "${wxWidgets_INCLUDE_DIR}" - "${WXWIDGETS_PREBUILT_PATH}/lib/wx/include/x86_64-w64-mingw32-msw-unicode-static-3.3" - ) - - target_link_libraries(panorama-client PRIVATE - "${wxWidgets_LIB_DIR}/libwx_mswu-3.3-x86_64-w64-mingw32.a" - "${wxWidgets_LIB_DIR}/libwxregexu-3.3-x86_64-w64-mingw32.a" - "${wxWidgets_LIB_DIR}/libwxexpat-3.3-x86_64-w64-mingw32.a" - "${wxWidgets_LIB_DIR}/libwxtiff-3.3-x86_64-w64-mingw32.a" - "${wxWidgets_LIB_DIR}/libwxjpeg-3.3-x86_64-w64-mingw32.a" - "${wxWidgets_LIB_DIR}/libwxpng-3.3-x86_64-w64-mingw32.a" - "${wxWidgets_LIB_DIR}/libwxzlib-3.3-x86_64-w64-mingw32.a" - # Windows system libraries - -lkernel32 -luser32 -lgdi32 -lcomdlg32 -lwinspool -lwinmm -lshell32 - -lshlwapi -lcomctl32 -lole32 -loleaut32 -luuid -lrpcrt4 -ladvapi32 - -lversion -lws2_32 -lwininet -loleacc -luxtheme - ) - - target_compile_definitions(panorama-client PRIVATE - __WXMSW__ - WXUSINGDLL=0 - _UNICODE - UNICODE - ) - - elseif(MSVC) - message(STATUS "Configuring for MSVC build") - - target_include_directories(panorama-client PRIVATE - "${wxWidgets_INCLUDE_DIR}" - "${wxWidgets_LIB_DIR}/vc_x64_lib/mswu" - ) - - # Find all wxWidgets libraries in the lib directory - file(GLOB WX_LIBS "${wxWidgets_LIB_DIR}/vc_x64_lib/wx*.lib") - - if(NOT WX_LIBS) - message(FATAL_ERROR "No wxWidgets libraries found in ${wxWidgets_LIB_DIR}") - endif() - - message(STATUS "Found wxWidgets libraries:") - foreach(lib ${WX_LIBS}) - get_filename_component(lib_name ${lib} NAME) - message(STATUS " ${lib_name}") - endforeach() - - target_link_libraries(panorama-client PRIVATE - ${WX_LIBS} - # Windows system libraries - kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib - shell32.lib shlwapi.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib - rpcrt4.lib advapi32.lib version.lib ws2_32.lib wininet.lib oleacc.lib - uxtheme.lib gdiplus.lib - ) - - target_compile_definitions(panorama-client PRIVATE - __WXMSW__ - _UNICODE - UNICODE - ) - endif() - - message(STATUS "Using wxWidgets prebuilt from ${WXWIDGETS_PREBUILT_PATH}") - -elseif(APPLE) +if(APPLE) # macOS: Use system-installed wxWidgets from /usr/local find_program(wxWidgets_CONFIG_EXECUTABLE wx-config HINTS /usr/local/bin @@ -183,11 +105,7 @@ else() # linux message(STATUS "Using locally built wxWidgets via wx-config: ${wxWidgets_CONFIG_EXECUTABLE}") endif() -if(MSVC) - target_compile_options(panorama-client PRIVATE /W4) -else() - target_compile_options(panorama-client PRIVATE -Wall -Wextra -Wpedantic) -endif() +target_compile_options(panorama-client PRIVATE -Wall -Wextra -Wpedantic) install(TARGETS panorama-client diff --git a/scripts/wxbuild_msvc.ps1 b/scripts/wxbuild_msvc.ps1 deleted file mode 100644 index 364b2b83..00000000 --- a/scripts/wxbuild_msvc.ps1 +++ /dev/null @@ -1,195 +0,0 @@ -# Build wxWidgets for Windows using MSVC -# Run this from PowerShell on Windows with Visual Studio installed - -param( - [string]$Config = "Release" -) - -$WXWIDGETS_VERSION = "3.3.1" -$WXWIDGETS_URL = "https://github.com/wxWidgets/wxWidgets/releases/download/v$WXWIDGETS_VERSION/wxWidgets-$WXWIDGETS_VERSION.zip" -$BUILD_DIR = Join-Path $PSScriptRoot "..\build\wxwidgets-build" -$OUTPUT_BASE = Join-Path $PSScriptRoot "..\client\third_party\wxwidgets-prebuilt" -$OUTPUT_WINDOWS = Join-Path $OUTPUT_BASE "windows-x64" - -# Create directories -New-Item -ItemType Directory -Force -Path $BUILD_DIR | Out-Null -New-Item -ItemType Directory -Force -Path $OUTPUT_BASE | Out-Null -New-Item -ItemType Directory -Force -Path $OUTPUT_WINDOWS | Out-Null - -# Download and extract wxWidgets if not present -$zipFile = Join-Path $BUILD_DIR "wxWidgets-$WXWIDGETS_VERSION.zip" - -if (!(Test-Path $zipFile)) { - Write-Host "Downloading wxWidgets $WXWIDGETS_VERSION..." - try { - Invoke-WebRequest -Uri $WXWIDGETS_URL -OutFile $zipFile -UseBasicParsing - } catch { - Write-Error "Failed to download wxWidgets: $_" - exit 1 - } -} - -# Extract if not already extracted -$extractCheck = Join-Path $BUILD_DIR "wxWidgets-$WXWIDGETS_VERSION" -if (!(Test-Path $extractCheck)) { - Write-Host "Extracting wxWidgets..." - try { - Expand-Archive -Path $zipFile -DestinationPath $BUILD_DIR -Force - } catch { - Write-Error "Failed to extract: $_" - exit 1 - } -} - -# Find the actual wxWidgets source directory -Write-Host "Looking for wxWidgets source..." -$possiblePaths = @( - (Join-Path $BUILD_DIR "wxWidgets-$WXWIDGETS_VERSION"), - (Join-Path $BUILD_DIR "wxwidgets-$WXWIDGETS_VERSION"), - $BUILD_DIR -) - -$WXWIDGETS_SRC = $null -foreach ($path in $possiblePaths) { - $testPath = Join-Path $path "build\msw" - if (Test-Path $testPath) { - $WXWIDGETS_SRC = $path - Write-Host "Found wxWidgets source at: $WXWIDGETS_SRC" - break - } -} - -# If not found, list what we have and check subdirectories -if ($null -eq $WXWIDGETS_SRC) { - Write-Host "`nCannot find wxWidgets source. Contents of build directory:" - Get-ChildItem $BUILD_DIR | Format-Table Name - - # Check subdirectories - $subdirs = Get-ChildItem $BUILD_DIR -Directory - foreach ($subdir in $subdirs) { - $testPath = Join-Path $subdir.FullName "build\msw" - if (Test-Path $testPath) { - $WXWIDGETS_SRC = $subdir.FullName - Write-Host "Found wxWidgets in subdirectory: $WXWIDGETS_SRC" - break - } - } -} - -if ($null -eq $WXWIDGETS_SRC) { - Write-Error "Could not locate wxWidgets source with build\msw directory" - exit 1 -} - -Write-Host "`nBuilding wxWidgets for Windows x64 with MSVC..." - -# Set up Visual Studio environment -$vsPaths = @( - "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat", - "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat", - "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat", - "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" -) - -$vsPath = $null -foreach ($path in $vsPaths) { - if (Test-Path $path) { - $vsPath = $path - break - } -} - -if ($null -eq $vsPath) { - Write-Error "Visual Studio 2022 not found. Please install Visual Studio 2022." - exit 1 -} - -Write-Host "Using Visual Studio from: $vsPath" - -# Build using MSBuild -$buildMswPath = Join-Path $WXWIDGETS_SRC "build\msw" -Push-Location $buildMswPath - -Write-Host "Building in: $buildMswPath" -Write-Host "Running: nmake /f makefile.vc BUILD=release SHARED=0 UNICODE=1 MONOLITHIC=1" - -# Use cmd to set up environment and build -$buildCmd = "`"$vsPath`" && nmake /f makefile.vc BUILD=release SHARED=0 UNICODE=1 MONOLITHIC=1" -$result = cmd /c $buildCmd - -Pop-Location - -if ($LASTEXITCODE -ne 0) { - Write-Error "Build failed with exit code $LASTEXITCODE" - exit 1 -} - -# Copy output files -Write-Host "`nCopying binaries to $OUTPUT_WINDOWS..." - -# Copy include files -$includeDir = Join-Path $OUTPUT_WINDOWS "include\wx-3.3" -$wxIncludeSrc = Join-Path $WXWIDGETS_SRC "include\wx" -New-Item -ItemType Directory -Force -Path $includeDir | Out-Null - -if (Test-Path $wxIncludeSrc) { - Write-Host "Copying include files..." - Copy-Item -Path "$wxIncludeSrc\*" -Destination (Join-Path $includeDir "wx") -Recurse -Force -} else { - Write-Warning "Include directory not found at: $wxIncludeSrc" -} - -# Copy setup.h - it could be in different locations depending on build -$setupLocations = @( - (Join-Path $WXWIDGETS_SRC "lib\vc_x64_lib\mswu\wx\setup.h"), - (Join-Path $WXWIDGETS_SRC "lib\vc_lib\mswu\wx\setup.h") -) - -$setupFound = $false -foreach ($setupSrc in $setupLocations) { - if (Test-Path $setupSrc) { - Write-Host "Copying setup.h from: $setupSrc" - $setupDest = Join-Path $OUTPUT_WINDOWS "lib\mswu\wx" - New-Item -ItemType Directory -Force -Path $setupDest | Out-Null - Copy-Item -Path $setupSrc -Destination $setupDest -Force - $setupFound = $true - break - } -} - -if (!$setupFound) { - Write-Warning "setup.h not found in expected locations" -} - -# Copy library files -$libLocations = @( - (Join-Path $WXWIDGETS_SRC "lib\vc_x64_lib"), - (Join-Path $WXWIDGETS_SRC "lib\vc_lib") -) - -$libDir = Join-Path $OUTPUT_WINDOWS "lib" -New-Item -ItemType Directory -Force -Path $libDir | Out-Null - -$libsFound = $false -foreach ($libSrc in $libLocations) { - if (Test-Path $libSrc) { - Write-Host "Copying libraries from: $libSrc" - Copy-Item -Path "$libSrc\*.lib" -Destination $libDir -Force - $libsFound = $true - break - } -} - -if (!$libsFound) { - Write-Warning "Library files not found in expected locations" -} - -Write-Host "`nBuild complete! Libraries are in: $OUTPUT_WINDOWS" -Write-Host "`nLibrary files:" -if (Test-Path $libDir) { - Get-ChildItem "$libDir\*.lib" | Format-Table Name, Length -} else { - Write-Warning "Library directory not created" -} - -Write-Host "`nTo use these libraries, update your CMakeLists.txt to use MSVC configuration." \ No newline at end of file From f5a5291d0be0035b7f282c205273ae82a482b76a Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 7 Mar 2026 15:06:11 -0800 Subject: [PATCH 3/3] Removed unused files --- scripts/example.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 scripts/example.json diff --git a/scripts/example.json b/scripts/example.json deleted file mode 100644 index 306db949..00000000 --- a/scripts/example.json +++ /dev/null @@ -1,4 +0,0 @@ -[ -{"datatype": "light", "data": 0.200000, "dataunit": "nm", "timestamp": 1770504589}, -{"datatype": "temperature", "data": 29.500000, "dataunit": "celsius", "timestamp": 1770504589} -] \ No newline at end of file