先日YouTubeの動画にコメントをいただいた、Petit Labさんのチャンネルに車両のスピードを計測する動画があった。
実は先日試していたフォトリフレクタで物体の検出をやっていて、これを活かせる方法としてスピード計測はぼんやりと考えていた。実際にやっている人がいるのを知って自分もやってみようと思った。
ついでにOLEDディスプレイもあるので、これでスピードを表示しようと思った。
で、出来たのが次の動画。
フォトリフレクタは赤外線の反射で検出するので、もっとダイレクトに「赤外線の遮蔽で検出した方がいいかな?」と赤外線LEDとフォトトランジスタで検出することにした。
回路は簡単で、次のようにした。
D1, Q1が先に検出する左側のセンサーになり、D2, Q2が右側のセンサーとなります。
あと、KiCadに部品が無かったので代わりに使った部品に0.96インチのOLEDディスプレイのU1に使わない端子(VLCD, V0)や”3 Lines 12 Character”の文字列がありますが、無視して下さい。
赤外線LEDとフォトトランジスタを別々に設置する必要があり、しかも正確に対峙するように設置したかったので、LEGOテクニックのビームの穴を利用することにした。
赤外線LEDとフォトトランジスタは4つの5×3のL字型ビームそれぞれに5つ穴の方の端から2番目に刺した。5mmのサイズだと全部は入らないので、適当に。
それを13穴の直線ビームの端に付けて、それらのビームを9穴のビームでつないだ。
これがセンサーフレームで、中央に線路の直線部分をはめ込んで使用する。
ただし、若干、線路より狭いのでちょっと強めに線路を押し込んだ。
左右のセンサー間の距離は95mmでした。
線路の高さを合わせるために、次のようなものをLEGOテクニックで作った。
上の方の大きなものはセンサーと逆側の直線部分に。その下の2つは各コーナーの頂点部分に。5つ穴のビーム4つはそれらの間に入れた。5つ穴のビームの両端には黒いピンが刺さっています。
Arduinoのスケッチは次のようになります。
#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); #define LOGO_HEIGHT 16 #define LOGO_WIDTH 16 static const unsigned char PROGMEM logo_off[] = { B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B11111111, B11111110, B00000001, B11111110, B00000001, B11111110, B00000001, B11111110, B00000001, B11111110, B00000001, B11111110, B00000001, B11111110, B00000001, B11111110, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000 }; static const unsigned char PROGMEM logo_on[] = { B00000000, B00000000, B11111111, B00000000, B11111111, B00000000, B11111111, B00000000, B11111111, B00000000, B11111111, B00000000, B11111111, B00000000, B11111111, B00000000, B11111111, B00000000, B10000000, B00000000, B10000000, B00000000, B10000000, B00000000, B10000000, B00000000, B10000000, B00000000, B10000000, B00000000, B10000000, B00000000 }; #define FLAG_ON_VALUE (900) int analogPin1 = 0; int analogPin2 = 1; int ledPin1 = 8; int ledPin2 = 9; int val1 = 0; int val2 = 0; unsigned char prev_flag1 = 0; unsigned char curr_flag1 = 0; unsigned char chng_flag1 = 0; unsigned char prev_flag2 = 0; unsigned char curr_flag2 = 0; unsigned char chng_flag2 = 0; unsigned char need_draw = 0; unsigned long time0 = 0; unsigned long time1 = 0; unsigned long time2 = 0; float val_speed = 0.0; void setup() { // put your setup code here, to run once: Serial.begin(9600); Serial.println("SSD1306 OLED 128x64 i2c"); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64 Serial.println(F("SSD1306 allocation failed")); for(;;); // Don't proceed, loop forever } // Show initial display buffer contents on the screen -- // the library initializes this with an Adafruit splash screen. display.display(); delay(2000); // Pause for 2 seconds // Clear the buffer display.clearDisplay(); pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); drawflags(); delay(1000); } void drawbitmap_off(int x, int y) { display.drawBitmap( x, y, logo_off, LOGO_WIDTH, LOGO_HEIGHT, 1); } void drawbitmap_on(int x, int y) { display.drawBitmap( x, y, logo_on, LOGO_WIDTH, LOGO_HEIGHT, 1); } void drawflags() { int x, y; y = LOGO_HEIGHT / 2; //y = (display.height() - LOGO_HEIGHT) / 4; display.clearDisplay(); x = display.width() / 4 - LOGO_WIDTH / 2; if (val1 > FLAG_ON_VALUE) { drawbitmap_on(x, y); // Draw a small bitmap image } else { drawbitmap_off(x, y); // Draw a small bitmap image } x = display.width() * 3 / 4 - LOGO_WIDTH / 2; if (val2 > FLAG_ON_VALUE) { drawbitmap_on(x, y); // Draw a small bitmap image } else { drawbitmap_off(x, y); // Draw a small bitmap image } display.setTextSize(2); // Draw 2X-scale text display.setTextColor(SSD1306_WHITE); display.setCursor(25, display.height() / 2); int speed_int = (int)(val_speed * 3.6 + 0.5); if (speed_int < 100) { display.print(F(" ")); } if (speed_int < 10) { display.print(F(" ")); } display.print(speed_int); display.print(F(" km/h")); display.display(); } void loop() { // put your main code here, to run repeatedly: digitalWrite(ledPin1, HIGH); digitalWrite(ledPin2, HIGH); delay(3); val1 = analogRead(analogPin1); val2 = analogRead(analogPin2); digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW); need_draw = 0; time0 = millis(); if (time0 - time1 > 15000) { val_speed = 0; time1 = time2 = time0; need_draw = 1; } if (time0 - time2 > 15000) { val_speed = 0; time1 = time2 = time0; need_draw = 1; } if (val1 > FLAG_ON_VALUE) { curr_flag1 = 1; } else { curr_flag1 = 0; } if (prev_flag1 != curr_flag1) { chng_flag1 = 1; if (curr_flag1 > 0) { time1 = millis(); } } else { chng_flag1 = 0; } if (val2 > FLAG_ON_VALUE) { curr_flag2 = 1; } else { curr_flag2 = 0; } if (prev_flag2 != curr_flag2) { chng_flag2 = 1; if (curr_flag2 > 0) { time2 = millis(); if (time2 > time1) { float delta_t = time2 - time1; val_speed = 150.0 * 95.0 / delta_t; } } } else { chng_flag2 = 0; } if (chng_flag1 + chng_flag2 + need_draw > 0) { drawflags(); } delay(7); prev_flag1 = curr_flag1; prev_flag2 = curr_flag2; }