A reference for anyone who has salvaged a Stern trough assembly (SPIKE or SPIKE 2 era) for a
homebrew machine and wants to read its opto sensors from non-Stern control hardware (OPP, a
generic microcontroller, etc.) instead of Stern's own CPU/node system. Written up here because
the "Serial Opto Receiver" name is misleading and the obvious assumption — that it needs Stern's
proprietary node-bus protocol — is wrong for at least one confirmed board revision. If you're
building a Portal Pinball V4.0-style machine and landed here from that project: this file holds
the knowledge (how the board works, how to read it, the tools and pitfalls, and the bench history
that got there — see "Bench history" at the end); "The ATmega328P bridge as built" below is that
project's working build, and tools/atmega328p-trough-bridge/ is its firmware.
Confirmed by physically examining two boards (parts 520-7001-00A and 520-8516-00, see below;
the read has been proven working end-to-end on the 520-8516-00). The rest of the part-number
family is inferred from how Stern/resellers describe and price them, not independently opened and
checked. If you have a different revision, verify before assuming it matches.
RCK is inverted on the board. It reaches the shiftSH/LD through an inverting buffer, so RCK HIGH = load, RCK LOW = shift. IdleRCK low, pulse it high to latch, return it low, then clock. Doing it the normal 74HC165 wayVCC/RCK/SCK/MISO/GND (or similar), you canStern has sold several trough opto board revisions across the SPIKE/SPIKE 2 line, referenced in
parts listings as:
| Part | Description (as sold) |
|---|---|
| 520-5344-00 / 520-5345-00 | Trough Serial Opto Transmitter (early SPIKE) |
| 520-7001-00 | Node Board Serial Opto Trough Receiver Assembly (SPIKE II) |
| 520-1051-00 | Trough Opto Receiver Extension Node Board (replaces 520-7001-00) |
| 520-8516-00 | SPIKE 2 Trough Serial Opto Receiver (current, replaces 520-1051-00) |
Stern has not published schematics or a parts list for any of these (confirmed via their own
support/PinWiki — see Sources). A Stern schematic for the 520-7001-00A was later obtained and is in
this repo (docs/opto-atmega/520-7001-00A-TROUGH-RECEIVER-BOARD.pdf); it is what shows the inverted RCK
routing. The board physically examined for this writeup is silkscreened
520-7001-00A, "...RD TROUGH" ("[STANDA]RD TROUGH" or similar, partially obscured by a label).
Whether the newer 520-1051-00/520-8516-00 revisions use the same shift-register design was an
open question when this section was first written — since independently checked on a
520-8516-00 unit, see "A different board revision (520-8516-00)" below for what's actually
confirmed there (a real 74HCT165 shift register is present, but with genuine differences from
this section's assumptions - don't assume this writeup transfers directly without reading that
section too). 520-1051-00 remains unchecked.
Genuine Stern Spike/Spike 2 machines have a separate, actually-proprietary link: node boards talk
RS-485 over Cat5e back to a Spike CPU board, and that protocol has only been partially
reverse-engineered (see Mission Pinball Framework's spike: platform, which rides that bus via a
real Spike CPU board's own USB debug/bridge port — it does not synthesize the bus from scratch).
That bus is not what's on this trough board's connector. If you don't have a genuine Spike
CPU + node board and were about to go source one just to read a trough, stop — you very likely
don't need it. The two links get conflated easily because both use the word "serial" and both are
Stern/Spike-branded; they are unrelated protocols solving different problems (inter-board bus
vs. local sensor serialization on one small board).
In a real Stern machine the trough board isn't read by the Spike CPU at all. Stern's SPIKE System
Manual classifies it as a "node extension": it hangs off a full node board (e.g. a Playfield
Node) over a short serial cable, and that node's own microcontroller reads it, using Stern's closed
firmware. That's why no bit-level description of the read exists anywhere public — which turned out
not to matter, since the board is just a shift register behind a buffer (archived copy of the
manual's relevant pages: docs/references/raw/stern-spike-system-manual/content.md).
Two ICs on the board examined:
RCK on its way to U1's SH/LD — the detail that decides how the board has to be read.So "Trough Serial Opto Receiver" means: up to 8 opto sensors' states get parallel-loaded into a
shift register and clocked out over one wire, instead of running 8 individual wires back to
whatever's reading them. That's it — a wiring-reduction trick, not a data protocol Stern
designed.
The board's populated connector is silkscreened directly with the signal names — no probing or
guessing required:
| Pin | Meaning |
|---|---|
VCC |
5V supply |
GND |
ground |
SCK |
shift clock — 74HC165's CLK |
RCK |
register/latch clock — reaches the 74HC165's SH/LD inverted, through a 220Ω resistor and one channel of the 74HC540. RCK HIGH = parallel load, LOW = shift |
MISO |
serial data out — 74HC165's QH |
This naming (MISO/SCK/RCK) is the standard convention hobbyists use for bare 74HC165
breakout boards, and it maps onto any microcontroller's hardware SPI peripheral — except that the
latch polarity is the reverse of a bare 74HC165 breakout:
RCK low while idle (register in shift mode).RCK high for a few µs to load the current sensor states, then return it low.SCK 8 times (e.g. an SPI transfer(0x00) in master mode, SPI Mode 0, MSB first) whileMISO — the 74HC165 presents input H first, A last.Drive RCK from a 5V-logic source. It enters a plain 74HC (not HCT) buffer, which at 5V needs
about 3.5V to see a valid high — a 3.3V microcontroller or a Bus Pirate v3's AUX pin (which only
reached ~3.2V at the board) isn't enough.
How the wrong polarity shows up: if you pulse RCK low and clock with it high, the register
sits in load mode, ignores the clock, and MISO shows input H the whole time — so every byte has all
8 bits identical, no matter what you block. If you ever see only 0x00/0xFF, suspect the latch
polarity before suspecting the chip.
On the 520-7001-00A, MOSI isn't part of the connector. On the 520-8516-00 it is broken out, but
has no effect on the read.
Two things Stern doesn't document anywhere and that this writeup can't hand you either, because
they're facts about how a specific board was laid out, not about the shift-register mechanism:
For the 520-8516-00 both are now known — see its section below. For any other revision, trace or
verify both empirically (hand-block one opto channel at a time and watch which bit of the read
byte changes) before trusting a mapping. Don't ship a build that assumes an unverified
mapping — get it wrong and a working trough position can silently misreport, which is a bad
failure mode for a machine that trusts the trough for ball-count logic.
On the 520-7001-00A, MISO read a constant byte no matter what was blocked, while power, ground,
wiring, clean SPI waveforms at the connector, and live sensor data on U1's own input legs were all
verified. That was diagnosed as U1 (the 74HC165) being dead — good inputs, dead output.
That diagnosis was most likely wrong. Every test pulsed RCK low and clocked with it high,
which — because the board inverts RCK — held the register in load mode the whole time. In load
mode the clock is ignored and QH just shows input H — confirmed to be a spare, fixed input on the
520-8516-00, and apparently unused on the 520-7001-00A's schematic too (its 74HC540 has 7 sensor
channels plus the RCK inverter). That produces exactly "good inputs, constant output," including in the
"latch-without-clocking" test that seemed to prove the chip dead. A brand-new 520-8516-00 showed the
identical symptom, and reading it with the corrected polarity worked immediately. The 520-7001-00A
itself hasn't been retested yet.
So: if a board like this returns a constant byte, check the RCK polarity first. Only if the
byte stays constant with RCK idling low and pulsed high is a dead 165 worth considering.
Two ways forward if the chip really is dead:
D0–D7 parallel input pins (or U2's outputT 74HC165 in the same SOIC-16A second project unit, silkscreened 520-8516-00 (the current SPIKE 2 part number), has been
read successfully with the corrected RCK polarity. It differs from the 520-7001-00A in a few
confirmed ways:
CN1 ("SERIAL IN", pin order VCC, RCK, SCK, MOSI, MISO, GND) and CN3CN1 is the one to read from — that's whereCN3 is presumably the cascade to a further board and isn't needed.CN1 also has an unlabelled 7th pin position that carries nothing, even in a real machine.RCK inversion), U2 = 74HCT165D (theMISO only). Note the U1/U2 numberingRCK path was confirmed with a meter in Ω mode:CN1 RCK → 220Ω → U1 pin 9 (A8) → U1 pin 11 (Y8) → U2 pin 1 (SH/LD).CN2 is a small 3-pin power-only tap (JAM/GND/VCC). The jam sensor is not wiredMOSI is broken out, but has no effect on the read.Verified bit map (blocking one sensor at a time; first bit shifted out = raw bit 7):
| Raw bit | Signal |
|---|---|
| 7 | spare input H — always 0 |
| 6 | jam |
| 5 | trough position 1 |
| 4 | trough position 2 |
| 3 | trough position 3 |
| 2 | trough position 4 |
| 1 | trough position 5 |
| 0 | trough position 6 |
All seven channels read 1 = clear, 0 = blocked. With everything clear the byte is 0b01111111.
Pin 1 is at the dot/bevel on each chip. SOIC pins run down one side from pin 1 and back up the
other, so the last pin sits directly opposite pin 1.
| Chip | Pins worth knowing |
|---|---|
| 74HCT165D (the shift register; U2 on the 520-8516-00, U1 on the 520-7001-00A), 16 pins | 1 SH/LD (from RCK, inverted) · 2 CLK (SCK) · 3–6 inputs E–H · 7 QH̄ (complementary out) · 8 GND · 9 QH (serial out) · 10 SER · 11–14 inputs A–D · 15 CLK INH · 16 VCC |
| 74HC540D (U1 on the 520-8516-00), 20 pins | 1/19 output enables · 2–9 inputs A1–A8 (9 = A8, fed by RCK through 220Ω) · 10 GND · 11–18 outputs Y8–Y1 (11 = Y8, drives SH/LD) · 20 VCC |
| 74HC14D (U3 on the 520-8516-00), 14 pins | inputs 1, 3, 5, 9, 11, 13 · 7 GND · 14 VCC |
Measured on a 520-8516-00, power off: CN1 RCK → U1 pin 9 = 219.5Ω (the series resistor), U1
pin 11 → U2 pin 1 = 0.1Ω. Use the meter's Ω mode, not continuity mode — most meters stay silent
through 220Ω.
spi_bit_bang (untested)Not used in Portal Pinball V4.0 (the ATmega bridge was chosen instead, CHANGES.md #29), but a
real option for anyone running Mission Pinball Framework. MPF 0.80 ships a spi_bit_bang platform
(mpf/platforms/spi_bit_bang.py, config spec in mpf/config_spec.yaml) that reads a 74HC165-style
register by bit-banging it through another platform's hardware: two MPF digital_outputs for the
latch and clock, and one ordinary switch input for the data line. OPP's own firmware has no
shift-register input mode (MPF's OPP platform only knows solenoid, input, incandescent, matrix and
neopixel wings), so on OPP this is the only no-extra-microcontroller way.
How it maps onto this board. OPP outputs are low-side FETs, so each needs a pull-up resistor
to 5V (roughly 1–2.2kΩ; check the Stern pin reaches ≥3.5V, since RCK enters a plain 74HC540).
With pull-ups, "output enabled" = LOW and "disabled" = HIGH. That matches the board: the platform
holds its chip-select disabled (HIGH = load) between reads and enabled (LOW = shift) while
clocking — exactly the inverted RCK this board wants. The clock idles HIGH and each 1ms pulse
ends on a rising edge, when the 74HC165 shifts. The first bit read is input H (the unused bit 7),
MSB first, so switch numbers "0"–"6" equal raw bits 0–6. An OPP input reads active when pulled
low, so the bits arrive inverted: a switch is active when its sensor is blocked.
# Untested sketch - output/input numbers are placeholders
digital_outputs:
trough_rck: {number: "<free OPP output>", type: driver} # -> CN1 RCK, pull-up to 5V
trough_sck: {number: "<free OPP output>", type: driver} # -> CN1 SCK, pull-up to 5V
switches:
trough_miso: {number: "<free OPP input>"} # <- CN1 MISO
s-trough1: {number: "5", platform: spi_bit_bang}
s-trough2: {number: "4", platform: spi_bit_bang}
s-trough3: {number: "3", platform: spi_bit_bang}
s-trough4: {number: "2", platform: spi_bit_bang}
s-trough5: {number: "1", platform: spi_bit_bang}
s-trough6: {number: "0", platform: spi_bit_bang}
s-trough-jam: {number: "6", platform: spi_bit_bang}
spi_bit_bang:
cs_pin: trough_rck
clock_pin: trough_sck
miso_pin: trough_miso
inputs: 8
bit_time: 50ms # default; must exceed OPP's switch-report latency (OPP poll_hz defaults to 100)
Trade-offs versus a microcontroller bridge:
type in MPF.bit_time. Fine for trough ball counting, much laggier than aSCK, D12 = MISO (input), D11 = MOSI (output). Swapping D11/D12 makes the Unofx2lafw driver via sigrok-cli) — good for proving theRCK inversion.RCK. Its AUXCS, as it was configured) reached only ~3.2V at the board, below the ~3.5V a 74HC540VPU to +5V), which worked forSCK/MOSI. Its mode-setup menus auto-select defaults within a fraction of a second unless theCLK, which is a clock edge. That is the mostRCK by hand with a wire moved between the board's GND and+5V.docs/references/raw/buspirate5-hardware-rev10/content.md.RCK's 220Ω series resistor keeps continuity mode silent; measure in Ω mode.Condensed. The full blow-by-blow (every test, including the retracted theories) is preserved in
git: plans/read-opto.md as of commit 21a9e8b; decisions are logged in CHANGES.md #27–#29.
2026-09-11 — old board (520-7001-00A). The first bridge (ATmega328P/Uno) read a constant byte.
Power, ground, wiring, the Uno's own SPI (a loopback test), clean RCK/SCK waveforms at the
connector, and live sensor data on the 165's input legs were all verified, so the 165 was declared
dead. Most likely a misdiagnosis — the firmware already used the inverted latch polarity. That
board hasn't been retested with the fix.
2026-09-26 — new board (520-8516-00). A new board, seen working in a real Stern machine,
showed the same constant byte. A long session with a Uno, the logic analyzer and a Bus Pirate v3.6:
MISO/MOSI wires swapped; the Bus Pirate unable to drive RCKMOSI (all 256 byte values, fast and each held for 24+ seconds, andSCK idle polarities, CN1's unlabelled 7th pin (noCN3 (it read a constant 1 where CN1 read 0; neverMOSI; the output "tri-stating during load" and aRCK routed through the 74HC540. Confirmed on the new board with a meter,RCK low / pulse high, and the read worked first time. The bit mapLessons:
Wired in 2026-09-27 and confirmed working on the cabinet 2026-10-04: it replaced the fragile
hand-soldered taps on the opto
board (the wiring damage of commit 859a6b9). A bare ATmega328P-PU reads the board through CN1
and drives one output per opto into an ordinary OPP switch input. A bare chip because it's 5V like
the rest of the OPP wiring (no level shifters), the parts were in stock, and it leaves a small,
permanent footprint.
Firmware (tools/atmega328p-trough-bridge/atmega328p-trough-bridge.ino): every 10ms it pulses
RCK high then low (the board inverts RCK), clocks one byte over hardware SPI, requires 3
matching reads in a row (debounce), and sets the 7 outputs. BIT_CHANNEL holds the bit map above;
BIT_INVERT is true on all channels, so an output goes high when its opto is blocked. On
the MPF side all seven switches are type: NC: an OPP input reads closed when pulled low, NC
inverts that, so each switch is active when a ball is there and s-trough-jam is active when the
trough is jammed.
Parts: ATmega328P-PU (DIP-28, socketed so it can be pulled and reflashed), a 10kΩ reset
pull-up, a 0.1µF ceramic decoupling capacitor at the chip, and a proper plug for CN1 — never
solder to the Stern board, that's what caused the original damage. Flashed with an Uno running
ArduinoISP on an ISP shield.
Wiring (DIP pin numbers):
| ATmega pin | Connects to |
|---|---|
| 7 VCC, 20 AVCC | +5V |
| 8, 22 GND | GND |
| 1 RESET | +5V through 10kΩ |
| 7–8 | 0.1µF ceramic, right at the chip |
| 16 SS | +5V (forces SPI master) |
| 9, 10 XTAL, 17 MOSI | not connected (internal 8MHz clock) |
| 14 (PB0) | CN1 RCK |
| 19 (PB5) | CN1 SCK |
| 18 (PB4) | CN1 MISO |
| 5 (PD3), 6 (PD4), 11 (PD5), 12 (PD6), 13 (PD7), 23 (PC0) | s-trough1…s-trough6 |
| 24 (PC1) | s-trough-jam |
On this machine the bridge→OPP wiring was changed on 2026-10-11 and re-verified end to end by
blocking one opto at a time: s-trough1 (eject end) → 2-0-22, s-trough2…s-trough6 →
2-0-16…2-0-20, s-trough-jam → 2-0-21. hardware-switches.yaml is the source of truth
for these numbers.
CN1 VCC/GND go to the same +5V/GND, and its MOSI stays unconnected. The ATmega's ground
must be shared with the OPP board. Power comes from the machine's 5V logic supply; the draw is
modest (a Uno on USB powered the whole opto board on the bench). The OPP inputs are on
chain2-0x20 — not chain2-0x21 as first planned and as board Overviews.xlsx's design sheet says;
live hardware wins.
Programming: tools/flash-atmega328p.ps1 (ArduinoISP, MiniCore, 8MHz internal clock; the SPI
and serial speeds work at 8MHz with no firmware change). Set the fuses ("Burn Bootloader") on
every chip, whatever its history: a spare previously set for an external crystal sits dead with
no clock and no error message until its fuses are rewritten.
Bench test before installing: a 220–330Ω resistor and an LED on each output; block each opto
by hand and check that the matching LED toggles. An Uno wired the same way (CN1 → D8/D13/D12,
outputs D3–D7, A0, A1) is a quick stand-in for calibrating, since it reflashes over USB in
seconds.
Alternatives on file: leave an Uno in permanently (no soldering, just bigger); a Pro Micro
(ATmega32U4, 16MHz/5V — its SPI pins differ, so the firmware's pin map would change); or no
microcontroller at all, via spi_bit_bang above.
This repo (Portal Pinball V4.0) has a full reference implementation built on the above, bridging
this board to an OPP-based control system with no Stern hardware and no MPF Spike platform
involved:
docs/opto-atmega/520-7001-00A-TROUGH-RECEIVER-BOARD.pdf — Stern'sRCK → 74HC540 → SH/LD inversion.print/trough-opto-bridge.html —tools/flash-atmega328p.ps1 — scripts flashing a bareavrdude fuse bytes.tools/atmega328p-trough-bridge/atmega328p-trough-bridge.ino —tools/atmega328p-trough-bridge/diag-slow-toggle/, diag-hold-latch/, diag-multichannel-read/,diag-mosi-patterns/ — small bench-only sketches from the investigation: a multimeter-visibleRCK/SCK, a latch-then-hold for probing QH (which on the 520-8516-00 only everMOSI byte sweep. Their latch polarity has been corrected to match the board.The approach generalizes beyond ATmega328P/OPP — any microcontroller with an SPI peripheral (or
even a bit-banged 3-wire interface) and a way to drive a few GPIOs works the same way.
docs/references/raw/pinwiki-stern-spike-repair/content.md) —docs/references/raw/stern-spike-system-manual/content.md; full PDF:docs/SPIKE-System-Manual.pdf) — the "node extension" classification.docs/references/raw/buspirate5-hardware-rev10/content.md) — Bus Pirate 5 I/O levels.docs/opto-atmega/520-7001-00A-TROUGH-RECEIVER-BOARD.pdf.