Selected work

rust_c6_os

rust based realtime operating systemm,,, the codebase in aetheris os folder

  • Makefile

✦ AETHERIS OS ✦

A phone-style operating environment for the ESP32-C6, written in Rust

Dual-OLED shell · multi-tap keypad login · 27 apps · piano with chords · Wi-Fi · OTA updates

Powered by Seleste Technologies

Rust no_std ESP32-C6 Wi-Fi 6 Embassy OTA


🧱 Tech stack

┌──────────────────────────────────────────────────────────────────────────────┐
│  APPS            27 apps · Piano · Settings · Wi-Fi · Games · IR lab · OTA   │
├──────────────────────────────────────────────────────────────────────────────┤
│  SHELL (os.rs)   Boot · Login · Home · App grid · Lock · Sleep · Wallpapers  │
├──────────────────────────────────────────────────────────────────────────────┤
│  UI TOOLKIT      Fb 128×64 framebuffer · embedded-graphics fonts · icons     │
│                  multi-tap text entry · phone-style keypad legend            │
├──────────────────────────────────────────────────────────────────────────────┤
│  SERVICES        Buzzer sound engine · IR/NEC decoder · Settings (CRC+SHA)   │
│                  Wi-Fi manager · DHCP · SNTP clock · OTA HTTP endpoint       │
├──────────────────────────────────────────────────────────────────────────────┤
│  RUNTIME         Rust 2024 · #![no_std] + alloc · Embassy async executor     │
│                  esp-rtos · esp-hal 1.2 · esp-radio · embassy-net/smoltcp    │
├──────────────────────────────────────────────────────────────────────────────┤
│  HARDWARE        ESP32-C6 (R8N16, 16 MB flash) · 2× SSD1306 OLED · 4×4 keypad│
│                  IR sensor · passive buzzer · green status LED               │
└──────────────────────────────────────────────────────────────────────────────┘
BlockTechnologyUsed for
🦀 LanguageRust 2024, #![no_std], allocall firmware
⚙️ HALesp-hal 1.2GPIO, I²C, LEDC (PWM), RNG, reset
🧵 Scheduleresp-rtos + Embassyasync tasks, timers
📡 Radioesp-radio (Wi-Fi 6, 2.4 GHz)scan, join, auto-reconnect
🌐 Networkembassy-net (smoltcp)DHCP, DNS, UDP (SNTP), TCP (OTA)
🖥 Graphicsembedded-graphics + own SSD1306/SH1106 drivertwo OLED panels
💾 Storageesp-storage + esp-bootloader-esp-idfsettings, partitions, OTA slots
🔐 Securitysha2 (salted SHA-256)login password hash
🔊 AudioLEDC PWM square waveboot jingle, alerts, piano, music

Honest scope: Aetheris is a firmware shell — it runs on the Espressif ROM + bootloader and a Rust runtime. It is not a standalone desktop-class OS, but it boots, logs you in, launches apps, stores settings, joins Wi-Fi and updates itself over the air like one.


✨ Features

  • Boots like a computer – BIOS-style self-test lines on the main OLED while cmatrix digital rain falls on the second one, then the SELESTE TECHNOLOGIES splash with a boot jingle.
  • Login screen – username + password typed on the keypad like an old button phone (multi-tap: 2 = a b c 2 …). Phone keypad legend is shown live on the second OLED. 5 wrong attempts → 15 s lockout.
  • Home screen – clock, date, Wi-Fi & volume icons, per-screen animated wallpapers (9 to choose from, one per OLED), quick-launch dock.
  • App grid – icons of every app drawn on the second OLED (4×2 per page), name/description on the main one.
  • Settings like a small phone – Wi-Fi (scan, join, forget, auto-connect), display (wallpapers, brightness, invert, screen-off, auto-lock), sound (volume, key click, boot sound), security (username/password), date & time, IR, system (reboot, sleep, factory reset).
  • Wi-Fi – scans and lists networks with signal; password typed on the keypad. When connected the green LED lights and a Wi-Fi icon appears. Clock syncs by SNTP.
  • IR sensor – wakes the screen with a wave, counts objects, measures RPM, decodes NEC remote codes.
  • Buzzer voices – boot jingle, accept, error, back, notify, alarm, lock/unlock, connect/disconnect, shutdown… all with adjustable volume.
  • Piano – every semitone (sharps/flats) with real frequencies, octave shift, and a chord mode (maj, min, 7, maj7, m7, dim, aug, sus2, sus4, 6, m6, add9).
  • OTA – flash new firmware over Wi-Fi into the inactive slot, automatic rollback protection.
  • Persistent – settings and notes survive reboots (CRC-protected flash partition).

🔌 Hardware

#PartNotes
1ESP32-C6 dev board, R8N16 module (16 MB flash)3.3 V logic
2OLED A – 0.96″ 128×64, yellow + blue, I²C (GND VDD SCK SDA)main screen: top 16 px (yellow) = title bar
3OLED B – 0.96″ 128×64, blue, I²C (GND VDD SCK SDA)second screen: visuals, icons, wallpapers
44×4 membrane keypad (1-9 0 * # A B C D, 8 pins)4 rows + 4 columns
5IR sensor (OUT GND VCC)obstacle/proximity module or TSOP-style receiver
6Passive buzzer (GND I/O VCC)driven with PWM
7Green LED + resistor (220 Ω – 1 kΩ)Wi-Fi status

🔧 Wiring (all connections)

Pin map

ComponentPin on componentESP32-C6Note
OLED A (yellow/blue)SDAGPIO6hardware I²C0
SCK / SCLGPIO7hardware I²C0
VDD3V3
GNDGND
OLED B (blue)SDAGPIO20own bus (bit-banged)
SCK / SCLGPIO21own bus (bit-banged)
VDD3V3
GNDGND
Keypad rowsR1 / R2 / R3 / R4GPIO0 / GPIO1 / GPIO2 / GPIO3driven low one at a time
Keypad columnsC1 / C2 / C3 / C4GPIO10 / GPIO11 / GPIO18 / GPIO19inputs with pull-up
IR sensorOUTGPIO14active-low
VCC3V3
GNDGND
Green LEDanode → resistor →GPIO22
cathodeGND
Passive buzzerI/OGPIO23PWM tone
VCC (VSS)3V3
GNDGND

Why two separate I²C buses? Both panels normally share the address 0x3C, so they cannot sit on one bus. Giving OLED B its own pins means no address jumpers or multiplexer are needed and each screen is fully independent.

Pins deliberately not used: GPIO4, 5, 8, 9, 15 (boot/strapping), GPIO12/13 (USB), GPIO16/17 (UART0 console), GPIO24-30 (flash).

Wiring diagram

flowchart LR
    subgraph MCU["ESP32-C6 DevKit (R8N16)"]
        G6["GPIO6 (SDA0)"]
        G7["GPIO7 (SCL0)"]
        G20["GPIO20"]
        G21["GPIO21"]
        R["GPIO0 · 1 · 2 · 3"]
        C["GPIO10 · 11 · 18 · 19"]
        G14["GPIO14"]
        G22["GPIO22"]
        G23["GPIO23"]
        V["3V3"]
        GND["GND"]
    end
    OA["OLED A<br/>yellow + blue<br/>SDA · SCK · VDD · GND"]
    OB["OLED B<br/>blue<br/>SDA · SCK · VDD · GND"]
    KP["4x4 keypad<br/>R1-R4 · C1-C4"]
    IR["IR sensor<br/>OUT · VCC · GND"]
    LED["Green LED<br/>+ resistor"]
    BZ["Passive buzzer<br/>I/O · VCC · GND"]
    G6 --- OA
    G7 --- OA
    G20 --- OB
    G21 --- OB
    R --- KP
    C --- KP
    G14 --- IR
    G22 --- LED
    G23 --- BZ
    V -.3.3V.- OA & OB & IR & BZ
    GND -.GND.- OA & OB & IR & LED & BZ

Keypad matrix

The 8-pin membrane connector is normally ordered R1 R2 R3 R4 C1 C2 C3 C4 (left → right, rows first).

              C1(GPIO10) C2(GPIO11) C3(GPIO18) C4(GPIO19)
R1 (GPIO0)  ┤     1          2          3          A
R2 (GPIO1)  ┤     4          5          6          B
R3 (GPIO2)  ┤     7          8          9          C
R4 (GPIO3)  ┤     *          0          #          D

If your membrane is ordered differently, open Menu → Key Test: it shows the row/column of every key you press, and KEYMAP in src/input.rs (or the pin order in src/bin/main.rs) can be adjusted to match.

Power

Everything runs from the board's 3.3 V rail and a shared GND. Total current is well within USB power (two OLEDs ≈ 20-40 mA, Wi-Fi bursts ≈ 250 mA peak).


🏗 Architecture

flowchart TB
    subgraph HW["Hardware tasks (src/hw)"]
        KS["Keypad scanner<br/>10 ms · debounce · long-press · repeat"]
        IRT["IR task<br/>edge timestamps · NEC decode"]
        BZT["Buzzer task<br/>LEDC PWM · glides · pre-emption"]
        WFT["Wi-Fi manager<br/>scan · join · reconnect"]
        NET["embassy-net<br/>DHCP · DNS · SNTP"]
        OTA["OTA HTTP server :80"]
        STO["Storage task<br/>debounced flash writes"]
    end
    subgraph CORE["Hardware-independent core"]
        OS["Os shell<br/>boot · login · home · menu · power"]
        APPS["27 Apps<br/>trait App"]
        SYS["Sys context<br/>settings · wifi · ir · rng · effects"]
    end
    KS -->|KeyEvent| OS
    IRT -->|IrSnap| SYS
    WFT -->|WifiState| SYS
    OS --> APPS
    APPS -->|Effect: sound/led/wifi/save| SYS
    SYS -->|SoundCmd| BZT
    SYS -->|WifiCmd| WFT
    SYS -->|encoded settings| STO
    OS -->|Fb A + Fb B| OLEDS["OLED A (I²C0) · OLED B (soft I²C)"]
    WFT --- NET --- OTA

Design rule: apps never touch hardware. They draw into two framebuffers and push effects (play sound, pulse LED, save, join Wi-Fi…). The main loop turns effects into real actions. That keeps the whole UI testable on a PC (see aetheris-sim).

Boot sequence

stateDiagram-v2
    [*] --> BIOS: power on
    BIOS: BIOS self-test (OLED A text + OLED B cmatrix rain)
    BIOS --> Splash: 3.6 s
    Splash: SELESTE TECHNOLOGIES logo + boot jingle
    Splash --> Login: 7 s (or any key)
    Login --> Home: correct credentials
    Login --> Login: wrong (5 tries then 15 s lockout)
    Home --> Menu: A / 5 / #
    Menu --> App: open
    App --> Menu: back
    Home --> Login: lock (C or auto-lock)
    Home --> Sleep: idle
    Sleep --> Home: key press or IR wave

Two screens, two jobs

OLED A (yellow/blue)OLED B (blue)
Rolemain interaction + title bar (yellow strip)visuals & context
BootBIOS text → logocmatrix rain → logo reveal
Loginusername / password fieldslive phone-keypad legend
Homeclock, date, status iconswallpaper + favourite-app dock
Menuapp name + description + pageicon grid (4×2)
Settingsthe list you navigatepreview (wallpapers, volume, Wi-Fi…)
Appscontrols and valuesgraphs, keyboards, visualisers

⌨ Keypad

KeyMenusText entryAnywhere
2 / Aup–
8 / Bdown–
4 / Cleft–
6 / Dright–
5 / #OK# = confirm
*back* = backspace (hold = clear)hold * = jump to Home
0-9multi-tap letters

Multi-tap text entry (login, Wi-Fi password, notes, Morse…): press a key repeatedly within 1 s to cycle its characters. A = abc / ABC / 123 mode · B = show/hide password · C = clear · D = cancel / switch field.

 1 .,?!@-_   2 abc   3 def
 4 ghi       5 jkl   6 mno
 7 pqrs      8 tuv   9 wxyz
 *  del      0 space #  ok

Home shortcuts: A/5/# apps · B settings · C lock · D Wi-Fi · 1-4 favourite apps (Settings, Piano, Calculator, Snake).


📱 Apps (27)

#AppWhat it doesControls
1Settingsfull phone-style settings treearrows · 5 OK · * back
2Wi-Fiscan networks, signal & channel2/8 select · 4/6 rescan
3Pianochromatic keyboard + chord mode, live keyboard on OLED Bsee below
4Music Box7 public-domain melodies with piano-roll visualiser2/8 song · # play
5Calculator+ − × ÷ with decimals and historyA + B − C × D ÷ · * point · # = · hold # clear
6Clockdigital time/date + analog face# 12/24 h
7Stopwatchlaps# start/stop · A lap · B reset
8Timercountdown with alarmdigits mmss · A/B/C = 1/5/10 min · # go
9Notes600-char notepad saved to flashmulti-tap · # save · D exit
10Snakearcade classic, speeds uparrows · # restart
11Pongvs CPU2/8 paddle · # serve
12Simonmemory game with tones + LED1/A 3/D 7/B 9/C
13Reactionreflex timer (ms)any key
14DiceD6 ×1-3, D20, coin, 1-100 (hardware RNG)5 roll · A mode · B count
15Metronome30-240 BPM, time signatures, LED + pendulum4/6 ±1 · 2/8 ±5 · 0 signature · # start
16Tone Labtone / sweep / siren generator4/6 ±10 Hz · 2/8 ±100 Hz · 0 mode · # on
17Morsetype text → buzzer + LED in Morse# send · B speed
18IR Monitorsensor state, pulse widths, NEC remote codes# reset
19IR Countercounts objects breaking the beam# reset
20TachometerRPM from IR pulses4/6 pulses/rev
21Matrixfull-screen cmatrix rain on both OLEDs2/8 speed · # invert
22LifeConway's Game of Life2468 cursor · 5 toggle · # run · A random
23SignalWi-Fi RSSI graph + "find the router" Geiger beeper# beeper
24Systemheap, uptime, flash, MAC, FPS, memory graph2/8 page
25Key Testshows row/col of each key, live matrixhold * exit
26Torchboth OLEDs white + LEDany key = mode
27OTA Updatewireless firmware update# server on/off

🎹 Piano

Notes follow the keypad's reading order, so one octave of all 12 semitones is under your fingers:

  1  C     2  C#    3  D
  4  D#    5  E     6  F
  7  F#    8  G     9  G#
  *  A     0  A#    #  B
  • Note mode – hold a key to sustain; A/B = octave up/down (1-7); the exact frequency is shown (A4 = 440.00 Hz).
  • Chord mode (C) – press a root note and the chord is arpeggiated at 55 ms per note (a single buzzer cannot play polyphony, so this is how chiptunes do it); A/B pick the chord type: maj min 7 maj7 m7 dim aug sus2 sus4 6 m6 add9. OLED B lights the chord tones on the keyboard.
  • D = exit (the * key is a note here).

🔊 Sounds

SoundWhenCharacter
Boot jinglesplash screenrising glide + C-major arpeggio + sparkle
Boot beepseach BIOS lineshort tick
Click / typingkey presses7-9 ms tick
Acceptsuccesstwo rising notes
Errorwrong password, invalid inputlow double buzz
Back / Selectnavigationshort blips
Notifyscan results, boot donetriple chime
Alarmtimer done3× beep pattern
Lock / Unlocklock screendown / up arpeggio
Connect / DisconnectWi-Firising / falling
Shutdownrebootdescending + glide

Volume 0-5 (Settings → Sound) changes the PWM duty cycle. A passive buzzer cannot play recorded speech; it plays square-wave tones.


⚙ Settings map

mindmap
  root((Settings))
    Wi-Fi
      Radio on/off
      Scan networks
      Status / IP
      Saved network
      Auto-connect
      Forget
      Sync clock
    Display
      Wallpaper A
      Wallpaper B
      Brightness A/B
      Invert A/B
      Screen off
      Auto-lock
    Sound
      Volume
      Key click
      Boot sound
      Test sounds
    Security
      Username
      Password
      Auto-lock
      Lock now
    Date & Time
      Set time
      Set date
      Time zone
      Sync via Wi-Fi
    IR Sensor
      Wake on IR
      Live state
    System
      Free heap
      Storage
      Sleep screens
      Reboot
      Factory reset
    About

Wallpapers: Matrix Rain · Starfield · Waves · Retro Grid · Bubbles · Seleste · Circuit · Radar · Plain — chosen independently for each OLED with a live preview on the second screen.


📡 Wi-Fi, time and OTA updates

  • Join a network: Settings → Wi-Fi → Scan networks → pick one → type the password → connected. The credentials are saved and re-used at every boot (Auto-connect).
  • Status LED: solid = connected · fast blink = connecting · slow blink = scanning.
  • Clock: synced from pool.ntp.org right after connecting (time zone default UTC+3, change in Settings → Date & Time).
  • OTA update:
    1. Connect to Wi-Fi, open Menu → OTA Update, press # (the screen shows the IP address).
    2. On your PC:
      cargo build --release
      espflash save-image --chip esp32c6 target/riscv32imac-unknown-none-elf/release/aetheris aetheris.bin
      curl --data-binary @aetheris.bin http://<device-ip>/ota
      
    3. The image is written to the inactive slot (ota_0 ⇄ ota_1), validated (ESP32-C6 image header), activated, and the device reboots into it. A newly booted image is marked valid automatically; if it crashes before that the bootloader can roll back.

    OTA only works if the first flash used the custom partition table (partitions.csv, done automatically by cargo run). The OTA endpoint has no password: it is only open while you have the OTA app running — use it on a trusted network.


🚀 Build & flash

# one-time: toolchain (rust-toolchain.toml selects nightly + rust-src automatically)
cargo install espflash

cargo build --release          # compile
cargo run --release            # flash over USB with partitions.csv + open serial monitor

Put the board in download mode (hold BOOT, tap RESET) if the port is not detected. Logs: ESP_LOG=info (set in .cargo/config.toml).

First login

UsernamePassword
admin1234

The login screen shows this hint only while the default login is still active. Change both in Settings → Security. Passwords are stored as salted SHA-256; the saved Wi-Fi password must be stored in clear text (the radio needs it).

Memory & flash budget

Used
Firmware image≈ 0.9 MB (slots are 3 MB each)
Heap150 KB reserved (Wi-Fi ≈ 55 KB)
Flash layoutnvs 24K · otadata 8K · phy 4K · storage 64K · ota_0 3M · ota_1 3M

🗂 Project structure

aetheris/
├── Cargo.toml · rust-toolchain.toml · build.rs · partitions.csv
├── .cargo/config.toml          target, runner (espflash + partition table), build-std
├── wokwi.toml · diagram.json   Wokwi simulation of the wiring above
├── CODEX.md                    notes for AI/code assistants working on this repo
└── src/
    ├── bin/main.rs             hardware init, task spawning, main UI loop
    ├── lib.rs
    ├── os.rs                   shell: boot · login · home · menu · power
    ├── sys.rs · settings.rs    shared context · persistent settings (CRC + SHA-256)
    ├── input.rs                keys, debounce, long-press, multi-tap text entry
    ├── fb.rs · gfx.rs · icons.rs · wallpaper.rs · trig.rs     drawing
    ├── sound.rs · notes.rs · tunes.rs · ir_decode.rs · clock.rs
    ├── apps/                   mod.rs (registry) · settings · tools · music · games · ir · net · widgets
    └── hw/                     oled · soft_i2c · keypad · buzzer · ir · storage · net · shared

Adding your own app

  1. Create a struct that implements App (key, tick, draw) in src/apps/.
  2. Add one line to the apps! { … } table in src/apps/mod.rs (name, description, icon number, constructor).
  3. Add an icon N in src/icons.rs (24×24, drawn with lines/circles/rects).
  4. Request sound/LED/Wi-Fi/save through sys.snd(), sys.led_pulse(), sys.wifi_cmd(), sys.save().

🧪 PC simulator

The hardware-independent half of the OS (shell, apps, UI, settings codec, NEC decoder…) can be run and unit-tested on a PC. It lives in the separate aetheris-sim folder (next to this project) so the ESP target config does not interfere.


🩺 Troubleshooting

SymptomFix
A screen stays blackcheck SDA/SCK order and 3V3/GND; OLED …: false in the serial log means no ACK. Address is 0x3C (OLED_ADDR in main.rs; some modules use 0x3D)
Image garbled / shifted 2 pxyour panel is a 1.3″ SH1106 → set OLED_A_SH1106 / OLED_B_SH1106 = true
Screen upside downset OLED_A_FLIP / OLED_B_FLIP = true
Wrong keysopen Key Test, compare with the matrix table, adjust KEYMAP / pin order
Buzzer too quietSettings → Sound → Volume 5; a 3-pin module with transistor is louder than a bare piezo
IR counts nothingan obstacle module needs its onboard pot adjusted; a TSOP receiver only reacts to 38 kHz remotes
Wi-Fi won't join2.4 GHz only; WPA2 networks; password length 8-63
curl OTA says "not an ESP32-C6 image"use espflash save-image --chip esp32c6 …, not the raw ELF

Aetheris OS · built with 🦀 Rust · Seleste Technologies

Visit project ↗

← All projects