feat: Integrate AVR8js microcontroller simulator into Elemes LMS with custom compilation and simulation workflow
parent
09827bf3ff
commit
5f57e67a25
468
proposal.md
468
proposal.md
|
|
@ -1,123 +1,427 @@
|
|||
# Proposal: Studi Kelayakan dan Perencanaan Implementasi CircuitJS1 di Elemes LMS
|
||||
# Proposal: Integrasi AVR8js (Simulator Mikrokontroler AVR) ke Elemes LMS
|
||||
|
||||
## Pendahuluan
|
||||
Dokumen ini merupakan proposal dan studi kelayakan untuk mengintegrasikan **[CircuitJS1](https://github.com/pfalstad/circuitjs1)** (sebuah simulator rangkaian elektronik berbasis web) ke dalam ekosistem Elemes LMS. Tujuannya adalah untuk mentransisikan atau menambahkan kapabilitas LMS yang saat ini fokus pada pemrograman (Code Editor) menjadi platform pembelajaran rangkaian elektronik yang interaktif.
|
||||
|
||||
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. Cara Implementasi dengan Sistem Elemes yang Sekarang
|
||||
## 1. Apakah Memerlukan Compile Custom?
|
||||
|
||||
Sistem Elemes saat ini terdiri dari Backend (Flask) dan Frontend (SvelteKit). Implementasi CircuitJS1 dapat dilakukan dengan mulus karena CircuitJS1 berjalan sepenuhnya di sisi klien (browser).
|
||||
**Ya, memerlukan kompilasi khusus.**
|
||||
|
||||
### a. Integrasi Frontend (SvelteKit)
|
||||
- **Komponen Baru (`CircuitEditor.svelte`):** Komponen `CodeEditor.svelte` (berbasis CodeMirror) akan diganti atau didampingi dengan komponen baru yang memuat CircuitJS1.
|
||||
- **Metode Embed:** CircuitJS1 dapat diintegrasikan menggunakan elemen `<iframe>` (dengan file HTML bawaan CircuitJS) yang disematkan di dalam halaman `/lesson/[slug]`. Alternatif lainnya adalah me-load file JavaScript CircuitJS secara langsung ke dalam DOM container SvelteKit.
|
||||
- **Manajemen State (Auto-save):** CircuitJS1 memiliki fitur *Export/Import as Text*. Teks representasi rangkaian ini akan diperlakukan sama seperti "kode sumber" pada sistem saat ini. Teks tersebut dapat disimpan ke dalam `sessionStorage` (untuk auto-save) dan di-load kembali saat halaman di-refresh.
|
||||
AVR8js adalah *execution engine* saja — ia menjalankan machine code AVR yang sudah dikompilasi (format Intel HEX). Library ini **tidak menyertakan compiler**. Oleh karena itu:
|
||||
|
||||
### b. Peran Backend (Flask)
|
||||
- Karena kompilasi dan simulasi berjalan di browser (menggunakan JavaScript/HTML5), fungsi kompilasi di backend (`/api/compile` dan *compiler factory*) tidak lagi diperlukan untuk pelajaran elektronik.
|
||||
- Backend hanya perlu fokus pada *API proxy*, autentikasi token (`/api/login`), penyajian konten pelajaran, dan pencatatan progres (`/api/track-progress`).
|
||||
| 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. Cara Sistem Melakukan Evaluasi Terhadap Rangkaian Siswa
|
||||
## 2. Dua Mode Bahasa: C Standar (avr-libc) & Arduino C++
|
||||
|
||||
Pada sistem pemrograman C/Python, evaluasi dilakukan dengan menangkap `stdout` (Output) dari eksekusi kode di backend. Untuk rangkaian listrik, evaluasi dipindah ke **sisi klien (Frontend)** dengan dua pendekatan yang bisa digabungkan:
|
||||
Sistem mendukung dua mode pemrograman, ditentukan per-lesson melalui `active_tabs`:
|
||||
|
||||
### a. Pendekatan Statis (Pencocokan Teks/Komponen)
|
||||
CircuitJS merepresentasikan rangkaian dalam bentuk string teks baris per baris.
|
||||
- Saat pengguna menekan tombol **"Submit" / "Cek Rangkaian"**, SvelteKit akan meminta teks representasi rangkaian dari iframe CircuitJS.
|
||||
- Sistem akan mengecek ketersediaan komponen-komponen wajib menggunakan fitur yang mengadopsi mekanisme `---KEY_TEXT---` saat ini. Misalnya, memastikan ada resistor (`r`), sumber tegangan (`v`), atau LED dalam teks rangkaian siswa.
|
||||
### a. Mode AVR-C (avr-libc)
|
||||
- Tag: `active_tabs` mengandung `'avr'`
|
||||
- Sintaks: `#include <avr/io.h>`, manipulasi register langsung (`PORTB`, `DDRB`, dll.)
|
||||
- PlatformIO framework: `none` (bare-metal avr-libc)
|
||||
- Cocok untuk: pelajaran arsitektur mikrokontroler, register-level programming
|
||||
|
||||
### b. Pendekatan Dinamis (Pencocokan Nilai Simulasi)
|
||||
Karena CircuitJS disajikan secara *Same-Origin* (satu *domain/port* dengan SvelteKit), Elemes dapat mengakses langsung API global dari simulator tersebut tanpa batasan CORS.
|
||||
```c
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
Dengan pendekatan ini, data yang dimasukkan instruktur pada tag `---EXPECTED_STATE---` adalah berformat **JSON** yang mendefinisikan kriteria kelulusan simulasi.
|
||||
|
||||
**Contoh Format JSON `---EXPECTED_STATE---` di Markdown**:
|
||||
```json
|
||||
{
|
||||
"nodes": {
|
||||
"TestPoint_A": { "voltage": 5.0, "tolerance": 0.5 }
|
||||
},
|
||||
"elements": {
|
||||
"Resistor_1": { "voltage_drop": 2.5 }
|
||||
int main(void) {
|
||||
DDRB |= (1 << PB5); // Pin 13 sebagai output
|
||||
while (1) {
|
||||
PORTB ^= (1 << PB5); // Toggle LED
|
||||
_delay_ms(500);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sistem SvelteKit (melalui JavaScript) secara periodik akan mengekstrak state aktual komponen dengan merujuk pada **[Dokumentasi jsinterface CircuitJS1 (GitHub)](https://github.com/pfalstad/circuitjs1/blob/master/war/jsinterface.html)** atau melalui **[Live Example Interaktifnya](https://www.falstad.com/circuit/jsinterface.html)**:
|
||||
1. Menarik API Simulator: `var sim = iframe.contentWindow.CircuitJS1;`
|
||||
2. **`sim.getNodeVoltage(String n)`**: Mengambil nilai tegangan spesifik pada node observasi (misal node berlabel `TestPoint_A`).
|
||||
3. **`sim.getElements()`**: Mengambil sekumpulan *array* objek komponen yang aktif di *canvas*. Frontend kemudian akan memanipulasinya (contoh pencarian berdasarkan indeks/teks properti) lalu memanggil properti internal seperti `.getVoltage(0)` atau *current*-nya.
|
||||
### 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
|
||||
|
||||
- SvelteKit mencocokkan kondisi aktual simulator vs ekspektasi JSON. (*Contoh: Apakah tegangan di TestPoint_A masuk dalam range 4.5V s.d. 5.5V?*)
|
||||
- Jika seluruh evaluasi menyatakan **LULUS**, maka SvelteKit mengirimkan HTTP request `POST /api/track-progress` ke Flask Backend untuk mencatat kelulusan siswa, lalu memunculkan *CelebrationOverlay* (kembang api).
|
||||
```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. Mengikuti Pola Pembuatan Konten yang Sekarang (Markdown)
|
||||
## 3. Implementasi dengan Sistem Tab yang Sudah Ada
|
||||
|
||||
Salah satu keunggulan arsitektur Elemes saat ini adalah kemudahan membuat konten hanya dengan file Markdown (`.md`) dan pemisah teks (delimiter). **Pola ini dapat dipertahankan 100% tanpa mengubah struktur parser backend secara masif.**
|
||||
Sistem tab Elemes saat ini (`info | exercise | editor | circuit | output`) dimanfaatkan sepenuhnya:
|
||||
|
||||
Pembuat konten (guru) cukup membuat rangkaian di CircuitJS versi publik, melakukan *Export as Text*, lalu memasukkannya ke dalam format Markdown Elemes.
|
||||
| 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()`). |
|
||||
|
||||
**Pemetaan Delimiter Markdown Baru:**
|
||||
### Integrasi Tab di `active_tabs`
|
||||
|
||||
| Tipe C/C++ (Default) | Tipe Python (Baru) | Tipe Electronics | Tipe Kuis (Tambahan) | Keterangan / Fungsi pada Modul |
|
||||
|---|---|---|---|---|
|
||||
| `---LESSON_INFO---` | `---LESSON_INFO---` | `---LESSON_INFO---` | `---LESSON_INFO---` | Sama. Berisi tujuan pembelajaran. |
|
||||
| `---EXERCISE---` | `---EXERCISE---` | `---EXERCISE---` | `---EXERCISE---` | Sama. Soal instruksi untuk siswa. |
|
||||
| `---INITIAL_CODE---` | `---INITIAL_PYTHON---` | `---INITIAL_CIRCUIT---` | `---INITIAL_QUIZ---` | Initial blok kode, simulator, atau *payload* kuis (JSON) saat dibuka. |
|
||||
| `---SOLUTION_CODE---` | `---SOLUTION_PYTHON---` | `---SOLUTION_CIRCUIT---` | `---SOLUTION_QUIZ---` | Solusi akhir yang benar (kunci jawaban). |
|
||||
| `---KEY_TEXT---` | `---KEY_TEXT---` | `---KEY_TEXT---` | `---KEY_TEXT---` | Komponen wajib atau validasi teks statis. |
|
||||
| `---EXPECTED_OUTPUT---`| `---EXPECTED_OUTPUT---`| `---EXPECTED_STATE---` | `---EXPECTED_JSON---` | Output/Rules/Format evaluasi untuk hit *Track Progress*. |
|
||||
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)
|
||||
|
||||
### Konfigurasi Multi-Tab Workspace (Mode Hybrid)
|
||||
Agar sistem Elemes tahu antarmuka mana yang perlu dimuat, kita akan menggunakan **Pendekatan Implisit Kolektif**.
|
||||
### Flow Tombol "Run" untuk Mode AVR/Arduino
|
||||
|
||||
Alih-alih memaksa satu modul *hanya* menjadi "pelajaran C" atau "pelajaran Elektronika", parser SvelteKit dan Backend akan bersikap asimilatif. Jika seorang instruktur (misalnya untuk modul *Embedded System* berbekal AVR8js) memasukkan semua tag sekaligus ke dalam satu materi, sistem akan merender **Sistem Multi-Tab di Workspace**.
|
||||
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
|
||||
|
||||
Cara kerjanya (Pendeteksian Paralel):
|
||||
- Apakah `---INITIAL_CODE---` ada? -> Aktifkan Tab **CodeEditor (C/C++)**.
|
||||
- Apakah `---INITIAL_PYTHON---` ada? -> Aktifkan Tab **CodeEditor (Python)**.
|
||||
- Apakah `---INITIAL_CIRCUIT---` ada? -> Aktifkan Tab **CircuitEditor**.
|
||||
- Apakah `---INITIAL_QUIZ---` ada? -> Aktifkan Tab **QuizPanel**.
|
||||
### Tombol Kontrol Tambahan
|
||||
|
||||
Visi ini sangat **Backward Compatible** dan sangat visioner secara infrastruktur. Ratusan materi lama Anda yang secara konvensional hanya memiliki tag `_CODE` murni akan tetap tampil sebagai satu layar editor penuh C/C++. Sedangkan materi yang kompleks di masa depan bisa memadukan keempat fungsi (*C, Python, Simulator, Quiz*) dalam sistem tab yang elegan.
|
||||
- **Run**: Compile + Start simulation
|
||||
- **Stop**: Hentikan simulation loop
|
||||
- **Reset**: Stop + reset CPU state + clear serial output
|
||||
|
||||
**Workflow Pembuatan Konten oleh Guru:**
|
||||
1. Buka CircuitJS di browser.
|
||||
2. Gambar rangkaian "awal" (sebagian belum lengkap). Klik *File -> Export as Text*, salin ke dalam tag `---INITIAL_CIRCUIT---`.
|
||||
3. Selesaikan rangkaiannya. Klik *Export as Text*, salin ke dalam tag `---SOLUTION_CIRCUIT---`.
|
||||
4. Definisikan komponen yang diwajibkan di `---KEY_TEXT---`.
|
||||
5. Siswa membuka Elemes LMS, SvelteKit mem-parsing string `INITIAL_CIRCUIT` dan menampilkannya sebagai rangkaian hidup di dalam CircuitJS.
|
||||
---
|
||||
|
||||
## 4. Analisa Source Code & Refactoring Backend
|
||||
Sistem `elemes/services/lesson_service.py` saat ini (`render_markdown_content`) memiliki parser *hardcoded* yang mengembalikan `7 item tuple` yang sangat restriktif. Menambahkan data elektronika ke *tuple* ini secara mentah akan menghasilkan rantai kembalian panjang (12+ item) yang rawan eror.
|
||||
## 4. Backend: PlatformIO sebagai Compiler Universal
|
||||
|
||||
Oleh karena itu, bagian teknis dari proposal ini menyertakan perbaikan (*refactoring*) pada *blueprint* backend:
|
||||
### Arsitektur Compiler Baru
|
||||
|
||||
### a. Modifikasi `elemes/services/lesson_service.py`
|
||||
- Mengubah titik temu fungsi `render_markdown_content` menjadi tipe data **Dictionary** (*key-value*) bernama `parsed_data`.
|
||||
- Menerapkan pola deteksi Kolektif: Fungsi *extract* akan memverifikasi ekstensi tag dan mengumpulkannya ke dalam *array* metadata, misal `active_tabs`.
|
||||
- Jika `---INITIAL_CODE---` ada -> `active_tabs.append("c")`
|
||||
- Jika `---INITIAL_PYTHON---` ada -> `active_tabs.append("python")`
|
||||
- Jika `---INITIAL_CIRCUIT---` ada -> `active_tabs.append("circuit")`
|
||||
- Jika `---INITIAL_QUIZ---` ada -> `active_tabs.append("quiz")`
|
||||
File: `elemes/compiler/avr_compiler.py`
|
||||
|
||||
### b. Modifikasi Router `elemes/routes/lessons.py`
|
||||
- Menyesuaikan API endpoint `/lesson/[slug].json` agar memproses keluaran `active_tabs` ini dengan format JSON yang valid ke Frontend.
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### c. Modifikasi Frontend SvelteKit (`elemes/frontend/src/routes/lesson/[slug]/+page.svelte`)
|
||||
- Berdasarkan **analisa *source code* UI saat ini**, komponen *Workspace* Elemes sudah memiliki infrastruktur `<div class="panel-tabs">` dan skema *state* bawaan.
|
||||
- *State type* bawaan `activeTab` yang semula hanya menampung tipe statis (`'info'|'exercise'|'editor'|'output'`) akan diperlebar menjadi dinamis (`'editor_c' | 'editor_python' | 'circuit' | 'quiz'`).
|
||||
- SvelteKit cukup melacak *array* `active_tabs` rakitan backend untuk membangun komponen *tab/button* secara kondisional. Jika *array* lebih dari 1, *tab* aktif bergantian (*Embedded Systems Mode*). Jika *array* hanya 1 (pada file lama), antarmuka langsung kembali ke 100% *full-editor* seperti kondisi *legacy*.
|
||||
### 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
|
||||
Perubahan dari LMS pemrograman ke LMS simulasi rangkaian menggunakan CircuitJS1 **sangat feasible (layak)** dan **hemat sumber daya** karena:
|
||||
1. Tidak membutuhkan komputasi simulasi berat di server (semua dilimpahkan ke client secara penuh).
|
||||
2. Mempertahankan gaya pembuatan konten berbasis Markdown yang *author-friendly*; menggunakan pendekatan pendeteksian tag *Implisit* (redundansi pengetikan nol, 100% backward-compatible dengan skenario C/Python lama).
|
||||
3. Proses migrasinya sangat terpusat: hanya butuh merapikan *parser tuple-to-dictionary* di backend dan membuat *komponen UI tunggal iframe CircuitJS1* yang diletakkan pada SvelteKit frontend.
|
||||
|
||||
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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue