- name
- dingopdm-config
- description
- Generate and edit dingoConfig JSON configuration files for dingoPDM power-distribution modules, and apply them to a device with the dingo-cli tool. Use whenever the task is to configure dingoPDM outputs / inputs / CAN inputs+outputs / virtual-input logic / conditions / counters / flashers / wiper / starter / keypads, drive a CANBoard from a PDM, or write a config to hardware. Covers the exact JSON schema, the variable (var-map) reference indices, all enum values, and the behavioral semantics needed to build a correct config from scratch.
# dingoPDM configuration (dingoConfig JSON)
A **dingoPDM** is a CAN-connected automotive power-distribution module (8 solid-state
outputs with current sensing, 2 digital inputs, plus CAN-driven logic). It is
configured by a **dingoConfig `.json` file** — the canonical format authored by the
dingoConfig GUI and consumed by `dingo-cli`.
Upstream projects:
- dingoPDM firmware: https://github.com/corygrant/DingoPDM_FW
- dingoConfig GUI (file format owner): https://github.com/corygrant/dingoConfig
This skill is everything needed to **hand-write or edit** that JSON correctly. To
**apply** a file to a device, use the `dingo` CLI:
`dingo apply [-port <port>] [-base <id>] [-burn] <file.json>` — flags and the file
may be given in any order. Run `dingo -h` / `dingo <cmd> -h` for the full surface. The CLI only reads the file (never rewrites it), selects the PDM whose
`baseId` matches `-base`, and verifies the device's parameter count + CRC, so a
successful `apply` means the device holds exactly the file.
`-port` is the device's serial port (macOS: typically `/dev/cu.usbmodem*`; Linux:
`/dev/ttyACM*`). `-base` is the target device's `baseId` (default 222). `-burn`
persists to flash; without it the config is live but reverts on power-cycle.
A complete, real example file lives at `internal/pdmcfg/testdata/example.json` —
read it for the full shape; start from a copy and edit.
## Core rules (read first)
- **All enums and all variable references are plain integers** in the JSON (never
strings). E.g. `"resetMode": 2`, `"mode": 0`, `"input": 119`.
- **CAN ids are decimal integers** — JSON has no hex, so write `0x642` as `1602`.
- **Floats** are JSON numbers (`"currentLimit": 13`, `"arg": 13.5`). Integer-valued
floats may be written without a decimal.
- **Arrays are addressed by position, not by `number`.** `outputs[0]` is Output 1,
`outputs[3]` is Output 4 — 0-based array index `i` = hardware unit `i+1` = the
var-map "n" of `i+1`. Every object also carries a `"name"` (free text) and a
1-based `"number"`; both are cosmetic (the projection ignores them), but keep
`number` == position+1 for GUI compatibility. (Keypad `buttons`/`dials` objects
additionally carry `keypadNumber`, the 1-based owning keypad.)
- **What you omit resets to the firmware default on `apply`.** `apply` first resets
every parameter to its firmware default, then applies what the file contains — so
an omitted field, or an omitted array *tail* (e.g. only `outputs[0..3]`), takes the
firmware default (NOT JSON zero). A minimal/partial file is therefore valid to
`apply`. (Arrays must be a dense prefix from index 0 — you can't skip the middle,
because position is the instance index.)
- **For a file you will also open in the dingoConfig GUI, keep objects and arrays
complete.** The GUI fills missing JSON with type-zero (e.g. `currentLimit` 0, not
the firmware default 20), so a partial file looks wrong there. The reliable way to
author a complete file is to **copy `internal/pdmcfg/testdata/example.json` and edit
it** — it contains every object with firmware-default values. Per-field default
values are not all enumerated in this skill; that example is the source for them.
- Full-PDM array counts: `inputs` 2, `outputs` 8, `canInputs` 32, `canOutputs` 32,
`virtualInputs` 16, `flashers` 4, `counters` 4, `conditions` 32, `keypads` 2
(each 20 buttons, 2 dials); `wipers` and `starterDisable` are single objects.
Reduced variants (dingoPDM-Max = 4 outputs) just use shorter arrays.
## Top-level structure
```json
{
"PdmDevices": [ { /* one PDM device, see below */ } ],
"CanboardDevices": [],
"DbcDevices": [],
"BlinkMarineKeypads": [],
"GrayhillKeypads": []
}
```
All five keys are present. **Only `PdmDevices` is meaningful to dingo-cli** (it
programs PDMs). Leave the other four as-is (`[]` or whatever the GUI wrote);
dingo-cli ignores them. A file may contain multiple PDMs — `apply` picks the one
matching `-base`.
## PdmDevice
| field | type | meaning |
|---|---|---|
| `pdmType` | int | 0 = dingoPDM. Identifier; not written to the device. |
| `name` | string | label; not written to the device. |
| `baseId` | int | device CAN base id, 0..0x7FF (default 222 / 0x0DE). Used to select the device and is itself written. |
| `sleepEnabled` | bool | enable low-power sleep (see Sleep/wake). |
| `filtersEnabled` | bool | enable the CAN acceptance filter. |
| `connectUsbToCan` | bool | bridge USB<->CAN (keep true for normal use). |
| `bitrate` | enum CanBitrate | CAN speed. All bus nodes (PDM, CANBoards, keypads) must match. |
Then these arrays/objects (full-PDM counts):
`inputs` [2], `outputs` [8], `canInputs` [32], `canOutputs` [32],
`virtualInputs` [16], `wipers` {single object}, `flashers` [4],
`starterDisable` {single object}, `counters` [4], `conditions` [32],
`keypads` [2].
## Variables (the var-map) — used by every `input`/`var` field
Any field that selects "what drives this" (`output.input`, `virtualInput.var0/1/2`,
`condition.input`, `counter.incInput`, `flasher.input`, `wiper.*Input`,
`canOutput.input`, `keypad.dimmingVar`, button `valVars`, …) is an **integer index
into this global variable list**. An output/flasher/etc. is considered ON while its
referenced variable is non-zero/true.
| index | variable | notes |
|---|---|---|
| 0 | AlwaysFalse | constant 0 |
| 1 | AlwaysTrue | constant 1 |
| 2 | State | DeviceState, **numeric**: Run=0, Sleep=1, OverTemp=2, Error=3 (isolate one state via a condition — see conditions) |
| 3 | BoardTemp | board temperature |
| 4 | BattVolt | battery voltage |
| 5 | DigIn1 | PDM digital input 1 |
| 6 | DigIn2 | PDM digital input 2 |
| 7 + 2·(n-1) | CanIn{n} **Out** | boolean result of CAN input n (n=1..32) |
| 8 + 2·(n-1) | CanIn{n} **Val** | decoded numeric value of CAN input n |
| 71 + (n-1) | VirtIn{n} | virtual input n (n=1..16) |
| 87 + 4·(n-1) | Out{n} **Active** | output n on (n=1..8) |
| 88 + 4·(n-1) | Out{n} **Current** | output n measured current |
| 89 + 4·(n-1) | Out{n} **Overcurrent** | output n overcurrent flag |
| 90 + 4·(n-1) | Out{n} **Fault** | output n fault flag |
| 119 + (n-1) | Flasher{n} | n=1..4 |
| 123 + (n-1) | Cond{n} | n=1..32 |
| 155 + (n-1) | Counter{n} | n=1..4 |
| 159..164 | Wiper Slow/Fast/Park/Inter/Wash/Swipe out | in that order |
| 165 + 26·(k-1) | Keypad{k} block (k=1,2) | within block: button{b} = base+(b-1); dial{d} = base+20+(d-1); analog{a} = base+22+(a-1) |
Total var-map size = 217 (indices 0..216). Worked examples: `DigIn1`=5,
`AlwaysTrue`=1, `Flasher1`=119, `Out1Active`=87, `Out4Active`=87+4·(4-1)=99,
`Out8Fault`=90+4·(8-1)=118, `CanIn1Out`=7, `CanIn2Out`=9, `CanIn3Out`=7+2·(3-1)=11,
`CanIn32Out`=69 (`CanIn32Val`=70), `VirtIn1`=71, `Cond1`=123, `Counter1`=155,
`Keypad1Button3`=165+(3-1)=167, `Keypad2Dial1`=191+20=211.
Notes on variables:
- **Boolean consumers** (`output.input`, `flasher.input`, `virtualInput.varN`, etc.)
treat the variable as `!= 0`. Wiring a *numeric* var (`BattVolt`, `BoardTemp`,
`Out{n}Current`, `CanIn{n}Val`, `Counter{n}`) straight into a boolean input means
"on whenever non-zero" — usually not what you want. To threshold a numeric value,
route it through a **condition** (→ `Cond{n}`) or a CAN input's `Out`, then
reference that boolean.
- **Units** of the analog system vars: `BattVolt` = volts, `BoardTemp` = °C,
`Out{n}Current` = amps. (A low-voltage condition's `arg` is therefore in volts.)
- A function with `enabled: false` outputs its variable as 0/false.
## Enums (integer values)
- **CanBitrate** (`bitrate`): 1000K=0, 500K=1, 250K=2, 125K=3, 100K=4
- **ByteOrder** (`byteOrder`): LittleEndian/Intel=0, BigEndian/Motorola=1
- **InputMode** (`mode` on inputs / canInputs / virtualInputs): Momentary=0, Latching=1
- Momentary = the var tracks the live level (true while asserted).
- Latching = the var toggles on each rising edge.
- **InputPull** (digital input `pull`): None=0, Up=1, Down=2
- **InputEdge** (counter `incEdge`/`decEdge`/`resetEdge`): Rising=0, Falling=1, Both=2
- **Operator** (canInput `operator`, condition `operator`): Equal=0, NotEqual=1,
GreaterThan=2, LessThan=3, GreaterThanOrEqual=4, LessThanOrEqual=5, BitwiseAnd=6,
BitwiseNand=7
- **BoolOperator** (virtualInput `cond0`/`cond1`): And=0, Or=1, Nor=2
- **ProfetResetMode** (output `resetMode`): None=0, Count=1, Endless=2
- **WiperMode** (`wipers.mode`): DigIn=0, IntIn=1, MixIn=2
- **WiperSpeed** (`wipers.speedMap[]`): Park=0, Slow=1, Fast=2, Intermittent1=3 …
Intermittent6=8
- **KeypadModel** (`keypads.model`): Blink2Key=0, Blink4Key=1, Blink5Key=2,
Blink6Key=3, Blink8Key=4, Blink10Key=5, Blink12Key=6, Blink15Key=7,
Blink15Key2Dial=8, Grayhill1Key=10, Grayhill6Key=20, Grayhill8Key=21,
Grayhill12Key=22, Grayhill15Key=23, Grayhill20Key=24
## Function field reference
### outputs[] (solid-state output, current-limited)
`enabled` bool · `currentLimit` A float 0..100 (running limit) · `inrushCurrentLimit`
A float 0..100 (allowed during inrush window) · `inrushTime` ms (inrush window after
turn-on) · `resetMode` ProfetResetMode · `resetTime` ms (retry interval) ·
`resetCountLimit` 0..20 (max retries for Count mode) · `input` var ref (output ON
while true) · `pwmEnabled` bool · `softStartEnabled` bool · `variableDutyCycle` bool ·
`dutyCycleInput` var ref · `fixedDutyCycle` 0..100 · `frequency` Hz 0..400 ·
`softStartRampTime` ms · `dutyCycleDenominator` 1..5000 · `minDutyCycle` 0..100 ·
`primaryOutput` int8 (-1 = standalone; else a 0-based output index to gang/follow).
Overcurrent behavior: while ON, current above `currentLimit` (after the inrush
window) or above `inrushCurrentLimit` (during it) trips the output OFF.
- `None`: latch off (Fault) immediately — requires power cycle to recover.
- `Count`: retry every `resetTime` ms up to `resetCountLimit` times, then latch off
(Fault, power-cycle to clear). The retry count resets to 0 whenever the output is
commanded off (its `input` goes false).
- `Endless`: retry every `resetTime` ms forever.
The output is never held on during/after an overcurrent — it fails safe to OFF. A
hardware fault (dead short/over-temp) latches off regardless of mode. Choosing by load:
motors/pumps → `Count` or `Endless` (self-recover from transient trips); lamps/fixed
relays → `Count`, or `None` to hard-latch on fault. If a scenario doesn't specify,
`Count` with `resetCountLimit` 3-5 is a safe default.
Inrush: for `inrushTime` ms after turn-on, the limit is `inrushCurrentLimit`; after
that, `currentLimit`. Set inrush ≥ motor/lamp surge and `inrushTime` long enough to
cover startup but short enough to still protect a stalled load.
**Match limits to the physical channel:** on a standard dingoPDM, Outputs 1–2 are
13 A pins and Outputs 3–8 are 8 A pins (see the `dingopdm-hardware` skill). Set the
**continuous `currentLimit` at or below the channel rating**. `inrushCurrentLimit`
*may* exceed the channel rating to pass a brief motor/lamp surge, as long as it is
bounded by a short `inrushTime` — e.g. the window-lift example below allows 80 A for
800 ms on a 13 A channel. Don't leave the template default (`inrushCurrentLimit` 50)
on a load that can't actually tolerate that surge.
**Gating / overriding an output:** an output has exactly one driver (`input`) and no
separate inhibit/disable field (the only built-in force-off is `starterDisable`, and
that's specific to engine cranking). To force an output off under some condition, fold
it into the driver via a virtualInput — set `output.input` to a `VirtIn{n}` computed
as `normalDriver AND NOT override` (see virtualInputs for the exact field values).
### inputs[] (digital inputs, 2)
`enabled` · `invert` bool · `mode` InputMode · `debounceTime` ms · `pull` InputPull.
Exposes `DigIn{n}` (n = array position + 1). For a maintained/level switch closed while ON:
`mode`=Momentary (the var follows the contact), and set `invert`/`pull` to match
wiring (switch-to-ground + `pull`=Up needs `invert`=true so closed→true). For a
**momentary push-button that toggles** an output on/off (each press flips it):
`mode`=Latching — `DigIn{n}` then flips on each press (starts false at boot), and you
drive the output directly from it. Latching behaves the same on `inputs`,
`canInputs`, and `virtualInputs`. Set `enabled: true` on any input you reference —
a disabled input is not scanned, so treat its `DigIn{n}` as unavailable (0).
### canInputs[] (decode a CAN signal, 32)
`enabled` · `timeoutEnabled` bool · `timeout` ms · `ide` bool (extended id) ·
`id` CAN id · `startBit` 0..63 · `bitLength` 1..32 · `byteOrder` ByteOrder ·
`signed` bool · `factor` float · `offset` float · `operator` Operator ·
`operand` float · `mode` InputMode.
- `id` is the CAN id as a **decimal integer** (0x642 → 1602). Set `ide`=false for a
在 GitHub 查看