Belajar Firmware Engineer - Firmware Testing & CI
Episode 14 of 28

Belajar Firmware Engineer - Firmware Testing & CI

Menguji firmware sebelum merusak hardware: unit testing host-based dengan mock, lapisan SIL dan Hardware-in-the-Loop, static analysis, serta pipeline CI firmware di GitHub Actions yang otomatis membangun, menguji, dan memverifikasi setiap commit.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 13 firmware kita bisa diperbarui dari jauh, pertanyaan berikutnya adalah: bagaimana memastikan yang kita kirim tidak merusak ribuan perangkat? Jawabannya adalah pengujian sistematis — dan di firmware, pengujian punya tantangan khusus: hardware mahal, lambat, dan tidak selalu tersedia.

Mengapa testing penting? Karena bug firmware sangat mahal: satu bug di lapangan berarti mengganti perangkat, mengirim teknisi, atau memperbarui ribuan unit. 2026 menandai era di mana CI firmware jadi standar rekrutmen (dari episode 1) — perusahaan tidak lagi menerima "saya test manual di board saya". Episode ini membangun fondasi quality engineering yang membedakan produk dari prototype.

Lapisan Pengujian Firmware

Sama seperti software, firmware punya piramida pengujian:

100%
LapisanLingkunganKecepatanCakupan
Unit testHost (PC native)MilidetikLogika murni, state machine, parser
SILSimulasi (QEMU, emulasi)DetikPerilaku chip, interrupt, timing
HILHardware nyataMenitRegister nyata, sensor, integrasi

Aturan praktis: sebanyak mungkin test di unit (cepat, murah), sedikit tapi kritis di HIL (mahal, lambat). Firmware produksi tidak pernah mengandalkan satu lapisan saja.

Unit Testing: Logika Murni di Host

Kunci unit testing firmware adalah memisahkan logika dari hardware. Kode yang "murni" — state machine, parser, algoritma, kalkulasi sensor — bisa dikompilasi dan diuji di PC:

Logika murni yang bisa diuji di host
// parser.c - tanpa hardware sama sekali
int parse_command(const char *buf, uint16_t len, cmd_t *out) {
    if (len < 4) return -1;
    out->type = (cmd_type_t)buf[0];
    out->value = (buf[1] << 8) | buf[2];
    return 0;
}

Untuk kode yang menyentuh hardware, gunakan mock — ganti fungsi hardware dengan kembalian tetap:

Mock transport agar driver bisa diuji
// transport_stub.c (dipakai di test, bukan di firmware)
int i2c_read_regs(...) { return 0; }   // mock: selalu sukses
uint8_t mock_buf[4] = {0x10, 0x00, 0x1E, 0x0};

Framework populer: Unity + CMock + Ceedling (C), GoogleTest (C++), atau throwaway (test sederhana dengan assert). Test ditulis setara dengan test software biasa.

Test parser dengan Unity
void test_parse_simple_command(void) {
    cmd_t cmd;
    TEST_ASSERT_EQUAL(0, parse_command("AB\x01\x00", 4, &cmd));
    TEST_ASSERT_EQUAL(CMD_AB, cmd.type);
    TEST_ASSERT_EQUAL(0x0100, cmd.value);
}

Hardware-in-the-Loop (HIL)

HIL menguji firmware di board nyata dengan kontrol otomatis. Skema khas:

Setup HIL
Host (test runner) --USB/serial--> Board
Board --> sensor/aktuator nyata atau simulator
Host baca output, bandingkan dengan ekspektasi, lapor PASS/FAIL

HIL yang baik menguji: GPIO nyala benar, UART mengirim frame tepat, watchdog berfungsi, dan bootload/OTA benar-benar bisa naik-turun versi. Otomatisasi HIL memakai tool seperti pytest + pyserial, atau framework vendor (STM32CubeMonitor).

Pipeline CI Firmware

CI firmware menjalankan unit test di host (tanpa hardware), membangun binary, dan menyimpan artefak. Contoh workflow GitHub Actions:

.github/workflows/firmware-ci.yml
name: firmware-ci
 
on: [push, pull_request]
 
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Install toolchain
        run: sudo apt-get install -y gcc-arm-none-eabi
 
      - name: Unit tests (host)
        run: make test              # kompilasi native + jalankan Unity
 
      - name: Build firmware
        run: make build             # build release binary
 
      - name: Static analysis
        run: cppcheck --error-exitcode=1 src/
 
      - name: Check memory usage
        run: |
          arm-none-eabi-size build/firmware.elf
          test $(arm-none-eabi-size -A build/firmware.elf | \
                 awk '/flash|text/ {sum+=$2} END {print sum}') -lt 60000
 
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: firmware
          path: build/firmware.bin

Perhatikan langkah terakhir: gate ukuran memori (episode 8) — CI menolak build yang melampaui budget flash/RAM sebelum pernah sampai ke tangan QA.

Tip

Tambahkan formatting check dan linting ke pipeline: clang-format --dry-run untuk gaya konsisten dan cppcheck untuk bug statis. Tim yang men-deploy firmware dengan 100% test hijau tapi penuh inkonsistensi gaya akan kesulitan review — CI yang baik menjaga kualitas, bukan cuma "build sukses".

Praktik: Pipeline Testing

Susun strategi test produk kalian:

Strategi test minimal untuk produksi
✓ Unit test: state machine, parser, kalkulasi sensor (host, tiap commit)
✓ Build firmware: release + debug (tiap commit)
✓ Memory gate: flash & RAM di bawah budget (tiap commit)
✓ Static analysis: cppcheck / coverity (tiap commit)
✓ HIL: boot, GPIO, UART, watchdog (tiap release / nightly)
✓ OTA test: naik/turun versi + rollback (tiap release)

Kesalahan Umum Testing Firmware

  1. Mengandalkan board untuk semua test — satu board, lambat, tidak otomatis.
  2. Kode tidak dipisahkan dari hardware — logika nyangkut di ISR/register; tidak bisa diuji.
  3. Mock yang "terlalu baik" — mock selalu sukses, bug transport lolos ke HIL.
  4. CI hanya build, tanpa test — tidak ada nilai proteksi.
  5. HIL tanpa otomasi — "test manual di meja" bukan HIL.
  6. Tidak ada memory gate — binary membengkak hingga melewati flash tanpa disadari.

Penutup

Inti yang harus dibawa pulang:

  • Piramida test firmware: unit (host) → SIL (simulasi) → HIL (hardware).
  • Unit testing butuh logika murni terpisah dari hardware + mock transport.
  • HIL menguji perilaku nyata board secara otomatis.
  • CI firmware: unit test + build + static analysis + memory gate per commit.
  • Pipeline test yang baik adalah perbedaan antara produk dan prototype.

Di episode 15 selanjutnya kita belajar bahasa yang akan menulis banyak firmware 2026: Rust for Firmwareno_std, embedded Rust, memory safety tanpa garbage collector, dan praktik menulis firmware Rust pertama. Masa depan firmware mulai di sini!