Bitfocus Companion 5 connection module (ESM, @companion-module/base ~2.1.3, runtime node22) for the DiGiCo S21/S31. It occupies the console's single iPad slot, controls per-channel sends/assigns to one buss, aux send level/on/pan, mutes, faders and snapshots, and relays any number of DiGiCo iPad apps through to the console byte-for-byte. The S31's iPad extension was probed live: it speaks the SD/DiGiCo Pad OSC tree (console receives on 8000, replies only to the configured iPad port 9000). tools/mock-s31.js emulates the console and test/selftest.js runs the link + relay end to end against it. docs/design-v2.md is the brief for the generic v2 rewrite. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
97 lines
4.9 KiB
Markdown
97 lines
4.9 KiB
Markdown
# DiGiCo S31 (S-Series) — Studio Mon control + iPad relay
|
||
|
||
This connection does two things:
|
||
|
||
1. **Controls which channels feed one buss** — by default the *Studio* buss — plus any aux send
|
||
level/on/pan, channel mute/fader, buss master fader/mute and snapshot recall, with feedbacks,
|
||
variables and ready-made presets.
|
||
2. **Relays the DiGiCo iPad app.** An S-Series console talks to exactly one iPad at a time.
|
||
Companion takes that slot; the iPad apps point at the Companion computer instead of the
|
||
console. Everything an app sends is forwarded to the console unchanged and every console
|
||
message is copied to every app seen recently, so several iPads can be used at once.
|
||
|
||
## What the console speaks (verified on "S31 AB", 2026-10-15)
|
||
|
||
The S31's **iPad** extension speaks the DiGiCo Pad / SD-style OSC tree over UDP:
|
||
|
||
| Address | Reply | Notes |
|
||
| --- | --- | --- |
|
||
| `/Console/Name/?` | `/Console/Name "S31 AB"` | used as keepalive |
|
||
| `/Console/Input_Channels/?`, `/Console/Aux_Outputs/?` | int counts (60, 25) | 25 busses, each in aux *or* group mode |
|
||
| `/Console/Aux_Outputs/modes/?` | 25 ints, 2 = stereo, 1 = mono | |
|
||
| `/Input_Channels/N/Channel_Input/name/?` | string | |
|
||
| `/Input_Channels/N/fader/?`, `/mute/?` | float dB (−150 = −∞), float 0/1 | |
|
||
| `/Input_Channels/N/Aux_Send/M/send_level` | float dB, −150 = off | write a float |
|
||
| `/Input_Channels/N/Aux_Send/M/send_on` | float 0/1 | write int 0/1 |
|
||
| `/Input_Channels/N/Aux_Send/M/send_pan` | float 0..1, 0.5 centre | |
|
||
| `/Input_Channels/N/Group_Send/M/group` | float 0/1 | group assign; write int 0/1 |
|
||
| `/Aux_Outputs/M/Buss_Trim/name/?`, `/Group_Outputs/M/Buss_Trim/name/?` | string | a buss is named on one side only |
|
||
| `/Aux_Outputs/M/fader`, `/mute` (and `/Group_Outputs/M/…`) | float | buss masters |
|
||
| `/Snapshots/Current_Snapshot/?`, `/Snapshots/names/?` | int; `/Snapshots/name [i, cue×100, 0, name]` | |
|
||
|
||
A query is the address plus `/?` with no argument; the reply is the same address without `/?`.
|
||
Every reply goes to the controller IP and **Send Port** configured on the console, never to the
|
||
asking socket, which is why a fixed local port is bound. The lowercase S-Series *OSC Control*
|
||
command list (`/channel/N/…`, `/console/resend`) is **not** answered on the iPad leg; it belongs
|
||
to the separate *OSC Control* extension and is available here as an alternative command set.
|
||
|
||
## Console setup (once, at the surface)
|
||
|
||
Main Menu → **Extensions → iPad**:
|
||
|
||
| Console field | Value |
|
||
| --- | --- |
|
||
| iPad IP address | the Companion computer (AB-ProPresenter: `10.3.11.152`) |
|
||
| Send port *(console → iPad)* | this connection's **Local receive port** (default `9000`) |
|
||
| Receive port *(iPad → console)* | this connection's **Console receive port** (default `8000`) |
|
||
| iPad connection enabled | yes |
|
||
|
||
## iPad app setup
|
||
|
||
In the DiGiCo S app connect page, for every iPad:
|
||
|
||
| iPad field | Value |
|
||
| --- | --- |
|
||
| Console IP | the Companion computer, **not** the console |
|
||
| Send port | this connection's **Relay port** (default `8000`) |
|
||
| Receive port | this connection's **iPad receive port** (default `9000`) |
|
||
|
||
An iPad that goes quiet for *Forget an iPad after* seconds is dropped from the fan-out list.
|
||
`$(S31:relay_clients)` shows how many are live.
|
||
|
||
## Studio Mon buss
|
||
|
||
Set **Studio Mon buss number** (1-25, the console's buss order). The module reads the buss
|
||
names and works out whether it is an **aux** (send level + on/off per channel) or a **group**
|
||
(assign on/off per channel); override with *Studio Mon buss type* if needed. On this console:
|
||
|
||
| Buss | Aux name | Group name |
|
||
| --- | --- | --- |
|
||
| 1-8 | VOCAL VERB, VOCAL SLAP, TRACKS, CLK-GD-MD, VOCAL DELAY, VOCALS, TB, MC/SpkrBcastOut | |
|
||
| 9-13 | | BAND, VOCALS, MISC, DRUMS, DRUM CRUSH |
|
||
| 14-17 | DRUM PLATE, AG VERB, FOH Spkr, Studio TB | |
|
||
| 18 | | Broadcast mix |
|
||
| 19-21 | FX, Prop & Spot, Drum Room Verb | |
|
||
| 23-25 | | CAFE, **Studio**, Master |
|
||
|
||
The default is buss **24 "Studio"** (stereo group). Presets under *Studio Mon* give one
|
||
on/off button per input (green when assigned/on, label = channel name); for an aux buss the
|
||
+1 dB / −1 dB nudge presets appear as well.
|
||
|
||
## Variables
|
||
|
||
`$(S31:ch_N_name)`, `$(S31:aux_N_name)`, `$(S31:group_N_name)`, `$(S31:buss_N_mode)`,
|
||
`$(S31:mon_N_on)`, `$(S31:mon_N_level)`, `$(S31:mon_name)`, `$(S31:mon_mode)`,
|
||
`$(S31:snapshot_name)`, `$(S31:connected)`, `$(S31:relay_clients)`, `$(S31:last_send_touched)`,
|
||
`$(S31:last_rx)`.
|
||
|
||
## Troubleshooting
|
||
|
||
- **No reply from console**: the console only sends to the iPad IP + Send Port set on the
|
||
console. Check those, and that nothing else on this computer owns the local receive port
|
||
(on AB-ProPresenter UDP 8002 belongs to Dante Via; 8000 and 9000 are free).
|
||
- **Learn mode** logs every new address pattern once (Connections → log). Touch a control on
|
||
the surface and the matching address appears; `$(S31:last_send_touched)` shows the last
|
||
send/assign that changed.
|
||
- Press **Reload from console** after a session change to refresh names and levels.
|