PINSCOPEField Instrument 005
GitHub

Reference

Every feature, in detail
v1.3 6300 lines 12 test suites GPL-3.0

Pin model

Every device card maps the host MCU's pins to a uniform model:

D0 and D1 are reserved (USB serial uses them) and grayed out.

Sample rate

The Hz selector on each device card sets how often the firmware sends state packets. Range is 1 to 50 Hz. The default is 10 Hz. Higher rates give smoother strip charts and finer FFT resolution but consume more transport bandwidth.

Inside the firmware, the state push period is 1000 / hz milliseconds. Lower the rate if the wire log shows tx pressure or if you're on a slow transport like BLE; raise it for fast transients.

Calibration

Click any A0-A5 (or V0-V3) label on a device card to open the calibration modal. Two modes:

Manual

Type a slope m, offset b, unit, decimals, and a label. Linear conversion y = m·raw + b is applied to every reading on that pin and propagates through the strip chart, FFT, spectrogram, scatter, stats, alerts, and CSV export.

Wizard (two-point fit)

Apply a known value at the low end (often a short to ground), click CAPTURE LOW. Apply a known value at the high end, click CAPTURE HIGH. PinScope computes:

m = (knownHi - knownLo) / (rawHi - rawLo)
b = knownLo - m * rawLo

The wizard shows a live preview using the current raw reading, then SAVE writes the calibration to the pin. If the two raw captures are identical, the wizard refuses to fit and tells you so.

Calibrations persist in the session and export with the JSON.

Virtual channels

PinScope has four virtual analog slots (V0-V3) that show up in every visualization next to A0-A5. Two sources can feed a virtual slot:

Math overrides I2C if both target the same slot.

Cross-pin math

The math engine lets a virtual channel be a function of any other channels. The expression syntax is a small JS subset: arithmetic, the math globals (sin, cos, sqrt, log, exp, abs, pow, min, max, floor, ceil, round), and references to channels by their key (a0 to a5, d0 to d13, v0 to v3, f0 to f13).

ExpressionWhat it derives
(a0 + a1) / 2average of two analog inputs
sqrt(a0*a0 + a1*a1)magnitude of an XY pair
a0 - a1differential measurement
v0 * 0.0078125TMP117 raw to degrees Celsius
floor(v0 / 16) * 10 + (v0 % 16)BCD-unpacked seconds from a DS3231

Expressions are compiled via new Function(...) with strict mode and no access to globals beyond the math helpers. Bad syntax surfaces in a toast; null inputs produce null outputs.

Strip chart

The strip chart shows up to 14 channels at once. Click any chip in the ANALOG / DIGITAL / VIRTUAL rows under the chart to toggle that channel on. Each trace gets a deterministic color keyed off its name.

ButtonAction
15s 1m 5m 15mwindow length
STATSper-trace stats overlay (min, max, mean, std dev)
PAUSEfreeze chart and recorder
CLEARdrop the recorder buffer
CSVone-shot dump to a CSV file
STREAMappend every new sample to a file (Chromium only)
CAPTUREsave buffer as a replayable JSON file
BASELINEload a capture as a faded ghost overlay
DIFFtoggle live-minus-baseline trace
TRIGopen the trigger modal
PNGsnapshot current chart

The recorder buffer caps at 9000 samples (about 15 minutes at 10 Hz, 3 minutes at 50 Hz). For longer runs use STREAM.

Baseline + diff view

The strip chart can overlay a previously captured run as a faded dashed ghost and render a live-minus-baseline diff trace on top. Useful for before-vs-after comparisons: tweak a circuit, flash new firmware, swap a sensor, see exactly how the new behavior differs.

  1. CAPTURE a baseline run.
  2. BASELINE loads a capture file as the baseline overlay. Click again to clear.
  3. DIFF toggles the difference trace, computed as live - baseline for each active channel, normalized and centered on the chart's midline.

The baseline is aligned to "now" by tail-shifting its timestamps, so a capture from yesterday lines up with the current strip-chart window without clock-sync gymnastics. Diff is nearest-neighbor by time.

Oscilloscope-style trigger

The strip chart can capture a fixed window of samples around a threshold crossing and freeze on the event for inspection. Useful for one-shot events that are hard to spot in the live stream: a glitch, a power transient, a single-shot pulse from a sensor.

Click TRIG on the chart toolbar, pick a channel (any A, V, F, or D pin), pick a condition (rising, falling, above, below), set a threshold in raw units, define pre and post sample windows in seconds. ARM the trigger. The button pulses red while armed.

When the condition fires, the chart freezes onto the captured window with a red vertical line at the fire time and a red horizontal threshold line. The recorder auto-pauses. RE-ARM resets and resumes recording; DISARM clears it entirely.

FFT and spectrogram

The FFT tab shows the magnitude spectrum of the active analog or virtual channel. Window length comes from the strip chart selector. Window functions (Hann, Hamming, Blackman, none) are selectable. Magnitude is plotted on a log y-axis by default.

The Spectrogram tab is a rolling waterfall of the same FFT over time. Useful for non-stationary signals where the frequency content shifts.

XY scatter

The Scatter tab plots two channels against each other for Lissajous-style visualization. Pick X and Y from the dropdowns. Optional connecting lines link adjacent samples in time, so you can see the trajectory and not just the cloud. Useful for phase analysis, sensor correlation, or just looking at a circuit's transfer function.

Frequency measurement

Pins in FREQ mode count rising edges via attachInterrupt. The firmware has one ISR per pin (small wrappers around a per-pin volatile counter). Every 250 ms the main loop reads and resets the counters with interrupts briefly off, and converts counts to Hz.

Only pins with hardware interrupt support can be put in FREQ mode. The firmware returns "no interrupt on pin" if you try to set FREQ on an unsupported pin.

Threshold alerts

Each device card has a Threshold Alerts panel for raising an alert when a channel crosses a value. Pick a pin, pick a condition (above, below, rising, falling, equal), a value, optionally a sound. Alerts fire when the condition transitions from false to true (not held continuously).

I2C / Qwiic

Every device card has an I2C / Qwiic section with three tools:

Signed reads are supported (the firmware sign-extends per the byte count). Polls are paused while a one-shot read or write runs, then resume.

Companion sensor library

The I2C panel ships with one-click presets for common breakout sensors. Pick a sensor from the dropdown, confirm the address, APPLY drops the right I2C poll into the first free virtual slot and prefills the math input below with the unit-conversion expression.

PresetReadsNotes
TMP10212-bit temperature0.0625 °C/LSB
TMP117precision temperature0.0078125 °C/LSB
DS3231RTC seconds counterBCD-unpacked
MCP980813-bit signed temperatureMicrochip
INA219shunt + bus voltagetwo-slot preset
APDS-9301ambient light channel 0requires power-on init write
PCF8591ADC channel 0NXP

Preset definitions live in SENSOR_PRESETS near the top of the I2CPanel code in pinscope.html. Adding a sensor is a few lines.

RGB LED panel

Three dropdowns (R, G, B) select PWM pins, a color picker picks a color, APPLY puts each pin in PWM mode and writes the matching duty. OFF stops driving. Useful for common-anode/cathode RGB LEDs or any three-channel PWM device.

Scripted automation

Every device card has a SCRIPTED AUTOMATION section: a textarea where you write a short async JS sequence to exercise the board.

HelperWhat it does
setMode(pin, mode)change a pin's mode
setDigital(pin, hi)drive a digital output
setPWM(pin, val)set PWM duty (0..255)
await wait(ms)sleep
read(pin)latest raw value
log(msg)print to output panel
assert(cond, msg)throw if cond is falsy

Loops, conditionals, declarations, await all work. Network and storage globals (fetch, localStorage, eval, Function, setTimeout, window, document) are blocked at compile time via an identifier blocklist.

EXAMPLE
setMode(9, 'pwm');
for (let duty = 0; duty <= 255; duty += 32) {
  setPWM(9, duty);
  await wait(200);
  log('duty=' + duty + ' a0=' + read('a0'));
}

CSV export

The CSV button on the strip chart toolbar does a one-shot dump of the recorder buffer. Columns: timestamp_ms, ISO 8601 time, every digital pin, every analog raw plus its calibrated value if a calibration is set, every virtual raw plus calibrated.

For longer runs, the STREAM button opens a save-file picker (File System Access API), then appends every new sample to that file in batches. Click STREAM again to stop and close the file.

Browser support

CSV streaming is Chromium-only. In Firefox and Safari the STREAM button is disabled with a tooltip; fall back to the one-shot CSV or to a capture file.

Replay

The REPLAY button on the rail loads a capture JSON file as a synthetic [replay] device card. The card plays the captured samples back in real time. Useful for sharing a problematic recording with a colleague, or for reviewing a run without having to recreate the hardware setup.

Sessions

Every device card auto-saves its configuration to localStorage 400 ms after any change. Saved fields:

The EXPORT button writes the same snapshot to a JSON file. IMPORT reads one back. Session file format is currently version 1.3 (additive over 1.2, 1.1, 1.0).

Session diff

The DIFF button (between EXPORT and IMPORT on each device card) compares the current session against a saved JSON file. A modal shows exactly what changed: calibrations, alerts, I2C polls, math expressions, trigger config, scripted automation source (with a line-by-line LCS diff), plugin state.

Read-only; doesn't modify the active session. Useful for catching calibration drift between test rigs, confirming that an "identical" board is actually identical, pre-flight checking a session JSON before committing it to a repo.

Wire protocol

PinScope speaks a small JSON line protocol over every transport. One JSON object per line, newline-terminated.

Host to board

CommandNotes
{"cmd":"hello"}request a hello packet
{"cmd":"poll"}request a state packet on demand
{"cmd":"mode","pin":N,"mode":M}M is one of off, in, inp, out, pwm, freq
{"cmd":"set","pin":N,"val":V}V is 0 or 1; only valid in out mode
{"cmd":"pwm","pin":N,"val":V}V is 0-255; only valid in pwm mode
{"cmd":"hz","val":H}H is 1-50
{"cmd":"i2c","op":"scan"}scan the bus
{"cmd":"i2c","op":"read","addr":A,"reg":R,"count":N}one-shot read
{"cmd":"i2c","op":"write","addr":A,"reg":R,"data":[...]}one-shot write
{"cmd":"i2c","op":"poll","slot":S,"addr":A,"reg":R,"count":N,"hz":H,"signed":bool}configure polling slot
{"cmd":"i2c","op":"stoppoll","slot":S}stop a polling slot

Board to host

PacketNotes
{"t":"hello","id":"...","name":"...","hz":H,"adcMax":N}sent on connect and on hello cmd
{"t":"state","d":[...],"a":[...],"m":[...],"v":[...],"f":[...]}periodic
{"t":"i2c","op":"scan","addrs":[...]}scan result
{"t":"i2c","op":"read","addr":A,"reg":R,"data":[...]}read result
{"t":"ack","cmd":"..."}command accepted
{"t":"err","msg":"..."}command rejected

Keyboard shortcuts

KeyAction
S W M Bopen SERIAL / WIFI / MQTT / BLE dialog
Ropen a capture file (REPLAY)
Popen the plugin manager
Gtoggle stacked vs grid layout
?open the help modal
1 2 3 4switch tabs on the active device
Spacepause / resume the strip chart
Cclear the wire log
Escclose any open modal