rushabhcodes/ESP32-E-Reader

A compact ESP32-C3 e-reader PCB integrating LiPo charging/protection, USB-C, 3.3 V regulation, e-paper display bias generation, microSD storage, and tactile controls.

Version
1.0.85
License
unset
Stars
3

scripts/check-controls-schematic.mjs

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";

const tolerance = 1e-6;
const pointKey = ({ x, y }) =>
	`${Math.round(x / tolerance)},${Math.round(y / tolerance)}`;
const liesOn = (point, edge) => {
	const dx = edge.to.x - edge.from.x;
	const dy = edge.to.y - edge.from.y;
	const cross = (point.x - edge.from.x) * dy - (point.y - edge.from.y) * dx;
	return (
		Math.abs(cross) < tolerance &&
		point.x >= Math.min(edge.from.x, edge.to.x) - tolerance &&
		point.x <= Math.max(edge.from.x, edge.to.x) + tolerance &&
		point.y >= Math.min(edge.from.y, edge.to.y) - tolerance &&
		point.y <= Math.max(edge.from.y, edge.to.y) + tolerance
	);
};

export function checkControlsSchematic(circuit) {
	const sheet = circuit.find(
		(e) => e.type === "schematic_sheet" && e.name === "controls",
	);
	assert(sheet, "Missing controls sheet");
	const components = new Map(
		circuit
			.filter((e) => e.type === "source_component")
			.map((e) => [e.name, e]),
	);
	const pin = (reference, number) => {
		const component = components.get(reference);
		const source = circuit.find(
			(e) =>
				e.type === "source_port" &&
				e.source_component_id === component?.source_component_id &&
				e.pin_number === number,
		);
		assert(source, `Missing physical pin ${reference}.${number}`);
		const schematic = circuit.find(
			(e) =>
				e.type === "schematic_port" &&
				e.source_port_id === source.source_port_id &&
				e.schematic_sheet_id === sheet.schematic_sheet_id,
		);
		assert(schematic, `Missing schematic terminal ${reference}.${number}`);
		return {
			name: `${reference}.${number}`,
			source,
			point: schematic.center,
			componentId: schematic.schematic_component_id,
		};
	};
	const continuousWire = (pins) => {
		const net = pins[0].source.subcircuit_connectivity_map_key;
		assert(net, `Missing net for ${pins[0].name}`);
		assert(
			pins.every((p) => p.source.subcircuit_connectivity_map_key === net),
			"Expected pins to share one electrical net",
		);
		const edges = circuit
			.filter(
				(e) =>
					e.type === "schematic_trace" &&
					e.schematic_sheet_id === sheet.schematic_sheet_id &&
					e.subcircuit_connectivity_map_key === net,
			)
			.flatMap((e) => e.edges);
		const points = new Map(
			[
				...pins.map((p) => p.point),
				...edges.flatMap((e) => [e.from, e.to]),
			].map((p) => [pointKey(p), p]),
		);
		const graph = new Map([...points.keys()].map((key) => [key, new Set()]));
		for (const edge of edges) {
			const start = pointKey(edge.from);
			for (const [key, point] of points) {
				if (!liesOn(point, edge)) continue;
				graph.get(start).add(key);
				graph.get(key).add(start);
			}
		}
		const reached = new Set();
		const queue = [pointKey(pins[0].point)];
		for (const key of queue) {
			if (reached.has(key)) continue;
			reached.add(key);
			queue.push(...graph.get(key));
		}
		for (const p of pins) {
			assert(
				reached.has(pointKey(p.point)),
				`No continuous schematic wire from ${pins[0].name} to ${p.name}`,
			);
		}
	};

	continuousWire([
		pin("R11", 2),
		...["SW2", "SW3", "SW4", "SW6"].map((ref) => pin(ref, 4)),
	]);
	for (const [resistor, button] of [
		["R1", "SW2"],
		["R3", "SW3"],
		["R6", "SW4"],
	]) {
		continuousWire([pin(resistor, 1), pin(button, 1)]);
	}
	continuousWire([pin("R10", 2), pin("S2", 2)]);
	for (const reference of ["SW2", "SW3", "SW4", "SW6"]) {
		for (const number of [1, 4]) {
			const terminal = pin(reference, number);
			assert(
				circuit.some(
					(e) =>
						e.type === "schematic_line" &&
						e.schematic_component_id === terminal.componentId &&
						[
							pointKey({ x: e.x1, y: e.y1 }),
							pointKey({ x: e.x2, y: e.y2 }),
						].includes(pointKey(terminal.point)),
				),
				`Switch symbol does not touch terminal ${terminal.name}`,
			);
		}
	}
	return "Controls schematic: all four ADC terminals, common bus, resistor branches, and wake pull-up are connected.";
}

if (import.meta.main) {
	const path = process.argv[2] ?? "dist/index/circuit.json";
	console.log(checkControlsSchematic(JSON.parse(await readFile(path, "utf8"))));
}