Лазерный хронометр для слот-кар на Arduino Mega с GLCD-дисплеем — точное замерное оборудование
Компоненты и материалы
- Arduino Mega 2560 — 1 шт. (нужен для GLCD ks0108 — много пинов)
- Лазерный модуль KY-008 (650 нм, 5V) — 1 шт.
- Фотосенсор (парный к KY-008, обычно KY-008 включает приемник) — 1 шт.
- Графический LCD (GLCD) на контроллере KS0108 (128×64) — 1 шт.
- Провода, макетная плата / монтажная плата
Приложения
- Arduino IDE
О проекте
Автор купил пару KY-008 (лазер + фоторезистор/фототранзистор) и решил сделать точные хронометры для слот-кар (автомодели на шлейфе). Лазерный луч пересекает трассу; когда машинка прерывает луч, сенсор фиксирует момент. Arduino Mega измеряет время между прерываниями (круги) и выводит на GLCD: номер круга, текущее время круга, время последнего круга, лучшее время.
Аппаратная часть
GLCD KS0108
Параллельный интерфейс (8 бит данных + управление) — требует много пинов, поэтому Arduino Mega. Распиновка и библиотека:
- Библиотека openGLCD (современный форк GLCDks0108): bitbucket.org/bperrybap/openglcd
- Информация о подключении: playground.arduino.cc
Автор сделал удобный разъем-переходник для GLCD (фото в проекте).
Лазер KY-008
3 пина: –, +, S (управление). Для постоянного свечения S и + замкнуты на 5V. Для управляемого — S на цифровой пин.
Фотосенсор
3 пина: GND, VCC (5V), SIGNAL → цифровой вход Arduino (D2 в коде).
Главная сложность
Точная оптическая выравнивание лазера и сенсора по разные стороны трассы. На фото видно крепление на профилях.
Программный код (полный скетч)
/* Time keeping digital gate
* Code by: Tal Ofer
* talofer99@hotmail.com
*/
#include "openGLCD.h"
// Переменные для кругов
unsigned long currentRunStartMillis;
unsigned long lastRunInMillis;
unsigned long bestRunInMillis;
int currentLap;
unsigned long savedMillis;
// Текстовые области на GLCD
gText t1; // номер круга (крупно)
gText t2; // LAST
gText t3; // BEST
int sec_val, milli_val;
// Лазерные ворота
const int gateSensorPin = 2;
int gateSensorState;
int lastGateSensorState = LOW;
unsigned long lastDebounceTime = 0;
const int debounceDelay = 50; // мс, антидребезг
void setup() {
pinMode(gateSensorPin, INPUT);
delay(50); // дать сенсору и лазеру включиться, чтобы не зафиксировать ложный круг
GLCD.Init(NON_INVERTED);
// Области вывода
t1.DefineArea(textAreaTOP, lcdnums14x24); // крупные цифры сверху
t2.DefineArea(0, GLCD.CenterY, 8, 2, fixednums7x15); // LAST
t3.DefineArea(GLCD.CenterX, GLCD.CenterY, 8, 2, fixednums7x15); // BEST
t3.SetFontColor(WHITE);
t3.ClearArea();
// Статические надписи
GLCD.SelectFont(System5x7);
GLCD.CursorTo(1, 4); GLCD.print("LAST");
GLCD.CursorTo(11, 4); GLCD.print("BEST");
// Сброс параметров
currentRunStartMillis = 0;
lastRunInMillis = 0;
bestRunInMillis = 0;
currentLap = 0;
}
void loop() {
int reading = digitalRead(gateSensorPin);
// Антидребезг
if (reading != lastGateSensorState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != gateSensorState) {
gateSensorState = reading;
// Луч прерван (LOW) — проезд машинки
if (gateSensorState == LOW) {
savedMillis = millis(); // зафиксировали момент
if (currentLap > 0) {
lastRunInMillis = savedMillis - currentRunStartMillis;
if (lastRunInMillis < bestRunInMillis || bestRunInMillis == 0) {
bestRunInMillis = lastRunInMillis;
}
}
currentRunStartMillis = savedMillis; // старт нового круга
currentLap++;
}
}
}
lastGateSensorState = reading;
// Обновление дисплея
t1.CursorTo(0, 0);
t1.Printf(F("%02d"), currentLap); // номер круга
savedMillis = millis();
if (currentLap > 0) {
calcResultFromMillis(savedMillis - currentRunStartMillis, &sec_val, &milli_val);
} else {
calcResultFromMillis(0, &sec_val, &milli_val);
}
// CURRENT (текущий незавершенный круг)
t1.CursorTo(3, 0);
t1.Printf(F("%02d.%03d"), sec_val, milli_val);
// LAST
calcResultFromMillis(lastRunInMillis, &sec_val, &milli_val);
t2.CursorTo(1, 1);
t2.Printf(F("%02d.%03d"), sec_val, milli_val);
// BEST
calcResultFromMillis(bestRunInMillis, &sec_val, &milli_val);
t3.CursorTo(1, 1);
t3.Printf(F("%02d.%03d"), sec_val, milli_val);
}
// Перевод миллисекунд в секунды и миллисекунды для вывода
void calcResultFromMillis(unsigned long value, int *sec_val, int *milli_val) {
*sec_val = int(value / 1000);
*milli_val = value - *sec_val * 1000;
}
Особенности реализации
- Антидребезг 50 мс устраняет ложные срабатывания от вибраций/помех.
- Время круга измеряется как разница между двумя последовательными прерываниями луча.
- Лучшее время обновляется только если новый круг быстрее.
- Текущее время круга обновляется в реальном времени (millis() - start).
- Используются большие шрифты (lcdnums14x24) для наглядности.
Применение
Идеально для домашних треков слот-кар, соревнований, тестирования шасси/моторов. Можно расширить: сохранение на SD, беспроводная передача результатов, поддержка нескольких полос.
Производственный процесс
- Экспертное руководство по лазерной маркировке алюминия: точность, долговечность и отраслевые применения
- Полное руководство по лазерной маркировке: типы, преимущества и применения
- Волоконно-лазерная маркировка: точность, скорость и универсальность для современных отраслей
- Лазерный указатель: дизайн, производство и обзор безопасности
- Американские горки: от саней на снегу к стальным гигантам
- Полупроводниковые лазеры: точное генерирование света для современных технологий
- Твердотельные лазеры: технологии, применения и будущие тренды
- Песочные часы: история, ремесло и современная привлекательность
- Установка дисплея Kuman 3.5″ TFT на RetroPie — Пошаговое руководство
- Rpibot: Дешёвая расширяемая мобильная платформа для изучения встроенных систем