ミニ四駆用ラップタイマーの作り方

ミニ四駆

概要

基本的に市販品で組み立てが出来るように考案したミニ四駆用ラップタイマーです。
相当加工は必要ですが、一般の人が買えないような部品は極力使用していません。
基本3レーン用でラップタイム測定になります。
使用上は5レーンまで対応となります。
併走のラップタイムは測定できません。
※技術的には可能で、wifiで結果を送信しwebアプリで表示することも将来的に出来るようになるかもしれません。

分からない所等はTwitterにコメントかDMをお願いします。
なるべく修正・追記するようにします。

材料

M5ATOM Lite 1個

追加予定

センサー加工

追加予定

回路図

トランジスタはセンサーへの電源供給用で、使用していない際にセンサーを止めて電源の消耗を押さえます。
コンセントからの供給等で消費電流を気にしない場合は、直接接続してもいいかもしれません。
赤外線発光LEDは時間によって消耗するので注意してください。

自作基板ほしい方はTwitterにコメントかDMで連絡ください。
何か方法を考えます。

下図は回路の一例です。
部品も回路もわかる方は自由に組んでください。

3Dモデルデータ

追加予定

取付台作成

木材の1/4材を用意し、下記の通りにカットします。
1. 436mm以上にカット
2. 中心にΦ20mmの穴を空ける
3. 穴の位置から両側117mmの位置にΦ20mmの穴を空ける
4. 足用に91mmにカットする
(91mmだとスロープの土台としても使用できます)
詳細は下図を参照願います。

プログラム

とりあえず動作確認まで。
表示関係は後日修正します。

// M5Atom Lite
// M5GFX 使用

// debug用
#define debug

#define BRIGHTNESS 5 // LEDの明るさセット

#include <Arduino.h>

#include <esp_now.h>
#include <WiFi.h>

#include <EEPROM.h>

#include <FastLED.h>

// OLED関係
#include <M5UnitOLED.h>
M5UnitOLED display(26, 32, 400000);
M5Canvas canvas(&display);

#define led_quantity 25  // ATOM matrixの時は25にする
CRGB leds[led_quantity]; // LEDの色情報を入れる変数

/*
// stamp u3c
// ボタンピン
#define Btn_pin 9 // 9pin
// LED データピン
#define LED_pin 2 // 2pin
// 各センサーのピン番号
#define sensor_pow 10
#define sensor1 4
#define sensor2 5
#define sensor3 6
#define sensor4 7
#define sensor5 8
*/

// ATOM

// ボタンピン
#define Btn_pin 39 // 9pin

// LED データピン
#define LED_pin 27 // 2pin

// 各センサーのピン番号
#define sensor_pow 19
#define sensor1 21
#define sensor2 22
#define sensor3 23
#define sensor4 25
#define sensor5 33

RTC_DATA_ATTR uint8_t boot_count = 0; // sleepしても消えない

// 割り込み用変数
volatile uint8_t Btn_trig = 1;                      // ボタン押したときの判定用 初期値
volatile uint32_t Btn_time = 0;                     // ボタン押したときの時間(長押し判定用)
volatile uint8_t mode = 0;                          // モード判定
volatile uint32_t start_time = 0;                   // スタートした時間
volatile uint8_t start_senser = 0;                  // スタートしたセンサー No.
volatile uint32_t sensor_time[5] = {0, 0, 0, 0, 0}; // 検出時の時間
volatile uint8_t sensor_trig[5] = {0, 0, 0, 0, 0};  // 検出したかの判定
volatile uint8_t sensor_turn[5] = {0, 0, 0, 0, 0};  // 検出した順番
volatile uint8_t sensor_count = 0;                  // 検出した回数
volatile uint32_t action_time = 0;                  // 最後に動作した時間

// led_set
void led_set(uint8_t R, uint8_t G, uint8_t B)
{
  for (uint8_t i = 0; i < led_quantity; i++)
  {
    leds[i] = CRGB(R, G, B);
    FastLED.show();
  }
}

// Sleep 開始
void sleep_start()
{
  led_set(0, 0, 0); // LED消灯
  // 100000us = 0.1sのタイマー設定
  esp_sleep_enable_timer_wakeup(100000);
  // ディープスリープ
  esp_deep_sleep_start();
  delay(1000); // sleep移行待機
}

// ボタンに変化が有ったときの処理
void Btn_task()
{
  if (Btn_trig == 0)
  {
    // ボタン押し判定
    Btn_trig = 1;
    Btn_time = millis();
  }
}

// センサー反応時の処理
void time_write(uint8_t number)
{
  if (start_time == 0)
  {
    start_time = millis();
    start_senser = number;
    return;
  }

  if (sensor_time[number] == 0 && start_time + 5000 < millis())
  {
    sensor_time[number] = millis();
    sensor_trig[number] = 1;
  }
}

// センサー割り込み処理
void sensor1_isr()
{
  time_write(0);
}
void sensor2_isr()
{
  time_write(1);
}
void sensor3_isr()
{
  time_write(2);
}
void sensor4_isr()
{
  time_write(3);
}
void sensor5_isr()
{
  time_write(4);
}

// スタート処理
void set_start()
{
  led_set(0, 255, 0);

  digitalWrite(sensor_pow, HIGH); // センサー電源 ON
  delay(100);
  attachInterrupt(sensor1, sensor1_isr, CHANGE);
  attachInterrupt(sensor2, sensor2_isr, CHANGE);
  attachInterrupt(sensor3, sensor3_isr, CHANGE);
  attachInterrupt(sensor4, sensor4_isr, CHANGE);
  attachInterrupt(sensor5, sensor5_isr, CHANGE);

  // スタート完了したので青を点灯
  led_set(0, 0, 255);
}

// 終了処理
void set_end()
{
  led_set(0, 255, 0);

  detachInterrupt(sensor1);
  detachInterrupt(sensor2);
  detachInterrupt(sensor3);
  detachInterrupt(sensor4);
  detachInterrupt(sensor5);
  delay(100);
  digitalWrite(sensor_pow, LOW); // センサー電源 OFF

  // 終了完了したので赤を点灯
  led_set(255, 0, 0);
}

// 待機画面
void waiting()
{
  display.fillScreen(TFT_BLACK);
  canvas.setCursor(10, 0);   // 座標を指定
  canvas.print("Lap Timer"); // 表示内容をcanvasに準備
  canvas.pushSprite(0, 0);   // 準備したcanvasを座標を指定して表示する
  Btn_trig = 0;
  mode = 1;
}

// 計測中
void measure()
{
  // 値初期化
  for (int i = 0; i < 5; i++)
  {
    sensor_time[i] = 0; // 検出時の時間
    sensor_trig[i] = 0; // 検出したかの判定
    sensor_turn[i] = 0; // 検出した順番
  }
  sensor_count = 0; // 検出した回数

  display.fillScreen(TFT_BLACK);
  canvas.setCursor(10, 0);     // 座標を指定
  canvas.print(F("Ready...")); // 表示内容をcanvasに準備
  canvas.pushSprite(0, 0);     // 準備したcanvasを座標を指定して表示する
  Btn_trig = 0;

  // 計測ループ
  while (true)
  {
    // ボタンを押したら戻る
    if (Btn_trig != 0)
    {
      mode = 0;
      Btn_trig = 1;
      return;
    }

    // 検出
    for (int i = 0; i < 5; i++)
    {
      // センサーに検出があったら表示する
      if (sensor_trig[i] == 1)
      {
        sensor_count++;
        sensor_turn[i] = sensor_count;
        sensor_trig[i] = 2; // 出力済み
        if (i == start_senser)
        {
          Btn_trig = 1;
          mode = 2;
          return;
        }

        float time = (float)(sensor_time[i] - start_time) / 1000;
        canvas.setCursor(10, 0);
        canvas.print(sensor_count);
        canvas.print(F(":"));
        canvas.print(String(time, 3));
        canvas.pushSprite(0, 0);

        delay(1000);
      }
    }
    // 時間表示判定
    if (start_time != 0)
    {
      float time = (float)(millis() - start_time) / 1000;
      canvas.setCursor(10, 0);
      canvas.print(F("  "));
      canvas.print(String(time, 3));
      canvas.pushSprite(0, 0);
    }
    delay(1); // wdt対策
  }
}

// 結果表示
void result()
{
  // 結果表示
  int16_t count = start_senser;
  Btn_trig = 1;
  // 待機 & 切替
  while (true)
  {

    if (Btn_trig != 0)
    {
      delay(50);
      Btn_trig = 0;
      while (digitalRead(Btn_pin) == LOW)
      {
        if (Btn_time + 500 < millis())
        {
          // リセット表示

          while (digitalRead(Btn_pin) == LOW)
          {
            delay(100);
          } // ボタン離すの待ち
          delay(50);
          Btn_trig = 1; // モード切替用
          mode = 0;
          return;
        }
        delay(10);
      }
      // 表示切り替え
      count++;
      for (int i = 0; i < 5; i++)
      {
        if (count - 1 == i)
        {
          float time = (float)(sensor_time[i] - start_time) / 1000;
          canvas.setCursor(10, 0);
          if (i != start_senser)
          {
            canvas.print(i + 1);
          }
          else
          {
            canvas.print("L");
            count = 0;
          }
          canvas.print(F(":"));
          canvas.print(String(time, 3));
          canvas.pushSprite(0, 0);
          break;
        }
      }
    }
    delay(100); // wdt対策
  }
}

// 初期設定
void setup()
{
  setCpuFrequencyMhz(80); // CPU周波数変更
#ifdef debug
  Serial.begin(115200); // デバック用等
#endif
  FastLED.addLeds<NEOPIXEL, LED_pin>(leds, led_quantity); // LED初期化
  FastLED.setBrightness(BRIGHTNESS);

  // OLED(M5GFX)初期設定
  display.init();          // 液晶表示器初期化
  display.setRotation(1);  // 表示方向(コネクタ位置基準):0下,1右,2上,3左
  canvas.setColorDepth(1); // モノクロ(1ビット、白黒2色)カラーの場合は対応するビット数
  canvas.setFont(&fonts::lgfxJapanMinchoP_32);
  canvas.setTextWrap(false);                              // 改行をしない(画面をはみ出す時自動改行する場合はtrue)
  canvas.setTextSize(1);                                  // 文字サイズ(倍率)
  canvas.createSprite(display.width(), display.height()); // canvasサイズ
  display.fillScreen(TFT_BLACK);

  // 各種ピンモード
  pinMode(sensor_pow, OUTPUT);
  pinMode(Btn_pin, INPUT_PULLUP);
  pinMode(sensor1, INPUT_PULLUP);
  pinMode(sensor2, INPUT_PULLUP);
  pinMode(sensor3, INPUT_PULLUP);
  pinMode(sensor4, INPUT_PULLUP);
  pinMode(sensor5, INPUT_PULLUP);

  // ボタン割り込み開始
  attachInterrupt(Btn_pin, Btn_task, CHANGE);

  // 割り込みOFF
  set_end();
}

void loop()
{
  // ボタンが押されていた時の処理
  if (Btn_trig != 0)
  {
    switch (mode)
    {
    // 起動直後 & リセット後
    case 0:
      waiting();
      break;

    // 計測開始
    case 1:
      set_start();
      measure();
      set_end();
      break;

    // 結果表示
    case 2:
      result();
      break;
    }
    delay(10);
    // Btn_trig = 0;
    action_time = millis(); // スリープ用のタイマリセット
  }

  // 最後に操作してから30秒経過したらスリープ
  if (action_time + 30000 < millis())
  {
    sleep_start();
  }

  delay(1);
}

組立方法

追加予定

タイトルとURLをコピーしました