Jadi gue punya robot AI kecil di meja. Kotak item, ada layar kecil di depannya, bisa diajak ngobrol. Namanya EZTECH AI Robot X1 — di casing-nya ketulis HaloBaby. Dalemnya dia pakai firmware XiaoZhi (小智), proyek open source dari Tiongkok yang lagi rame banget buat speaker AI DIY.
Pas beli, jualannya manis banget: teman ngobrol, teman tidur, bisa diajak latihan bahasa. Dan emang bener gitu. Minggu pertama gue ajak ngobrol tiap malem, lucu.
Minggu kedua? Basi.
Soalnya ngobrol sama AI yang nggak tau apa-apa soal hidup lo itu cepet banget abisnya. Dia nggak tau gue besok ada meeting apa. Nggak tau portofolio gue lagi merah apa ijo. Gue ngomong sesuatu yang penting, dia cuma bisa jawab manis — terus lupa. Nggak nyatet.
Jujur, dia udah hampir gue masukin laci. Nambahin koleksi barang-yang-dibeli-pas-khilaf.
Terus gue nemu satu hal yang bikin dia batal gue buang: robot ini bisa disambungin ke MCP. Dan begitu nyambung, dia berubah total — dari teman ngobrol jadi asisten yang beneran kepake tiap hari: ngecek saham, cuaca, kalender, sampai nyatet langsung ke server gue sendiri.
Nah, gara-gara dia jadi sering gue pakai, barulah satu hal kecil mulai ganggu: mukanya cuma emoji diem. 😶 🙂 😆 Gitu doang. Nggak gerak. Menunya campur aduk Mandarin. Latarnya putih terang kayak lampu kamar mandi. Buat barang yang nangkring di meja kerja gue tiap hari, itu lama-lama nyebelin.
Gue mikir: “ah paling tinggal ganti file gambar doang kan.”
Wkwk. Nggak. Gue abis semaleman buat ini, sempet bikin robotnya boot loop — mati-nyala-mati-nyala tiap 7 detik — dan biang keroknya cuma 46 KB. Mari gue ceritain.

Barangnya: EZTECH AI Robot X1 (HaloBaby) — cek harganya di Shopee (link afiliasi: gue dapet komisi kecil, harga buat lo tetep sama)
Jadi Ini Robot Apa Sih?
Sebelum ngoprek, gue nggak mau nebak-nebak. Gue bongkar dulu isi firmware-nya biar tau persis gue lagi ngadepin apa. Hasilnya:
- Chip: ESP32-C3, flash 8 MB, dan ini yang penting — nggak ada PSRAM. Jadi RAM-nya cuma yang nempel di chip, sempit banget.
- Layar: ST7789 1,54 inci, 240×240.
- Suara: ada chip khusus namanya VB6824 yang ngurus mikrofon, speaker, sama wake word “halo baby”. Jadi ngomongnya nggak lewat ESP32 langsung.
- Board aslinya: Zuowei MZ01-C3-LCD.
Catatan penting buat lo yang mau ikut-ikutan: di folder Download gue sempet ada file firmware XiaoZhi yang gue kira cocok. Ternyata itu buat ESP32-S3, chip yang beda. Kalau gue hajar flash aja tanpa ngecek, robot gue udah jadi paperweight malem itu juga. Cek dulu. Serius.
Yang Bikin Dia Batal Masuk Laci: MCP
Sebelum lanjut ke urusan muka, gue ceritain dulu bagian yang paling ngubah nasib robot ini. Karena kalau nggak ada ini, gue nggak bakal repot-repot bikin firmware sendiri — barangnya udah nganggur dari kemarin.
MCP (Model Context Protocol) itu, gampangnya, cara ngasih “tangan” ke AI. Modelnya tetep model — yang berubah: lo kasih dia daftar alat yang boleh dipakai. Jadi pas lo nanya “besok gue ada apa?”, dia nggak ngarang. Dia manggil alat calendar_upcoming, ngambil data beneran, baru jawab.
Di XiaoZhi ini ada dua lapis MCP, dan ini yang bikin gue bingung di awal. Beda banget fungsinya:
Lapis pertama: MCP di dalam robotnya. Ini udah nempel di firmware, kelihatan pas booting:
MCP: Add tool: self.audio_speaker.set_volume
MCP: Add tool: self.screen.set_brightness
MCP: Add tool: self.screen.set_theme
MCP: Add tool: self.get_device_status
Jadi gue bisa ngomong “kecilin suaranya” atau “redupin layarnya” — dan si agent manggil balik ke robotnya. Nggak perlu aplikasi, nggak perlu flash ulang.
Lapis kedua: MCP ke server gue sendiri. Ini yang bikin dia pinter. Di console XiaoZhi, masuk ke Configure → Extensions → Custom Services → MCP Endpoint, nanti dikasih alamat wss://api.xiaozhi.me/mcp/?token=.... Alamat itu gue colok ke MCP server yang gue hosting sendiri di homelab. Isinya:
| Tool | Gunanya |
|---|---|
note_save / note_search | gue ngomong, catetannya masuk ke server gue sendiri — bukan ke cloud orang |
calendar_upcoming | “besok gue ada apa?” — baca jadwal gue (belum bisa bikin acara, baru bisa baca) |
stock_report | ngecek portofolio tanpa buka HP |
weather_forecast | ya cuaca |
Jadi sekarang gue bisa ngomong ke kotak kecil di meja: “halo baby, besok gue ada meeting apa?” — dan dia jawab dari kalender gue beneran. Atau “catet: besok follow up si A” — dan catetan itu mendarat di server gue sendiri, bukan nyangkut di percakapan yang bakal ilang.
Di titik itu dia berhenti jadi mainan.
Dan inget baik-baik bagian ini, karena sebentar lagi dia yang nentuin semua keputusan teknis gue: koneksi inilah yang nggak boleh putus, apapun yang gue lakuin ke mukanya.
⚠️ Satu peringatan: alamat MCP Endpoint itu ada token-nya. Siapapun yang pegang token itu bisa nyuruh-nyuruh tool lo — baca catetan, bikin acara di kalender. Jangan di-screenshot mentah-mentah, jangan ke-commit ke Git.
Kenapa Nggak Bisa Ganti Emoji Lewat Setting Aja?
Nah ini jawaban yang bikin gue harus muter jauh.
Di firmware versi lama (punya gue 1.7.6), emoji-nya itu bukan file gambar. Dia font. Jadi 😶 itu huruf, sama kayak huruf A, cuma bentuknya muka. Dan font itu ikut ke-compile jadi satu sama aplikasinya.
Artinya: nggak ada file yang bisa lo tuker, nggak ada folder yang bisa lo isi, nggak ada OTA yang bisa nambahin. Mau ganti muka = mau nggak mau bikin firmware sendiri.
Ya udah. Gas.
Milih Fondasi Firmware: Bagian yang Hampir Gue Salah Pilih
Ini bagian yang menurut gue paling berharga dari semua cerita ini, dan paling nggak keliatan dari luar.
Ada satu proyek namanya OpenEmbodied yang udah punya dukungan board MZ01 ini, lengkap, udah diverifikasi di hardware asli. Kelihatannya jelas pilihan paling masuk akal. Gue hampir pakai itu.
Untung gue ngecek dulu isinya. Ternyata protokol ngobrolnya beda: dia ngomong ke Coze, bukan ke server XiaoZhi. Kalau gue flash itu, muka robot gue memang bakal animasi — tapi dia putus dari agent gue.
Nah, inget bagian MCP tadi? Semua itu nyantol di agent XiaoZhi gue. Flash OpenEmbodied = semua tool itu putus. Saham, kalender, catetan, semuanya ilang dalam sekali flash.
Jadi pilihannya jelas banget: muka animasi tapi robotnya balik jadi bego — alias balik ke versi yang hampir gue buang — atau cari fondasi lain yang lebih ribet tapi semuanya tetep jalan.
Gue ambil yang kedua: fork namanya DOIT_AI (XiaoZhi v1.8.2). Dia satu-satunya yang punya tiga hal sekaligus — protokol XiaoZhi resmi, driver VB6824 yang udah jadi, dan tabel partisi yang sama persis sama bawaan pabrik. Yang terakhir ini artinya gue cuma nimpa aplikasinya doang, Wi-Fi sama pairing-nya selamat.
Pelajaran yang gue catet: kompatibilitas protokol itu lebih mahal daripada dukungan hardware. Hardware bisa lo tambal sendiri. Protokol yang salah bikin semua integrasi lo mati.
Boot Loop. 46 KB. Dan Ide Gue yang Salah Total.
Oke, masuk ke bagian paling gokil.
Gue dapet pack muka GIF animasi (dari komunitas, nanti gue sebut sumbernya di bawah). Gue tempelin ke firmware, gue pakai decoder GIF bawaan LVGL, gue build, gue flash.
Dan… jalan! Mukanya animasi! Gue udah seneng banget.
Terus robotnya restart sendiri. Terus restart lagi. Terus lagi. Tiap 7 detik.
Log-nya gini:
E (3621) esp-tls: Failed to initialize TLS
E (4102) Application: Opus codec task is out of memory, free_sram: 8352
Guru Meditation Error: Core 0 panic'ed (Store access fault)
Diterjemahin: sisa RAM-nya tinggal 8 KB. Gara-gara itu TLS gagal nyala (alias nggak bisa konek ke cloud), task audio gagal jalan, terus firmware-nya nulis ke memori yang gagal dialokasiin tadi — dan panic.
Nah, di titik ini gue ngomong ke Claude, dengan penuh percaya diri:
“maybe remove some face to get more space”
Masuk akal kan? RAM penuh, ya kurangin isinya. Dan gue salah total.
Penjelasannya bikin gue nyengir sendiri: yang dibuka itu cuma SATU muka dalam satu waktu. Jumlah muka cuma makan flash (penyimpanan), bukan RAM. Yang mahal itu ukuran satu muka yang lagi dibuka. Decoder GIF-nya LVGL itu nahan kanvas ARGB8888 ukuran lebar × tinggi × 4 plus satu buffer frame lagi — totalnya sekitar 46 KB buat muka 96×96 piksel.
Jadi kalaupun gue hapus 18 muka dan nyisain satu, tetep boot loop. Gue ngilangin yang bukan masalahnya.
Ini pelajaran yang kepake di luar ngoprek juga sih: sebelum ngurangin sesuatu, pastiin dulu yang lo kurangin itu beneran biayanya.
Solusinya: Jangan Pakai Decoder Sama Sekali
Kalau decoder-nya yang rakus, ya jangan pakai decoder.
Gue bikin script yang nge-render semua frame GIF jadi format RGB565 duluan, terus ditaruh di flash. RGB565 itu kebetulan format asli layarnya. Jadi LVGL tinggal nyalin mentah-mentah ke layar — nggak ada yang perlu di-decode, nggak ada yang perlu disalin ke RAM. Animasinya cukup pakai timer yang gantiin pointer frame tiap sekian milidetik.
Hasilnya:
| Sebelum (decoder GIF) | Sesudah (RGB565 di flash) | |
|---|---|---|
| RAM buat decoder | ~46 KB | 0 |
| Sisa RAM | 8 KB → panic | 96 KB |
| Cloud | gagal konek | nyambung |
| Audio | gagal start | jalan |
Dari 8 KB ke 96 KB. Robotnya langsung anteng, konek ke api.tenclass.net, dan semua tool MCP gue balik hidup.
Ongkosnya pindah ke flash: 19 muka buat 21 ekspresi, ukuran 96 piksel, 3 frame per muka = sekitar 1 MB. Masih sisa 290 KB di partisi aplikasinya. Mau muka lebih gede? Tinggal kurangin jumlah muka atau jumlah frame. Trade-off-nya jadi jelas dan bisa diatur — bukan lagi tebak-tebakan.
Beresin Tampilan: Muka di Tengah, Teks di Bawah
Pas udah jalan, ternyata tatanannya aneh: mukanya malah nongol di bawah tulisan.
Ternyata area kontennya itu kotak flex vertikal, dan gambar muka gue dibikin paling belakang — jadi ya ditaro paling bawah. Gue keluarin muka sama teksnya dari tatanan itu, terus gue taro manual: muka di tengah, teks nempel di bawah. Teksnya gue batesin cuma sebanyak baris yang muat di bawah muka, lebihnya dipotong titik-titik. Jadi kalau si robot ngejawab panjang lebar, mukanya nggak bakal kedorong keluar layar.
Terus gue ganti bahasanya jadi Inggris. Dan ini bonus yang gue nggak nyangka: suaranya ikut ganti. Ternyata klip suara buat aktivasi, setup Wi-Fi, sama baca kode angka itu disimpen per bahasa. Jadi sekali ganti, tulisan sama suaranya English dua-duanya. Ada dua kalimat Mandarin nyangkut yang nggak ikut file bahasa, itu gue tambal manual.
Kenapa Layarnya Tetep Nggak Item Banget?
Nah ini pertanyaan bagus yang muncul pas gue liat hasilnya.
Gue kira “dark mode” di firmware ini artinya item. Ternyata enggak: warnanya #121212 sama #1E1E1E — abu-abu gelap ala Material Design. Terus ada garis tepi #333333 yang nggak dimatiin. Padahal muka GIF gue latarnya item beneran (#000000, 81% dari tiap frame). Jadi mukanya keliatan kayak ditempelin di kotak abu-abu. Jelek.
Gue bikin tema khusus board ini jadi item beneran. Begitu latarnya #000000, kotak mukanya ilang — yang keliatan cuma mukanya doang, ngambang di layar. Jauh lebih enak dilihat.
Tapi — lihat lagi foto di atas. Layarnya masih keliatan biru keabu-abuan, kan? Itu bukan temanya. Itu lampu latarnya. ST7789 itu LCD, dan LCD selalu disinarin lampu dari belakang. “Item” di LCD cuma berarti shutter-nya ditutup, tapi cahayanya tetep nembus dikit. Rasio kontrasnya sekitar 800–1000:1.
Jadi: selama layarnya masih LCD, item beneran itu nggak ada. Mau item total? Harus OLED. Yang bisa lo lakuin cuma turunin brightness — makin redup lampu latarnya, makin pekat item-nya.
Repo-nya Gue Buka
Semuanya udah gue rapihin dan gue taro di GitHub: github.com/mewt/eztech-x1-custom-faces
Isinya bukan salinan firmware segede gaban — cuma bagian yang gue ubah: file board-nya, patch-nya, script buat render muka, sama script backup/build/flash/restore. Ada install.sh yang ngurus sisanya.
Satu hal yang gue tekenin di sana dan gue tekenin lagi di sini: backup dulu sebelum ngapa-ngapain. Dump 8 MB flash-nya ke file. Itu satu-satunya jalan pulang lo kalau ada apa-apa. Dan hati-hati — file backup itu isinya password Wi-Fi lo dalam bentuk teks biasa. Jangan taro di Google Drive bareng-bareng, jangan ke-commit ke Git.
Muka-mukanya sendiri bukan bikinan gue. Itu pack dari komunitas yang dikumpulin sama Loa AI, nggak ada nama pembuat aslinya dan nggak ada lisensinya. Gue tulis itu terang-terangan di repo-nya, lengkap sama tawaran buat hapus kalau yang punya keberatan. Mending jujur dari awal daripada sok-sokan ngaku karya sendiri.
Oh iya, barangnya gue beli di sini: EZTECH AI Robot X1 di Shopee. Itu link afiliasi ya — kalau lo beli lewat situ gue dapet komisi kecil, harganya tetep sama buat lo. Gue beli pakai duit sendiri, nggak ada yang nyeponsorin, dan firmware-nya jalan sama aja mau lo beli dari mana.
Pelajaran yang Gue Bawa Pulang
1. Jangan ngurangin sesuatu sebelum tau mana yang beneran mahal. Ide gue “hapus aja beberapa muka” itu kedengeran logis dan salah total. Yang mahal bukan jumlahnya, tapi ukuran satu yang lagi dibuka. Ini berlaku juga pas gue ngurusin budget campaign, jujur.
2. Kompatibilitas itu fitur. Fondasi yang “lebih siap” tapi ngomong protokol beda itu bukan jalan pintas — itu jalan buntu yang dikasih karpet.
3. Yang keliatan jalan belum tentu sehat. Muka gue animasi dengan mulus… sambil robotnya sekarat kehabisan RAM. Kalau gue nggak baca log, gue cuma bakal bingung kenapa dia restart mulu.
4. Hardware murah + data lo sendiri = barang yang beda kelas. Robot ini nggak berubah sedikitpun secara fisik. Chip-nya tetep ESP32-C3 yang RAM-nya seuprit. Yang berubah cuma: dia dikasih akses ke kalender, catetan, sama portofolio gue. Itu doang. Dan itu bedanya antara “mainan yang nganggur di laci” sama “barang yang gue ajak ngomong tiap pagi”.
5. Batas fisik itu nggak bisa ditawar sama kode. Gue bisa bikin temanya seitem mungkin, tapi LCD tetep LCD. Kadang jawabannya bukan “optimasi lagi”, tapi “ya emang segitu kemampuannya”.
Ini kerjaan yang nggak ada hubungannya sama KPI gue di Smart Salary. Nggak nambah lead, nggak nambah konversi. Tapi tiap kali gue ngelirik meja dan ada muka kecil kedap-kedip di layar item, terus gue bilang “halo baby, besok gue ada meeting apa?” dan dia jawab dari catatan di server gue sendiri — rasanya worth it.
Barang yang dulu hampir gue masukin laci, sekarang jadi barang yang paling sering gue ajak ngomong di meja. Lucu juga.
Mirip-mirip pas gue ngoprek launcher TV TCL kemarin: niatnya kecil, ujungnya belajar banyak. Dan kayak biasa, gue ngerjainnya nggak sendirian — sama partner AI gue, yang kali ini tugas utamanya adalah ngasih tau gue kalau ide gue salah.
Cheers (dan semoga robot lo nggak boot loop), Agung Sukariman
