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
93 lines
4.6 KiB
GDScript
93 lines
4.6 KiB
GDScript
@tool
|
|
class_name CabinetPCBConfig
|
|
extends Resource
|
|
|
|
# This resource describes wiring. It never configures or writes GPIO.
|
|
@export var connections: Array[PCBConnection] = []
|
|
|
|
const TYPES := {
|
|
"rfid": "RFID reader", "button": "Button / switch", "sensor": "Digital sensor",
|
|
"led": "LED / lighting driver", "motor": "Motorized switch / motor driver",
|
|
"relay": "Relay / MOSFET module", "i2c": "I2C peripheral", "other": "Other (document wiring)"
|
|
}
|
|
|
|
static func ports() -> Array[Dictionary]:
|
|
var result: Array[Dictionary] = []
|
|
for index in range(8):
|
|
result.append({"id": "J%d" % (index + 1), "family": "reader", "label": "%d%d%d" % [(index >> 2) & 1, (index >> 1) & 1, index & 1], "pins": "Selector: BCM22=%d, BCM27=%d, BCM17=%d\nShared reset: BCM20. Reader connector contact pinout is not documented." % [(index >> 2) & 1, (index >> 1) & 1, index & 1], "types": ["rfid", "other"], "bcm": -1})
|
|
result.append({"id": "J9", "family": "host", "label": "Pi ribbon", "pins": "Host GPIO ribbon connector. Confirm pin-1 orientation against the physical board.", "types": [], "bcm": -1})
|
|
var signals := [5, 6, 13, 19, 26, 21]
|
|
for index in range(6):
|
|
result.append({"id": "J%d" % (10 + index), "family": "powered", "label": "BCM%d" % signals[index], "pins": "1 GND (black) | 2 3V3 (red) | 3 signal (yellow)\nBCM%d" % signals[index], "types": ["button", "sensor", "led", "motor", "relay", "other"], "bcm": signals[index]})
|
|
result.append({"id": "J16", "family": "i2c", "label": "I2C1", "pins": "1 GND (black) | 2 3V3 (red) | 3 SCL (white) | 4 SDA (yellow)", "types": ["i2c", "other"], "bcm": -1})
|
|
signals = [16, 12, 24, 23, 18, 25]
|
|
for index in range(6):
|
|
result.append({"id": "J%d" % (17 + index), "family": "signal", "label": "BCM%d" % signals[index], "pins": "1 GND (black) | 2 signal (red)\nBCM%d - red is signal, not a power supply." % signals[index], "types": ["button", "sensor", "led", "motor", "relay", "other"], "bcm": signals[index]})
|
|
return result
|
|
|
|
static func port(id: String) -> Dictionary:
|
|
for definition in ports():
|
|
if definition.id == id:
|
|
return definition
|
|
return {}
|
|
|
|
func connection(id: String) -> PCBConnection:
|
|
for item in connections:
|
|
if item != null and item.port_id == id:
|
|
return item
|
|
return null
|
|
|
|
func set_connection(item: PCBConnection) -> void:
|
|
for index in range(connections.size() - 1, -1, -1):
|
|
if connections[index] != null and connections[index].port_id == item.port_id:
|
|
connections.remove_at(index)
|
|
if not item.device_type.is_empty():
|
|
connections.append(item)
|
|
emit_changed()
|
|
|
|
func issues(hardware: HardwareConfig = null) -> PackedStringArray:
|
|
var messages := PackedStringArray()
|
|
var used_ports: Dictionary = {}
|
|
var used_ids: Dictionary = {}
|
|
for item in connections:
|
|
if item == null:
|
|
messages.append("Empty connection resource.")
|
|
continue
|
|
var definition := port(item.port_id)
|
|
if definition.is_empty() or not definition.types.has(item.device_type):
|
|
messages.append("%s: unsupported connector/device combination." % item.port_id)
|
|
if used_ports.has(item.port_id):
|
|
messages.append("%s: connector is assigned more than once." % item.port_id)
|
|
used_ports[item.port_id] = true
|
|
if item.device_id.strip_edges().is_empty():
|
|
messages.append("%s: enter a device ID." % item.port_id)
|
|
elif used_ids.has(item.device_id):
|
|
messages.append("Duplicate device ID: %s." % item.device_id)
|
|
used_ids[item.device_id] = true
|
|
if item.device_type in ["led", "motor", "relay"] and not item.binding_id.is_empty():
|
|
var action: HardwareAction = hardware.get_action(StringName(item.binding_id)) if hardware else null
|
|
if action == null:
|
|
messages.append("%s: hardware action '%s' does not exist." % [item.port_id, item.binding_id])
|
|
elif not definition.is_empty() and action.gpio_pin != definition.bcm:
|
|
messages.append("%s: action uses BCM%d, but connector uses BCM%d." % [item.port_id, action.gpio_pin, definition.bcm])
|
|
if hardware:
|
|
for action in hardware.actions:
|
|
if action != null and action.gpio_pin in [17, 27, 22, 20]:
|
|
messages.append("Action '%s' uses reserved PCB selector/reset BCM%d." % [action.action_id, action.gpio_pin])
|
|
return messages
|
|
|
|
func feedback_devices() -> Array[Dictionary]:
|
|
var result: Array[Dictionary] = []
|
|
for item in connections:
|
|
if item == null:
|
|
continue
|
|
var kind := ""
|
|
if item.device_type == "rfid": kind = "rfid"
|
|
elif item.device_type in ["button", "sensor"]: kind = "input"
|
|
elif item.device_type in ["led", "motor", "relay"]: kind = "output"
|
|
else:
|
|
push_warning("Feedback driver not implemented for %s (%s)" % [item.device_id, item.device_type])
|
|
continue
|
|
result.append({"id": item.device_id, "name": item.display_name, "port": item.port_id, "kind": kind, "pull_up": item.input_pull_up})
|
|
return result
|