はじめに
極力市販品を流用し、なるべく簡単に製作出来るように設計しました。
オリジナルの部品は3Dプリンタの造形物1点のみとなります。
配線はコネクタの接続のみとなり、はんだ作業はありません。
プログラムと3Dのモデルデータは公開してありますので、モデルのプリントが出来れば製作出来ると思います。
3Dモデルは下記のリンク先にあります。
使用部品
使用部品は下記の通りです。
使用部品(市販品)
・M5StickC または M5StickC Plus 1個
・180°赤外線反射ユニット(ITR9606) 1個
使用部品(ミニ四駆パーツ)
・HG 軽量19mmオールアルミベアリングローラー 1個
・2mm丸ビス 15mm 3本
・2mmナット 3個
・シャフト用プラベアリング 2個
・ミニ四駆AOパーツ AO-1018 ベアリングローラー用スペーサー 2個
・ミニ四駆用パーツ 2mmワッシャー小 1個
・ミニ四駆用パーツ 2mmワッシャー大 1個
・2mm スプリングワッシャー [ミニ4駆 AOパーツシリーズ] 1個
組み立て方
3Dプリンタでプリントした部品を [180°赤外線反射ユニット] に取り付けます。
シャフト用プラベアリングをネジに通し、2カ所の穴に通します。
取り付けると下記の通りとなります。
次に19mmベアリングを取り付けます。
下記の順番でねじに通してください。
・2mm 丸ビス 15mm
・スプリングワッシャー
・ワッシャー小
・ベアリングスペーサ
・HG 軽量19mmオールアルミベアリングローラー
・ベアリングスペーサ
・ワッシャー大
・2mmナット
取り付けると下記の通りとなります。
これでハードウェアは完成です。
プログラムの書き込み
arduino IDEを使用してプログラムの書き込みを行います。
下記のプログラムをコピーして貼り付けて書き込んでください。
今回はライブラリはM5StickCだけで他は必要ありません。
M5StickCの書き込みは色々な方が解説してくれていますので、探してみてください。
※ 2022/05/10 プログラム変更(変更理由は末尾参照)
// 使用するマイコン選択<ここから>
#define M5StickC_def // M5stickC
//#define M5StickC_Plus_def // M5stickC Plus
// 使用するマイコン選択<ここまで>
// FFT解析モード (使用する場合はコメントアウトを外す)
// ※ 高速時対策用
// #define FFT_mode_def
// ユーザー変更箇所 <ここまで>
// インクルードファイル
#include <Arduino.h>
#include "driver/pcnt.h" // パルスカウンタ用
#define speed_rate 9.943 // 1パルスあたりの距離
#if defined(M5StickC_def)
#include <M5StickC.h> // M5stickC
#define font_size 2 // フォントサイズ
#define line1_x 10
#define line1_y 2
#define line2_x 10
#define line2_y 20
#define line3_x 10
#define line3_y 40
#define line4_x 10
#define line4_y 60
#elif defined(M5StickC_Plus_def)
#include <M5StickCPlus.h> // M5stickC Plus
#define font_size 2 // フォントサイズ
#define line1_x 10
#define line1_y 2
#define line2_x 10
#define line2_y 20
#define line3_x 10
#define line3_y 40
#define line4_x 10
#define line4_y 60
#endif
// 各種定数
#define pulse_pin 33 // パルス入力用のピン
// FFT解析モード (インクルードファイル)
#ifdef FFT_mode_def
// インクルードファイル
#include "arduinoFFT.h"
arduinoFFT FFT = arduinoFFT(); // FFT用
// 定数定義
#define SAMPLING_FREQUENCY 10000 // サンプリング周期(Hz) Max50000Hz(ADC)
#define SAMPLES 2048 // 計算で使用するサンプル数の計算
double vReal[SAMPLES]; // FFT計算用の配列
double vImag[SAMPLES]; // FFT計算用の配列
// グローバル変数定義
float average_value = 0;
float max_value = 0;
float min_value = 0;
// サンプリング
float sampling()
{
unsigned long sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY)); // サンプリング周期(時間)
unsigned long start_time = micros(); // スタート時間保存
for (int i = 0; i < SAMPLES; i++)
{
while (micros() - start_time < sampling_period_us * i) // オーバーフロー・サンプリング周期ずれ 対策済み
;
vReal[i] = analogRead(pulse_pin); // データ読取
}
unsigned long end_time = micros(); // 終了時間保存
// 各種算出
float sample_sum = 0;
max_value = 0;
min_value = 1000000;
for (int i = 0; i < SAMPLES; i++)
{
vImag[i] = 0.0;
sample_sum += vReal[i];
if (max_value < vReal[i])
max_value = vReal[i];
if (min_value > vReal[i])
min_value = vReal[i];
}
average_value = sample_sum / SAMPLES;
// サンプリング周期確認用(ESP32でADCが遅くなる場合が有ったので確認用)
// 実際のサンプリング速度を返す
return 1000000.0 / float(end_time - start_time) * SAMPLES;
}
// FFT解析用
float FFT_analysis()
{
// FFT
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); // 窓関数
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); // FFT解析
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES); // 複素数 to 実数
// peakの周波数を計算する
float peak_frequency = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
Serial.println(peak_frequency);
return peak_frequency; // 計算結果を戻す
}
#endif
// FFT解析モード ここまで
// スピードチェッカー
void speed_checker()
{
// 初期化処理
#if defined(FFT_mode_def)
pinMode(pulse_pin, ANALOG);
analogSetAttenuation(ADC_11db);
#else
// カウンタの設定
pinMode(pulse_pin, INPUT_PULLUP); // ピンモード
pcnt_config_t pcnt_config; //設定用の構造体の宣言(A相)
pcnt_config.pulse_gpio_num = pulse_pin; // パルスのピン指定
pcnt_config.ctrl_gpio_num = PCNT_PIN_NOT_USED;
pcnt_config.lctrl_mode = PCNT_MODE_KEEP;
pcnt_config.hctrl_mode = PCNT_MODE_KEEP;
pcnt_config.channel = PCNT_CHANNEL_0;
pcnt_config.unit = PCNT_UNIT_0; // ユニット番号(最大8個)
pcnt_config.pos_mode = PCNT_COUNT_INC; // 信号ピン立上り指定 カウントアップ
pcnt_config.neg_mode = PCNT_COUNT_DIS; // 信号ピン立下り指定 カウント無効
pcnt_config.counter_h_lim = 32768; // カウンタの上限
pcnt_config.counter_l_lim = -32767; // カウンタの下限
pcnt_unit_config(&pcnt_config); //ユニット初期化
pcnt_counter_pause(PCNT_UNIT_0); //カウンタ一時停止
pcnt_set_filter_value(PCNT_UNIT_0, 100); // フィルター
pcnt_filter_enable(PCNT_UNIT_0);
pcnt_counter_clear(PCNT_UNIT_0); //カウンタ初期化
pcnt_counter_resume(PCNT_UNIT_0); //カウント開始
unsigned long start_time = millis();
unsigned long end_time = 0;
int16_t puls_count = 0; // スピードカウント用
#endif
float speed_value = 0;
float speed_max = 0; // 速度最大値
//画面クリア
M5.Lcd.fillScreen(TFT_BLACK);
M5.Lcd.setCursor(line1_x, line1_y);
M5.Lcd.print(F("Speed Check"));
//ここからループ
while (1)
{
#if defined(FFT_mode_def)
//Serial.println(sampling()); //サンプリング
sampling(); //サンプリング
speed_value = 0;
if (max_value - min_value > 200) // 動いていないときの分岐
speed_value = FFT_analysis() * speed_rate / 1000000.0 * 3600.0; // FFT解析と速度計算
#else
// 速度計算
pcnt_get_counter_value(PCNT_UNIT_0, &puls_count); // パルスカウント取得
end_time = millis();
// speed_value = float(puls_count / (end_time - start_time)) * eeprom_data.speed_rate * 1000.0 / 3600.0;
speed_value = float(puls_count) / float(end_time - start_time) * speed_rate / 1000.0 * 3600.0;
pcnt_counter_clear(PCNT_UNIT_0); //カウンタ初期化
// puls_count = 0;
start_time = millis();
#endif
// 最大値更新
if (speed_max < speed_value)
{
speed_max = speed_value;
}
M5.Lcd.setCursor(line2_x, line2_y);
M5.Lcd.print(F("Speed:"));
M5.Lcd.setCursor(line3_x, line3_y);
M5.Lcd.print(speed_value, 2);
M5.Lcd.print(F("km/h "));
M5.Lcd.setCursor(line4_x, line4_y);
M5.Lcd.print(speed_max, 2);
M5.Lcd.print(F("km/h(MAX) "));
delay(500);
M5.update();
uint8_t BtnC = M5.Axp.GetBtnPress();
if (M5.BtnA.wasPressed())
{
speed_max = 0;
}
else if (M5.BtnB.wasPressed())
{
}
else if (BtnC == 2)
{
}
else if (BtnC == 1)
{
}
delay(10);
}
#if defined(FFT_mode_def)
// 終了時の処理無し
#else
// 終了時の処理
pcnt_counter_pause(PCNT_UNIT_0); //カウンタ一時停止
pinMode(pulse_pin, INPUT);
detachInterrupt(pulse_pin);
#endif
}
//セットアップ
void setup()
{
M5.begin();
delay(50);
setCpuFrequencyMhz(240); // CPU周波数変更
// 未使用ピンの(G25)のフローティング
#ifdef M5StickC_Plus
gpio_pulldown_dis(GPIO_NUM_25);
gpio_pullup_dis(GPIO_NUM_25);
#endif
//画面は初期化されているのでこっち
//画面初期化
M5.Axp.ScreenBreath(8); // バックライトの明るさ(7-15)
M5.Lcd.setRotation(1); // 表示の向き
M5.Lcd.fillScreen(BLACK); // LCD背景色
M5.Lcd.setHighlightColor(BLACK);
M5.Lcd.setTextFont(1);
M5.Lcd.setTextSize(font_size); // 文字のサイズ
M5.Lcd.setTextColor(WHITE, BLACK); // 文字の色
}
//メインルーチン
void loop()
{
speed_checker();
}
M5StackC Plusの方ははじめの4行を下記の通りに書き換えてください。
// 使用するマイコン選択<ここから>
//#define M5StickC_def // M5stickC
#define M5StickC_Plus_def // M5stickC Plus
// 使用するマイコン選択<ここまで>
書き込んだ後、下記の画面が表示されたら正常に書き込みが出来ています。
使い方
先ほど組み立てたユニットをM5StickCに接続します。
19mmローラーを回すと速度が表示されます。
最高速度は正面にあるボタンを押すとリセットされます。
※少し長めに押してください
更新履歴
2022/05/10 更新分
回転数検出にFFT解析モードを追加しました。
FFT解析モードを使用する場合は下記の通りコメントアウトを外してください。
6行目~になります。
// FFT解析モード (使用する場合はコメントアウトを外す)
// ※ 高速時対策用
#define FFT_mode_def
センサーや使用するローラーによって高速時の測定が出来ないことがありましたので、その対策で実装しました。
このモードを使用する事により改善されることがあります。
デメリットとしてはノイズなどの影響を受けやすくなります。
その他の対策としては、間にコンパレータを入れる、ローラーの穴位置がフォトカプラの中心に来るように加工する等があります。