Browse by type
ESP32-S3 N16R8 · ILI9341 IPS 320×240 · LVGL 9.x · CAS Engine · Natural Display V.P.A.M.
🚀 Join the Hardware Waitlist here to get notified when PCB beta units drop.
Youth Hacking 4 Freedom 2026 submission: see
YH4F_SUBMISSION.md.Demo video: youtu.be/IOTeKleOHQY — launcher, Calculation, natural-display math, and Grapher.
NumOS is an open-source scientific and graphing calculator operating system built on the ESP32-S3 N16R8 microcontroller (16 MB Flash QIO + 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:
src/math/giac/GiacBridge.cpp for evaluation. Legacy CAS-S3 modules remain documented as historical milestones and optional local tooling.ROW_WRAP (dynamic rows) with fixed card sizing
instead of a static grid descriptor. See docs/UI_CHANGES.md for developer migration notes and
docs/fluid2d_plan.md for an example app (Fluid2D) integrated into the new APPS[] schema.begin/end/load/handleKey), orchestrated by SystemApp.| 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 $d/dx$ differentiation (17 rules) and numerical/symbolic $\int dx$ integration (Slagle heuristic: table lookup, linearity, u-substitution, integration by parts/LIATE), tab-based mode switching, automatic simplification, and step-by-step output |
| 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 |
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
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.
-DDOUBLEVAL.-DARDUINO_LOOP_STACK_SIZE=65536.complex_mode(false) and preserved imaginary unit behavior (i^2 = -1).sum, int, solve, and simplify.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)"]
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"]
| 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) |
| Phase | Tests | Coverage |
|---|---|---|
| A — Foundations | 1–18 | Rational: add, subtract, multiply, divide, simplification. SymPoly: arithmetic, derivation, normalisation. |
$ claude mcp add NeoCalculator \
-- python -m otcore.mcp_server <graph>