diff --git a/frontend/src/assets/Raspberry_Pi_1_illustration.svg b/frontend/src/assets/Raspberry_Pi_1_illustration.svg new file mode 100644 index 00000000..b87e05bd --- /dev/null +++ b/frontend/src/assets/Raspberry_Pi_1_illustration.svg @@ -0,0 +1,180 @@ + + Raspberry Pi 1 Model B+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GPIO + + + Raspberry Pi Model B+ V1.2 + (c) Raspberry Pi 2014 + + C E + FCC + + + + + BROADCOM + BCM2835 + ARM1176JZF-S + + + + + LAN9514 + + + + + + DISPLAY + + + + + CAMERA + + + + + + + + + + + + + + + + ETHERNET + + + + + PWR IN + + + + + HDMI + + + + A/V + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/Raspberry_Pi_2_illustration.svg b/frontend/src/assets/Raspberry_Pi_2_illustration.svg new file mode 100644 index 00000000..7b93a59b --- /dev/null +++ b/frontend/src/assets/Raspberry_Pi_2_illustration.svg @@ -0,0 +1,180 @@ + + Raspberry Pi 2 Model B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GPIO + + + Raspberry Pi 2 Model B V1.1 + (c) Raspberry Pi 2014 + + C E + FCC + + + + + BROADCOM + BCM2836 + ARM Cortex-A7 + + + + + LAN9514 + + + + + + DISPLAY + + + + + CAMERA + + + + + + + + + + + + + + + + ETHERNET + + + + + PWR IN + + + + + HDMI + + + + A/V + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/Raspberry_Pi_Zero_illustration.svg b/frontend/src/assets/Raspberry_Pi_Zero_illustration.svg new file mode 100644 index 00000000..44852ad8 --- /dev/null +++ b/frontend/src/assets/Raspberry_Pi_Zero_illustration.svg @@ -0,0 +1,149 @@ + + Raspberry Pi Zero 2 W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GPIO + + + + + + + + + + + + RP3A0-AU + 2041 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + USB + + + + PWR IN + + + diff --git a/frontend/src/components/ComponentPickerModal.tsx b/frontend/src/components/ComponentPickerModal.tsx index 45f8c233..7a6262f5 100644 --- a/frontend/src/components/ComponentPickerModal.tsx +++ b/frontend/src/components/ComponentPickerModal.tsx @@ -47,6 +47,9 @@ import { type OnlineOnlyBoardAd, type OnlineOnlyComponentAd, } from '../lib/onlineOnlyBoards'; +import raspberryPiZeroSvg from '../assets/Raspberry_Pi_Zero_illustration.svg'; +import raspberryPi1Svg from '../assets/Raspberry_Pi_1_illustration.svg'; +import raspberryPi2Svg from '../assets/Raspberry_Pi_2_illustration.svg'; import raspberryPi3Svg from '../assets/Raspberry_Pi_3_illustration.svg'; import raspberryPi4Png from '../assets/raspberry-pi-4-board.png'; import raspberryPi5Png from '../assets/raspberry-pi-5-board.png'; @@ -821,15 +824,18 @@ const BoardCard: React.FC = ({ kind, onSelect, hoverApi }) => { }; }, [kind]); - const reactThumbnail = - // Zero/1/2 render on canvas through the Pi-3 element (same 40-pin art), - // so their cards reuse the same illustration. - kind === 'raspberry-pi-3' || - kind === 'raspberry-pi-zero' || - kind === 'raspberry-pi-1' || - kind === 'raspberry-pi-2' ? ( + // Every Pi shows its own board. The cards used to share the Pi 3's picture, + // which made the picker claim a Zero looks like a Pi 3. + const piArt: Partial> = { + 'raspberry-pi-zero': raspberryPiZeroSvg, + 'raspberry-pi-1': raspberryPi1Svg, + 'raspberry-pi-2': raspberryPi2Svg, + 'raspberry-pi-3': raspberryPi3Svg, + }; + + const reactThumbnail = piArt[kind] ? ( {BOARD_KIND_LABELS[kind]} diff --git a/frontend/src/components/simulator/BoardOnCanvas.tsx b/frontend/src/components/simulator/BoardOnCanvas.tsx index a7db42eb..bd7c61fe 100644 --- a/frontend/src/components/simulator/BoardOnCanvas.tsx +++ b/frontend/src/components/simulator/BoardOnCanvas.tsx @@ -10,6 +10,9 @@ import { ArduinoMega } from '../velxio-components/ArduinoMega'; // boardKind by mistake — kept the import out so future contributors don't // re-wire it back in. If someone genuinely needs a Nano RP2040 Connect // board (D2..D13 labels), add a new boardKind 'arduino-nano-rp2040'. +import { RaspberryPiZero } from '../velxio-components/RaspberryPiZero'; +import { RaspberryPi1 } from '../velxio-components/RaspberryPi1'; +import { RaspberryPi2 } from '../velxio-components/RaspberryPi2'; import { RaspberryPi3 } from '../velxio-components/RaspberryPi3'; import { RaspberryPi4 } from '../velxio-components/RaspberryPi4'; import { RaspberryPi5 } from '../velxio-components/RaspberryPi5'; @@ -167,12 +170,18 @@ export const BoardOnCanvas = ({ case 'raspberry-pi-pico': case 'pi-pico-w': return ; - // Zero/1/2 share the Pi-3's 40-pin board art (their canvas identity is - // the header, and the ComponentRegistry metadata reuses the same tag); - // only the backend QEMU profile differs per kind. + // Each Pi draws as itself. Zero/1/2 used to borrow the Pi 3's picture + // "because the header is the canvas identity" — but the Zero is 65x30 mm + // against the Pi 3's 85x56, ships its header unpopulated and has neither + // Ethernet nor full-size USB, so that shortcut drew a board the student + // was not holding. 1B+ and 2B do share the Pi 3's outline; their silicon + // and silkscreen do not. case 'raspberry-pi-zero': + return ; case 'raspberry-pi-1': + return ; case 'raspberry-pi-2': + return ; case 'raspberry-pi-3': return ; case 'raspberry-pi-4': diff --git a/frontend/src/components/velxio-components/RaspberryPi1.tsx b/frontend/src/components/velxio-components/RaspberryPi1.tsx new file mode 100644 index 00000000..b0ca43f5 --- /dev/null +++ b/frontend/src/components/velxio-components/RaspberryPi1.tsx @@ -0,0 +1,66 @@ +import { useEffect, useRef } from 'react'; +import boardSvg from '../../assets/Raspberry_Pi_1_illustration.svg'; +import { buildPi40PinHeader } from './pi40PinHeader'; + +/** + * Raspberry Pi 1 Model B+ — Velxio board art. + * + * Shares the 85 x 56 mm "B+ form factor" with the Pi 3, which is why this + * board used to be drawn AS a Pi 3. The outline and the connector positions + * really are identical; the silicon is not — BCM2835 with the RAM stacked on top and no separate Elpida package — and neither is the + * silkscreen, so the picture now says which board the student is holding. + * + * Geometry is in the SVG's own units (10 per mm), the same numbers the art is + * drawn from, so the pin tips land on the drawn pads. Pitch is a true 2.54 mm. + */ + +const SVG_VB_W = 884; // 85 mm of PCB + the USB/Ethernet overhang on the right +const SVG_VB_H = 578; // 56 mm of PCB + the connector overhang at the bottom +const HDR_X0 = 72; +const HDR_STEP = 25.4; +const HDR_Y_TOP = 42; +const HDR_Y_BOT = 67.4; + +const DISPLAY_W = 330; +const SCALE = DISPLAY_W / SVG_VB_W; +const DISPLAY_H = Math.round(SVG_VB_H * SCALE); + +interface Props { + id?: string; + x?: number; + y?: number; +} + +export const RaspberryPi1 = ({ id = 'raspberry-pi-1', x = 0, y = 0 }: Props) => { + const imgRef = useRef(null); + + useEffect(() => { + if (imgRef.current) { + (imgRef.current as unknown as { pinInfo: unknown }).pinInfo = buildPi40PinHeader({ + xStart: HDR_X0 * SCALE, + xStep: HDR_STEP * SCALE, + yTop: HDR_Y_TOP * SCALE, + yBot: HDR_Y_BOT * SCALE, + }); + } + }, []); + + return ( + Raspberry Pi 1 Model B+ + ); +}; diff --git a/frontend/src/components/velxio-components/RaspberryPi2.tsx b/frontend/src/components/velxio-components/RaspberryPi2.tsx new file mode 100644 index 00000000..7f018a00 --- /dev/null +++ b/frontend/src/components/velxio-components/RaspberryPi2.tsx @@ -0,0 +1,66 @@ +import { useEffect, useRef } from 'react'; +import boardSvg from '../../assets/Raspberry_Pi_2_illustration.svg'; +import { buildPi40PinHeader } from './pi40PinHeader'; + +/** + * Raspberry Pi 2 Model B — Velxio board art. + * + * Shares the 85 x 56 mm "B+ form factor" with the Pi 3, which is why this + * board used to be drawn AS a Pi 3. The outline and the connector positions + * really are identical; the silicon is not — BCM2836, the first quad-core, with its RAM moved to the underside — and neither is the + * silkscreen, so the picture now says which board the student is holding. + * + * Geometry is in the SVG's own units (10 per mm), the same numbers the art is + * drawn from, so the pin tips land on the drawn pads. Pitch is a true 2.54 mm. + */ + +const SVG_VB_W = 884; // 85 mm of PCB + the USB/Ethernet overhang on the right +const SVG_VB_H = 578; // 56 mm of PCB + the connector overhang at the bottom +const HDR_X0 = 72; +const HDR_STEP = 25.4; +const HDR_Y_TOP = 42; +const HDR_Y_BOT = 67.4; + +const DISPLAY_W = 330; +const SCALE = DISPLAY_W / SVG_VB_W; +const DISPLAY_H = Math.round(SVG_VB_H * SCALE); + +interface Props { + id?: string; + x?: number; + y?: number; +} + +export const RaspberryPi2 = ({ id = 'raspberry-pi-2', x = 0, y = 0 }: Props) => { + const imgRef = useRef(null); + + useEffect(() => { + if (imgRef.current) { + (imgRef.current as unknown as { pinInfo: unknown }).pinInfo = buildPi40PinHeader({ + xStart: HDR_X0 * SCALE, + xStep: HDR_STEP * SCALE, + yTop: HDR_Y_TOP * SCALE, + yBot: HDR_Y_BOT * SCALE, + }); + } + }, []); + + return ( + Raspberry Pi 2 Model B + ); +}; diff --git a/frontend/src/components/velxio-components/RaspberryPiZero.tsx b/frontend/src/components/velxio-components/RaspberryPiZero.tsx new file mode 100644 index 00000000..c19bfa9a --- /dev/null +++ b/frontend/src/components/velxio-components/RaspberryPiZero.tsx @@ -0,0 +1,74 @@ +import { useEffect, useRef } from 'react'; +import raspberryPiZeroSvg from '../../assets/Raspberry_Pi_Zero_illustration.svg'; +import { buildPi40PinHeader } from './pi40PinHeader'; + +/** + * Raspberry Pi Zero 2 W — Velxio board art. + * + * The Zero used to borrow the Pi 3's picture. That is wrong in the one way a + * board picture can be wrong: it is a different SIZE. The Zero is 65 x 30 mm + * against the Pi 3's 85 x 56 mm, its header ships unpopulated, and it has no + * Ethernet and no full-size USB. A canvas that draws it as a Pi 3 tells the + * student the opposite of what the hardware looks like. + * + * Geometry below is in the SVG's own units (10 per mm) and is the SAME set of + * numbers the artwork is drawn from, so the pin tips cannot drift off the + * pads. Header pitch is a true 2.54 mm. + */ + +const SVG_W = 650; // 65 mm +const SVG_VB_H = 314; // 30 mm of PCB + the connector overhang at the bottom +const HDR_X0 = 82; +const HDR_STEP = 25.4; +const HDR_Y_TOP = 31; +const HDR_Y_BOT = 56.4; + +// 65 mm next to the Pi 3's 85 mm at 320 px: 320 * 65 / 85 ~ 245. Round to 250 +// so the Zero reads as the small board it is when both sit on one canvas. +const DISPLAY_W = 250; +const SCALE = DISPLAY_W / SVG_W; +const DISPLAY_H = Math.round(SVG_VB_H * SCALE); + +interface RaspberryPiZeroProps { + id?: string; + x?: number; + y?: number; +} + +export const RaspberryPiZero = ({ + id = 'raspberry-pi-zero', + x = 0, + y = 0, +}: RaspberryPiZeroProps) => { + const imgRef = useRef(null); + + useEffect(() => { + if (imgRef.current) { + (imgRef.current as unknown as { pinInfo: unknown }).pinInfo = buildPi40PinHeader({ + xStart: HDR_X0 * SCALE, + xStep: HDR_STEP * SCALE, + yTop: HDR_Y_TOP * SCALE, + yBot: HDR_Y_BOT * SCALE, + }); + } + }, []); + + return ( + Raspberry Pi Zero 2 W + ); +}; diff --git a/frontend/src/components/velxio-components/pi40PinHeader.ts b/frontend/src/components/velxio-components/pi40PinHeader.ts index 8b7954de..8e2e63e4 100644 --- a/frontend/src/components/velxio-components/pi40PinHeader.ts +++ b/frontend/src/components/velxio-components/pi40PinHeader.ts @@ -4,9 +4,12 @@ * BCM GPIO assignment, same naming convention — so wires drawn against * a Pi 3 example transfer to a Pi 4 or Pi 5 board without re-routing. * - * Coordinates are in CSS pixels relative to the board element's top-left, - * sized to fit the 250×160 footprint shared by every Velxio Pi element. - * Pin "1" sits top-left; odd pins on the top row, even on the bottom. + * Coordinates are in CSS pixels relative to the board element's top-left. + * Every Pi draws its header at a different place on its own artwork, so the + * geometry is a parameter: pass the header origin, the column pitch and the + * two row offsets AS MEASURED ON THE ART, and get pin tips that land on the + * drawn pads. Pin "1" sits top-left; odd pins on the top row, even on the + * bottom. * * Names follow the Pi 3 wrapper convention (BCM GPIO numbers like * "GPIO14" plus power/ground labels) so example wires using those names @@ -20,10 +23,24 @@ export interface PinInfo { signals: string[]; } -const PIN_X_START = 20; -const PIN_X_STEP = 10; -const PIN_Y_TOP = 10; -const PIN_Y_BOT = 20; +export interface Pi40HeaderGeometry { + /** x of pin 1 / pin 2 (column 0), in CSS pixels. */ + xStart: number; + /** Column pitch in CSS pixels — 2.54 mm scaled by the art's own scale. */ + xStep: number; + /** y of the odd (top) row. */ + yTop: number; + /** y of the even (bottom) row. */ + yBot: number; +} + +/** The 250x160 footprint the first Velxio Pi element was drawn at. */ +const DEFAULT_GEOMETRY: Pi40HeaderGeometry = { + xStart: 20, + xStep: 10, + yTop: 10, + yBot: 20, +}; const PI_PIN_NAMES: Record = { 1: '3V3', 2: '5V', @@ -48,22 +65,25 @@ const PI_PIN_NAMES: Record = { 39: 'GND', 40: 'GPIO21', }; -export function buildPi40PinHeader(): PinInfo[] { +export function buildPi40PinHeader( + geometry: Pi40HeaderGeometry = DEFAULT_GEOMETRY, +): PinInfo[] { + const { xStart, xStep, yTop, yBot } = geometry; const pins: PinInfo[] = []; for (let col = 0; col < 20; col++) { const oddPin = col * 2 + 1; // top row const evenPin = col * 2 + 2; // bottom row - const px = PIN_X_START + col * PIN_X_STEP; + const px = xStart + col * xStep; pins.push({ name: PI_PIN_NAMES[oddPin] ?? `P${oddPin}`, x: px, - y: PIN_Y_TOP, + y: yTop, signals: [], }); pins.push({ name: PI_PIN_NAMES[evenPin] ?? `P${evenPin}`, x: px, - y: PIN_Y_BOT, + y: yBot, signals: [], }); }