feat: enhance RP2040 and AVR simulators with serial baud rate handling; update editor toolbar and library manager modal for improved state management and UI

This commit is contained in:
David Montero Crespo 2026-03-05 21:07:10 -03:00
parent 4ba2ccb877
commit 9b8747349f
10 changed files with 181 additions and 56 deletions

View File

@ -224,6 +224,13 @@ class ArduinoCLIService:
# arduino-cli requires sketch name to match directory name # arduino-cli requires sketch name to match directory name
sketch_file = sketch_dir / "sketch.ino" sketch_file = sketch_dir / "sketch.ino"
# For RP2040 boards, redirect Serial (USB CDC) to Serial1 (UART0)
# The emulator captures UART0 output, but the arduino-pico core
# defaults Serial to USB CDC which isn't emulated.
if "rp2040" in board_fqbn:
code = "#define Serial Serial1\n" + code
sketch_file.write_text(code) sketch_file.write_text(code)
print(f"Created sketch file: {sketch_file}") print(f"Created sketch file: {sketch_file}")

View File

@ -3,12 +3,18 @@ import { useEditorStore } from '../../store/useEditorStore';
import { useSimulatorStore, BOARD_FQBN, BOARD_LABELS } from '../../store/useSimulatorStore'; import { useSimulatorStore, BOARD_FQBN, BOARD_LABELS } from '../../store/useSimulatorStore';
import { compileCode } from '../../services/compilation'; import { compileCode } from '../../services/compilation';
import { LibraryManagerModal } from '../simulator/LibraryManagerModal'; import { LibraryManagerModal } from '../simulator/LibraryManagerModal';
import { CompilationConsole } from './CompilationConsole';
import { parseCompileResult } from '../../utils/compilationLogger'; import { parseCompileResult } from '../../utils/compilationLogger';
import type { CompilationLog } from '../../utils/compilationLogger'; import type { CompilationLog } from '../../utils/compilationLogger';
import './EditorToolbar.css'; import './EditorToolbar.css';
export const EditorToolbar = () => { interface EditorToolbarProps {
consoleOpen: boolean;
setConsoleOpen: (open: boolean | ((v: boolean) => boolean)) => void;
compileLogs: CompilationLog[];
setCompileLogs: (logs: CompilationLog[] | ((prev: CompilationLog[]) => CompilationLog[])) => void;
}
export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compileLogs, setCompileLogs }: EditorToolbarProps) => {
const { code } = useEditorStore(); const { code } = useEditorStore();
const { const {
boardType, boardType,
@ -23,12 +29,10 @@ export const EditorToolbar = () => {
const [compiling, setCompiling] = useState(false); const [compiling, setCompiling] = useState(false);
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
const [libManagerOpen, setLibManagerOpen] = useState(false); const [libManagerOpen, setLibManagerOpen] = useState(false);
const [consoleOpen, setConsoleOpen] = useState(false);
const [compileLogs, setCompileLogs] = useState<CompilationLog[]>([]);
const addLog = useCallback((log: CompilationLog) => { const addLog = useCallback((log: CompilationLog) => {
setCompileLogs((prev) => [...prev, log]); setCompileLogs((prev: CompilationLog[]) => [...prev, log]);
}, []); }, [setCompileLogs]);
const handleCompile = async () => { const handleCompile = async () => {
setCompiling(true); setCompiling(true);
@ -45,7 +49,7 @@ export const EditorToolbar = () => {
// Parse the full result into log entries // Parse the full result into log entries
const resultLogs = parseCompileResult(result, boardLabel); const resultLogs = parseCompileResult(result, boardLabel);
setCompileLogs((prev) => [...prev, ...resultLogs]); setCompileLogs((prev: CompilationLog[]) => [...prev, ...resultLogs]);
if (result.success) { if (result.success) {
if (result.hex_content) { if (result.hex_content) {
@ -203,18 +207,6 @@ export const EditorToolbar = () => {
<div className="toolbar-error-detail">{message.text}</div> <div className="toolbar-error-detail">{message.text}</div>
)} )}
{/* Compilation Console */}
{consoleOpen && (
<div style={{ height: 200, flexShrink: 0 }}>
<CompilationConsole
isOpen={consoleOpen}
onClose={() => setConsoleOpen(false)}
logs={compileLogs}
onClear={() => setCompileLogs([])}
/>
</div>
)}
<LibraryManagerModal isOpen={libManagerOpen} onClose={() => setLibManagerOpen(false)} /> <LibraryManagerModal isOpen={libManagerOpen} onClose={() => setLibManagerOpen(false)} />
</> </>
); );

View File

@ -191,6 +191,12 @@
animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite;
} }
.lib-spinner-center {
width: 32px;
height: 32px;
margin-bottom: 8px;
}
@keyframes spin { @keyframes spin {
from { from {
transform: rotate(0deg); transform: rotate(0deg);

View File

@ -33,26 +33,29 @@ export const LibraryManagerModal: React.FC<LibraryManagerModalProps> = ({ isOpen
} }
}, []); }, []);
// Fetch installed list when modal opens (to cross-reference in search tab) // Reset state when modal closes
useEffect(() => { useEffect(() => {
if (isOpen) { if (!isOpen) {
fetchInstalled(); setSearchQuery('');
setSearchResults([]);
setStatusMsg(null);
} }
}, [isOpen, fetchInstalled]); }, [isOpen]);
// Also refresh when switching to the installed tab // Fetch installed list when modal opens or switching to installed tab
useEffect(() => { useEffect(() => {
if (isOpen && activeTab === 'installed') { if (isOpen && activeTab === 'installed') fetchInstalled();
fetchInstalled();
}
}, [isOpen, activeTab, fetchInstalled]); }, [isOpen, activeTab, fetchInstalled]);
useEffect(() => { useEffect(() => {
if (!searchQuery.trim()) { if (isOpen) fetchInstalled();
setSearchResults([]); }, [isOpen, fetchInstalled]);
return;
} // Search: immediate on open (empty query), debounced when typing
useEffect(() => {
if (!isOpen) return;
if (debounceRef.current) clearTimeout(debounceRef.current); if (debounceRef.current) clearTimeout(debounceRef.current);
const delay = searchQuery ? 400 : 0;
debounceRef.current = setTimeout(async () => { debounceRef.current = setTimeout(async () => {
setLoadingSearch(true); setLoadingSearch(true);
setStatusMsg(null); setStatusMsg(null);
@ -65,10 +68,10 @@ export const LibraryManagerModal: React.FC<LibraryManagerModalProps> = ({ isOpen
} finally { } finally {
setLoadingSearch(false); setLoadingSearch(false);
} }
}, 500); }, delay);
return () => { if (debounceRef.current) clearTimeout(debounceRef.current); }; return () => { if (debounceRef.current) clearTimeout(debounceRef.current); };
}, [searchQuery]); }, [searchQuery, isOpen]);
const handleInstall = async (libName: string) => { const handleInstall = async (libName: string) => {
setInstallingLib(libName); setInstallingLib(libName);
@ -89,9 +92,6 @@ export const LibraryManagerModal: React.FC<LibraryManagerModalProps> = ({ isOpen
}; };
const handleClose = () => { const handleClose = () => {
setStatusMsg(null);
setSearchQuery('');
setSearchResults([]);
onClose(); onClose();
}; };
@ -193,18 +193,20 @@ export const LibraryManagerModal: React.FC<LibraryManagerModalProps> = ({ isOpen
</div> </div>
<div className="lib-list"> <div className="lib-list">
{!searchQuery.trim() && ( {loadingSearch && (
<div className="lib-empty"> <div className="lib-empty">
<p>Type a library name to search</p> <svg className="lib-spinner lib-spinner-center" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
<p className="lib-empty-sub">e.g. "ArduinoJson", "Servo", "LiquidCrystal"</p> <path d="M21 12a9 9 0 1 1-6.219-8.56" />
</svg>
<p className="lib-empty-sub">{searchQuery ? `Searching "${searchQuery}"…` : 'Loading libraries…'}</p>
</div> </div>
)} )}
{searchQuery.trim() && searchResults.length === 0 && !loadingSearch && ( {!loadingSearch && searchResults.length === 0 && (
<div className="lib-empty"> <div className="lib-empty">
<p>No libraries found for "{searchQuery}"</p> <p>{searchQuery ? `No libraries found for "${searchQuery}"` : 'No libraries available'}</p>
</div> </div>
)} )}
{searchResults.map((lib, i) => ( {!loadingSearch && searchResults.map((lib, i) => (
<div key={i} className="lib-item"> <div key={i} className="lib-item">
<div className="lib-item-info"> <div className="lib-item-info">
<div className="lib-item-header"> <div className="lib-item-header">

View File

@ -8,6 +8,7 @@ import { useSimulatorStore } from '../../store/useSimulatorStore';
export const SerialMonitor: React.FC = () => { export const SerialMonitor: React.FC = () => {
const serialOutput = useSimulatorStore((s) => s.serialOutput); const serialOutput = useSimulatorStore((s) => s.serialOutput);
const serialBaudRate = useSimulatorStore((s) => s.serialBaudRate);
const running = useSimulatorStore((s) => s.running); const running = useSimulatorStore((s) => s.running);
const serialWrite = useSimulatorStore((s) => s.serialWrite); const serialWrite = useSimulatorStore((s) => s.serialWrite);
const clearSerialOutput = useSimulatorStore((s) => s.clearSerialOutput); const clearSerialOutput = useSimulatorStore((s) => s.clearSerialOutput);
@ -49,6 +50,9 @@ export const SerialMonitor: React.FC = () => {
<div style={styles.header}> <div style={styles.header}>
<span style={styles.title}>Serial Monitor</span> <span style={styles.title}>Serial Monitor</span>
<div style={styles.headerControls}> <div style={styles.headerControls}>
{serialBaudRate > 0 && (
<span style={styles.baudRate}>{serialBaudRate.toLocaleString()} baud</span>
)}
<label style={styles.autoscrollLabel}> <label style={styles.autoscrollLabel}>
<input <input
type="checkbox" type="checkbox"
@ -127,6 +131,15 @@ const styles: Record<string, React.CSSProperties> = {
alignItems: 'center', alignItems: 'center',
gap: 8, gap: 8,
}, },
baudRate: {
color: '#569cd6',
fontSize: 11,
fontFamily: 'monospace',
background: '#1e1e1e',
border: '1px solid #3a3a3a',
borderRadius: 3,
padding: '1px 6px',
},
autoscrollLabel: { autoscrollLabel: {
color: '#999', color: '#999',
fontSize: 11, fontSize: 11,

View File

@ -6,18 +6,35 @@ import React, { useRef, useState, useCallback } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { CodeEditor } from '../components/editor/CodeEditor'; import { CodeEditor } from '../components/editor/CodeEditor';
import { EditorToolbar } from '../components/editor/EditorToolbar'; import { EditorToolbar } from '../components/editor/EditorToolbar';
import { CompilationConsole } from '../components/editor/CompilationConsole';
import { SimulatorCanvas } from '../components/simulator/SimulatorCanvas'; import { SimulatorCanvas } from '../components/simulator/SimulatorCanvas';
import { SerialMonitor } from '../components/simulator/SerialMonitor'; import { SerialMonitor } from '../components/simulator/SerialMonitor';
import { useSimulatorStore } from '../store/useSimulatorStore'; import { useSimulatorStore } from '../store/useSimulatorStore';
import type { CompilationLog } from '../utils/compilationLogger';
import '../App.css'; import '../App.css';
const BOTTOM_PANEL_MIN = 80;
const BOTTOM_PANEL_MAX = 600;
const BOTTOM_PANEL_DEFAULT = 200;
const resizeHandleStyle: React.CSSProperties = {
height: 5,
flexShrink: 0,
cursor: 'row-resize',
background: '#2a2d2e',
borderTop: '1px solid #3c3c3c',
borderBottom: '1px solid #3c3c3c',
};
export const EditorPage: React.FC = () => { export const EditorPage: React.FC = () => {
const [editorWidthPct, setEditorWidthPct] = useState(45); const [editorWidthPct, setEditorWidthPct] = useState(45);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const resizingRef = useRef(false); const resizingRef = useRef(false);
const serialMonitorOpen = useSimulatorStore((s) => s.serialMonitorOpen); const serialMonitorOpen = useSimulatorStore((s) => s.serialMonitorOpen);
const toggleSerialMonitor = useSimulatorStore((s) => s.toggleSerialMonitor); const toggleSerialMonitor = useSimulatorStore((s) => s.toggleSerialMonitor);
const [serialHeightPct, setSerialHeightPct] = useState(30); const [consoleOpen, setConsoleOpen] = useState(false);
const [compileLogs, setCompileLogs] = useState<CompilationLog[]>([]);
const [bottomPanelHeight, setBottomPanelHeight] = useState(BOTTOM_PANEL_DEFAULT);
const handleResizeMouseDown = useCallback((e: React.MouseEvent) => { const handleResizeMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault(); e.preventDefault();
@ -44,6 +61,27 @@ export const EditorPage: React.FC = () => {
document.addEventListener('mouseup', handleMouseUp); document.addEventListener('mouseup', handleMouseUp);
}, []); }, []);
const handleBottomPanelResizeMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault();
const startY = e.clientY;
const startHeight = bottomPanelHeight;
const onMove = (ev: MouseEvent) => {
const delta = startY - ev.clientY;
setBottomPanelHeight(Math.max(BOTTOM_PANEL_MIN, Math.min(BOTTOM_PANEL_MAX, startHeight + delta)));
};
const onUp = () => {
document.body.style.cursor = '';
document.body.style.userSelect = '';
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
};
document.body.style.cursor = 'row-resize';
document.body.style.userSelect = 'none';
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
}, [bottomPanelHeight]);
return ( return (
<div className="app"> <div className="app">
<header className="app-header"> <header className="app-header">
@ -90,11 +128,33 @@ export const EditorPage: React.FC = () => {
</header> </header>
<div className="app-container" ref={containerRef}> <div className="app-container" ref={containerRef}>
<div className="editor-panel" style={{ width: `${editorWidthPct}%` }}> <div className="editor-panel" style={{ width: `${editorWidthPct}%`, display: 'flex', flexDirection: 'column' }}>
<EditorToolbar /> <EditorToolbar
<div className="editor-wrapper"> consoleOpen={consoleOpen}
setConsoleOpen={setConsoleOpen}
compileLogs={compileLogs}
setCompileLogs={setCompileLogs}
/>
<div className="editor-wrapper" style={{ flex: 1, overflow: 'hidden', minHeight: 0 }}>
<CodeEditor /> <CodeEditor />
</div> </div>
{consoleOpen && (
<>
<div
onMouseDown={handleBottomPanelResizeMouseDown}
style={resizeHandleStyle}
title="Drag to resize"
/>
<div style={{ height: bottomPanelHeight, flexShrink: 0 }}>
<CompilationConsole
isOpen={consoleOpen}
onClose={() => setConsoleOpen(false)}
logs={compileLogs}
onClear={() => setCompileLogs([])}
/>
</div>
</>
)}
</div> </div>
{/* Resize handle */} {/* Resize handle */}
@ -103,13 +163,20 @@ export const EditorPage: React.FC = () => {
</div> </div>
<div className="simulator-panel" style={{ width: `${100 - editorWidthPct}%`, display: 'flex', flexDirection: 'column' }}> <div className="simulator-panel" style={{ width: `${100 - editorWidthPct}%`, display: 'flex', flexDirection: 'column' }}>
<div style={{ flex: serialMonitorOpen ? `0 0 ${100 - serialHeightPct}%` : '1 1 auto', overflow: 'hidden', position: 'relative' }}> <div style={{ flex: 1, overflow: 'hidden', position: 'relative', minHeight: 0 }}>
<SimulatorCanvas /> <SimulatorCanvas />
</div> </div>
{serialMonitorOpen && ( {serialMonitorOpen && (
<div style={{ flex: `0 0 ${serialHeightPct}%`, minHeight: 100, display: 'flex', flexDirection: 'column' }}> <>
<div
onMouseDown={handleBottomPanelResizeMouseDown}
style={resizeHandleStyle}
title="Drag to resize"
/>
<div style={{ height: bottomPanelHeight, flexShrink: 0 }}>
<SerialMonitor /> <SerialMonitor />
</div> </div>
</>
)} )}
</div> </div>
</div> </div>

View File

@ -47,6 +47,8 @@ export class AVRSimulator {
/** Serial output buffer — subscribers receive each byte or line */ /** Serial output buffer — subscribers receive each byte or line */
public onSerialData: ((char: string) => void) | null = null; public onSerialData: ((char: string) => void) | null = null;
/** Fires whenever the sketch changes Serial baud rate (Serial.begin) */
public onBaudRateChange: ((baudRate: number) => void) | null = null;
private lastPortBValue = 0; private lastPortBValue = 0;
private lastPortCValue = 0; private lastPortCValue = 0;
private lastPortDValue = 0; private lastPortDValue = 0;
@ -94,6 +96,11 @@ export class AVRSimulator {
this.onSerialData(String.fromCharCode(value)); this.onSerialData(String.fromCharCode(value));
} }
}; };
this.usart.onConfigurationChange = () => {
if (this.onBaudRateChange && this.usart) {
this.onBaudRateChange(this.usart.baudRate);
}
};
// TWI (I2C) // TWI (I2C)
this.twi = new AVRTWI(this.cpu, twiConfig, 16000000); this.twi = new AVRTWI(this.cpu, twiConfig, 16000000);
@ -262,6 +269,11 @@ export class AVRSimulator {
this.usart.onByteTransmit = (value: number) => { this.usart.onByteTransmit = (value: number) => {
if (this.onSerialData) this.onSerialData(String.fromCharCode(value)); if (this.onSerialData) this.onSerialData(String.fromCharCode(value));
}; };
this.usart.onConfigurationChange = () => {
if (this.onBaudRateChange && this.usart) {
this.onBaudRateChange(this.usart.baudRate);
}
};
this.twi = new AVRTWI(this.cpu, twiConfig, 16000000); this.twi = new AVRTWI(this.cpu, twiConfig, 16000000);
this.i2cBus = new I2CBusManager(this.twi); this.i2cBus = new I2CBusManager(this.twi);

View File

@ -115,9 +115,16 @@ export class RP2040Simulator {
this.rp2040.core.PC = 0x10000000; this.rp2040.core.PC = 0x10000000;
// ── Wire UART0 (default Serial port for Arduino-Pico) ──────────── // ── Wire UART0 (default Serial port for Arduino-Pico) ────────────
let serialBuffer = '';
this.rp2040.uart[0].onByte = (value: number) => { this.rp2040.uart[0].onByte = (value: number) => {
const ch = String.fromCharCode(value);
serialBuffer += ch;
if (ch === '\n') {
console.log('[RP2040 UART0]', serialBuffer.trimEnd());
serialBuffer = '';
}
if (this.onSerialData) { if (this.onSerialData) {
this.onSerialData(String.fromCharCode(value)); this.onSerialData(ch);
} }
}; };

View File

@ -22,9 +22,11 @@ function setAdcVoltage(simulator: AnySimulator, pin: number, voltage: number): b
const channel = pin - 26; const channel = pin - 26;
// RP2040 ADC: 12-bit, 3.3V reference // RP2040 ADC: 12-bit, 3.3V reference
const adcValue = Math.round((voltage / 3.3) * 4095); const adcValue = Math.round((voltage / 3.3) * 4095);
console.log(`[setAdcVoltage] RP2040 ch${channel} = ${adcValue} (${voltage.toFixed(3)}V)`);
simulator.setADCValue(channel, adcValue); simulator.setADCValue(channel, adcValue);
return true; return true;
} }
console.warn(`[setAdcVoltage] RP2040 pin ${pin} is not an ADC pin (26-29)`);
return false; return false;
} }
// AVR: pins 14-19 → ADC channels 0-5 // AVR: pins 14-19 → ADC channels 0-5
@ -94,15 +96,21 @@ PartSimulationRegistry.register('rgb-led', {
PartSimulationRegistry.register('potentiometer', { PartSimulationRegistry.register('potentiometer', {
attachEvents: (element, simulator, getArduinoPinHelper) => { attachEvents: (element, simulator, getArduinoPinHelper) => {
const pin = getArduinoPinHelper('SIG'); const pin = getArduinoPinHelper('SIG');
if (pin === null) return () => { }; console.log(`[Potentiometer] attachEvents called, SIG pin resolved to: ${pin}`);
if (pin === null) {
console.warn('[Potentiometer] No SIG pin found — skipping ADC attachment');
return () => { };
}
// Determine reference voltage based on board type // Determine reference voltage based on board type
const isRP2040 = simulator instanceof RP2040Simulator; const isRP2040 = simulator instanceof RP2040Simulator;
const refVoltage = isRP2040 ? 3.3 : 5.0; const refVoltage = isRP2040 ? 3.3 : 5.0;
console.log(`[Potentiometer] Board type: ${isRP2040 ? 'RP2040' : 'AVR'}, refV: ${refVoltage}`);
const onInput = () => { const onInput = () => {
const raw = parseInt((element as any).value || '0', 10); const raw = parseInt((element as any).value || '0', 10);
const volts = (raw / 1023.0) * refVoltage; const volts = (raw / 1023.0) * refVoltage;
console.log(`[Potentiometer] pin=${pin}, raw=${raw}, volts=${volts.toFixed(3)}`);
if (!setAdcVoltage(simulator, pin, volts)) { if (!setAdcVoltage(simulator, pin, volts)) {
console.warn(`[Potentiometer] ADC not available for pin ${pin}`); console.warn(`[Potentiometer] ADC not available for pin ${pin}`);
} }

View File

@ -51,6 +51,7 @@ interface SimulatorState {
// Serial monitor state // Serial monitor state
serialOutput: string; serialOutput: string;
serialBaudRate: number;
serialMonitorOpen: boolean; serialMonitorOpen: boolean;
// Actions // Actions
@ -164,6 +165,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
selectedWireId: null, selectedWireId: null,
wireInProgress: null, wireInProgress: null,
serialOutput: '', serialOutput: '',
serialBaudRate: 0,
serialMonitorOpen: false, serialMonitorOpen: false,
setBoardType: (type: BoardType) => { setBoardType: (type: BoardType) => {
@ -178,7 +180,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
simulator.onSerialData = (char: string) => { simulator.onSerialData = (char: string) => {
set((s) => ({ serialOutput: s.serialOutput + char })); set((s) => ({ serialOutput: s.serialOutput + char }));
}; };
set({ boardType: type, simulator, compiledHex: null, serialOutput: '' }); if (simulator instanceof AVRSimulator) {
simulator.onBaudRateChange = (baudRate: number) => set({ serialBaudRate: baudRate });
}
set({ boardType: type, simulator, compiledHex: null, serialOutput: '', serialBaudRate: 0 });
console.log(`Board switched to: ${type}`); console.log(`Board switched to: ${type}`);
}, },
@ -191,7 +196,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
simulator.onSerialData = (char: string) => { simulator.onSerialData = (char: string) => {
set((s) => ({ serialOutput: s.serialOutput + char })); set((s) => ({ serialOutput: s.serialOutput + char }));
}; };
set({ simulator, serialOutput: '' }); if (simulator instanceof AVRSimulator) {
simulator.onBaudRateChange = (baudRate: number) => set({ serialBaudRate: baudRate });
}
set({ simulator, serialOutput: '', serialBaudRate: 0 });
console.log(`Simulator initialized: ${boardType}`); console.log(`Simulator initialized: ${boardType}`);
}, },
@ -239,7 +247,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
simulator.addI2CDevice(new I2CMemoryDevice(0x50) as RP2040I2CDevice); simulator.addI2CDevice(new I2CMemoryDevice(0x50) as RP2040I2CDevice);
} }
simulator.start(); simulator.start();
set({ running: true }); set({ running: true, serialMonitorOpen: true });
} }
}, },
@ -259,7 +267,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
simulator.onSerialData = (char: string) => { simulator.onSerialData = (char: string) => {
set((s) => ({ serialOutput: s.serialOutput + char })); set((s) => ({ serialOutput: s.serialOutput + char }));
}; };
set({ running: false, serialOutput: '' }); if (simulator instanceof AVRSimulator) {
simulator.onBaudRateChange = (baudRate: number) => set({ serialBaudRate: baudRate });
}
set({ running: false, serialOutput: '', serialBaudRate: 0 });
} }
}, },