前回の記事 Groovy-IoTをNode.jsから操ってみた で、Groovy-IoTをNode.jsから扱えるようにしたのですが、Node.js向けのAPIがまだArduinoっぽくなかったので、今回はもう少しArduinoに近づけます。
例えば、
Serial.println()
とするべきところを、
Serial_println()
となっていました。これを直します。
これで、Arduinoにより近づいたので、いくつかのGroveのドライバを移植しました。試したのは以下の4つです。すべてマルツ秋葉原本店( https://www.marutsu.co.jp/GoodsListNavi.jsp?path=1100020003 )で購入しました。
- Grove - LED Bar
- Grove - PIR Motion Sensor
- Grove - Digital Light Sensor
- Grove - Light Sensor(P)
(2つLight Sensorがあるのですが、前者がI2C接続、後者がアナログGPIO接続です)
(ちなみに、Grove - Temperature, Humidity, Pressure and Gas Sensor(BME680)も買ったのですが、C言語すぎて移植を中断中)
ソースコードもろもろはGithubに上げてあります。
https://github.com/poruruba/node-groovy-iot
ArduinoっぽいAPI
ネイティブ拡張モジュールの実装では、すべてのAPIは横並びでした。
たとえば、Serial_printlnだったりWire_beginTransmissionしました。
一方、Arduinoでは、Serial.println となっていて、「.」で表現しています。これは、世にいうオブジェクト指向のClass/Objectに相当します。
Node.jsにもあるので、横並びのAPIを呼び出すNode.jsのクラスを実装しようと思います。
さっそくソースコードです。
const mcp2221 = require('mcp2221native');
var mcp2221_initialized = false;
function mcp2221_initialize(){
var ret = mcp2221.initialize();
if( ret != 0 )
throw 'mcp2221.Serial_initialize error';
mcp2221_initialized = true;
}
class Serial{
initialize(sport){
if( !mcp2221_initialized )
mcp2221_initialize();
var ret = mcp2221.Serial_initialize(sport);
if( ret != 0 )
throw 'mcp2221.Serial_initialize error';
this.DEC = -1;
this.BIN = -2;
this.OCT = -3;
this.HEX = -4;
}
begin(speed){
return mcp2221.Serial_begin(speed);
}
end(){
mcp2221.Serial_end();
}
available(){
return mcp2221.Serial_available();
}
read(){
return mcp2221.Serial_read();
}
peek(){
return mcp2221.Serial_peek();
}
flush(){
mcp2221.Serial_flush();
}
write(param, len){
if( len === undefined)
return mcp2221.Serial_write(param);
else
return mcp2221.Serial_write(param, len);
}
write(buf, len){
}
convert(data, format){
var str;
if( typeof data == 'string'){
str = data;
}else{
if( format >= 0 ){
str = data.toFixed(format);
}else{
switch(format){
case this.BIN:
str = data.toString(2);
break;
case this.OCT:
str = data.toString(8);
break;
case this.HEX:
str = data.toString(16);
break;
default:
str = data.toString(10);
break;
}
}
}
return str;
}
print(data, format){
var str = this.convert(data, format);
return mcp2221.Serial_print(str);
}
println(data, format){
var str = this.convert(data, format);
return mcp2221.Serial_println(str);
}
}
class GPIO{
initialize(){
if( !mcp2221_initialized )
mcp2221_initialize();
var ret = mcp2221.GPIO_initialize();
if( ret != 0 )
throw 'mcp2221.GPIO_initialize error';
this.INPUT = 0;
this.OUTPUT = 1;
this.LOW = 0;
this.HIGH = 1;
this.DEFAULT = 0;
}
pinMode(pin, mode){
mcp2221.GPIO_pinMode(pin, mode);
}
digitalWrite(pin, value){
mcp2221.GPIO_digitalWrite(pin, value);
}
digitalRead(pin){
return mcp2221.GPIO_digitalRead(pin);
}
analogRead(pin){
return mcp2221.GPIO_analogRead(pin);
}
analogWrite(pin, value){
mcp2221.GPIO_analogWrite(pin, value);
}
analogReference(type){
mcp2221.GPIO_analogReference(type);
}
analogReadResolution(bits){
mcp2221.GPIO_analogReadResolution(bits);
}
analogWriteResolution(bits){
mcp2221.GPIO_analogWriteResolution(bits);
}
}
class Wire{
initialize(){
if( !mcp2221_initialized )
mcp2221_initialize();
var ret = mcp2221.Wire_initialize();
if( ret != 0 )
throw 'mcp2221.Wire_initialize error';
}
beginTransmission(address){
mcp2221.Wire_beginTransmission(address);
}
endTransmission(){
mcp2221.Wire_endTransmission();
}
requestFrom(address, count){
return mcp2221.Wire_requestFrom(address, count);
}
available(){
return mcp2221.Wire_available();
}
read(){
return mcp2221.Wire_read();
}
write(param, len){
if( len === undefined )
return mcp2221.Wire_write(param);
else
return mcp2221.Wire_write(param, len);
}
}
var gpio = new GPIO();
module.exports = {
initialize : mcp2221_initialize,
Serial : new Serial(),
GPIO: gpio,
Wire: new Wire(),
pinMode : gpio.pinMode,
digitalWrite: gpio.digitalWrite,
digitalRead: gpio.digitalRead,
analogRead: gpio.analogRead,
analogWrite: gpio.analogWrite,
analogReference: gpio.analogReference,
analogReadResolution: gpio.analogReadResolution,
analogWriteResolution: gpio.analogWriteResolution
}
使い方
まずは、先ほど作成したNode.jsのソースコードをrequireします。
const { Serial, GPIO, Wire, pinMode, digitalRead, analogRead } = require('./mcp2221lib');
ここで、利用したいクラスと、利用したいGPIO関係の関数を指定します。
次に、初期化します。
Serial.initialize(0);
GPIO.initialize();
Wire.initialize();
Serial.initializeに指定している数字は/dev/ttyACM0 の数字です。複数の仮想COMポートを持ったデバイスをマイコンに接続していると、Groovy-IoTに割り当たる数字が変わってきますので、確認して指定してください。
あとは、Arduinoでよく見るAPI呼び出しができるようになります。Serial.printlnやWire.beginTransactionなど。
以下を参考にさせていただいて真似ました。
Arduino 日本語リファレンス
http://www.musashinodenpa.com/arduino/ref/
ドライバの移植
ドライバが必要なのは、以下の2つです。
- Grove - LED Bar
- Grove - Digital Light Sensor
それぞれ、Grove_LED_Bar.js、Digital_Light_TSL2561.js というファイル名で作成しました。
Arduinoにライブラリインストールされたcまたはcpp言語ファイルを移植したものです。
ソースコードは、GitHubを参照してください。
以下の2つは、Digital InputとAnalog Inputですので、ドライバは不要です。
- Grove - PIR Motion Sensor
- Grove - Light Sensor(P)
使い方はこんな感じです。
const { Serial, GPIO, Wire, pinMode, digitalRead, analogRead } = require('./mcp2221lib');
const ledbar = require('./Grove_LED_Bar');
const TSL2561 = require('./Digital_Light_TSL2561');
async function test(){
Serial.initialize(0);
GPIO.initialize();
Wire.initialize();
/* UART */
/*
Serial.begin(9600);
var ret = Serial.println("Hello World");
console.log(ret);
*/
/* Grove - PIR Motion Sensor */
/*
pinMode(2, GPIO.INPUT);
while(true){
if( digitalRead(2) )
console.log("Hi, people is comming");
}
*/
/* Grove - Digital Light Sensor */
/*
TSL2561.init();
console.log(await TSL2561.readVisibleLux());
*/
/* Grove - LED Bar */
/*
var Grove_LED_Bar = new ledbar(1, 0, 0, 10);
Grove_LED_Bar.setLevel(3);
*/
/* Grove - Light Sensor(P) */
/*
console.log( analogRead(2) );
*/
return;
}
test();
ちょっと、補足すると、digitalReadやanalogReadで指定している番号は、PIN番号になります。
どのPIN番号が、Groovy-IoTのどのポートに割り当たっているかは、以下の方のページが非常に参考になります。
Groovy-IoT + kikori (2. 標準ハードウェア)
結局、以下の通りです。
・GPIO1(GPIO/ADC/CLKR)
Pin1:GP0 ★これがPIN番号0
Pin2:GP1/ADC1/CLKR ★これがPIN番号1
・GPIO2(GPIO/ADC/DAC)
Pin1:GP2/ADC2/DAC1 ★これがPIN番号2
Pin2:GP3/ADC3/DAC2 ★これがPIN番号3
digitalRead(2) や analogRead(2) では2を指定しているので、GPIO2のGrove端子のPin1に対応します。(GPIO1のPin1も使いたかったのですが、DACやADCとしては使えないようです。)
以上