Raspberry Pi 3とADS7843を使ってタッチスクリーンの情報を受け取る(Cでpigpioを使ってSPI通信→Nodejs)

1. 概要

前回、Raspberry Pi 3とADS7843を使ってタッチスクリーンの情報をNodejsで受け取る - QiitaではNode.jsを使ってセンサの値を受け取ったのですが、センサ周辺のプログラムはCで書いた方が処理が速いと聞いたのでCで書いてnode.jsで受け取ることにしました。

アプリケーション間(Cとnode.js)のデータのやり取りの方法がちょっと変わっているなと感じました。oscみないなものを使うと思っていたらnode.jsからCを起動してprintfを監視する方法でした。

Cでは最初Wiring Piを使おうとしましたがうまくいかず、pigpioを使用しました。

2. 環境、使用機器

3. 参考サイト

  1. pigpio library
    • 公式サイト
  2. gpio - pigpio spiXfer in C++ - Raspberry Pi Stack Exchange

    • pigpioでSPIを使うサンプルコードがあまり多くなく、この回答がとても参考になりました。
  3. windows で Node.js の child_process.exec を利用する - 自習室

    • Node.jsでCからデータを受け取る方法として参考にしました。
  4. node の spawn に関して調べてみた - Qiita

    • Node.jsでCからデータを受け取る方法として参考にしました。

4. 方法

4.1 配線

ADS7843 Raspberry Pi
CS GPIO8 (CE0) / GPIO7 (CE1)
DOUT GPIO9 (MISO)
DIN GPIO10 (MOSI)
DCLK GPIO11 (SCLK)
5V 5V
GND GND

4.2 pigpioの導入

参考サイト1のダウンロードのページが詳しいです。
apt-getでインストールできるけど古いと書いてあります。

$ sudo apt-get update
$ sudo apt-get install pigpio

日本語のサイトをいくつか見ているとデーモンを先に起動しないといけないということや、サービスに登録しておく手順などが紹介されていますが、バージョンが違うのか私の場合は不要でした。逆にデーモンが立ち上がっているとエラーが発生してしまいまた。公式サイトにも書いていない気がするので、デーモンのことは気にしなくても使えると思います。

私はサービスに登録してしまったので、Raspberry Pi起動時にデーモンが起動して毎回エラーが出てハマりました。エラーについては4.5で説明しています。

4.3 pigpioでSPI通信をするC言語のプログラムを書く

ads7843.c
#include <stdio.h>
#include <stdlib.h>
#include <pigpio.h>

#define PRESS_DETECTION 22  // GPIO22(pin15) screen press detection

int handle;

void onPressed();
int getValueX();
int getValueY();
int isPressed = 0;   // タッチスクリーンが押されたら1

int main()
{
  int speed= 1000000;

  // GPIO初期化
  if (gpioInitialise() < 0) {
    printf("initialize error");
    exit(1);
  }
  gpioSetMode(PRESS_DETECTION, PI_INPUT);         // GPIO22を入力に設定 
  gpioSetPullUpDown(PRESS_DETECTION, PI_PUD_UP);  // GPIO22を
  gpioSetAlertFunc(PRESS_DETECTION, onPressed);   // GPIOの値が変わったときに呼ばれる

  // SPI初期化
  handle = spiOpen(0, speed, 0);
  if (handle < 0) {
    printf("SPI open error");
  }

  while(1)
  {
    if(isPressed == 1){
      printf("x,%d,", getValueX());
      printf("y,%d\n", getValueY());
      fflush(stdout);
      usleep(10000);
    } 
  }

  // SPI、GPIO終了
  spiClose(handle);
  gpioTerminate();

  return 0;
}

void onPressed(int gpio, int level, uint32_t tick)
{
  if(level == 0 ){  // 0:change to low(a falling edge)
    isPressed = 1;
  }else{
    isPressed = 0;
  }

}

int getValueX(){
  int value;
  unsigned char buf[3] = {0x94, 0x00, 0x00};

  spiXfer(handle, buf, buf, 3);
  value = (((buf[1] & 0x7f) << 8) | buf[2]) >> 3;

  return value;
}

int getValueY(){
  int value;
  unsigned char buf[3] = {0xD4, 0x00, 0x00};

  spiXfer(handle, buf, buf, 3);
  value = (((buf[1] & 0x7f) << 8) | buf[2]) >> 3;

  return value;
}

コンパイルします。

コンパイル方法(ads7843.cというファイルをコンパイルしてads7843という実行ファイルを作る場合)
gcc -Wall -pthread -o ads7843 ads7843.c -lpigpio -lrt

ads7843.cと同じフォルダにads7843というファイルができる。

実行します。タッチスクリーンに触れるとx、yの座標が表示されます。

実行方法
sudo ./ads7843

4.4 node.jsで値を受け取るプログラムを書く

node.jsの中でcの実行ファイルを動かしてprintfの出力を受け取るプログラムになります。
受け取ったデータがカンマ区切りになっているので、カンマで分けてx座標、y座標の変数に入れます。

sensor.js
var spawn = require('child_process').spawn;

var sp = spawn("./ads7843",[]);
var valueX = 0, valueY = 0;

sp.stdout.on('data', function (data){      // データがある場合
  //console.log("data : " + data);
  var lines = data.toString().split(",");  // 文字列にしてカンマで分ける
  for(var i = 0; i < lines.length; i++){
    if (lines[i] == 'x'){
      valueX = lines[i+1];
    }
    else if (lines[i] == 'y'){
      valueY = lines[i+1];
    }
  }
  console.log("x:" + valueX);
  console.log("y:" + valueY);
});

sp.stderr.on('data', function (data){
  console.log("err : " + data);
});

以下のコマンドで実行します。表示されるデータはCのみで実行したものとほぼ同じです。

実行方法
sudo node sensor.js

4.5 エラー発生時

4.2でも書きましたが、私はデーモンをサービスに登録してしまっていたので、実行時にエラーが発生することが多々ありました。

エラーの種類がいくつかありましたが、例えば以下のようなエラーが発生します。

エラー例1
err : 2018-04-09 10:21:18 initInitialise: Can't lock /var/run/pigpio.pid
エラー例2
err : 2018-04-18 13:28:16 initInitialise: bind to port 8888 failed (Address already in use)

解決策はこちらのサイトに以下のように記載されており、デーモンを終了して、該当ファイルを削除してみてください。

image.png

対策
sudo killall -9 pigpiod # kill the daemon
sudo rm /var/run/pigpio.pid

最終的な解決はサービスのファイル/lib/systemd/system/pigpiod.serviceを削除しました。いろんなサイトを見て設定しているうちに設定していましたが、古い情報だったのかもしれません。
ちなみに中身はこれです。

pigpiod.service
[Unit]
Description=Daemon required to control GPIO pins via pigpio
[Service]
ExecStart=/usr/bin/pigpiod -l
ExecStop=/bin/systemctl kill pigpiod
Type=forking
[Install]
WantedBy=multi-user.target
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.