2018年6月2日土曜日

小型工作機CNC2418 その52(CandleをJoystick操作)

マーティーのCNC2418では、
非力なPCなので、Pythonで動くbCNCは重く、動作が追従しないので
GRBL ControllerにGitHubのCandle v.1.1.7を使っております。
CNC2418に付属していたSoftwareのgrblControl.exeのGRBL v1.1対応版です。
この時にGRBL v1.1fにUpgradeしました。

それからだいぶ前には、とっさの時にPAUSEやRESETができるように
外部SWボックスを付けました。

いつ気づいたのか忘れたけど、ふとCandleを操作していると
Jogの下の方に「Keyboard control」ってのがチェックできるように
なっているのが目に止まったわけです。
早速、チェックを入れてキーを適当にいじってみると
CandleをKeyboard制御するキーは、大体分かりました。
カーソルキーを使っていないので、USBテンキーで操作を想定しているようです。
Bluetoothのテンキーにもできますね~

まとめると、こんな動きです。
普通は、マウスでチェックを入れてControl ONしますが
ScrollLock:Keyboard control ON/OFF(トグル)もできます。
ただ、Overriddingは、グレーアウトして操作できなくなってしまいます。
これが、できると最高だったのですが
他にも何かできないか探していると
GitHubのここにキー操作もありますが、上で調べたのと少し異なります。
画像をよく見るとGRBL 0.9j用のCandle v.1.0.11より前のバージョンですね。

これをDigisparkかSparFun Pro MicroのUSB Keyboard機能で作ることにします。
これまでのArduino遊びの成果でできるだろうか?

全ての操作を行えるようにすると、キーは、14個になります。
マトリクスにすると4x4、Digisparkの空き4pinしかないので
74HC139(Decorder)と74HC148(Encorder)外付けにするしかないです。
もう何十年もロジックIC使ってないな~
ちょっと複雑になるので嫌ですね~

抵抗で分圧して14レベルをADCして判別しようかな?
この「しなぷすのハード製作記」さんの所に、詳しい解説付きでありますね~
但し、同時押しはできません。

必要最低限のキー数にするべく仕様をじっくり練ることにします。
手動操作の必要性を考えると
・Y軸:2、8 ⇒ 必須
・X軸:4、6 ⇒ 必須
・Z軸:3、9 ⇒ 必須
・Step:1、7 ⇒ ジョイスティックで検知できないだろうか?
・Feed:+、- ⇒ 固定でいいかも?
・Spindle ON/OFF:0   ⇒ これは不要
・Spindle Speed UP/DOWN:*、/  ⇒ これは不要

StepとFeedをどうしようか?
通常の操作は、まず、大きく移動してから、Stepを小さくして、少しずつ動かし
最後にミルの先を見ながら微調整。という感じの使い方が多いはず。
Stepを小さくし忘れて一気に動いて激突ってことだけは避けたいです。
数十mmとか距離指定で移動の時は、PC上からやることにします。

<方針>
◆Joystick 0.3sec以内の傾けでは、角度に応じて0.01、0.1、1.0 mmの1回移動。
◆0.3sec以上傾けたままだったら150msec間隔でキー連打送出して連続移動。
◆Feedは、100mm/min 辺りで固定。

とすると
ジョイスティックのX、Y、Zだけをアナログ3本で見ればいいです。
分解能は上下左右に3段階。

Keyboard Controlに入る時、StepもFeedもどこになっているか不定なので
Step:Continuous、0.01、0.1、1、5、10、100の7段階。
Feed:10, 50, 100, 500, 1000, 2000の6段階。
毎回設定する必要があります。
Step 100とかでいきなり動き出すとエンドミル折れ事故になりかねません。

PCのCandle画面で「Keyboard control」にチェックをいれるのではなく
Joystickの横にタクトスイッチをつけ「Scroll Lock」キーにします。
必ず、そのキーでKyboard Controlモードにすることにすれば
「Scroll Lock」キー押して「Keyboard control」に入る時点で
「1」6回、「7」1回で、Step:0.01
「-」5回、「+」2回で、Feed:100
すれば初期化できそうです。

とするとマイコンの必要入力は、
・ジョイスティックX:アナログ1(分解能:7段階)
・ジョイスティックY:アナログ2( 同上 )
・ジョイスティックZ:アナログ3( 同上 )
・Scroll Lock キー  :デジタル1(モーメンタリーSW)
ということになります。
ジョイスティックは、可変抵抗なので
センター位置と片側倒しに3段階とすると
センター+両側倒しで1+3 x 2=7段階の分解能が必要になります。

では、実験に取り掛かります。
SerialとKeyboardを使うのでこの前使ったSparkFun Pro Microを使います。
まず、Joystickを可動してADC値をみて7段階の閾値を決めることにします。

スケッチは、
Arduino IDE v.1.8.3の標準サンプル
[スケッチの例]-[09.USB]-[Mouse]-[JoystickMouseControl]
を雛形にしてモディファイしていきます。
*****
/*
 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)
*/

#include "Keyboard.h"

const int xAxis = A0;         // joystick X axis
const int yAxis = A1;         // joystick Y axis
const int zAxis = A2;         // joystick Z axis
const int ControlEnPin = 2;    // for Keyboard Control ON/OFF on Candle
const int ledPin = 5;         // Keyboard control LED (TX LED on board)

boolean KeyContIsActive = false;  // Keyboard Control Enable/Disable Flag
int lastSwitchState = HIGH;        // previous Control state (LOW Active)

void setup() {
  pinMode(ControlEnPin, INPUT_PULLUP);       // Control Enable Pin
  pinMode(ledPin, OUTPUT);         // the LED pin
  Serial.begin(9600);
  Keyboard.begin();
}

void loop() {
  int switchState = digitalRead(ControlEnPin);  //Control ON/OFF SW
  // if it's changed and it's low, toggle the keyboard control state:
  if (switchState != lastSwitchState) {
    if (switchState == LOW) {
      KeyContIsActive = !KeyContIsActive;
      // turn on LED to indicate Keyboard control state:
      digitalWrite(ledPin, KeyContIsActive);
      if (KeyContIsActive) {
      Serial.println("Keyboard Control Enable");
      }
    }
  }

  // save switch state for next comparison:
  lastSwitchState = switchState;
  // read the x, y & z axes:
  int xReading = analogRead(xAxis);
  int yReading = analogRead(yAxis);
  int zReading = analogRead(zAxis);

  // if the Keyboard control state is active, Candle control by Joystick:
  if (KeyContIsActive) {
    Serial.print("X-Axis: ");
    Serial.println(xReading);
    Serial.print("Y-Axis: ");
    Serial.println(yReading);
  }
  delay(5);
}
*****

ブレッドボード図は、JoystickMouseの時と同じです。
Joystickも同じくこれです。
X、Y軸に可変抵抗器 5KΩ。
右下のタクトスイッチは、ノブを押すとGNDに落ちます。

SparkFun Pro Microは、これ
AVRマイコンは、ATmega32u4です。
Keyboard(USB-HID)使ってSerial(USB-Serial)でデバッグとなると
これか、Arduino Leonardoになります。

これの書き込みの素行について、ここでやったので、つまずくことなく書き込めます。
Arduino IDE v.1.8.3のこの設定で書き込みます。
書き込み完了しました。
デバイスマネージャーのポート(COMとLPT)は、
「USB シリアル デバイス(COM12)」⇒ COM14に変わっています。
前回は、JoystickMouseの時は、COM15だったのですが
この辺の規則がわかりませんね~

シリアルモニタを開いて、Joystickのノブを押すとControlアクティブになります。
Joystickは、振れてない状態なのでセンター値です。
Xは、1023の1/2ですが、Yは、15程ズレています。
ちょっとシリアルプロッタで遊んでみます。初プロッタです。
前のスケッチを赤文字のように変えます。
「,」で区切ると別系列のプロットになるそうです。
*****
  if (KeyContIsActive) {
//    Serial.print("X-Axis: ");
//    Serial.println(xReading);
    Serial.print(xReading);
    Serial.print(",");
//    Serial.print("Y-Axis: ");
    Serial.println(yReading);
  }
*****
書き込んで、Arduino IDEの[シリアルプロッタ]を開きます。
Joystickのノブを押し、Controlアクティブにして
Joystickをグリグリ回すと、こんな感じでプロットしてくれます。こりゃ面白い!
ちゃんと赤と青の2系列になっています。
これでJoystickの傾け具合で7段階の切り分けポイントを見ようと思ったのですが
縦軸レンジが自動で大きく変化しまくるので、見難いですね~
そこで、あえて「0」と「1023」もプロットしてレンジを固定します。
*****
  if (KeyContIsActive) {
    Serial.print(0);
    Serial.print(",");
    Serial.print(1023);
    Serial.print(",");
//    Serial.print("X-Axis: ");
    Serial.print(xReading);
    Serial.print(",");
//    Serial.print("Y-Axis: ");
    Serial.println(yReading);
  }
*****
縦軸レンジを固定できました。
センターから片側いっぱい、ゆっくり動かした時のプロットです。
Joystickノブの傾き可動域の70%位でMAXになっている感じなので
分度器で傾き vs ADC値 を測ってみます。
ボールペンの芯を針代わりにします。
この分度器、最後に使ったのは、何十年前だったか?
可動域は、約±32°です。
正確に傾けるのは難しく、±2°は読取り誤差という感じです。
何と!中央から±5°以内・±20°以上は、不感帯で変化しません。
つまり読取有効可動域:±15°
ん~ん、中央の遊びが広いのは、いいのか悪いのか...
実測したJoystickのこの軸は、中央が少し右寄りにズレているのがわかります。
これを±3段階かあ~ なかなかシビアーになりそうです。
X・Y・Z個別に微調することも考えてスケッチを進めないといけなさそう。
部品箱の奥にこれがあったのを思い出しました。
どうやって入手したか全く記憶に残っておりません。
分度器を当て難いのでざっくり測ったのですが
可動域も広いし、直線性もわりとよさそうです。
ただ、抵抗器が500KΩもありノイズを拾い易いんです。
Joystickによる差は大きいんですね~
どうやら純日本製かな?Aliさんにないですね~
ひとまず、最後の手段として置いといて、さっきのJoystickで進めることにします。
で、簡単なスケッチで少々試行錯誤して、この様に分割することにしました。
この実験に使ったJoystickの軸は、
センター値:496で、1023÷2=511より-15ズレていてグラフが少し右寄りです。
もう一方の軸は、センター512なのですが、分度器が当てられなかったのです。
微妙なズレですが、補正がないとその影響がでて
DOWN方向より、UP方向の方が、傾け角が大きくなるのがわかる程なので
感覚的に同程度になうように、境界値の補正を入れました。
0~1023なのでセンター511が妥当ですが
この軸のVRは、センター496を示すので、その差15を補正することにします。
Joystickのノブの傾きとPosition No.の関係図です。
境界値の検討は、ひとまず、これにしておいて。

次は、検知方法です。
ADCは、約10msecでXYZ一式データを取り込んでいます。
1.0mmのPosition1や7へのJoystickの往復では、
0.1mm(Position2、6)や0.01mm(Position3、5)を通過するので
単純にADC値だけで振り分けると、同時に検知してしまいます。
短時間で通過する場合は、検知しないようにしなければなりません。
それと、少し長めの時間(0.3sec以上)特定のPositionに留まっている場合は、
移動命令を連続で送出するようにしたいのです。

これには、DigisparkとArduino UNOでCapacitiveSensing(容量検知センサー)
でやっているカウント方式の考え方が参考になりました。
これまでArduinoで遊んだ事が予言だったかのように役に立ちます。

その前にvoid loop() 内がやけに長くなったので、関数にしたり少し整理します。
マーティーには、フローチャートから始めるいう習慣がないもので(-_-;)
Y軸だけで試行錯誤・右往左往を繰り返すこと...3、4日
やっといい感じに操作できるスケッチになってきました!

で、同一Positionに40msec以上いれば検出しPosition情報を出力させます。
40msec未満のノイズも防御してくれます。
これ以上早くすると大きく傾けた時の途中のPositionにも反応します。
長過ぎると素早い動きに反応しないことが起こります。
一度反応すると200msecは、反応しないようしてシングル動作とします。
最大まで傾けた時は、素早くやっても160msec程かかり、
ADCは、10msec間隔で取り込んでいるので
この処理をしないと10msecおきに連打した反応になってしまいます。
また、同じPositionに300msec以上いると150msec間隔で連続送出します。
傾け具合で上下3段階を認識できるようになりました。

ちょっと長いですが、この時点のスケッチ。まだY軸だけ。
途中経過の恥ずかしいスケッチなので、最小文字です。記録です。
***ProMicro_JoystickTEST_07.ino***
/*
 For 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)
*/

#include "Keyboard.h"


const int xAxis = A0;      // joystick X axis

const int yAxis = A1;      // joystick Y axis
const int zAxis = A2;      // joystick Z axis
const int ScrLkPin = 2;    // for Keyboard Control ON/OFF on Candle
const int ledPin = 5;      // Keyboard control LED (TX LED on board)

boolean KeyContIsActive = false;   // Keyboard Control Enable/Disable

int lastSwitchState = HIGH;        // previous Control state (LOW Active)

int xReading = 511;


int yReading = 511;                  // 1023÷2

int yCenter = 496;                   // 実測のY-Axis Center値
int yCenterCorrect = yCenter - 511;  // Y軸Center補正(Parts VRバラツキ補正)
short yJoyPosition = 4;              // JoystickのPosition(7段階) 4:中央
short last_yJoyPosition = 4;         // 直前のJoystick Position
unsigned long yPosCont = 0;          // 同一Positionの時の経過をカウント
short yWaitCont = 0;                 // Single Step移動時の無効時間カウント

int zReading = 511;


void setup() {

  pinMode(ScrLkPin, INPUT_PULLUP);  // ScrollLock Pin
  pinMode(ledPin, OUTPUT);          // the LED pin
  Serial.begin(9600);
  Keyboard.begin();
}

void loop() {

  int switchState = digitalRead(ScrLkPin);  //ScrollLockキーでControl ON/OFF
  // switchStateは、変化したか?
  if (switchState != lastSwitchState) {
    // switchStateがLOWならば、Keyboard control stateをトグルする
    if (switchState == LOW) {
      KeyContIsActive = !KeyContIsActive;
      // turn on LED to indicate Keyboard control state:
      digitalWrite(ledPin, KeyContIsActive);
      if (KeyContIsActive) {
      Serial.println("Keyboard Control Enable");
      }
    }
  }

  // save switch state for next comparison:

  lastSwitchState = switchState;
  // read the x, y & z axes:
  xReading = analogRead(xAxis);
  yReading = analogRead(yAxis);
  zReading = analogRead(zAxis);

// if the Keyboard control state is active, Candle control by Joystick:

  if (KeyContIsActive) {
    Joy_Y_Detector();
  }
  delay(10);
}

void Joy_Y_Detector() {

    // 0 以上 ~ 20未満:DOWN 1.0mm Step
    if (( 0 <= yReading ) && ( yReading < 20)) {
    yJoyPosition = 1;
    Y_MoveProcess();
    }
    // 20 以上 ~ 300+yCenterCorrect 未満:DOWN 0.1mm Step
    if (( 20 <= yReading ) && ( yReading < (300 + yCenterCorrect))) {
    yJoyPosition = 2;
    Y_MoveProcess();
    }
    // 300 以上 ~ 480+yCenterCorrect 未満:DOWN 0.01mm Step
    if (((300 + yCenterCorrect) <= yReading ) && ( yReading < (480 + yCenterCorrect))) {
    yJoyPosition = 3;
    Y_MoveProcess();
    }
    // 480+yCenterCorrect 以上 ~ 520+yCenterCorrect未満:中央の不感帯
    if (((480 + yCenterCorrect) <= yReading ) && ( yReading < (520 + yCenterCorrect))) {
    yJoyPosition = 4;
    last_yJoyPosition = yJoyPosition;
    }
    // 520 + yCenterCorrect 以上 ~ 720 + yCenterCorrect 未満:UP 0.01mm Step
    if (((520 + yCenterCorrect) <= yReading ) && ( yReading < (720 + yCenterCorrect))) {
    yJoyPosition = 5;
    Y_MoveProcess();
    }
    // 720 + yCenterCorrect 以上 ~ 1000未満:UP 0.1mm Step
    if (((720 + yCenterCorrect) <= yReading ) && ( yReading < 1000 )) {
    yJoyPosition = 6;
    Y_MoveProcess();
    }
    // 1000 以上 ~ 1023以下:UP 1.0mm Step
    if (( 1000 <= yReading ) && ( yReading <= 1023 )) {
    yJoyPosition = 7;
    Y_MoveProcess();
    }
    //異常値
    if (( 1023 < yReading ) || ( yReading < 0 )) {
    Serial.println( "ERROR" );
    }
}

void Y_MoveProcess() {

     if ( last_yJoyPosition == yJoyPosition ) {
      yPosCont = yPosCont + 10;
        // 同一Positionに30~300msec定位していれば1step移動命令
        if ( 30 < yPosCont && yPosCont < 300 ) {  
          if ( yWaitCont == 0 ) {
          Serial.print("SingleMode Position: ");
          Serial.print(yJoyPosition);
          Serial.print("  ResponseTime: ");
          Serial.println(yPosCont);
          // ここにPositionに応じてコントロールするキー操作が入る。
          yWaitCont = yWaitCont + 10;
          } else {
            // 一定期間、不感時間を設ける為のカウンタ
            yWaitCont = yWaitCont + 10;
            Serial.print("   WaitingCounter: ");
            Serial.println(yWaitCont);
            if( yWaitCont > 200){
            yWaitCont = 0;
            }
          }
        }
        // 同一Positionに300msec以上定位していれば連続移動命令
        if ( yPosCont > 300 ){
        // 一定間隔で出力する連続モード
        Serial.print("ContinuousMode Position: ");
        Serial.print(yJoyPosition);
        Serial.print("  ContinuousTime: ");
        Serial.println(yPosCont);
        // ここにPositionに応じてコントロールするキー操作が入る。  
        delay(150);
        }
      } else {
      // 初めてのPositionにした時の初期化処理
      // ノイズも含め全ての検出は、最初にここを通る
      Serial.print("      Rejected Position: ");
      Serial.print(yJoyPosition);
      Serial.print("  ResponseTime: ");
      Serial.println(yPosCont);
      last_yJoyPosition = yJoyPosition;
      yPosCont = 0;
      yWaitCont = 0;  
      }
}
*****

上のスケッチを動作させてデバッグ中のシリアルモニタの表示です(-_-;)
ノブをPosition7 ⇒ Position1へ移動させた場合、
途中のPosition 6~2は、40msec以内なので、Rejectされています。
最下位(Position1)に一杯の角度(約30°)で約0.3sec以上傾けたままにすると
150msec間隔で検出通知を出す連続移動モードになります。
これで検出方法の基本の考え方は、良さそうです。
次は、キー操作ルーチンです。

<STEP・FEEDの初期化>
Stepは、100、10、5、1、0.1、0.01、Continuousの7段階
Feedは、10, 50, 100, 500, 1000, 2000の6段階
Keyboard Controlに入った時点で不確定なので
キー押下回数で毎回クリアして設定する必要があります。

PCのCandle画面で「Keyboard control」にチェックをいれるのではなく
ジョイスティックの横の「Keyboard Control開始」キーを押すと
「Scroll Lock」キーコードを発行して、Keyboard Control ONにするのです。
このボタンは、トグル動作になります。

必ず、そのキーでKeyboard Controlモードにすることにすれば
「Keyboard Control開始」キー押して「Keyboard control」に入る時点で
「7」6回、「1」1回で、Step:0.01
「-」5回、「+」2回で、Feed:100
とキーコードを送出して初期化します。
Feedは、このまま固定にします。

<STEP設定>
変数 StepPosition:1~7 で管理します。
STEP          :Continuous、0.01、0.1、1、5、10、100
StepPosition:     1         、  2   、 3  、4、5、 6、  7
「7」キーでDown(上記の左方向)
「1」キーでUp(上記の右方向)

スケッチは、Joystick Positionに応じた変数StepPositionにより
switch命令で分岐させることにします。
***
switch(StepPosition){
   case 1: // DOWN Step 1.0mm
      実行文1
      break;
   case 2: // DOWN Step 0.1mm
      実行文2
      break;
   case 3: // DOWN Step 0.01mm
      実行文2
      break;
   default:
      break;
}
***

この辺でZ軸のJoystickも配線します。
勢いで2個ポチっていてよかった!
で、早速、
Keyboard Controlを有効にするScrollLockキーからつまずきました(´-﹏-`;)

この時のDigisparkでは、
「DigiKeyboard.sendKeyStroke();」でしたが、Pro Microでは、違っていて、
Arduino標準の「Keyboard.write();」を使えます。

Arduino IDE v.1.8.3 では、
C:\Program Files (x86)\Arduino1.8.3\libraries\Keyboard\src
のKeyboard.hに特殊キーの定義がありますが、ScrollLockは定義されていません。

内蔵KeyboardとUSB KeyboardのCodeは異なるので
USB.orgのPDFの仕様書「USB HID USAGE TABLES」で調べると
USB Key Code(HIDキーコード)は、
・ScrollLock:71(0x47)
所が! Keyboard.write(71); では、ウンともウンとも...
で、ここにヒントが有りました。
すぐに 「Keyboard.press(71+136);」をやってみると、
お~~! ScrollLockキーが効いてPCのLEDが点きました!

更に('-')と('+')も キーコード(86)(87)だけではダメで
Keyboard.write(86); ⇒ Keyboard.write(86+136);
Keyboard.write(87); ⇒ Keyboard.write(87+136);
と「+136」しないと動作しないのです。
USB.orgのPDFの仕様書「USB HID USAGE TABLES」では、
「136:kyboard International2」とあり、意味がわかりませんね~
2進数にしてみると
86(0x56)+136(0x88) =0101 0110 + 0x88 =1101 1110(0xDE)
87(0x57)+136(0x88) =0101 0111 + 0x88 =1101 1111(0xDF)
ニブルの最上位ビットに意味がありそうです。
数字キーもKeyboard.write('7'); で動作する所としない所があるのです。
安全を見て全てに「+136」を付けることにします。
おまじない」として記憶に留めておかねばならないようです。

ひとまず、一通り動くスケッチができました\(^o^)/
Version 0.9ってことにします。
(マーティーの管理No.:ProMicro_JoystickTEST_09.ino)

この時点での問題:
 Step切替の反応が少し変、ずっとやるとズレて、最後は、変わらなくなります。
 どうもStepが誤動作している感じ、Joystick位置の認識とStep値変更がズレます。
   ずっといじっていると0.01mm位置の傾きで1.0mmStepになったりします。
 Joystick位置は正しく認識しているのにStepがズレているようです。

Woodpeckerを接続してデバッグ中。
Step切替の反応ズレは、
Keyboard.write(); の後の delay(10); ⇒ delay(1); で、
良くなったような気もするけど、しばしいじっていると狂ってきました。

Keyboard.write('777777'); は、この様な連続送出はできず
Keyboard.write('7'); と同じ、1打となります。

ここのKeyboard.write解説には、delayはないですね~
Digisparkの時は、delayが必要だったのでてっきり、必須かと思い込んで...
ひょっとしてdelayは不要なのか? delay(1); ⇒ 削除してみると、
動きますね~ 何となく良さそうなですが、まだ微妙です。
摺動子のノイズかもしれないので、後でADCの平均を取ることにして、
ひとまず先に進むことにします。

それと1.0mm移動の時は、Feed 100では、実際の移動に約1secかかります。
移動が完了する前にJoystick位置をあちこち変えると誤動作するのです。
実際には、マシンを見ながら操作するのでこの辺は、そう問題ないかもですが、
Feed:500固定に変更します。1.0mm移動なのでこれ以上は、変わりません。

不要になってきたデバッグ用のSerial.printを外したら随分良くなりました。

ノブを斜めにしてXY同時操作は大丈夫です。
同時に移動するのではなく、XY夫々にStep設定されてから順番に移動します。

ただ、最初の懸念通り、0.01、0.1の角度が微妙なので
操作に慣れる必要がありますが、特にZ軸が不意に1.0mm下がると怖いです。
起動時にセンター補正するようにしたので、センターの不感帯を狭くできるハズです。
センター不感帯域と0.01/0.1mm境界の閾値を変えてみます。
グラフのデータは、センター:511に補正しています。
更に0.01mm、0.1mm、1.0mmで最低検出時間の閾値を変えてみます。
ノブの傾きが大きい程、最低検出時間の閾値を上げてみます。
ノブを最大に傾けるのに160msec程かかるので
40~170msecの間に0.1mmの閾値を設けることにしますが、
100msecでは反応悪くなる感じで、なかなか良い所に調整できません。

やっぱし、ADCを10msec間隔の一発取りから、平均にしないといけないようです。
ATmega32u4のDatsheetを見るとADCの変換時間は、65~260μsec
Arduinoとして使う場合、Arduino IDE内は探せず、ADC Clockがわかりません。
Arduino.ccのここに、analogRead()は、100μsecとありました。
どうやら一律で固定されているようです。

XYZ軸で夫々10回の平均を取るとすると
単純計算で100μsec x 10回 x 3軸=3msec
delay(7); を入れ、約10msec間隔で10回平均ADCのXYZ軸全部が回ります。

そうすると、概ねうまく動きそうになってきました。
ようやく実働テストに行きま~す!

あ~ぁ Left・Rightが逆でした!
どうも0.1mm Stepが動かない時があります。
0.1mmStepでの反応時間を 80 ⇒ 60msec にすると、
0.1mmの時に0.01mmもダブルで動くことが多い(つまり0.11mm移動)
反応時間を0.01mm:40、0.1mm:60、1.0mm:170msec
⇒  0.01mm:80、0.1mm:40、1.0mm:170msec と逆転させると
意外や!かなり操作性が良くなりました!\(^o^)/

では、ようやく、その動きを動画で
Joystickのノブを押すとKeyboard controlが有効になりStepとFeedを初期します。
音的には「ヒューッ」ってのが1mm移動、
「ヒョン」は、0.1mm、「カッ」が、0.01mm移動です。
長押しで連続移動です。
左手でも何とか操作できているので、大丈夫でしょう!
CNC2418と一緒の動画
ちょっと小さくて判り難いですが、一応。
これで良さそうです!実用できそうです。
スケッチを見直して無駄な所や判り難い所を整理します。
できるだけ関数化して、Serial関連を全て削除して完成とします。

最後に完成スケッチです。マーティーとしては、かなり力作なのです。
*****
/*
 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_JoystickTEST_15.ino 2018.5.26 Marty Vessel 
*/

#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 Left)
#define KEY_6 35+136  //「6」キー(X-Axis Right)
#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() {  // Check Key & 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')では、まれにダメなことがある
    //delay(10);
  }
  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)ではダメ
    //delay(10);
  }
  for ( short i=1; i<=3; i++ ) {
    Keyboard.write(KEY_Plus);// 「+」3回、('+')や(87)ではダメ
    //delay(10);
  }
}


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 Front~ Back 1.0 ⇒ 0.1 ⇒ 0.01 ⇒ 0.01 ⇒ 0.1 ⇒ 1.0
//  7~12 Y-Axis Left ~Right 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_6);  //「6」
    break;
    case 2:  // X LEFT xyzJoyPosition[Axis]=4 or 5 or 6
      Keyboard.write(KEY_4);  //「4」
    break;
    case 3:  // Y FRONT xyzJoyPosition[Axis]=7 or 8 or 9
      Keyboard.write(KEY_8);  //「8」
    break;
    case 4:  // Y BACK xyzJoyPosition[Axis]=10 or 11 or 12
      Keyboard.write(KEY_2);  //「2」
    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;    // 削除してもいいかも
  }
}
*****

Joystickは、最初からやろうと思っていたわけではなく
最初の構想時にカッコよさにグラっときて、こんなものもポチっておりました。
モーメンタリーの単にON/OFFだけなので
Joystickの様に傾きで移動Stepを変えたりできません。
残念ながら、Joystickの傾き検知がうまくいったので出番なしです。
ノブのゴムブーツだけでもあるといいんですが。
4方向のXY軸用。


2方向のZ軸用。
どちらも結構でかいです。


実は、先週時点で概ねできていたのですが、本稿をまとめている時に
家族のPC(Win8.1)が突然起動不能に、原因はHDDの瀕死状態。
ここ数ヶ月、バックアップを取ってなかったので大事なデータを吸い取りに苦戦。
HDD取り出してWin10にUSB外付けで見ると起動ドライブのFormat形式すら見えず、
Ubuntu 18.04 LIVE DVDやらTrue Image 2014 Bootable DVDやらを動員して
転送レートが数Kbpsや0bpsになる事もしばしばあり冷や汗の連続(´-﹏-`;);;;
どうにかこうにか救済復旧できました!

次は、入れ物を作らねば。
3D PrinterはないのでCNC2418で頑張るつもりです。
Fusion360使うのも久しぶりです。

6 件のコメント:

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

CNC2418 随分と重装備になってきましたね。
ジョイスティック当方の死蔵パーツには、200KΩと100KΩのものがありました。
リクエストとして、リミッタSWと回転数計の装備をお願いしたい所です。(またまた他力本願モードです)

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

再送です。(前回は届かなかったようです)
CNC2418重装備になってきましたね。
リミットスイッチと回転数計を追加してもらえれば参考にしたいです。
最近、SMDクランプ(PX1810)というSMDパーツの半田づけ補助ツールを入手し、よる年波であきらめていた極小パーツを利用した電子工作にはまっています。
 

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

CNC2418重装備になってきましたね。
リミットスイッチや回転数計を追加すれば完璧ですね。(まねしやすい)
当方はPX1810というSMDクランプ(SMDパーツを手半田するための補助ツール)を入手しSMDパーツてんこ盛りのキットを製作中です。

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

CNC2418重装備になってきましたね。
リミットスイッチ、回転数計を備えれば完璧では。

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

大変申し訳ございません。メールの通知が全く来なくなっていました。
その後のコメントも至急見ますm(_ _)m
リクエスト、首を長~くしてお待ち下さい。

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

昔青年さん、何度もコメントいただきm(_ _)m
このSMDクランプ!すごいTOOLですね!
私も間違って0.5mmピッチの16pinパッケージ買ったので
というかそれしかなかったのですが、どうしようかと思っていた所です。
このTOOL、Aliにはないんですね~
Amazonでは、かなり高価、ちょっと手がでません。
自作するしかなさそうです。頑張ってみます。
それにしても何やらすごいもの作られていますね。
見たいです。