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

Node.jsでraspberry piのハードウェアを叩く7つの方法

More than 1 year has passed since last update.

RPiにはいろいろなHWインタフェースがあるわけですが
これだけの種類を押さえといたら使いこなせるだろう程度のHWインタフェース 7つ紹介

動作確認環境
Raspberry pi 3 type B+
nodejs v8.14.0

0. [前知識] Raspberry Pi のIO

前知識としてIOのマップはこんな感じ

  • GPIO
    • GPIOxx のピン
  • UART
    • UART0_TXD : 8pin
    • UART0_RXD : 10pin
  • SPI
    • SPI0_SCLK : 23pin
    • SPI0_MOSI : 19pin
    • SPI0_MISO : 21pin
    • SPI0_CE0_N : 24pin
    • SPI0_CE1_N : 26pin
  • I2C
    • I2C_SCL1 : 5pin
    • I2C_SDA1 : 3pin

image.png
引用 : https://medium.com/a-swift-misadventure/using-swift-to-control-the-raspberry-pi-gpio-pins-and-turn-an-led-on-f31e33c3cb9a

1. GPIOを叩く

IOをON/OFFしてLチカ

$ npm i rpi-gpio -s
var gpio = require('rpi-gpio');

const LED_PIN = 40; // 40番ピン(GPIO40ではない、GPIO21)
const LED_BLINK_DELAY_MS = 1000;  // 1秒毎にLチカ

var ledOn = true;

// LED ピンをOUT方向に設定
gpio.setup(LED_PIN, gpio.DIR_OUT, () => {

  // LED_BLINK_DELAY_MS毎にLED ON/OFF
  setInterval(() => {
    if(ledOn) {
      // LED ピンをlowに設定
      gpio.write(LED_PIN, false);
      ledOn = false;
    } else {
      // LED ピンをhighに設定
      gpio.write(LED_PIN, true);
      ledOn = true;
    }
  },LED_BLINK_DELAY_MS);
});

IMG_20181211_233126.jpg


入力待ちしたいときはEventEmitterで待つこともできる

IMG_20181211_233100.jpg

var gpio = require('rpi-gpio');

const BUTTON_PIN = 38;

// ボタンの値変化イベント
gpio.on('change', (ch, value) => {
  console.log('read channel : '+ch+', value : '+value);
});

// ボタンピンをIN方向かつON->OFF/OFF->ONの両エッジでイベント発生
gpio.setup(BUTTON_PIN, gpio.DIR_IN, gpio.EDGE_BOTH);

ボタン押す/離す度にvalueの値が変化

$ node button.js
read channel : 38, value : true
read channel : 38, value : false
read channel : 38, value : true
read channel : 38, value : false
read channel : 38, value : true
read channel : 38, value : false
read channel : 38, value : true

参考:
rpi-gpio
https://www.npmjs.com/package/rpi-gpio

2. UART通信する

マイコンとの通信はほぼこれ

RPiでUARTを使う場合は raspi-config でUARTを有効にする必要あり

$ sudo raspi-config

-> 5 Interfacing Option
-> P6 Serial
-> No : Would you like a login shell to accessible over serial?
-> Yes : Would you like the serial port hardware to be enabled?
-> OK
-> Finish
-> Reboot

リブート後デバイスファイル /dev/serial0 が出ていればOK

$ ls -la /dev/serial0
lrwxrwxrwx 1 root root 5 Dec 11 11:39 /dev/serial0 -> ttyS0

ちなみに、RPiのピンUARTじゃなくUSBシリアルを使ってもOK
FTDI系は /dev/ttyUSBx
Arduino系は /dev/ttyACMx
にデバイスファイルが現れる

今回はRPi上のTx/Rxの2ピンを直接つないでエコーバック通信させる

  • UART
    • UART0_TXD : 8pin
    • UART0_RXD : 10pin

IMG_20181211_233141.jpg

コーディングする

$ npm i serialport -s
var SerialPort = require('serialport');

const DEVICE_FILE_PATH = '/dev/serial0';
const SERIAL_OPTION = { baudRate: 115200 };

var port = new SerialPort(DEVICE_FILE_PATH, SERIAL_OPTION);

port.on('data', (data) => {
  console.log('receive data : ', data.toString());
});

port.on('error', (err) => {
  console.log('ERROR ', err);
});

var num = 0;

setInterval( () => {
  var text = 'hoge';
  port.write(text+num, (err) => {
    console.log('send data : ', text+num);
    num++;
  });
}, 1000);
$ sudo node uart.js
send data :  hoge0
receive data :  hoge0
send data :  hoge1
receive data :  hoge1
send data :  hoge2
receive data :  hoge2
send data :  hoge3
receive data :  hoge3
send data :  hoge4
receive data :  hoge4

参考:
node-serialport
https://github.com/node-serialport/node-serialport

3. SPI通信する

ICのインタフェースがSPIの場合はこれ

RPiでSPIを使う場合は raspi-config でSPIを有効にする必要あり

$ sudo raspi-config

-> 5 Interfacing Option
-> P4 SPI
-> Yes : Would you like the SPI interface to be enabled?
-> OK
-> Finish
-> Reboot

リブートするとSPIデバイスファイルができる
/dev/spidev0.0SPI0_CE0_N : 24pin をアサート
/dev/spidev0.1SPI0_CE1_N : 26pin をアサート

$ ls -la /dev/spidev0.*
crw-rw---- 1 root spi 153, 0 Dec 11 11:39 /dev/spidev0.0
crw-rw---- 1 root spi 153, 1 Dec 11 11:39 /dev/spidev0.1

RPiのピンとICとつなげる(今回は何もつなげず空振りさせる)

  • SPI
    • SPI0_SCLK : 23pin
    • SPI0_MOSI : 19pin
    • SPI0_MISO : 21pin
    • SPI0_CE0_N : 24pin
    • SPI0_CE1_N : 26pin

コーディングする

$ npm i pi-spi -s
var SPI = require('pi-spi');

const DEVICE_FILE_PATH = '/dev/spi0';

var spi = SPI.initialize(DEVICE_FILE_PATH);

var buf = new Buffer.alloc(8);

spi.transfer(buf, buf.length, function(err, data) {
    console.log('transfer return : ', data);
});

今回は何もつなげていないが、SPI通信可能なICを繋げてICの仕様に従った値を送れば、値が返ってくる

$ node spi.js
transfer return :  <Buffer 00 00 00 00 00 00 00 00>

参考:
pi-spi
https://www.npmjs.com/package/pi-spi

4. I2C通信する

ICのインタフェースがI2Cの場合はこれ

RPiでI2Cを使う場合は raspi-config でI2Cを有効にする必要あり

$ sudo raspi-config

-> 5 Interfacing Option
-> P5 I2C
-> Yes : Would you like the ARM I2C interface to be enabled?
-> OK
-> Finish
-> Reboot

リブートするとI2Cデバイスファイルができる

$ ls -la /dev/i2c-1
crw-rw---- 1 root i2c 89, 1 Dec 11 11:39 /dev/i2c-1

RPiのピンとICとつなげる(今回は何もつなげず空振りさせる)

  • I2C
    • I2C_SCL1 : 5pin
    • I2C_SDA1 : 3pin

コーディングする

$ npm i i2c-bus -s
var i2c = require('i2c-bus');

const DEVICE_NUMBER = 1;
const TARGET_IC_ADDR = 0x32;

var i2c1 = i2c.openSync(DEVICE_NUMBER);

var readBuf = new Buffer.alloc(0x10);
i2c1.i2cReadSync(TARGET_IC_ADDR, readBuf.length, readBuf);

console.log(readBuf);

適当にI2Cアドレスをあわせてつなぐと値を返してくれる

$ node i2c.js
<Buffer c0 12 58 12 02 11 12 18 00 7f 3f 7f 7f 3f 00 20>

ちなみに、I2Cアドレスが間違っていたり、ピンのつながりがよろしくないとエラーになる

$ node i2c.js
fs.js:675
  return binding.read(fd, buffer, offset, length, position);
                 ^

Error: EREMOTEIO: remote I/O error, read
    at Object.fs.readSync (fs.js:675:18)
    at Bus.i2cReadSync (/home/pi/Documents/akita3-expansion-driver/node_modules/i2c-bus/i2c-bus.js:536:15)
    at Object.<anonymous> (/home/pi/Documents/akita3-expansion-driver/test.js:10:6)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)

参考:
i2c-bus
https://www.npmjs.com/package/i2c-bus

5. USB HIDを使う

USBキーボード入力を待ち構えてキャッチできる
ピッと読み込むバーコードリーダーとかスワイプするタイプのカードリーダーとかにも使える

近場にあるUSBキーボード HHKB をひっぱりだしてきてつなげる
USBのVIDとPIDが必要になるので番号を控えておく

$ lsusb
Bus 001 Device 006: ID 04fe:0006 PFU, Ltd
Bus 001 Device 005: ID 04fe:0008 PFU, Ltd
Bus 001 Device 004: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

ID 04fe:0006 の部分に注目して VID:0x04fe, PID:0x0006 ということがわかる
※ HHKB挿すとどっちがHIDかわからん。。。

npm i するときにgypでコンパイルを通すためのパッケージを入れる

$ sudo apt-get install libusb-1.0-0-dev -y

コーディング

$ npm i node-hid-stream -s
var KeyboardCharacters = require('node-hid-stream').KeyboardCharacters;
var characters = new KeyboardCharacters({ vendorId: 0x04fe, productId: 0x0006 });

characters.on("data", function(data) {
  console.log(data);
});

実行には管理者権限が必要
適当に abcde と入力してみる

$ sudo node hid.js
a

b

c

d

e

参考:
node-hid-stream
https://www.npmjs.com/package/node-hid-stream

6. MQTTを使う

HW連携では時々使われる。RPi同士の通信でも便利。

とりあえずRPiを2台用意してお互いをネットにつなげておく

 | RPi A (broker:192.168.1.10, publisher) | -- network -- | RPi B (subscriber) |

[RPi A] mqtt ブローカを用意

$ sudo apt install mosquitto -y
$ sudo systemctl start mosquitto

[RPi B] subscriber でメッセージを待ち受ける

RPi B上にmqtt subscriberを置く

$ npm i mqtt -s
var mqtt = require('mqtt');
var client = mqtt.connect('mqtt://192.168.1.10');

const TOPIC = 'hoge/foo/bar';

client.on('connect', function(){
  console.log('subscriber connected.');
});

client.subscribe(TOPIC, function(err, granted){
  console.log('subscriber subscribed.');
});

client.on('message', function(topic, message){
  console.log('subscriber received topic:', topic, 'message:', message.toString());
});

subscriber側で待ち状態にする

$ node sub.js
subscriber connected.
subscriber subscribed.

[RPi A] publisher でメッセージを送る

$ npm i mqtt -s
var mqtt = require('mqtt');
var client = mqtt.connect('mqtt://192.168.1.10');

var message = 'Hello MQTT!';
var topic = 'hoge/foo/bar'

client.on('connect', function(){
  console.log('publisher connected.');

  client.publish(topic, message);
  console.log('send topic:', topic, ', message:', message);
});
$ node pub.js
publisher connected.
send topic: hoge/foo/bar , message: Hello MQTT!

[RPi B] subscriber でメッセージを受信する

さっき待ち受けていた端末にメッセージが来ている

$ node sub.js
subscriber connected.
subscriber subscribed.
subscriber received topic: hoge/foo/bar message: Hello MQTT!

7. Bluetooth Low Energyを使う

Bluetooth Low Energy もサクッと叩ける
GATTの構造だけ前提知識として入れておけばなんとかなる

※ 動作確認間に合わなかったので参考URLだけ

[RPi A] セントラルになる

$ npm i noble -s

このあたりのコード参考
https://github.com/noble/noble/blob/master/examples/

実行にはroot権限必要

[RPi B] ペリフェラルになる

$ npm i bleno

このあたりのコード参考
https://github.com/noble/bleno/tree/master/examples/

実行にはroot権限必要

参考:
noble
https://github.com/noble/noble

bleno
https://github.com/noble/bleno

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