ESP32マイコンのBLEを使い、ゲームコントローラの製作を行いました。
右図は完成品です。PlayStationの操作ボタンをイメージして、設計しています。
単3電池用の電池ケースを内蔵しています。
機能と回路設計
基本はボタン10個です。操作したい本体が、ボタンのオン・オフで動作するので、アナログジョイスティックはありません。
ボタンの他に、LEDを内蔵しています。電池の電圧を測定し、電池の容量が低下して、電圧が下がると、LEDで表示させる機能です。
このため、回路はLED(電流制限抵抗を直列接続)とスイッチをESP32マイコンに接続しているだけです。
ケース設計
基板を内蔵して完成となります。
プログラミング
ゲーム本体とコントローラともESP32マイコンです。ゲーム本体をマスター、コントローラをスレーブとして、BLEのUART通信機能を使い通信を行います。マスター側からスレーブ側に要求があると、スイッチ情報とバッテリ情報を返すプログラムを作成します。
(1)ライブラリ「BluetoothSerial」
ESP32マイコン同士をBLEの通信「BluetoothSerial」ライブラリをインクルードすると、スケッチ例としてマスタ側「SerialToSerialBTM」、スレーブ側「SerialToSerialBT」が利用できます。
それぞれのプログラムを2台のESP32マイコンに書き込むと、お互いにペアリングして、通信できるようになります。
それぞれのプログラムを2台のESP32マイコンに書き込むと、お互いにペアリングして、通信できるようになります。
右が通信している様子です。それぞれのシリアルモニタから、メッセージを入力すると、相手のモニタに表示されます。
(2)スイッチ情報をBLE通信で送る
まずは、コントローラ(スレーブ側)に、スイッチ情報を読み取り、1秒ごとにマスター側に送信するプログラムに改良します。
プログラムの追加は、ポート設定、ポート読み込み・BLE送信です。スレーブ名も「ESP32-Controller-Slave」に変更しました。
#define LED_R 23
#define LED_Y 17
#define LED_B 16
#define SW_START 33
#define SW_SELECT 32
#define SW_LU 27 // Left side UP Button
#define SW_LD 13 // Left side DOWN Button
#define SW_LR 12 // Left side RIGHT Button
#define SW_LL 14 // Left side LEFT Button
#define SW_RU 22 // Right side UP Button
#define SW_RD 18 // Right side DOWN Button
#define SW_RR 19 // Right side RIGHT Button
#define SW_RL 21 // Right side LEFT Button
#define DC_VOLT 35
#include "BluetoothSerial.h"
BluetoothSerial SerialBT;
String device_name = "ESP32-Controller-Slave";
void setup() {
Serial.begin(115200);
SerialBT.begin(device_name);
Serial.printf("The device with name \"%s\" is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str());
pinMode(LED_R,OUTPUT);
pinMode(LED_Y,OUTPUT);
pinMode(LED_B,OUTPUT);
pinMode(SW_START,INPUT_PULLUP);
pinMode(SW_SELECT,INPUT_PULLUP);
pinMode(SW_LU,INPUT_PULLUP);
pinMode(SW_LD,INPUT_PULLUP);
pinMode(SW_LR,INPUT_PULLUP);
pinMode(SW_LL,INPUT_PULLUP);
pinMode(SW_RU,INPUT_PULLUP);
pinMode(SW_RD,INPUT_PULLUP);
pinMode(SW_RR,INPUT_PULLUP);
pinMode(SW_RL,INPUT_PULLUP);
pinMode(DC_VOLT,INPUT);
}
void loop() {
if (SerialBT.available()) {
Serial.write(SerialBT.read());
}
int sw_lu=digitalRead(SW_LU);
int sw_ld=digitalRead(SW_LD);
int sw_ll=digitalRead(SW_LL);
int sw_lr=digitalRead(SW_LR);
int sw_ru=digitalRead(SW_RU);
int sw_rd=digitalRead(SW_RD);
int sw_rl=digitalRead(SW_RL);
int sw_rr=digitalRead(SW_RR);
int sw_str=digitalRead(SW_START);
int sw_sel=digitalRead(SW_SELECT);
int dcv=analogRead(DC_VOLT);
SerialBT.printf("%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d:%4d\n",
sw_lu, sw_ld, sw_lr, sw_ll, sw_ru, sw_rd, sw_rl, sw_rr, sw_str, sw_sel, dcv);
delay(1000);
}
マスター側は、スレーブ名を変更するだけで受信できます。
スイッチ情報は、単純に「0」、「1」で送信しています。
1秒ごとに、スイッチ情報を受信していることが分かります。最後の4桁はバッテリ情報です。
これをゲーム本体に組み込むことで、コントローラ機能として働きます。
(3)送受信の改良
これで送受信できるのですが、よくシリアルモニタを見ていると、タイムラグが発生していることが分かります。
void loop() {
if (Serial.available()) {
SerialBT.write(Serial.read());
}
if (SerialBT.available()) {
Serial.write(SerialBT.read());
}
delay(20);
} 上記の受信部のプログラムを見ると、データがあるか判断(available関数)して、データがあるときには、受信(read関数)し、相手に送信(write関数)する処理をしています。ここでは、1バイトごとしか受信処理ができません。
このためスイッチ情報を送るプログラムでは、相手にスイッチ情報を15バイト(シリアルモニタの1行あたりの文字数)送信しています。つまり、15文字×20mS=300mS(0.3秒)受信にかかる計算になります。
ここの部分はプロトコルを設計し、「16進数で送信する」、「改行を入れ、改行まで受信させる」などの改良が必要です。
改良の例として、サンプル例の「Communication」にある「SerialEvent」のSerialEvent関数は参考になりそうです。
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag so the main loop can
// do something about it:
if (inChar == '\n') {
stringComplete = true;
}
}
}
このサンプル例では、改行コード「\n」を受信するまで定期的に処理し、改行コードを受信したら、受信フラグ「stringComplete」を「true」にする処理をしています。これで1文字ずつではなく、スイッチ情報ごとの受信ができるようになります。
これを組み入れたマスター側のプログラム(loop部)が次です。
boolean stringComplete=false;
String inputString;
void loop() {
serialBTEvent();
if (stringComplete) {
Serial.println(inputString);
inputString = "";
stringComplete = false;
}
delay(20);
}
void serialBTEvent() {
while (SerialBT.available()) {
char inChar = (char)SerialBT.read();
if (inChar == '\n') {
stringComplete = true;
}else{
inputString += inChar;
}
}
}
プログラムを動作させると、受信したときの表示処理が速くなっていることが分かります。
このプログラムは、まだ読み込むサンプルなので、16進にして送信するなどの処理を行っていませんが、スイッチ情報を計算で16進にして、SerialBT.printf関数のフォーマット指定子でで「%03x」とすると、16進で送信できます。
右の図では、スイッチ10ビット、バッテリ情報8ビットで処理しています。




0 件のコメント:
コメントを投稿