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

# 1. 概要

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

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

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

# 2. 環境、使用機器

* Raspberry Pi 3 (Rasbpian Stretch)
* 4線抵抗膜型タッチスクリーン(4.3インチ)
    * 1.25mmピッチ4P、FFCコネクタ
    * http://www.aitendo.com/product/16196
* タッチコントローラモジュールADS7843
    * 1.0mmピッチ4ピンFFCコネクタ（下接点）
    * http://www.aitendo.com/product/2308

# 3. 参考サイト

1. [pigpio library](http://abyz.me.uk/rpi/pigpio/index.html)
    * 公式サイト
2. [gpio - pigpio spiXfer in C++ - Raspberry Pi Stack Exchange](https://raspberrypi.stackexchange.com/a/79128)
    * pigpioでSPIを使うサンプルコードがあまり多くなく、この回答がとても参考になりました。

3. [windows で Node.js の child_process.exec を利用する - 自習室](http://izmiz.hateblo.jp/entry/2014/07/22/210600)
    * Node.jsでCからデータを受け取る方法として参考にしました。
4. [node の spawn に関して調べてみた - Qiita](https://qiita.com/TsuyoshiUshio@github/items/cf4b28e7999403f7a04c)
    * 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のダウンロードのページ](http://abyz.me.uk/rpi/pigpio/download.html)が詳しいです。
`apt-get`でインストールできるけど古いと書いてあります。

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

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

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

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

```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座標の変数に入れます。

```javascript: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)
```

解決策は[こちらのサイト](https://www.raspberrypi.org/forums/viewtopic.php?t=76559#p547423)に以下のように記載されており、デーモンを終了して、該当ファイルを削除してみてください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/94069/8416ea66-2cd9-f7bf-999f-ad59fd1f3169.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
```