Help Needed: ESP32 BLE Keystroke Issues with Android TV

Hi everyone,

I'm working on a project to send keystrokes via Bluetooth LE from an ESP32 to an Android TV, but I've run into two problems that I can’t seem to solve.

The basic setup works as follows: for testing, I send a keystroke using a simple HTTP request (e.g., curl -X GET "http://<ESP-IP>/send?keycode=102"). This successfully sends a power on/off command to the Android TV box.

** My Issues:**

  1. Reconnection after ESP32 Power Cycle:
    If I power cycle the ESP32, it reconnects to the Android TV box, but keystrokes are no longer processed by the TV. The ESP32 logs indicate that the keystrokes are being sent, but nothing happens on the TV. To fix this, I have to unpair and re-pair both devices. After re-pairing, everything works fine—until the ESP32 is power cycled again.
  2. Deep Standby Mode on Android TV:
    When the Android TV enters deep standby, the Bluetooth connection to the ESP32 breaks, and I cannot send the power-on command. However, regular remote controls can still wake the box, even when it’s in deep standby.

I’m unsure how to tackle these challenges and would greatly appreciate any advice or guidance. My code is attached for reference. I’m using PlatformIO with the Arduino framework.

Thank you in advance for your help!

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLEHIDDevice.h>
#include <HIDTypes.h>
#include <HIDKeyboardTypes.h>
#include <ESPAsyncWebServer.h>
#include <WiFi.h>

BLEHIDDevice* hid;
BLECharacteristic* input;
BLEServer* pServer;

const char* ssid = "MyAp";
const char* password = "1234";

AsyncWebServer server(80);

class MyServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    Serial.println("Gerät verbunden");
  }

  void onDisconnect(BLEServer* pServer) {
    Serial.println("Verbindung getrennt, starte Werbung neu...");
    pServer->startAdvertising();
  }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Starte BLE HID Tastatur...");

  // WiFi-Verbindung herstellen
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Verbinde mit WiFi...");
  }
  Serial.println("WiFi verbunden");

  // BLE initialisieren
  BLEDevice::init("ESP32 Keyboard");
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  hid = new BLEHIDDevice(pServer);
  input = hid->inputReport(1); // Report ID 1

  hid->manufacturer()->setValue("ESP32");

  uint8_t reportMap[] = {
    0x05, 0x01,                // Usage Page (Generic Desktop)
    0x09, 0x06,                // Usage (Keyboard)
    0xA1, 0x01,                // Collection (Application)
    0x85, 0x01,                // Report ID (1) - Keyboard Report
    0x05, 0x07,                // Usage Page (Key Codes)
    0x19, 0xE0,                // Usage Minimum (Left Control)
    0x29, 0xE7,                // Usage Maximum (Right Control)
    0x15, 0x00,                // Logical Minimum (0)
    0x25, 0x01,                // Logical Maximum (1)
    0x75, 0x01,                // Report Size (1)
    0x95, 0x08,                // Report Count (8)
    0x81, 0x02,                // Input (Data, Variable, Absolute) - Modifier byte
    0x95, 0x01,                // Report Count (1)
    0x75, 0x08,                // Report Size (8)
    0x81, 0x01,                // Input (Constant) - Reserved byte
    0x95, 0x05,                // Report Count (5)
    0x75, 0x01,                // Report Size (1)
    0x05, 0x08,                // Usage Page (LEDs)
    0x19, 0x01,                // Usage Minimum (Num Lock)
    0x29, 0x05,                // Usage Maximum (Kana)
    0x91, 0x02,                // Output (Data, Variable, Absolute) - LED report
    0x95, 0x01,                // Report Count (1)
    0x75, 0x03,                // Report Size (3)
    0x91, 0x01,                // Output (Constant) - LED report padding
    0x95, 0x06,                // Report Count (6)
    0x75, 0x08,                // Report Size (8)
    0x15, 0x00,                // Logical Minimum (0)
    0x25, 0xFF,                // Logical Maximum (101)
    0x05, 0x07,                // Usage Page (Key Codes)
    0x19, 0x00,                // Usage Minimum (0)
    0x29, 0xFF,                // Usage Maximum (255)
    0x81, 0x00,                // Input (Data, Array) - Key array (6 keys)
    0xC0                      // End Collection
  };

  hid->reportMap(reportMap, sizeof(reportMap));
  hid->startServices();

  BLEAdvertising* pAdvertising = pServer->getAdvertising();
  pAdvertising->setAppearance(HID_KEYBOARD);
  pAdvertising->addServiceUUID(hid->hidService()->getUUID());
  pAdvertising->start();

  Serial.println("BLE HID Tastatur gestartet, warte auf Verbindung...");

  // HTTP-Server konfigurieren
  server.on("/send", HTTP_GET, [](AsyncWebServerRequest *request){
    if (request->hasParam("keycode")) {
      String keycodeStr = request->getParam("keycode")->value();
      uint8_t keyCode[8] = {0};
      keyCode[2] = keycodeStr.toInt(); // Konvertiere String zu int
      input->setValue(keyCode, sizeof(keyCode));
      input->notify();
      Serial.println("Keycode gesendet: " + keycodeStr);
           // Taste loslassen
      delay(100); // Kurze Verzögerung
      memset(keyCode, 0, sizeof(keyCode));
      input->setValue(keyCode, sizeof(keyCode));
      input->notify();
      Serial.println("Taste losgelassen");

      request->send(200, "text/plain", "Keycode gesendet: " + keycodeStr);
    } else {
      request->send(400, "text/plain", "Fehlender Parameter: keycode");
    }
  });

  server.begin();
}

void loop() {
  // Keine Datenübertragung in der loop-Funktion
}

Most "regular remotes" use IR to send commands, not bluetooth. Is your remote that can wake up your TV sending IR commands? You can get an IR LED and use the IRRemote library to decode commands and replicate them, if that is the case.

1 Like

Nope. There is a IR LED in the remote, but it is not used to communicate to the Android Box. It is easy to check, you can block the ir diode, anyway the box is starting.
Btw: By my knowledge it is the same by Amazon Fire Tv, for example, only BT no IR.

Hi dezihh,
I used your sample code from above in my environment (Espressif ESP32-S3 and Wai.Pu TV 4k Stick as Google TV both on same Home WLAN) I modified the SSID and the PWD of course.
However I did not get it to run it correctly. Following is the Serial Monitor output:

Starte BLE HID Tastatur...
Verbinde mit WiFi...
WiFi verbunden
BLE HID Tastatur gestartet, warte auf Verbindung...
(1)
Keycode gesendet: 102
Taste losgelassen
(2)
Gerät verbunden
E (14128481) BT_SMP: smp_calculate_link_key_from_long_term_key failed to update link_key. Sec Mode = 2, sm4 = 0x00
E (14128482) BT_SMP: smp_derive_link_key_from_long_term_key failed

E (14128488) BT_BTM: btm_proc_smp_cback received for unknown device
E (14128630) BT_BTM: BTM_GetSecurityFlags false

E (14128630) BT_BTM: BTM_GetSecurityFlags false

---- several of these lines -----

E (14129912) BT_BTM: BTM_GetSecurityFlags false

E (14129913) BT_GATT: GATT_INSUF_AUTHENTICATION

E (14130002) BT_BTM: BTM_GetSecurityFlags false

E (14130003) BT_GATT: gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION,handle 0043, perm 0022
(3)
Keycode gesendet: 102
Taste losgelassen

It starts well until (1) and then waits. OK so far.
When I issue an curl -X GET "http://192.168.2.x/send?keycode=102 it runs until (2).
When I connect the ESP32 via my normal remote control with the Wai.Pu TV via its Settings --> Keyboard dialogs, it runs until (3) with the shown errors.
Finally another curl..... or any other curls ... with different keycodes do nothing on the stick, namely on the screen. No reaction at all.

What is different in your environment? What is wrong in mine or what do I miss? Anything I need to setup before? Any help is welcome.

Hi Roland,

in the end I gave up. I was unable to reactivate the connection after the device was in deep standby or went offline.
All I received was a vague indication that Google was using a stripped down protocol for my request. I haven't figured out how to implement this. If there is any new information there, I would be very happy about it and fire up the soldering iron again, but for now the things are well stored in the cupboard. :cry:
According to my interpretation, your problem lies in another area that I didn't have, namely security.
The error messages indicate that there are problems with the security authentication, which is necessary to establish a secure connection between the ESP32 and the target device. Make sure that both the ESP32 and the target device (Waipu.tv) meet the same security requirements. You may need to adjust the security level on your ESP32.
Google says something like

BLESecurity *pSecurity = new BLESecurity();
  pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND);

could help.
Good luck, and if you or anybody else solve my problem, I would be happy to get some feedback :slight_smile:

Cheers

Hi dezihh,

meanwhile I have a solution for my connection between the ESP32S3 and the Wai.Pu TV.

I changed my C++ code from BLE to NimBLE. Both the ESP32S3 and the NimBLE libraries only support Bluetooth Low Energy (BLE) and not Bluetooth Classic (BR/EDR).

I'm currently in the situation, that some key strokes are already transmitted from ESP32S3 to the Wai.Pu TV (Up/Down). This is a breakthrough on my side. I used the Smartphone APP nRF Sniffer to exactly record, what advertising the original Remote Controller issues and re-programmed this byte-by-byte in C++. Except some additional fields my program send more than the original (and the name of the device: B18F --> ESP), my program now does the same advertisement as the original. And this is now accepted by the Wai.Pu TV device. Pairing works perfectly resulting in two Remote Controls (the original and the ESP32S3) working.

Now I try to get the remaining key codes implemented (OK, PROG, INFO, Mute, etc.).

I'm searching for a good list. I can find several list with "normal" keyboard codes but non with the special Wai.Pu TV ones.

Back to your problem, it is also still open on my side to get the connection or re-connection permanently (i.e. pairing works, but bonding not yet). So I still can not help you with you problem.

Lets see.......

I'm a little bid further on. With your program from the first post, I only can sent the key strokes UP + DOWN + LEFT + RIGHT. But non of the other keys, e.g. OK. I need to use a different more complex Report Map.
Does still not solve your problem, but brings me further on into a situation, where I may get stocked as you.

Hello Roland,
I'm glad to hear about your progress. Indeed, I also worked with two report maps. Like you, I also discovered that the Bluetooth libraries (or lack thereof) can perform various functions, but as I said, I couldn't get the device to wake up from deep standby. This, coupled with the buggy Bluetooth libraries, didn't feel right in the end. Here's another tip from me: I would start solving the problem, at least if the Roku also has deep standby.
I've loosely documented my findings in a Git repo. It's all untested and unstructured, but maybe it will help you: GitHub - dezihh/Remote4HA

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.