Skip to content

WLED and HCR Audio

greghulette edited this page Jul 28, 2026 · 3 revisions

WLED and HCR Audio

Two device families a NaviCore button, switch, or knob action can drive: WLED LED controllers and the HCR (Human‑Cyborg Relations) vocalizer. Both share one design — the action carries only the command; the physical destination is a global setting — and both get their wire bytes from the shared WcbCmd library, so a device behaves identically whether NaviCore drives it out a local UART or a WCB drives it off the mesh.

This page covers the WLED routing table, the HCR destination, and the two newest HCR features: non‑blocking audio fades and per‑channel volume. For how you attach these commands to a control, see Action Editor and Command Library; for the full action‑type list, Actions Reference.


The shared model: command carries the verb, destination is global

For both device families the action holds only what to do:

  • WLED — an RA_WLED action stores a ;L<id>,<verb> string. Where WLED id physically lives is a per‑id table (wledSlots) edited in the WLED tab.
  • HCR — an RA_HCR action stores a numeric fn / chan / track. Where the vocalizer physically lives is one global setting (hcrDest) edited in the HCR tab.

The actual device bytes come from the WcbCmd library (currently v0.7.0), compiled by both NaviCore and the WCB firmware. It ships WcbWled, WcbHcr, WcbHcrFade, WcbMaestro, and WcbMp3. Because the same translator runs on both ends, one saved command produces the exact same bytes at the device whether it went out a NaviCore aux port or rode the ESP‑NOW mesh to a WCB. CI installs the library straight from the greghulette/WcbCmd GitHub repo.


WLED lighting

The routing table (WLED tab)

NaviCore holds 4 WLED slots (RC_NUM_WLED = 4) even though ids 1–9 are addressable — one physical controller per id, a few is plenty. Each slot in the WLED tab has:

Field Values Meaning
ID 19 (0 = unused) The id your ;L<id>,VERB actions address
Mode Off / Local port / Remote WCB How this id is reached
Local port S3 / S4 / S5 A WLED wired to one of this board's aux serial ports
Remote WCB 120 A WLED hosted on another board; the ;L string is forwarded over ESP‑NOW

⚠️ Baud caveat. WLED wants 115200 baud, but only S3 (hardware UART0) is reliable that fast. S4/S5 are bit‑banged SoftwareSerial and should stay ≤ 57600, so a WLED on those ports may be unreliable at high rates. Baud belongs to the port, not the slot — set it in the Baud Rates tab.

Authoring a WLED action

WLED actions are authored from the WLED Lighting board in the Command Library — there is no "Send to" picker. The action shows "Routed by WLED id (WLED tab)" because it auto‑routes on the id you set:

Command Template Result Args
Lights on ;L<id>,ON {"on":true}
Lights off ;L<id>,OFF {"on":false}
Toggle ;L<id>,TOGGLE {"on":"t"}
Set brightness ;L<id>,BRI,<n> {"bri":N} n = 0–255
Recall preset ;L<id>,PS,<n> {"ps":N} preset # (the primary workflow)
Set color ;L<id>,COL,<hex> segment 0 color RRGGBB or RRGGBBWW (RGBW)
Set effect ;L<id>,FX,<n>[,<sx>,<ix>] {"seg":[{"fx":N…}]} index; optional speed sx 0–255, intensity ix 0–255
Set palette ;L<id>,PAL,<n> {"seg":[{"pal":N}]} palette index
Raw JSON ;L<id>,JSON,{…} passthrough any /json/state document (escape hatch)

An unknown verb or bad arguments emit nothing to the WLED.

How the id is read. The id is the digits immediately after L, no comma — ;L3,PS,2 → id 3. A bare ;L,VERB (no id, id 0) means the lowest‑id local WLED on this board. Valid ids are 1–9; an id greater than 9 is rejected and the action is skipped.

How a WLED command is delivered

At dispatch NaviCore looks the id up in wledSlots and routes it:

  • Local slot (remoteWCB == 0, serialPort 3/4/5) → WcbWled::emit() builds the WLED JSON and writes it newline‑framed out that aux port.
  • Remote slot (remoteWCB 1–20) → the full ;L<id>,… string is sent to that WCB over ETM, and the host WCB's own router drives its WLED.

A remote WLED needs its id configured in two places: on NaviCore (WLED tab → Remote WCB, so it knows which board to forward to) and on the remote WCB itself (its own wledConfigs, so it knows the local port). NaviCore forwards the raw string; the remote WCB looks the id up in its table. A bare ;L,VERB (id 0) is local‑only — it is not forwardable to a remote WCB.


HCR audio (Human‑Cyborg Relations vocalizer)

The destination (HCR tab)

Every HCR action shares one global destination, hcrDest, chosen in the HCR tab:

  • Serial (local) — pick S3 / S4 / S5. NaviCore formats the vocalizer's native bytes and writes them straight to that port.
  • WCB (unicast) — pick a WCB ID 1–20. NaviCore sends a ;H,… command over ETM; the receiving WCB drives its own locally wired vocalizer.

The default is Serial / S3. HCR over WCB is unicast only — broadcast is unsupported, and the command rides ETM (retried until ACK'd) because HCR can't tolerate a dropped frame. On the WCB path the serial port is chosen on the receiving WCB via ?HCR,PORT, not here; the legacy wcbPort field is ignored by current firmware.

Function reference

An HCR action carries a function (fn), a channel (chan — an emotion or an audio channel depending on the fn), and a track/value:

fn Function chan track / value
2 SetEmotion emotion 0–3 level 0–99
3 Trigger emotion 0–3 level 0–99
4 Stimulate emotion 0–3 level 0–99
5 Overload
6 Muse (one shot)
7 Muse gap min gap 0–99 s max gap 0–99 s
8 Stop (all audio + emote)
9 StopEmote
10 Override emotions 0 = off / 1 = on
11 ResetEmotions
13 Auto‑muse 0 = off / 1 = on
14 PlayWAV audio 0=V / 1=A / 2=B track 0–9999
16 StopWAV audio 0=V / 1=A / 2=B
17 SetVolume audio 0=V / 1=A / 2=B / 3 = All 0–99
18 Volume Up 0 = All / 1=V / 2=A / 3=B step (0 = default 5)
19 Volume Down 0 = All / 1=V / 2=A / 3=B step (0 = default 5)
12 Fade In A (1) / B (2) seconds (0 = instant)
15 Fade Out A (1) / B (2) seconds (0 = instant)

Emotion channel (fn 2/3/4): 0 = Happy, 1 = Sad, 2 = Mad, 3 = Scared.

⚠️ Emotion channel 4 (Overload) isn't valid on Trigger/Stimulate — WcbCmd rejects chan 4 and the action is silently dropped. Use fn 5 (Overload) instead.

Audio‑channel enums differ by fn. For PlayWAV/StopWAV/SetVolume (fn 14/16/17) it's 0=V, 1=A, 2=B (and 3 = All for SetVolume). For Volume Up/Down (fn 18/19) the encoding is deliberately offset: 0 = All, then V/A/B = 1/2/3. Legacy Volume actions carry chan 0 meaning All, so don't "fix" one to match the other.

Audio fades (Fade In / Fade Out)

Fades (fn 12 = Fade In, fn 15 = Fade Out) are non‑blocking: NaviCore ramps the volume over time in the background instead of blocking loop(). They are handled by the shared HcrFade ramp, not by the normal HCR codec.

  • Channels A or B only — the Vocalizer channel V is never faded (matching the WCB).
  • track = duration in seconds. 0 seconds is an instant jump, not a fade.
  • Fade In ramps 0 → the channel's current volume. Fade Out ramps current → 0, then issues StopWAV and restores the pre‑fade level.
  • The ramp is 150 ms per step, linear interpolation. Every step writes through a SetVolume, so the volume shadow stays authoritative.

Local vs mesh. A local fade is advanced by loop() and runs only while the HCR destination is a local serial port. If the destination is a WCB (or you move it to a WCB mid‑fade, or the local port becomes unresolvable), the in‑flight local ramp is cancelled rather than frozen. Over WCB, NaviCore instead sends ;H,FADEIN|FADEOUT,<A|B>,<sec> and that WCB runs its own fade.

Per‑channel volume (SetVolume / Volume Up / Volume Down)

  • SetVolume (fn 17) sets an absolute level 0–99 on V, A, B, or All.
  • Volume Up / Down (fn 18/19) nudge relative by step (blank or 0 = default 5), clamped 0–99. With chan 0 = All they step V+A+B together; with chan 1/2/3 NaviCore reads the shadow for that one channel, adds the step, clamps, and emits a single absolute SetVolume.

No volume readback. HCR has no way to report its current volume, so relative steps and Fade In's start level rely on a per‑channel shadow that seeds to 50 and becomes exact only after the first SetVolume on that transport. A relative nudge right after boot assumes 50.

A manual audio command (PlayWAV/StopWAV/SetVolume/Volume Up/Down) cancels any in‑flight fade on the channel(s) it touches — otherwise the next fade tick would overwrite your manual change.


Authoring in the config tool

HCR actions use a legacy editor: a Function dropdown plus per‑fn widgets built for each function.

  • Fade In / Fade Out shows an Audio chan select (A / B) and a Seconds number. A new fade action defaults to 2 s, but a stored 0 is preserved (a 0 is an intentional instant jump).
  • Volume Up / Down shows a 4‑option Audio chan select (All / V / A / B) and Step (0 = 5).

This editor is kept for backward compatibility; new HCR (and MP3) actions are meant to be authored from the Command Library (the vendored DroidNet board). WLED actions are already command‑library only.

Debugging. The debug console has dedicated WLED and HCR chips that surface the [DISPATCH] WLED… / [DISPATCH] HCR… routing lines — including skip reasons like not configured, WCB not ready, or fade chan must be A/B.


Gotchas and caveats

  • Only 4 WLED controllers can be configured at once, even though ids 1–9 are addressable.
  • A remote WLED must have its id set in two places (NaviCore's WLED tab and the remote WCB's own config).
  • A bare ;L,VERB (id 0) only drives a local WLED — it can't be forwarded to a remote WCB.
  • WLED at 115200 is reliable only on S3; S4/S5 (SoftwareSerial) should stay ≤ 57600.
  • HCR fades touch A and B only; the Vocalizer channel V is never faded. Fade seconds = 0 = instant, not a fade.
  • A local HCR fade only runs on a local serial destination; a WCB destination (or moving to one mid‑fade) cancels the local ramp — the remote WCB runs its own.
  • The fn 18/19 volume‑channel encoding (0 = All) deliberately differs from the fn 14/16/17 audio enum (0 = V, 3 = All).
  • HCR has no volume readback — the shadow seeds to 50 and is only exact after the first SetVolume.
  • HCR over WCB is unicast only; the receiving WCB routes it to its own port, set via ?HCR,PORT.
  • HCR is fire‑and‑forget: query/poll functions are intentionally not implemented, and NaviCore formats the bytes itself via WcbCmd rather than using the HCRVocalizer library.
  • WcbCmd is a shared library compiled by both NaviCore and the WCB, and it exists as two physical copies. If the versions drift, the same command can emit different device bytes; CI floats the GitHub version (currently 0.7.0).

See also: Actions Reference · Action Editor and Command Library · Config Tool Guide · WCB Network

Clone this wiki locally