feat: Add Arduino Nano support to simulator and update related components
parent
c7f9d6d029
commit
41dfd20583
|
|
@ -0,0 +1,36 @@
|
|||
import '@wokwi/elements';
|
||||
import { useRef, useEffect } from 'react';
|
||||
|
||||
interface ArduinoNanoProps {
|
||||
id?: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
led13?: boolean;
|
||||
}
|
||||
|
||||
export const ArduinoNano = ({
|
||||
id = 'arduino-nano',
|
||||
x = 0,
|
||||
y = 0,
|
||||
led13 = false,
|
||||
}: ArduinoNanoProps) => {
|
||||
const nanoRef = useRef<HTMLElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (nanoRef.current) {
|
||||
(nanoRef.current as any).led13 = led13;
|
||||
}
|
||||
}, [led13]);
|
||||
|
||||
return (
|
||||
<wokwi-arduino-nano
|
||||
id={id}
|
||||
ref={nanoRef}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: `${x}px`,
|
||||
top: `${y}px`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@ import { useSimulatorStore, BOARD_LABELS } from '../../store/useSimulatorStore';
|
|||
import type { BoardType } from '../../store/useSimulatorStore';
|
||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import { ArduinoUno } from '../components-wokwi/ArduinoUno';
|
||||
import { ArduinoNano } from '../components-wokwi/ArduinoNano';
|
||||
import { NanoRP2040 } from '../components-wokwi/NanoRP2040';
|
||||
import { ComponentPickerModal } from '../ComponentPickerModal';
|
||||
import { ComponentPropertyDialog } from './ComponentPropertyDialog';
|
||||
|
|
@ -587,6 +588,12 @@ export const SimulatorCanvas = () => {
|
|||
y={boardPosition.y}
|
||||
led13={Boolean(components.find((c) => c.id === 'led-builtin')?.properties.state)}
|
||||
/>
|
||||
) : boardType === 'arduino-nano' ? (
|
||||
<ArduinoNano
|
||||
x={boardPosition.x}
|
||||
y={boardPosition.y}
|
||||
led13={Boolean(components.find((c) => c.id === 'led-builtin')?.properties.state)}
|
||||
/>
|
||||
) : (
|
||||
<NanoRP2040
|
||||
x={boardPosition.x}
|
||||
|
|
@ -602,8 +609,8 @@ export const SimulatorCanvas = () => {
|
|||
position: 'absolute',
|
||||
left: boardPosition.x,
|
||||
top: boardPosition.y,
|
||||
width: boardType === 'arduino-uno' ? 360 : 280,
|
||||
height: boardType === 'arduino-uno' ? 250 : 180,
|
||||
width: boardType === 'arduino-uno' ? 360 : boardType === 'arduino-nano' ? 175 : 280,
|
||||
height: boardType === 'arduino-uno' ? 250 : boardType === 'arduino-nano' ? 70 : 180,
|
||||
cursor: 'move',
|
||||
zIndex: 1,
|
||||
}}
|
||||
|
|
@ -621,7 +628,7 @@ export const SimulatorCanvas = () => {
|
|||
|
||||
{/* Board pin overlay */}
|
||||
<PinOverlay
|
||||
componentId={boardType === 'arduino-uno' ? 'arduino-uno' : 'nano-rp2040'}
|
||||
componentId={boardType === 'arduino-uno' ? 'arduino-uno' : boardType === 'arduino-nano' ? 'arduino-nano' : 'nano-rp2040'}
|
||||
componentX={boardPosition.x}
|
||||
componentY={boardPosition.y}
|
||||
onPinClick={handlePinClick}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export interface ExampleProject {
|
|||
category: 'basics' | 'sensors' | 'displays' | 'communication' | 'games' | 'robotics';
|
||||
difficulty: 'beginner' | 'intermediate' | 'advanced';
|
||||
/** Target board — defaults to 'arduino-uno' if omitted */
|
||||
boardType?: 'arduino-uno' | 'raspberry-pi-pico';
|
||||
boardType?: 'arduino-uno' | 'arduino-nano' | 'raspberry-pi-pico';
|
||||
code: string;
|
||||
components: Array<{
|
||||
type: string;
|
||||
|
|
|
|||
|
|
@ -7,15 +7,17 @@ import type { RP2040I2CDevice } from '../simulation/RP2040Simulator';
|
|||
import type { Wire, WireInProgress, WireEndpoint } from '../types/wire';
|
||||
import { calculatePinPosition } from '../utils/pinPositionCalculator';
|
||||
|
||||
export type BoardType = 'arduino-uno' | 'raspberry-pi-pico';
|
||||
export type BoardType = 'arduino-uno' | 'arduino-nano' | 'raspberry-pi-pico';
|
||||
|
||||
export const BOARD_FQBN: Record<BoardType, string> = {
|
||||
'arduino-uno': 'arduino:avr:uno',
|
||||
'arduino-nano': 'arduino:avr:nano:cpu=atmega328',
|
||||
'raspberry-pi-pico': 'rp2040:rp2040:rpipico',
|
||||
};
|
||||
|
||||
export const BOARD_LABELS: Record<BoardType, string> = {
|
||||
'arduino-uno': 'Arduino Uno',
|
||||
'arduino-nano': 'Arduino Nano',
|
||||
'raspberry-pi-pico': 'Raspberry Pi Pico',
|
||||
};
|
||||
|
||||
|
|
@ -184,7 +186,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
if (running) {
|
||||
get().stopSimulation();
|
||||
}
|
||||
const simulator = type === 'arduino-uno'
|
||||
const simulator = (type === 'arduino-uno' || type === 'arduino-nano')
|
||||
? new AVRSimulator(pinManager)
|
||||
: new RP2040Simulator(pinManager);
|
||||
// Wire serial output callback for both simulator types
|
||||
|
|
@ -200,7 +202,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
|
||||
initSimulator: () => {
|
||||
const { boardType } = get();
|
||||
const simulator = boardType === 'arduino-uno'
|
||||
const simulator = (boardType === 'arduino-uno' || boardType === 'arduino-nano')
|
||||
? new AVRSimulator(pinManager)
|
||||
: new RP2040Simulator(pinManager);
|
||||
// Wire serial output callback for both simulator types
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ const ARDUINO_UNO_ANALOG_MAP: Record<string, number> = {
|
|||
};
|
||||
|
||||
/** All known board component IDs in the simulator */
|
||||
export const BOARD_COMPONENT_IDS = ['arduino-uno', 'nano-rp2040'];
|
||||
export const BOARD_COMPONENT_IDS = ['arduino-uno', 'arduino-nano', 'nano-rp2040'];
|
||||
|
||||
/**
|
||||
* Check whether a componentId represents a board (not an external component).
|
||||
|
|
@ -69,7 +69,7 @@ export function isBoardComponent(componentId: string): boolean {
|
|||
* @returns Numeric pin/GPIO number, or null if unmapped
|
||||
*/
|
||||
export function boardPinToNumber(boardId: string, pinName: string): number | null {
|
||||
if (boardId === 'arduino-uno') {
|
||||
if (boardId === 'arduino-uno' || boardId === 'arduino-nano') {
|
||||
// Try numeric (covers '0' through '13', also legacy examples using just numbers)
|
||||
const num = parseInt(pinName, 10);
|
||||
if (!isNaN(num) && num >= 0 && num <= 21) return num;
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export interface VelxioComponent {
|
|||
}
|
||||
|
||||
export interface ImportResult {
|
||||
boardType: 'arduino-uno' | 'raspberry-pi-pico';
|
||||
boardType: 'arduino-uno' | 'arduino-nano' | 'raspberry-pi-pico';
|
||||
boardPosition: { x: number; y: number };
|
||||
components: VelxioComponent[];
|
||||
wires: Wire[];
|
||||
|
|
@ -53,9 +53,9 @@ export interface ImportResult {
|
|||
// ── Board mappings ────────────────────────────────────────────────────────────
|
||||
|
||||
// Wokwi board type → Velxio boardType
|
||||
const WOKWI_TYPE_TO_BOARD: Record<string, 'arduino-uno' | 'raspberry-pi-pico'> = {
|
||||
const WOKWI_TYPE_TO_BOARD: Record<string, 'arduino-uno' | 'arduino-nano' | 'raspberry-pi-pico'> = {
|
||||
'wokwi-arduino-uno': 'arduino-uno',
|
||||
'wokwi-arduino-nano': 'arduino-uno',
|
||||
'wokwi-arduino-nano': 'arduino-nano',
|
||||
'wokwi-arduino-mega': 'arduino-uno',
|
||||
'wokwi-raspberry-pi-pico': 'raspberry-pi-pico',
|
||||
};
|
||||
|
|
@ -63,12 +63,14 @@ const WOKWI_TYPE_TO_BOARD: Record<string, 'arduino-uno' | 'raspberry-pi-pico'> =
|
|||
// Velxio boardType → Wokwi type
|
||||
const BOARD_TO_WOKWI_TYPE: Record<string, string> = {
|
||||
'arduino-uno': 'wokwi-arduino-uno',
|
||||
'arduino-nano': 'wokwi-arduino-nano',
|
||||
'raspberry-pi-pico': 'wokwi-raspberry-pi-pico',
|
||||
};
|
||||
|
||||
// Velxio boardType → default Wokwi part id
|
||||
const BOARD_TO_WOKWI_ID: Record<string, string> = {
|
||||
'arduino-uno': 'uno',
|
||||
'arduino-nano': 'nano',
|
||||
'raspberry-pi-pico': 'pico',
|
||||
};
|
||||
|
||||
|
|
@ -140,8 +142,10 @@ export async function exportToWokwiZip(
|
|||
|
||||
// Build connections
|
||||
const connections: [string, string, string, string[]][] = wires.map((w) => {
|
||||
const startId = w.start.componentId === 'arduino-uno' ? boardId : w.start.componentId;
|
||||
const endId = w.end.componentId === 'arduino-uno' ? boardId : w.end.componentId;
|
||||
const isBoardStart = w.start.componentId === 'arduino-uno' || w.start.componentId === 'arduino-nano' || w.start.componentId === 'nano-rp2040';
|
||||
const isBoardEnd = w.end.componentId === 'arduino-uno' || w.end.componentId === 'arduino-nano' || w.end.componentId === 'nano-rp2040';
|
||||
const startId = isBoardStart ? boardId : w.start.componentId;
|
||||
const endId = isBoardEnd ? boardId : w.end.componentId;
|
||||
return [
|
||||
`${startId}:${w.start.pinName}`,
|
||||
`${endId}:${w.end.pinName}`,
|
||||
|
|
@ -193,6 +197,14 @@ export async function importFromWokwiZip(file: File): Promise<ImportResult> {
|
|||
const boardType = boardPart ? WOKWI_TYPE_TO_BOARD[boardPart.type] : 'arduino-uno';
|
||||
const boardId = boardPart?.id ?? 'uno';
|
||||
|
||||
// Velxio internal component ID for the board element (must match DOM element id)
|
||||
const VELXIO_BOARD_ID: Record<string, string> = {
|
||||
'arduino-uno': 'arduino-uno',
|
||||
'arduino-nano': 'arduino-nano',
|
||||
'raspberry-pi-pico': 'nano-rp2040',
|
||||
};
|
||||
const velxioBoardId = VELXIO_BOARD_ID[boardType] ?? 'arduino-uno';
|
||||
|
||||
// Board position from diagram (use directly as Velxio board position)
|
||||
const boardPosition = {
|
||||
x: boardPart?.left ?? 50,
|
||||
|
|
@ -221,8 +233,8 @@ export async function importFromWokwiZip(file: File): Promise<ImportResult> {
|
|||
const endPin = colonB >= 0 ? endStr.slice(colonB + 1) : '';
|
||||
|
||||
// Remap board part id → Velxio internal board id
|
||||
const startId = startCompRaw === boardId ? 'arduino-uno' : startCompRaw;
|
||||
const endId = endCompRaw === boardId ? 'arduino-uno' : endCompRaw;
|
||||
const startId = startCompRaw === boardId ? velxioBoardId : startCompRaw;
|
||||
const endId = endCompRaw === boardId ? velxioBoardId : endCompRaw;
|
||||
|
||||
return {
|
||||
id: `wire-${i}-${Date.now()}`,
|
||||
|
|
|
|||
Loading…
Reference in New Issue