電子趣味の部屋

電子系のガジェットやアプリ開発等の趣味の話題を書いてます

電子工作感覚でプログラミングできる ConnectJS

電子工作やFPGA等の部品同士を配線していくような感じでプログラミングできるJavascriptのライブラリを作りました。
ドキュメント等は簡易的なものしかありませんが、とりあえずGithubで公開してます。
github.com

モジュールさえ用意すれば接続していくだけで色々できるので、将来的にはGUIでブロックをつなげるようにプログラミングできればと考えています。

詳細な説明代わりに実例に沿って説明します。
LED、4bitシフトレジスタ、タイマーに見立てたモジュールを作り、LEDを1秒毎に順番に光らせるプログラムを作ります。

上の画像のようなHTMLのテーブルのセルをLEDに見立てて、左から順番に光らせて一番左の次はまた一番右に戻ります。
回路図でイメージすると、下の図のようになります。

ライブラリ本体(connectjs.js)はGithub内にあります。
サンプルソースを動作させるのに必要になります。
https://github.com/uosoft/ConnectJS/blob/main/src/connectjs.js

モジュール作成

まず、各モジュールを作ります。

LED

ファイル名 : LEDModule.js

// LEDModule
// モジュールはConnectModuleクラスを継承する
class LEDModule extends ConnectModule {
	//コンストラクタ
	constructor(led) {
		super();  //継承先でコンストラクタを定義する場合は親のコンストラクタを初めに呼ぶ
		this.led = document.getElementById(led);
	}
	// セットアップ
	setup() {
		this.addPort("led", true, false);  //ポート led1 を追加 (ポート名, IN許可, OUT許可)
	}
	// ポート led に値がセットされたときに動作する setter
	set_led(value) {
		if (value == 1) {
			this.led.style.backgroundColor = "#ff0000";
		} else {
			this.led.style.backgroundColor = "#ffffff";
		}
	}
}
4bitシフトレジスタ

ファイル名 : ShiftModule.js

// 4bitShiftModule
// モジュールはConnectModuleクラスを継承する
class ShiftModule extends ConnectModule {
	// セットアップ
	setup() {
		this.isCountup = false;
		this.addPort("si", true, false);  //ポート si を追加 (ポート名, IN許可, OUT許可)
		this.addPort("ck", true, false);  //ポート clock を追加 (ポート名, IN許可, OUT許可)
		this.addPort("qa", false, true);  //ポート qa を追加 (ポート名, IN許可, OUT許可)
		this.addPort("qb", false, true);  //ポート qb を追加 (ポート名, IN許可, OUT許可)
		this.addPort("qc", false, true);  //ポート qc を追加 (ポート名, IN許可, OUT許可)
		this.addPort("qd", false, true);  //ポート qd を追加 (ポート名, IN許可, OUT許可)
		this.port.qa = 0;
		this.port.qb = 0;
		this.port.qc = 0;
		this.port.qd = 0;
	}
	
	// ポート ck に値がセットされたときに動作する setter
	set_ck(value) {
		if (value) {
			if (!this.isCountup) {
				var si = this.port.si;
				if (si != 1) {
					si = 0
				}
				this.port.qd = this.port.qc;
				this.port.qc = this.port.qb;
				this.port.qb = this.port.qa;
				this.port.qa = si;
			}
			this.port.isCountup = true;
		} else {
			this.port.isCountup = false;
		}
	}
}
タイマー

ファイル名 : TimerModule..js

// TimerModule
class TimerModule extends ConnectModule {
	// セットアップ
	setup() {
		this.t = null;
		this.addPort("clockout", false, true);  //ポート clockout を追加 (ポート名, IN許可, OUT許可)
		this.addPort("interval", true, false);  //ポート interval を追加 (ポート名, IN許可, OUT許可)
	}
	// ポート interval に値がセットされたときに動作する setter
	set_interval(value) {
		if (this.t != null) {
			clearTimeout(this.t);
		}
		if (value != 0) {
			var self = this;
			self.t = setTimeout(function() {
				self.port.clockout = true;
				self.port.clockout = false;
				self.port.interval = value;
			}, value);
		}
	}
}

HTML

LED表示部分

HTMLのbodyにLEDに見立てたテーブルを作ります。

<table border=1>
	<tr>
		<td id="led1">&nbsp;&nbsp;&nbsp;&nbsp;</td>
		<td id="led2">&nbsp;&nbsp;&nbsp;&nbsp;</td>
		<td id="led3">&nbsp;&nbsp;&nbsp;&nbsp;</td>
		<td id="led4">&nbsp;&nbsp;&nbsp;&nbsp;</td>
	</tr>
</table>
モジュールを接続する処理

下のような処理をbodyのonload辺りに実行します。

// コネクトマネージャ
var cm = new ConnectManager();
// モジュール4bitShiftModule (シフトレジスタ的なもの)
var shift = new ShiftModule();
// モジュールTimerModule (オシレータ的なもの)
var timer = new TimerModule();
// モジュールLEDModule (LED的なもの)
var led1 = new LEDModule("led1");
var led2 = new LEDModule("led2");
var led3 = new LEDModule("led3");
var led4 = new LEDModule("led4");

// モジュールのポートを接続する
// (モジュール1, モジュール1のポート, モジュール2, モジュール2のポート)
cm.connect(shift, "ck", timer, "clockout");
cm.connect(shift, "qa", led1, "led");
cm.connect(shift, "qb", led2, "led");
cm.connect(shift, "qc", led3, "led");
cm.connect(shift, "qd", led4, "led");
cm.connect(shift, "qd", shift, "si");

// 各ポートの初期化処理
shift.port.si = 1;
shift.port.ck = true;
shift.port.ck = false;
timer.port.interval = 1000;
HTMLファイル全文

一通り動作するHTML全文です。
ファイル名 : led_shifter.html

<!DOCTYPE html>
<html>
	<head>
	<title>Connect JS Sample  -- LED Shifter --</title> 
	<meta name="viewport" content="width=device-width, initial-scale=1">
	
	<script type='text/javascript' src='connectjs.js'></script>
	<script type='text/javascript' src='ShiftModule.js'></script>
	<script type='text/javascript' src='TimerModule..js'></script>
	<script type='text/javascript' src='LEDModule.js'></script>

	<script type="text/javascript">
	
	function start() {
		
		// コネクトマネージャ
		var cm = new ConnectManager();
		// モジュール4bitShiftModule (シフトレジスタ的なもの)
		var shift = new ShiftModule();
		// モジュールTimerModule (オシレータ的なもの)
		var timer = new TimerModule();
		// モジュールLEDModule (LED的なもの)
		var led1 = new LEDModule("led1");
		var led2 = new LEDModule("led2");
		var led3 = new LEDModule("led3");
		var led4 = new LEDModule("led4");
		
		// モジュールのポートを接続する
		// (モジュール1, モジュール1のポート, モジュール2, モジュール2のポート)
		cm.connect(shift, "ck", timer, "clockout");
		cm.connect(shift, "qa", led1, "led");
		cm.connect(shift, "qb", led2, "led");
		cm.connect(shift, "qc", led3, "led");
		cm.connect(shift, "qd", led4, "led");
		cm.connect(shift, "qd", shift, "si");
		
		// 各ポートの初期化処理
		shift.port.si = 1;
		shift.port.ck = true;
		shift.port.ck = false;
		timer.port.interval = 1000;
	}
	
	</script>

</head>
<body onload="start();">
<table border=1>
	<tr>
		<td id="led1">&nbsp;&nbsp;&nbsp;&nbsp;</td>
		<td id="led2">&nbsp;&nbsp;&nbsp;&nbsp;</td>
		<td id="led3">&nbsp;&nbsp;&nbsp;&nbsp;</td>
		<td id="led4">&nbsp;&nbsp;&nbsp;&nbsp;</td>
	</tr>
</table>
</body>
</html>