【ESP32】無線ボリュームコントローラーの作り方

マイコン/Arduino

PCに物理的なボリュームダイヤルが欲しいと思いませんか?

キーボードのボリュームキーを使っている人、マウスでボリューム調整をする人、いろいろいらっしゃると思います。特に、youtubeなどでは動画によって音量が全く違っていて、近所迷惑にならないように調整するのは結構面倒ですよね。

そこで、今回はESP32とロータリーエンコーダー1だけを使って音量調整をするためだけのデバイスを作る方法を紹介します。記事先頭に設定してある画像(銀色のダイヤルがあるもの)のようなものを作ってみました。今はディスプレイの下の、さっと手が伸びる位置に設置しています。

今回はESP32でロータリーエンコーダーを使ってボリュームコントローラーを作る方法を紹介します。以前、ProMicroでロータリーエンコーダーを使用する方法を説明しました。今回もその時の方針と同様ピン入力による割り込みは使用しないことを方針としています。これは入力割り込みで使用できるピンの制限を嫌ったためです。

ProMicroの場合にはmstimer2というライブラリを使用することでタイマーによる割り込みを使用することができましたが、ESP32ではこのライブラリは使用できないようです。そこで、今回は別スレッドによる入力ピンの監視という方法で実現してみました。基本的な考え方はProMicroの場合と同じです。

回路構成

ロータリーエンコーダーのA相とB相をESP32の16番と17番に接続しています。どこのピンでも問題ないので、下記図とは異なるピン番号に接続した場合はプログラムソースは適宜読み替えてください。

また、例によってチャタリング防止のためのコンデンサや特別な回路は使用していませんが、十分実用できるボリュームコントローラーになります。

ESP32とロータリーエンコーダーの接続

スケッチ(プログラム)例

(修正:キーコマンドの送信をpressからwriteに変更しました。こちらの方が意図しないキーの連打がされにくいです。ctlキーを押しっぱなしにしたい場合などはpressが良いと思います。)

#include <BleKeyboard.h>
#include <driver/adc.h>
#include <Wire.h>

BleKeyboard bleKeyboard("VolumeController");

//ロータリーエンコーダー設定
const int PinEnc1A = 16;
const int PinEnc1B = 17;
volatile byte enc1c;
volatile byte enc1p;
volatile byte enc1pp;
volatile int Counter1 = 0;

//コア0に割り当てるスレッド
TaskHandle_t thp[1];

void ROTALY(void *args) {
  while (1) {
    enc1c = (digitalRead(PinEnc1A) << 1) | (digitalRead(PinEnc1B));
    if (!(enc1c == enc1p)) {
      byte enc1 = enc1pp<<4 | enc1p << 2 | enc1c;
      if ( enc1 == 0b111000) {
        Counter1++; 
      }
      else if (enc1 == 0b001011) {
      Counter1--;
      }
      enc1pp = enc1p;
      enc1p = enc1c;
    }
    delay(1);//重要
  }
}

void setup() {
  pinMode(PinEnc1A, INPUT_PULLUP);
  pinMode(PinEnc1B, INPUT_PULLUP);

  xTaskCreatePinnedToCore(ROTALY, "ROTALY", 4096, NULL, 3, &thp[0], 0);
  bleKeyboard.begin();
}

void loop() {
  if (bleKeyboard.isConnected()) {
    if (Counter1 > 0) {
      bleKeyboard.write(KEY_MEDIA_VOLUME_UP);
      delay(10);
      Counter1--;
    }
    else if (Counter1 < 0) {
      bleKeyboard.write(KEY_MEDIA_VOLUME_DOWN);
      delay(10);
      Counter1++;
    }
  }
  Counter1=0;
  delay(2);
}

 

解説

ロータリーエンコーダの読み取りドクトリンについてはProMicroで紹介した方法と同じなので省略します。

ESP32にはCore0とCore1の2コア構成です(※)。通常のLoopはCore1で処理されるらしいのですが、これと並行してコア0を使うことができます。そこで、以下の部分の処理でROTARY()という関数をCore0で走らせるようにしています。

TaskHandle_t thp[1];
~
xTaskCreatePinnedToCore(ROTALY, "ROTALY", 4096, NULL, 3, &thp[0], 0);

ここで注意しなければならないのは、Core0はWifiやBTなどのシステムの処理を行っているらしいので関数ROTALY内でdelay(1)をいれてシステム処理をする隙を作らないとダメです。これを入れないとシステムのWDTが働いてリセットを繰り返してしまいます。エンコーダーを接続しているピンの監視を短いスパンでやりたいのでdelayMicrosecondsなどを使いたくなりますが、必ずdelayを使ってください。

Core0で実行させる関数内でdelay()を必ず入れること

※シングルコアのESP32もあるらしいです。ESP32は種類が多すぎてよくわからない…

コメント