Skip to content

Releases: rdlu/sofleii

v3.1.0 — right-half battery, build info over RPC, sofleii CLI

Choose a tag to compare

@rdlu rdlu released this 19 Oct 19:11
5c53b30

This release adds two things to the left half for desktop tools: it reports the right half's battery to the host, and it reports which build it runs over Studio RPC. It also adds the first release of sofleii, the CLI that reads both. The keymap is unchanged.

Upgrading from v3.0.0

No settings reset or re-pairing is needed. Only the left half's firmware changed: copy eyelash_sofle_studio_left.uf2 to the left half. mise run flash-release flashes both halves from the latest main build, which also works; reflashing the right half does no harm.

Firmware

  • Right half's battery reaches the host (#45, PR #65). The left (central) half fetches the right half's level over the split link and exposes it as a second Battery Service. Its 0x2901 description is "Peripheral 0". BlueZ's Battery1 and upower still only see the left half's level; sofleii battery reads both.
  • Build info over Studio RPC (#46, PR #70), from cormoran/zmk-feature-device-info at 8cedd2dd, left half only. It reports this repo's version, the zmk version, the Zephyr version, the board, the build time and hash, the device id and the reset cause. Reads work without unlocking Studio.
    • A CI build reports the bare short SHA of the commit it was built from. This one reports 5c53b30. A local build reports git describe output, with -dirty if tracked files had uncommitted changes.
  • Our own physical layout (PR #57), adapted from ZMK's MIT Sofle layout. It replaces the vendor's unlicensed layout files. config/eyelash_sofle.json is now generated from the shield's .dtsi (mise run layout).

sofleii CLI (new)

A Rust CLI in sofleii/ that talks to the keyboard over USB or Bluetooth, with no browser and no Chrome flags. Run it with mise run sofleii -- <command>.

Command What it shows
sofleii status transport, active BLE profile, output priority, both batteries; --format waybar for a bar module
sofleii battery both halves' battery levels, read over BlueZ GATT
sofleii info device, build info, hardware, Studio subsystems
sofleii profiles the 5 BLE profiles (active, connected, bonded, address, name)
  • --transport auto (the default) uses USB when the keyboard's serial port is present and answers, and otherwise uses Bluetooth.
  • Every command takes --json, with a stable output format ("schema": 1), and returns documented exit codes (#44).
  • The commands only read for now. Profile switching, output priority, update checks and a ratatui TUI are planned (#44, #47, #48).

Studio over Bluetooth only answers while Bluetooth is the selected output. While USB is plugged in and output priority is USB, Bluetooth requests time out; auto already handles this. Docs: https://sofleii.dlu.one/docs/sofleii/

Docs site

  • Printable keymap PDF: four A4 pages, in colour carried by lines and text, not fills. https://sofleii.dlu.one/sofleii-keymap.pdf, and attached to this release.
  • The keymap is now its own section of the site, and the site has a new magenta theme (#54, #59).

Memory budget

The left half uses 460 KB of 792 KB flash (58.1%) and 124 KB of 256 KB RAM. That's about 3.3 KB more flash and 0.8 KB more RAM than v3.0.0, for the battery proxy and device info. The right half is unchanged at 323 KB / 77 KB.

Full changelog: v3.0.0...v3.1.0

v3.0.0 — ZMK main / Zephyr 4.1

Choose a tag to compare

@rdlu rdlu released this 17 Oct 16:12
dc7a81c

The firmware moves off ZMK v0.3 / Zephyr 3.5 onto ZMK main (Zephyr 4.1), via cormoran's main+dya fork. The keymap is unchanged: same layers, home-row mods, combos, game mode and underglow. This is a major version because flashing it needs a settings reset and re-pairing.

⚠️ Upgrading from v2.x

  1. Flash settings_reset.uf2 on both halves.
  2. Flash the new left and right firmware.
  3. Power both off, then turn on the right half first, then the left.
  4. On each computer, remove the old "Eyelash Sofle" pairing (for example bluetoothctl remove <MAC>), then pair again from SYS|NUM + BT0–BT4.
mise run flash-release-reset   # once per half
mise run flash-release         # left, then right

Highlights

  • ZMK main + Zephyr 4.1 (cormoran main+dya @ e5c9b691, Zephyr v4.1.0+zmk-fixes @ 10ba6d0c, both SHA-pinned). Upstream ZMK has no v0.4 release. This is the line cormoran's "v4" modules target.
  • Studio still works over USB and Bluetooth (locking off). The custom-RPC plumbing is kept: cormoran_ble (BLE profile list/switch/rename/unpair, output priority) and cormoran_rsr (runtime encoder binding). That's the groundwork for sofleii, a planned Rust/ratatui status + control TUI.
  • Leaner module set. Removed battery-history and settings-rpc (still v0.3-only upstream) and runtime-input-processor (only the vendor overlay used it). Their DYA Studio panels are gone.
  • Shield vendored in-repo at config/boards/shields/eyelash_sofle/. The vendor module only supported v0.3. While copying it, a duplicate ext-power node was removed (the v4 board already drives P0.13), along with a deprecated BT_CTLR default.

Breaking / renamed

  • Board: nice_nano_v2 → nice_nano//zmk.
  • Reset artifact: settings_reset-nice_nano_v2-zmk.uf2 → settings_reset.uf2.
  • CONFIG_WS2812_STRIP removed (no longer exists on Zephyr 4.1).

Tested on hardware (2026-10-17)

Typing on both halves, split link, volume encoder, joystick mouse, per-layer underglow, all layers, game mode, nice!view, soft-off (Q+S+Z, 2 s; wake with one reset press), BLE host pairing, and Studio over USB (raw RPC: device info, custom subsystems, BLE profiles). Studio over BLE in the browser wasn't re-tested.

Memory budget

Left 457 KB / 792 KB flash (57.7%), 123 KB / 256 KB RAM. Right 323 KB (40.8%) / 77 KB. The newer Zephyr/BLE stack costs about 47–53 KB of flash per half versus v2.8.0, even after dropping three modules. Plenty of headroom remains.

Known issue (upstream)

cormoran_ble.get_split_info returns an empty reply. The module never sets nanopb's has_info, and it hardcodes peripheral_connected = false anyway. This doesn't affect the keyboard; it only matters to RPC clients.

Docs

CLAUDE.md said the soft-off combo was Q+A+Z. It's Q+S+Z (positions 14 28 40).

Full changelog: v2.8.0...v3.0.0

v2.8.0 — No Man's Sky gaming profile + Delete restored on BASE

Choose a tag to compare

@rdlu rdlu released this 02 Sep 19:51
cd41c50

A third gaming profile for No Man's Sky, and Delete comes back to the base layer — it had gone missing in v2.7.0.

Highlights

  • NOMANSKY gaming layer (8) — pick it from the game launcher with joystick ← (↑ is still Minecraft, ↓ generic, center cancels). Same locked chassis as the other game layers: no home-row mods, no hold-taps, pure &kp so every hold works for movement.

    • LSHIFT and LCTRL are swapped versus the Minecraft/GAME layers — Shift (run) sits on the pinky at pos 39, Ctrl on the left thumb at pos 56. NMS runs on Shift and leaves Ctrl unused.
    • pos 0 = F12 for Steam screenshots, replacing Minecraft's F3 debug screen and GAME's ` console.
    • Underglow: white/silver. The OLED reads NOMANSKY.
    • Both game combos work here too — 0+12 exits to BASE, 0+5 re-opens the picker.
  • Delete is back on BASE: mo(CODE) + BSPC. v2.7.0 swapped the right-thumb &lt 2 DEL for a plain &mo 2 (to remove the hold-tap lag entering CODE) and relocated DEL to the MEDIA layer, but the docs never caught up — so it read as a broken key rather than a moved one. DEL now lives on the CODE layer at the left-thumb BSPC position: hold CODE with the right thumb, tap BSPC with the left. Cross-hand, so no same-hand roll, and CODE stays instant with no hold-tap cost reintroduced. MEDIA + left-index still types DEL as well.

Docs

KEYMAP.md and CLAUDE.md updated for both changes, plus two pre-existing errors fixed: the game-chassis thumb positions were documented off by one (LSHIFT is @56 with SPACE @57+59), and the gaming RGB note still claimed AUTO_OFF_USB=y when it has been =n since v2.6.0.

Memory budget

Nine layers now. Left half 410 KB / 792 KB flash (51.8%) and 109 KB / 256 KB RAM — the new layer cost about 1 KB flash and 2 KB RAM. Right half unchanged at 270 KB / 62 KB. Plenty of headroom.

Fixed after release: local builds

The release notes originally shipped a "known issue" here claiming config/west.yml pinned zmk to a moving branch that had advanced to Zephyr 4.1. That diagnosis was wrong. The manifest was correct; the local west workspace had drifted from it — the zmk/ clone's git remote still pointed at zmkfirmware/zmk from before the switch to cormoran's fork, so west update silently could not fetch the pinned revision and left the tree on an unrelated commit. CI was never affected, and this release was built from CI.

Fixed in #41, which also adds mise run doctor / doctor-fix / doctor-ci to catch that class of drift automatically.

Flashing

mise run flash-release        # double-tap reset: left, then right

If a previous ZMK Studio edit is stuck, run mise run flash-release-reset first.

Full changelog: v2.7.0...v2.8.0

v2.7.0 — RGB layer module: moods, master brightness, persistence

Choose a tag to compare

@rdlu rdlu released this 28 Jun 18:20

The macro-based per-layer underglow from v2.6.0 is replaced by an event-driven firmware module (zmk-rgb-layer/). Lower latency, switchable resting "moods", a brightness that every colour respects, and settings that survive a reboot.

Highlights

  • Per-layer colour — the underglow shows the highest active layer (NAV indigo, CODE magenta, MEDIA amber, SYS red, gaming green/blue). A diff-cache emits a single &rgb_ug call per change, so it's noticeably snappier than the old macros.
  • Three base moods — on the SYS layer, Z = dark (off), X = dim teal (default), C = cycle effects (breathe → spectrum → swirl). Live preview while you hold SYS.
  • Master brightness (day/night) — SYS Q/A brighten/dim everything; the level is applied to every layer colour.
  • Persists across reboots — your mood + brightness are saved and restored on boot, so a power-cycle or soft-off comes back the way you left it instead of resetting to the default teal.
  • Both halves stay in sync — if one half powers on later than the other, the central re-asserts the correct colour once they reconnect.

Under the hood

  • Central-only Zephyr module; the peripheral strip updates via the forwarded global &rgb_ug behaviour.
  • Now built in CI (the in-repo module is wired into build.yaml via ZMK_EXTRA_MODULES).
  • Keymap cleanup: CODE is a plain &mo 2 (no hold-tap), and DEL moved to the MEDIA layer.

Not included (tracked separately)

  • A reactive "typed-column lights up" effect — the underglow is per-column-addressable, but it needs a custom strip-driving engine. Follow-up issue filed.

Flashing

mise run flash-release        # double-tap reset: left, then right

If a previous ZMK Studio edit is stuck, run mise run flash-release-reset first. See the README for details.

Closes #33.

v2.6.0 — Per-layer underglow colour feedback (smart-off)

Choose a tag to compare

@rdlu rdlu released this 27 Jun 19:50
5d501fd

Per-layer underglow colour feedback (smart-off)

The base layer rests dark; holding a layer lights the underglow with that layer's colour, and releasing turns it off again — the strip glows only while you're in a non-base layer. Works the same on USB and Bluetooth.

Layer Colour
BASE (resting) off / dark
NAV indigo
CODE magenta
MEDIA amber
SYS|NUM red
MINECRFT / GAME green / blue (unchanged)

Implemented entirely in the keymap by wrapping each momentary-layer key in an &rgb_ug macro (RGB_ON + solid + colour on hold, RGB_OFF on release). CODE uses a custom &lt_code hold-tap so tap=DEL is preserved.

Also in this release

  • Corrected AUTO_OFF_USB handling — it's the battery-saver "off when USB disconnected" (not "off on USB"); now disabled so the base stays dark on USB.
  • Fixed a latent mise run draw bug (parse step was missing -c, silently dropping keymap-drawer icon/label maps).
  • KEYMAP.md underglow section + README memory-budget refresh.

Known issue

  • Colour-change lag on layer holds — structural to the stateless keymap approach (three sequential split-forwarded &rgb_ug calls + 50 ms render tick). A proper fix (event-driven listener, optional stateful base moods) is tracked in #33.

Not possible on this hardware

  • A QMK-style per-key reactive "ripple" effect — the board has only 7 sparse underglow LEDs per half (not per-key) and ZMK underglow has no keypress-reactive engine.

Full changelog: PR #32

v2.5.0 — ZMK Studio over USB + Bluetooth (cormoran fork)

Choose a tag to compare

@rdlu rdlu released this 25 Jun 12:33

Live keymap editing via ZMK Studio over USB and Bluetooth, built on cormoran's ZMK-Studio fork. Hardware-verified on the Eyelash Sofle (nice!nano v2). The Studio web app lives in a separate project (#30); this release is firmware.

Highlights

  • USB + BLE keymap editing — connect ZMK Studio over /dev/ttyACM (CDC-ACM) or Bluetooth (GATT). BLE editing is the headline feature.
  • Encoder is web-configurable — switched to runtime-sensor-rotate (volume by default) on all 8 layers; reassign it live in Studio.
  • Studio locking off (CONFIG_ZMK_STUDIO_LOCKING=n) — no unlock key needed.

Under the hood

  • ZMK → cormoran/zmk@v0.3-branch+dya (v0.3 base) + 5 cormoran DYA modules; eyelash_sofle pinned to dya commit 4848e21f.
  • Board → shield migration: builds are now -DSHIELD="eyelash_sofle_* nice_view"; .uf2 names and flash tasks unchanged.
  • Custom keymap (layers, HRM, gaming) otherwise untouched.

Memory budget (CI build, both transports compiled in)

Build Flash RAM
Left (Studio + nice_view) 51.5% 41.9%
Right (nice_view) 34.2% 24.3%
Settings reset 5.7% 4.4%

Flashing & use

  • Flash: mise run flash-release (download from CI) — or build locally with mise run build-flash.
  • Studio: connect the left half via USB, or pair over BT, then open the Studio app. On Linux, BLE editing needs Chrome's chrome://flags/#enable-experimental-web-platform-features flag (Web Bluetooth) — not a firmware limitation.
  • Rollback: mise run flash-release after reverting, or reflash a prior release.

See PR #31 for full detail and the hardware verification log.

v2.0.0 — Gaming mode (picker + Minecraft + generic GAME layers)

Choose a tag to compare

@rdlu rdlu released this 21 Jun 14:23

Adds a dedicated gaming mode (closes #11) and supersedes the v2.0.0-rc1 prototype, re-applied onto current main (was v1.6.0).

Gaming mode (layers 5–7)

Locked layers that strip all home-row-mods / hold-tap / caps_word — WASD, SPACE and modifiers are pure keys.

  • Enter: hold mo(SYS|NUM) (left thumb) → tap the right-thumb outer key → GAME? picker. Joystick ↑ = Minecraft, ↓ = generic GAME (FPS/MOBA), center = cancel.
  • Chassis: plain WASD/QWER + plain right half for chat; LCTRL (pinky), LSHIFT (left thumb), SPACE on both thumbs, F13 push-to-talk, mo(MEDIA) reachable. Joystick = audio cluster + center Enter.
  • Exit/switch: top corners (0 + 12) → exit to BASE (RGB off); top-left pair (0 + 5) → re-pick.
  • Indicator: OLED shows MINECRFT / GAME. Per-game RGB underglow shows on battery (suppressed on USB by AUTO_OFF_USB).

The original entry (SYS joystick-center) became the mouse left-click in v1.5.0, so entry moved to a free SYS thumb key; the joystick-mouse is preserved.

Tooling

  • Printable PDF now paginates — mise run draw-pdf puts one A4 page per group of layers (LAYERS_PER_PAGE, default 4 → 2 pages) instead of cramming all layers onto one.

Firmware budget

Left 380 KB / 792 KB (48.0%), right 269 KB (33.9%) — gaming adds ~4 KB.

Flashing

mise run flash-release   # downloads the latest main build and flashes both halves

Full details in PR #27. See KEYMAP.md for the layer reference.

v1.6.0 — Printable keymap PDF + build-flash memory-budget summary

Choose a tag to compare

@rdlu rdlu released this 25 Apr 22:05
4884c43

Summary

Tooling-only release. No firmware changes — v1.5.0 firmware remains the latest functional build (this release does not affect the .uf2 artifacts).

New mise tasks — printable keymap layout

Generate a portrait A4 PDF of the keymap from the keymap-drawer SVG. One task per source — mirrors the mental model of "which SVG do I want printed?":

Task Source SVG
mise run draw-pdf The existing local file (no regen, no fetch).
mise run draw-pdf-fresh Regenerate locally (mise run draw) first.
mise run draw-pdf-ci Fetch the CI-rendered SVG from origin/main (generally crisper than the local cache).
mise run open-pdf Generate from local SVG + open in the default viewer (xdg-open).

PDF layout: A4 portrait, image centered, UTF-8-safe header (Eyelash Sofle — Keymap Layout · v1.6.0) and footer (Generated 2026-04-25 · github.com/rdlu/zmk-sofle · Page 1 / 1). Output goes to keymap-drawer/eyelash_sofle.pdf (gitignored).

build-flash improvement

The canonical build+flash task now prints a memory budget summary block at the end:

===============================================
  Firmware memory budget
===============================================
  Left  (Studio + nice_view):
               FLASH:      385400 B       792 KB     47.52%
                 RAM:       98346 B       256 KB     37.52%
  Right (nice_view)         :
               FLASH:      274972 B       792 KB     33.90%
                 RAM:       62908 B       256 KB     24.00%

✅ Both halves built and flashed. They will auto-reset and re-pair over BT.

Tool management

weasyprint is declared in [tools] as pipx:weasyprint = "latest" — provision with mise install, upgrade with mise upgrade pipx:weasyprint. Each PDF task fails fast with a friendly error if the binary is missing.

PR: #26

v1.5.0 — Central media keys + dedicated mouse fallback layer

Choose a tag to compare

@rdlu rdlu released this 25 Apr 21:51

Summary

Reorganizes the central joystick column on the BASE/NAV layers and consolidates mouse keys onto a single fallback layer.

Changes

BASE

  • Central joystick column (top→bottom): Vol↑ · Vol↓ · Play/Pause · Next · Mute — direct media access without holding mo(MEDIA). Encoder rotation continues Vol↑/Vol↓.

NAV

  • Central joystick column: ↑ · ↓ · ← · → · Enter — secondary arrow cluster (the right-side HJKL arrows on row 2 stay).
  • Mouse keys removed from this layer entirely.

SYS|NUM — emergency mouse fallback

Key Action
F / G / V Mouse 1 / 2 / 3 (LCLK / RCLK / MCLK)
R / T Mouse 4 / 5 (MB4 / MB5 — back / forward)
Central joystick column Pointer move (↑/↓/←/→)
Central joystick row 4 Left click

MEDIA

  • Central column kept duplicating BASE (Vol↑/Vol↓/PP/Next/Mute) intentionally for clarity when reading the layer.

Memory budget (local v1.5.0 build)

Build Flash RAM
Left (Studio + nice_view) 376 KB / 792 KB (47.5%) 96 KB / 256 KB (37.5%)
Right (nice_view) 269 KB / 792 KB (33.9%) 61 KB / 256 KB (24.0%)

Byte-identical to v1.4.2 — the rearrangement is structure-neutral (same count of mkp/mmv behaviors, just different positions and layers).

Flashing

mise run flash-release will download these artifacts and flash both halves.

PR: #25

v1.4.2 — AltGr moves to right-thumb F12

Choose a tag to compare

@rdlu rdlu released this 25 Apr 21:23

Summary

Maintenance release: AltGr (RALT) moves off the home/index R and U keys onto a single right-thumb key.

Changes

  • R and U — restored to plain &kp (no more ralt_mt hold-tap on the alpha block)
  • Right-thumb F12 — now ralt_mt RALT F12: tap sends F12, hold activates RALT (AltGr)
  • Behavior block unchanged: tapping-term-ms=280, require-prior-idle-ms=230, quick-tap-ms=175, flavor=balanced, no positional restriction (AltGr combos still span both hands)

Why

The two alpha-key ralt_mt instances added a hold-tap context to commonly-typed letters. Holding R or U for any reason silently armed RALT, and slow deliberate single presses occasionally felt sluggish. Moving AltGr to a thumb key — where holds are natural — removes that friction from the alpha block entirely while keeping AltGr easily reachable for accent combos like é, ó, ñ, ü.

Memory budget (local v1.4.2 build)

Build Flash RAM
Left (Studio + nice_view) 376 KB / 792 KB (47.5%) 96 KB / 256 KB (37.5%)
Right (nice_view) 269 KB / 792 KB (33.9%) 61 KB / 256 KB (24.0%)

Left flash dropped ~7 KB vs v1.4.1 from removing the two alpha-key hold-tap contexts.

Flashing

Use mise run flash-release to download these artifacts and flash both halves interactively.

PR: #24