fix(examples): night-light LDR shorted AO to VCC — "1M" is milliohms in SPICE
100d-auto-night-light shipped `dark: "1M"` meaning 1 megaohm. The value parser follows the SPICE convention where M is MILLI and mega is `Meg`, so the dark resistance became 0.001 ohm. With the divider's top leg a short, AO sat at VCC: `ldr.read()` returned 4095 on every sample, the sketch's `light < 500` branch never ran, and the lamp LED never lit. Dragging the lux control changed nothing either, because every lux value still divides a short (measured n1 = 3.299999996 V, which is exactly what 100k / (100k + 0.00011) predicts). The netlist snapshot had frozen the bug in place: - R_night-ldr_ldr vcc_rail n1 0.00011111111111111112 + R_night-ldr_ldr vcc_rail n1 111111.11111111111 Diagram now says "1Meg" (regenerated from test/test_100_days), and the mapper clamps a sub-1k dark resistance to the documented 1Meg default with a console warning naming the milli/mega trap — a photoresistor that reads as a short is always this mistake, and it fails in the most confusing way possible: the part looks alive, it just never moves.
This commit is contained in:
parent
5c95ade35f
commit
9b0b4bc038
|
|
@ -22,7 +22,7 @@ R_autopull_n7 n7 0 100Meg
|
||||||
exports[`netlist snapshot — 100-days (49 examples) > 100d-auto-night-light-using-ldr-esp32-plus-micropython 1`] = `
|
exports[`netlist snapshot — 100-days (49 examples) > 100d-auto-night-light-using-ldr-esp32-plus-micropython 1`] = `
|
||||||
"* Velxio circuit
|
"* Velxio circuit
|
||||||
V_VCC_RAIL vcc_rail 0 DC 5
|
V_VCC_RAIL vcc_rail 0 DC 5
|
||||||
R_night-ldr_ldr vcc_rail n1 0.00011111111111111112
|
R_night-ldr_ldr vcc_rail n1 111111.11111111111
|
||||||
R_night-ldr_pull n1 0 100000
|
R_night-ldr_pull n1 0 100000
|
||||||
R_night-r1 n0 n2 220
|
R_night-r1 n0 n2 220
|
||||||
V_night-led1_sense n2 night-led1_sense_mid DC 0
|
V_night-led1_sense n2 night-led1_sense_mid DC 0
|
||||||
|
|
|
||||||
|
|
@ -410,3 +410,36 @@ describe('componentToSpice — ngspice accepts every card', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('photoresistor — dark resistance suffix', () => {
|
||||||
|
const mapOne = (properties: Record<string, unknown>) => {
|
||||||
|
const nets: Record<string, string> = { VCC: 'vcc', GND: '0', AO: 'n_ao' };
|
||||||
|
return componentToSpice(
|
||||||
|
{ id: 'ldr1', metadataId: 'photoresistor-sensor', properties },
|
||||||
|
(pin: string) => nets[pin] ?? null,
|
||||||
|
{} as never,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('reads "1Meg" as one megaohm', () => {
|
||||||
|
const out = mapOne({ lux: 400, dark: '1Meg', k: 20, pullup: '100k' });
|
||||||
|
// R = 1e6 / (1 + 20*400/1000) = 111111.1 ohm
|
||||||
|
expect(out?.cards[0]).toMatch(/R_ldr1_ldr vcc n_ao 111111/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not short AO to VCC when dark is written "1M"', () => {
|
||||||
|
// Regression: 100d-auto-night-light shipped dark: "1M" meaning 1 megaohm.
|
||||||
|
// SPICE reads M as milli, so it parsed to 0.001 ohm — AO sat at VCC, the
|
||||||
|
// ADC read 4095 forever and dragging the lux control changed nothing
|
||||||
|
// (every lux value still divides a short). Clamp instead of emitting it.
|
||||||
|
const out = mapOne({ lux: 400, dark: '1M', k: 20, pullup: '100k' });
|
||||||
|
const ldrCard = out?.cards[0] ?? '';
|
||||||
|
const ohms = Number(ldrCard.split(/\s+/).pop());
|
||||||
|
expect(ohms).toBeGreaterThan(1_000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still emits the pull-down leg to GND', () => {
|
||||||
|
const out = mapOne({ lux: 400, dark: '1Meg', k: 20, pullup: '100k' });
|
||||||
|
expect(out?.cards[1]).toBe('R_ldr1_pull n_ao 0 100000');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ while True:
|
||||||
` },
|
` },
|
||||||
],
|
],
|
||||||
code: '',
|
code: '',
|
||||||
components: [{"type": "wokwi-photoresistor-sensor", "id": "night-ldr", "x": 430, "y": 300, "properties": {"lux": 400, "dark": "1M", "k": 20, "pullup": "100k"}}, {"type": "wokwi-resistor", "id": "night-r1", "x": 620, "y": 40, "properties": {"value": "220"}}, {"type": "wokwi-led", "id": "night-led1", "x": 620, "y": 140, "properties": {"color": "yellow", "label": "LAMP"}}],
|
components: [{"type": "wokwi-photoresistor-sensor", "id": "night-ldr", "x": 430, "y": 300, "properties": {"lux": 400, "dark": "1Meg", "k": 20, "pullup": "100k"}}, {"type": "wokwi-resistor", "id": "night-r1", "x": 620, "y": 40, "properties": {"value": "220"}}, {"type": "wokwi-led", "id": "night-led1", "x": 620, "y": 140, "properties": {"color": "yellow", "label": "LAMP"}}],
|
||||||
wires: [{"id": "night-w1", "start": {"componentId": "arduino-uno", "pinName": "3V3"}, "end": {"componentId": "night-ldr", "pinName": "VCC"}, "color": "#ff4444"}, {"id": "night-w2", "start": {"componentId": "arduino-uno", "pinName": "GND"}, "end": {"componentId": "night-ldr", "pinName": "GND"}, "color": "#000000"}, {"id": "night-w3", "start": {"componentId": "night-ldr", "pinName": "AO"}, "end": {"componentId": "arduino-uno", "pinName": "34"}, "color": "#22aaff"}, {"id": "night-w4", "start": {"componentId": "arduino-uno", "pinName": "2"}, "end": {"componentId": "night-r1", "pinName": "1"}, "color": "#cc44cc"}, {"id": "night-w5", "start": {"componentId": "night-r1", "pinName": "2"}, "end": {"componentId": "night-led1", "pinName": "A"}, "color": "#cc44cc"}, {"id": "night-w6", "start": {"componentId": "night-led1", "pinName": "C"}, "end": {"componentId": "arduino-uno", "pinName": "GND"}, "color": "#000000"}],
|
wires: [{"id": "night-w1", "start": {"componentId": "arduino-uno", "pinName": "3V3"}, "end": {"componentId": "night-ldr", "pinName": "VCC"}, "color": "#ff4444"}, {"id": "night-w2", "start": {"componentId": "arduino-uno", "pinName": "GND"}, "end": {"componentId": "night-ldr", "pinName": "GND"}, "color": "#000000"}, {"id": "night-w3", "start": {"componentId": "night-ldr", "pinName": "AO"}, "end": {"componentId": "arduino-uno", "pinName": "34"}, "color": "#22aaff"}, {"id": "night-w4", "start": {"componentId": "arduino-uno", "pinName": "2"}, "end": {"componentId": "night-r1", "pinName": "1"}, "color": "#cc44cc"}, {"id": "night-w5", "start": {"componentId": "night-r1", "pinName": "2"}, "end": {"componentId": "night-led1", "pinName": "A"}, "color": "#cc44cc"}, {"id": "night-w6", "start": {"componentId": "night-led1", "pinName": "C"}, "end": {"componentId": "arduino-uno", "pinName": "GND"}, "color": "#000000"}],
|
||||||
tags: ["100-days", "esp32", "ldr", "micropython"],
|
tags: ["100-days", "esp32", "ldr", "micropython"],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1206,7 +1206,24 @@ const MAPPERS: Record<string, Mapper> = {
|
||||||
// The DO (digital threshold) pin is ignored for analog simulation.
|
// The DO (digital threshold) pin is ignored for analog simulation.
|
||||||
photoresistor: (comp, netLookup) => {
|
photoresistor: (comp, netLookup) => {
|
||||||
const lux = Number(comp.properties.lux ?? 500);
|
const lux = Number(comp.properties.lux ?? 500);
|
||||||
const Rdark = parseValueWithUnits(comp.properties.dark, 1_000_000);
|
// `dark` goes through the SPICE suffix convention, where M is MILLI and
|
||||||
|
// mega is `Meg`. A dark resistance below 1k is not a photoresistor — it
|
||||||
|
// is almost always "1M" written meaning 1 megaohm, which parses to
|
||||||
|
// 0.001 ohm and shorts AO straight to VCC. The symptom is nasty because
|
||||||
|
// it looks like the part works: the ADC just reads full scale forever and
|
||||||
|
// the lux control does nothing (every value is still a short). Clamp to
|
||||||
|
// the documented default and say so, rather than emitting a short.
|
||||||
|
const darkRaw = parseValueWithUnits(comp.properties.dark, 1_000_000);
|
||||||
|
let Rdark = darkRaw;
|
||||||
|
if (!(darkRaw >= 1_000)) {
|
||||||
|
Rdark = 1_000_000;
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn(
|
||||||
|
`[spice] ${comp.id}: dark resistance ${JSON.stringify(comp.properties.dark)} ` +
|
||||||
|
`parsed as ${darkRaw} ohm — too low for a photoresistor. SPICE reads M as ` +
|
||||||
|
`milli; write "1Meg" for 1 megaohm. Using 1Meg.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
const k = Number(comp.properties.k ?? 5);
|
const k = Number(comp.properties.k ?? 5);
|
||||||
const Rldr = Rdark / (1 + (k * lux) / 1000);
|
const Rldr = Rdark / (1 + (k * lux) / 1000);
|
||||||
const vcc = netLookup('VCC');
|
const vcc = netLookup('VCC');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue