Groovy-IoTをNode.jsから操ってみた

今回遊ぶのは、大宮技研さんの「Groovy-IoT」です。
　http://www.omiya-giken.com/?page_id=2262

世の中にたくさん出回っているGroveセンサをつなぐことができます。
ホストPCとはUSBで接続するので、Raspberry-Piなどの一般的なPCやマイコンで制御することができるようになります。
今回は、ホストPCとして、Raspberry-Piを使います。

大宮技研さんの方で、「Kikori」というclojureベースのツールを用意していただいているのですが、もっと気軽に扱いたかったので、Node.jsから扱えるようにネイティブ拡張モジュールを作ってみました。

(参考) Kikori
　https://gitlab.com/myst3m/kikori

それから、Groveセンサを操作するためのライブラリは、Arduinoが多いので、ネイティブ拡張モジュールのAPIは、ArduinoのGPIOとI2CとSerialのそれに似た形してみました。

今回サンプルとして動かしてみるのは、「Grove - LED Bar」です。
　https://www.seeedstudio.com/Grove-LED-Bar-v2-0.html

ソースコードもろもろをGithubに上げておきました。
　https://github.com/poruruba/node-groovy-iot

[2019/8/4 追記]
　アナログ入出力に対応させました。ちょっと動きが怪しいかも。

この続編です。
　[Groovy-IoTをNode.jsから操ってみた：もう少しArduinoっぽいAPI](https://qiita.com/poruruba/items/a21f4579fe01ce6d1586)

# Groovy-IoT をC言語で触ってみる

Groovy-IoT には、Microchip製のMCP2221Aというチップが乗っています。
そこにI2C、GPIO、UARTのペリフェラルがありGroveモジュールの接続端子に接続されています。
USBでGroovy-IoTとホストPCを接続するのですが、操作はHIDで行うので、ホストPC側はHIDさえ扱えればよいです。
ちょうど、MPC2221用にライブラリを提供していただいている方がいましたので、ありがたく使わせていただきました。

libmcp2221
　https://github.com/zkemble/libmcp2221

コンパイルに外部ライブラリが必要ですので、インストールしておきます。

```
> apt-get install libudev-dev libusb-1.0-0-dev
```

以下のファイルを使いますので、libmcp2221のWebページを参考にダウンロードしておきます。

* libmcp2221.c
* libmcp2221.h
* hid.c
* hidapi.h

もう一つ補足しておきます。
ホストPCに接続すると、HIDのほかに、仮想COMポートも割り当たります。
MCP2221AのペリフェラルとしてUARTがあり、Grove端子に割り当てられていますが、それにアクセスするには、ホストPC側に現れた仮想COMポートを使うのです。

dmesgを載せておきます。

```
[164546.769340] Indeed it is in host mode hprt0 = 00021501
[164546.979214] usb 1-1: new full-speed USB device number 13 using dwc_otg
[164546.979575] Indeed it is in host mode hprt0 = 00021501
[164547.222987] usb 1-1: New USB device found, idVendor=04d8, idProduct=00dd, bcdDevice= 1.00
[164547.223008] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[164547.223017] usb 1-1: Product: MCP2221 USB-I2C/UART Combo
[164547.223026] usb 1-1: Manufacturer: Microchip Technology Inc.
[164547.240438] cdc_acm 1-1:1.0: ttyACM0: USB ACM device
[164547.250507] hid-generic 0003:04D8:00DD.000C: hiddev96,hidraw0: USB HID v1.11 Device [Microchip Technology Inc. MCP2221 USB-I2C/UART Combo] on usb-20980000.usb-1/input2
```

# Groovy-IoT を一般ユーザで触る

Groovy-IoTをRaspberry-Piにつなぐと、特に追加のデバイスドライバのインストールをせずとも、自動的に認識してくれるので楽ちんです。
ですが、そのままではルート権限を持ったユーザしかアクセスできませんので、一般ユーザでもアクセスできるようにします。

まず、HIDアクセスです。
/etc/udev/rules.d 配下に、以下のファイルを作成して配置します。

```99-hid.rules
KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="00dd", MODE="0666"
```

次に、仮想COMポートです。
仮想COMポートは、/dev/ttyACM0 として見えますが、この所有者groupはdialoutです。ですので、そのgroupに、アクセスしたい一般ユーザを追加すればよいです。

```
> sudo gpasswd -a ユーザ名 dialout
```

念のため、設定後、Raspberry Piを再起動しましょう。

# Arduino っぽく扱う

以下、こんな感じのC言語の関数にしてみました。
Arduinoっぽくないですか？ソースコードは割愛します。Githubを参照して下さい。

```c:mcp2221.h
#ifndef _MCP2221_H_
#define _MCP2221_H_

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

long MCP2221_initialize(void);

long Wire_initialize(void);
long Wire_dispose(void);
void Wire_beginTransmission(unsigned char address);
int Wire_write_byte(unsigned char value);
int Wire_write_str(const char *p_str);
int Wire_write_array(const unsigned char *p_data, int length);
void Wire_endTransmission(void);
int Wire_requestFrom(unsigned char address, int count);
int Wire_available(void);
unsigned char Wire_read(void);

#define HIGH	1
#define LOW		0

#define INPUT			0
#define OUTPUT			1
#define INPUT_PULLUP	2

#define DEFAULT		0

long GPIO_initialize(void);
void pinMode(unsigned char pin, unsigned char mode);
void digitalWrite(unsigned char pin, unsigned char value);
unsigned char digitalRead(unsigned char pin);

unsigned short analogRead(unsigned char pin);
void analogWrite(unsigned char pin, unsigned short value);
void analogReference(unsigned char type);
void analogReadResolution(unsigned char bits);
void analogWriteResolution(unsigned char bits);

void Serial_initialize(unsigned char sport);
long Serial_begin(unsigned long speed);
void Serial_end(void);
int Serial_available(void);
int Serial_read(void);
int Serial_peek(void);
void Serial_flush(void);
int Serial_write_byte(unsigned char val);
int Serial_write_str(const char *p_str);
int Serial_write_array(const unsigned char *p_data, int len);
int Serial_print(const char *data);
int Serial_println(const char *data);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif
```

# Node.jsネイティブ拡張モジュールを作成する

ネイティブ拡張モジュールを作成するために、node-gypを使います。

```
> npm install -g node-gyp
```

まずは適当なフォルダを作成しましょう

```
> mkdir mcp2221native
> cd mcp2221native
```

binding.gypを作成します。

```gyp:binding.gyp
{
	"targets": [
	{
		"target_name": "mcp2221native",
		"sources": [ "mcp2221native.cc", "mcp2221.c", "hid.c", "libmcp2221.c", "utility.cc"],
		"libraries": [
			"-ludev"
		]
	}
	]
}
```

上記gypファイル見てわかる通り、以下のファイルを使います。

* mcp2221native.cc：これがネイティブ拡張モジュールの肝となるファイルです。
* mcp2221.c：先ほど作成しました。
* hid.c：ダウンロードしておいたファイルです。
* libmcp2221.c：ダウンロードしておいたファイルです。
* utility.cc：ユーティリティを集めたものです。作成しておきました。

mcp2221native.cc がネイティブ拡張の中心的な役割を果たしているのですが、やっていることは、Node.jsから呼ばれるI/Fとそれをmcp2221.cで作成した関数に橋渡ししているだけです。

ちなみに、"-ludev" は、内部でudevを使っており、このライブラリをリンクする必要があるためです。

まずは、以下。コンパイルの下準備だそうです。フォルダやMakefileを作ってくれます。

```
> node-gyp configure
```

以下で、ソースコードをコンパイルします。

```
> node-gyp build
```

これで、フォルダ「mcp2221native\build\Release」に、「mcp2221native.node」ができあがりました。

つぎに、このnodeファイルを、Nodeから、require('mcp2221native') って感じで使えるようにします。

どこか適当なフォルダを作成し、そこにコピー（もしくはシンボリックリンク）します。
例えば、以下の通りです。

```
> mkdir ~/node_native
> cp mcp2221native/build/Release/mcp2221native.node ~/node_native/
```

次に、Nodeがこれを検索できるようにします。
.bashrcの最後に以下を追記します。

```
export NODE_PATH=~/node_native
```

これでOKです。.bashrcを反映するために以下を実行しておきます。

```
source ~/.bashrc
```

# Groove LED Barを使ってみる

さっそく、Groovy-IoTとそれにつないだGroveセンサを操作するNode.jsのソースを作成ましょう。
以下のような感じでGroovy-IoTにアクセスできます。
詳細は、Githubのmcp2221_test/index.jsを参考にしてください。

```js:index.js
// まずはrequreして。。。
const mcp2221 = require('mcp2221native');
// Groovy-IoTに接続して。。。
var ret = mcp2221.initialize(0);
// ペリフェラルを初期化して。。。
ret = mcp2221.GPIO_initialize();
ret = mcp2221.Wire_initialize();
ret = mcp2221.Serial_initialize();
ret = mcp2221.Serial_begin(115200);
// 例えばこんな感じ。。。
mcp2221.GPIO_pinMode(__pinClock, OUTPUT);
mcp2221.GPIO_pinMode(__pinData, OUTPUT);
```

以上