ブラウザからラズパイをコントロールしてみる

ラズパイで GPIO の入出力を今まで試してきましたが、
せっかく Node.js を使っているので、Web サーバーと組み合わせてブラウザから
LED の点灯が操作できないかと思って作ってみました。

あぁ、Nodejs+ラズパイ楽しい。

目次

準備編

適当なフォルダを準備して
npm initを実行

今回は Web サーバーに micro、開発用に micro-dev を使います。
https://github.com/zeit/micro
https://github.com/zeit/micro-dev
npm でパッケージのダウンロードをしていきます。
npm install micro
npm install micro-dev
package.json も micro-dev の README を見ながら少しいじります。

1
2
3
4
"scripts": {
"start": "micro",
"dev": "micro-dev"
}

作成編

index.js という名前で以下を保存します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const fs = require("fs");
const path = require("path");
const { json } = require("micro");

//使用するgpioピンナンバー
const pinnum = 21;
//使用する仮想ファイルのパスを定義
const pathstr = "/sys/class/gpio/";
const gpio21 = path.join(pathstr, "gpio" + pinnum);

const htmltext = fs.readFileSync(process.cwd() + "/console.html");
const jquery = fs.readFileSync(process.cwd() + "/jquery-3.3.1.min.js");

//gpio使用の準備
fs.writeFileSync(path.join(pathstr, "export"), pinnum);
fs.writeFileSync(path.join(gpio21, "direction"), "out");

//終了時の処理を定義
process.on("exit", () => {
fs.writeFileSync(path.join(pathstr, "unexport"), pinnum);
console.log("PROCESS EXIT");
});

//サーバー処理定義
module.exports = async (req, res) => {
const url = req.url;

console.log(`url=${url}`);
let ms = "ON";
if (url == "/state") {
let state = fs.readFileSync(path.join(gpio21, "value"), "utf8");
res.write(`${state}`);
} else if (url == "/sw") {
let state = fs.readFileSync(path.join(gpio21, "value"), "utf8");
state = state == 0 ? 1 : 0;
fs.writeFileSync(path.join(gpio21, "value"), state);
} else if (url == "/jquery-3.3.1.min.js") {
res.writeHead(200, { "Content-Type": "text/javascript" });
res.write(jquery);
} else {
res.writeHead(200, { "Content-Type": "text/html" });
res.write(htmltext);
}
res.end();
};

サーバーに対する URL で、

  1. LED のステータス送信
  2. LED の点灯切り替え
  3. javascript ファイル送信
  4. html ファイル送信
    を処理します。

プロセスの停止は Ctrl+c で行うのですが、
今までだとecho 21 > /sys/class/gpio/unexport;を実行してからでないと、
アプリケーションを実行できない問題があったけど、
process.on('exit',関数echo 21 > /sys/class/gpio/unexport;
割り当てると、終了時に GPIO の解放処理を入れることができた。
Node.js で GPIO を触るときの定型パターンかもしれない。

加えて、クライアント側の html を作ります。
console.html として以下を保存します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<html lang="ja">
<head>
<style>
main {
margin: 10px;
}
#state {
height: 300;
width: 300;
background-color: #000;
margin: 10px;
}
#ledsw {
height: 50;
width: 100;
margin: 10px;
margin-left: 100px;
font-size: 20px;
}
</style>
</head>
<body>
<main>
<div>
<div id="state"></div>
<button id="ledsw">state</button>
</div>
</main>
<script src="./jquery-3.3.1.min.js"></script>
<script>
//LED状態読み取り処理
let readstate = function () {
$.ajax({
url: "./state",
type: "POST",
data: {},
cache: false,
}).done(function (data) {
if (data.replace(/\r?\n/g, "") == "0") {
$("#ledsw").text("ON");
$("#state").css("background-color", "#000");
} else {
$("#ledsw").text("OFF");
$("#state").css("background-color", "#f00");
}
});
};
//LED状態切り替え処理
let changestate = function () {
$.ajax({
url: "./sw",
type: "POST",
data: {},
cache: false,
}).done(function (data) {
readstate();
});
};
//ページ初期化処理
$(function () {
readstate();
});
//LED状態切り替え処理イベント登録
$("#ledsw").click(function () {
changestate();
});
</script>
</body>
</html>

あと console.html で jquery を使用しているので、jquery-3.3.1.min.js を保存しておきます。
細かいバージョンはこだわらなくていい気がします。

ここまででフォルダ構成は以下のようになります

---console.html
 |-index.js
 |-jquery-3.3.1.min.js
 |-package-lock.json
 |-package.json
 `-node_modules

実行するときはsudo npm start
実行したら、ブラウザで http://[ラズパイの IP アドレス]:3000 にアクセスしてみましょう。
下の画像の画面が開きます。

LED の点灯状況とブラウザ側での表示が連動します。
LED が付くとブラウザ側でも表示が赤くなります。

もし、LED が付いたままブラウザを更新しても、
ページ取得時に現在の LED の点灯ステータスを取得しているので、
整合性も崩れずに済みます。

これでクライアントサイドの WEB 画面と、LED の点灯を連動させることができました。

今回でラズパイをブラウザからコントロールする渡りが付いたので、
ブラウザからサーボモーターとセンサーを操作してみたいですね。

ではでは