【scratch-vm応用講座】スクラッチの拡張機能でブラウザから直接シリアル通信を操作する
※ 当ページには【広告/PR】を含む場合があります。
2021/12/17
拡張機能でArduinoを動かす?
Arduino側の準備
// スイッチが押されている = LOW
const int buttonON = LOW;
// スイッチが押されている = HIGH
const int buttonOFF = HIGH;
// スイッチのON/OFF状態
int buttonState = 0;
// 文字列の送信フラグ
boolean sendOnceFlag = false;
// デジタルピン#4
const int buttonPin = 4;
// ビルドインLED
const int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(9600);
delay(200);
}
void loop(){
buttonState = digitalRead(buttonPin);
if (buttonState == buttonON) {
digitalWrite(ledPin, HIGH);
if (!sendOnceFlag) {
Serial.println("ボタンが押されました");
sendOnceFlag = true;
}
} else {
digitalWrite(ledPin, LOW);
if (sendOnceFlag) {
sendOnceFlag = false;
}
}
}
INPUT_PULLUP
スクラッチで拡張機能作成
文字列の受信
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 Scratch3WebSerialMonitor {
constructor (runtime) {
this.runtime = runtime;
this.stopFlag = false;
}
getInfo () {
return {
id: 'webserialmonitor',
name: 'Simple Test for Web Serial API',
blocks: [
{
opcode: 'connectSerial',
blockType: BlockType.COMMAND
},
{
opcode: 'disconnectSerial',
blockType: BlockType.COMMAND
}
],
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) {
const reader = port.readable.getReader();
try {
while (!this.stopFlag) {
const { value, done } = await reader.read();
if (done) {
console.log("INFO: 読込モード終了");
break;
}
//👇生データはバイナリなので、ユニコード文字へデコード
const inputValue = new TextDecoder().decode(value);
console.log(inputValue);
//👇ついでに生のバイナリ(Uint8Arrayインスタンス)も表示
console.log(value);
}
} catch (error) {
console.log("ERROR: 読み出し失敗");
console.log(error);
} finally {
reader.releaseLock();
await port.close();
console.log("INFO: 接続を切断しました");
}
}
} catch (error) {
console.log("ERRORR: ポートが開けません");
console.log(error);
}
}
stopSerial() {
this.stopFlag = true;
}
connectSerial() {
console.log('Connected!');
this.startSerial();
}
disconnectSerial() {
console.log('Disconnected');
this.stopSerial();
}
}
module.exports = Scratch3WebSerialMonitor;
設定ポイント①
port.open
flowControl: "none"
flowControl: "hardware"
reader.read
13 10
index.js
startSerial
//...中略
async startSerial() {
try {
//...中略
try {
const buff_ = [];
let lastByte;
while (!this.stopFlag) {
const { value, done } = await reader.read();
if (done) {
console.log("INFO: 読込モード終了");
break;
}
if (value) {
console.log(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_));
console.log(inputValue);
buff_.splice(0);
console.log("INFO: 読込完了");
break;
}
lastByte = value[i];
}
}
}
//...以降省略
reader.read
buff_
13 > 10
スクラッチプログラムに表示させる
//...中略
constructor (runtime) {
//...中略
//👇最新のメッセージをキャプチャ
this.message = '';
//👇メッセージ更新時のトリガーとして利用
this.isRecieveMessage = false;
}
getInfo () {
return {
//...中略
blocks: [
//...中略
//👇メッセージ受信時のイベント(HATブロック)
{
opcode: 'updateMessage',
blockType: BlockType.HAT
},
//👇メッセージ取得(REPORTERブロック)
{
opcode: 'getMessage',
blockType: BlockType.REPORTER
}
],
//...中略
};
}
async startSerial() {
try {
//...中略
try {
const buff_ = [];
let lastByte;
while (!this.stopFlag) {
const { value, done } = await reader.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);
//👇メッセージの更新
this.message = inputValue;
//👇HATブロックにtrueを流す
this.isRecieveMessage = true;
console.log("INFO: 読込完了");
break;
}
lastByte = value[i];
}
}
}
//...中略
}
//...中略
//👇HATブロックの中身
updateMessage() {
const triggered = this.isRecieveMessage;
this.isRecieveMessage = false;
return triggered;
}
//👇REPORTERブロックの中身
getMessage() {
return this.message;
}
//...以降省略
まとめ
参考サイト
記事を書いた人
ナンデモ系エンジニア
これからの"地方格差"なきプログラミング教育とは何かを考えながら、 地方密着型プログラミング学習関連テーマの記事を不定期で独自にブログ発信しています。
カテゴリー