🚉

鉄道模型をラジコン化してみた

2023/08/23に公開

まえがき

2022年国際鉄道模型コンベンションFM01ブースにて出展していたRFC(Railway Fan Club)のyoshina9aです。山手線ゲーム2ndで新しいアプローチを仕掛けてみたのでその紹介をしていきます。あれから1年経ってしまいましたがw
実験的な部分が多くありますが、悩むよりやってみることを念頭にトライしました。

モチベーション

以前よりRFCではNゲージとDCCシステムとテーマ路線のダイヤグラムと多人数を組み合わせて、1日を24分に縮小してダイヤ運転をデモンストレーションしていました。そこにはハードウェア的な課題がいくつかありました。

  • 線路の安定性・汚れ問題
  • 集電不良による車両の瞬停
  • 電源容量不足
  • 大量のDCC機材
  • 大量の無線スロットル用の9V電池問題

どちらかというと線路や給電に関する問題が多くありました。どうしてもその場で線路レイアウトと電気配線を組み上げるので、安定性を保証することが困難なことがありました。またDCCを連続で使用しているとコマンドステーションが仕事を放棄し始めたりパケロスが始まったりと普通ではない使い方をしている分

そこで、もはや車両に受信機とバッテリーを積んでラジコン化して線路給電に関する問題を忘れてしまおプロジェクトを立ち上げました。

また、以前より体験運転のようなニーズが有ることは、ダイヤ運転を見に来てくださった方々の声からは把握していて、私としてもそういったお楽しみを提供したい部分がありました。
私達の体験運転の場合、椅子に座って体験運転をしてもらうというよりは、運転している車両と一緒に歩いて移動することがおおく、無線スロットルであることは必至でした。しかし初めての人にUT4DJを貸し出すにはまだ余計なボタンが多く、またこのコロナ禍において人の触ったものを使い回すような運用は避けたいと考えていました。
そこで(2回目)スロットルもスマホからできるようにしてWebページでサクッとアクセスできるようにしちゃえば、お手軽に体験運転を提供できるかもな〜というふうに考えました。

改めて、車両に受信機とバッテリーを積んでラジコン化してスマホで車両を動かしちゃおう〜〜プロジェクトにしました。

やりたいこと

ここでやりたいことを整理します。

  • 車両にバッテリーを積む
  • 車両に受信機を積む
  • スマホで車両を動かす

そのついでにやりたいことを列挙します。

  • 車両の個体検知したい
  • 発車ベルスイッチや車掌(ドア)スイッチを操作して運転士感を高めたい
  • スマホの運転台ページもそれっぽくしたい

今回は鉄道模型をラジコン化したお話に触れていきますので、それ以外のお話はまた別のタイミングでしたいと思います。

作ったもの

基板編

Ardiunoとモータードライバをセットにして自作した基板に乗せてオリジナルのデコーダー?受信機?を作成しました。基板の作成は詳しい人にやっていただいて、回路の部分は素人なのでえいやで作りました。電流とかも知らん基板よ小さくなあれ〜〜✨✨✨✨
(いまとなってはESP32-C3とか使ったほうが良さそうですが😇)


回路図


できあがり

とにかく小さくしたかったのと手軽に入るものにしたかったので、通信のモジュールには安価に手に入るESP8266(WROOM-02)を使いました。念の為12V昇圧モジュールを入れていますが、それがなければ1枚1000円しないぐらいで作れると思います。

一応線路から電源取りたいときのためのコネクタを用意しています。。また、つなぎ方によってはそのままポイントデコーダーに応用できます。

プログラム編

Ardiuno

Ardiunoとのやり取りにはMQTTを使いました。適当なJSONでモーターへの指示を送ります。Ardiuno側ではJSONを解釈してモーターへの指示をします。ここではTB67H450の使い方は説明しませんが、PWM制御してあげればよきです。

サンプルプログラムをおいておきます。
各自で設定が必要な項目は、

  • MQTTサーバー
  • Wi-Fiの接続先
  • Subscribeするtopic名(サンプルは"Arduino/"+esp_id)
    です。セキュリティ的な処理は一切入れていないです。
main.ino
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <string.h>

#define pinFin 12
#define pinRin 14

#define SERIAL_SPEED 74880

// PWM
#define PWM_FREQ 1000 // PWM frequency: 1000Hz(1kHz)
#define PWM_RANGE 100 // PWM range: 0-100
#define SPEED 100

const char* ssid = "***";         // ESP-WROOM-02用 Wifi SSID(各自設定してください)
const char* password = "******";  // ESP-WROOM-02用 Wifi パスワード(各自設定してください)

WiFiClient wifiClient;
PubSubClient client(wifiClient);

char* server = "*********";

void callback(char* topic, byte* payload, unsigned int length) {
  char message[200];
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    message[i] = (char)payload[i];
  }
  Serial.println();
  Serial.println(message);

  // JSON parse
  StaticJsonDocument<200> jsonBuffer;
  auto error = deserializeJson(jsonBuffer, message);
  if (error) {
    Serial.print(F("deserializeJson() failed with code "));
    Serial.println(error.c_str());
  } else {
    const char* direction = jsonBuffer["direction"];
    const int speed = jsonBuffer["speed"];

    driveMotor(speed, direction);
  }
}

// TB67H450への指示出し
void driveMotor(int speed, const char* direction) {
  if (strcmp(direction, "forward") == 0) {
    analogWrite(pinFin, speed);
    analogWrite(pinRin, 0);
  } else if (strcmp(direction, "reverse") == 0) {
    analogWrite(pinFin, 0);
    analogWrite(pinRin, speed);
  } else {
    analogWrite(pinFin, 0);
    analogWrite(pinRin, 0);
  }
}

void setup() {
  Serial.begin(SERIAL_SPEED);
  Serial.println();

  // Wi-Fiにつなぐ
  Serial.print("Connecting to ");
  Serial.print(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect(true);
  WiFi.begin(ssid, password);

  analogWriteRange(PWM_RANGE);
  analogWriteFreq(PWM_FREQ);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");

  // トピック名
  esp_id = "ESP-" + String(ESP.getChipId(), HEX);
  subtopic =  "Arduino/"  + esp_id;
  
  client.setServer(server, 1883);
  client.setCallback(callback);
}

void loop() {
  if (!!!client.connected()) {
    Serial.print("Reconnecting client to ");
    Serial.println(server);
    while (!client.connect(esp_id.c_str())) {
      Serial.print(".");
      delay(500);
    }
    if (client.subscribe(subtopic.c_str())) {
      Serial.println("Subscribe ok");
    } else {
      Serial.println("Subscribe failed");
    }
    Serial.println();
  }

  client.loop();
  delay(10);
}

MQTTサーバーとWebサーバー

ローカルで適当に立てちゃいましょう

完成

山手線ゲーム2ndの様子

一連の操作を動画にしました。ちなみに先程のデコーダー以外にも発車ベルSWなども同様にArduinoを使用してSWの状態を取得しています。

https://youtu.be/f-uIS4iyYoI

あとがき

下書きを失くしたと思っていたところ、zennの方に眠っていたのを発掘しました。
今後もこちらを活用した展示や体験運転もしていきますので、よろしくお願いいたします。

こうやったらいいやんとかありましたらお声をいただけると幸いです〜〜

Discussion