中止となった技術書典8に代わってオンライン開催の技術書典 応援祭に「GR-ROSEでいこう!」を出しました。今回の応援祭では電子版のみですが、紙の本もただいま印刷中です。シリアルサーボ、ROS/ROS2、EtherCAT、Amazon FreeRTOSなど盛りだくさんの内容となっております。
私は2章「シリアルサーボを制御する」と7章「EtherCATでロボットの制御」を担当しました。
応援祭終了後の電子版および紙の本の販売については後日お知らせします。
年末以来準備を進めてきた技術書典8が中止になったのは残念でした。作った同人誌はオンライン開催の「技術書典 応援祭」に出す予定です。3月以降もイベント中止が相次ぐかんじですが、開発のほうはしっかり進めていきたい。
マイコンでEthernetを使いたい場合、ハードウェアの構成に3つのパターンがある。いずれの場合もRJ45ジャックにはパルストランス内蔵のもの(MagJackなど)を使用するものとする。
この場合、SPI接続等でEthernetのMAC/PHYチップを外付けする。このような外付けMAC/PHYチップの代表的なものとして、WIZnet社のW5200やW5500がある。
例えば、Arduino UnoにEthernet Shield 2 (W5500搭載)を挿して使うような構成がこれに当たる。W5200やW5500の場合にはEthernetのMACのみならずTCP/IPやUDP/IPのプロトコルまで担ってくれるので、マイコン側の負荷が小さくてすむ。
この場合、EthernetのPHYチップを外付けする。マイコンとPHYとの接続にはMIIやRMIIという規格がある。このような外付けPHYチップの代表的なものとして、Microchip社のLAN8720A、LAN8742AやTI社のDP83848などがある。
EthernetのMACを持つマイコンの場合、外付けPHYチップとRJ45コネクタを搭載したマイコンボードが市販されている場合も多い。例えばNucleo-F767ZI (STマイクロ STM32F767ZI) や GR-SAKURA (ルネサスRX63N)などである。mbed LPC1768 (NXP LPC1768)はPHYは搭載しているがRJ45コネクタは搭載していないので外付けとなる。PHYとRJ45コネクタはTX+,TX-,RX+,RX-の4線で接続する。
マイコンの選択肢はかなり絞られ、ハイエンド寄りとなる。また、プロトコルスタックのライブラリなりミドルウェアなりが提供されていることが必須となる。
C++で「undefined reference to `typeinfo for クラス名'」というよく意味の分からないエラーに遭遇した。
undefined reference to `typeinfo for BMI160Class'
どうも仮想関数(virtual が付いてる関数)を持つクラスで、仮想関数の最初の親クラスで実体が定義されていない場合に出るらしい。もうちょっとそれと分かるようなエラーメッセージにしてほしい。
Mbedのプロジェクトをオンラインコンパイラからエクスポートして、WindowsでEclipse系のオフラインビルド環境にインポートしてビルドすると次のようなエラーが出る場合がある。
make (e=87): パラメータが間違っています。
英語環境だと次の通り。
make (e=87): The parameter is incorrect.
このエラーはピルドのコマンドラインが長すぎる場合に発生する。WindowsのCreateProcess()でプロセスを生成する場合、コマンドラインの長さは最大32768文字であり、これを超える場合にこのエラーが出る。Mbed OS 5のプロジェクトで大量のオブジェクトファイルをリンクして実行バイナリを生成するときにこのエラーが発生することがある。実際、エラー発生時のリンカのコマンドラインを調べるとなんと5万文字を超えていた。
なお、LinuxやMacでは発生しないようだ。LinuxやMacのような「適切な」OSを使用する以外に回避策はないのかもしれない(笑)
Windowsではゲームパッド入力はDirectXを介するのでキーボード入力ほど簡単には扱えない。C#アプリでゲームパッド入力を取るにはDirectXをラップしたなんらかの.NETライブラリが必要になる。このようなライブラリには有名なものとして SlimDX と SharpDX がある。SlimDX は2012年を最後にアップデートされておらず、DirectX 11までの対応となっている。SharpDX も残念ながら2019年に開発終了となってしまった。代替手段が見つかるまで当面は SharpDX を使うことにする。
DirectInputはDirectXの一部であり、マウス、キーボード、ゲームパッド等の入力を得るためのAPIである。SharpDX.DirectInputはこれをラップした.NETライブラリである。
「プロジェクト」→「NuGetパッケージの管理」で「DirectDX.DirectInput」を検索し、プロジェクトにインストールする。
また、C#ソースの冒頭に下記のusingを追加する。
using SharpDX; using SharpDX.DirectInput;
// DirectInput DirectInput dinput = new DirectInput(); // ゲームパッド Joystick joystick = null; // 使用するゲームパッドのID Guid joystickGuid = Guid.Empty;
ゲームパッドデバイスまたはジョイスティックデバイスの中から最初に見つかったもののIDを取得する。
// ゲームパッドを探す if (joystickGuid == Guid.Empty) { foreach (DeviceInstance device in dinput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices)) { joystickGuid = device.InstanceGuid; break; } } // ジョイスティックを探す if (joystickGuid == Guid.Empty) { foreach (DeviceInstance device in dinput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices)) { joystickGuid = device.InstanceGuid; break; } }
// 見つかった場合 if (joystickGuid != Guid.Empty) { // ゲームパッドの取得 joystick?.Dispose(); joystick = new Joystick(dinput, joystickGuid); // ゲームパッドが取得できた場合 if (joystick != null) { // バッファサイズを指定 joystick.Properties.BufferSize = 128; // ジョイスティックの軸の最小値と最大値を -1000~+1000に設定 foreach (DeviceObjectInstance deviceObject in joystick.GetObjects()) { switch (deviceObject.ObjectId.Flags) { // 絶対軸または相対軸 case DeviceObjectTypeFlags.Axis: case DeviceObjectTypeFlags.AbsoluteAxis: case DeviceObjectTypeFlags.RelativeAxis: var ir = joystick.GetObjectPropertiesById(deviceObject.ObjectId); if (ir != null) { try { ir.Range = new InputRange(-1000, 1000); } catch (Exception) { } } break; } } } }
ゲームパッドの入力を取得する。
// ゲームパッドの入力を取得 try{ joystick.Acquire(); joystick.Poll(); }catch{ // ゲームパッドが抜けた joystick?.Dispose(); joystick = null; return; } // ゲームパッドのデータ取得 var jState = joystick.GetCurrentState(); // 取得できない場合 if (jState == null) { return; }
ボタン1,2,3...に対応する添え字が0,1,2...であることに注意する。
また、ボタン番号とボタンの対応はゲームパッド製品によってバラバラなのでなんらかのテーブル(配列変数)で対応付けを管理する。
// ボタン1が押された場合 if(jState.Buttons[0]){ }
十字ボタンはハットスイッチ(POV、視点の角度切替え)として扱われる場合と、X軸/Y軸として扱われる場合があり、製品によって異なる。
// POVの場合 if (jState.PointOfViewControllers[0] == 0){} // 上 if (jState.PointOfViewControllers[0] == 9000){} // 右 if (jState.PointOfViewControllers[0] == 18000){} // 下 if (jState.PointOfViewControllers[0] == 27000){} // 左
// X軸/Y軸の場合 if (jState.X < -500){} // 左 if (jState.X > 500){} // 右 if (jState.Y < -500){} // 上 if (jState.Y > 500){} // 下
一般的なゲームパッドにはアナログスティックが2本つまり4軸あるが、DirectInputが対応しているアナログ入力はX,Y,Z軸、X,Y,Z軸の回転、POVなどであり、現実のゲームパッドとうまく対応しない。軸の割り当てはこれまた製品によってまちまちである。下記に一例を示す。
// 左のアナログスティックはX軸とY軸 int x1 = jState.X; int y1 = jState.Y; // 右のアナログスティックはZ軸とZ軸の回転 int x2 = jState.RotationZ; int y2 = jState.Z;
基板取付用LANコネクタ(RJ45)は、LEDやシールドのピンを除くとどれも千鳥足の8ピンなのだが、ものによってピン配置がぜんぜん異なるので要注意。
パルストランスを内蔵してないものの場合は、ケーブルの線が足に直結しており、ケーブルのピン番号と足のピン番号は一致する。
1 - TD+ 2 - TD- 3 - RD+ 4 - N.C. 5 - N.C. 6 - RD- 7 - N.C. 8 - N.C.
1 - TD+ 2 - TD- 3 - TCT (Vdd) 4 - N.C. 5 - CHS.GND 6 - RCT (Vdd) 7 - RD+ 8 - RD-
1 - TD+ 2 - TD- 3 - RD+ 4 - TCT (Vdd) 5 - RCT (Vdd) 6 - RD- 7 - N.C. 8 - CHS.GND
1 - TD+ 2 - TCT (Vdd) 3 - TD- 4 - RD+ 5 - RCT (Vdd) 6 - RD- 7 - N.C. 8 - CHS.GND