Node.jsのアプリケーションからArduinoのロボットを制御する

前回 、babbler_hライブラリを備えたArduinaを使用して、ロボットで質問と回答モードを使用して独自のミニ端末を作成する方法を検討しました。 今日は、JavaScript + Node.jsを使用して、同じライブラリを使用してデスクトップアプリケーションからロボットを制御する方法を見ていきます。



ロボットとデータを交換するには、JavaScript + Node.jsのクライアント部分で、この機会のために特別に作成されたBabbler.jsライブラリを使用します。 Babbler.jsは、標準のノードシリアルポートライブラリを使用してシリアルポートを操作しますが、いくつかの追加機能を構築します。



ライブラリ機能



-ライブラリを使用すると、デバイスに接続し、コマンドを送信し、回答を受信できます。

-ライブラリ自体は接続を維持し、内部のすべての技術的なニュアンスを隠します。ギャップを監視し、接続ステータスのすべての変更を通知し、切断と再接続を許可します。

-コマンドは送信キューに追加され、デバイスに1つずつ送信されます。

-ライブラリは、キューに追加された瞬間から応答が受信されるかエラーが発生するまで、チームで各パッケージを監視します。 デバイスステータスの表示やデバッグに役立つかもしれない公開イベントを生成します。

-ユーザーコードは、チームのライフパスの完了に関する通知(デバイスからの応答またはエラーメッセージ)を常に受け​​取ります。

-ライブラリは、デバイスへの途中のコマンドで発生する可能性のある例外的な状況を処理し、対応するエラーメッセージを生成します。 たとえば、コマンドを送信キューに追加し、接続コードを引き出すことができます:ユーザーコードはコマンド実行エラーに関するメッセージを受け取ります(コマンドがロボットに送信される前に接続が切断されます/コマンドがロボットに送信された後に接続が切断さます)、その後アプリケーションはデバイスに再接続します(ロボットがもちろん、それは再びワイヤーで接続されます)、動作し続けます。

-ライブラリは不正なデバイスの動作を許容します。ロボットは回答の送信を忘れたり、間違った時刻に回答を送信したり、誤った回答を送信したり、デバッグガベージを通信チャネル(シリアルポート)にダンプしたりする場合があります。 ライブラリは、せいぜい無効なパケットを無視し、正しいパケットを待ちます。最悪の場合、ロボットがコマンドを完了していないことを示すメッセージをユーザーコードに送信します(つまり、応答が受信されていません)。

-2つの条件が満たされた後、デバイスは接続されたと見なされます:通信チャネルが開いている、デバイスがpingコマンドに正しい答え「ok」を送信した。



ロボットのファームウェアに関する追加の制限:



-ロボットはコマンドを受け入れ、クライアントコマンド識別子をサポートするJSON形式で応答を送信する必要があります。

-ロボットのファームウェアには、必ずpingコマンドが含まれている必要があります (これがないと、接続は確立されません)。

-デバイスは、受信したコマンドに5秒以内に応答を送信する必要があります。そうしないと、クライアントコードはコマンドが完了していないと見なします(BBLR_ERROR_REPLY_TIMEOUTエラーを受信します)。

-ロボットが、コマンドで、5秒以上続く(ポイントAからポイントBに移動する)特定の長続きするアクションを実行し、アクションが完了したことをコントロールパネルに通知する必要がある場合があります。 この場合、ロボットファームウェアに次の2つのコマンドを入力する必要があります。「 アクションの実行プロセスを開始する 」(コード「ok」で即座に戻る)と「 起動したアクションの実行ステータスを取得する 」(「進行中」/「準備完了」)。 コンソールは、最初のコマンドでアクションを実行するプロセスを開始し、定期的にそのステータスを確認して、2番目のコマンドを繰り返し送信します。



主なリンク:



-ロボット用ライブラリ: babbler_h

-Node.jsのライブラリ: babbler-js

-babbler-jsの例: babbler-js-demo



プロトコル



ロボットはコマンドを受信し、JSON形式で応答を送信する必要があります。 データパケットは、コマンドまたは応答を含むJSON文字列です。 データパケットは、改行文字で区切られます。



ロボットは、次の形式のJSON形式のコマンドを受け入れる必要があります。



{"cmd": "help", "id": "34", "params":["--list"]}
      
      





ここに:

-cmd-コマンド名、文字列

-params-コマンドパラメーター、文字列の配列

-id-クライアントコマンド識別子、文字列(オプション)



コマンド名とパラメーターは明確です。 クライアント識別子-クライアントによって生成され、チームとともに送信される任意の値。ロボットはそれを回答とともに送信します。 チーム識別子により、クライアントは送信されたコマンドのどれが答えになったかを簡単に判断できます。 値の一意性はクライアント側で提供され、ロボットは受信した値を応答として単にコピーし、それ以上分析しません。



答えは、次の形式のJSON形式でパッケージ化する必要があります。



 {"cmd": "help", "id": "34", "reply": "help ping ledon ledoff"}
      
      





ここに:

-cmd-ソースコマンド、行

-id-クライアントコマンド識別子(元の値がコピーされます)、文字列

- 応答 -応答(コマンド実行の結果)、行



おそらく、新しいバージョンでは、params値は元のコマンドパラメーターのコピーと共に応答内に表示されます。 これはリソースの効率的な消費ではないかもしれませんが、デバッグの追加の利便性です。



ロボットのファームウェア



前の記事でArduinoのbabbler_hライブラリのインストールとそのア​​プリケーションの機能を確認することをお勧めします。 ここで、コマンドを受け取り、JSON形式で応答を送信できるスケッチの例をすぐに示します。 JSONを操作するために必要な機能はbabbler_jsonモジュールに実装されてます。



コードを見る



例として、電球_2_babbler_custom_cmd.inoを点滅させるためのledonledoffの 2つのユーザーコマンドの例を取り上げ 、リクエストを受け入れてJSON形式で応答を送信します。 元のコマンドラインオプションと比較すると、正確に2つの違いがあります。



1.ヘッダーのbabbler_json.hライブラリを接続します。



 #include "babbler_json.h"
      
      





2. setupのプリセットのbabbler_serial_set_input_handlerで、 handle_input_simpleハンドラーをhandle_input_jsonに置き換えます



  babbler_serial_set_input_handler(handle_input_json);
      
      





の代わりに



  babbler_serial_set_input_handler(handle_input_simple);
      
      





ユーザーコマンドのコードを含む(最初に)違いはまったくありません。



ファイル→例→babbler_h→ babbler_json_io.ino



 #include "babbler.h" #include "babbler_cmd_core.h" #include "babbler_simple.h" #include "babbler_json.h" #include "babbler_serial.h" //         #define SERIAL_READ_BUFFER_SIZE 128 #define SERIAL_WRITE_BUFFER_SIZE 512 //         . // +1       char serial_read_buffer[SERIAL_READ_BUFFER_SIZE+1]; char serial_write_buffer[SERIAL_WRITE_BUFFER_SIZE]; #define LED_PIN 13 /**   ledon ( ) */ int cmd_ledon(char* reply_buffer, int reply_buf_size, int argc=0, char *argv[]=NULL) { digitalWrite(LED_PIN, HIGH); //   strcpy(reply_buffer, REPLY_OK); return strlen(reply_buffer); } /**   ledoff ( ) */ int cmd_ledoff(char* reply_buffer, int reply_buf_size, int argc=0, char *argv[]=NULL) { digitalWrite(LED_PIN, LOW); //   strcpy(reply_buffer, REPLY_OK); return strlen(reply_buffer); } babbler_cmd_t CMD_LEDON = { /*   */ "ledon", /*       */ &cmd_ledon }; babbler_man_t MAN_LEDON = { /*   */ "ledon", /*   */ "turn led ON", /*  */ "SYNOPSIS\n" " ledon\n" "DESCRIPTION\n" "Turn led ON." }; babbler_cmd_t CMD_LEDOFF = { /*   */ "ledoff", /*       */ &cmd_ledoff }; babbler_man_t MAN_LEDOFF = { /*   */ "ledoff", /*   */ "turn led OFF", /*  */ "SYNOPSIS\n" " ledoff\n" "DESCRIPTION\n" "Turn led OFF." }; /**   */ extern const babbler_cmd_t BABBLER_COMMANDS[] = { //   babbler_cmd_core.h CMD_HELP, CMD_PING, //   CMD_LEDON, CMD_LEDOFF }; /**    */ extern const int BABBLER_COMMANDS_COUNT = sizeof(BABBLER_COMMANDS)/sizeof(babbler_cmd_t); /**     */ extern const babbler_man_t BABBLER_MANUALS[] = { //   babbler_cmd_core.h // commands from babbler_cmd.core.h MAN_HELP, MAN_PING, //   // custom commands MAN_LEDON, MAN_LEDOFF }; /**      */ extern const int BABBLER_MANUALS_COUNT = sizeof(BABBLER_MANUALS)/sizeof(babbler_man_t); void setup() { Serial.begin(9600); Serial.println("Starting babbler-powered device with JSON i/o," " type {\"cmd\": \"help\", \"id\": \"34\", \"params\":[]} for list of commands"); //       // {"cmd": "help", "id": "34", "params":[]} babbler_serial_set_packet_filter(packet_filter_newline); babbler_serial_set_input_handler(handle_input_json); //babbler_serial_setup( // serial_read_buffer, SERIAL_READ_BUFFER_SIZE, // serial_write_buffer, SERIAL_WRITE_BUFFER_SIZE, // 9600); babbler_serial_setup( serial_read_buffer, SERIAL_READ_BUFFER_SIZE, serial_write_buffer, SERIAL_WRITE_BUFFER_SIZE, BABBLER_SERIAL_SKIP_PORT_INIT); pinMode(LED_PIN, OUTPUT); } void loop() { //     ,    babbler_serial_tasks(); }
      
      





Arduino環境での簡単なテストでは、同じツール→ポートモニターを開き、フォームのコマンドをロボットに送信できます。



 {"cmd": "help", "id": "34", "params":["--list"]}
      
      





答えは次のとおりです。



 {"cmd": "help", "id": "34", "reply": "help ping ledon ledoff"}
      
      





もちろん、JSON形式で文字列を手動で入力することはあまり便利ではありませんが、JavaScriptアプリケーションの場合、このような通信チャネルはネイティブチャネルのようになります。



Node.jsでクライアント側を設定する



-githubのBabbler.jsライブラリ。

-babbler-js-demoの



新しいプロジェクトを手動で構成するには、babbler-jsパッケージをインストールします。



 npm install babbler-js
      
      





または、例を含む完成したプロジェクトの場合は、次のようにします



 git clone https://github.com/1i7/babbler-js-demo.git cd babbler-js-demo/babbler-basic npm install
      
      





簡単な例 :デバイスに接続し、 pingおよびhelp --listコマンドを実行します



babbler-js-demo / babbler -basic / babbler-basic.js



 var BabblerDevice = require('babbler-js'); var babbler = new BabblerDevice(); babbler.on('connected', function() { console.log("connected"); console.log("send cmd: ping"); babbler.sendCmd("ping", [], // onReply function(cmd, params, reply) { console.log("got reply on '" + cmd + " " + params + "': " + reply); }, // onError function(cmd, params, err) { console.log("fail with '" + cmd + " " + params + "': " + err); } ); console.log("send cmd: help --list"); babbler.sendCmd("help", ["--list"], // onReply function(cmd, params, reply) { console.log("got reply on '" + cmd + " " + params + "': " + reply); }, // onError function(cmd, params, err) { console.log("fail with '" + cmd + " " + params + "': " + err); } ); }); babbler.on('disconnected', function(error) { console.log("disconnected" + (error != undefined ? ": " + error : "")); }); babbler.connect("/dev/ttyUSB0"); //babbler.connect("/dev/ttyUSB0", {baudRate: 9600});
      
      





実行:



 node babbler-basic.js
      
      





私たちが観察するターミナルで:



 connected send cmd: ping send cmd: help --list got reply on 'ping ': ok got reply on 'help --list': help ping ledon ledoff
      
      





ロボットでUSBケーブルを引き抜くと、プログラムは最後のメッセージを書き込んで終了します。



 disconnected: Device unplugged
      
      





例はもう少し興味深いです:



-プログラムはデバイスに接続し、2秒ごとに電球の点灯( leodonコマンド)と消灯( ledoffコマンド)を開始します。

-デバイスの電源がオフになっている場合、プログラムは接続するまで3秒ごとに再接続を試みます。その後、ライトが再び点滅し始めます。



babbler-basic / babbler-basic-blink.js

 var BabblerDevice = require('babbler-js'); var babbler = new BabblerDevice(); var blinkIntervalId; babbler.on('connected', function() { console.log("connected"); //    2  var ledstatus = "off"; blinkIntervalId = setInterval(function() { if(ledstatus === "on") { console.log("send cmd: ledoff"); babbler.sendCmd("ledoff", [], // onReply function(cmd, params, reply) { console.log("got reply on '" + cmd + " " + params + "': " + reply); ledstatus = "off"; }, // onError function(cmd, params, err) { console.log("fail with '" + cmd + " " + params + "': " + err); } ); } else { // ledstatus === "off" console.log("send cmd: ledon"); babbler.sendCmd("ledon", [], // onReply function(cmd, params, reply) { console.log("got reply on '" + cmd + " " + params + "': " + reply); ledstatus = "on"; }, // onError function(cmd, params, err) { console.log("fail with '" + cmd + " " + params + "': " + err); } ); } }, 3000); }); babbler.on('connecting', function() { console.log("connecting..."); }); babbler.on('disconnected', function(error) { console.log("disconnected" + (error != undefined ? ": " + error : "")); //  ,    clearInterval(blinkIntervalId); //     3  setTimeout(function() { babbler.connect("/dev/ttyUSB0"); }, 3000); }); babbler.connect("/dev/ttyUSB0"); //babbler.connect("/dev/ttyUSB0", {baudRate: 9600});
      
      





以下を開始します。



 node babbler-basic-blink.js
      
      





点滅する光を観察します。



 connecting... connected send cmd: ledon got reply on 'ledon ': ok send cmd: ledoff got reply on 'ledoff ': ok send cmd: ledon got reply on 'ledon ': ok send cmd: ledoff got reply on 'ledoff ': ok send cmd: ledon got reply on 'ledon ': ok disconnected: Device unplugged connecting... disconnected: Error: Error: No such file or directory, cannot open /dev/ttyUSB0 connecting... disconnected: Error: Error: No such file or directory, cannot open /dev/ttyUSB0 connecting... connected send cmd: ledon got reply on 'ledon ': ok send cmd: ledoff got reply on 'ledoff ': ok send cmd: ledon got reply on 'ledon ': ok disconnected: Device unplugged
      
      





このプロセスでは、ロボットにつながるUSBワイヤーを引き出してから、再び差し込むことができます。



All Articles