ラズパイ4デジカメ計画②〜NodejsとGPIOでラズパイの起動・シャットダウンを操作する
※ 当ページには【広告/PR】を含む場合があります。
2024/02/10
2024/02/15

ラズパイ4Bでなんちゃってデジタルトイカメラに仕立てる話の続きです。
今回もまだカメラの話までは行かずに、Nodejsの動作環境をラズパイに整えていくことに注力していきます。
ラズパイをデジカメ化する上で気にしたい電源管理の注意点
まず、ラズパイを起動・停止させる上で気をつけたいのが、
文字通り、電源を入れるか、電源を切るか、二択しかなく、頻繁にON/OFFする使い方をするであろうデジカメにはかなり不利な性質であることは頭においておきましょう。
ラズパイを"スタンバイ"させることができないため、すぐに写真を取りたいタイミングがあっても、電源を付けて、バックグラウンドでアプリが立ち上がるまでに30秒程度は待たなければなりません。
では、
ラズパイ4では、典型的なアプリ動作時には4~5W程度の電力を消費しています。 そして何もしていないCPUが低負荷の状態でも、3Wくらい電力消費しているとされています。
問題となるのは、
ラズパイの電源を切っているから、大したこと無いだろうとたかをくくっていると、
ともかくラズパイをモバイルバッテリー等で動作させるような用途だと、バッテリーとラズパイ本体の接続を物理的に切断する
1. ラズパイには休止機能がないので、
電源投入からアプリケーション起動までに時間がかかる
2. ラズパイ本体の電源を切っても、
通電していたら割と電力を消費してしまう
こういったことを踏まえて、ラズパイのデジカメ化を以降で進めていきましょう。
ラズパイへNode.js(v16)環境を準備する
巷ではこの手の記事を探すと、おおよそPythonでコードの紹介されている場合がほとんどかと思います。
GPIOを動かすだけであれば、Pythonのほうがたくさん応用事例が紹介されているため、とてもユーザーフレンドリーです。
組み込みでもあえてnodejsを採用する理由は、後々で「TCP/UDPサーバー機能を追加」するまで考えたときに、組込み処理とサーバーとの統合がやりやすいというメリットがあります。
いざNodejsで組込みのプログラムを動かすことを考えたとき、できる限り最新のバージョンで動かしたいのは山々ですが、node18以降ではプログラミング作法がガラリと変わり、可能な限り
ESModule
技術の移り変わりが激しい時期でもあり、ラズパイ向けの組込み用途でNodejsのライブラリ提供されている有志の方のほとんどがESModule対応に追いつけていないため、node18以降に対応したラズパイ向けのライブラリもまだあまり存在していない状況です。
このためラズパイの組込み方面に使われるnodeはレガシーも多く、node18より前の
Commonjs
こういった事情を汲んで、より安定性あるのコーディングが可能な
node16
ラズパイに特定のバージョンのNodejsを導入
以前の記事で、Dockerコンテナ越しにnodejsを動かしてラズパイのLチカを試した内容を紹介していました。
今回の用途だと、Dockerを介したデバイスの操作した場合、どうしてもラズパイ本体のON/OFFのたびにDockerエンジン自体のコールドスタートがオーバーヘッド時間で乗ってくる(とはいえ数秒くらいの違いでしょうけれど...)懸念があります。
そこで、デジカメ化に際してはDockerを使わず、ラズパイに直接nodejsを仕込みます。
ラズパイ(Raspberry Pi OS)へのnodejsのインストール方法は現在では様々あります。
昔ながらの外部サイト(
https://deb.nodesource.com
$ curl -s https://deb.nodesource.com/setup_16.x | sudo bash
This script, located at https://deb.nodesource.com/setup_X, used to
install Node.js is deprecated now and will eventually be made inactive.
Please visit the NodeSource distributions Github and follow the
instructions to migrate your repo.
https://github.com/nodesource/distributions
The NodeSource Node.js Linux distributions GitHub repository contains
information about which versions of Node.js and which Linux distributions
are supported and how to install it.
https://github.com/nodesource/distributions
SCRIPT DEPRECATION WARNING
================================================================================
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
================================================================================
TO AVOID THIS WAIT MIGRATE THE SCRIPT
Continuing in 60 seconds (press Ctrl-C to abort) ...
...と直近のnodejs事情でv16のインストールスクリプトはサポート切れで利用不能になってしまいました。
そこでちょっと回りくどいですが、
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
インストールはスクリプト一発で簡単ですが、以下のnvmのインストールスクリプトのバージョンはその都度公式のページで確認してください。
スクリプトの処理が完了すると、nvmの起動に必要な環境変数を利用するため、
.bashrc
#...
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
設定が追加されなかった場合には、手動で追加しておきましょう。
あとはこれを反映して、実行するだけで完了です。
$ source ~/.bashrc
$ nvm --version
0.39.7
では本題のnode16を入れていきます。
$ nvm list-remote | grep v16
v16.0.0
v16.1.0
v16.2.0
#...中略
v16.20.1 (LTS: Gallium)
v16.20.2 (Latest LTS: Gallium)
と利用可能なv16がリストアップされています。
v16の特定のマイナーバージョンは開発環境のものと揃えるか、それほど理由がなければ最新のものを選択しインストールしましょう。
なお、以下のようにメジャーバージョンだけ指定すると、最新のものが自動でインストールされます。
$ nvm install v16
$ node --version
v16.20.2
$ npm --version
8.19.4
なお、パッケージマネージャーも
yarn
$ npm install -g yarn
$ yarn --version
1.22.21
実際に組込み用のプログラムで、nodeコマンドを使おうと思うと、GPIO等の内部デバイスにアクセスするための権限の設定にもう少し手を加える必要があります。
このことについては後述します。
「sudo node ...」では"command not found"になってしまう問題
組込み向けに
node
sudo node
ですが、単に
sudo
$ sudo node --version
sudo: node: command not found
となって最初に直面するときには、かなり困惑します。
これは現在のユーザーと、ルートユーザーのシステム変数が異なるため、
node
#👇現在のユーザーのパス変数
$ printenv PATH | sed -r 's/:/\n/g'
/home/user/.nvm/versions/node/v16.20.2/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/local/games
/usr/games
#👇ルートユーザーのパス変数
$ sudo printenv PATH | sed -r 's/:/\n/g'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
見てのように、現在のユーザーにはnodeコマンドの所在(
/home/user/.nvm/versions/node/v16.20.2/bin
そこで、もっともかんたんな対処法としては
which
$ sudo $(which node) --version
v16.20.2
としてあげるとルートユーザーでも上手く処理を行ってくれます。
which
/usr/bin/
node
$ ln -s $(which node) /usr/bin/
ただし、この方法は、nodejsのバージョンやインストール場所が変更になるたびに手動で再度シンボリックリンクを張り直す必要があるので、忘れたころに「nodeバージョンがずれるけどなんで?」となるかもしれません。
ピンレイアウトの確認
ラズパイ用の液晶タッチパネルは結構ピンを占有してしまうので、あまり自由に数を実装できませんが、ボタン操作を実装していきます。
利用している製品にもよりますが、前回に引き続き、液晶タッチパネルは「
この製品のピンレイアウトは
1-26
951x537

ではどこらへんが空いているかを確認するため
pinout
$ pinout
#...
J8:
3V3 (1) (2) 5V
GPIO2 (3) (4) 5V
GPIO3 (5) (6) GND
GPIO4 (7) (8) GPIO14
GND (9) (10) GPIO15
GPIO17 (11) (12) GPIO18
GPIO27 (13) (14) GND
GPIO22 (15) (16) GPIO23
3V3 (17) (18) GPIO24
GPIO10 (19) (20) GND
GPIO9 (21) (22) GPIO25
GPIO11 (23) (24) GPIO8
GND (25) (26) GPIO7
GPIO0 (27) (28) GPIO1
GPIO5 (29) (30) GND
GPIO6 (31) (32) GPIO12
GPIO13 (33) (34) GND
GPIO19 (35) (36) GPIO16
GPIO26 (37) (38) GPIO20
GND (39) (40) GPIO21
ということで空きピンは
27-40
GPIO操作系ライブラリを使う上での注意点
前回四苦八苦しながら、ラズパイ用のタッチパネルをセットアップする内容を説明しておりました。
使用したタッチパネルは
「WiringPi」
例えば、node18に対応しているGPIO系のライブラリ・
このため、タッチパネル製品にもよりますが、画面の描画になんらかの不具合が起こってしまうようです。
こういった理由で今回は、WiringPiとは競合せず、かつ、node16までサポートしている
GPIOピンからソフトウェアシャットダウン
まずは適当なGPIOピンを使って、子プロセスから
shutdown
ここでは、
GPIO20(38番ピン)
pigpiodのインストール
「pigpio」はラズパイの組込み向けに提供されているCで作成されたライブラリ群です。
nodejs版のpigpioは、このインストールしたライブラリを使ってGPIOを利用する方式になっているため、先にベースのプログラムを準備しておく必要があります。
現在のRaspberry Pi OSでは、以下のコマンドで一発導入できます。
$ sudo apt update
$ sudo apt install pigpio
$ pigpiod -v
79
OSのバージョンが古かったり、Debian系以外のOSであれば、ソースコードビルドするとインストール可能です。
$ wget https://github.com/joan2937/pigpio/archive/master.zip
$ unzip master.zip
$ cd pigpio-master
$ sed -i 's,ldconfig,,' Makefile
$ make
$ sudo make install
$ pigpiod -v
79
nodejsプロジェクトの新規作成
まずは適当なフォルダに移動し、以下の
package.json
{
"name": "raspi-toy-camera",
"version": "1.0.0",
"description": "RPi camera app for armhf built in x64 OS",
"main": "main.js",
"private": true,
"dependencies": {
"pigpio": "^3.3.1"
}
}
この作業フォルダ内で、
$ yarn install
すると、準備はOKです。
では早速pigpioを使ったシャットダウンボタン機能を実装していきます。
以下のように
main.js
const { execSync } = require('node:child_process');
const Gpio = require('pigpio').Gpio;
//👇GPIO20(38番ピン)をプルアップ入力モードに設定
const button = new Gpio(20, {
mode: Gpio.INPUT,
pullUpDown: Gpio.PUD_UP,
});
process.on('SIGINT', () => {
console.log('Ctrl+Cでプロセス終了します...');
process.exit(0);
});
const sleep = (_ms) => {
return new Promise(resolve => {setTimeout(() => resolve(), _ms)});
}
(async () => {
//👇試しに0.5秒間隔でポーリング
while(true) {
console.log("Listening...");
if (button.digitalRead() === 0) {
console.log("シャットダウンボタンが押されました!");
//👇シャットダウンコマンドを呼び出す
execSync(`/sbin/shutdown -h now 'Turn off by GPIO'`);
}
await sleep(500);
}
})();
これで、38番ピンをGNDとショートさせたときに、シャットダウンコマンドが同期的に呼び出され、電源を切ってくれるだけのプログラムが完成します。
なお、実行中のプログラムを手動で止めたいときには、
Ctrlキー+Cキー
プルアップモードでスイッチを配線
ラズパイ内臓のプルアップ抵抗を使った「プルアップ入力モード」を使えば、わざわざ外部に適当な大きさの抵抗を付けなくてもスイッチ入力のテストが簡単に準備できます。
827x672

注意が必要なのは、プルアップ設定ですので、スイッチを押していないフロートの状態ではON、スイッチを押したショートの状態ではOFFとなります。
ですので、先程の
pigpio
- スイッチを押さない ... <PIN#>.digitalRead() --> 1
- スイッチを押す ... <PIN#>.digitalRead() --> 0
となりますので、入力シグナルの対応は注意してください。
nodeでmain.jsプログラムを起動してみる
ではスイッチとの配線も終わったので、先程の
main.js
何も考えずに
node
$ node main.js
2024-02-08 13:50:11 initCheckPermitted:
+---------------------------------------------------------+
|Sorry, you don't have permission to run this program. |
|Try running as root, e.g. precede the command with sudo. |
+---------------------------------------------------------+
/home/*********/*********/node_modules/pigpio/pigpio.js:54
pigpio.gpioInitialise();
^
Error: pigpio error -1 in gpioInitialise
at initializePigpio (/home/*********/*********/node_modules/pigpio/pigpio.js:54:12)
at new Gpio (/home/*********/*********/node_modules/pigpio/pigpio.js:158:5)
at Object.<anonymous> (/home/*********/*********/main.js:10:16)
at Module._compile (node:internal/modules/cjs/loader:1198:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10)
at Module.load (node:internal/modules/cjs/loader:1076:32)
at Function.Module._load (node:internal/modules/cjs/loader:911:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:22:47
といきなり
実行権限
ちなみに、ラズパイにはデフォルトユーザーとは別にルートユーザーが存在しています。
特に設定していないと、デフォルトユーザーは自由にデバイスにアクセスできる権限がないので、組込向けのファイルの実行権限には注意が必要です。
ということで、
sudo
node
$ sudo $(which node) main.js
2024-02-08 13:52:43 initCheckPermitted:
+---------------------------------------------------------+
|Sorry, you don't have permission to run this program. |
|Try running as root, e.g. precede the command with sudo. |
+---------------------------------------------------------+
/home/*********/*********/node_modules/pigpio/pigpio.js:54
pigpio.gpioInitialise();
^
Error: pigpio error -1 in gpioInitialise
#...
ん?おかしい...。
sudeでルートの実行権限を与えてもまだ実行権限エラーが出ます。
ここでnodeの実行バイナリファイルの所有者情報を詳しく調べます。
$ ls -la $(which node)
#...
-rwsr-xr-x 1 user user 74174792 Aug 9 2023 node
すると、nvm経由で入れたnodeは、rw...
"s"
この場合、ルートユーザーのファイル実行権が「s」となっているため、他のユーザーからnodeコマンドを実行すると、
つまり、sudoしても結局はルート実行権を持たないファイル所有者(ここでは
user
こういったアクセス権設定は、内部デバイスなど、ルートユーザー以外からは絶対に実行してほしくないときには便利ですが、ラズパイの組込開発だとしばしば不便なときがあります。
ということで、ルート特権を通常の
755
$ sudo chmod 755 $(which node)
$ ls -la $(which node)
...
-rwxr-xr-- 1 user user 74174792 Aug 9 2023 node
これでnodeコマンドをsudoで実行した場合に、ちゃんとルート権限ありで実行されるはずです。
$ sudo $(which node) main.js
Listening...
Listening...
Listening...
...
#👇スイッチを押すとラズパイがシャットダウン
シャットダウンボタンが押されました!
オーバーレイによるシャットダウンピンの書き換え
ラズパイでシャットダウンといえば、デスクトップの電源アイコンをクリックしたり、CUIなら
shutdown
ラズパイをシャットダウンするだけで良ければ、
DT(デバイスツリー)オーバーレイ
先程説明した「ソフトウェアシャットダウン」との違いは、常時外部から強制終了できるか否かという点にあります。
ソフトウェアシャットダウンの場合、処理中のプロセスが何らかのエラーで中断してしまったり、暴走して操作を受け付けなくなったりすると、使えなくなってしまいます。
対して、ラズパイ本体に備わっている物理的なシャットダウン機能は、他のプロセスとは独立した機能であるため、異常な事態が起こっても確実に電源を落とすことができます。
まずオーバーレイ機能を変えるには
/boot/config.txt
sudo nano /boot/config.txt
#...中略
#40番ピン(GPIO21)に「シャットダウンボタン」を設定
dtoverlay=gpio-shutdown,gpio_pin=21
起動用物理ピン(GPIO3)を使う
ラズパイの起動に関しては、冒頭でも述べたように、物理的な電源投入(電源プラグの抜き差しや電源スイッチの利用)が好ましいのですが、わざわざ起動のたびにケーブルを抜き差しするのが面倒な場合があります。
わざわざ電源の切断と接続を繰り返さなくても、パソコンのような物理的な電源ボタンとなる特別なピンが存在しています。
デフォルトでは
GPIO3(5番ピン)
このピンは他のピンとは異なり特別であり、GNDピンと短絡することで
「起動」
と言うのは簡単なんですが、ラズパイ用のブレークアウト基板等で、すでにこのGPIO3が使用されている場合、なかなか後付で起動ボタンを追加することが難しい場合があります。
今回の例が分かりやすいですが、以下の写真のようにラズパイ基板の上からも横からも5番ピンに容易に接続できません。
600x479

ということで、少しトリッキーな感じになりますが、裏側のすでにハンダされてるところを狙って、ワイヤーを一本引き出してあげましょう。
847x599

既にハンダされているピンにそのまま上からハンダすると、なかなか思うように付かないので、あらかじめフラックスを塗布してからハンダすると良いでしょう。
この方法だと、接続がどうしても弱くなって、何気ない衝撃でハンダ箇所が外れてしまうことも考えられるので、より強固にしたい場合には、グルーガンなどで被覆すると良いかもしれません。
まとめ
今回は、デジカメ化とは直接的には関係しませんが、持ち運び可能なラズパイガジェット化にとってはとても重要なことなので、先んじて本体電源のピン操作をまとめてみました。
次回からは、デジカメ化のコアな内容である、カメラモジュールの実装に関して解説してきます。