428 lines
15 KiB
Markdown
428 lines
15 KiB
Markdown
# 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 <avr/io.h>`, manipulasi register langsung (`PORTB`, `DDRB`, dll.)
|
|
- PlatformIO framework: `none` (bare-metal avr-libc)
|
|
- Cocok untuk: pelajaran arsitektur mikrokontroler, register-level programming
|
|
|
|
```c
|
|
#include <avr/io.h>
|
|
#include <util/delay.h>
|
|
|
|
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.
|