【Arduinoで自作コントローラーを作る①】ジョイパッドから信号入力するためのスクラッチ拡張機能
※ 当ページには【広告/PR】を含む場合があります。
2021/12/19
材料の調達
Arduinoで基本機能をテスト実装
Arduinoでジョイスティックモジュールの使い方
端子名 | 備考 |
---|---|
+5V | +5VDC電源 |
GND | グラウンド(コモン) |
VRx | X軸方向のアナログ読み取り |
VRy | Y軸方向のアナログ読み取り |
SW | センタークリック(ON/OFF) |
Arudinoプログラムの実装
/**
デジタルピン#4(Aボタン)
デジタルピン#5(Bボタン)
デジタルピン#6(スタートボタン)
デジタルピン#7(セレクトボタン)
デジタルピン#8(センタースイッチのON/OFF)
アナログピンA0(X軸方向入力用)
アナログピンA1(Y軸方向入力用)
*/
const int A_PIN = 4, B_PIN = 5, ST_PIN = 6, SL_PIN = 7,
SW_PIN = 8, X_PIN = A0, Y_PIN = A1;
char buf[48];
void setup() {
pinMode(X_PIN, INPUT); // アナログ0ピンは通常入力
pinMode(Y_PIN, INPUT); // アナログ1ピンは通常入力
// デジタル4-8ピンはプルアップ入力
pinMode(A_PIN, INPUT_PULLUP);
pinMode(B_PIN, INPUT_PULLUP);
pinMode(ST_PIN, INPUT_PULLUP);
pinMode(SL_PIN, INPUT_PULLUP);
pinMode(SW_PIN, INPUT_PULLUP);
Serial.begin(9600);
delay(200);
}
void loop(){
/**
x: X軸座標(0 - 1023)
y: Y軸座標(0 - 1023)
sw: センタースイッチのON/OFF
a: AボタンのON/OFF
b: BボタンのON/OFF
st: スタートボタンのON/OFF
sl: セレクトボタンのON/OFF
*/
sprintf(buf, "{x:%d,y:%d,sw:%d,a:%d,b:%d,st:%d,sl:%d}",
analogRead(X_PIN), analogRead(Y_PIN), digitalRead(SW_PIN),
digitalRead(A_PIN), digitalRead(B_PIN), digitalRead(ST_PIN), digitalRead(SL_PIN)
);
Serial.println(buf);
delay(100);
}
スクラッチ拡張機能の作成
const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Cast = require('../../util/cast');
const log = require('../../util/log');
class Scratch3GamepadController {
constructor (runtime) {
this.runtime = runtime;
this.stopFlag = false;
this.serialReader = null;
//👇メッセージ更新時のトリガーとして利用
this.isRecieveMessage = false;
//👇最新のゲームパッド状態を保存
this.x = 0;
this.y = 0;
this.sw = false;
this.ab = false;
this.bb = false;
this.st = false;
this.sl = false;
}
getInfo () {
return {
id: 'gamepadcontroller',
name: 'Gamepad Controller',
blocks: [
{
opcode: 'connectSerial',
blockType: BlockType.REPORTER
},
{
opcode: 'disconnectSerial',
blockType: BlockType.COMMAND
},
//👇メッセージ受信時のイベント(HATブロック)
{
opcode: 'updateMessage',
blockType: BlockType.HAT
},
//👇ゲームパッド状態の取得(REPORTERブロック)
{
opcode: 'getX',
blockType: BlockType.REPORTER
},
{
opcode: 'getY',
blockType: BlockType.REPORTER
},
{
opcode: 'getSW',
blockType: BlockType.BOOLEAN
},
{
opcode: 'getStart',
blockType: BlockType.BOOLEAN
},
{
opcode: 'getSelect',
blockType: BlockType.BOOLEAN
},
{
opcode: 'getA',
blockType: BlockType.BOOLEAN
},
{
opcode: 'getB',
blockType: BlockType.BOOLEAN
}
],
menus: {}
};
}
async startSerial() {
try {
console.log("INFO: 接続が確立しました");
this.stopFlag = false;
const port = await navigator.serial.requestPort();
await port.open({
baudRate: 9600,
dataBits: 8,
stopBits: 1,
parity: "none",
bufferSize: 255,
flowControl: "hardware"
});
while (port.readable) {
this.serialReader = port.readable.getReader();
try {
const buff_ = [];
let lastByte;
while (!this.stopFlag) {
const { value, done } = await this.serialReader.read();
if (done) {
console.log("INFO: 読込モード終了");
break;
}
if (value) {
for (let i=0;i < value.length;i++) {
buff_.push(value[i]);
if (value[i] == 10 && lastByte == 13) {
const inputValue = new TextDecoder("utf-8").decode(new Uint8Array(buff_));
buff_.splice(0);
//👇メッセージの更新
console.log(inputValue);
const mtch_ = inputValue.match(/{x:(\d+),y:(\d+),sw:(\d?),a:(\d?),b:(\d?),st:(\d?),sl:(\d?)}/);
if (mtch_) {
if (mtch_[1]) this.x = parseInt(mtch_[1],10);
if (mtch_[2]) this.y = parseInt(mtch_[2],10);
if (mtch_[3]) this.sw = parseInt(mtch_[3],10);
if (mtch_[4]) this.ab = parseInt(mtch_[4],10);
if (mtch_[5]) this.bb = parseInt(mtch_[5],10);
if (mtch_[6]) this.st = parseInt(mtch_[6],10);
if (mtch_[7]) this.sl = parseInt(mtch_[7],10);
}
//👇HATブロックにtrueを流す
this.isRecieveMessage = true;
console.log("INFO: 読込完了");
break;
}
lastByte = value[i];
}
}
}
} catch (error) {
console.log("ERROR: 読み出し失敗");
console.log(error);
} finally {
this.serialReader.releaseLock();
await port.close();
console.log("INFO: 接続を切断しました");
}
}
} catch (error) {
console.log("ERRORR: ポートが開けません");
console.log(error);
}
}
stopSerial() {
this.stopFlag = true;
this.serialReader.cancel();
}
connectSerial() {
console.log('Connected!');
this.startSerial();
return 'Connected!';
}
disconnectSerial() {
console.log('Disconnected.');
this.stopSerial();
}
//👇HATブロックの中身
updateMessage() {
const triggered = this.isRecieveMessage;
this.isRecieveMessage = false;
return triggered;
}
//👇各ゲームパッドの状態値を返す関数
getX() { return this.x; }
getY() { return this.y; }
getA() { return this.ab; }
getB() { return this.bb; }
getSW() { return this.sw; }
getStart() { return this.st; }
getSelect() { return this.sl; }
}
module.exports = Scratch3GamepadController;
ゲームパッドからスクラッチアプリを動かす
まとめ
参考サイト
記事を書いた人
ナンデモ系エンジニア
これからの"地方格差"なきプログラミング教育とは何かを考えながら、 地方密着型プログラミング学習関連テーマの記事を不定期で独自にブログ発信しています。
カテゴリー