2020年11月29日日曜日

エンコーダー(可変抵抗)が付いた有線コントローラーを作ってみた


背景

以前有線コントローラーを作りましたが、エンコーダーを使いたい場面があったので、それが付いた物を作ってみました。
コントローラーのマイコンとして利用したATmega328PBはI2Cのバスが2つ使えたので、LCDも付けてみました。

スイッチサイエンスで購入できます。
I2cControlPanel

使ったもの

  • I2cControlPanel
    今回紹介するコントローラーです。
  • Groveケーブル
    長さは3mまで色々あります。今回は1mのケーブルを使いました。
  • M5Stack
    LCD、Groveのコネクタ、バッテリーなどが付いている便利装置です。
    今回はBasicを利用しました。
  • M5Stackにプログラムを書き込めるPC
    Arduino IDEとPlatformIOで動作確認しました
  • I2cControlPanelのArduinoライブラリ
    I2cControlPanel_asukiaaaというライブラリを公開しています。
    設定方法はプログラムの解説時に説明します。

物理的な機能一覧

これらの部品や基板のパターンが付いています。
  • Grove I2Cコネクタ 2つ
  • リセットボタン
  • I2C向け5.08mmピッチの穴
  • アドレス設定用パッド(ジャンパ)
  • ジョイスティック左
  • ジョイスティック右
  • LCD 8x2
  • LED0
  • LED1
  • LED2
  • LED3
  • ボタン左0
  • ボタン左1
  • ボタン右0
  • ボタン右1
  • エンコーダー0
  • エンコーダー1
  • スライドスイッチ0
  • スライドスイッチ1


I2C接続のためのGrove端子が2つ付いているので、数珠繋ぎ可能です。
5.08mmピッチの穴を配置しているので、ターミナルブロックを付けたり信号線をはんだ付けしたりすることで、I2Cの信号を引き出せます。


標準のI2Cアドレスは0x40 (10進数で64)を設定しています。
パッドをはんだで盛って繋げたりカッターで切断したりすることで、アドレスを変更可能です。


ジョイスティックは左下が0で、右上が255となり、何も操作していない時は128前後の値になります。


エンコーダーの値は最小が0で最大が255です。


LCDとLEDに情報を表示できます。


裏側はこうなっています。


横から見るとこうなっています。
背の高い部品は高い順に、エンコーダー、ジョイスティック、ボタンです。


大きさ

10x10cmです。
四隅と左右の中央にM4ネジ向けの固定穴を配置しています。


プログラムでの読み書き

ライブラリを設定

Arduino IDEの場合はライブラリマネージャーからI2cControlPanel_asukiaaaをインストールしてください。


PlatformIOの場合は、platformio.iniのlib_depsにI2cControlPanel_asukiaaaを追加してください。

platformio.ini
lib_deps = I2cControlPanel_asukiaaa

サンプルプログラムの全体像の参照先

サンプルコードを全て表示すると長くなるので、全体を見たい場合はどちらかで確認してください。

  • githubに上げているサンプルコード
  • Arduino IDEのスケッチ例
    I2cControlPanel_asukiaaaをインストールした状態で「ファイル -> スケッチ例 -> I2cControlPanel_asukiaaa -> PrintOnSerial」を選ぶと開けます。


初期化

ライブラリを読み込んで、コントローラーを宣言します。
setupでbeginを呼ぶと、内部でWire.begin()が呼ばれ、I2C通信出来る状態になります。
#include <I2cControlPanel_asukiaaa.h>

I2cControlPanel_asukiaaa::Driver controlPanel;

void setup() {
controlPanel.begin();
}

コントローラーには標準で0x40がアドレスとして割り当てていますが、アドレス設定用のパットを接続切断してアドレスを変更した場合は、宣言の第一引数でアドレスを渡せます。
I2cControlPanel_asukiaaa::Driver controlPanel(0x41); // custom address

情報の読み書き

読み書きは I2cControlPanel_asukiaaa::Info のインスタンスをwriteやread関数に渡して行います。
I2cControlPanel_asukiaaa::Info info;

writeの結果は関数の戻り値で受け取れます。
  int writeState = controlPanel.write(info);
if (writeState == 0) {
Serial.println("Write info about leds and lcdChars");
} else {
Serial.println("Cannot write. Error: " + String(writeState));
}

readの結果は関数の戻り値でも受け取れますし、info.stateReadにも結果が格納されるので、それを見ることでも結果を判断できます。
  controlPanel.read(&info);
if (info.stateRead == 0) {
// on success
} else {
Serial.println("Cannot read info. Error: " + String(info.stateRead));
}

読み書き時の注意: 1秒間通信がなければリセットします

このコントローラーのファームウェアはウォッチドッグタイマーを利用しているため、1秒間通信がなければリセットする仕様になっています。

LCDやLEDの表示を維持したい場合は、1秒未満の間隔で書き込みか読み込みを行ってください。

値の扱い

Infoにボタン、ジョイスティック、LCD、LED、スライドスイッチ、エンコーダーの値が格納されており、下記のように参照できます。
  Serial.println("buttonsLeft: " +
String(info.buttonsLeft[0]) + " " +
String(info.buttonsLeft[1]));
Serial.println("buttonsRight: " +
String(info.buttonsRight[0]) + " " +
String(info.buttonsRight[1]));
Serial.println("buttonsJoyLeft: " +
String(info.buttonJoyLeft));
Serial.println("buttonsJoyRight: " +
String(info.buttonJoyRight));
Serial.println("slideSwitches: " +
String(info.slideSwitches[0]) + " " +
String(info.slideSwitches[1]));
Serial.println("Encoders: " +
String(info.encoders[0]) + " " +
String(info.encoders[1]));
Serial.println("joyLeftHori: " + String(info.joyLeftHori));
Serial.println("joyLeftVert: " + String(info.joyLeftVert));
Serial.println("joyRightHori: " + String(info.joyRightHori));
Serial.println("joyRightVert: " + String(info.joyRightVert));
Serial.print("leds:");
for (int i = 0; i < 4; ++i) {
Serial.print(" ");
Serial.print(info.leds[i]);
}
Serial.println("");
Serial.print("lcdChars[0to7] : ");
for (int i = 0; i < 8; ++i) {
Serial.print(info.lcdChars[i]);
}
Serial.println("");
Serial.print("lcdChars[8to15] : ");
for (int i = 8; i < 16; ++i) {
Serial.print(info.lcdChars[i]);
}
Serial.println("");

物理的な機能の説明でも言及したとおり、ジョイスティックとエンコーダーの取り得る値は0〜255です。
  Serial.println("Encoders: " +
String(info.encoders[0]) + " " +
String(info.encoders[1]));
Serial.println("joyLeftHori: " + String(info.joyLeftHori));
Serial.println("joyLeftVert: " + String(info.joyLeftVert));
Serial.println("joyRightHori: " + String(info.joyRightHori));
Serial.println("joyRightVert: " + String(info.joyRightVert));

ジョイスティックが上か下、左か右に移動されているか判断するのに便利な下記の関数を用意しています。
bool joyLeftOperatedDown();
bool joyLeftOperatedLeft();
bool joyLeftOperatedRight();
bool joyLeftOperatedUp();
bool joyRightOperatedDown();
bool joyRightOperatedLeft();
bool joyRightOperatedRight();
bool joyRightOperatedUp();

このように使えます。
if (info.joyRightOperatedUp()) {
Serial.println("Right is up");
}

write前にLEDの値をtrueにすれば点灯、falseにすると点灯します。
  int ledToBright = millis() % 1000 % 5;
for (int i = 0; i < 4; ++i) {
info.leds[i] = i == ledToBright;
}

write前にlcdCharを変えると、変えた文字がLCDに反映されます。
putStringLcdCharsという関数を用意しており、これを使うと文字列を特定のindex以後に置けて便利です。
  String strToShow = String(count);
info.putStringToLcdChars(strToShow, 16 - strToShow.length());

プログラムの解説は以上です。
ライブラリの実装を知りたい場合はソースコードをご覧ください。

動作の様子

サンプルプログラムPrintOnSerialをM5Stackに書き込んで、シリアルポートで値を表示しつつ操作を行う様子を写した写真です。

ジョイステイックを動かしたり、ボタンを押したりして、どういう値が取得できるか確認できます。

右のジョイステイックを上にスライドさせたときです。
JoyRightVertが255になります。


右のジョイステイックを下にスライドさせたときです。
JoyRightVertが0になります。


まとめ

ジョイスティック、エンコーダー、LCDなどの情報をI2Cでやりとりできる有線コントローラーを紹介しました。
良かったらご利用ください。

スイッチサイエンスで購入できます。
I2cControlPanel

更新履歴

2021.01.01
スイッチサイエンスの商品ページへのリンクを追加しました。

0 件のコメント :