# Proposal: Integrasi AVR8js (Simulator Mikrokontroler AVR) ke Elemes LMS ## Pendahuluan Dokumen ini merupakan proposal untuk mengintegrasikan **[avr8js](https://github.com/wokwi/avr8js)** — sebuah simulator mikrokontroler AVR 8-bit berbasis JavaScript — ke dalam ekosistem Elemes LMS. Referensi implementasi diambil dari **[Falstad Circuit Simulator + AVR8js](https://www.falstad.com/circuit/avr8js/index.html)**. Tujuannya: siswa dapat menulis kode C/Arduino, meng-compile, dan mensimulasikan eksekusi pada mikrokontroler virtual — lengkap dengan koneksi ke simulator rangkaian CircuitJS1 yang sudah ada. --- ## 1. Apakah Memerlukan Compile Custom? **Ya, memerlukan kompilasi khusus.** AVR8js adalah *execution engine* saja — ia menjalankan machine code AVR yang sudah dikompilasi (format Intel HEX). Library ini **tidak menyertakan compiler**. Oleh karena itu: | Aspek | Penjelasan | |-------|-----------| | **Compiler yang dibutuhkan** | `avr-gcc` (toolchain AVR), bukan `gcc` biasa | | **Solusi yang dipilih** | **PlatformIO** — mengelola `avr-gcc` + Arduino core secara otomatis | | **Lokasi kompilasi** | Backend (Flask) — sama seperti alur compile C yang sudah ada | | **Output kompilasi** | Intel HEX string, dikirim ke frontend via JSON response | | **Eksekusi** | Frontend (browser) — avr8js menjalankan HEX di browser | ### Alur Kerja ``` ┌──────────────┐ POST /api/compile ┌──────────────────┐ │ Code Editor │ ──── {code, language} ────→ │ Flask Backend │ │ (SvelteKit) │ │ + PlatformIO │ └──────────────┘ └────────┬─────────┘ ↑ │ │ {success, hex: "..."} │ └──────────────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────────────────┐ │ Browser: avr8js │ │ ┌─────────┐ USART ┌───────────────┐ │ │ │ CPU │ ──────→ │ Output Panel │ (Serial) │ │ │ Timer │ │ (Console) │ │ │ │ GPIO │ ──────→ │ CircuitJS1 │ (LED, dll) │ │ └─────────┘ └───────────────┘ │ └──────────────────────────────────────────────────────┘ ``` --- ## 2. Dua Mode Bahasa: C Standar (avr-libc) & Arduino C++ Sistem mendukung dua mode pemrograman, ditentukan per-lesson melalui `active_tabs`: ### a. Mode AVR-C (avr-libc) - Tag: `active_tabs` mengandung `'avr'` - Sintaks: `#include `, manipulasi register langsung (`PORTB`, `DDRB`, dll.) - PlatformIO framework: `none` (bare-metal avr-libc) - Cocok untuk: pelajaran arsitektur mikrokontroler, register-level programming ```c #include #include int main(void) { DDRB |= (1 << PB5); // Pin 13 sebagai output while (1) { PORTB ^= (1 << PB5); // Toggle LED _delay_ms(500); } } ``` ### b. Mode Arduino C++ - Tag: `active_tabs` mengandung `'arduino'` - Sintaks: `setup()`, `loop()`, `digitalWrite()`, `Serial.println()`, dll. - PlatformIO framework: `arduino` (include Arduino core library) - Cocok untuk: pelajaran pemula, rapid prototyping ```cpp void setup() { pinMode(13, OUTPUT); Serial.begin(9600); } void loop() { digitalWrite(13, HIGH); Serial.println("LED ON"); delay(500); digitalWrite(13, LOW); Serial.println("LED OFF"); delay(500); } ``` --- ## 3. Implementasi dengan Sistem Tab yang Sudah Ada Sistem tab Elemes saat ini (`info | exercise | editor | circuit | output`) dimanfaatkan sepenuhnya: | Tab | Fungsi untuk AVR | |-----|------------------| | **Editor** | Menulis kode AVR-C atau Arduino C++. CodeMirror 6 sudah support `lang-cpp`. | | **Circuit** | CircuitJS1 iframe — rangkaian terhubung ke pin AVR via bridge. | | **Output** | Serial console — menampilkan output USART dari avr8js (seperti `Serial.println()`). | ### Integrasi Tab di `active_tabs` Lesson markdown menentukan mode via tag konten: - `---INITIAL_CODE_AVR---` → aktifkan tab editor mode AVR-C - `---INITIAL_CODE_ARDUINO---` → aktifkan tab editor mode Arduino - `---INITIAL_CIRCUIT---` → aktifkan tab circuit (sudah ada) - Keduanya bisa hadir bersamaan (hybrid lesson) ### Flow Tombol "Run" untuk Mode AVR/Arduino 1. Ambil kode dari CodeEditor 2. POST ke `/api/compile` dengan `language: 'avr'` atau `'arduino'` 3. Backend compile via PlatformIO → return Intel HEX 4. Frontend parse HEX → load ke avr8js CPU memory 5. Jalankan simulation loop (60fps via requestAnimationFrame) 6. USART output → tampilkan di Output Panel sebagai serial console 7. GPIO changes → update CircuitJS1 via bridge ### Tombol Kontrol Tambahan - **Run**: Compile + Start simulation - **Stop**: Hentikan simulation loop - **Reset**: Stop + reset CPU state + clear serial output --- ## 4. Backend: PlatformIO sebagai Compiler Universal ### Arsitektur Compiler Baru File: `elemes/compiler/avr_compiler.py` ``` BaseCompiler (base_compiler.py) ├── CCompiler (c_compiler.py) ← sudah ada ├── PythonCompiler (python_compiler.py) ← sudah ada └── AVRCompiler (avr_compiler.py) ← BARU ├── mode 'avr' → PlatformIO framework=none └── mode 'arduino' → PlatformIO framework=arduino ``` ### Cara Kerja AVRCompiler 1. Buat temporary PlatformIO project directory 2. Generate `platformio.ini`: ```ini [env:uno] platform = atmelavr board = uno framework = arduino ; atau kosong untuk avr-libc ``` 3. Tulis source code ke `src/main.cpp` (Arduino) atau `src/main.c` (AVR-C) 4. Jalankan `pio run` → compile 5. Baca output `.hex` dari `.pio/build/uno/firmware.hex` 6. Return HEX string sebagai bagian dari JSON response: ```json { "success": true, "hex": ":100000000C9462000C947E00...", "output": "Compilation successful" } ``` 7. Cleanup temporary directory ### Response Format Baru ```typescript interface CompileResponse { success: boolean; output: string; // stdout/stderr dari compiler error: string; // error message jika gagal hex?: string; // Intel HEX (hanya untuk avr/arduino) } ``` ### Perubahan Docker ```dockerfile # Tambahan di elemes/Dockerfile RUN pip install platformio # Pre-cache PlatformIO packages (agar first compile cepat) RUN mkdir -p /tmp/pio-warmup/src && \ echo '[env:uno]\nplatform = atmelavr\nboard = uno\nframework = arduino' \ > /tmp/pio-warmup/platformio.ini && \ echo 'void setup(){} void loop(){}' > /tmp/pio-warmup/src/main.cpp && \ cd /tmp/pio-warmup && pio run && \ rm -rf /tmp/pio-warmup ``` --- ## 5. Frontend: AVR8js Simulation Engine ### Komponen Baru: `avr-simulator.ts` File: `elemes/frontend/src/lib/services/avr-simulator.ts` **Tanggung jawab:** - Parse Intel HEX → `Uint16Array` (program memory 32KB) - Inisialisasi avr8js CPU, Timer, USART - Simulation loop via `requestAnimationFrame` - Callback: `onSerialOutput(char)` — untuk serial console - Callback: `onPortWrite(port, value)` — untuk GPIO bridge **Intel HEX Parser:** Format Intel HEX per baris: `:LLAAAATT[DD...]CC` - `LL` = byte count - `AAAA` = address - `TT` = type (00=data, 01=EOF) - `DD` = data bytes - `CC` = checksum **Simulation Loop:** ``` requestAnimationFrame → run N instructions (16MHz / 60fps ≈ 266,666 cycles per frame) → tick timers → check USART output → repeat ``` **Pertimbangan performa:** - 266K cycles per frame cukup untuk real-time simulation - Jika perlu, bisa dipindah ke Web Worker untuk non-blocking UI - `delay()` di Arduino code berjalan secara natural karena timer simulation --- ## 6. Bridge: AVR8js ↔ CircuitJS1 ### Mekanisme Koneksi File: `elemes/frontend/src/lib/services/avr-circuit-bridge.ts` Bridge menghubungkan pin AVR dengan elemen CircuitJS1 menggunakan API yang sudah tersedia: | Arah | Mekanisme | |------|-----------| | **AVR → CircuitJS** | `cpu.writeHooks[PORTB]` mendeteksi GPIO write → `circuitApi.setExtVoltage(name, voltage)` | | **CircuitJS → AVR** | `circuitApi.ontimestep` callback → `circuitApi.getNodeVoltage(name)` → set AVR PIN register | ### Konfigurasi Pin Map (per lesson) Setiap lesson AVR+Circuit menyertakan JSON pin mapping di markdown: ``` ---AVR_PIN_MAP--- { "outputs": { "PB5": { "extVoltageName": "arduino_d13", "type": "digital" } }, "inputs": { "PC0": { "circuitNode": "sensor_out", "type": "analog" } } } ---END_AVR_PIN_MAP--- ``` **Cara kerja:** - **Output (AVR → Circuit):** Saat AVR menulis ke PORTB bit 5, bridge memanggil `setExtVoltage("arduino_d13", 5.0)` atau `setExtVoltage("arduino_d13", 0.0)`. - **Input (Circuit → AVR):** Setiap CircuitJS timestep, bridge membaca `getNodeVoltage("sensor_out")` dan set bit di register PIN AVR. ### Prasyarat untuk Lesson Author Rangkaian CircuitJS harus menyertakan elemen **External Voltage** (ExtVoltageElm) dengan nama yang sesuai pin map. Contoh: elemen bernama `arduino_d13` mewakili output digital pin 13 Arduino. --- ## 7. Format Lesson Markdown — Marker Baru ### Marker Baru untuk AVR | Marker | Fungsi | |--------|--------| | `---INITIAL_CODE_AVR---` | Kode awal mode AVR-C (avr-libc) | | `---INITIAL_CODE_ARDUINO---` | Kode awal mode Arduino C++ | | `---AVR_PIN_MAP---` | JSON konfigurasi pin mapping AVR ↔ CircuitJS | | `---EXPECTED_SERIAL_OUTPUT---` | Expected serial output untuk auto-grading | ### Deteksi `active_tabs` (Backward Compatible) Parser mendeteksi tag secara implisit (sama seperti sistem saat ini): - `---INITIAL_CODE_AVR---` ada → `active_tabs.append('avr')` - `---INITIAL_CODE_ARDUINO---` ada → `active_tabs.append('arduino')` - Tag lama (`---INITIAL_CODE---`, `---INITIAL_CIRCUIT---`, dll.) tetap berfungsi tanpa perubahan. ### Contoh Lesson: Blink LED (Arduino) ```markdown ---LESSON_INFO--- Pelajaran Arduino: LED Blink dengan Simulator AVR. - Memahami fungsi setup() dan loop() - Mengontrol LED menggunakan pin digital ---END_LESSON_INFO--- # Blink LED Program klasik Arduino: menyalakan dan mematikan LED bergantian. ---EXERCISE--- Buat program Arduino yang menyalakan LED selama 500ms dan mematikan selama 500ms. Serial monitor harus menampilkan "ON" dan "OFF". --- ---INITIAL_CODE_ARDUINO--- void setup() { // Inisialisasi pin 13 sebagai output // Inisialisasi Serial } void loop() { // Nyalakan LED, print "ON", delay 500ms // Matikan LED, print "OFF", delay 500ms } ---END_INITIAL_CODE_ARDUINO--- ---INITIAL_CIRCUIT--- $ 1 0.000005 ... v arduino_d13 ... r 220 ... 162 ... ---END_INITIAL_CIRCUIT--- ---EXPECTED_SERIAL_OUTPUT--- ON OFF ---END_EXPECTED_SERIAL_OUTPUT--- ---AVR_PIN_MAP--- { "outputs": { "PB5": { "extVoltageName": "arduino_d13", "type": "digital" } }, "inputs": {} } ---END_AVR_PIN_MAP--- ---KEY_TEXT--- Serial.println digitalWrite ---END_KEY_TEXT--- ---SOLUTION_CODE--- void setup() { pinMode(13, OUTPUT); Serial.begin(9600); } void loop() { digitalWrite(13, HIGH); Serial.println("ON"); delay(500); digitalWrite(13, LOW); Serial.println("OFF"); delay(500); } ---END_SOLUTION_CODE--- ``` --- ## 8. Evaluasi & Auto-Grading Evaluasi lesson AVR menggunakan kombinasi metode yang sudah ada: | Kriteria | Mekanisme | Existing? | |----------|-----------|-----------| | **Serial output** | Bandingkan USART output vs `expected_serial_output` | Adaptasi dari `expected_output` | | **Node voltages** | `getNodeVoltage()` vs `expected_circuit_output` JSON | Sudah ada | | **Key text** | Cek keyword wajib di source code | Sudah ada | | **Hybrid AND** | Semua kriteria harus lulus | Sudah ada | --- ## 9. Perubahan File — Ringkasan ### File Baru | File | Deskripsi | |------|-----------| | `elemes/compiler/avr_compiler.py` | PlatformIO compiler class | | `frontend/src/lib/services/avr-simulator.ts` | AVR8js simulation engine + HEX parser | | `frontend/src/lib/services/avr-circuit-bridge.ts` | Bridge GPIO ↔ CircuitJS1 | | `content/blink_led.md` | Contoh lesson Arduino | ### File yang Dimodifikasi | File | Perubahan | |------|-----------| | `elemes/compiler/__init__.py` | Register AVRCompiler | | `elemes/services/lesson_service.py` | Parse marker AVR baru | | `elemes/routes/lessons.py` | Tambah field AVR ke JSON response | | `elemes/Dockerfile` | Install PlatformIO + warmup cache | | `frontend/package.json` | Tambah dependency `avr8js` | | `frontend/src/lib/types/lesson.ts` | Tambah field AVR | | `frontend/src/lib/types/compiler.ts` | Tambah field `hex` | | `frontend/src/lib/types/circuitjs.ts` | Tambah `setExtVoltage`, `ontimestep`, dll | | `frontend/src/lib/components/WorkspaceHeader.svelte` | Tab AVR/Arduino | | `frontend/src/routes/lesson/[slug]/+page.svelte` | Wire compile→simulate→output→bridge | | `frontend/src/lib/components/OutputPanel.svelte` | Section serial console | --- ## 10. Potensi Tantangan & Mitigasi | Tantangan | Mitigasi | |-----------|---------| | PlatformIO cold start lambat di Docker | Pre-cache packages saat `docker build` (warmup step) | | avr8js tidak support semua peripheral | Untuk edukasi (LED, serial, digital I/O) sudah cukup. Dokumentasikan limitasi. | | Sinkronisasi timing AVR ↔ CircuitJS | Keduanya berjalan real-time di browser event loop — natural sync | | HEX file besar | Arduino sketch tipikal 5-50KB — cukup kecil untuk JSON response | | CircuitJS `setExtVoltage` butuh elemen ExtVoltage | Lesson author wajib menyertakan elemen ini di initial circuit. Buat template. | --- ## Kesimpulan Integrasi AVR8js ke Elemes LMS **sangat layak** karena: 1. **Infrastruktur sudah siap** — Tab system, CircuitJS1 iframe, output panel, dan compiler factory sudah ada. AVR8js hanya menambah layer baru di atas fondasi yang kokoh. 2. **PlatformIO menyederhanakan toolchain** — Tidak perlu install avr-gcc manual; PlatformIO mengelola semua dependency secara otomatis dan support dua mode (avr-libc dan Arduino). 3. **Backward compatible** — Semua lesson C/Python yang sudah ada tidak terpengaruh. Deteksi tag implisit memastikan kompatibilitas penuh. 4. **Simulasi penuh di browser** — Setelah compile di backend, eksekusi sepenuhnya di client. Tidak membebani server. 5. **Bridge CircuitJS1 sudah feasible** — API `setExtVoltage` dan `getNodeVoltage` yang sudah ada di CircuitJS1 memungkinkan koneksi dua arah antara kode AVR dan simulasi rangkaian.