目次
DSshieldAUTO
DSshieldAUTO(AUTOスケッチ)は、DSshieldとArduinoUNOだけで、パソコンなしに小規模な自動運転を実現するためのソフトウェアソリューションです。
小さいジオラマの車両やギミックを小さなArduinoUNO+DSshieldによるコマンドステーションで、DCCの自動制御ができます。手を離しながら模型やジオラマをお楽しみいただける仕組みを提供しています。ボリュームの入力でスピードを変えたり、ボタンで動き方を変えたり、いろいろな演出が出来ます。
AUTOスケッチでできること
- パソコンなしで、DCC車両やポイントを気軽に動かせます。(最初のプログラミングするときだけ、パソコンを使用します)。ボタンやセンサーに応じて、車両やポイントを動かすことも出来ます。ArduinUNOにACアダプタをつなげるだけ。
- DSシールドだけで完結。小規模な用途にも、数万円の高価なコマンドステーションは不要。
- メルクリン(Marklin Motorola, MM1, MM2)やDCCの車両・ポイントに対応
- 教育向け、研修向け、自分の技術向上、ジオラマのギミックのパワーアップなどに使用できます。
AUTOスケッチでできないこと
- 複雑な制御。並列で何個も複雑な条件で動く制御パターン。Arduinoで並列処理を書くのが難しいため。
- 大電流を消費する環境。
- 大きなレイアウト。
必要な物
- DCCポイントやDCC車両、レール、ジオラマなど皆様がお持ちのDCCデコーダ搭載物
- Arduino UNOもしくは互換機
- 必要に応じて、ボタンやS88デコーダ(在線検出器)、LEDなど。
- パソコン
- Arduino IDE(無料)
配線の仕方
スケッチの書き込み方法
- Arduino IDEをインストールする。Arduino UNO(互換機)のUSBドライバもインストールする。
- ボードをArduino UNOにして、ポートをArduino UNOとして認識したポートを指定する。空白はNG。
- Uploadボタン(→マーク)を押して書き込む
スケッチの作り方
はじめに
全体の構成
Arduinoのスケッチは、setup()とloop()で大きく構成されます。一方で、AUTOスケッチでは、user_init()とuser_program()の2つで構成されます。
setup()とloop()は、DCC制御で使用しておりますので、user_の関数にプログラミングしていただきます。なお、user_initはsetupからコールされています。user_programはloopからコールされています。ほとんど同じように使用することが出来ます。
使用できるピン
ピン・端子を使用するときは、pinModeをuser_init()の中で必ずピンの設定します。
//D8ピンを出力にする。 pinMode(8, OUTPUT); //D7ピンを入力にする。 pinMode(7, INPUT); //A2ピンを入力にする。 pinMode(A2, INPUT);
実際に使う場合には、以下のように使用します。
//D8ピンをHIGH(1)で出力する。ピンには5Vが出力される。0VにしたいときはLOW(0)を指定する。 digitalWrite(8, HIGH); //D7ピンからピン状態を読み取る。D7が5Vのときは1がaSensorに入る。0Vのときは0が入る。 int aSensor = digitalRead(7); //A2ピンからアナログの状態を読み取る。A2ピンが5Vのとき1024が入る。2.5Vのとき512、0Vのときは0になる。 int aAnalog = analogRead(A2);
DSシールドで解放されているピンは以下の通りです。この他は、S88や電圧チェック機能で使用されています。 なお、いかに記載はありませんが、5V、3.3V、GNDピンは自由に使用できます。
| デジタル入出力ピン | D7,D8,D11,D12 | 0V,5Vの信号読み取りや、出力を行えます。ボタンの入力、LEDの出力に使用できます。 |
| アナログおよび デジタル入出力ピン | A2,A3 | デジタルに加え、光センサや可変抵抗器などのアナログセンサを使えます。 |
| I2C通信または アナログおよび デジタル入出力ピン | A4,A5 | 上記に加え、I2C通信が使用できます。wire.hをインクルードする必要があります。 |
主な命令
AUTOスケッチでは、Arduinoの命令は全部使えますが、たぶん混乱するだけなので、以下の命令を中心に、まずは使ってみてください。
車両アドレス: 「ADDR_DCC+DCCアドレス」で指定してください。DCCアドレス8102であれば、ADDR_DCC+8102です。MM2アドレス5なら、ADDR_MM2+5です。
アクセサリアドレス:「ADDR_ACC_DCC+DCCアクセサリアドレス」で指定してください。DCCポイント32であれば、ADDR_ACC_DCC+32です。MM2のポイント5であれば、ADDR_ACC_MM2+5です。
| 命令 | 解説 | 使用例 |
|---|---|---|
| CMD_END() | 永久ループで止めます。この命令の後は一切制御できなくなります。 | CMD_END(); |
| CMD_ScanS88() | S88からデータを収集する処理。 | CMD_ScanS88(); |
| CMD_GetS88(S88アドレス) | 収集したS88データから希望のS88アドレス(1-16)の状態(0 or 1)を得る。 | aSensor = CMD_GetS88(2); |
| CMD_Wait(待ち時間ミリ秒) | ミリ秒単位(1000で1秒)で待ちます。 | CMD_Wait(1000); |
| CMD_Power(オンオフ命令) | 0で線路電源オフ、1で線路電源オン。 | CMD_Power(1); |
| CMD_LocSpeed(車両アドレス, 速度) | 速度は、0-1023で与えてください。 | CMD_LocSpeed(ADDR_DCC+3,500); |
| CMD_LocDirection(車両アドレス, 進行方向); | 進行方向は0:FWD, 1:REVです。 | CMD_LocDirection(ADDR_DCC+3,1); |
| CMD_LocFunction(車両アドレス, ファンクション番号, ファンクション状態); | ファンクション番号(0-28)を操作します。 | CMD_LocFunction(ADDR_DCC+3,1,1); |
| CMD_LocFunctionMomentary(車両アドレス, ファンクション番号); | ファンクション番号(0-28)を操作します。0→1→0のMomentary動作を自動で行います。 | CMD_LocFunctionMomentary(ADDR_DCC+3,1); |
| CMD_Turnout(アクセサリアドレス, 分岐状態) | ポイントや信号機を操作します。0:分岐, 1:直進です。 | CMD_Turnout(ADDR_ACC_DCC+32,1); |
| Serial.print, Serial,println | シリアル通信でPC等にメッセージを送れます。 | Serial.println(“車両1検出”); |
C言語の基本
C言語の細かい記述方法は、本屋さんに売っているC言語入門の本を斜め読みしながら覚えてください。いきなり全てを覚える必要はありません。サンプルをいじりながら、修正してみて手探りしながら体で覚えていただく形が良いと思います。
まず、変数を覚えましょう。
//0-255の値を使うとき、aHako を作る byte aHako; //-32768~32767を使うとき、intを使う int aHako;
staticを付けると、変数は消されずに値が保持されます。
static byte sHako;
なお、C言語のルールでは無く、独自のルールですが、変数の名前は先頭に種類を示す小文字を付けると見やすくなります。関数内で宣言して使う変数には“a”(autoのa)、1つのソースファイル全体で使う変数には“g”(globalのg)、関数内でstatic宣言して使う変数には“s”(staticのs)とします。
プログラムは基本3要素で成り立っています。シーケンス(順序実行)、分岐、繰り返しです。
//シーケンスの例
Serial.println("Power On");
CMD_Power(1);
CMD_LocFunction(ADDR_DCC + 3, 0, 1);
CMD_LocFunction(ADDR_DCC + 3, 1, 1);
CMD_Wait(500);
Serial.println("Loc 3, Func 10, On");
CMD_LocFunction(ADDR_DCC + 3, 10, 1);
CMD_Wait(500);
分岐は以下の通りです。
//分岐の例。A,Bルートが交互に実行される
static byte sFlag0 = 10;
if( sFlag0 == 10)
{
//Aルート
sFlag0 = 0;
Serial.println("Loc 3, Func 10, On");
CMD_LocFunction(ADDR_DCC + 3, 10, 1);
CMD_Wait(500);
CMD_LocFunction(ADDR_DCC + 3, 10, 0);
}
else
{
//Bルート
sFlag0 = 10;
Serial.println("Loc 3, Func 10, On");
CMD_LocFunction(ADDR_DCC + 3, 11, 1);
CMD_Wait(500);
CMD_LocFunction(ADDR_DCC + 3, 11, 0);
}
繰り返しは、似たような命令を、シンプルに表現する方法です。繰り返しは、シーケンスと分岐を組み合わせるとできますが、C言語では専用命令forやwhileがありますので、これを使うと楽が出来て便利です。
スピードを徐々にあげるような命令は、繰り返し命令を使うと短く記載できて楽です。
//繰り返しの例。車両のスピードを変える
for( int i = 0; i < 11; i++)
{
CMD_LocSpeed(ADDR_DCC + 3, i * 10);
CMD_Wait(200);
}
スケッチの例
アドレス3車両のファンクション操作
車両のファンクションを動かすサンプルです。DSシールドをPLCのようにする その2(挫折&方針変更)で詳細を紹介しています。
/*****************************************************************/
/* User Program Area */
/*****************************************************************/
void user_init(void)
{
}
void user_program(void)
{
Serial.println("Power On");
CMD_Power(1);
CMD_LocFunction(ADDR_DCC + 3, 0, 1);
CMD_LocFunction(ADDR_DCC + 3, 1, 1);
CMD_Wait(500);
Serial.println("Loc 3, Func 10, On");
CMD_LocFunction(ADDR_DCC + 3, 10, 1);
CMD_Wait(500);
Serial.println("Loc 3, Func 11, On");
CMD_LocFunction(ADDR_DCC + 3, 11, 1);
CMD_Wait(500);
Serial.println("Loc 3, Func 9, On");
CMD_LocFunction(ADDR_DCC + 3, 9, 1);
CMD_Wait(500);
Serial.println("End...");
/* 永久ループ(終了) */
while(1){}
}
タクトスイッチによるルート切り替え
詳細は、管理者ブログの記事(DSシールドをPLCのようにする その3)を参照ください。
/*****************************************************************/
/* User Program Area */
/*****************************************************************/
void user_init(void)
{
pinMode(8, INPUT);
pinMode(11, INPUT);
pinMode(12, INPUT);
digitalWrite(8, HIGH);//内部プルアップON
digitalWrite(11, HIGH);//内部プルアップON
digitalWrite(12, HIGH);//内部プルアップON
CMD_Power(1);
Serial.println("Power On");
}
void user_program(void)
{
//ボタンが押されたとき、digitalReadは0を返す。押さないときは1を返す。
if( digitalRead(8) == 0)
{
Serial.println("Route 1");
CMD_Turnout(ADDR_ACC_DCC + 2, 0);
CMD_Wait(1000);
}
if( digitalRead(11) == 0)
{
Serial.println("Route 2");
CMD_Turnout(ADDR_ACC_DCC + 1, 1);
CMD_Wait(1000);
CMD_Turnout(ADDR_ACC_DCC + 2, 1);
}
if( digitalRead(12) == 0)
{
Serial.println("Route 3");
CMD_Turnout(ADDR_ACC_DCC + 1, 0);
CMD_Wait(1000);
CMD_Turnout(ADDR_ACC_DCC + 2, 1);
}
}
Youtube動画:
メルクリン車両の単純往復シャトル運転
メルクリン車両(Marklin Digital, Marklin Motorola 2)の往復シャトル運転です。 ファンクション操作や、ボタンでスタートをかけるなどの動きを実現しています。
詳細は、管理者ブログの記事(DSシールドをPLCのようにする その5)を参照ください。
/*****************************************************************/
/* User Program Area */
/*****************************************************************/
void user_init(void)
{
pinMode(8, INPUT);
pinMode(11, INPUT);
pinMode(12, INPUT);
digitalWrite(8, HIGH);//内部プルアップON
digitalWrite(11, HIGH);//内部プルアップON
digitalWrite(12, HIGH);//内部プルアップON
//F0 Light ON
CMD_LocFunction(ADDR_MM2 + 2, 0, 1);
//SPEED 0
CMD_LocSpeed(ADDR_MM2 + 2, 0);
//POWER ON
CMD_Power(1);
Serial.println("Power On");
}
void user_program(void)
{
static byte sButtonState = 1;
//ボタンが押されたとき、digitalReadは0を返す。押さないときは1を返す。
if( digitalRead(8) == 0)
{
Serial.println("Button 1");
sButtonState = 1;
CMD_Wait(100);
/* 前進 */
//FWD
CMD_LocDirection(ADDR_MM2 + 2, 0);
//警笛
CMD_LocFunction(ADDR_MM2 + 2, 2, 1);
CMD_Wait(2000);
CMD_LocFunction(ADDR_MM2 + 2, 2, 0);
//急加速
CMD_LocSpeed(ADDR_MM2 + 2, 100);
}
if( digitalRead(11) == 0)
{
Serial.println("Button 2");
sButtonState = 2;
CMD_Wait(100);
//急停車
CMD_LocSpeed(ADDR_MM2 + 2, 100);
}
if( digitalRead(12) == 0)
{
Serial.println("Button 3");
sButtonState = 3;
CMD_Wait(100);
}
/* S88のスキャン */
CMD_ScanS88();
if( CMD_GetS88(0) == 1)
{
Serial.println("S88 Detected");
CMD_LocSpeed(ADDR_MM2 + 2, 100);
CMD_Wait(500);
CMD_LocSpeed(ADDR_MM2 + 2, 75);
CMD_Wait(500);
CMD_LocSpeed(ADDR_MM2 + 2, 50);
CMD_Wait(500);
CMD_LocSpeed(ADDR_MM2 + 2, 25);
CMD_Wait(500);
CMD_LocSpeed(ADDR_MM2 + 2, 0);
CMD_Wait(1000);
//REV
CMD_LocDirection(ADDR_MM2 + 2, 1);
Serial.println("REV");
//警笛
CMD_LocFunction(ADDR_MM2 + 2, 2, 1);
CMD_Wait(2000);
CMD_LocFunction(ADDR_MM2 + 2, 2, 0);
/* 加速 */
CMD_LocSpeed(ADDR_MM2 + 2, 25);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 50);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 75);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 100);
CMD_Wait(3000);
CMD_LocFunction(ADDR_MM2 + 2, 3, 1);
CMD_Wait(2000);
CMD_LocFunction(ADDR_MM2 + 2, 3, 0);
/* 減速 */
CMD_LocSpeed(ADDR_MM2 + 2, 75);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 50);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 25);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 0);
CMD_Wait(2000);
//FWD
CMD_LocDirection(ADDR_MM2 + 2, 0);
Serial.println("FWD");
//Clear
CMD_ScanS88();
//警笛
CMD_LocFunction(ADDR_MM2 + 2, 2, 1);
CMD_Wait(100);
CMD_LocFunction(ADDR_MM2 + 2, 2, 0);
/* 加速 */
CMD_LocSpeed(ADDR_MM2 + 2, 25);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 50);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 75);
CMD_Wait(1000);
CMD_LocSpeed(ADDR_MM2 + 2, 100);
}
}
Youtube動画:
