fix(esp32sim): a rebuilt board adopts the I2C devices its parts attached
The pro overlay loads async, so a deep-linked example attaches its part device models to the shim wired around the STOCK QEMU bridge — whose attachSyncI2cDevice is a silent optional no-op — and only then does the overlay's factory rebuild the board with the js-engine bridge. The rebuild made a brand-new shim; the parts on the canvas hold the old one in their attach closures and never re-attach. Result: the engine booted with an EMPTY I2C bus and arduino's i2c-ng driver failed every transaction with ESP_ERR_INVALID_STATE (the seated C6 gesture example — measured live with the bus device table at Map(0)). Whether it broke was a chunk-load race; the pro bundle's growth turned it into a sure loss. The shim now records every sync-attached device and a rebuilt shim adopts its predecessor's — through addI2CDevice, so the new Delegating bridge buffers them pre-connect and its start() replays them into the engine after reset, as it always did for attaches it actually heard.
This commit is contained in:
parent
8b2715affd
commit
77e947ef98
|
|
@ -421,8 +421,20 @@ class Esp32BridgeShim {
|
|||
* find the device. ProtocolParts calls this on the ESP32 path
|
||||
* alongside the existing `registerSensor` + `addI2CTransactionListener`.
|
||||
*/
|
||||
/** Sync-attached part devices, kept so a bridge REBUILD can adopt them.
|
||||
* The overlay loads async: a deep-linked example attaches its parts to the
|
||||
* shim wired around the stock QEMU bridge (whose attachSyncI2cDevice is
|
||||
* missing — a silent no-op), and only then does the overlay's factory
|
||||
* rebuild the board. Without this record the part's device existed
|
||||
* nowhere: the C6 gesture example booted with an EMPTY engine I2C bus and
|
||||
* arduino's i2c-ng driver failed every transaction (ESP_ERR_INVALID_STATE)
|
||||
* — whether it broke depended on a chunk-load race the pro bundle's growth
|
||||
* turned into a sure loss. */
|
||||
private syncI2cParts = new Map<number, I2CDevice>();
|
||||
|
||||
addI2CDevice(device: I2CDevice, _bus: 0 | 1 = 0): void {
|
||||
this.i2cBusInstance.addDevice(device);
|
||||
this.syncI2cParts.set(device.address, device);
|
||||
// An in-browser JS-emulator substitute bridge (velxio-prod overlay) plugs
|
||||
// the part's real device model straight onto the engine's synchronous I2C
|
||||
// bus, so the firmware's own reads hit it (sensors answer, displays
|
||||
|
|
@ -431,6 +443,14 @@ class Esp32BridgeShim {
|
|||
(this.bridge as { attachSyncI2cDevice?: (d: I2CDevice) => void }).attachSyncI2cDevice?.(device);
|
||||
}
|
||||
|
||||
/** A rebuilt board gets a fresh shim; the parts on the canvas do NOT
|
||||
* re-attach (their closures hold the old shim), so the new shim re-plays
|
||||
* everything the old one had. Runs through addI2CDevice so the new bridge
|
||||
* (a pre-connect-buffering Delegating bridge) hears about each device. */
|
||||
adoptPartsFrom(prev: Esp32BridgeShim): void {
|
||||
for (const dev of prev.syncI2cParts.values()) this.addI2CDevice(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach (or clear, with null) the microphone sample source feeding the
|
||||
* board's I2S RX path: one signed 16-bit sample per call. Same forwarding
|
||||
|
|
@ -477,6 +497,7 @@ class Esp32BridgeShim {
|
|||
/** Remove a previously-registered virtual device. */
|
||||
removeI2CDevice(addr: number, _bus: 0 | 1 = 0): void {
|
||||
this.i2cBusInstance.removeDevice(addr);
|
||||
this.syncI2cParts.delete(addr);
|
||||
(this.bridge as { detachSyncI2cDevice?: (a: number) => void }).detachSyncI2cDevice?.(addr);
|
||||
}
|
||||
|
||||
|
|
@ -1333,6 +1354,13 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
if (existingShim?.clearAllProxies) {
|
||||
try { existingShim.clearAllProxies(); } catch { /* ignore */ }
|
||||
}
|
||||
// The parts on the canvas attached their I2C device models to the OLD
|
||||
// shim (their attach closures hold it) and will not re-attach for a
|
||||
// bridge rebuild — carry them over, or the rebuilt engine boots with an
|
||||
// empty I2C bus and every Wire transaction fails.
|
||||
if (existingShim instanceof Esp32BridgeShim) {
|
||||
try { shim.adoptPartsFrom(existingShim); } catch { /* ignore */ }
|
||||
}
|
||||
simulatorMap.set(id, shim);
|
||||
// The Interconnect's serial fan-out wraps the INSTANCE's
|
||||
// onSerialData; a fresh simulator (compile, reset, engine
|
||||
|
|
|
|||
Loading…
Reference in New Issue