ESPHome BLE Keyboard

It is strange that the Dmitry has not yet made this correction. After all, without it, media buttons do not work at all. :roll_eyes:

I am a little lost, so this allows me to use an ESP for as virtual Bluetooth keyboard for devices? (Similar to how the Logitech remotes did?)

Also, can I switch between multiple devices, or would I need a separate ESP for each one? I have a Shield, Android TV and PC for example, can I switch between each device?

I’ve run out of ESPs, so working out how many more I need to order
Thanks :slight_smile:

Hi,
I am having the following problem:
After the ipad connects to the esp32 bluetooth, every time I restart the esp32, the ipad cannot automatically reconnect to the esp32 via bluetooth. On Android phones I don’t have this problem. Currently I have not found out the cause of the problem

How to install this?

Variant1:


Variant2:


This is cool for sure and much appreciated. Maybe its just me and a lack of imagination but, im struggling to think of any reasons to need a keyboard. Sure, you can find a use for controlling the volume on something or a few keys to toggle this or that but a full size keyboard seems overkill for those things. What am i missing or not thinking of here?

I’m having this same compile problem with EspHome 2024.3.0

Any ideas?

Compiling .pioenvs/esp-keyboard-wake/lib3d0/ESP32 BLE Arduino/BLEAdvertising.cpp.o
In file included from .piolibdeps/esp-keyboard-wake/ESP32 BLE Arduino/src/BLECharacteristic.h:19,
                 from .piolibdeps/esp-keyboard-wake/ESP32 BLE Arduino/src/BLEDescriptor.h:14,
                 from .piolibdeps/esp-keyboard-wake/ESP32 BLE Arduino/src/BLE2902.h:13,
                 from .piolibdeps/esp-keyboard-wake/ESP32 BLE Arduino/src/BLE2902.cpp:15:
.piolibdeps/esp-keyboard-wake/ESP32 BLE Arduino/src/FreeRTOS.h:61:28: error: 'ringbuf_type_t' has not been declared
  Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
                            ^~~~~~~~~~~~~~

@Tomsdad did you ever resolve this? I am trying to use this and am getting the same error unfortunately…

I never did. I fell back to the old solution that I had that uses this: https://github.com/clubcotton/esphome/blob/main/playground/esp32_ble_keyboard.h

I had the same issue. Managed to resolve the compilation by setting use_default_libs to false.

In the example provided in the documentation, in the last line, replace true with false.

ble_keyboard:
id: mamontech_keyboard
name: “MamonTechKeyboard”
manufacturer_id: “MamonTech”
battery_level: 50
reconnect: true
buttons: true
use_default_libs: false

1 Like

Your change helped it get further, but now I get a new error:

Linking .pioenvs/esphome-web-c66cb0/firmware.elf
/config/.esphome/platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/esphome-web-c66cb0/src/main.cpp.o:(.literal._Z5setupv+0x670): undefined reference to `vtable for esphome::esp32_ble_server::NimBLEServer'
/config/.esphome/platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/esphome-web-c66cb0/src/main.cpp.o:(.literal._Z5setupv+0x674): undefined reference to `vtable for esphome::esp32_ble_server::NimBLEServer'
/config/.esphome/platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/esphome-web-c66cb0/src/main.cpp.o:(.literal._Z5setupv+0x678): undefined reference to `vtable for esphome::esp32_ble_server::NimBLEServer'
collect2: error: ld returned 1 exit status
*** [.pioenvs/esphome-web-c66cb0/firmware.elf] Error 1
========================= [FAILED] Took 26.31 seconds =========================

Any ideas?

Did you replace or download the external component? For me it works like this (replace the substitutions with your own :)):

# BLE Keyboard project
# https://github.com/dmamontov/esphome-blekeyboard

############# User configuration #############

substitutions:
  board: esp32-poe-iso #!! Include your own board
  mdns_name: my_ble_kb

  # Wifi credentials
  wifi_ssid: !secret wifi_ssid
  wifi_password: !secret wifi_password

  # OTA and API
  ota_password: "abcde"
  api_password: "FulG0C3MsqsWr01isFywdnO7azeXU67qIVXtJHBz9js="

  # Software configuration
  input_text_entity_id: "input_text.blekeyboard_new_message" # A string object for entering arbitrary text.


########### End user configuration ###########


############# Base configuration #############
esphome:
  name: $mdns_name
  comment: "https://github.com/dmamontov/esphome-blekeyboard"

esp32:
  board: $board

# Enable logging
logger:

# Enable OTA
ota:
  password: $ota_password
  platform: esphome

# Enable WiFi
wifi:
  #fast_connect: on
  ssid: $wifi_ssid
  password: $wifi_password
  
# Enable Home Assistant API
api:
  encryption:
    key: $api_password

########### End base configuration ###########

# Enable components
external_components:
#  - source:
#      type: local
#      path: ../components
  - source: github://dmamontov/esphome-blekeyboard

ble_keyboard:
  id: my_ble_keyboard
  name: $mdns_name
  manufacturer_id: "Apple"
  reconnect: true
  battery_level: 50
  buttons: true

text_sensor:
  - platform: homeassistant
    id: input_text
    entity_id: $input_text_entity_id
    internal: true

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    update_interval: 60s

button:
  - platform: restart
    entity_category: "config"
    name: "Restart"

  - platform: template
    name: "Release"
    id: key_release
    icon: "mdi:send"
    on_press:
      then:
        - ble_keyboard.print:
            id: my_ble_keyboard
            text: !lambda "return id(input_text).state;"
        - ble_keyboard.release: my_ble_keyboard

#  - platform: template
#    name: "Ctrl + A"
#    id: key_ctrl_a
#    icon: "mdi:vector-combine"
#    on_press:
#      then:
#        - ble_keyboard.combination:
#            id: my_ble_keyboard
#            delay: 8
#            keys:
#              - 0x80
#              - "a"

#  - platform: template
#    name: "Calc"
#    id: key_calc
#    icon: "mdi:calculator"
#    on_press:
#      then:
#        - ble_keyboard.press:
#            id: my_ble_keyboard
#            code:
#              - 0
#              - 2

  - platform: template
    name: "_Start advertising"
    id: start_advertising
    icon: "mdi:bluetooth-connect"
    on_press:
      then:
        - ble_keyboard.start: my_ble_keyboard

  - platform: template
    name: "_Stop advertising"
    id: stop_advertising
    icon: "mdi:bluetooth-off"
    on_press:
      then:
        - ble_keyboard.stop: my_ble_keyboard

However I am experiencing a different issue. After the ESP restarted, it is not connected anymore. From the looks of this thread no one else experienced that. Is this an isolated issue on my device? Or am I missing some configuration?

For those having trouble with devices (particularly Apple devices) not reconnecting after the ESP32 restarts, I’ve pushed some changes which seems to resolve the issue for me: GitHub - zachdekoning/esphome-blekeyboard: ESPHome BLE Keyboard

From doing some research, it seems that Apple devices don’t like the lack of security in the pairing process. The way around this is to set a proper pairing code, which seems to allow my iPad to properly reconnect when the ESP32 restarts.

You can try out these changes with the following:

external_components:
  - source: github://zachdekoning/esphome-blekeyboard

ble_keyboard:
  id: keyboard_id_here
  name: "Keyboard name here"
  ...
  use_default_libs: false # Must set this to false
  pairing_code: 123456 # Six digit pairing code

Note that from ESPHome 2025.10+ onwards, you need to manually enable bluetooth in the framework sdkconfig_options or else it will fail to compile:

framework:
  type: arduino
  sdkconfig_options:
    CONFIG_BT_ENABLED: "y"

Friends, I can’t upload this config to the new builder. Everything worked fine two years ago, but now it doesn’t. Please help me edit this if anyone sees an error.

Summary
 - source: github://zachdekoning/esphome-blekeyboard
        
ble_keyboard:
 id: esp32_bleKeyboard
 name: "Esp32_BleKeybrd_02"
 reconnect: true
 buttons: true
 use_default_libs: false
 #pairing_code: 123456

globals:
 - id: keyboard_enabled
   type: bool
   restore_value: no
   initial_value: "false"

switch:
 - platform: template
   name: Enabled
   icon: mdi:power
   lambda: |-
     if (id(keyboard_enabled)) {
       return true;
     } else {
       return false;
     }
   turn_on_action:
     - ble_keyboard.start: esp32_bleKeyboard
     - lambda: |-
         id(keyboard_enabled) = true;
   turn_off_action:
     - ble_keyboard.stop: esp32_bleKeyboard
     - lambda: |-
         id(keyboard_enabled) = false;

sensor:
 - platform: wifi_signal
   name: "WiFi Signal"
   update_interval: 60s

button:
 - platform: restart
   entity_category: "config"
   name: "Restart"

 - platform: template
   name: "Start advertising"
   id: start_advertising
   icon: "mdi:bluetooth"
   on_press:
     then:
       - ble_keyboard.start: esp32_bleKeyboard
 
 - platform: template
   name: "Stop advertising"
   id: stop_advertising
   icon: "mdi:bluetooth-off"
   on_press:
     then:
       - ble_keyboard.stop: esp32_bleKeyboard
           
 - platform: template
   name: "Next track"
   id: next_track
   icon: mdi:skip-next
   on_press:
     then:
       - ble_keyboard.press:
           id: esp32_bleKeyboard
           code:
             - 1
             - 0
       - delay: 100ms
       - ble_keyboard.release: esp32_bleKeyboard

 - platform: template
   name: "previous track"
   id: previous_track
   icon: mdi:skip-previous
   on_press:
     then:
       - ble_keyboard.press:
           id: esp32_bleKeyboard
           code:
             - 2
             - 0
       - delay: 100ms
       - ble_keyboard.release: esp32_bleKeyboard

 - platform: template
   name: "Play⁄Pause"
   id: play_pause
   icon: mdi:play-pause
   on_press:
     then:
       - ble_keyboard.press:
           id: esp32_bleKeyboard
           code:
             - 8
             - 0    
       - delay: 100ms
       - ble_keyboard.release: esp32_bleKeyboard

 - platform: template
   name: "Vol up"
   id: KEY_MEDIA_VOLUME_UP
   icon: mdi:volume-plus
   on_press:
     then:
       - ble_keyboard.press:
           id: esp32_bleKeyboard
           code:
             - 32
             - 0
       - delay: 100ms
       - ble_keyboard.release: esp32_bleKeyboard

 - platform: template
   name: "Vol down"
   id: KEY_MEDIA_VOLUME_DOWN
   icon: mdi:volume-minus
   on_press:
     then:
       - ble_keyboard.press:
           id: esp32_bleKeyboard
           code:
             - 64
             - 0    
       - delay: 100ms
       - ble_keyboard.release: esp32_bleKeyboard

 - platform: template
   name: "Stop Play"
   id: play_stop
   icon: mdi:stop
   on_press:
     then:
       - ble_keyboard.press:
           id: esp32_bleKeyboard
           code:
             - 4
             - 0
       - delay: 100ms
       - ble_keyboard.release: esp32_bleKeyboard ```

Crashes with ESPHome 2026.4.2:

WARNING Found stack trace! Trying to decode it
WARNING Decoded 0x400f22a3: ble_npl_mutex_pend at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/.piolibdeps/living-room-tablet-keyboard/NimBLE-Arduino/src/nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h:249
 (inlined by) ble_hs_lock_nested at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/.piolibdeps/living-room-tablet-keyboard/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs.c:187
WARNING Decoded 0x400f22c7: ble_hs_lock at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/.piolibdeps/living-room-tablet-keyboard/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs.c:245
WARNING Decoded 0x400f1fe7: ble_gatts_reset at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/.piolibdeps/living-room-tablet-keyboard/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gatts.c:3302
WARNING Decoded 0x400e967d: NimBLEDevice::createServer() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/.piolibdeps/living-room-tablet-keyboard/NimBLE-Arduino/src/NimBLEDevice.cpp:143
WARNING Decoded 0x400faf4f: BleKeyboard::begin() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/.piolibdeps/living-room-tablet-keyboard/ESP32 BLE Keyboard/BleKeyboard.cpp:118
WARNING Decoded 0x400d6c83: esphome::ble_keyboard::Esp32BleKeyboard::setup() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/src/esphome/components/ble_keyboard/ble_keyboard.cpp:25
 (inlined by) esphome::ble_keyboard::Esp32BleKeyboard::setup() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/src/esphome/components/ble_keyboard/ble_keyboard.cpp:17
WARNING Decoded 0x400dcce6: esphome::PollingComponent::call_setup() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/src/esphome/core/component.cpp:494
WARNING Decoded 0x40197b49: esphome::Component::call() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/src/esphome/core/component.cpp:260
WARNING Decoded 0x400dc830: esphome::Application::setup() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/src/esphome/core/application.cpp:82
WARNING Decoded 0x400e0d33: setup() at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/living-room-tablet-keyboard.yaml:97
WARNING Decoded 0x400d713f: esphome::loop_task(void*) at /home/user/Devops/esphome/config/.esphome/build/living-room-tablet-keyboard/src/esphome/components/esp32/core.cpp:66

Trying a few things to see what mitigates the problem…

OK 2026.2.4 works fine! So the problem with the BLE keyboard lies between 2026.2 and 2026.4

Incorrect. Diagnosing more…

Last ESPHome version that works stable is 2025.12.4. Sorry but no dice with any later versions.

Yeah in ESPHome 2026.1 there was a change made where it doesn’t include all arduino libraries by default (to reduce the compile time and binary size).

I have tried a bit of mucking around to get it working but haven’t figured it out yet.

For now I just compile it via ESPHome 2025.12.

If you’re running ESPHome as an addon in HA, I’d recommend adding the following repo and installing the old version (will run separately to the main version): GitHub - khenderick/esphome-legacy-addons: ESPHome addons from previous versions. Just in case something isn't working with the new version · GitHub

1 Like

Update — Fork migrated to ESP-IDF NimBLE

Hi everyone! I wanted to share that I’ve created a fork of this excellent component, migrated from the Arduino framework to ESP-IDF native NimBLE.

Why? The original relies on NimBLE-Arduino, which doesn’t build on newer ESP32 variants like the C3, C6, and H2. The ESP-IDF version uses the native esp_hid component, so it works across the full ESP32 family.

What changed:

  • Framework: arduino → esp-idf
  • BLE stack: NimBLE-Arduino → ESP-IDF esp_hid + NimBLE
  • No external libraries needed
  • Same YAML API (print, press, combination, start/stop, battery, etc.)

If you’re hitting build errors on ESP32-C3 or newer chips, give it a try:
:backhand_index_pointing_right: GitHub - adesanto84/esphome-blekeyboard: ESPHome BLE Keyboard ¡ GitHub

Full credit to @dmamontov (dmamontov (Dmitry Mamontov) · GitHub) for the original implementation — this fork just adapts the runtime layer for ESP-IDF compatibility.

1 Like

Thank you! I will probably soon move over to your fork because I'm stuck on ESPHome March 2026.