Использование Nano 33 BLE в качестве HID-девайса (беспроводной клавиатуры), нужна помощь

tema_ra

✩✩✩✩✩✩✩
14 Мар 2023
4
0
Нужна помщь!

Я разрабатываю проект на базе новой для себя Arduino Nano 33 BLE.

У меня есть необходимость отправлять с нее в компьютер клавиатурные команды (напр., Shift+F3), по блютусу.

Обычно шоткаты прекрасно отправляются с помощью любой стандартной библиотеки (вроде USBKeyboard.h): зажимаешь одну кнопку, зажимаешь другую, ждешь 100мс, отпускаешь обе. К сожалению, на Nano 33 BLE все эти библиотеки не работают, так как у нее другой чип. Поэтому, задача по ее превращению в HID-устройство лично для меня нетривиальная.

В этой связи (а так же ввиду отсутствия опыта работы с блютусом до этого) я пошел на гитхаб, чтобы научиться на чужих проектах.

Я довольно быстро нашел ровно то что искал: имплементацию блютусного HID-устройства на базе Mbed:

Человек сделал веселую вещь: блютусную пишущую машинку из фильма "Сияние". После установки соединения с платой, в компьютер начинает приходить текст, буковка за буковкой:

Screenshot 2023-03-14 at 04.33.40.png

Тогда я подключил к Ардуине кнопку, и немного переделал проект, чтобы просто отправлять в компьютер символ по ее нажатию. Все получилось без проблем. Однако, я не вижу никакого способа заставить эту библиотеку посылать вместо символов именно клавиатурные команды.

Я такой не один:

Похоже что проект уже заброшен, и автор не выходит на связь. На форуме Arduino ответов тоже нашлось немного.

Если у кого-то был успешный опыт создания беспроводных HID-устройств на базе этого контроллера, поделитесь пожалуйста. Пока что у меня не получилось с легкостью овладеть этой темой.

А может быть, кто-то при беглом взгляде на проект мог бы навести меня на мысль, как заставить эту штуку работать.

В любом случае, буду очень признателен за участие
 
Изменено:

Старик Похабыч

★★★★★★★
14 Авг 2019
4,272
1,303
Москва
Если я правильно понял. то сначала надо послать нажатие шифта:
keydown(KEY_SHIFT )
потом F3 , возможно тоже через нажатие, код KEY_F3
а потом все отпустить
keyup()
 
  • Лойс +1
Реакции: tema_ra

tema_ra

✩✩✩✩✩✩✩
14 Мар 2023
4
0
Вы правы, я тоже надеялся что именно таким способом все должно получиться.


Вот часть моего кода, посвященная сабжу.

Я создаю bluetooth-клавиатуру, и задаю ей имя для отображения на компе (строка 3). Проверяю, чтобы с ней было соединение (16). Потом читаю состояние кнпопки, подключенной к пятому пину (14), и через простую проверку (19) отрабатываю нажатие на нее так, чтобы оно было однократным.

Мой проект:
#include "Nano33BleHID.h"
#include "signal_utils.h"
Nano33BleKeyboard bleKb("Waifupad");
int BT1 = 5;
unsigned long lastButtonPress = 0;
unsigned long screenLast = millis();
void setup() {
  pinMode(BT1, INPUT_PULLUP);
  bleKb.initialize();
  MbedBleHID_RunEventThread();
}

void loop() {
  int BT1S = digitalRead(BT1);
  auto *k2b = bleKb.hid();
  if (!bleKb.connected()) { 
    return;
  }
  if (BT1S == 0) {  
    if (millis() - lastButtonPress > 50) { 
    // CCW = Shift+F9
    k2b->sendCharacter('.');
    //k2b->keydown(KeySym_t(RIGHT_ARROW));
    } 
    lastButtonPress = millis();
  }  
}

Как видите, я сначала пробовал отправить в комп символ точки (26). С этим никаких проблем.

А вот попытка скомпилировать код, отправляющий нажатите на какую-нибудь кнопку описанным вами способом (27), выдает ошибку.

Вот ее код:

Error Message:
/Users/ra/Dropbox/Arduino/ble1/ble1.ino: In function 'void loop()':
/Users/ra/Dropbox/Arduino/ble1/ble1.ino:30:28: error: no matching function for call to 'HIDKeyboardService::keydown(FunctionKeyChar_t)'
     k2b->keydown(LEFT_ARROW);
                            ^
In file included from /Users/ra/Documents/Arduino/libraries/mbed-ble-hid-master/src/Nano33BleHID.h:13:0,
                 from /Users/ra/Dropbox/Arduino/ble1/ble1.ino:1:
/Users/ra/Documents/Arduino/libraries/mbed-ble-hid-master/src/services/HIDKeyboardService.h:90:8: note: candidate: void HIDKeyboardService::keydown(KeySym_t)
   void keydown(KeySym_t keysym);
        ^~~~~~~
/Users/ra/Documents/Arduino/libraries/mbed-ble-hid-master/src/services/HIDKeyboardService.h:90:8: note:   no known conversion for argument 1 from 'FunctionKeyChar_t' to 'KeySym_t'

exit status 1

Compilation error: no matching function for call to 'HIDKeyboardService::keydown(FunctionKeyChar_t)'

К сожалению, я не могу понять, какой именно аргумент я пропустил.

На всякий случай привожу здесь содержимое сервиса HIDKeyboardService.h, через который все это работает (94).

HIDKeyboardService.h:
#ifndef BLE_HID_KEYBOARD_SERVICE_H__
#define BLE_HID_KEYBOARD_SERVICE_H__

#if BLE_FEATURE_GATT_SERVER

#include "services/HIDService.h"

/* -------------------------------------------------------------------------- */

/* Ascii index of special function char. */
enum FunctionKeyChar_t {
    KEY_F1 = 128,       /* F1 key */
    KEY_F2,             /* F2 key */
    KEY_F3,             /* F3 key */
    KEY_F4,             /* F4 key */
    KEY_F5,             /* F5 key */
    KEY_F6,             /* F6 key */
    KEY_F7,             /* F7 key */
    KEY_F8,             /* F8 key */
    KEY_F9,             /* F9 key */
    KEY_F10,            /* F10 key */
    KEY_F11,            /* F11 key */
    KEY_F12,            /* F12 key */

    KEY_PRINT_SCREEN,   /* Print Screen key */
    KEY_SCROLL_LOCK,    /* Scroll lock */
    KEY_CAPS_LOCK,      /* caps lock */
    KEY_NUM_LOCK,       /* num lock */
    KEY_INSERT,         /* Insert key */
    KEY_HOME,           /* Home key */
    KEY_PAGE_UP,        /* Page Up key */
    KEY_PAGE_DOWN,      /* Page Down key */

    RIGHT_ARROW,        /* Right arrow */
    LEFT_ARROW,         /* Left arrow */
    DOWN_ARROW,         /* Down arrow */
    UP_ARROW,           /* Up arrow */
};

typedef uint16_t KeyCode_t;

/* Represent a key that could be sent by the HID. */
struct KeySym_t {
  enum Modifier {
    KEY_NONE  = 0,
    KEY_CTRL  = 1 << 0,
    KEY_SHIFT = 1 << 1,
    KEY_ALT   = 1 << 2,
  };

  /* Construct a Keysym directly by raw values. */
  KeySym_t(uint8_t _usage, uint8_t _modifiers)
    : usage(_usage)
    , modifiers(_modifiers)
  {}

  /* Construct a Keysym from a specific keycode. */
  explicit KeySym_t(KeyCode_t _keycode);

  uint8_t usage;
  uint8_t modifiers;
};

/**
* BLE HID Keyboard Service
*
* @par usage
*
[LIST]
[*]When this class is instantiated, it adds a keyboard HID service in
[*]the GattServer.
[/LIST]
*
[LIST]
[*]@attention Multiple instances of this hid service are not supported.
[*]@see HIDService
[/LIST]
*/
class HIDKeyboardService : public HIDService {
public:
  HIDKeyboardService(BLE &_ble);

  ble::adv_data_appearance_t appearance() const override {
    return ble::adv_data_appearance_t::KEYBOARD;
  }

  /* Return the KeySym for a specific character. */
  KeySym_t charToKeySym(unsigned char c) const;

  /* Send a press & release report for a single character. */
  void sendCharacter(unsigned char c);

  /* Register a down report for the specific keysym. */
  void keydown(KeySym_t keysym);

  /* Register a release report. */
  void keyup();
};

/* -------------------------------------------------------------------------- */

#endif // BLE_FEATURE_GATT_SERVER

#endif // BLE_HID_KEYBOARD_SERVICE_H__

Возможно, опытный глаз сразу заметит, в чем тут дело
 
Изменено:

Старик Похабыч

★★★★★★★
14 Авг 2019
4,272
1,303
Москва
Да, там не так все очевидно, как казалось. Передавать в keydown надо объект, а не код.
C++:
KeySym_t::KeySym_t(KeyCode_t _keycode)
  : usage(0)
  , modifiers(0)
{
  _keycode &= KEYCODE_MASK;
 
  if (_keycode > ALTGR_MASK) {
    _keycode -= ALTGR_MASK;
    modifiers |= Modifier::KEY_ALT;
  }
  if (_keycode > SHIFT_MASK) {
    _keycode -= SHIFT_MASK;
    modifiers |= Modifier::KEY_SHIFT;
  }

  usage = (_keycode & 0xff);
}
Возможно подойдет так:
k2b->keydown(KeySym_t(LEFT_ARROW));
 

tema_ra

✩✩✩✩✩✩✩
14 Мар 2023
4
0
Спасибо! В такой форме код стал компилиться.

Однако, нажатия в комп не передаются.
  • Пробовал подключаться к пк (Win10), и к макбуку на м1 (12.5.1)
  • Пробовал передавать RIGHT_ARROW и F5
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,272
1,303
Москва
Если посмотреть исходник, то отсылка символа идет вот по такой схеме:
C++:
keydown(keysym);
SendReport();
keyup();
SendReport();
Может добавить отсылку надо ?
 

tema_ra

✩✩✩✩✩✩✩
14 Мар 2023
4
0
Хах, интересно!

Оно заработало, но с оговорками 😅

Я заменил в своем оригинальном коде 22 строку (которая отсылала один символ k2b->sendCharacter('.');) на вот такой модуль, по вашему совету:

Модуль отсылки нажатия F5:
    k2b->keydown(KeySym_t(KEY_F5));
    k2b->SendReport();
    k2b->keyup();
    k2b->SendReport();

Что теперь происходит на компе, если нажать на кнопку?
  • На маке в блокноте печатается символ ƒ (того же эффекта можно добиться комбинацией клавиш alt+f)
  • На ПК на один кадр пропадает курсор мыши (т. е. какое-то нажатие в комп приходит, но отследить его с помощью утилиты OnScreen Keyboard мне не удалось)
Если попробовать отправить последовательность из двух нажатий подряд, происходит то же самое.

Последовательность из F5 и F4:
    k2b->keydown(KeySym_t(KEY_F5));
    k2b->SendReport();
    k2b->keydown(KeySym_t(KEY_F4));
    k2b->SendReport();
    k2b->keyup();
    k2b->SendReport();

(Причем, F5 у меня в данном примере стоит не в конце, а значит печатается не что-то последнее пришедшее.)

Если попробовать вместо KEY_F5 поставить KEY_CTRL, то код не будет компилиться, со следующей ошибкой:

Ошибка компиляции кода отправки клавиши Ctrl:
/Users/ra/Dropbox/Arduino/ble1/ble1.ino: In function 'void loop()':
/Users/ra/Dropbox/Arduino/ble1/ble1.ino:27:27: error: 'KEY_CTRL' was not declared in this scope
     k2b->keydown(KeySym_t(KEY_CTRL));
                           ^~~~~~~~
/Users/ra/Dropbox/Arduino/ble1/ble1.ino:27:27: note: suggested alternative: 'KEY_HOME'
     k2b->keydown(KeySym_t(KEY_CTRL));
                           ^~~~~~~~
                           KEY_HOME

exit status 1

Compilation error: 'KEY_CTRL' was not declared in this scope

Кажется, такие модификаторы как Ctrl, Shift и Alt отправляются каким-то иным способом. (Не очень понятно причем, как прожимать сложносоставные конструкции, типа Ctrl+Alt+Shift+F5)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,272
1,303
Москва
Клавиш шифт, контрл и альт являются модификаторами нажатия (из приведенного вами же куска кода в сообщении 3), там же есть конструктор для создания нужного с модификатором:
k2b->keydown(KeySym_t(KEY_HOME,KEY_CTRL));