diff --git a/.gitignore b/.gitignore index 55deb02..bd70eef 100644 --- a/.gitignore +++ b/.gitignore @@ -93,8 +93,16 @@ backend/app/services/esp32c3-rom.bin test/esp32-emulator/**/*.elf test/esp32-emulator/**/*.map test/esp32-emulator/out_*/ + +# Arduino-cli compilation by-products (not needed for tests) +**/*.ino.eep +**/*.ino.with_bootloader.bin +**/*.ino.with_bootloader.hex +**/*.ino.map +**/*.ino.uf2 .claude/settings.json # Google Cloud service account credentials velxio-ba3355a41944.json -marketing/* \ No newline at end of file +marketing/* +docs/github-issues/* \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3f0e337..3686644 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,7 +15,9 @@ "@xterm/xterm": "^6.0.0", "avr8js": "file:../wokwi-libs/avr8js", "axios": "^1.13.6", + "idb-keyval": "^6.2.2", "jszip": "^3.10.1", + "littlefs": "^0.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router-dom": "^7.13.1", @@ -3669,6 +3671,12 @@ "node": ">= 14" } }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "license": "Apache-2.0" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3997,6 +4005,12 @@ "immediate": "~3.0.5" } }, + "node_modules/littlefs": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/littlefs/-/littlefs-0.1.0.tgz", + "integrity": "sha512-mNwh4CHkuw83HCIAxzNE3KLV59A5IKkxiFbUUCPkA8gIZ9HeAOovFbrfdpAgVwLAX1tJrM5jP/I1cGPSFgBNZw==", + "license": "ISC" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 3997974..52ea80e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,7 +27,9 @@ "@xterm/xterm": "^6.0.0", "avr8js": "file:../wokwi-libs/avr8js", "axios": "^1.13.6", + "idb-keyval": "^6.2.2", "jszip": "^3.10.1", + "littlefs": "^0.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router-dom": "^7.13.1", diff --git a/frontend/public/firmware/micropython-rp2040.uf2 b/frontend/public/firmware/micropython-rp2040.uf2 new file mode 100644 index 0000000..14880cb Binary files /dev/null and b/frontend/public/firmware/micropython-rp2040.uf2 differ diff --git a/frontend/src/components/editor/EditorToolbar.tsx b/frontend/src/components/editor/EditorToolbar.tsx index 07cbb6a..8e35fd9 100644 --- a/frontend/src/components/editor/EditorToolbar.tsx +++ b/frontend/src/components/editor/EditorToolbar.tsx @@ -1,8 +1,8 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import { useEditorStore } from '../../store/useEditorStore'; import { useSimulatorStore } from '../../store/useSimulatorStore'; -import type { BoardKind } from '../../types/board'; -import { BOARD_KIND_FQBN, BOARD_KIND_LABELS } from '../../types/board'; +import type { BoardKind, LanguageMode } from '../../types/board'; +import { BOARD_KIND_FQBN, BOARD_KIND_LABELS, BOARD_SUPPORTS_MICROPYTHON } from '../../types/board'; import { compileCode } from '../../services/compilation'; import { CompileAllProgress } from './CompileAllProgress'; import type { BoardCompileStatus } from './CompileAllProgress'; @@ -49,6 +49,8 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi boards, activeBoardId, compileBoardProgram, + loadMicroPythonProgram, + setBoardLanguageMode, startBoard, stopBoard, resetBoard, @@ -112,6 +114,25 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi return; } + // MicroPython mode — no backend compilation needed + if (activeBoard?.languageMode === 'micropython' && activeBoardId) { + addLog({ timestamp: new Date(), type: 'info', message: 'MicroPython: loading firmware and user files...' }); + try { + const groupFiles = useEditorStore.getState().getGroupFiles(activeBoard.activeFileGroupId); + const pyFiles = groupFiles.map((f) => ({ name: f.name, content: f.content })); + await loadMicroPythonProgram(activeBoardId, pyFiles); + addLog({ timestamp: new Date(), type: 'success', message: 'MicroPython firmware loaded successfully' }); + setMessage({ type: 'success', text: 'MicroPython ready' }); + } catch (err) { + const errMsg = err instanceof Error ? err.message : 'Failed to load MicroPython'; + addLog({ timestamp: new Date(), type: 'error', message: errMsg }); + setMessage({ type: 'error', text: errMsg }); + } finally { + setCompiling(false); + } + return; + } + const fqbn = kind ? BOARD_KIND_FQBN[kind] : null; const boardLabel = kind ? BOARD_KIND_LABELS[kind] : 'Unknown'; @@ -155,9 +176,37 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi } }; - const handleRun = () => { + const handleRun = async () => { if (activeBoardId) { const board = boards.find((b) => b.id === activeBoardId); + + // MicroPython mode: auto-load firmware + files, then start + if (board?.languageMode === 'micropython') { + trackRunSimulation(board.boardKind); + if (!board.compiledProgram) { + // Need to load MicroPython first + setCompiling(true); + setMessage(null); + addLog({ timestamp: new Date(), type: 'info', message: 'MicroPython: loading firmware and user files...' }); + try { + const groupFiles = useEditorStore.getState().getGroupFiles(board.activeFileGroupId); + const pyFiles = groupFiles.map((f) => ({ name: f.name, content: f.content })); + await loadMicroPythonProgram(activeBoardId, pyFiles); + addLog({ timestamp: new Date(), type: 'success', message: 'MicroPython firmware loaded' }); + } catch (err) { + const errMsg = err instanceof Error ? err.message : 'Failed to load MicroPython'; + addLog({ timestamp: new Date(), type: 'error', message: errMsg }); + setMessage({ type: 'error', text: errMsg }); + setCompiling(false); + return; + } + setCompiling(false); + } + startBoard(activeBoardId); + setMessage(null); + return; + } + const isQemuBoard = board?.boardKind === 'raspberry-pi-3' || board?.boardKind === 'esp32' || board?.boardKind === 'esp32-s3'; if (isQemuBoard || board?.compiledProgram) { trackRunSimulation(board?.boardKind); @@ -302,14 +351,39 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi