diff --git a/frontend/src/components/DynamicComponent.tsx b/frontend/src/components/DynamicComponent.tsx index 7c6e8e3c..a3c8eb23 100644 --- a/frontend/src/components/DynamicComponent.tsx +++ b/frontend/src/components/DynamicComponent.tsx @@ -276,12 +276,37 @@ export const DynamicComponent: React.FC = ({ */ 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( diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index e807dc81..d82ef668 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -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; } diff --git a/frontend/src/simulation/spice/componentToSpice.ts b/frontend/src/simulation/spice/componentToSpice.ts index 8face639..6fd97c83 100644 --- a/frontend/src/simulation/spice/componentToSpice.ts +++ b/frontend/src/simulation/spice/componentToSpice.ts @@ -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