2014年9月27日

Fanatec Shifter USB Adapter

ポルコンのHシフター(初代タイプ)を、DIYでUSBでPCに繋げられるにしよう!
手持ちの便利デバイス Arduino Uno R3 を、ドライバー不要のヒューマンインターフェースデバイス(USBゲームパッド)にするよ。
(Arduino Leonardoを使うともっと簡単だよ!!)

Arduino UNO Big Joystick HID firmware
スーパー便利なファームウェアが公開されているので、これを使ってかんたん実現!

やりかた

1.ポルコンシフターをArduinoに接続する

こんなmini-Din6pinメス→バラ端子のコネクターを作ったよ。
ピンアサインはスケッチ参照。

2.いったん下記スケッチを書き込む
Arduinoエディタのシリアル通信窓(115200bps)で、各ギア位置にレバーを持っていったときの x=*** y=*** の数値をメモ

3.測定した数値をもとにスケッチの下記部分の数字を編集し、正しい動作になるか再チェック
#define value_X_R 570
#define value_X_12 540
#define value_X_34 240
#define value_X_56 050
#define value_Y_R135 450
#define value_Y_246 040
↑これがうちのポルコンシフターから拾った数値をざっくり入力したもの。
1速/2速や5速/6速でx軸の数値が変わったりするけど、隣の列との間のうまく動く数値に調整しよう!ちゃんと動けばアバウトな数値でOK!
この数値をもとに、閾値を計算している。
書き込み後、シリアル通信窓で gear= の部分がうまくうごけばOK。

ポルコンシフターの仕組みは、レバーにXYの2軸の抵抗値を読み取るアナログポテンショメーターがついてて、その抵抗値を読んでレバー位置を判定する仕組み。アナログなので個体差が出るから、ポルコン本体ではキャリブレーションが必須なんだね。

4.本番スケッチを書き込む
#define DEBUG
行を、
#undef DEBUG
にしてから転送

5.Unoをdfuモードにし、HIDファームウェアを書き込む
dfu-programmerが必要。
やりかたは、冒頭のファームウェア公開ページをよく読んでね!
(1)Arduino基板上のピンをショートさせる
(2)コマンドライン上で指定のコマンドを打ち込む

6.完成!
良さそうなHシフターが発売されるまではこれでいける!


以下、よく理解してないけどどうやら動くスケッチを公開!



/* Fanatec Shifter USB Adapter with Arduino Uno R3
 Author: ukfunk 2014
 derived from http://hunt.net.nz/users/darran/weblog/15f92/Arduino_UNO_Big_Joystick_HID_firmware.html
 
 Shifter Pin Assign mini-Din 6 pin (female socket)
  _1  2_    1:Y-axis  2:X-axis
  3    4    3:GND     4:3.3V
   5[]6     5:mode    6:N/C
 please connect 1,2,3,4 pins to Arduino
 
 (When the 6+1 gated shifter is connected pins 5 and 3 are jumped together to tell the wheel that the gated shifter is conected.)
 */

#define DEBUG // #define DEBUG mode  #undef HID mode

#define pinY A0 // Y-axis analog input (mini-Din pin 1)
#define pinX A1 // X-axis analog input (mini-Din pin 2)

// set individuality of your shifter using DEBUG mode
#define value_X_R    570
#define value_X_12   540
#define value_X_34   240
#define value_X_56    50
#define value_Y_R135 450
#define value_Y_246   40

// axis thresholds calculation
const int threshold_X_R1   = (value_X_R + value_X_12)/2;
const int threshold_X_13   = (value_X_12 + value_X_34)/2;
const int threshold_X_35   = (value_X_34 + value_X_56)/2;
const int threshold_Y_R135 = value_Y_R135 - (value_Y_R135 - value_Y_246)/5;
const int threshold_Y_246  = value_Y_246 + (value_Y_R135 - value_Y_246)/5;

int gear = 0;
int pastgear = 0;
int yvalue = (value_Y_R135 - value_Y_246)/2;
int xvalue = value_X_34;
int xp = 0;

#define NUM_BUTTONS 40
#define NUM_AXES 8        // 8 axes, X, Y, Z, etc

typedef struct joyReport_t {
  int16_t axis[NUM_AXES];
  uint8_t button[(NUM_BUTTONS+7)/8]; // 8 buttons per byte
} 
joyReport_t;
joyReport_t joyReport;

void setup(void);
void loop(void);
void setButton(joyReport_t *joy, uint8_t button);
void clearButton(joyReport_t *joy, uint8_t button);
void sendJoyReport(joyReport_t *report);

void setup(){
  Serial.begin(115200);
  delay(200);
  for (uint8_t ind=0; ind<sizeof(joyReport.button); ind++) {
    joyReport.button[ind] = 0;
  }
}

// Send an HID report to the USB interface
void sendJoyReport(struct joyReport_t *report){
#ifndef DEBUG
  Serial.write((uint8_t *)report, sizeof(joyReport_t));
#else
  // check individuality of your shifter
  Serial.print(\"x= \");
  Serial.print(xvalue);
  Serial.print(\"\\ty= \");
  Serial.print(yvalue);
  Serial.print(\"\\tgear=\");
  Serial.print(gear);
  Serial.println();
  // dump human readable output for debugging
  for (uint8_t ind=0; ind<NUM_BUTTONS/8; ind++) {
    Serial.print(\"button[\");
    Serial.print(ind);
    Serial.print(\"]= \");
    Serial.print(report->button[ind], HEX);
    Serial.print(\" \");
  }
  Serial.println();
  delay(1000);
#endif
}

// turn a button on
void setButton(joyReport_t *joy, uint8_t button)
{
  uint8_t index = button/8;
  uint8_t bit = button - 8*index;
  joy->button[index] |= 1 << bit;
}

// turn a button off
void clearButton(joyReport_t *joy, uint8_t button)
{
  uint8_t index = button/8;
  uint8_t bit = button - 8*index;
  joy->button[index] &= ~(1 << bit);
}

void loop(){
  // get gear position
  yvalue = analogRead(pinY);
  if (yvalue > threshold_Y_R135){
    gear = xposition();
  } else if (yvalue < threshold_Y_246){
    gear = xposition() + 1;
  } else {
    gear = 0;
  }

  // set output status
  if (pastgear == gear){
    ;// do nothing
  } else if (pastgear == 0 && gear > 0){
    setButton(&joyReport, gear-1);
  } else if (pastgear > 0 && gear == 0){
    clearButton(&joyReport, pastgear-1);
  } else {
    setButton(&joyReport, gear-1);
    clearButton(&joyReport, pastgear-1);
  }
  sendJoyReport(&joyReport);
  pastgear = gear;
  delay(10);
}

int xposition(){
  xvalue = analogRead(pinX);
  if      (xvalue > threshold_X_R1 && yvalue > threshold_Y_R135) {xp = 7;} // Reverse Gear
  else if (xvalue > threshold_X_13) {xp = 1;} // 1-2
  else if (xvalue < threshold_X_35) {xp = 5;} // 5-6
  else                              {xp = 3;} // 3-4
  return xp;
}

0 コメント:

コメントを投稿