目次

DSshieldAUTO

DSshieldAUTO(AUTOスケッチ)は、DSshieldとArduinoUNOだけで、パソコンなしに小規模な自動運転を実現するためのソフトウェアソリューションです。
小さいジオラマの車両やギミックを小さなArduinoUNO+DSshieldによるコマンドステーションで、DCCの自動制御ができます。手を離しながら模型やジオラマをお楽しみいただける仕組みを提供しています。ボリュームの入力でスピードを変えたり、ボタンで動き方を変えたり、いろいろな演出が出来ます。

AUTOスケッチでできること

AUTOスケッチでできないこと

必要な物

配線の仕方

  1. DSシールドを買って、秋月電子で部品を入手しておき、半田付けします。
  2. Arduino UNOとDSシールドは合体させておきます。

buin2gou.sakura.ne.jp_sblo_files_powerele_image_dsauto_route2-thumbnail2.jpg

スケッチの書き込み方法

  1. Arduino IDEをインストールする。Arduino UNO(互換機)のUSBドライバもインストールする。
  2. ここに掲載されているスケッチをダウンロードし、Arduino IDEで開く。

  3. ボードをArduino UNOにして、ポートをArduino UNOとして認識したポートを指定する。空白はNG。


  1. Uploadボタン(→マーク)を押して書き込む

スケッチの作り方

はじめに

Arduinoの仕組みを使って、DCC鉄道模型の制御を行います。このため、Arduinoの入門書や知識が必要となります。

参考サイト:

全体の構成

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動画: