Help us understand the problem. What is going on with this article?

ODROID-C2にNode.jsをインストールし、GPIOを使えるようにする

More than 1 year has passed since last update.

概要

僕はPythonがピーマンより苦手なので、ODROID-C2にNode.jsをインストールして、JavaScriptで動かせる環境を構築します。

RaspberryPiと異なる点は、64bit版OSなので、ARMv8(arm64)バイナリが実行可能な所です。

実行環境

1. Node.jsのインストール

1-1. いつもの

sudo apt update
sudo apt upgrade
sudo apt autoremove

まずはここから。
最近は apt-get より apt が流行りのようですね。

1-2. PATH通し(初回のみ)

~/.profile
# Node.js
export nodejs=
export nodemodules=
export PATH=${nodejs}:${nodemodules}:${PATH}

後述のスクリプトとの兼ね合いで、先にPATHを追記しておきます。

1-3. ダウンロード & インストール

nodejs-install.sh
if [ $# -ne 1 ]; then
    echo "Version number is required as argument."
    echo "Example: sudo ./nodejs-install.sh 10.0.0"
    exit 1
fi

version=${1}
filename=node-v${version}-linux-arm64
directory=/usr/local/lib/nodejs

mkdir -p ${directory}
cd ${directory}

if [ -e ${filename} ]; then
    echo "Version ${version} is already installed."
    exit 1
fi

wget https://nodejs.org/dist/v${version}/${filename}.tar.xz
tar -Jxvf ./${filename}.tar.xz
rm -rf ./${filename}.tar.xz

sed -ie "s|nodejs=.*$|nodejs="${directory}"/"${filename}"/bin|g" ~/.profile
sed -ie "s|nodemodules=.*$|nodemodules="${directory}"/"${filename}"/lib/node_modules|g" ~/.profile

source ~/.profile

ダウンロード & インストール & PATH変更
全部お任せスクリプトを書いてみました。

sudo ./nodejs-install.sh 10.1.0

インストールしたいバージョンを引数で渡すだけ。

1-4. 動作確認

node -v
npm -v

再起動後、適当にコマンドを入力して応答があったら、インストールは無事完了です。

2. GPIOのセットアップ

2-1. アクセス権の設定

sudo touch /etc/udev/rules.d/90-odroid-sysfs.rules
90-odroid-sysfs.rules
KERNEL=="gpiochip*"
SUBSYSTEM=="gpio"
ACTION=="add"
PROGRAM="/bin/sh -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'"

KERNEL=="gpio*"
SUBSYSTEM=="gpio"
ACTION=="add"
PROGRAM="/bin/sh -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'"

空のルールファイルを作成し、中にルールを記述します。

sudo addgroup gpio
sudo usermod -a -G gpio odroid

後はgpioと言うグループを追加して、先ほどのルールを適用させます。

2-2. PWM有効化

# 1系統(SPI使用可)
sudo modprobe pwm-meson
sudo modprobe pwm-ctrl

# 2系統(SPI使用不可)
# sudo modprobe pwm-meson npwm=2
# sudo modprobe pwm-ctrl

PWMは再起動毎に無効化されてしまうので、毎回このコマンドが必要になります。
面倒であれば、rcスクリプトに登録しておくと良いかも知れません。

ODROID-C2は2系統のPWMを持っていますが、片方はSPIと排他となっています。

2-3. Node.js向けGPIOラッパー

ODROID-C2用のモジュールが無かったので、作ってみました。
気が向いたらnpmにアップロードするかも?

JavaScript
c2io.js
"use strict";

const execSync = require("child_process").execSync;

const gpioDirectory = "/sys/class/gpio";
const pwmDirectory = "/sys/devices/platform/pwm-ctrl";
const adcDirectory = "/sys/class/saradc";

const pinMap = {
  7: 249,
  11: 247,
  12: 238,
  13: 239,
  15: 237,
  16: 236,
  18: 233,
  19: 235,
  21: 232,
  22: 231,
  23: 230,
  24: 229,
  26: 225,
  29: 228,
  31: 219,
  32: 224,
  33: 234,
  35: 214,
  36: 218
};

function xor(a, b){
  return (a || b) && !(a && b);
}

exports.C2GPIO = class C2GPIO{
  constructor(){}

  open(pin, direction){
    if(!pinMap[pin] || !xor(direction != "in", direction != "out")){
      console.log("Argument Invalid");
      return false;
    }
    execSync("echo " + pinMap[pin] + " > " + gpioDirectory + "/export");
    execSync("echo " + direction + " > " + gpioDirectory + "/gpio" + pinMap[pin] + "/direction");
    this.write(pin, 0);
    return true;
  }

  close(pin){
    if(!pinMap[pin]){
      console.log("Argument Invalid");
      return false;
    }
    execSync("echo " + pinMap[pin] + " > " + gpioDirectory + "/unexport");
    return true;
  }

  read(pin){
    if(!pinMap[pin]){
      console.log("Argument Invalid");
      return false;
    }
    return Number(execSync("cat " + gpioDirectory + "/gpio" + pinMap[pin] + "/value").toString());
  }

  write(pin, value){
    if(!pinMap[pin] || !xor(value != 1, value != 0)){
      console.log("Argument Invalid");
      return false;
    }
    execSync("echo " + value + " > " + gpioDirectory + "/gpio" + pinMap[pin] + "/value");
    return true;
  }
}

exports.C2PWM = class C2PWM{
  constructor(){}

  start(ch){
    if(!xor(ch != 0, ch != 1)){
      console.log("Argument Invalid");
      return false;
    }
    execSync("echo 1 > " + pwmDirectory + "/enable" + ch);
    return true;
  }

  stop(ch){
    if(!xor(ch != 0, ch != 1)){
      console.log("Argument Invalid");
      return false;
    }
    execSync("echo 0 > " + pwmDirectory + "/enable" + ch);
    return true;
  }

  setDuty(ch, ratio){
    if(!xor(ch != 0, ch != 1) || (ratio < 0 || 1024 < ratio)){
      console.log("Argument Invalid");
      return false;
    }
    execSync("echo " + (1023 / 100 * ratio) + " > " + pwmDirectory + "/duty" + ch);
    return true;
  }

  setFreq(ch, frequency){
    if(!xor(ch != 0, ch != 1) || (frequency < 0 || 1000000 < frequency)){
      console.log("Argument Invalid");
      return false;
    }
    execSync("echo " + frequency + " > " + pwmDirectory + "/freq" + ch);
    return true;
  }
}

exports.C2ADC = class C2ADC{
  constructor(){}

  read(ch){
    if(!xor(ch != 0, ch != 1)){
      console.log("Argument Invalid");
      return false;
    }
    return Number(execSync("cat " + adcDirectory + "/ch" + ch).toString());
  }
}

2-4. 動作確認

gpio-test.js
"use strict";

const c2io = require("./c2io");
const gpio = new c2io.C2GPIO();

gpio.open(7, "out");
gpio.write(7, 1);
node gpio-test.js

上記の自作モジュールをインポートしています。
7番ピンがHIGHになるはずなので、電圧計やLEDを繋いで確認します。

ここまで無事に動けば、目標達成です。

まとめ

公式配布のバイナリを使用する事で、ビルドの手間が省け、思ってたよりも簡単に構築出来ました。
もっと躓くかと思っていましたが、全然そんな事は無かったです。
これで、Expressとかに組み合わせて、イケてるIoTを開発する準備が整いました。

お疲れ様でした。

自作GPIOモジュール補足

sysfs制御なので、スイッチング/サンプリングは1~2kHz程度と低速です。

高速スイッチングが必要な場合は、PWMを推奨します。
ODROID-C2のPWM仕様は下記となっています。

  • 周波数: 最大1MHz
  • デューティ精度: 1024段階(10bit)

(/dev/gpiomem直叩きだと1MHz以上も余裕らしい...)

参考/引用(Special Thanks!)

dojyorin
独学でJavaScriptとC++やってます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away