私の入門記録であって、入門者向け解説サイトではありません。

マルチ化

  • 投稿日:
  • Category:

リレーとサーボモーターの動作確認が出来たところで、実際のポイント制御に応用するべく、これらを何セットか動かす環境へと持って行く。今回は手持ちパーツの都合上、2セットを動かす回路及びプログラムを組んでみた。

[ スケッチ:Esp32_AsyncWebServer_Relay ]

#include <WiFi.h>
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
#include <ArduinoJson.h>
#include <ESP32Servo.h> // for Servo
#include <ServoEasing.hpp>
const char ssid[] = "********";
const char pass[] = "********";
const IPAddress ip(192,168,3,17);
const IPAddress gateway(192,168,1,1);  // デフォルトゲートウェイ
const IPAddress subnet(255,255,255,0);
const int rel_pins[] = {0,32,33};  // リレー用Pin
const int srv_pins[] = {1,18,19};  // サーボ用Pin
const int SIZEOF_REL_PINS = sizeof(rel_pins)/sizeof(rel_pins[0]);
const int srv_min = 1000;
const int srv_max = 2000;
const int srv_hrz = 50;
const int srv_deg = 180;
const int srv_sec = 2000;
AsyncWebServer server(80);            // ポート設定
// Jsonオブジェクトの初期化
StaticJsonDocument<512> doc;
// ブラウザから受信する変数
uint8_t pnt_number;  // ポイント配置番号
uint8_t pnt_status;  // ポイント状態制御
// for Servo
ServoEasing servo1;  // create servo object to control a servo
ServoEasing servo2;  // create servo object to control a servo
void setup()
{
  Serial.begin(115200);
  for(int i =1; i <= SIZEOF_REL_PINS; i++) {
    pinMode(rel_pins[i], OUTPUT); // pinを出力設定に
    pinMode(srv_pins[i], OUTPUT); // pinを出力設定に
  }
  // SPIFFSのセットアップ
  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
  WiFi.config(ip, gateway, subnet);
  WiFi.begin (ssid, pass);
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 1000 );
    Serial.print ( "." );
  }
  // 各種情報を表示
  Serial.print("SSID: ");
  Serial.println(ssid);
  Serial.print("AP IP address: ");
  Serial.println(ip);
  // GETリクエストに対するハンドラーを登録
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");  // rootにアクセスされた時のレスポンス
  });
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");  // style.cssにアクセスされた時のレスポンス
  });
  // Pointの制御変数の変更リクエスト
  server.on(
    "/post_test",
    HTTP_POST,
    [](AsyncWebServerRequest * request){},
    NULL,
    [](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
      String resjson = "";
      for (size_t i = 0; i < len; i++) {
        //Serial.write(data[i]);
        resjson.concat(char(data[i]));
      }
      Serial.println(resjson);
      DeserializationError error = deserializeJson(doc, resjson);
      if(error){
        Serial.println("deserializeJson() faild");
        request->send(400);
      }
      else{
        pnt_number = doc["PNT_NUMBER"];
        pnt_status = doc["PNT_STATUS"];
        request->send(200);
      }
  });
  pnt_number = 0;
  pnt_status = 0;
  // サーバースタート
  server.begin();
  Serial.println("Server start!");
  // for Servo
  servo1.setEasingType(EASE_CUBIC_IN_OUT); // EASE_LINEAR is default
  servo1.setPeriodHertz(srv_hrz); // setPeriodHertz(int hertz)
  servo1.attach(srv_pins[1], srv_min, srv_max); // attach(int pin, int min, int max)
  servo2.setEasingType(EASE_CUBIC_IN_OUT); // EASE_LINEAR is default
  servo2.setPeriodHertz(srv_hrz); // setPeriodHertz(int hertz)
  servo2.attach(srv_pins[2], srv_min, srv_max); // attach(int pin, int min, int max)
} // End setup()
void loop() {
  if(pnt_number > 0) {
    // Relay
    relayGo(pnt_number, pnt_status);
    // Servo
    servoGo(pnt_number, pnt_status);
  }
  pnt_number = 0;
  delay(10);
} // End loop()
void relayGo(int pnt_number, int pnt_status) {
  if(pnt_status == 0){
    digitalWrite(rel_pins[pnt_number], LOW);
  } else {
    digitalWrite(rel_pins[pnt_number], HIGH);
  }
}
void servoGo(int pnt_number, int pnt_status) {
  if(pnt_number == 1) {
    servo1.setEaseToD(pnt_status*srv_deg, srv_sec); // setEaseToD(int aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove)
  }
  if(pnt_number == 2) {
    servo2.setEaseToD(pnt_status*srv_deg, srv_sec); // setEaseToD(int aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove)
  }
  synchronizeAllServosStartAndWaitForAllServosToStop();
}

[ HTML:index.html ]

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" charset="UTF-8">
    <title>ESP32 Web Server</title>
    <link rel="icon" href="data:,">
    <link rel="stylesheet" type="text/css" href="style.css">
  </head>
  <body>
    <h1>ESP32 Web Server</h1>
    <div id="btn_msg" style="visibility:hidden;">送信OK</div>
    <div style="margin: 0 2em; padding: 1em; border: solid 1px #aac; width: 10em;">
    1:<input type="radio" onclick="send_status(parseInt(this.name), 1);" name="1">R<br>
    1:<input type="radio" onclick="send_status(parseInt(this.name), 0);" name="1">L<br>
    2:<input type="radio" onclick="send_status(parseInt(this.name), 1);" name="2">R<br>
    2:<input type="radio" onclick="send_status(parseInt(this.name), 0);" name="2">L</div>
  </body>
  <script>
    // json返り値のテンプレート
    var json_temp = {
      PNT_NUMBER: 1,
      PNT_STATUS: 0
    }
    // 送信ステータス表示
    function btn_msg_display(msg_txt, txt_color){
      var id_btn_msg = document.getElementById("btn_msg")
      id_btn_msg.style.color = txt_color;
      id_btn_msg.textContent = msg_txt;
      id_btn_msg.style.visibility ="visible";
    }
    // タイムスタンプ文字列の作成
    function get_time_stamp() {
      var now_time = new Date();
      var now_date = ('000' + now_time.getFullYear()).slice(-4) + '-' + ('0' + (now_time.getMonth() + 1)).slice(-2) + '-' + ('0' + now_time.getDate()).slice(-2);
      var now_time_val = ('0' + now_time.getHours()).slice(-2) + ':' + ('0' + now_time.getMinutes()).slice(-2) + ':' + ('0' + now_time.getSeconds()).slice(-2);
      var time_stamp = now_date + " " + now_time_val + " ";
      return time_stamp;
    }
    // Pointの状態変更リクエストの送信
    function send_status(pnt, send_status){
      var str_status = "";
      if(send_status == 0){
        str_status = "PNT_LEFT";
      }
      else{
        str_status = "PNT_RIGHT";
      }
      // JSONデータの作成
      var send_json = json_temp;
      send_json.PNT_NUMBER = pnt;
      send_json.PNT_STATUS = send_status;
      send_json = JSON.stringify(send_json);
      console.log(send_json);
      // リクエストを送信
      var xhr = new XMLHttpRequest()
      xhr.open("POST", "/post_test", true)
      xhr.setRequestHeader("Content-Type", "application/json")
      xhr.timeout = 5000; // タイムアウト設定(ms)
      xhr.onload = () => {
        btn_msg_display(get_time_stamp() + str_status + " 送信 OK", "#00aa00");
      };
      xhr.onerror = () => {
        btn_msg_display(get_time_stamp() + str_status + " 送信 NG", "#ff0000");
      };
      xhr.ontimeout = () => {
        btn_msg_display(get_time_stamp() + str_status + " 送信 タイムアウト", "#ff0000");
      };
      xhr.send(send_json)
    }
  </script>
</html>

Web画面の方も、マルチ化と共に実使用状態に合わせて少し編集を入れた。今回のサンプルでは、Point1とPoint2を操作するパネルを想定し、それぞれRight/Leftを切り替えるラジオボタンを配している。クリックすると、JSON形式でPoint番号(PNT_NUMBER)と切替方向(PNT_STATUS)が送信されるようにした。とりあえずの動作確認用なので、今のところ見てくれは度外視している。

[ 画面表示:index.html ]

index20240126.png

サーボモーター

  • 投稿日:
  • Category:

今後、ポイントをスローアクションで動かしたいと考えているので、ポイントマシンとしてサーボモーターの使用も検証しておかねばならない。下記 2サイトを参考にスケッチを作成した。2つ目のリンクサイトは、ServoEasingライブラリの使用法が役にたった。このライブラリを使用すると、サーボの回転角のみならず、動かし方(回転速度の設定、最初と最後はゆっくり等)の制御が可能になるのである。

[ スケッチ:Esp32_AsyncWebServer_Relay ]

#include <WiFi.h>
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
#include <ArduinoJson.h>
#include <ESP32Servo.h> // for Servo
#include <ServoEasing.hpp>
const char ssid[] = "********";
const char pass[] = "********";
const IPAddress ip(192,168,3,17);
const IPAddress gateway(192,168,1,1);  // デフォルトゲートウェイ
const IPAddress subnet(255,255,255,0);
AsyncWebServer server(80);            // ポート設定
// Jsonオブジェクトの初期化
StaticJsonDocument<512> doc;
#define LED_PIN 33
#define REL_PIN 32
uint8_t led_status;  //LEDの状態制御用変数
void setup()
{
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT); // GPIO25を出力設定に
  pinMode(REL_PIN, OUTPUT); // GPIO25を出力設定に
  // SPIFFSのセットアップ
  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
  WiFi.config(ip, gateway, subnet);
  WiFi.begin (ssid, pass);
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 1000 );
    Serial.print ( "." );
  }
  // 各種情報を表示
  Serial.print("SSID: ");
  Serial.println(ssid);
  Serial.print("AP IP address: ");
  Serial.println(ip);
  // GETリクエストに対するハンドラーを登録
  // rootにアクセスされた時のレスポンス
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");
  });
  // style.cssにアクセスされた時のレスポンス
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");
  });
  // LED の制御変数の変更リクエスト
  server.on(
    "/post_test",
    HTTP_POST,
    [](AsyncWebServerRequest * request){},
    NULL,
    [](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
      String resjson = "";
      for (size_t i = 0; i < len; i++) {
        //Serial.write(data[i]);
        resjson.concat(char(data[i]));
      }
      Serial.println(resjson);
      DeserializationError error = deserializeJson(doc, resjson);
      if(error){
        Serial.println("deserializeJson() faild");
        request->send(400);
      }
      else{
        led_status = doc["LED_STATUS"];       //zz Tilt Motor Default SPEED
        request->send(200);
      }
  });
  led_status = 0;
  // サーバースタート
  server.begin();
  Serial.println("Server start!");
  // for Servo
  // Allow allocation of all timers
  myservo.setPeriodHertz(50);    // standard 50 hz servo
  myservo.attach(servoPin, 1000, 2000); // attaches the servo on pin 18 to the servo object
  synchronizeAllServosStartAndWaitForAllServosToStop();
}
void loop() {
//if (led_status != led_status_tmp) {
  // LED状態変更
  if(led_status == 0){
    digitalWrite(LED_PIN, LOW);
    digitalWrite(REL_PIN, LOW);
    myservo.easeToD(0, 2000);
  }
  else{
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(REL_PIN, HIGH);
    myservo.easeToD(180, 2000);
  }
}

リレー動作

  • 投稿日:
  • Category:

ここからはより実用的な使用法を想定する中で、まずはリレー動作を行なってみる。例としては、ポイントと連動したレイアウトの閉塞区間への電源供給オンオフ、等の用途である。実験用として用意したのは秋月電子の「ドライバー内蔵リレーモジュールキット」。キットなので簡単な半田付けが必要だが、基板上にドライバー回路も含まれているので、その分実装は楽になる。組み立て方、及び使い方は以下を参考にした。

電子工作の部屋 - ドライバ内蔵リレーモジュールキット (K-13573)

スケッチは前記事の物にリレー部分(Arduino時のコード)を追加したが、信号用のPIN番号が異なるのでそこは変えている。

[ スケッチ:Esp32_AsyncWebServer_Relay ]

#include <WiFi.h>
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
#include <ArduinoJson.h>
const char ssid[] = "********";
const char pass[] = "********";
const IPAddress ip(192,168,3,17);
const IPAddress gateway(192,168,1,1);  // デフォルトゲートウェイ
const IPAddress subnet(255,255,255,0);
AsyncWebServer server(80);            // ポート設定
// Jsonオブジェクトの初期化
StaticJsonDocument<512> doc;
#define LED_PIN 33
#define REL_PIN 32  //リレー用
uint8_t led_status;  //LEDの状態制御用変数
void setup()
{
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT); // GPIO33を出力設定に
  pinMode(REL_PIN, OUTPUT); // GPIO32を出力設定に
  // SPIFFSのセットアップ
  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
  WiFi.config(ip, gateway, subnet);
  WiFi.begin (ssid, pass);
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 1000 );
    Serial.print ( "." );
  }
  // 各種情報を表示
  Serial.print("SSID: ");
  Serial.println(ssid);
  Serial.print("AP IP address: ");
  Serial.println(ip);
  // GETリクエストに対するハンドラーを登録
  // rootにアクセスされた時のレスポンス
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");
  });
  // style.cssにアクセスされた時のレスポンス
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");
  });
  // LED の制御変数の変更リクエスト
  server.on(
    "/post_test",
    HTTP_POST,
    [](AsyncWebServerRequest * request){},
    NULL,
    [](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
      String resjson = "";
      for (size_t i = 0; i < len; i++) {
        //Serial.write(data[i]);
        resjson.concat(char(data[i]));
      }
      Serial.println(resjson);
      DeserializationError error = deserializeJson(doc, resjson);
      if(error){
        Serial.println("deserializeJson() faild");
        request->send(400);
      }
      else{
        led_status = doc["LED_STATUS"];       //zz Tilt Motor Default SPEED
        request->send(200);
      }
  });
  led_status = 0;
  // サーバースタート
  server.begin();
  Serial.println("Server start!");
}
void loop() {
  // LED&リレー状態変更
  if(led_status == 0){
    digitalWrite(LED_PIN, LOW);
    digitalWrite(REL_PIN, LOW);
  }
  else{
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(REL_PIN, HIGH);
  }
}

PECOポイント改造

  • 投稿日:
  • Category:

では、ユニフログタイプのPECOポイントを、エレクトロフログ(選択式)に改造する手法を検討してみよう。

Img_6303_2.png

まず一番単純な案としては、既存の配線を一旦取り外し、その後にポイント部、リード部、クロッシング部、及びその先の分岐線路内側のレールを全て直列にジャンパで繋いでしまい、電気的に一体化する事である(左図の黄色部分)。そうすれば、ポイント部のトングレールが左右どちらのレールに接触しているかによって、開通方向のみに+-の電気が流れるようになる。

これだとポイントだけで完結するので非常にシンプルなのだが、その為には多くの配線を繋ぎ直し、新規の配線追加も必要になる為、ちょっとハードルが高い。それと、トングレールの接触により極性を切り替えるので、給電が不安定になる可能性も高そうである。


PECOポイント

  • 投稿日:
  • Category:

ESP32から少しそれるが、前に西郊日誌にも記事を書いたように次のレイアウト用に PECOのポイントを集めており、その制御に使おうとしているのでここで少し触れておく。

これまで、線路が実感的に見えるcode55のタイプを使って来たが、ポイントはどこも在庫切れで、見つけ次第無条件にポチっとする、という作業の繰り返し。その結果、E(エレクトロフログ)と U(ユニフログ)が混在するという事態になってしまった。Eタイプはトラディショナルな選択式なのでそのまま使えば良いのだが、ここで問題はUタイプである。事は少々複雑なので、以下、写真を交えて少し整理してみよう。

Img_6299.jpg

左写真は、手元にある PECO製ポイントの一つ SL-U395F であり、型番に入っているUがユニフログを示す。その特徴は、配線の変更により色々なタイプの使い方が出来る事で、写真でもクロッシング(フログ)部裏面に接続された裸(すずメッキ?)線が見えている。配線はこの一本に留まらず、裏面を見てみると下写真のようになっていた。

Img_6300.jpg