@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