Seeeduino_XIAOでArduino Keyboard.h互換ライブラリを利用してMicrosoft Teamsミュートキーを作製する


初めに

きっかけは…

 昨今の時流なのか、最近特にMicrosoft TeamsによるWeb会議が増えているのですが、発言していないときは自分の鼻息やら通信帯域やらに気を使ってミュートにするようにしています。
が、頻繁にミュートを切り替えているうちに、いくつか困り事が出てきました。

  • 会議漬けで疲れてくると間違えて挙手ボタンを押してしまい、慌てて解除するも変な注目を浴びてしまう

  • ミュート解除を忘れて相手に聞こえず、ただの呟きになってしまう

  • 今現在、ミュートになっているのか、ぱっと見だと判りづらい

 Teamsにはミュートのキーボードショートカットもあるにはあるのですが、CTRL_Shift_M という非常に押しにくいキーバインドになっていて、変更もできない模様。
 ヘッドセットケーブルの途中に付いている物理マイクスイッチだとTeams側のミュートとは連動せず、Windows側のマイクミュートは、組み合わせによってはTeamsと連動するのですが、 Fn+8などに割り当てられており、こちらも頻繁に切り替えるには面倒なので、外付けミュート専用キーのようなものを探していました。

1キーは市販されていたが…

 最初に探したのはミュート機能の付いている動画配信用の音声ミキサーでしたが、流石に値段が高いしサイズも大きいので見送ることに。
 続いて 同人向けの自作1キー組み立てキットが販売されていたのですが、売り切れでガッカリ。ただ良く見るとArduino_LeonardoとキーSWを組み合わせたシンプルな構成だったので、これなら自分でもできそうだと思い、さっそくアマゾンで類似品を物色。
結果、USB-HIDに対応していて、かつサイズが非常に小さいSeeeduino_XIAOというArduino互換ボードを発見したので、これとキーSWを組み合わせて自作することに。

検討/利用したライブラリ

1.メーカー標準ライブラリ?

さて、このSeeeduino XIAOですが、Arduino互換ということで、いつも通りボードマネージャーで対応したボード(Ver 1.8.0) を読み込んで、サンプルを書き込めば動くだろうと高を括っていたのですが、そうは問屋が卸さず...
 XIAO用のスケッチ例と書かれている中にあるUSBHost > KeyboardController を書き込んだところ、ユーザーLEDもCOMポートも消えてしまい、文鎮状態になってしまうじゃありませんか!
いやいや、そもそもサイズが小さすぎて文鎮にもなりませんから...
 気を取り直してピンセットでファームリセット用のランドパターンを素早く2度タップして、すぐに復旧できるところがせめてもの救いですが、何度やってもミニ文鎮にしかならず。一体何が悪いのか?
 メーカーWeb上ではCircuitPython経由でHIDを実装している事例はあったものの、このライブラリで動いているという事例を見つけられず、逆に今回のようにうまくいかなかったという記述が目に付くので、これはどうやらダメかもしれないと別の手を探すことに。
(*メーカー側もバグを認識している模様で、中国の国慶節明けにメンテする予定らしいですね。)

2. Adafruit TinyUSB Library for Arduino

 続いて、AdafruitのTinyUSBライブラリが使えそうだったので、早速入れてみたところ、キーのPressとReleaseについてはきちんと動作したので、これでOKかなと思いましたが、どうやら文字列関連の送出機能が未実装の様子。折角なので、よく使うフレーズを登録しておいて、1キーで入力出来たら便利かも...と少し欲が出てきたので、もう少し何か無いか探してみることに。

3. TinyUSB Mouse and Keyboard library

 もう少し見て回ったところ、どうやらこちらのライブラリは、ArduinoのKeyboard.hライブラリと互換性があり、標準の 'print' をオーバライドしているようで、'println' 等が普通にが使えそうだと分かったので、さっそくテストしてみる事に。結果、動作はするものの、連続して文字列を送ると、たまにPC側で取りこぼしが発生する模様で、バッファーの管理に癖があるのか、こんなものなのか原因ははっきりしなかったものの、ウエイトを適切に入れてあげれば大丈夫かなと思い、まずこちらを使って動作プログラムを作ってみる事にしました。(利用したのは、Ver 0.10.0)

キーボートプログラム

2キー仕様でハードを準備したのですが、まずは1キー専用で進める事にして、後で拡張する方向にしました。
仕様としては、Microsoft Teamsのミュートキーとして、

  1. キーSWを押す度にCTRL_Shift_Mが送られる。
  2. キーを押した瞬間にキーがPressされ、一定時間後にキーをReleaseする動作。
  3. キーを押し続けても、一定時間以上キーを離すまでは1回しか送信しない。(長押し+チャタリング対策)

となるように設定。書いたコードはこちら。
他にも処理を追加できるように、メインループ内には極力delay()を使わないようにしています。
(Keyboardライブラリ内で少し使われている程度)

Teams_Mute_Key_single_1.2

#include <TinyUSB_Mouse_and_Keyboard.h>

uint8_t pins[]    = { A9 , A10 };
uint8_t pincount = sizeof(pins) / sizeof(pins[0]);

uint8_t val1;
unsigned long ChatteringDelayTime = 30;     // for delay time(ms)
unsigned long KeyOffDelayTime = 100;
unsigned long afterPressKey;
unsigned long afterReleaseKey;
boolean session = false;
boolean KeyOn = false;

void setup() {
  Keyboard.begin();   //Unlike Arduino Keyboard.h, you must use begin.
  // led pin
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  // Set up pin as input
  for (uint8_t i = 0; i < pincount; i++) {
    pinMode(pins[i], INPUT_PULLUP);
  }
}

void loop() {
  val1 = digitalRead(pins[1]);

  if (val1 == LOW ) {       // スイッチが押された
    if (session == false ) { // 初回
      sendKeys_Mute();
      KeyOn = true;
      session = true;
      afterPressKey = millis();
    }
    afterReleaseKey = millis(); // スイッチを押し続けている
  }
  // スイッチが押されて一定時間経過
  if (KeyOn == true && millis() - afterPressKey >= KeyOffDelayTime) {
    KeyOff();
    KeyOn = false;
  }
  // スイッチを押して離した後、一定時間経過
  if (session == true && millis() - afterReleaseKey >= ChatteringDelayTime) {
    session = false;
  }
}

void sendKeys_Mute() {
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_SHIFT);
  Keyboard.write('M');
  digitalWrite(LED_BUILTIN, LOW);
}

void KeyOff() {
  Keyboard.releaseAll();
  digitalWrite(LED_BUILTIN, HIGH);
}

製作・組み立て

今回はGNDに近くて配線しやすかった、D10ピンをキーSW入力として採用。

3Dプリンタで箱を作って、とりま完成!

キーSWはCherry MXの青軸を搭載。動作も良好でした。
2つ目のキーSWは今後の拡張用で、左右同時押しした時などでも何か機能を持たせるつもりです。
後は、Teamsがミュート状態のときにマスコットのLEDが点滅するようにできれば完璧なのですが、
こちらはまだ研究中です。