fix(ui+spice+example): interactive wokwi components, NTC formula, photoresistor alias
Three independent fixes uncovered during a systematic example-by-example audit (plan/full_test_plan/): 1. DynamicComponent.handleMouseDown was calling e.stopPropagation() unconditionally in the capture phase. That swallowed pointerdown BEFORE wokwi-potentiometer / pushbutton / slide-switch / joystick could see it, so the rotary knob would not rotate and buttons wouldn't press even with a real OS mouse. Now we skip the swallow when the click target is an inner wokwi-* element during a live simulation, letting the wokwi component own its own pointerdown while still allowing the canvas drag-to-rearrange flow on the wrapper / non-interactive surface. 2. examples.ts uno-ntc (and pico-ntc) sketch had the NTC divider formula inverted relative to both the SPICE mapper topology (VCC -> R_NTC -> A1 -> R_pull -> GND, the standard module wiring) and real wokwi-ntc-temperature-sensor modules. Moving the slider to 60 C made the firmware print -3.42 C. Flipped the formula to r = SERIES_R * (VCC - v) / v. Now slider 60 C -> Serial reports 60.12 C and A1 voltmeter shows 4.00 V. 3. componentToSpice.ts photoresistor mapper was only registered under the bare key `photoresistor`, but example components use the metadataId `photoresistor-sensor`. Added an alias so the LDR + pull-down divider gets emitted for the real component instance. All three reproduce visually in seconds; documented per-example in plan/full_test_plan/examples/. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
2b668d882c
commit
dd22bcfe50
|
|
@ -276,12 +276,37 @@ export const DynamicComponent: React.FC<DynamicComponentProps> = ({
|
|||
*/
|
||||
const handleMouseDown = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (onMouseDown) {
|
||||
e.stopPropagation();
|
||||
onMouseDown(e);
|
||||
if (!onMouseDown) return;
|
||||
// Don't swallow the pointerdown when the user is interacting with a
|
||||
// live wokwi component (rotating a potentiometer knob, pressing a
|
||||
// pushbutton, toggling a slide-switch, dragging the joystick stick,
|
||||
// etc.). The wokwi-elements internally use pointerdown/move/up on
|
||||
// their shadow-DOM SVGs; if we call stopPropagation() in the capture
|
||||
// phase here, that internal logic NEVER receives the event and the
|
||||
// knob can't rotate, the button never reports pressed, etc.
|
||||
//
|
||||
// The canvas's drag-to-rearrange flow still works: the user can grab
|
||||
// the component WRAPPER (its padding/border) or any non-interactive
|
||||
// part of the body. For interactive parts the wokwi component owns
|
||||
// the click — same UX as Wokwi/Tinkercad.
|
||||
//
|
||||
// We additionally allow the canvas drag when the simulation is NOT
|
||||
// running (interactionRunning=false): the user is in "edit mode"
|
||||
// arranging the board, so capturing the click into a canvas drag
|
||||
// is the correct behaviour even on interactive components.
|
||||
const target = e.target as HTMLElement;
|
||||
const targetIsInteractive =
|
||||
isInteractive &&
|
||||
interactionRunning &&
|
||||
target.tagName.toLowerCase().startsWith('wokwi-');
|
||||
if (targetIsInteractive) {
|
||||
// Let the wokwi component own this pointerdown.
|
||||
return;
|
||||
}
|
||||
e.stopPropagation();
|
||||
onMouseDown(e);
|
||||
},
|
||||
[onMouseDown],
|
||||
[onMouseDown, isInteractive, interactionRunning],
|
||||
);
|
||||
|
||||
const handleDoubleClick = useCallback(
|
||||
|
|
|
|||
|
|
@ -5185,7 +5185,13 @@ const float NOM_TEMP_K = 298.15; // 25 °C in Kelvin
|
|||
float readTempC() {
|
||||
int raw = analogRead(NTC_PIN);
|
||||
float v = raw * (VCC / 1023.0);
|
||||
float r = SERIES_R * v / (VCC - v); // voltage divider
|
||||
// Voltage divider topology used by standard NTC modules and by Velxio's
|
||||
// wokwi-ntc-temperature-sensor: VCC → R_NTC → A1 → R_pull (10k) → GND.
|
||||
// Higher temperature → R_NTC drops → V rises, so the NTC resistance is
|
||||
// r = R_pull * (VCC - v) / v
|
||||
// Using the inverted form (r = R_pull * v / (VCC - v)) gives the wrong
|
||||
// sign and Steinhart-Hart returns negative temperatures for hot inputs.
|
||||
float r = SERIES_R * (VCC - v) / v;
|
||||
// Steinhart–Hart simplified equation
|
||||
float st = log(r / NOM_R) / B_COEFF + 1.0 / NOM_TEMP_K;
|
||||
return (1.0 / st) - 273.15;
|
||||
|
|
@ -5514,7 +5520,8 @@ const float NOM_TEMP_K = 298.15; // 25 °C
|
|||
float readTempC() {
|
||||
int raw = analogRead(NTC_PIN);
|
||||
float v = raw * (VCC / 1023.0);
|
||||
float r = SERIES_R * v / (VCC - v);
|
||||
// VCC → R_NTC → A1 → R_pull → GND (standard NTC module topology)
|
||||
float r = SERIES_R * (VCC - v) / v;
|
||||
float st = log(r / NOM_R) / B_COEFF + 1.0 / NOM_TEMP_K;
|
||||
return (1.0 / st) - 273.15;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1220,6 +1220,14 @@ for (const [presetId, baseId] of Object.entries(PASSIVE_PRESETS)) {
|
|||
MAPPERS[presetId] = MAPPERS[baseId];
|
||||
}
|
||||
|
||||
// Alias: metadata id for the wokwi-photoresistor-sensor breakout is
|
||||
// `photoresistor-sensor`, but the mapper is registered under the short
|
||||
// name `photoresistor` (matches the bare LDR/discrete element). Without
|
||||
// this alias, any example that drops a photoresistor sensor on the
|
||||
// canvas gets a null mapping → no R_ldr / R_pull emitted → A0 net
|
||||
// floats → analogRead returns 0 even though the divider should solve.
|
||||
MAPPERS['photoresistor-sensor'] = MAPPERS['photoresistor'];
|
||||
|
||||
/**
|
||||
* Public entry: map one Velxio component to SPICE cards.
|
||||
* Returns null if we have no mapping for this metadataId (caller should
|
||||
|
|
|
|||
Loading…
Reference in New Issue