Expand Glitch operations hub, cabinet authoring, deployment, and RFID support

Operator hub
- Introduce Escape Gallery branding and Glitch: the Dark Machine room views
- Add cabinet illustrations, Pac-Man marquee/gameplay artwork, and waiting states
- Add device feedback panels with RFID readings, input/output states, and stale status
- Add logged operator hardware interventions with request deduplication

Room and cabinet authoring
- Add a visual custom-PCB connection editor with per-cabinet device inventory
- Support device IDs, bindings, wiring notes, input polarity, and validation
- Add physical-room label/dimension toggles and editable doorway annotations
- Preserve doorway changes through undo, save, and revert
- Expand cabinet profiles, room layout, and Glitch scenario resources

Scenarios and gameplay
- Add per-assignment game configuration and runtime feature validation
- Add timed test wins and Frogger individual-home objectives
- Add the three-cabinet timed/score/Frogger test scenario
- Add Cabinet 1 Pac-Man RFID credits, credit sound, and CREDIT 0/1 display
- Require physical Start after an accepted scan and prevent duplicate credits

Pi hardware and deployment
- Add GPIO feedback sampling and a separate multiplexed RFID helper
- Validate reader pin ownership and input/output conflicts
- Add SSH cabinet startup and Pi desktop-session service support
- Add master packaging, site setup, SSH enrollment, and staged cabinet releases
- Verify deployment manifests and support explicit activation/status/log commands

Documentation and verification
- Consolidate the root README around the current architecture and setup workflow
- Add the complete master/Pi/RFID export and deployment walkthrough
- Document network isolation, credentials, card identification, and rollback
- Add PCB, room-editor, feedback, credit, deployment, and controller tests
- Isolate protocol tests from designer-edited cabinet configurations

Known limitations
- RFID helper compilation and physical Pi commissioning remain unverified
- Fleet provisioning does not yet configure RFID-only installations completely
- Broader Pac-Man tests retain a ghost-capture assertion failure
- Staged exports must be rebuilt and checked before deploying runtime 2.3
This commit is contained in:
Jeff Luke committed 2026-10-17 14:53:54 -07:00
1 parent 705ec31e0b
commit c1b2195834
90 files changed
+4969 -456

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
[package]
name = "arcade-rfid"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1"
rppal = "0.22.1"
rppal_mfrc522 = "0.4.1"
serde_json = "1"
+43
View File
@@ -0,0 +1,43 @@
// Uses the same MFRC522 library and selector sequence as Odin; no Odin room logic.
use rppal::{gpio::Gpio, spi::{Bus, Mode, SlaveSelect, Spi}};
use rppal_mfrc522::Mfrc522;
use std::{thread, time::Duration, io::{self, Write}};
fn main() -> anyhow::Result<()> {
let arg = std::env::args().nth(1).unwrap_or_else(|| "0,1".into());
let slots: Vec<usize> = arg.split(',').map(str::parse).collect::<Result<_,_>>()?;
anyhow::ensure!(!slots.is_empty() && slots.len() <= 8 && slots.iter().all(|x| *x < 8), "Slots must be 0..7");
let gpio = Gpio::new()?;
let mut a = gpio.get(17)?.into_output_low();
let mut b = gpio.get(27)?.into_output_low();
let mut c = gpio.get(22)?.into_output_low();
let mut reset = gpio.get(20)?.into_output_low();
thread::sleep(Duration::from_millis(100));
let mut spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 1_000_000, Mode::Mode0)?;
let mut reader = Mfrc522::new(&mut spi);
loop {
for slot in &slots {
reset.set_low();
if slot & 1 != 0 {a.set_high()} else {a.set_low()}
if slot & 2 != 0 {b.set_high()} else {b.set_low()}
if slot & 4 != 0 {c.set_high()} else {c.set_low()}
reset.set_high();
thread::sleep(Duration::from_millis(50));
let sample = (|| -> anyhow::Result<serde_json::Value> {
reader.reset()?;
thread::sleep(Duration::from_millis(100));
let version = reader.version()?;
anyhow::ensure!(version != 0 && version != 255, "Reader did not respond");
Ok(match reader.uid(Duration::from_millis(250)) {
Ok(uid) => serde_json::json!({"slot":slot,"uid":format!("{:08X}",uid.to_u32()),"error":""}),
Err(_) => serde_json::json!({"slot":slot,"uid":"","error":"No tag read (absent or unreadable)"}),
})
})();
reset.set_low();
thread::sleep(Duration::from_millis(50));
let value = sample.unwrap_or_else(|e| serde_json::json!({"slot":slot,"uid":"","error":e.to_string()}));
println!("{}",value);
io::stdout().flush()?;
}
}
}