BVEワンコインマスコンをつくる(2)

スポンサーリンク

※2017年5月30日1時06分追記
この記事の内容が若干わかりにくいと感じたため、もう少し簡単にしたバージョンを新たに投稿します。そちらもご覧になることをお勧めします。

※2018年1月2日18時15分追記:少しわかりやすくした記事へのリンクを追加しました。

みなさんこんにちは。昨日1時間ぐらいかけて、GIPIに使えるコマンドを送信してくれるArduinoのヘッダファイルを作っていましたがうまくいかず…まぁそのうち学校でも使うことになると思うのでなるべく早く攻略しておきたいです。完成したら公開できたらナーと思います。

 

さて、今回はBVE用ワンコインマスコンを作る記事の第2回になります。今回は技術的説明が多いです。実践は最後のほうになります。

 

この記事を見ている方は「Arduinoとはどんなものか」をある程度わかっている前提で話を進めます。まだよくわかっていない人は先に調べてから読むことをお勧めします。

この記事に書いてあることは実体験および私の記憶に基づくことなので、だれでも絶対にできるとは限りません。失敗して損害が出た場合でもそれは自己責任でお願いします。自信がない方はTwitterもしくはこの記事のコメント欄にて無償でサポートを行います。お気軽にどうぞ。

ではでは話を進めましょう。

対BVEの通信について

BVEとArduinoの通信は、COMポートを使用したシリアル通信にて行いますが、BVE本体に備わる機能に「シリアル通信」機能はありません。そこで、BVEに対してプラグインを導入することで外部からの通信を実現しています。デフォルトではTSマスコンに対応するプラグインが導入されていて、BVEの「設定」画面の「入力デバイス」タブにて「TS Master Controller」を選択することでTSマスコンとの通信ができるようになりますが、私の環境ではArduinoから送信したTSマスコンと同じコマンドをプラグインは正常に読み取ってくれませんでした。そこで、今回は私の作成した「GIPI」を利用します。GIPIは私の所持するArduino互換品(Arduino NANO , Arduino UNO , Arduino Leonardo , Arduino MEGA 2560)すべてで動作確認を行った、Arduinoに特化した入力デバイスプラグインです。TSマスコン用のプラグイン以上のマスコン段数・ブレーキ段数に対応し、新幹線や機関車の全段数の操作をする運転も可能にしたプラグインです。現状、おそらく送信する必要のあるコマンドが公開されているプラグインはこれだけなんじゃないかと思っています。

で、プラグインとArduinoの通信においてArduino側で設定する必要があるのが「通信速度」です。数字が大きいほど速いのですが、プラグイン側で9600bpsと指定しているのでArduino側もそれに合わせます。設定の方法は以下の通りです。

[cpp classname="myclass" collapse="false" firstline="1″ gutter="true" highlight="2″ smarttabs="true" tabsize="3″ toolbar="true" title="Serial.begin.ino"]
void setup(){
Serial.begin(9600);//シリアル通信開始
while(!Serial);//シリアル通信が開始するのを待つ
}

void loop(){} [/cpp]

この状態だと、開始しただけで何もコマンドを送信していないのでBVEの操作は行えません。そこで出てくるのが「Serial.print」です。以下のように使用します。

[cpp classname="myclass" collapse="false" firstline="1″ gutter="true" highlight="7″ smarttabs="true" tabsize="3″ toolbar="true" title="Serial.print.ino"]
void setup(){
Serial.begin(9600);
while(!Serial);
}

void loop(){
Serial.print(“");//""の中に送信したいコマンドを入力
} [/cpp]

なお、Serial.printf()を使用することによって自動的に改行記号を付けて送信してくれますが、GIPIでその機能はいらないのでSerial.printを使用します。
例えば「ブレーキを4段目に設定したい」というのであれば「Serial.print(“TOB4\r")」と送信すればOKです。詳しいことはプラグインに付属しているコマンド一覧をご覧ください。なお、バックスラッシュと円記号は同義です。

 

これでArduinoからBVEを操作することができるようになりました。ただ、これだけだと「現実世界のノッチの位置に応じて対応するコマンドを送信する」という機能は実現できません。実現するためにはif文やswitch-case文などを使用して条件分岐を行うのですが、初心者には難しいので途中まで作りました。長いので、見たい場合は「GIPI1.ino」をクリックしてください。

[cpp classname="myclass" collapse="true" firstline="1″ gutter="true" smarttabs="true" tabsize="3″ toolbar="true" title="GIPI1.ino"]
//Tech Otter ver1.0.0
//mascon位置読み取り機能は未実装
const int maspin = A1;//マスコンの可変抵抗を接続するピンを選択
const int levpin = A3;//レバーサーの可変抵抗を接続するピンを選択
const int bt1pin = 3;//ボタン1を接続するピン番号の選択
const int levF = 750;//F側閾値
const int levR = 250;//R側閾値 levF<levRの場合はlever()の変更が必要
int mas = 0;
int lev = 0;
int bt1now = 0;
int comold = 57;
int bt1old = 0;
void setup(){
delay(100);
Serial.begin(9600);
while (!Serial);
Serial.print(“TORN\r");
mascom(57);
pinMode(bt1, INPUT_PULLUP);
delay(500);
}
void loop() {
lever();//レバーサー位置を読み取りコマンド出力
mascon();//マスコン位置を読み取りコマンド出力
button();//ボタン操作を読み取りコマンド出力
delay(25);
}
void mascon() {
mas = analogRead(maspin);
}
void mascom(int comm){
if (comm != comold) {
comold = comm;
comm = abs(comm);
if (comm >= 50){
comm = comm – 50;
Serial.print(“TOB");
Serial.print(comm);
Serial.print(“\r");
} else {
Serial.print(“TOP");
Serial.print(comm);
Serial.print(“\r");
}
}
}

void lever() {
int lread = analogRead(levpin);
if (lread < levF && lread > levR && lev != 0) {
Serial.print(“TORN\r");
lev = 0;
} else if (lread > levF && lev != 1) {
Serial.print(“TORF\r");
lev = 1;
} else if (lread < levR && lev != 2) {
Serial.print(“TORR\r");
lev = 2;
}
}

void button() {
bt1now = digitalRead(bt1pin);
if (bt1now != bt1old) {
bt1old = bt1now;
if (bt1now == HIGH) {
Serial.print(“TOK1U\r");
} else {
Serial.print(“TOK1D\r");
}
}
delay(50);
}[/cpp]

汚いですね…はい。。。ちょっと解説していきます。
constだとかintだとかifだとかについてはArduino 日本語リファレンスなどを利用してご自身で調べてください。
setupではシリアル通信を開始した後に定位置(逆転機中立、ブレーキ7段目)に設定しています。そして、loopで25ms(ミリセカンド=0.001秒)ごとにlever()とmascon()とbutton()を実行します。この3つで行うことは下のほうで設定しています。
void lever(){}でレバーサーの可変抵抗がどのくらい回っているかを測定し、その位置に応じて対応するコマンドを送信しています。ここは変えなくても大丈夫です。
void button(){}でボタンが押されているのか押されていないのかを判断します。とりあえずボタン数1つまで対応していますが、「bt1now = digitalRead(bt1)」から「delay(50);」の直前まで複製し、さきほど紹介したconstがたくさん書いてあるところの「const int bt1pin = 3;」およびその少し下にある「int bt1now = 0;」および「int bt1old = 0;」をそれぞれの直下にコピペしたうえで「1」という数字を「2」などと代えていきます。念のため変更例を示しておきます。なお、ボタンの部分以外は省略しています。

[cpp classname="myclass" collapse="false" firstline="1″ gutter="true" smarttabs="true" tabsize="3″ toolbar="true" highlight="2,5,6,20-28″ title="GIPI1/buttonplus.ino"]
const int bt1pin = 3;//ボタン1を接続するピン番号の選択
const int bt2pin = 4;//ボタン2を接続するピン番号の選択
int bt1old = 0;
int bt1now = 0;
int bt2old = 0;
int bt2now = 0;
void setup(){}
void loop(){}

void button() {
bt1now = digitalRead(bt1pin);
if (bt1now != bt1old) {
bt1old = bt1now;
if (bt1now == HIGH) {
Serial.print(“TOK1U\r");
} else {
Serial.print(“TOK1D\r");
}
}
bt2now = digitalRead(bt2pin);
if (bt2now != bt2old) {
bt1old = bt2now;
if (bt2now == HIGH) {
Serial.print(“TOK2U\r");
} else {
Serial.print(“TOK2D\r");
}
}
delay(50);
}[/cpp]

変更した行をハイライトしてあります。このような感じで20個まで対応できると思うのですが未検証です。18個までは使えるはずです。

次にvoid mascom()についてです。ここでマスコンの操作情報のコマンドを送信しています。mascom(1)とすればマスコンの力行側1段目に対応するコマンド、mascom(56)とすればマスコンのブレーキ側6段目に対応するコマンドを送信します(つまり、ブレーキ側のコマンドを送信するには、設定したい段数+50の数字をmascomの括弧内に入力すればOKです)。
この関数内で、入力された数値の絶対値をとっているので負の数を入力されても対応できますが、ふつうは使わないでしょう。

では最後にvoid mascon()について解説します。が、実はこの関数は未完成です。最初に設定したマスコンのピンの状態を読み取ることまでは書いておきましたがその先はまだ書いていません。これから作っていきましょう。

マスコンの状態を読み取ろう

今回は可変抵抗の位置によってマスコンの位置を設定するのでanalogRead()を使用します。analogRead()では、括弧内に書いたピンに入力されている電圧の値を0~1023の範囲内で教えてくれます。なお、0のときは0V、1023のときは5Vです。
この数値がある範囲内の時に対応する数字をmascom()に入力してあげるような仕組みにすればよいでしょう。

そのためにまず下準備として、Arduino IDEに付属しているファイルの例から「AnalogInOutSerial」を利用して、マスコンを動かしたときの最大値と最小値を測りましょう。シリアルモニタにて数値を確認してみてください。最大値と最小値の差を、使用したい力行段、制動段(非常ブレーキを含む)の数の和で割ってください。例えば、最大値1000,最小値300,力行段数5段,制動段数9段の場合は、(1000-300)/(5+9)=50となります。計算して出た値の半分を最小値に足した後、どんどん計算結果を足していってください。そして、「次で最大値を超えてしまう」というところまでいったら終了です。
終了したら、その値を下からif文で範囲の条件を指定していって、「ある範囲でmascom()にこの数字を入れる」とすれば完成です。

なんかすごいわかりにくいですね。一応先ほどの条件であった場合の例文を一部だけ載せておきます。なお、この例文は最大値の時にP5、最小値の時にB9であるという想定です。

[cpp classname="myclass" collapse="true" firstline="1″ gutter="true" smarttabs="true" tabsize="3″ toolbar="true" title="GIPI1.ino"]
void mascon(){
mas=analogRead(maspin);
if(mas<325){
mascom(59);
}else if(mas<375){
mascom(58);
}

//(省略)

}else if(mas<925){
mascom(4);
}else if(mas<975){
mascom(5);
}
}
[/cpp]

こんな感じです。あまりに長くなると見づらいので省略しました。

とりあえずこれでプログラムは完成したはずです。Arduino IDEからArduinoにこれを書き込んでみましょう。そうすれば動作するはずです。
閾値は必ず変更してください。忘れると正常に動作しません。

あ、結線図載せてませんでしたね。

結線図

とりあえず例文通りの結線としています。必要に応じて変更してください。

茶色の丸のある部品がボタンです。3番ピンとつながっています。その右にあるのが可変抵抗です。左側の可変抵抗がマスコン用、右側がレバーサー用です。可変抵抗は最大値が10kΩのものを選ぶと良いでしょう。あ、左のほうにある青い基板がArduino NANOです。

可変抵抗は、両端の端子をそれぞれ5V端子とGND端子に接続してください。抵抗にプラスマイナスはないのでどっちがどっちかは特に気にしなくて大丈夫です。真ん中の端子はアナログピン(A1~A7)ピンに接続します。なお、求めていた数値の動きと逆の数値の動きをした場合は、5V端子とGND端子に接続する端子を逆にすればOKです。

 

…以上になります。なんかわかりにくかったですよね…質問には極力答えていくので、この記事へのコメントもしくはTwitterのリプライ機能などでご質問ください。

 

ではでは~

2017年5月28日21時15分追記:この記事内で紹介したコードはご自由にお持ち帰りください。inoファイルに組み込んでネット上で公開する場合は許可なしで行っていただいて構いませんが、公開前に公開先をコメントにてお知らせください。リンクを張らせていただきます。

記事一覧

BVE,コンピュータ,鉄道部品

Posted by Tetsu Otter