feat: update pin mappings and descriptions for Raspberry Pi Pico examples

pull/47/head
David Montero Crespo 2026-03-21 14:02:46 -03:00
parent dc5dfb8635
commit b900f960ad
4 changed files with 86 additions and 32 deletions

View File

@ -3467,16 +3467,16 @@ void loop() {
{
id: 'pico-hcsr04',
title: 'Pico: HC-SR04 Ultrasonic Distance',
description: 'Measure distance with an HC-SR04 sensor on the Raspberry Pi Pico. TRIG on GP9, ECHO on GP10.',
description: 'Measure distance with an HC-SR04 sensor on the Raspberry Pi Pico. TRIG on D5 (GP17), ECHO on D6 (GP18).',
category: 'sensors',
difficulty: 'beginner',
boardType: 'raspberry-pi-pico',
boardFilter: 'raspberry-pi-pico',
code: `// Raspberry Pi Pico — HC-SR04 Ultrasonic Distance Sensor
// Wiring: TRIG → GP9 | ECHO → GP10 | VCC → 3.3V | GND → GND
// Wiring: TRIG → D5(GP17) | ECHO → D6(GP18) | VCC → 3.3V | GND → GND
#define TRIG_PIN 9 // GPIO 9
#define ECHO_PIN 10 // GPIO 10
#define TRIG_PIN 17 // GPIO 17 (D5)
#define ECHO_PIN 18 // GPIO 18 (D6)
void setup() {
Serial.begin(115200);
@ -3510,22 +3510,22 @@ void loop() {
wires: [
{ id: 'pcs-vcc', start: { componentId: 'nano-rp2040', pinName: '3.3V' }, end: { componentId: 'pico-sr1', pinName: 'VCC' }, color: '#ff4444' },
{ id: 'pcs-gnd', start: { componentId: 'nano-rp2040', pinName: 'GND.1' }, end: { componentId: 'pico-sr1', pinName: 'GND' }, color: '#000000' },
{ id: 'pcs-trig', start: { componentId: 'nano-rp2040', pinName: 'GP9' }, end: { componentId: 'pico-sr1', pinName: 'TRIG' }, color: '#ff8800' },
{ id: 'pcs-echo', start: { componentId: 'nano-rp2040', pinName: 'GP10' }, end: { componentId: 'pico-sr1', pinName: 'ECHO' }, color: '#22cc22' },
{ id: 'pcs-trig', start: { componentId: 'nano-rp2040', pinName: 'D5' }, end: { componentId: 'pico-sr1', pinName: 'TRIG' }, color: '#ff8800' },
{ id: 'pcs-echo', start: { componentId: 'nano-rp2040', pinName: 'D6' }, end: { componentId: 'pico-sr1', pinName: 'ECHO' }, color: '#22cc22' },
],
},
{
id: 'pico-pir',
title: 'Pico: PIR Motion Detector',
description: 'Detect movement with a PIR sensor on GP14. The built-in LED (GP25) activates when motion is detected.',
description: 'Detect movement with a PIR sensor on D4 (GP16). The built-in LED (GP25) activates when motion is detected.',
category: 'sensors',
difficulty: 'beginner',
boardType: 'raspberry-pi-pico',
boardFilter: 'raspberry-pi-pico',
code: `// Raspberry Pi Pico — PIR Motion Sensor
// Wiring: OUT → GP14 | VCC → 3.3V | GND → GND
// Wiring: OUT → D4(GP16) | VCC → 3.3V | GND → GND
#define PIR_PIN 14 // GPIO 14
#define PIR_PIN 16 // GPIO 16 (D4)
#define LED_PIN 25 // on-board LED (LED_BUILTIN on Pico)
bool prevMotion = false;
@ -3557,24 +3557,24 @@ void loop() {
wires: [
{ id: 'pp-vcc', start: { componentId: 'nano-rp2040', pinName: '3.3V' }, end: { componentId: 'pico-pir1', pinName: 'VCC' }, color: '#ff4444' },
{ id: 'pp-gnd', start: { componentId: 'nano-rp2040', pinName: 'GND.1' }, end: { componentId: 'pico-pir1', pinName: 'GND' }, color: '#000000' },
{ id: 'pp-out', start: { componentId: 'nano-rp2040', pinName: 'GP14' }, end: { componentId: 'pico-pir1', pinName: 'OUT' }, color: '#ffcc00' },
{ id: 'pp-out', start: { componentId: 'nano-rp2040', pinName: 'D4' }, end: { componentId: 'pico-pir1', pinName: 'OUT' }, color: '#ffcc00' },
],
},
{
id: 'pico-servo',
title: 'Pico: Servo Motor Sweep',
description: 'Sweep a servo motor from 0° to 180° and back on the Raspberry Pi Pico using GP15 (PWM).',
description: 'Sweep a servo motor from 0° to 180° and back on the Raspberry Pi Pico using D3 / GP15 (PWM).',
category: 'robotics',
difficulty: 'beginner',
boardType: 'raspberry-pi-pico',
boardFilter: 'raspberry-pi-pico',
code: `// Raspberry Pi Pico — Servo Motor Sweep
// Wiring: PWM → GP15 | V+ → 3.3V | GND → GND
// Wiring: PWM → D3(GP15) | V+ → 3.3V | GND → GND
// Uses built-in Servo library
#include <Servo.h>
#define SERVO_PIN 15 // GPIO 15
#define SERVO_PIN 15 // GPIO 15 (D3)
Servo myServo;
@ -3596,7 +3596,7 @@ void loop() {
wires: [
{ id: 'psv-vcc', start: { componentId: 'nano-rp2040', pinName: '3.3V' }, end: { componentId: 'pico-sv1', pinName: 'V+' }, color: '#ff4444' },
{ id: 'psv-gnd', start: { componentId: 'nano-rp2040', pinName: 'GND.1' }, end: { componentId: 'pico-sv1', pinName: 'GND' }, color: '#000000' },
{ id: 'psv-pwm', start: { componentId: 'nano-rp2040', pinName: 'GP15' }, end: { componentId: 'pico-sv1', pinName: 'PWM' }, color: '#ff8800' },
{ id: 'psv-pwm', start: { componentId: 'nano-rp2040', pinName: 'D3' }, end: { componentId: 'pico-sv1', pinName: 'PWM' }, color: '#ff8800' },
],
},
{
@ -3649,18 +3649,18 @@ void loop() {
{
id: 'pico-joystick',
title: 'Pico: Analog Joystick',
description: 'Read X/Y axes and button press from an analog joystick. VERT on A0 (GP26), HORZ on A1 (GP27), SEL button on GP14.',
description: 'Read X/Y axes and button press from an analog joystick. VERT on A0 (GP26), HORZ on A1 (GP27), SEL button on D4 (GP16).',
category: 'sensors',
difficulty: 'beginner',
boardType: 'raspberry-pi-pico',
boardFilter: 'raspberry-pi-pico',
code: `// Raspberry Pi Pico — Analog Joystick
// Wiring: VERT → A0(GP26) | HORZ → A1(GP27) | SEL → GP14
// Wiring: VERT → A0(GP26) | HORZ → A1(GP27) | SEL → D4(GP16)
// VCC → 3.3V | GND → GND
#define JOY_VERT A0 // GP26
#define JOY_HORZ A1 // GP27
#define JOY_BTN 14 // GP14
#define JOY_BTN 16 // GP16 (D4)
void setup() {
Serial.begin(115200);
@ -3691,7 +3691,7 @@ void loop() {
{ id: 'pj-gnd', start: { componentId: 'nano-rp2040', pinName: 'GND.1' }, end: { componentId: 'pico-joy1', pinName: 'GND' }, color: '#000000' },
{ id: 'pj-vert', start: { componentId: 'nano-rp2040', pinName: 'A0' }, end: { componentId: 'pico-joy1', pinName: 'VERT' }, color: '#22aaff' },
{ id: 'pj-horz', start: { componentId: 'nano-rp2040', pinName: 'A1' }, end: { componentId: 'pico-joy1', pinName: 'HORZ' }, color: '#22cc44' },
{ id: 'pj-sel', start: { componentId: 'nano-rp2040', pinName: 'GP14' }, end: { componentId: 'pico-joy1', pinName: 'SEL' }, color: '#aa44ff' },
{ id: 'pj-sel', start: { componentId: 'nano-rp2040', pinName: 'D4' }, end: { componentId: 'pico-joy1', pinName: 'SEL' }, color: '#aa44ff' },
],
},

View File

@ -232,10 +232,10 @@ export class RP2040Simulator {
const isHigh = state === GPIOPinState.High;
this.pinManager.triggerPinChange(pin, isHigh);
if (this.onPinChangeWithTime && this.rp2040) {
// Use clock cycles if available, otherwise 0
// IClock interface exposes `nanos` (not `timeUs`)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const clk = (this.rp2040 as any).clock;
const timeMs = clk ? clk.timeUs / 1000 : 0;
const timeMs = clk ? (clk.nanos as number) / 1_000_000 : 0;
this.onPinChangeWithTime(pin, isHigh, timeMs);
}
});

View File

@ -274,26 +274,72 @@ PartSimulationRegistry.register('servo', {
const MAX_PULSE_US = 2400;
const CPU_HZ = 16_000_000;
// ── Primary: cycle-accurate pulse width measurement ────────────────
// ── RP2040 path: read PWM CC/TOP registers directly ────────────────
// Arduino-Pico Servo library uses analogWriteFreq(50) + analogWriteRange(20000)
// Each PWM slice CC value / (TOP+1) * 20000 gives pulse width in µs.
// GPIO n → slice = n>>1, channel A (even) or B (odd) of `pwm.channels[slice].cc`.
if (avrSimulator instanceof RP2040Simulator && pinSIG !== null) {
const rp2040 = (avrSimulator as unknown as Record<string, unknown>).rp2040 as Record<string, unknown> | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pwmChannels = (rp2040 as any)?.pwm?.channels as Array<{ cc: number; top: number }> | undefined;
if (pwmChannels) {
const slice = pinSIG >> 1;
const isChannelB = (pinSIG & 1) === 1;
let lastPulseUs = -1;
let rafId: number | null = null;
const pollPWM = () => {
if (avrSimulator.isRunning()) {
const ch = pwmChannels[slice];
if (ch && ch.top > 0) {
const ccRaw = ch.cc;
const ccVal = isChannelB ? (ccRaw >>> 16) & 0xffff : ccRaw & 0xffff;
// Servo period is 20ms (50 Hz via analogWriteFreq(50))
const pulseUs = Math.round((ccVal / (ch.top + 1)) * 20_000);
if (pulseUs !== lastPulseUs) {
lastPulseUs = pulseUs;
if (pulseUs >= MIN_PULSE_US && pulseUs <= MAX_PULSE_US) {
const angle = Math.round(
((pulseUs - MIN_PULSE_US) / (MAX_PULSE_US - MIN_PULSE_US)) * 180
);
el.angle = angle;
}
}
}
}
rafId = requestAnimationFrame(pollPWM);
};
rafId = requestAnimationFrame(pollPWM);
return () => { if (rafId !== null) cancelAnimationFrame(rafId); };
}
}
// ── AVR primary: cycle-accurate pulse width measurement ────────────
if (pinSIG !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pinManager = (avrSimulator as any).pinManager as import('../PinManager').PinManager | undefined;
if (pinManager) {
let riseTime = -1; // cpu.cycles at last rising edge
let riseTime = -1; // cycle count at last rising edge
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getCycles = () => typeof (avrSimulator as any).getCurrentCycles === 'function'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
? (avrSimulator as any).getCurrentCycles() as number
// eslint-disable-next-line @typescript-eslint/no-explicit-any
: ((avrSimulator as any).cpu?.cycles ?? 0) as number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const clockHz = typeof (avrSimulator as any).getClockHz === 'function'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
? (avrSimulator as any).getClockHz() as number
: CPU_HZ;
const unsubscribe = pinManager.onPinChange(pinSIG, (_pin, state) => {
const cpu = (avrSimulator as any).cpu;
if (!cpu) return;
if (state) {
// Rising edge — record cycle count
riseTime = cpu.cycles;
riseTime = getCycles();
} else if (riseTime >= 0) {
// Falling edge — compute pulse width in µs
const pulseCycles = cpu.cycles - riseTime;
const pulseUs = (pulseCycles / CPU_HZ) * 1_000_000;
const pulseCycles = getCycles() - riseTime;
const pulseUs = (pulseCycles / clockHz) * 1_000_000;
riseTime = -1;
// Only update if pulse is in valid RC servo range
if (pulseUs >= MIN_PULSE_US && pulseUs <= MAX_PULSE_US) {
const angle = Math.round(
((pulseUs - MIN_PULSE_US) / (MAX_PULSE_US - MIN_PULSE_US)) * 180

View File

@ -47,6 +47,14 @@ export function calculatePinPosition(
if (!pin && !pinName.includes('.')) {
pin = pinInfo.find((p: any) => p.name === `${pinName}.1`);
}
// Fallback: GP-prefix → match description field (e.g. 'GP15' → description 'GPIO15')
// Needed for Nano RP2040 Connect which uses D-prefix pin names but GPIO descriptions
if (!pin && pinName.startsWith('GP')) {
const gpioNum = parseInt(pinName.substring(2), 10);
if (!isNaN(gpioNum)) {
pin = pinInfo.find((p: any) => p.description === `GPIO${gpioNum}`);
}
}
if (!pin) {
console.warn(`[pinPositionCalculator] Pin ${pinName} not found on component ${componentId}`);
console.warn(`Available pins:`, pinInfo.map((p: any) => p.name));