Arduinロボット用のJavaScript / Node.jsデスクトップリモートコントロール

今日、シリアルポート経由でArduinのロボットを制御するためのグラフィカルインターフェイスを備えたデスクトップアプリケーションを作成しています。 ReactJS + MaterialUIウィジェットを備えたElectronプラットフォームのJavaScript。



画像





CNCマシンのコントロールパネルは、Webサイトを作成するのと同じくらい簡単です。



以前:



-パート1: Arduinのロボットのコンソール

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



主なリンク



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

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

-Node.js + ReactJS + MaterialUI用のBabblerウィジェット:babbler -js-material-ui

-babbler-jsのサンプルアプリケーション: babbler-js-demo



ツールリンク



-Node.jsのシリアルポートnode- serialportgithub.com/EmergingTechnologyAdvisors/node-serialport

-電子プラットフォーム: electron.atom.io

-ReactJS: facebook.github.io/react

-ReactJSのMaterialUIのウィジェット(コンポーネント): www.material-ui.com/#



サイトリンク



-NWJSプラットフォーム: nwjs.io

-Reactの他のウィジェット:

github.com/facebook/react/wiki/Complementary-Tools#ui-components



クイックスタート



1. 前のストーリーの babbler_json_io.inoスケッチをArduinoにフラッシュします



このファームウェアはJSON形式で通信し、ライトを点滅させることができ、 pinghelpledonledoffの 4つのコマンドを含みます



2.リモートコントロールをダウンロードします。



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





3.コントロールパネルを起動します。



 ./babbler-serial.sh
      
      





4.ボタンを押して、ライトを点滅させます。



画像



プロジェクト構造



プロジェクトのタスクは、Babbler.jsライブラリを起動して、Electronに囲まれたロボットと通信し(Google Chromeコードに基づいてJavaScriptアプリケーションを別のウィンドウで起動)、ReactJSをグラフィカルインターフェイスのMaterialUIウィジェットに接続することです。 一般に、リストされたプロジェクトの「Hello World」よりも複雑ではありませんが、1つのアプリケーションでこれらすべてのライブラリを収集する過程で、いくつかの問題とニュアンスが特定されたため、新しいプロジェクトのテンプレートとして、この例のソースを基礎として使用することをお勧めします。



前提条件:node.js、npm、および(できれば)gitがコンピューターにインストールされている。



もう一度、ソースをダウンロードしてbabbler-js-demo / babbler-serial-reactプロジェクトに移動します:



 git clone https://github.com/1i7/babbler-js-demo.git cd babbler-js-demo/babbler-serial-react
      
      





プロジェクトファイル



-package.json -npm(ノードパッケージマネージャー)のプロジェクト:プロジェクト設定、依存関係のリスト。



そのおかげで、package.jsonにリストされているすべての依存関係(Electronプラットフォームを含む)をプロジェクト内の1つのコマンドでインストールできます。



 npm install
      
      





-main.js -Electronアプリケーションのメインファイル(electronの「hello world」から取得)。



コンテンツはそれ自体を物語っています。 唯一の興味深い場所は、アプリケーションの起動時に開発ツールでパネルを開くコマンドです。



  //  DevTools. mainWindow.webContents.openDevTools();
      
      





アプリケーションの開発時にこの呼び出しを終了し(メニューから手動で開く:[表示]→[開発者ツールの切り替え])、リリースを削除/コメントすることをお勧めします。



-index.html -Electronアプリケーションのメイン画面の内容。



なぜなら Reactを使用してグラフィカルインターフェイスを形成するには、本文内にある必要があるのは、id = "app-content"のdiv要素のみです。



 <body> <div id="app-content"></div> </body>
      
      





このファイルは編集しません。メインコードはさらに編集します。



-react-app-ui.js-メインアプリケーションファイル、ここではメイン画面のメインウィジェットツリー(id = "app-content"のdivのindex.htmlに送信)、すべてのユーザーコード、修正のみ。



-babbler - serial.sh-アプリケーション起動スクリプト



 #!/bin/sh ./node_modules/electron-prebuilt/dist/electron .
      
      





追加のニュアンス



メモについて



-node-serialportとElectronの問題



node-serialportライブラリーは、Electronプラットフォームの最新バージョンでは動作しませんでした(「裸の」Node.jsですべてが正常であったという事実にもかかわらず)。



詳細を説明することなく、古いバージョンのElectron 1.1.3にロールバックするか、(議論の1つで言うように)ソースから再構築することで問題を回避できることに注意してください。



 sudo npm i -g electron-prebuilt@1.1.3
      
      





同じ問題がNWJSプラットフォーム(Electronの代替)でも見られます。明らかに、Chromeエンジンで何かが壊れていました。



Electronの動作バージョンは、package.jsonのプロジェクトの依存関係に示されているため、デモプロジェクトではすべて問題ありません。



プロジェクトバグトラッカーのメッセージ:



ノードシリアルポート

電子

NWJS



おそらく次のリリースのいずれかで問題が修正され、その場合、Electronをより新しいバージョンに切り替えることが可能になるでしょう。



-Babel、JSX、およびES6構文



ReactJSアプリケーションとコンポーネントは、特別なJSX構文を使用します-これは、JavaScriptコードでアプリケーションコントロールツリーの構造を直接記述するHTMLに似たXMLです。 また、アプリケーション内では、一部の場所で拡張JavaScript ES6構文を使用します(これは、JavaScript標準にまだ入っていないか、少し前に入ったすべての種類の構文言語構成要素のセットであるため、最新のブラウザーバージョンでもまだ実装されていません)。 最初は、プロジェクトで不要な構成を行わないように、ES6コンストラクト(すべてを「クラシック」JavaScriptのアナログに置き換えることができます)を除外したかったのです。 でも彼はあきらめた ReactJS(特にMaterialUI)のインターネット上の多くの例はES6構文を使用して記述されており、その場合、それらすべてを古いJavaScript構文に変換する必要があります。



古いJavaScriptエンジンで非標準の構文を使用するために、特別なツール-Babelを使用します。 プロジェクトの設定が正しい場合、カスタムデザインをその場で対応するJavaScriptに変換できます。 ここから松葉杖と庭が始まります。 プロジェクトテンプレートでは、必要なすべての設定が既に設定されているため、詳細には分析せず、主なポイントをリストします。



-package.jsonにはBabel設定のブロックが含まれている必要があります。



 "babel": { "presets": ["es2015", "react", "stage-1"] }
      
      





-ウィジェットを現在のディレクトリ以外のディレクトリからプロジェクトにインポートする場合、同様の設定を.babelrcファイルで指定する必要があります(例:babbler-js-meterial-ui / src / .babelrc )。



-HTMLファイル(index.htmlがあります)のスクリプトブロック(type = "text / babel")でBabelの変換を有効にするには、同じファイルにbrowser.min.jsスクリプトをインポートする必要があります(プロジェクトのローカルコピー:babbler- serial-react / script / browser.min.js (インターネットに依存しないように)。



-個別のjsファイルでBabelの変換を有効にするには、「babel-register」モジュールをロードし、 require( './ react-app-ui.js')を介してjsファイル自体をダウンロードする必要があります (すべて同じindex.htmlを参照してください)。



たぶんしばらくして、ES6の技術革新がGoogle ChromeバージョンのメインJavaScriptブランチ(およびそこからElectron)に移行し、これらのプロップの一部が不要なものとして捨てられる可能性があります。



-MaterialUIウィジェットがindex.htmlで機能するには、「react-tap-event-plugin」モジュールをロードし、injectTapEventPlugin()を実行する必要があります



ソースコードを見る



すべての便利なユーザーコードは1つのファイルにあります-babbler-js-demo / babbler-serial-react / react-app-ui.js



ここでコンポーネントを作成します-電球を備えたロボット制御パネルであり、メイン画面の制御ツリーも形成します。



事前準備


基本Reactオブジェクト:



 var React = require('react'); var ReactDOM = require('react-dom');
      
      





MaterialUIウィジェット-ボタン、アイコン、タブ、パネル:



 //  MaterialUI import getMuiTheme from 'material-ui/styles/getMuiTheme'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import Paper from 'material-ui/Paper'; import {Tabs, Tab} from 'material-ui/Tabs'; import Divider from 'material-ui/Divider'; import RaisedButton from 'material-ui/RaisedButton'; import FontIcon from 'material-ui/FontIcon'; import {red200, green200} from 'material-ui/styles/colors'; import Subheader from 'material-ui/Subheader';
      
      





babbler -js-material-uiプロジェクトのBabblerウィジェット-デバイスインタラクション:



-BabblerConnectionPanel-接続パネル:ドロップダウンリストからデバイスを選択し、接続/切断ボタン(接続状態に応じて)

-BabblerConnectionStatusIcon-デバイス接続ステータスアイコン:切断、接続、接続

-BabblerConnectionErrorSnackbar-切断およびその他の接続エラーを通知する画面下部のポップアップパネル

-BabblerDataFlow-リアルタイムの完全なログ:コマンドをキューに追加したり、デバイスとデータを交換したりなど。

-BabblerDebugPanel(現在ライブラリでは定義されていないが、テストプロジェクト内)-デバッグパネル:デバイスへのコマンドの手動送信、ヘルプ、pingボタン、BabblerDataFlowでのログ



 //  Babbler MaterialUI import BabblerConnectionStatusIcon from 'babbler-js-material-ui/lib/BabblerConnectionStatusIcon'; import BabblerConnectionErrorSnackbar from 'babbler-js-material-ui/lib/BabblerConnectionErrorSnackbar'; import BabblerConnectionPanel from 'babbler-js-material-ui/lib/BabblerConnectionPanel'; import BabblerDataFlow from 'babbler-js-material-ui/lib/BabblerDataFlow'; import BabblerDebugPanel from './widgets/BabblerDebugPanel';
      
      





デバイスと通信するBabbler.js:



 // Babbler.js import BabblerDevice from 'babbler-js';
      
      





ボタンスタイル:



 const btnStyle = { margin: 12 };
      
      





最後に、最も興味深い部分は、ロボット、電球のコントロールパネルとの通信です


パネルは通常のReactコンポーネントです:「電球をオンにする」ボタン、「電球をオフにする」ボタン、および電球ステータスアイコン。



Reactコンポーネントについて知っておく必要があります。



-Reactコンポーネントは、ステートマシン(ステートマシン)として機能します。

-コンポーネントの現在の状態は、 this.propsの静的プロパティとthis.stateの動的状態の2つの値グループによって決定されます。

-静的プロパティthis.props :画面に追加するときにコンポーネントタグのパラメーターを介して渡されます。

-this.stateの動的状態:アプリケーションの実行中に変更され、 this.setStateを使用して適切なタイミングで設定されます。

-this.setStateによる状態の変更は、コンポーネントの再描画につながります。

-コンポーネントはレンダリング関数で再描画され、外観はthis.propsとthis.stateの値に依存します。

- レンダリング内のコンポーネントの外観は、React JSX構文によって定義されます。



私たちの場合:



-BabblerDeviceオブジェクトは、静的パラメーターthis.props.babblerDeviceを介してコンポーネントに入ります。

-babblerDeviceイベントは、コンポーネントの動的状態を変更します(接続されている場合、すべてのボタンをアクティブにし、接続されていない場合、非アクティブにします)。

-[ライトをオンにする]および[ライトをオフにする] ボタンは、 babblerDevice介しledonおよびledoffコマンドをデバイスに送信します。

-肯定的な応答の場合、「ok」プロパティthis.state.ledOn (true / false)の値を書き込むことにより、電球のステータスの画像を変更します。



 //   var BabblerLedControlPnl = React.createClass({ // http://www.material-ui.com/#/components/raised-button // http://www.material-ui.com/#/components/subheader getInitialState: function() { return { deviceStatus: this.props.babblerDevice.deviceStatus(), ledOn: false }; }, componentDidMount: function() { //    this.deviceStatusListener = function(status) { this.setState({deviceStatus: status}); }.bind(this); this.props.babblerDevice.on(BabblerDevice.Event.STATUS, this.deviceStatusListener); }, componentWillUnmount: function() { //   this.props.babblerDevice.removeListener(BabblerDevice.Event.STATUS, this.deviceStatusListener); }, render: function() { var connected = this.state.deviceStatus === BabblerDevice.Status.CONNECTED ? true : false; return ( <div style={{textAlign: "center"}}> <div> <RaisedButton label=" " onClick={this.cmdLedon} disabled={!connected} style={btnStyle} /> <RaisedButton label=" " onClick={this.cmdLedoff} disabled={!connected} style={btnStyle} /> </div> <FontIcon className="material-icons" style={{fontSize: 160, marginTop: 40}} color={(this.state.ledOn ? green200 : red200)} >{(this.state.ledOn ? "sentiment_very_satisfied" : "sentiment_very_dissatisfied")}</FontIcon> </div> ); }, cmdLedon: function() { this.props.babblerDevice.sendCmd("ledon", [], // onReply function(cmd, params, reply) { if(reply == 'ok') { this.setState({ledOn: true}); } }.bind(this), // onError function(cmd, params, err) { console.log(cmd + (params.length > 0 ? " " + params : "") + ": " + err); }.bind(this) ); }, cmdLedoff: function() { this.props.babblerDevice.sendCmd("ledoff", [], // onReply function(cmd, params, reply) { if(reply == 'ok') { this.setState({ledOn: false}); } }.bind(this), // onError function(cmd, params, err) { console.log(cmd + (params.length > 0 ? " " + params : "") + ": " + err); }.bind(this) ); } });
      
      





アプリケーションのホーム画面


ロボットに接続するBabblerDeviceデバイスを作成します。



 //  Babbler,     var babblerDevice1 = new BabblerDevice();
      
      





アプリケーションのメイン画面の最終レイアウトは、ReactJS JSX構文(JavaScriptコード内のHTMLに似たXML)です。 コントロールのツリーを描画し、id = 'app-content'のdivでindex.htmlに送信します。



ここには、デバイス内に接続するためのパネルがあります-上部のバー、ロボットとの通信ブロック-タブの内側。



 //   ReactDOM.render( <MuiThemeProvider muiTheme={getMuiTheme()}> <div> <Paper> <BabblerConnectionPanel babblerDevice={babblerDevice1}/> <BabblerConnectionStatusIcon babblerDevice={babblerDevice1} iconSize={50} style={{position: "absolute", right: 0, marginRight: 14, marginTop: 5}} /> </Paper> <Divider style={{marginTop: 20, marginBottom: 20}}/> <Tabs> <Tab label="" > <BabblerLedControlPnl babblerDevice={babblerDevice1}/> </Tab> <Tab label="" > <BabblerDebugPanel babblerDevice={babblerDevice1}/> </Tab> <Tab label="" > <BabblerDataFlow babblerDevice={babblerDevice1} reverseOrder={true} maxItems={10000} timestamp={true} // filter={{ err: false, data: false }} // filter={{ data: {queue: false} }} // filter={{ err: {in: false, out: false, queue: false}, data: {in: false, out: false, queue: false} }} style={{margin: 20}}/> </Tab> </Tabs> <BabblerConnectionErrorSnackbar babblerDevice={babblerDevice1}/> </div> </MuiThemeProvider>, document.getElementById('app-content') );
      
      





打ち上げ



 ./babbler-serial.sh
      
      





デバイスを選択してください:



画像



接続します:



画像



待っています:



画像



ライトをオンにします。



画像



ライトをオフにします。



画像



ログを確認します。



画像



手動モードのヘルメットチーム:



画像



画像






All Articles