2018年6月16日土曜日

小型工作機CNC2418 その54(Joystickコントローラー始動)

前々回のブレッドボード検討前回のケース加工に引き続き
CandleのJoystick Cotrollerの製作の組立と実働チェックです。
Candleとは、GRBL Controllerで、GitHubのCandle v.1.1.7のことです。
役目としてはGRBL Senderが正しいのかもですが。

まずは、SparkFun Pro Microにつけるピンヘッダー。
L型のピンヘッダーの持ちわせがないので、ストレートの先を折り曲げます。
SparkFun Pro Microにピンヘッダーをハンダ付けします。
ハンダゴテ新調したのです!
小手先5本、予備セラミックヒーター予備付きで、25%OFF US$ 6.22
噂によると、温度コントロールの温度目盛りは、あてにならないらしいので
溶け具合で自分なりの位置を覚えておく必要があるようです。
小手先の劣化が激しいらしいので使う時だけ温度を上げるなど工夫も必要そうですが
軽くて小さいのに60Wと強力なので、立ち上がりも早くなかなか使い易い印象です。
アフィリンクはなく直リンなのでその内リンク切れるかも。

配線には、アセンブリー品のメス~メス 10cmのこれ使います。
メス~メス、メス~オス、オス~オスが夫々40本ずつ、全部で120本!
これでお値段 US$ 1.84+送料0.42
その内、ピンとカシメ具も欲しいなあ~

思ったより配線のスペースがいるんですよね~
2叉になるVCCとGND以外は10cmのまま使ったのでギュウギュウです。
上側から
組み上がりました!
中の空洞が少ないので、約150g 軽過ぎず、いい感じの重さになりました。
USBに繋いでみます。
LEDは、赤:TX、緑:Power、右上は、RXなのでスケッチ書込時しか点灯しません。
別アングルも
CNC2418とこのコントローラーを夫々USBに接続し、
CNCの主電源なしでCandle(GRBL Controller)を起動してPC画面で確認です。
XY軸のJoysitickボタンを押すとKeyboard ControlがONになり無事動作します。
ケースに組み込んだ状態は、下の写真のブレッドボードの時から90°回転しているので
X軸とY軸が逆、X軸は左右も逆、Z軸も90°回転しています。
一応、すべての動作はOKです。
後から調整することを想定して配線を全てコネクタにしたわけでして
配線を修正して、また蓋をします。

しばらく操作していると
Joystickは、左側の方が微妙な操作ができる事がわかってきました。
こちらの方が特に左右の微妙な動作をできるのです。
親指ってのは、健が真っ直ぐの時が一番精密な動きをしてくれるのだろうか?
こちらは、親指の健が曲がるからなのか?
人差し指と親指間の筋肉が収縮しているかなのか?、わかりませんが
特に左右に動かす時に震えたりするのです。上下動作は問題ありません。
もしかしてマーティーの手がポンコツなのだろうか?
でもこれを見直して安心しました。
PS4のコントローラーのJoystickの位置が内側にあるのは同じ理由ではないだろうか?
人間工学に基づいてレイアウトが設計されているんだと思います。
ということで、XY軸用とZ軸用は、入れ替えます。
Candleの操作画面と同じ並びになるわけです。
左をXY軸用、右をZ軸用に配線変更して(ほんとコネクタ式にしといてよかった)
X軸の左右逆は、スケッチを修正して。
Y軸は、どうしようか?
ステージが手前に来る時は、座標はマイナス側に動くのですが
Joystickを手前に倒した時にステージも手前に動いた方が直感的に判り易いかな~
結局、左から右側の仕様に変更しました。
(実は、Y軸の前後、これとは逆が良いのに最後に気づくのであります^^;)
では、Candle画面と一緒の動画!
ノブのSWでStepとFeedが初期化されます。
ノブの傾きでStepが0.01 ⇒ 0.1 ⇒ 1.0 と変わります。
大きく倒すと移動量が大きくなります。
途中、斜めになったのでXY両方が変化しています。
CNC2418実機操作中の動画です。
ノブの傾きで移動量が3段階に変わっています。
一瞬倒すと単発移動、0.3sec以上倒していると連続移動になります。
なかなか使い心地良いです。
ただ、Candle画面をアクティブにしていないと
ブログの画面なんかに「7777771-----+++」とキー連打がでたりまします。

USBケーブルが短いのでカールコードの長いのにしたいなあ~
ということで、球形がてら、これをポチっておきます。
40cm~250cm  -8%Sale中 US$ 1.84!

USBには、マウス、CNC2418に、このJoystickで3ポート
これにUSBカメラなんか付けると、USB-HUB必須ですね~
CNC2418側にUSB-HUBがあると便利そうだな~

その後、実際の位置合わせ作業を実感するために、
ステージに図面を置いて、操作しております...と
Y軸の動作、スピンドルを手前に持ってきたい時は
ジョグを手前に(つまりステージは奥へ)の方が直感的なのです(-_-;)
逆なのでした! 冷静に考えれば、当たり前ですね~
ステージだけ見ていたので錯覚していたわけです。
というわけで、Y軸操作は、これに変更!
右往左往してCandle画面と同じがいいという結論になりました。
最後に組込後の最終版スケッチです。
まだ、たまにStepが小さい方に狂う問題がありますが、
ControlをON/OFFし直せば、戻るので、まあいいか^^;
それにしても、USBデバイスがこんなに手軽に作れるなんて、
ATmega32u4というか、SparkFun Pro Micro、なかなか面白いです。
もう一個ポチっておこうかな!

Arduino IDE v1.8.3
***組み込み後の最終版スケッチ***
/*
 Joystick Controller for "Candle(GRBL Contoroller)"
 with SparkFun Pro Micro, Arduino Leonardo (ATmega32u4)
 A0:Joystick X軸のADC
 A1:Joystick Y軸のADC
 A2:Joystick Z軸のADC
 D2:Scroll Lock キー用(LOWアクティブ、内蔵Pull-UP)
 D5:Keyboard Control Enable LED(ボード内のTX LED)
 ケースに組み込み完成バージョン
 ProMicro_Joystick_FIX_11.ino 2018.6.13 Ver.1.1 Marty Since 2018.5.20 
*/

#include "Keyboard.h"
// Keyboard.write() では、USB-HIDのKeyCode+136しないと、ほとんどの場合で動作せず。
#define KEY_ScrLk 71+136  //「ScrollLock」キー(USB-HID)
#define KEY_4 33+136  //「4」キー(X-Axis Right)
#define KEY_6 35+136  //「6」キー(X-Axis Left)
#define KEY_2 31+136  //「2」キー(Y-Axis Front)
#define KEY_8 37+136  //「8」キー(Y-Axis Back)
#define KEY_3 32+136  //「3」キー(Z-Axis Down)
#define KEY_9 38+136  //「9」キー(Z-Axis UP)
#define KEY_1 30+136  //「1」キー(Step Down)
#define KEY_7 36+136  //「7」キー(Step UP)
#define KEY_Minus 86+136  //「-」キー(Feed Down)
#define KEY_Plus 87+136  //「+」キー(Feed UP)
#define KEY_0 29+136  //「0」キー(Spindle ON/OFF)(今回未使用)
#define KEY_5 34+136  //「5」キー(Stop)(今回未使用)
#define KEY_Slash 84+136  //「/」キー(Spindle Speed Down)(今回未使用)
#define KEY_Asterisk 85+136  //「*」キー(Spindle Speed UP)(今回未使用)

const int xAxisPin = A0;      // joystick X-Axis
const int yAxisPin = A1;      // joystick Y-Axis
const int zAxisPin = A2;      // joystick Z-Axis
const int KeyContEnablePin = 2;    // for Keyboard Control ON/OFF
const int ledPin = 5;      // Keyboard control LED (TX LED on board ProMicro)
const short xAxis = 0;
const short yAxis = 1;
const short zAxis = 2;

boolean KeyContIsActive = false;   // Keyboard Control Enable/Disable
short lastSwitchState = HIGH;      // 直前のControl state (LOW Active)
// STEP        :Continuous、0.01、0.1、1、5、10、100
// StepPosition:     1    、  2 、 3 、4、5、 6、 7 
short StepPosition = 2;            // 現在のStep Position
short LastStepPosition = 2;         // 直前のStep Position

short xyzRead[3] = { 511, 511, 511 };     // XYZ軸のADC読取値 (初期値 1023÷2)
short xyzCenter[3] = { 511, 511, 511 };   // XYZ軸の実測センター平均値 (初期値 1023÷2)
short xyzCorrectCenter[3] = { 0, 0, 0 };  // センター補正値(Joystick VRバラツキ補正)
short xyzJoyPosition[3] = { 4, 4, 4 };  // JoystickのPosition(7段階) 4:中央
short last_xyzJoyPosition[3] = { 4, 4, 4 };// 直前のJoystick Position
short PosThreshold[3] = { 80, 40, 170 }; // 検出する最低時間の閾値(約msec)ノブ傾きにより3段階
unsigned long xyzPosCont[3] = { 0, 0, 0};// 同じPositionに停滞している経過時間をカウント
short xyzWaitCont[3] = { 0, 0, 0 };     // Single Step移動時の無効化時間カウント
short ContinuousMode = 0; // 連続移動モード 0:OFF、1:ON(0:シングル移動モード)

void setup() {
  pinMode(KeyContEnablePin, INPUT_PULLUP);  // Keyboard Control Enable SW Pin
  pinMode(ledPin, OUTPUT);          // LED pin
  Keyboard.begin();
}

void loop() {
  CheckKeyContSW_Init();  // Check Keyboard Control ON/OFF Switch と Initialize
  if (KeyContIsActive) {  // Keyboard Control ONであればJoystick Move処理する
    JoystickControllMain();  // CandleをKeyboard ControlにしてJoystickで操作する処理
  }
    delay(7);  // analogReadを各軸10回平均で3msec、loop total time 約10msecにする
}


void CheckKeyContSW_Init() {  // CheckKey & Initialize
  short switchState = digitalRead(KeyContEnablePin);  //Keyboard Control EnableキーでControl ON/OFF
  // switchStateは、変化したか?
  if (switchState != lastSwitchState) {
    // switchStateがLOWならば、Keyboard control stateをトグルする
    // 「ScrollLock」キー送出してCandleをKeyboard Controlに切り替える
    if (switchState == LOW) {
      KeyContIsActive = !KeyContIsActive;
      // 以下は、switchStateが変化した直後のみに1回だけ行う為、この位置にある
      Keyboard.write(KEY_ScrLk);  //ScrollLockキーでCandleをKeyboard Controlモードへ(トグル動作)
      // turn on LED to indicate Keyboard control state:
      digitalWrite(ledPin, KeyContIsActive);
      if (KeyContIsActive) {
        InitStepFeed();  // Step、Feedの初期化
        delay(500);  // 0.5sec delayで安定させ、XYZセンター値を取得し補正値を保存
        InitCorrectCenterXYZ();  // XYZ軸センター値の補正
      }
    }
  }
  // save switch state for next comparison:
  lastSwitchState = switchState;
}



void InitStepFeed() {
//  StepとFeedの初期位置設定
// 「1」6回、「7」1回で、Step:0.01 に初期値を設定
// 「-」5回、「+」3回で、Feed:500 に固定
  // Step初期値 0.01に設定
  for ( short i=1; i<=6; i++ ) {
    Keyboard.write(KEY_7); // 「7」6回 StepPosition最下位に設定、('7')では、まれにダメなことがある
  }
  Keyboard.write(KEY_1);  // 「1」1回、('1')では、まれにダメなことがある
  StepPosition = 2; // StepPosition:2(0.01)に設定
  LastStepPosition = 2;
  // Feed初期値 500に設定
  for ( short i=1; i<=5; i++ ) {
    Keyboard.write(KEY_Minus);// 「-」5回、('-')や(86)ではダメ
  }
  for ( short i=1; i<=3; i++ ) {
    Keyboard.write(KEY_Plus);// 「+」3回、('+')や(87)ではダメ
  }
}


void InitCorrectCenterXYZ() {
  short xyzReadSum[4] = { 0, 0, 0 };  //XYZ軸のセンター値を10回読み込み積算する
  for (short i=0; i<10; i++ ) {
    xyzRead[xAxis] = analogRead(xAxisPin);
    xyzRead[yAxis] = analogRead(yAxisPin);
    xyzRead[zAxis] = analogRead(zAxisPin);
    xyzReadSum[xAxis] += xyzRead[xAxis];
    xyzReadSum[yAxis] += xyzRead[yAxis];
    xyzReadSum[zAxis] += xyzRead[zAxis];
    delay(10);
  }
  for (short Ax=0; Ax<3; Ax++ ) {
    xyzCenter[Ax] = xyzReadSum[Ax] / 10;  // XYZ軸センター実測10回の平均
    xyzCorrectCenter[Ax] = xyzCenter[Ax] - 511;  // XYZ軸センター補正(Parts VRバラツキ補正)
  }
}


void JoystickControllMain() {
    // 各軸ADC10回の平均を取る Convertion Total Time ≒3msec  
    short xReadSum = 0;
    short yReadSum = 0;
    short zReadSum = 0;
    for ( int i=0; i<10; i++ ) {
      xReadSum += analogRead(xAxisPin);  // Convertion Time = 100μsec
      yReadSum += analogRead(yAxisPin);
      zReadSum += analogRead(zAxisPin);
    }
      xyzRead[xAxis] = xReadSum / 10;
      xyzRead[yAxis] = yReadSum / 10;
      xyzRead[zAxis] = zReadSum / 10;

      Joy_Pos_Detector(xAxis);  // Joystickの傾き検出
      if (xyzJoyPosition[xAxis] < 19) {  // Joystick Center以外の時
        MoveProcess(xAxis);   // JoystickでのX軸の移動処理
      }
      Joy_Pos_Detector(yAxis);
      if (xyzJoyPosition[yAxis] < 19) {
        MoveProcess(yAxis);   // JoystickでのY軸の移動処理
      }
      Joy_Pos_Detector(zAxis);
      if (xyzJoyPosition[zAxis] < 19) {
        MoveProcess(zAxis);   // JoystickでのZ軸の移動処理
      }
}


void Joy_Pos_Detector(short Axis) {
//  1~ 6 X-Axis Right~ Left 1.0 ⇒ 0.1 ⇒ 0.01 ⇒ 0.01 ⇒ 0.1 ⇒ 1.0
//  7~12 Y-Axis Front~ Back 1.0 ⇒ 0.1 ⇒ 0.01 ⇒ 0.01 ⇒ 0.1 ⇒ 1.0 
//  8~18 Z-Axis Down ~ Up   1.0 ⇒ 0.1 ⇒ 0.01 ⇒ 0.01 ⇒ 0.1 ⇒ 1.0
// 19~20 Center X、Y、Z
//
// STEP        :Continuous、0.01、0.1、1、5、10、100
// StepPosition:     1    、  2 、 3 、4、5、 6、 7 
//
  // 0 以上 ~ 20未満:Frot/Left/Down 1.0mm Step
  if (( 0 <= xyzRead[Axis] ) && ( xyzRead[Axis] < 20)) {
    StepPosition = 4;
    xyzJoyPosition[Axis] = 1 + Axis * 6; // X:1, Y:7, Z:13
  }
  // 20 以上 ~ 312+xyzCorrectCenter[Axis] 未満:Frot/Left/Down 0.1mm Step
  if (( 20 <= xyzRead[Axis] ) && ( xyzRead[Axis] < (312 + xyzCorrectCenter[Axis]))) {
    StepPosition = 3;
    xyzJoyPosition[Axis] = 2 + Axis * 6; // X:2, Y:8, Z:14
  }
  // 312 以上 ~ 507+xyzCorrectCenter[Axis] 未満:Frot/Left/Down 0.01mm Step
  if (((312 + xyzCorrectCenter[Axis]) <= xyzRead[Axis] ) && ( xyzRead[Axis] < (507 + xyzCorrectCenter[Axis]))) {
    StepPosition = 2;
    xyzJoyPosition[Axis] = 3 + Axis * 6; // X:3, Y:9, Z:15
  }
  // 507+xyzCorrectCenter[Axis] 以上 ~ 515+xyzCorrectCenter[Axis]未満:中央の不感帯
  if (((507 + xyzCorrectCenter[Axis]) <= xyzRead[Axis] ) && ( xyzRead[Axis] < (515 + xyzCorrectCenter[Axis]))) {
    xyzJoyPosition[Axis] = 19 + Axis;  // センターは、最も大きい番号にする(case処理で最後にするため)
    last_xyzJoyPosition[Axis] = xyzJoyPosition[Axis];
  }
  // 515 + xyzCorrectCenter[Axis] 以上 ~ 710 + xyzCorrectCenter[Axis] 未満:Back/Right/Up 0.01mm Step
    if (((515 + xyzCorrectCenter[Axis]) <= xyzRead[Axis] ) && ( xyzRead[Axis] < (710 + xyzCorrectCenter[Axis]))) {
    StepPosition = 2;
    xyzJoyPosition[Axis] = 4 + Axis * 6; // X:4, Y:10, Z:16
  }
  // 710 + xyzCorrectCenter[Axis] 以上 ~ 1000未満:Back/Right/Up 0.1mm Step
  if (((710 + xyzCorrectCenter[Axis]) <= xyzRead[Axis] ) && ( xyzRead[Axis] < 1000 )) {
    StepPosition = 3;
    xyzJoyPosition[Axis] = 5 + Axis * 6; // X:5, Y:11, Z:17
  }
  // 1000 以上 ~ 1023以下:Back/Right/Up 1.0mm Step
  if (( 1000 <= xyzRead[Axis] ) && ( xyzRead[Axis] <= 1023 )) {
    StepPosition = 4;
    xyzJoyPosition[Axis] = 6 + Axis * 6; // X:6, Y:12, Z:18
  }
  //異常値
  if (( 1023 < xyzRead[Axis] ) || ( xyzRead[Axis] < 0 )) {
    // Serial.println( "ERROR" );
  }
}


// 応答時間&不感帯判定、シングル/連続モード判定
void MoveProcess(short Axis) {
  if ( last_xyzJoyPosition[Axis] == xyzJoyPosition[Axis] ) {
     xyzPosCont[Axis] = xyzPosCont[Axis] + 10;
     // 同一Positionに最低閾値~300msec定位していればシングルstep移動命令
       if ( PosThreshold[ StepPosition - 2 ] <= xyzPosCont[Axis] && xyzPosCont[Axis] < 300 ) {  
          if ( xyzWaitCont[Axis] == 0 ) {
          ContinuousMode = 0;  // 連続モードOFF
          Send_Key_Comand(Axis); // Joystick Positionに応じてコントロールするキー操作
          xyzWaitCont[Axis] = xyzWaitCont[Axis] + 10;
        } else {
          // 一定期間(300msec弱)不感時間を設ける為のカウンタ
          xyzWaitCont[Axis] = xyzWaitCont[Axis] + 10;
        if( xyzWaitCont[Axis] > 290) {
             xyzWaitCont[Axis] = 0;
          }
        }
      }
      // 同一Positionに300msec以上定位していれば連続移動命令
      if ( xyzPosCont[Axis] >= 300 ){
        // 一定間隔で出力する連続モード
        ContinuousMode = 1;  // このモードでは、Step設定不要なので、連続モードON
        Send_Key_Comand(Axis);  // Joystick Positionに応じてコントロールするキー操作  
        delay(150);
      }
  } else {
    // 初めてのPositionにした時の初期化処理
    // ノイズも含め全ての検出は、最初にここを通る
    last_xyzJoyPosition[Axis] = xyzJoyPosition[Axis];
    xyzPosCont[Axis] = 0;
    xyzWaitCont[Axis] = 0;  
  }
}


void Send_Key_Comand(short Axis) {
// Joystick位置に応じてStepを設定
  // STEP        :Continuous、0.01、0.1、1、5、10、100
  // StepPosition:     1    、  2 、 3 、4、5、 6、 7 
  short StepDiff = StepPosition - LastStepPosition;  // 前回Stepから今回Stepへの移動数(キー打数)
LastStepPosition = StepPosition;
if ( ContinuousMode == 0 ) {  // シングル移動モードの時だけ毎回Step設定が必要、連続モードでは不要
// Step設定
if ( StepDiff > 0 ) {
for ( short i=0; i< StepDiff; i++ ) {
Keyboard.write(KEY_1); // Step上げる「1」キー(ここも'1'ではダメ)
}
}
if ( StepDiff < 0 ) {
for ( short i=0; i< -StepDiff; i++ ) {
Keyboard.write(KEY_7); // Step下げる「7」キー(ここも'7'ではダメ)
}
}
}

  // switchの中( (xyzJoyPosition[Axis] -1)/ 3 + 1 )だとcaseを少なくできる
  short MoveDirection = (xyzJoyPosition[Axis] -1)/ 3 + 1; // 移動方向
  // X-RIGHT, X-LEFT, Y-FRONT, Y-BACK, Z-DOWN, Z-UP, XYZ-CENTER
  //    1   ,   2   ,   3    ,   4   ,   5   ,  6  ,    7
  switch ( MoveDirection ) {  // Joystick Positionに応じてキー押下を発行
    case 1:  // X RIGHT xyzJoyPosition[Axis]=1 or 2 or 3
      Keyboard.write(KEY_4);  //「4」
    break;
    case 2:  // X LEFT xyzJoyPosition[Axis]=4 or 5 or 6
      Keyboard.write(KEY_6);  //「6」
    break;
    case 3:  // Y BACK xyzJoyPosition[Axis]=7 or 8 or 9
      Keyboard.write(KEY_2);  //「2」
    break;
    case 4:  // Y FRONT xyzJoyPosition[Axis]=10 or 11 or 12
      Keyboard.write(KEY_8);  //「8」
    break;
    case 5:  // Z DOWN xyzJoyPosition[Axis]=13 or 14 or 15
      Keyboard.write(KEY_3);  //「3」
    break;
    case 6:  // Z UP xyzJoyPosition[Axis]=16 or 17 or 18
      Keyboard.write(KEY_9);  //「9」
    break;
    case 7:  // センター 何もしない  xyzJoyPosition[Axis]=19 or 20 or 21
    break;
    default:  // 削除してもいいかも
    break;    
  }
}
*****

3 件のコメント:

昔青年 さんのコメント...

どんどん重装備になりますね!
当方は、このところSMD部品のはんだ付けに悪戦苦闘中です。

昔青年 さんのコメント...

コメントがうまく入らないようです。

マーティーの工房日誌 さんのコメント...

昔青年さん、すべて私の確認ミスであります。m(_ _)m
昔青年さんどうしてらっしゃるかな~って思っていたんですよ。
本当です。
沢山のコメントありがとうございます。