2023年08月13日
【PC】ハンコンに実車ウインカーレバーを取り付けた話
皆さんおはこんばんにちは、マヨけんです。
過去にPC用のハンドルコントローラーに実車のウインカーレバーを取り付けをしたのですが、その際に使用したArduinoのスケッチを解説して欲しいと要望がありました。
正直人に見せれるようなキレイなコードを書いてないので不安しかありませんが、せっかく要望があったので自分なりに簡単な解説をしていこうと思います。
過去にPC用のハンドルコントローラーに実車のウインカーレバーを取り付けをしたのですが、その際に使用したArduinoのスケッチを解説して欲しいと要望がありました。
正直人に見せれるようなキレイなコードを書いてないので不安しかありませんが、せっかく要望があったので自分なりに簡単な解説をしていこうと思います。
もくじ
1. そもそもどんな物を作ったの?
そもそもどんな物を作ったのか?という事が分かるように、制作を行った際の動画を御覧ください。
2. スケッチの中身は?
作った際の作業工程はここでは割愛しますので、作成したスケッチの中身をそのまま公開します。
汚いコードとなっていますが、素人が書いたコードのためご了承ください。
また、本スケッチは使用するウインカーレバーの動作とArduinoの配線の仕方が全て一致していないと使用できないため、あくまで処理方法の参考レベルで御覧ください。
#include "Keyboard.h"
#include "Joystick.h"
//ジョイスティックの設定
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 0x04,
32, 0, // Button Count, Hat Switch Count
false, false, false, // X, Y, Z
false, false, false, // Rx, Ry, Rz
false, false, // Rudder, Throttle
false, false, false); // Accelerator, Brake, Steering
//ピン定義
#define BAR_R 1
#define BAR_L 0
#define BAR_LM1 2
const int BAR_LM2 = 14;
#define BAR_HB 4
#define BAR_WINT 5
#define BAR_WLO 6
#define BAR_WHI 7
#define BAR_WPULL 8
#define BAR_REW 9
#define BAR_WOFF 10
#define BAR_REWW 16
//一個前の値保管用
boolean gLastR = HIGH;
boolean gLastL = HIGH;
boolean gLastLM1 = HIGH;
boolean gLastLM2 = HIGH;
boolean gLastHB = HIGH;
boolean gLastREW = HIGH;
boolean gLastREWW = HIGH;
boolean gLastWMIST = HIGH;
boolean gLastWOFF = HIGH;
boolean gLastWINT = HIGH;
boolean gLastWLO = HIGH;
boolean gLastWHI = HIGH;
boolean gLastWPULL = HIGH;
//グローバル変数
int lightmode = 0;
int wipermode = 0;
int rearswitch = 0;
int passflg = 0;
int lever_chk = 0;
//初期設定
void setup() {
barInitPin();
Joystick.begin();
}
//バーのピンの設定
void barInitPin()
{
//プルアップしておく
pinMode(14,OUTPUT);
digitalWrite(14,HIGH);
pinMode( BAR_R, INPUT_PULLUP);
pinMode( BAR_L, INPUT_PULLUP);
pinMode( BAR_LM1, INPUT_PULLUP);
pinMode( BAR_HB, INPUT_PULLUP);
pinMode( BAR_REW, INPUT_PULLUP);
pinMode( BAR_REWW, INPUT_PULLUP);
pinMode( BAR_WINT, INPUT_PULLUP);
pinMode( BAR_WLO, INPUT_PULLUP);
pinMode( BAR_WHI, INPUT_PULLUP);
pinMode( BAR_WPULL, INPUT_PULLUP);
pinMode( BAR_WOFF, INPUT_PULLUP);
}
//メインループ
void loop() {
barPolling();
}
//レバーの機能別処理入り口
void barPolling()
{
boolean REW = digitalRead( BAR_REW );
if (gLastREW != REW) {
if (REW == LOW) {
rearswitch = 1;
}
else {
rearswitch = 0;
}
gLastREW = REW;
}
barLR();
barLM();
barHB();
barFW();
barRW();
}
//バーのLR処理
void barLR()
{
boolean L = digitalRead( BAR_L );
boolean R = digitalRead( BAR_R );
if (gLastR != R) {
if (rearswitch == 0)
{
Joystick.pressButton(0);
delay(100);
Joystick.releaseButton(0);
delay(100);
}
gLastR = R;
}
if (gLastL != L) {
if (rearswitch == 0)
{
Joystick.pressButton(1);
delay(100);
Joystick.releaseButton(1);
delay(100);
}
gLastL = L;
}
}
//ハイビーム処理
void barHB()
{
if (rearswitch == 0) {
boolean HB = digitalRead( BAR_HB );
if (gLastHB != HB) {
if (HB == LOW) {
Joystick.pressButton(3);
delay(100);
}
else {
Joystick.releaseButton(3);
delay(100);
}
gLastHB = HB;
}
}
}
//ヘッドライト処理
void barLM()
{
boolean LM1 = digitalRead( BAR_LM1 );
boolean LM2 = digitalRead( BAR_LM2 );
boolean HB = digitalRead( BAR_HB );
//■ライトモード0→1
if (lightmode == 0) {
if (LM1 == 0) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 1;
}
}
//■ライトモード1→0
if (lightmode == 1) {
if (LM1 == 1) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
delay(50);
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 0;
}
}
//■ライトモード1→2
if (lightmode == 1) {
if (LM2 == 0) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 2;
}
}
//■ライトモード2→0
if (lightmode == 2) {
if (LM1 == 1) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 0;
}
}
//■ライトモード2→2
if (lightmode == 2) {
if (LM2 == 0) {
//何もしない
}
}
}
//ワイパー処理
void barFW()
{
if (rearswitch == 0) {
boolean WOFF = digitalRead( BAR_WOFF );
boolean WINT = digitalRead( BAR_WINT );
boolean WLO = digitalRead( BAR_WLO );
boolean WHI = digitalRead( BAR_WHI );
boolean WPULL = digitalRead( BAR_WPULL );
//レバー初期位置チェック
if (lever_chk == 0) {
if (WOFF == 0) {
wipermode = 0;
lever_chk = 1;
}
if (WINT == 0) {
wipermode = 1;
lever_chk = 1;
}
if (WLO == 0) {
wipermode = 2;
lever_chk = 1;
}
if (WHI == 0) {
wipermode = 3;
lever_chk = 1;
}
}
if (wipermode == 0) {
if (WLO == 0) {
Joystick.pressButton(5);
} else {
Joystick.releaseButton(5);
}
}
// ----- ↓2ボタンタイプ -----
if (wipermode == 0) {
if (WINT == 0) {
Joystick.pressButton(6);
Joystick.releaseButton(6);
//Keyboard.print("D");
wipermode = 1;
}
}
if (wipermode == 1) {
if (WLO == 0) {
Joystick.pressButton(6);
Joystick.releaseButton(6);
//Keyboard.print("D");
wipermode = 2;
}
}
if (wipermode == 2) {
if (WHI == 0) {
Joystick.pressButton(6);
Joystick.releaseButton(6);
wipermode = 3;
}
}
if (wipermode == 3) {
if (WLO == 0) {
Joystick.pressButton(7);
Joystick.releaseButton(7);
wipermode = 2;
}
}
if (wipermode == 2) {
if (WINT == 0) {
Joystick.pressButton(7);
Joystick.releaseButton(7);
wipermode = 1;
}
}
if (wipermode == 1) {
if (WOFF == 0) {
Joystick.pressButton(7);
Joystick.releaseButton(7);
wipermode = 0;
}
}
// ----- ワイパーバーPULL
if (gLastWPULL != WPULL) {
if (WPULL == LOW) {
Joystick.pressButton(10);
}
else {
Joystick.releaseButton(10);
}
gLastWPULL = WPULL;
}
}
}
void barRW()
{
boolean REW = digitalRead( BAR_REW );
boolean REWW = digitalRead( BAR_REWW );
if (REW == 1) {
if (REWW == 0) {
Joystick.pressButton(11);
}
else {
Joystick.releaseButton(11);
}
}
if (REW == 0) {
if (REWW == 0) {
//Joystick.pressButton(12);
Keyboard.press(KEY_F12);
}
else {
//Joystick.releaseButton(12);
Keyboard.releaseAll();
}
}
}
上記のスケッチを書き込むのに使用したArduino互換機は以下の物を使用しました。
3. こだわった部分
スケッチの中で特にこだわった部分はヘッドライトの動作です。
私はETS2というトラックゲームを遊んでいますが、ETS2のヘッドライト動作は3ステートのトグルスイッチになっています。
ゲーム内ではライトに割り当ててあるボタンを1回押すと「ライトOFF→ポジションランプ」となり、もう一度ボタンを押すと「ポジションランプ→ロービーム」となり、さらにもう一度ボタンを押すと「ロービーム→ライトOFF」となります。
ライトOFF→ポジションランプ→ロービーム→ライトOFF...と繰り返す動作になっているため、実際のウインカーレバーで操作する際には少し工夫が必要です。
これがライトOFFです。
※スケッチの「LM1=1」の状態です。
これがポジションランプです。
※スケッチの「LM1=0」の状態です。
これがロービームです。
※スケッチの「LM2=0」の状態です。
このスイッチの状態(LM1、LM2のON/OFF状態)と、現在のライトモードを管理する「lightmode」のフラグを使って、ETS2のライトを操作出来るようにスケッチを書いています。
//ヘッドライト処理
void barLM()
{
boolean LM1 = digitalRead( BAR_LM1 );
boolean LM2 = digitalRead( BAR_LM2 );
boolean HB = digitalRead( BAR_HB );
//■ライトモード0→1
if (lightmode == 0) {
if (LM1 == 0) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 1;
}
}
//■ライトモード1→0
if (lightmode == 1) {
if (LM1 == 1) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
delay(50);
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 0;
}
}
//■ライトモード1→2
if (lightmode == 1) {
if (LM2 == 0) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 2;
}
}
//■ライトモード2→0
if (lightmode == 2) {
if (LM1 == 1) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 0;
}
}
//■ライトモード2→2
if (lightmode == 2) {
if (LM2 == 0) {
//何もしない
}
}
}
ライトをポジションライトからライトOFFにする際が注意です。
ゲーム(ETS2)上はポジションライトからライトOFFにするためには、一度ロービームを経由しなければいけないため、「Joystick.pressButton(2); / Joystick.releaseButton(2);」を2回呼び出してライトをOFFにしています。
これを行わないと、実際のライトスイッチ状態とゲーム内のライト状態がズレてしまいます。
また、delayをほとんど記載していないのも理由があります。
delayを多く記載すると、ライトのスイッチを高速で動かした際にゲーム内のライト状態とズレが生じる場合があります。
そのため、「ライトモード1→0」の場合以外はdelayを使用していませんが、私の環境では問題なく動作しています。
//■ライトモード1→0
if (lightmode == 1) {
if (LM1 == 1) {
if (rearswitch == 0) {
Joystick.pressButton(2);
Joystick.releaseButton(2);
delay(50);
Joystick.pressButton(2);
Joystick.releaseButton(2);
}
lightmode = 0;
}
}
4. 最後に
いかがだったでしょうか。初めてのブログで何を書いたらいいかも分からないため、話が散乱していたらすみませんでした。
汚いコードで申し訳ないのですが、少しでも処理方法で悩んでいた方の参考になれば幸いです。