Skip to content
All Posts

November 8, 2026

Build Your Own Home Mini and Make It Speak Turkish

Google Home Mini does not speak Turkish, so I built an assistant that wakes to "Hey Jarvis", speaks Turkish and runs the house through Home Assistant. Parts list, soldering, the ESPHome config, a 3D-printed enclosure (STLs included) and three weeks of fine-tuning, all in one place.

Smart HomeHome AssistantESPHomeESP32-S3Voice AssistantDIY3D PrintingTurkish

There has been a Google Home Mini in my house for years. As an announcement speaker it is flawless: it says "dishes are ready" when the dishwasher finishes and greets the kids when they get home from school. But asking it anything in Turkish is impossible; a Turkish assistant never came to Turkey, you cannot give it any name other than "Hey Google", and you cannot replace its brain. When the kids asked "why doesn't it talk to us", I decided: we would build our own assistant. Its name would be Jarvis, it would speak Turkish, and it would run the house.

Three weeks later, Jarvis sits in a 3D-printed box in the living room. Say "Hey Jarvis, how cold is it outside?" and it answers "It's 8.6 degrees outside, partly cloudy"; say "turn on the back lamp" and it does, replying "Back light is on"; ask it a riddle like the kids do and it answers with a riddle. This post is everything you need to build the same: parts list, soldering, firmware, the ESPHome config, the STL files for the enclosure and the lessons I learned.

I did not throw the Home Mini away; announcements still play from it. Jarvis does the talking.
I did not throw the Home Mini away; announcements still play from it. Jarvis does the talking.

Parts list

Everything came from Turkey (Robot Sepeti); the electronics add up to less than the price of a smart speaker.

PartWhat it doesNote
Seeed ReSpeaker Lite (USB 2-Mic)Two microphones + XMOS XU316 audio processor: echo and noise cancellation, speaker outputThe board has a XIAO socket, a 3.5 mm jack and a JST PH 2.0 speaker socket
Seeed XIAO ESP32-S3Wi-Fi + on-device "Hey Jarvis" wake word detectionComes with an external antenna (it stays inside the box)
WS2812B LED ring, 12 LEDs (Ø50 mm)Red while listening, purple while thinking, green while speakingA 16-LED (Ø67) ring works too; the box is sized for the 12
Speaker Ø45 mm, 4 Ω 5 WSound inside the boxAlternative: an external set via the 3.5 mm jack
USB-C adapter 5 V 2–3 APower, through the ReSpeaker's own USB-CA phone charger is enough
2×7 pin header (for the XIAO), 3 wires, (optional) 330 Ω resistor, 1000 µF capacitorSoldering
3D-printed enclosure, 5 parts, PETGBelow; STLs at the end of the post~60 g of filament

Software: ESPHome (Home Assistant add-on) + formatBCE's ReSpeaker Lite package. Far less hassle than Seeed's official route, and you get every feature of Home Assistant's own Voice PE device.

Assembly in photos

ReSpeaker Lite with its two microphones (MIC 1 / MIC 2) at the two ends of the board. The Usr/Mute buttons and the XIAO socket are visible on top. This is the first version of the box; I enlarged the USB-C and jack holes by 1 mm in the next version.
ReSpeaker Lite with its two microphones (MIC 1 / MIC 2) at the two ends of the board. The Usr/Mute buttons and the XIAO socket are visible on top. This is the first version of the box; I enlarged the USB-C and jack holes by 1 mm in the next version.
The XIAO sits on a pin header; the antenna's small cable is taped to the top of the board. The green LED below is the ReSpeaker's power LED, the orange one is the XIAO's charge LED.
The XIAO sits on a pin header; the antenna's small cable is taped to the top of the board. The green LED below is the ReSpeaker's power LED, the orange one is the XIAO's charge LED.
Two holes on the short side of the box: USB-C for power, a 3.5 mm jack for an external speaker. Both are the ReSpeaker's own sockets; the XIAO's USB-C is only for the first firmware flash.
Two holes on the short side of the box: USB-C for power, a 3.5 mm jack for an external speaker. Both are the ReSpeaker's own sockets; the XIAO's USB-C is only for the first firmware flash.
The 16-LED ring, the lid and the box. The holes in the lid let microphone sound through; the circle in the middle is where the diffuser goes.
The 16-LED ring, the lid and the box. The holes in the lid let microphone sound through; the circle in the middle is where the diffuser goes.
Three wires on the ring: red 5V, black GND, blue DIN. On my first attempt I soldered the blue wire to the GND pad; neither the ring nor the board's own LED lit up. Photograph the pads and read them before soldering.
Three wires on the ring: red 5V, black GND, blue DIN. On my first attempt I soldered the blue wire to the GND pad; neither the ring nor the board's own LED lit up. Photograph the pads and read them before soldering.
First test: the ring lights up after "Hey Jarvis". Once inside the box, the diffuser softens this light.
First test: the ring lights up after "Hey Jarvis". Once inside the box, the diffuser softens this light.
Three wires, two solder bridges. I finished all of it in one evening.
Three wires, two solder bridges. I finished all of it in one evening.

Soldering: three wires, two bridges

1. XIAO onto the ReSpeaker. The XIAO's position and the direction of its USB-C end are printed on the board; follow that outline and solder it with the pin header (not directly to the surface; it makes removal easy if you ever need to). For the first week I used the header without solder, just "plugged in"; the audio came through choppy and for two weeks I suspected Wi-Fi. The problem was a bad contact. Solder it.

2. Button bridges. The two buttons on the ReSpeaker connect to the XIAO through solder bridges: USR → D2, MUTE → D3. Without them the buttons and software mic muting do not work.

3. LED ring. There are four pads on the back of the ring: DI (DIN), DO, 5V, GND. DO stays empty.

Ring→XIAO
DIN→D0 (GPIO1)
5V→5V
GND→GND

D0 already drives the board's own RGB LED; the ring is wired in parallel to it and the two light up together. There is no other free pin, so this is a deliberate choice. A 330 Ω resistor in series on the data line protects the signal; if the ring flickers, put a 1N4001 diode on the 5V line (it drops the voltage to 4.3 V so the 3.3 V signal is read more reliably). If the ring's DIN pad lifts while soldering (mine did), you can solder the wire directly to the DIN leg of the first LED.

4. Speaker. A 2-pin JST PH 2.0 cable into the board's SPK socket. If you are using an external set, the 3.5 mm jack.

The XMOS firmware (once)

The ReSpeaker ships in USB microphone mode; for Home Assistant you need to flash the I2S 48 kHz firmware. Plug the cable into the ReSpeaker's own USB-C (the one next to the jack), not the XIAO's:

bash
brew install dfu-util          # Linux: apt install dfu-util
dfu-util -l                    # kartı gör
dfu-util -R -e -a 1 -D respeaker_lite_i2s_dfu_firmware_48k_v1.1.0.bin

Download the file from the same tag as the release in formatBCE's GitHub repository (e.g. 2026.10.3). Do not unplug during flashing; when it finishes, unplug and plug back in.

ESPHome: the full config

Home Assistant → ESPHome Builder → new device → ESP32-S3 → skip the wizard and paste this into the editor. The package does all the audio work; this file only replaces the LED ring and Jarvis's colours:

yaml
substitutions:
  respeaker_lite_ref: 2026.10.3

packages:
  respeaker-satellite: github://formatBCE/Respeaker-Lite-ESPHome-integration/config/common/respeaker-satellite-base.yaml@${respeaker_lite_ref}

esphome:
  name: jarvis-salon
  friendly_name: Jarvis Salon

wifi:
  power_save_mode: none        # (1) ses kesilmesinin ilk çözümü
  output_power: 11dB           # (2) modemin dibindeyse verici gücünü düşür

improv_serial:                 # Wi-Fi bilgisi ilk yüklemede web.esphome.io'dan girilir

ota:
  - platform: esphome
    id: ota_esphome

speaker:
  - id: !extend i2s_audio_speaker
    buffer_duration: 600ms     # (3) paket varsayılanı 100 ms; takılmanın asıl çözümü

button:
  - platform: restart
    name: Yeniden Başlat

sensor:
  - platform: wifi_signal
    name: Wi-Fi sinyal
    update_interval: 60s

light:
  - id: !remove led_internal
  - id: !remove led_respeaker_onboard

  - platform: esp32_rmt_led_strip
    id: led_internal
    internal: true
    channel_colors: GRB
    pin: GPIO1                 # XIAO D0
    num_leds: 12               # 16'lı halkada 16
    rmt_symbols: 192
    bit0_high: 300ns
    bit0_low: 975ns
    bit1_high: 650ns
    bit1_low: 650ns
    reset_low: 300us
    reset_high: 0us
    max_refresh_rate: 15ms
    default_transition_length: 0s
    color_correct: [55%, 55%, 55%]   # güç sınırı: 12 LED beyazda ~0,4 A
    effects:
      - addressable_lambda:
          name: "Slow Pulse"
          update_interval: 30ms
          lambda: |-
            static float f = 0.0; static bool up = true;
            auto v = id(led_internal)->current_values;
            Color c(v.get_red() * 255, v.get_green() * 255, v.get_blue() * 255);
            it.all() = Color(c.red * f, c.green * f, c.blue * f);
            f += up ? 0.05 : -0.05;
            if (f > 1.0) { f = 1.0; up = false; } else if (f < 0.0) { f = 0.0; up = true; }
      - addressable_lambda:
          name: "Fast Pulse"
          update_interval: 10ms
          lambda: |-
            static float f = 0.0; static bool up = true;
            auto v = id(led_internal)->current_values;
            Color c(v.get_red() * 255, v.get_green() * 255, v.get_blue() * 255);
            it.all() = Color(c.red * f, c.green * f, c.blue * f);
            f += up ? 0.05 : -0.05;
            if (f > 1.0) { f = 1.0; up = false; } else if (f < 0.0) { f = 0.0; up = true; }
      - addressable_lambda:
          name: "Spin"            # düşünürken halkada dönen kuyruklu ışık
          update_interval: 45ms
          lambda: |-
            static int head = 0;
            auto v = id(led_internal)->current_values;
            Color c(v.get_red() * 255, v.get_green() * 255, v.get_blue() * 255);
            const int n = it.size();
            for (int i = 0; i < n; i++) {
              int d = (head - i + n) % n;
              float f = d == 0 ? 1.0f : d == 1 ? 0.5f : d == 2 ? 0.22f : d == 3 ? 0.08f : 0.0f;
              it[i] = Color(c.red * f, c.green * f, c.blue * f);
            }
            head = (head + 1) % n;

  - platform: partition         # HA'dan açılıp kapatılabilen "gece lambası" ışığı
    id: led_respeaker_onboard
    name: LED halka
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF   # boşta sönük; sadece "Hey Jarvis" sonrası yanar
    segments:
      - id: led_internal
        from: 0
        to: 11

script:
  - id: !remove control_leds_voice_assistant_waiting_for_command_phase
  - id: !remove control_leds_voice_assistant_listening_for_command_phase
  - id: !remove control_leds_voice_assistant_thinking_phase
  - id: !remove control_leds_voice_assistant_replying_phase

  - id: control_leds_voice_assistant_waiting_for_command_phase   # "Hey Jarvis" dedin, bekliyor
    then:
      - light.turn_on: {id: led_internal, brightness: 35%, red: 100%, green: 0%, blue: 0%, effect: "Slow Pulse"}
  - id: control_leds_voice_assistant_listening_for_command_phase # dinliyor
    then:
      - light.turn_on: {id: led_internal, brightness: 35%, red: 100%, green: 0%, blue: 0%, effect: "None"}
  - id: control_leds_voice_assistant_thinking_phase              # düşünüyor
    then:
      - light.turn_on: {id: led_internal, brightness: 35%, red: 70%, green: 0%, blue: 100%, effect: "Spin"}
  - id: control_leds_voice_assistant_replying_phase              # konuşuyor
    then:
      - light.turn_on: {id: led_internal, brightness: 35%, red: 0%, green: 100%, blue: 25%, effect: "Fast Pulse"}

Compiling takes half an hour on a Raspberry Pi; be patient. First flash: Install → Manual download → Factory, then plug the cable into the XIAO's USB-C and in Chrome go to web.esphome.io → Install. Every later update is wireless (OTA). On first boot the XMOS version is verified; do not cut power while the LED blinks white.

The three numbered lines cost me three weeks:

  1. power_save_mode: none — the ESP32 default puts Wi-Fi to sleep and wakes it; the audio stream arrives choppy.
  2. output_power: 11dB — Jarvis sits right next to the router (−20 dBm). Too strong a signal causes problems too.
  3. buffer_duration: 600ms — the real fix. The package's 100 ms buffer stuttered at the start of every sentence. If you leave out !extend you get a "platform key" error.

Home Assistant settings

  • Device → Wake word: Hey Jarvis (recognised on-device, no cloud). Sensitivity: "Slightly sensitive".
  • Finished speaking detection: relaxed. "Aggressive" was cutting off the end of sentences.
  • Settings → Voice assistants → new pipeline: STT, brain, TTS. My choices are below. "Prefer handling commands locally" is on: "turn on the back light" runs in 0.3 seconds without reaching the LLM.
  • On the device page, select this pipeline as the assistant.
  • Put the device in an area in HA; when you switch devices in that area with a local command, it just plays a confirmation sound instead of speaking, which is far less tiring.
  • You can drive Jarvis's LED ring from other automations too (light.jarvis_salon_led_halka): red-and-yellow when a goal is scored, rainbow on birthdays.

Turkish: the hard part

The hardware was done in one evening; getting it to speak Turkish properly took three weeks.

Speech recognition (STT). Free Whisper hallucinates on short Turkish commands (it heard "turn on the decorative light" as "tree"). Whisper large-v3 via Groq is better, and passing device names as a hint text helps too, but the most accurate is Google's cloud STT. If you want fully local, there are Turkish fine-tuned Whisper models; the quality gap is noticeable.

Brain (LLM). Gemma 4 running on a computer at home; Gemini when the computer is off. The details and the full prompt are in the Hybrid AI Home Control System post. Whatever the model, the real work is in the prompt: without rules like "if the device is ambiguous, ask, don't guess", "don't answer the TV", "write numbers as digits", the assistant turns off lights at random.

Text-to-speech (TTS). Edge TTS's Turkish "Emel" voice is natural and free. Fully local Piper has Turkish voices but they are more robotic. A trap: ending the reply with "." froze TTS for 8 seconds; "Ok." fixed it. Emoji and bullet points get read aloud, so they are banned in the prompt.

The English error message. When the brain fails, HA says "Sorry, I had a problem…". With one line in the firmware (on_tts_start: raise a flag if the text starts with "Sorry") I have HA say "Something went wrong, could you try again?" in Turkish instead.

Family. Family members are introduced in the prompt; it explains things patiently and simply to the kids and keeps a conversation going with a short question. The younger one asks for riddles, the older one asks for match scores (which it answers thanks to the internet search tool).

The enclosure: 3D-printed, five parts

I designed the box myself (parametric, Python + Manifold) and it went through five versions: the ring did not fit the first, the jack hole was too tight in the second, the third got rails and a snap-fit lid, the fourth added a speaker compartment underneath, and in the fifth the jack and USB-C openings grew by 20% and the speaker cable hole moved to the socket end of the board.

Top to bottom: perforated lid, diffuser, LED ring, ReSpeaker Lite + XIAO, base. This is the drawing of the second version; the fourth adds a speaker compartment and feet under the same structure, which you can see in the 3D model below.
Top to bottom: perforated lid, diffuser, LED ring, ReSpeaker Lite + XIAO, base. This is the drawing of the second version; the fourth adds a speaker compartment and feet under the same structure, which you can see in the 3D model below.
  • Outer dimensions 91 × 59 × 57 mm; inner width of the board tray 54 mm.
  • The ReSpeaker slides into rails on the two long sides and is held by two M2×5 screws.
  • The lid snaps on; it opens with a fingernail at the notch on the XIAO end.
  • The speaker sits face-down in the base; sound exits through the grille and the gap left by the feet. Its cable passes through a Ø5 hole into the board tray.
  • Printing: PETG, 0.2 mm layers, 20% infill, no supports (every part is designed with its flat face on the bed). About 3.5 hours total.

STL files: jarvis_hoparlor_govdesi.stl, jarvis_taban.stl, jarvis_difuzor.stl, jarvis_kapak.stl, jarvis_ayaklar_4adet.stl — jarvis-kutu-v5-stl.zip. If you want a version for a 16-LED ring or without the speaker, write to me via Contact and I will generate it from the parametric file.

How long it took, what it cost

Time: soldering and the box took a weekend; the software and Turkish fine-tuning took evenings spread over three weeks. Today Jarvis's response time, from wake word to audio playing, is 2–3 seconds; local commands take 0.3 seconds.

Conclusion

Google Home Mini does not speak Turkish and never will. But with a weekend and four parts, it is possible to build an assistant that answers to whatever name you choose, speaks Turkish, lets you swap its brain whenever you like, and actually runs your home. And you get to decide whose server hears what you say.

For questions, the places where you get stuck, and photos of your own Jarvis, write to me via the Contact page.

Sources: formatBCE/Respeaker-Lite-ESPHome-integration (GitHub); Seeed ReSpeaker Lite wiki; Home Assistant "Assist" and "Voice" docs; ESPHome esp32_rmt_led_strip and i2s_audio docs.

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Comments containing links are not accepted.

Your name is shown with your comment. No email is required; your IP is stored only as an anonymous hash to prevent abuse.