Youth Hacking 4 Freedom 2026 submission: see
YH4F_SUBMISSION.md.Demo video: youtu.be/IOTeKleOHQY — launcher, Calculation, natural-display math, and Grapher.
Table of Contents
- Webpage
- What is NumOS?
- Key Features
- Photo Gallery
- System Architecture
- CAS Engine
- Hardware
- Quick Start
- User Manual — EquationsApp
- Project Structure
- Build Stats
- Critical Hardware Fixes
- Project Status
- Technology Stack
- Comparison with Commercial Calculators
- Documentation
- Contributing
What is NumOS?
NumOS is an open-source scientific and graphing calculator operating system built on the ESP32-S3 N16R8 microcontroller (16 MB QIO runtime flash, DIO ROM boot header + 8 MB PSRAM OPI). The project aims to provide advanced features comparable to established scientific calculators, including symbolic mathematics, graphing, and natural display formatting.
Working toward delivering a capable CAS experience for a ~€20 BOM as an open-source alternative in the educational calculator space.
NumOS delivers:
- Giac-backed CAS Engine — Symbolic math implementation using Giac C++ via
src/math/giac/GiacBridge.cppfor evaluation. Legacy CAS-S3 modules remain documented as historical milestones and optional local tooling. - Natural Display V.P.A.M. — Formulae rendered as they appear on paper: real stacked fractions, radical symbols (√), genuine superscripts, 2D navigation with a structural smart cursor.
- Modern LVGL 9.x Interface — Smooth transitions, animated splash screen, NumWorks-style launcher.
Recent launcher refactor: the launcher now uses LVGL Flex
ROW_WRAP(dynamic rows) with fixed card sizing instead of a static grid descriptor. Seedocs/UI_CHANGES.mdfor developer migration notes anddocs/fluid2d_plan.mdfor an example app (Fluid2D) integrated into the new APPS[] schema. - Custom Numeric Math Engine — Complete pipeline: Tokenizer → Shunting-Yard Parser → RPN Evaluator + Visual AST, implemented from scratch in C++17.
- Modular App Architecture — Each application is a self-contained module with explicit lifecycle (
begin/end/load/handleKey), orchestrated bySystemApp.
Key Features
| Feature | Description |
|---|---|
| Giac CAS Backend |
Experimental / in progress — not a validated CAS. Symbolic evaluation through GiacBridge with UART parser/eval flow exercised on hardware. Milestones: -DDOUBLEVAL, 64 KB loop stack, real-style complex_mode(false) with preserved i^2=-1 behavior |
| Unified Calculus App |
Experimental / still rough. Symbolic |
| EquationsApp | Experimental; the "steps" view is alpha. Solves linear, quadratic, and 2×2 systems (linear + non-linear via Sylvester resultant) with step-by-step display |
| Bridge Designer | Prototype. Real-time structural bridge simulator with Verlet integration physics, stress analysis (green→red beam visualisation), snap-to-grid editor, wood/steel/cable materials, and truck/car load testing — PSRAM-backed, 60 Hz fixed timestep |
| Particle Lab | Experimental. Powder-Toy-class sandbox: 30+ materials (Sand, Water, Lava, LN2, Wire, Iron, Titan, C4, Clone), spark electronics with Joule heating, phase transitions, reaction matrix (Water+Lava=Stone+Steam), Bresenham line tool, material palette overlay, LittleFS save/load |
| Settings App | System-wide toggles for complex number output (ON/OFF), decimal precision selector (6/8/10/12 digits), and angle-mode display |
| Natural Display | Real fractions, radicals, exponents, 2D cursors — mathematical rendering as it appears on paper |
| Graphing: y=f(x) | Real-time function plotter with zoom, pan, and value table |
| 53 CAS Unit Tests | CAS unit-test suite (Phases A–D), compile-time gated and off by default |
| PSRAMAllocator | CAS uses PSRAMAllocator<T> to isolate memory usage in the 8 MB PSRAM OPI |
| Variables A–Z + Ans | Persistent storage via LittleFS — 216 bytes in /vars.dat
|
| SerialBridge | Full calculator control from PC via Serial Monitor without physical hardware |
| SerialBridge Debug | Immediate byte echo, 5-second heartbeat, 8-event circular buffer |
Photo gallery
Neural Network Simulator:
Fluid 2D Simulator:
Periodic Table (Chemistry App):
Grapher App:
Steps (Equations App) (WIP, still in development, Alpha):
Calculus App:
Probability (Gaussian Distribution):
Python App:
Bridge Designer:
Circuit Simulator (Circuit Core, Alpha):
Particle Lab (Powder Toy like):
Optics Lab:
System Architecture
flowchart TB
subgraph esp[ESP32-S3 N16R8]
main["main.cpp: setup() → PSRAM, TFT, LVGL, Splash, SystemApp; loop(): lv_timer_handler(), app.update(), serial.poll()"]
system["SystemApp (Dispatcher)"]
main --> system
end
subgraph apps[Applications]
mm["MainMenu (LVGL)"]
calc["CalculationApp (Natural VPAM, History)"]
grapher["GrapherApp (y=f(x), Zoom & Pan)"]
eq["EquationsApp (CAS)"]
calculus["CalculusApp (d/dx, ∫dx)"]
settings["SettingsApp"]
end
system --> mm
system --> calc
system --> grapher
system --> eq
system --> calculus
system --> settings
math["Math Engine: Tokenizer · Parser · Evaluator · ExprNode · VariableContext · EquationSolver"]
procas["CAS Engine: CASInt · CASRational · SymExpr DAG · SymSimplify · SymDiff · SymIntegrate"]
system --> math
math --> procas
display["Display Layer: DisplayDriver · LVGL flush DMA · ILI9341 @ 10 MHz"]
input["Input Layer: KeyMatrix 5x10 · SerialBridge · LvglKeypad · LittleFS"]
system --> display
system --> input
display -->|SPI @ 10 MHz| ili["ILI9341 IPS 3.2 in — 320x240 · 16 bpp"]
ili -.-> esp
CAS Engine
The CAS (Computer Algebra System) now uses Giac C++ as the canonical symbolic backend. The migration is routed through src/math/giac/GiacBridge.cpp and consumed by the UART command path in src/input/SerialBridge.cpp.
Legacy CAS-S3 internals documented below remain as historical milestones and optional local components, but symbolic truth for current backend flows comes from Giac.
Giac Migration Milestones
- Big Switch complete: custom symbolic backend replaced by Giac as canonical CAS.
- Embedded numeric stabilization complete with
-DDOUBLEVAL. - Stack stabilization complete with
-DARDUINO_LOOP_STACK_SIZE=65536. - Real-style defaults complete:
complex_mode(false)and preserved imaginary unit behavior (i^2 = -1). - UART command path certified on hardware for
sum,int,solve, andsimplify.
CAS Pipeline (Derivatives)
flowchart TB
user["User input (CalculusApp): x^3 + sin(x)"]
user --> me["Math Engine: Parser + Tokenizer"]
me --> af["ASTFlattener: MathAST → SymExpr DAG"]
af --> sd["SymDiff → d/dx: 3x^2 + cos(x)"]
sd --> ss["SymSimplify (8-pass fixed-point)"]
ss --> sea["SymExprToAST: SymExpr → MathAST (Natural Display)"]
sea --> canvas["MathCanvas renders: 3x^2 + cos(x)"]
CAS Pipeline (Integrals)
flowchart TB
userInt["User input (CalculusApp, ∫dx mode): x · cos(x)"]
userInt --> af2["ASTFlattener → SymExpr DAG"]
af2 --> sint["SymIntegrate (Slagle): table → linearity → u-sub → parts (LIATE)"]
sint --> ss2["SymSimplify"]
ss2 --> conv["SymExprToAST::convertIntegral()"]
conv --> canvas2["MathCanvas renders: x·sin(x) + cos(x) + C"]
CAS Components
| Module | File | Responsibility |
|---|---|---|
CASInt |
cas/CASInt.h |
Hybrid BigInt: int64_t fast-path + mbedtls_mpi on overflow |
CASRational |
cas/CASRational.h/.cpp |
Overflow-safe exact fraction (num/den with auto-GCD) |
PSRAMAllocator<T> |
cas/PSRAMAllocator.h |
STL allocator → ps_malloc/ps_free for PSRAM |
SymExpr DAG |
cas/SymExpr.h/.cpp |
Immutable symbolic tree with hash (_hash) and weight (_weight) |
ConsTable |
cas/ConsTable.h |
PSRAM hash-consing table: deduplication of identical nodes |
SymExprArena |
cas/SymExprArena.h |
PSRAM bump allocator (16 blocks × 64 KB) + integrated ConsTable |
ASTFlattener |
cas/ASTFlattener.h/.cpp |
MathAST (VPAM) → SymExpr DAG with hash-consing |
SymDiff |
cas/SymDiff.h/.cpp |
Symbolic differentiation: 17 rules (chain, product, quotient, trig, exp, log) |
SymIntegrate |
cas/SymIntegrate.h/.cpp |
Slagle integration: table, linearity, u-substitution, parts (LIATE) |
SymSimplify |
cas/SymSimplify.h/.cpp |
Multi-pass simplifier (8 iterations, fixed-point, trig/log/exp) |
SymPoly |
cas/SymPoly.h/.cpp |
Univariable symbolic polynomial with CASRational coefficients |
SymPolyMulti |
cas/SymPolyMulti.h/.cpp |
Multivariable polynomial + Sylvester resultant |
SingleSolver |
cas/SingleSolver.h/.cpp |
Single-variable equation: linear / quadratic / Newton-Raphson |
SystemSolver |
cas/SystemSolver.h/.cpp |
2×2 system: Gaussian elimination + non-linear (resultant) |
OmniSolver |
cas/OmniSolver.h/.cpp |
Analytic variable isolation: inverses, roots, trig |
HybridNewton |
cas/HybridNewton.h/.cpp |
Newton-Raphson with symbolic Jacobian and 16-seed multi-start |
CASStepLogger |
cas/CASStepLogger.h/.cpp |
StepVec in PSRAM — detailed steps (INFO/FORMULA/RESULT/ERROR) |
SymToAST |
cas/SymToAST.h/.cpp |
Bridge: SolveResult → MathAST Natural Display |
SymExprToAST |
cas/SymExprToAST.h/.cpp |
Bridge: SymExpr → MathAST. Includes convertIntegral() (+C) |
CAS Tests — 53 Unit Tests
| Phase | Tests | Coverage |
|---|---|---|
| A — Foundations | 1–18 | Rational: add, subtract, multiply, divide, simplification. SymPoly: arithmetic, derivation, normalisation. |
| B — ASTFlattener | 19–32 | AST→SymPoly conversion for simple polynomials, constants, trig functions, powers. |
| C — SingleSolver | 33–44 | Linear (single solution), quadratic (2 real roots, repeated root, negative discriminant), steps. |
| D — SystemSolver | 45–53 | 2×2 determined system, indeterminate (infinite solutions), incompatible system. |
# platformio.ini — enable tests: build_flags = ... -DCAS_RUN_TESTS build_src_filter = +<*> +<../tests/CASTest.cpp>
Hardware
| Component | Specification |
|---|---|
| MCU | ESP32-S3 N16R8 CAM — Dual-core Xtensa LX7 @ 240 MHz |
| Flash | 16 MB QIO (default_16MB.csv) |
| PSRAM | 8 MB OPI (qio_opi — critical to prevent boot panic) |
| Display | ILI9341 IPS TFT 3.2" — 320×240 px — SPI @ 10 MHz (verified) |
| SPI Bus | FSPI (SPI2): MOSI=13, SCLK=12, CS=10, DC=4, RST=5 |
| Backlight | GPIO 45 — hardwired to 3.3V (pinMode(45, INPUT)) |
| Keyboard | 5×10 matrix (Phase 7) — Rows OUTPUT: GPIO 1,2,41,42,40 · Cols INPUT_PULLUP: GPIO 6,7,8… |
| Storage | LittleFS on dedicated partition — persistent A–Z variables |
| USB | Native USB-CDC on S3 — 115 200 baud |
Next physical milestone: transition to the ESP32-P4 ("No-Radio") as the hardware path to Exam Mode compliance, meeting school regulations without locking down the software.
Full Pinout
ILI9341 Display
| Signal | GPIO | Notes |
|---|---|---|
| MOSI | 13 | FSPI Data In |
| SCLK | 12 | FSPI Clock |
| CS | 10 | Chip Select (active LOW) |
| DC | 4 | Data/Command |
| RST | 5 | Reset |
| BL | 45 | Hardwired to 3.3V — always INPUT |
5×10 Keyboard Matrix (driver Keyboard, Phase 7)
| Row | GPIO | Role | Column | GPIO | Role |
|---|---|---|---|---|---|
| ROW 0 | 1 | OUTPUT | COL 0 | 6 | INPUT_PULLUP |
| ROW 1 | 2 | OUTPUT | COL 1 | 7 | INPUT_PULLUP |
| ROW 2 | 41 | OUTPUT | COL 2 | 8 | INPUT_PULLUP |
| ROW 3 | 42 | OUTPUT | COL 3–9 | 3,15,16,17,18,21,47 | not yet wired |
| ROW 4 | 40 | OUTPUT | — | — | — |
✅ GPIO 4/5 conflict resolved (2026-03-02): Keyboard columns C0 and C1 reassigned from GPIO 4/5 (
TFT_DC/TFT_RST) to GPIO 6/7. The three currently wired columns use GPIO 6, 7, and 8 — no display conflict.
Quick Start
Requirements
- PlatformIO IDE (VS Code extension)
- PlatformIO Core CLI (
pio) for the command-line builds below — bundled with the IDE extension's own terminal, or install it standalone for a normal Terminal/shell (macOS:brew install platformio; any OS:pip install -U platformio). Verify withpio --version. - USB drivers for ESP32-S3 (no external driver needed on Windows 11+)
- Python 3.x (PlatformIO installs it automatically)
Build and Flash
git clone https://github.com/El-EnderJ/NeoCalculator.git cd NeoCalculator # Build only pio run -e esp32s3_n16r8 # Build a validated production/recovery package python scripts/esp32_boot.py package # Application-only development upload python scripts/esp32_boot.py flash-app --port <PORT> # Open serial monitor (115 200 baud) pio device monitor
Run on Your PC — Desktop Emulator (no ESP32 required)
Want to try NumOS without buying any hardware? The SDL2 desktop emulator runs the real UI and math code on your computer — no ESP32-S3 board needed:
pio --version # confirm the PlatformIO CLI is installed and on your PATH pio run -e emulator_pc -t exec # build AND open the emulator window (one step)
-t execbuilds and runs it. Plainpio run -e emulator_pconly compiles the emulator — it does not open a window. Add-t exec(or use the run scripts) to actually launch it. macOS/ Linux can also use./scripts/run-emulator.sh; Windows uses./scripts/run-emulator-windows.ps1(it also resolvesSDL2.dll).
The
esp32s3_*targets above are firmware builds for real hardware;emulator_pcis the desktop target. A barepio run(no-e) builds every environment, which is slower — pass-e emulator_pcto build only the emulator.
VS Code users: to run the emulator, use Terminal → Run Task… → “Run NumOS Emulator” (or run the command above in a terminal). Do not press Run/Debug (F5) or pick “PIO Debug” / “Electron Main” — “PIO Debug” targets a real ESP32 over JTAG and fails without a board, and NumOS is not an Electron app. See Build vs. Run vs. Debug.
command not found: pio(macOSzsh/ Linux)? The PlatformIO CLI isn't on your PATH yet. The VS Code PlatformIO IDE extension bundles PlatformIO inside its own terminal — a normal Terminal/shell needs the standalone CLI. On macOS (Homebrew):brew install platformio # PlatformIO CLI brew install sdl2 pkg-config # SDL2 desktop dependency for the emulator pio --version # confirm the CLI is now on your PATH pio run -e emulator_pc
Full setup (including installing SDL2 on Windows/Linux/macOS) and a troubleshooting
guide live in the
SDL2 Desktop Emulator Quickstart — start
there if your esp32s3_* builds pass but emulator_pc fails (or if pio is
"command not found").
Serial Keyboard Control (SerialBridge)
With the Serial Monitor open, type characters to control the calculator:
| Key | Action | Key | Action | |
|---|---|---|---|---|
w |
↑ Up | z |
ENTER / Confirm | |
s |
↓ Down | x |
DEL / Delete | |
a |
← Left | c |
AC / Clear | |
d |
→ Right | h |
MODE / Return to menu | |
0–9 |
Digits | +-*/^.() |
Operators | |
S |
SHIFT | r |
√ SQRT | |
t |
sin | g |
GRAPH | |
e |
= (equation) |
R |
SHOW STEPS |
Note: lowercase
s= DOWN; uppercaseS= SHIFT. Disable CapsLock before use.
User Manual — EquationsApp
The EquationsApp solves single-variable polynomial equations and 2×2 systems (linear and non-linear), displaying complete solution steps via the CAS engine.
Access
- From the Launcher, select Equations with ↑↓ and press ENTER.
- The mode-selection screen appears.
Mode 1: Single-Variable Equation
- Select Equation (1 var) with ↑↓ and press ENTER.
- The editor opens. Type your equation with the
=sign:x^2 - 5x + 6 = 0→ x₁=2, x₂=32x + 3 = 7→ x=2x^2 = -1→ no real solution (Δ < 0)
- Press ENTER to solve.
- The result screen shows:
- Linear: a single solution
x = value - Quadratic: discriminant Δ and up to 2 solutions
x₁,x₂ - No real solution: negative discriminant message
- Linear: a single solution
- Press SHOW STEPS (
R) to view detailed steps:- Normalised equation
- Discriminant value Δ = b² − 4ac
- Quadratic formula applied
- Computed roots
- Press MODE (
h) to return to the main menu.
Mode 2: 2×2 System
- Select System (2×2) and press ENTER.
- Two fields appear: Eq 1 and Eq 2.
- Type the first equation in
xandy, press ENTER. - Type the second equation, press ENTER.
- Example:
2x + y = 5/x - y = 1→ x=2, y=1
- Type the first equation in
- Press ENTER to solve. Displays
x = value, y = value. - Press SHOW STEPS to view the full Gaussian elimination.
- Press MODE to return.
EquationsApp Keys
| Key | Action |
|---|---|
| ↑ ↓ ← → | Navigate selection / cursor in editor |
| ENTER | Confirm mode / Solve equation |
| DEL | Delete character |
| AC | Clear field |
| SHOW STEPS | View detailed steps (from result screen) |
| MODE | Return to main menu |
Project Structure
numOS/
├── src/
│ ├── main.cpp # Arduino entry point (setup/loop)
│ ├── SystemApp.cpp/.h # Central orchestrator and LVGL launcher
│ ├── Config.h # Global ESP32-S3 pinout
│ ├── lv_conf.h # LVGL 9.x configuration
│ ├── HardwareTest.cpp # Interactive keyboard test (inline)
│ ├── apps/
│ │ ├── CalculationApp.cpp/.h # Natural V.P.A.M. calculator
│ │ ├── GrapherApp.cpp/.h # y=f(x) graphing plotter
│ │ ├── EquationsApp.cpp/.h # CAS — Equation solver
│ │ ├── CalculusApp.cpp/.h # CAS — Unified symbolic derivatives + integrals
│ │ ├── BridgeDesignerApp.cpp/.h # Bridge structural simulator (Verlet physics)
│ │ ├── CircuitCoreApp.cpp/.h # Circuit simulator (MNA, 30 components)
│ │ ├── Fluid2DApp.cpp/.h # 2D fluid dynamics (Navier-Stokes)
│ │ ├── ParticleLabApp.cpp/.h # Powder-Toy sandbox (30+ materials, electronics)
│ │ ├── ParticleEngine.cpp/.h # Cellular automata engine (LUT, spark cycle)
│ │ └── SettingsApp.cpp/.h # Settings: complex roots, precision, angle mode
│ ├── display/
│ │ └── DisplayDriver.cpp/.h # TFT_eSPI FSPI + LVGL init + DMA flush
│ ├── input/
│ │ ├── KeyCodes.h # KeyCode enum (48 keys)
│ │ ├── KeyMatrix.cpp/.h # 5×10 hardware driver with debounce
│ │ ├── SerialBridge.cpp/.h # Virtual keyboard via Serial
│ │ └── LvglKeypad.cpp/.h # LVGL indev keypad adapter
│ ├── math/
│ │ ├── Tokenizer.cpp/.h # Lexical analyser
│ │ ├── Parser.cpp/.h # Shunting-Yard → RPN / Visual AST
│ │ ├── Evaluator.cpp/.h # Numerical RPN evaluator
│ │ ├── ExprNode.h # Expression tree (Natural Display)
│ │ ├── MathAST.h # V.P.A.M. tree: NodeRow/NodeFrac/NodePow…
│ │ ├── CursorController.h/.cpp # MathAST editing cursor
│ │ ├── EquationSolver.cpp/.h # Numerical Newton-Raphson
│ │ ├── VariableContext.cpp/.h # Variables A–Z + Ans
│ │ ├── VariableManager.h/.cpp # Persistent ExactVal storage
│ │ ├── StepLogger.cpp/.h # Parser step logger
│ │ └── cas/ # ★ Complete CAS Engine
│ │ ├── CASInt.h # Hybrid BigInt (int64 + mbedtls_mpi)
│ │ ├── CASRational.h/.cpp # Overflow-safe exact fraction
│ │ ├── ConsTable.h # Hash-consing PSRAM (dedup)
│ │ ├── PSRAMAllocator.h # STL allocator for PSRAM OPI
│ │ ├── SymExpr.h/.cpp # Immutable DAG with hash + weight
│ │ ├── SymExprArena.h # Bump allocator + ConsTable
│ │ ├── SymDiff.h/.cpp # Symbolic differentiation (17 rules)
│ │ ├── SymIntegrate.h/.cpp # Slagle integration (table/u-sub/parts)
│ │ ├── SymSimplify.h/.cpp # Fixed-point simplifier (8 passes)
│ │ ├── SymPoly.h/.cpp # Univariable symbolic polynomial
│ │ ├── SymPolyMulti.h/.cpp # Multivariable polynomial + resultant
│ │ ├── ASTFlattener.h/.cpp # MathAST → SymExpr DAG
│ │ ├── SingleSolver.h/.cpp # Analytic linear + quadratic solver
│ │ ├── SystemSolver.h/.cpp # 2×2 system (linear + NL resultant)
│ │ ├── OmniSolver.h/.cpp # Analytic variable isolation
│ │ ├── HybridNewton.h/.cpp # Newton-Raphson with symbolic Jacobian
│ │ ├── CASStepLogger.h/.cpp # Steps in PSRAM (StepVec)
│ │ ├── SymToAST.h/.cpp # SolveResult → visual MathAST
│ │ └── SymExprToAST.h/.cpp # SymExpr → MathAST (+C, ∫)
│ └── ui/
│ ├── MainMenu.cpp/.h # LVGL launcher grid 3×N
│ ├── MathRenderer.h/.cpp # 2D MathCanvas renderer
│ ├── StatusBar.h/.cpp # LVGL status bar
│ ├── GraphView.cpp/.h # Graph widget
│ ├── Icons.h # App icon bitmaps
│ └── Theme.h # Colour palette and UI constants
├── tests/
│ ├── CASTest.h/.cpp # CAS unit tests
│ ├── HardwareTest.cpp # TFT + physical keyboard test
│ └── TokenizerTest_temp.cpp # Tokenizer test
├── docs/
│ ├── CAS_UPGRADE_ROADMAP.md # ★ CAS roadmap (6 phases, complete)
│ ├── ROADMAP.md # Phase history + future plan
│ ├── PROJECT_BIBLE.md # Master software architecture
│ ├── MATH_ENGINE.md # Math engine + CAS in detail
│ ├── HARDWARE.md # ESP32-S3 pinout, wiring, and bring-up
│ ├── CONSTRUCTION.md # Physical assembly guide
│ └── DIMENSIONES_DISEÑO.md # 3D chassis specifications
├── platformio.ini # PlatformIO configuration
├── wokwi.toml # Wokwi simulator (optional)
└── diagram.json # Wokwi circuit diagram
Build Stats
Compiled with
pio run -e esp32s3_n16r8in production mode (CAS tests disabled)
| Resource | Used | Total | Percentage |
|---|---|---|---|
| RAM (data + bss) | 97 192 B | 327 680 B | 29.7 % |
| Flash (program storage) | 1 518 269 B | 6 553 600 B | 23.2 % |
Flash saved vs test mode: −39 444 B when deactivating -DCAS_RUN_TESTS.
To enable or disable CAS tests, edit platformio.ini:
; ---- Production mode (default) ---- ; -DCAS_RUN_TESTS ← commented out ; ---- Test mode — uncomment these two lines ---- ; -DCAS_RUN_TESTS ; +<../tests/CASTest.cpp> ← in build_src_filter
Critical Hardware Fixes
Issues discovered and resolved during bring-up. Essential for any fork or new build:
| # | Problem | Symptom | Solution |
|---|---|---|---|
| ① | Flash/PSRAM mode mismatch | ROM watchdog or PSRAM boot panic | DIO ROM header + QIO second stage + memory_type = qio_opi |
| ② | SPI StoreProhibited | Crash in TFT_eSPI::begin() at address 0x10 |
-DUSE_FSPI_PORT → SPI_PORT=2 → REG_SPI_BASE(2)=0x60024000 |
| ③ | Display noise | Horizontal lines and visual artefacts | Reduce SPI to 10 MHz: -DSPI_FREQUENCY=10000000 |
| ④ | LVGL black screen | lv_timer_handler() invokes flush but no image appears |
Buffers via heap_caps_malloc(MALLOC_CAP_DMA|MALLOC_CAP_8BIT) — never ps_malloc |
| ⑤ | GPIO 45 BL short | Display stops responding on backlight init | pinMode(45, INPUT) — the pin is hardwired to 3.3V |
| ⑥ | Serial CDC lost | Output invisible in Serial Monitor on connect | while(!Serial && millis()-t0 < 3000) + monitor_rts=0 in platformio.ini |
Project Status
| Phase | Description | Status |
|---|---|---|
| Phase 1 | Math Engine — Tokenizer, Shunting-Yard Parser, RPN Evaluator, ExprNode, VariableContext | ✅ Implemented |
| Phase 2 | Natural Display V.P.A.M. — fractions, radicals, exponents, smart 2D cursor | ✅ Implemented |
| Phase 3 | Launcher 3.0, SerialBridge, CalculationApp history, GrapherApp zoom/pan | ✅ Implemented |
| Phase 4 | LVGL 9.x — ESP32-S3 HW bring-up, DMA, animated splash screen, icon launcher | ✅ Implemented |
| Phase 5 | CAS-Lite Engine (SymPoly, SingleSolver, SystemSolver, 53 tests) + EquationsApp UI (legacy milestone) | ✅ Implemented |
| CAS | CAS-S3 internal milestones: BigNum, hash-consed DAG, SymDiff 17 rules, SymIntegrate Slagle, SymSimplify 8-pass, OmniSolver | ✅ Implemented |
| Giac Migration | Giac integration: GiacBridge, UART parser/eval flow, -DDOUBLEVAL, 64 KB loop stack, real-mode defaults with i preserved |
✅ Implemented |
| Phase 6 | Statistics, Regression, Sequences, Probability, Matrices, Bridge Designer (Prototype) | 🟡 In Development |
| Simulations | ParticleLab (30+ materials, electronics), CircuitCore (SPICE), Fluid2D (Navier-Stokes) (Experimental) | 🟡 In Development |
| Phase 7 | Complex numbers, base conversions | 🔲 Planned |
| Phase 8 | Physical keyboard (prototype stage), custom PCB, rechargeable battery, 3D enclosure, WiFi OTA | 🔲 Planned |
Technology Stack
| Layer | Technology | Version |
|---|---|---|
| MCU Framework | Arduino on ESP-IDF 5.x | PlatformIO espressif32 6.12.0 |
| UI / Graphics | LVGL | 9.5.0 |
| TFT Driver | TFT_eSPI | 2.5.43 |
| Filesystem | LittleFS | ESP-IDF built-in |
| Language | C++17 | lambdas, std::function, std::unique_ptr |
| CAS Memory | PSRAMAllocator STL custom | PSRAM OPI 8 MB |
| Build System | PlatformIO | 6.12.0 |
| Simulation | Wokwi | wokwi.toml |
Comparison with Commercial Calculators
| Feature | NumOS | NumWorks | TI-84 Plus CE | HP Prime G2 |
|---|---|---|---|---|
| Open Source | ✅ GPL-3.0-or-later | ✅ Source-available | ❌ | ❌ |
| Natural Display | ✅ | ✅ | ✅ | ✅ |
| Symbolic CAS | 🟡 Giac | ✅ SymPy | ❌ | ✅ |
| Symbolic derivatives | 🟡 | ✅ | ❌ | ✅ |
| Symbolic integrals | 🟡 | ✅ | ❌ | ✅ |
| Solution steps | 🟡 | ❌ | ❌ | ✅ |
| Colour graphing | ✅ | ✅ | ✅ | ✅ |
| Multi-function graphing | 🔲 | ✅ | ✅ | ✅ |
| Statistics & Regression | 🟡 | ✅ | ✅ | ✅ |
| Matrices | 🔲 | ✅ | ✅ | ✅ |
| Complex numbers | 🔲 | ✅ | ✅ | ✅ |
| Scripting / Python | 🟡 NeoLanguage + Python | ✅ | ✅ TI-BASIC | ✅ HP PPL |
| WiFi / Connectivity | 🔲 | ✅ | ❌ | ❌ |
| Rechargeable battery | 🔲 | ✅ | ❌ | ✅ |
| Estimated HW cost | ~€15-25 | €79 | €149 | €179 |
| Platform | ESP32-S3 | STM32F730 | Zilog eZ80 | ARM Cortex-A7 |
📐 NumOS is developing CAS capabilities and cost-effectiveness as an open-source alternative, working toward feature parity with established commercial calculators.
Legend: ✅ = working today · 🟡 = experimental / in progress · 🔲 = planned. See
YH4F_SUBMISSION.mdfor the honest current status of each feature.
Documentation
| Document | Description |
|---|---|
| ROADMAP.md | Complete phase history, milestones, and detailed future plan |
| PROJECT_BIBLE.md | Master architecture, modules, code conventions, and development guides |
| CAS_UPGRADE_ROADMAP.md | Full roadmap for the 6-phase CAS upgrade |
| MATH_ENGINE.md | Math engine + CAS: design, algorithms, pipeline, and examples |
| HARDWARE.md | ESP32-S3 pinout, complete wiring, critical bugs, and bring-up notes |
| ESP32_BOOT_FLASHING.md | Authoritative development upload, provisioning, and recovery workflow |
| CONSTRUCTION.md | Physical assembly guide, 3D printing, and hardware testing |
| DIMENSIONES_DISEÑO.md | Dimensional specifications for the 3D chassis |
Support the Project ☕
The EV grant graciously covers the core hardware prototyping. However, I have set a €500 goal on Ko-fi to fund the crucial "invisible" infrastructure of NumOS.
Your support directly funds:
- ** AI & Dev Tools:** Subscriptions for Claude/Copilot to accelerate C++ optimization and the Giac engine porting.
- Digital Presence: Domain hosting (
neocalculator.tech/numos.org) and backend web services. - Beta Shipping: Sending physical beta units to expert contributors globally to accelerate community development.
Every contribution keeps me focused on the mission and ensures NumOS stays independent and open-source.
Contributing
NumOS is an open-source project in active development that welcomes community contributions. The software is currently in prototype/alpha stage and validates features on the current ESP32-S3 N16R8 hardware platform.
- Fork the repository.
- Create a branch:
git checkout -b feature/descriptive-name - Follow the code conventions of the project.
- Verify the build passes:
pio run -e esp32s3_n16r8 - If you add math logic, include tests in
tests/. - Open a Pull Request with a clear description of your changes.
Areas Where Help Is Most Needed
| Module | Description |
|---|---|
| Custom PCB | KiCad schematic with integrated ESP32-S3 + TP4056 charger |
| Sequences App | Arithmetic and geometric sequences, Nth term, partial sums |
| Settings App | |
| Advanced CAS | |
| Better UI/UX | General improvement on UI and UX for real product release |
| Matrices | Matrix editor, determinant, inverse, multiplication |
| Physical keyboard | ✅ GPIO 4/5 conflict resolved — Keyboard driver 5×10 implemented (Phase 7) |
This project was developed with AI assistance (Claude/Copilot) for code generation, guided by the author's systems architecture decisions. All design choices like DAG structure, memory management, parser design, were made and validated by the author.
License & Intellectual Property
Software (Firmware)
NumOS-authored software carrying the project notice is offered under
GPL-3.0-or-later; see LICENSE-SOFTWARE. The repository
also contains separately licensed third-party components and generated font
data. The licence-history qualification is recorded in
docs/LICENSING_AUDIT_2026.md. In particular,
the modified Giac/KhiCAS snapshot remains
GPL-3.0-or-later, while Montserrat and STIX Two Math font software and their
font-derived data remain under OFL-1.1.
See LICENSE.md,
THIRD_PARTY_NOTICES.md, and
lib/giac/NUMOS_CHANGES.md for the precise
component boundaries, provenance, and modification record.
Hardware & Industrial Design
The hardware architecture of the NeoCalculator, including but not limited to:
- PCB Schematics and Layouts (KiCad files, Gerbers).
- Industrial Design and 3D Models (STL, STEP, CAD files).
- The "Exam Key" security architecture.
Is licensed under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal (CERN-OHL-S v2).
We believe that hardware accessibility is crucial for education. Under this license, anyone is free to study, modify, manufacture, and distribute the physical calculator.
The Reciprocal Rule: If you modify these designs and distribute or sell the resulting hardware, you must release your modified source files under this exact same CERN-OHL-S v2 license, ensuring the hardware ecosystem remains open and accessible forever.
Built with ❤️ and a lot of C++17
NumOS, An open-source graphing calculator for ESP32-S3
Last updated: June 2026














