Graphing Scientific Calculator Based on the ESP32

Hacker News Top Tools

Summary

NumOS is an open-source operating system for graphing scientific calculators based on the ESP32-S3 microcontroller, featuring a Giac-backed CAS engine and LVGL 9.x interface.

No content available
Original Article
View Cached Full Text

Cached at: 05/18/26, 12:55 PM

El-EnderJ/NeoCalculator

Source: https://github.com/El-EnderJ/NeoCalculator


πŸ”’ NumOS

Open-Source Scientific Graphing Calculator OS

ESP32-S3 N16R8 Β· ILI9341 IPS 320Γ—240 Β· LVGL 9.x Β· CAS Engine Β· Natural Display V.P.A.M.


PlatformIO LVGL ESP32-S3 Website Framework Language License Status RAM Flash GitHub Stars GitHub Forks Support via Ko-fi



Technical Status: Major Architectural Refactor
We are currently migrating the core math engine to Giac and implementing STIX Two Math for LaTeX-quality rendering. If you are compiling from main, you might experience UI glitches. For a stable experience, please check the Releases section.



Image

Table of Contents

  1. Webpage
  2. What is NumOS?
  3. Key Features
  4. System Architecture
  5. CAS Engine
  6. Hardware
  7. Quick Start
  8. User Manual β€” EquationsApp
  9. Project Structure
  10. Build Stats
  11. Critical Hardware Fixes
  12. Project Status
  13. Technology Stack
  14. Comparison with Commercial Calculators
  15. Documentation
  16. Contributing

What is NumOS?

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 become the best open-source calculator in the world, rivalling the Casio fx-991EX ClassWiz, the NumWorks, the TI-84 Plus CE, and the HP Prime G2.

NumOS delivers:

  • Giac-backed CAS Engine β€” Symbolic math now runs through Giac C++ via src/math/giac/GiacBridge.cpp (The Big Switch). 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. 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.
  • 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 by SystemApp.

Key Features

FeatureDescription
Giac CAS BackendSymbolic evaluation through GiacBridge with UART parser/eval flow validated on hardware. Migration milestones completed: -DDOUBLEVAL, 64 KB loop stack, real-style complex_mode(false) with preserved i^2=-1 behavior
Unified Calculus AppSymbolic 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 detailed step-by-step output
EquationsAppSolves linear, quadratic, and 2Γ—2 systems (linear + non-linear via Sylvester resultant) with full step-by-step display
Bridge DesignerReal-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 LabPowder-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 AppSystem-wide toggles for complex number output (ON/OFF), decimal precision selector (6/8/10/12 digits), and angle-mode display
Natural DisplayReal 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
85+ CAS Unit TestsComprehensive test suite for the CAS, enable/disable via compile-time flag
PSRAMAllocatorCAS uses PSRAMAllocator<T> to isolate memory usage in the 8 MB PSRAM OPI
Variables A–Z + AnsPersistent storage via LittleFS β€” 216 bytes in /vars.dat
SerialBridgeFull calculator control from PC via Serial Monitor without physical hardware
SerialBridge DebugImmediate byte echo, 5-second heartbeat, 8-event circular buffer

Photo gallery

Neural Network Simulator:

Image

Fluid 2D Simulator:

Image

Periodic Table (Chemistry App):

Image

Grapher App:

Image

Steps (Equations App) (WIP, still in development, Alpha):

Image Image

Calculus App:

Image

Probability (Gaussian Distribution):

Image

Python App:

Image Image

Bridge Designer:

Image

Circuit Simulator (Circuit Core, Alpha):

Image

Particle Lab (Powder Toy like):

Image

Optics Lab:

Image


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, and simplify.

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

ModuleFileResponsibility
CASIntcas/CASInt.hHybrid BigInt: int64_t fast-path + mbedtls_mpi on overflow
CASRationalcas/CASRational.h/.cppOverflow-safe exact fraction (num/den with auto-GCD)
PSRAMAllocator<T>cas/PSRAMAllocator.hSTL allocator β†’ ps_malloc/ps_free for PSRAM
SymExpr DAGcas/SymExpr.h/.cppImmutable symbolic tree with hash (_hash) and weight (_weight)
ConsTablecas/ConsTable.hPSRAM hash-consing table: deduplication of identical nodes
SymExprArenacas/SymExprArena.hPSRAM bump allocator (16 blocks Γ— 64 KB) + integrated ConsTable
ASTFlattenercas/ASTFlattener.h/.cppMathAST (VPAM) β†’ SymExpr DAG with hash-consing
SymDiffcas/SymDiff.h/.cppSymbolic differentiation: 17 rules (chain, product, quotient, trig, exp, log)
SymIntegratecas/SymIntegrate.h/.cppSlagle integration: table, linearity, u-substitution, parts (LIATE)
SymSimplifycas/SymSimplify.h/.cppMulti-pass simplifier (8 iterations, fixed-point, trig/log/exp)
SymPolycas/SymPoly.h/.cppUnivariable symbolic polynomial with CASRational coefficients
SymPolyMulticas/SymPolyMulti.h/.cppMultivariable polynomial + Sylvester resultant
SingleSolvercas/SingleSolver.h/.cppSingle-variable equation: linear / quadratic / Newton-Raphson
SystemSolvercas/SystemSolver.h/.cpp2Γ—2 system: Gaussian elimination + non-linear (resultant)
OmniSolvercas/OmniSolver.h/.cppAnalytic variable isolation: inverses, roots, trig
HybridNewtoncas/HybridNewton.h/.cppNewton-Raphson with symbolic Jacobian and 16-seed multi-start
CASStepLoggercas/CASStepLogger.h/.cppStepVec in PSRAM β€” detailed steps (INFO/FORMULA/RESULT/ERROR)
SymToASTcas/SymToAST.h/.cppBridge: SolveResult β†’ MathAST Natural Display
SymExprToASTcas/SymExprToAST.h/.cppBridge: SymExpr β†’ MathAST. Includes convertIntegral() (+C)

CAS Tests β€” 53 Unit Tests

PhaseTestsCoverage
A β€” Foundations1–18Rational: add, subtract, multiply, divide, simplification. SymPoly: arithmetic, derivation, normalisation.
B β€” ASTFlattener19–32ASTβ†’SymPoly conversion for simple polynomials, constants, trig functions, powers.
C β€” SingleSolver33–44Linear (single solution), quadratic (2 real roots, repeated root, negative discriminant), steps.
D β€” SystemSolver45–532Γ—2 determined system, indeterminate (infinite solutions), incompatible system.
# platformio.ini β€” enable tests:
build_flags    = ... -DCAS_RUN_TESTS
build_src_filter = +<*> +<../tests/CASTest.cpp>

Hardware

ComponentSpecification
MCUESP32-S3 N16R8 CAM β€” Dual-core Xtensa LX7 @ 240 MHz
Flash16 MB QIO (default_16MB.csv)
PSRAM8 MB OPI (qio_opi β€” critical to prevent boot panic)
DisplayILI9341 IPS TFT 3.2β€œ β€” 320Γ—240 px β€” SPI @ 10 MHz (verified)
SPI BusFSPI (SPI2): MOSI=13, SCLK=12, CS=10, DC=4, RST=5
BacklightGPIO 45 β€” hardwired to 3.3V (pinMode(45, INPUT))
Keyboard5Γ—10 matrix (Phase 7) β€” Rows OUTPUT: GPIO 1,2,41,42,40 Β· Cols INPUT_PULLUP: GPIO 6,7,8…
StorageLittleFS on dedicated partition β€” persistent A–Z variables
USBNative USB-CDC on S3 β€” 115 200 baud

Full Pinout

ILI9341 Display

SignalGPIONotes
MOSI13FSPI Data In
SCLK12FSPI Clock
CS10Chip Select (active LOW)
DC4Data/Command
RST5Reset
BL45Hardwired to 3.3V β€” always INPUT

5Γ—10 Keyboard Matrix (driver Keyboard, Phase 7)

RowGPIORoleColumnGPIORole
ROW 01OUTPUTCOL 06INPUT_PULLUP
ROW 12OUTPUTCOL 17INPUT_PULLUP
ROW 241OUTPUTCOL 28INPUT_PULLUP
ROW 342OUTPUTCOL 3–93,15,16,17,18,21,47not yet wired
ROW 440OUTPUTβ€”β€”β€”

βœ… 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)
  • 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/your-user/numOS.git
cd numOS

# Build only
pio run -e esp32s3_n16r8

# Build and flash to ESP32-S3
pio run -e esp32s3_n16r8 --target upload

# Open serial monitor (115 200 baud)
pio device monitor

Serial Keyboard Control (SerialBridge)

With the Serial Monitor open, type characters to control the calculator:

KeyActionKeyAction
w↑ UpzENTER / Confirm
s↓ DownxDEL / Delete
a← LeftcAC / Clear
d→ RighthMODE / Return to menu
0–9Digits+-*/^.()Operators
SSHIFTr√ SQRT
tsingGRAPH
e= (equation)RSHOW STEPS

Note: lowercase s = DOWN; uppercase S = 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

  1. From the Launcher, select Equations with ↑↓ and press ENTER.
  2. The mode-selection screen appears.

Mode 1: Single-Variable Equation

  1. Select Equation (1 var) with ↑↓ and press ENTER.
  2. The editor opens. Type your equation with the = sign:
    • x^2 - 5x + 6 = 0 β†’ x₁=2, xβ‚‚=3
    • 2x + 3 = 7 β†’ x=2
    • x^2 = -1 β†’ no real solution (Ξ” < 0)
  3. Press ENTER to solve.
  4. 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
  5. Press SHOW STEPS (R) to view detailed steps:
    • Normalised equation
    • Discriminant value Ξ” = bΒ² βˆ’ 4ac
    • Quadratic formula applied
    • Computed roots
  6. Press MODE (h) to return to the main menu.

Mode 2: 2Γ—2 System

  1. Select System (2Γ—2) and press ENTER.
  2. Two fields appear: Eq 1 and Eq 2.
    • Type the first equation in x and y, press ENTER.
    • Type the second equation, press ENTER.
    • Example: 2x + y = 5 / x - y = 1 β†’ x=2, y=1
  3. Press ENTER to solve. Displays x = value, y = value.
  4. Press SHOW STEPS to view the full Gaussian elimination.
  5. Press MODE to return.

EquationsApp Keys

KeyAction
↑ ↓ ← β†’Navigate selection / cursor in editor
ENTERConfirm mode / Solve equation
DELDelete character
ACClear field
SHOW STEPSView detailed steps (from result screen)
MODEReturn 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
β”‚   β”œβ”€β”€ CONSTRUCCION.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_n16r8 in production mode (CAS tests disabled)

ResourceUsedTotalPercentage
RAM (data + bss)97 192 B327 680 B29.7 %
Flash (program storage)1 518 269 B6 553 600 B23.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:

#ProblemSymptomSolution
β‘ Flash OPI PanicBoot β†’ Guru Meditation: Illegal instructionmemory_type = qio_opi + flash_mode = qio
β‘‘SPI StoreProhibitedCrash in TFT_eSPI::begin() at address 0x10-DUSE_FSPI_PORT β†’ SPI_PORT=2 β†’ REG_SPI_BASE(2)=0x60024000
β‘’Display noiseHorizontal lines and visual artefactsReduce SPI to 10 MHz: -DSPI_FREQUENCY=10000000
β‘£LVGL black screenlv_timer_handler() invokes flush but no image appearsBuffers via heap_caps_malloc(MALLOC_CAP_DMA|MALLOC_CAP_8BIT) β€” never ps_malloc
β‘€GPIO 45 BL shortDisplay stops responding on backlight initpinMode(45, INPUT) β€” the pin is hardwired to 3.3V
β‘₯Serial CDC lostOutput invisible in Serial Monitor on connectwhile(!Serial && millis()-t0 < 3000) + monitor_rts=0 in platformio.ini

Project Status

PhaseDescriptionStatus
Phase 1Math Engine β€” Tokenizer, Shunting-Yard Parser, RPN Evaluator, ExprNode, VariableContextβœ… Complete
Phase 2Natural Display V.P.A.M. β€” fractions, radicals, exponents, smart 2D cursorβœ… Complete
Phase 3Launcher 3.0, SerialBridge, CalculationApp history, GrapherApp zoom/panβœ… Complete
Phase 4LVGL 9.x β€” ESP32-S3 HW bring-up, DMA, animated splash screen, icon launcherβœ… Complete
Phase 5CAS-Lite Engine (SymPoly, SingleSolver, SystemSolver, 53 tests) + EquationsApp UI (legacy milestone)βœ… Complete
CASCAS-S3 internal milestones: BigNum, hash-consed DAG, SymDiff 17 rules, SymIntegrate Slagle, SymSimplify 8-pass, OmniSolverβœ… Complete
Giac MigrationBig Switch to Giac: GiacBridge integration, UART parser/eval flow, -DDOUBLEVAL, 64 KB loop stack, real-mode defaults with i preservedβœ… Complete
Phase 6Statistics, Regression, Sequences, Probability, Matrices, Bridge Designerβœ… Complete
SimulationsParticleLab (30+ materials, electronics), CircuitCore (SPICE), Fluid2D (Navier-Stokes)βœ… Complete
Phase 7Complex numbers, base conversionsπŸ”² Planned
Phase 8Physical keyboard, custom PCB, rechargeable battery, 3D enclosure, WiFi OTAπŸ”² Planned

Technology Stack

LayerTechnologyVersion
MCU FrameworkArduino on ESP-IDF 5.xPlatformIO espressif32 6.12.0
UI / GraphicsLVGL9.5.0
TFT DriverTFT_eSPI2.5.43
FilesystemLittleFSESP-IDF built-in
LanguageC++17lambdas, std::function, std::unique_ptr
CAS MemoryPSRAMAllocator STL customPSRAM OPI 8 MB
Build SystemPlatformIO6.12.0
SimulationWokwiwokwi.toml

Comparison with Commercial Calculators

FeatureNumOSNumWorksTI-84 Plus CEHP Prime G2
Open Sourceβœ… MITβœ… MIT❌❌
Natural Displayβœ…βœ…βœ…βœ…
Symbolic CASβœ… Proβœ… 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-20€79€149€179
PlatformESP32-S3STM32F730Zilog eZ80ARM Cortex-A7

πŸ† NumOS already surpasses the TI-84 in CAS capability and cost, and is on track to match the NumWorks.


Documentation

DocumentDescription
ROADMAP.mdComplete phase history, milestones, and detailed future plan
PROJECT_BIBLE.mdMaster architecture, modules, code conventions, and development guides
CAS_UPGRADE_ROADMAP.mdFull roadmap for the 6-phase CAS upgrade
MATH_ENGINE.mdMath engine + CAS: design, algorithms, pipeline, and examples
HARDWARE.mdESP32-S3 pinout, complete wiring, critical bugs, and bring-up notes
CONSTRUCCION.mdPhysical assembly guide, 3D printing, and hardware testing
DIMENSIONES_DISEΓ‘O.mdDimensional 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:

  1. ** AI & Dev Tools:** Subscriptions for Claude/Copilot to accelerate C++ optimization and the Giac engine porting.
  2. Digital Presence: Domain hosting (neocalculator.tech / numos.org) and backend web services.
  3. 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.

Support me on ko-fi.com

Contributing

NumOS is an open-source project that aspires to grow with a community. Contributions are welcome!

  1. Fork the repository.
  2. Create a branch: git checkout -b feature/descriptive-name
  3. Follow the code conventions of the project.
  4. Verify the build passes: pio run -e esp32s3_n16r8
  5. If you add math logic, include tests in tests/.
  6. Open a Pull Request with a clear description of your changes.

Areas Where Help Is Most Needed

ModuleDescription
Sequences AppArithmetic and geometric sequences, Nth term, partial sums
Settings AppAngle mode DEG/RAD/GRA, brightness, factory reset βœ… Done, remaining: brightness PWM, factory reset
Advanced CASSymbolic derivatives and integrals βœ… Done, remaining: definite integrals, series
Better UI/UXGeneral improvement on UI and UX for real product release
MatricesMatrix editor, determinant, inverse, multiplication
Physical keyboardβœ… GPIO 4/5 conflict resolved β€” Keyboard driver 5Γ—10 implemented (Phase 7)
Custom PCBKiCad schematic with integrated ESP32-S3 + TP4056 charger

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)

The NeoCalculator firmware (NumOS) is licensed under the GNU GPL v3. We are proud to build upon the Giac engine by Bernard Parisse. In accordance with the GPL v3, all software source code in this repository is open for study, modification, and redistribution.

Hardware & Industrial Design

All rights reserved. The hardware design, 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.

Are proprietary and the intellectual property of Juan RamΓ³n. No commercial use or reproduction of the hardware is permitted without express authorization.

Note on Hardware IP: This proprietary status is a necessary measure for now. Our primary goal is to maintain the strict hardware control required to eventually achieve official β€œExam Legal” certification and to protect the project’s integrity from low-quality, unauthorized clones. Once the ecosystem is mature and certified, we plan to evaluate open-hardware licensing models.


Built with ❀️ and a lot of C++17

NumOS, The best open-source scientific calculator for ESP32-S3

Last updated: May 2026

Similar Articles

Designing a Scientific Calculator from scratch in FPGA

Lobsters Hottest

A detailed blog series documenting the design and implementation of a scientific calculator from scratch using FPGA, covering numerical methods, CPU architecture, microcode, and hardware prototyping.

ESP-EEG is an affordable 8-channel biosensing board

Hacker News Top

The Cerelog ESP-EEG is an affordable 8-channel biosensing board using the ADS1299 chip, offering cleaner signal with closed-loop active bias at a lower price point than OpenBCI. It supports open-source firmware and software like Brainflow, but has limitations such as USB-only connectivity and non-commercial use restrictions.