Чтобы не "мусорить" в чужой теме...
Давно блуждали мысли, "поковырять" arduino, но так как реального применения не находилось, то так на уровне мыслей все и оставалось.
И тут наткнулся на тему
xu56857 масштабное измерение скорости
Первоначально планировалось только доработать авторский код с целью добавления возможности выбора масштаба и единиц измерения, но выслушав советы
BR95009 и
Qvan22, основательно "перелопатил" исходный скетч.
В чем отличие от авторского - как таковые убраны задержки обработки кода, т.е. программа работает постоянно, все задержки реализованы при помощи флагов.
Все пины и константы вынесены в переменные, что позволяет подключать датчики и кнопки к любому выходу с минимальными правками кода.
Добавлен выбор масштаба (HO, TT, N). При необходимости можно добавить любой масштаб
Добавлен выбор единиц измерения (км/ч, MPH). При необходимости можно добавить любые единицы (дюймы в сек, миллиметры в сек... Все, что душе угодно).
Для сохранения выбранных настроек используется энергонезависимая память контроллера (EEPROM), что позволяет при повторном включении вернутся к последним выбранным единицам измерения и масштабу
Добавлена анимация во время измерения. Вместо надписей GO LEFT/GO RIGHT - реализована бегущая строка из символов > и < движущихся в соответствующем направлении
Пока все реализовано на макетке на UNO, жду нужные компоненты от наших китайских товарищей и буду реализовывать в "железе".
Схема
скетч
Arduino код:
#include <LiquidCrystal.h> // Добавляем библиотеку LCD
#include <EEPROM.h> // Добавляем библиотеку работы с энергонезависимой памятью
// Добавляем библиотеку антидребезга кнопок
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Main code by Thomas O Fredericks (tof@t-o-f.info)
Previous contributions by Eric Lowry, Jim Schimpf and Tom Harkaway
[url]https://github.com/thomasfredericks/Bounce2/archive/master.zip[/url]
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <Bounce2.h>
// переменные, которые можно менять
float S = 70; //дистанция (расстояние между датчиками) в миллиметрах
int resultDelay = 3000; // время показа результата измерений в миллисекундах
int animationDelay = 50; // время задержки анимации в миллисекундах
unsigned long timeoutDelay = 60000; // время ожидания срабатывания второго датчика в миллисекундах
// флаги
unsigned long flagLeft = 0; // время срабатывания левого датчика
unsigned long flagRight = 0; // время срабатывания правого датчика
unsigned long flagShow = 0; //время окончания отображения результатов
unsigned long flagAnimation = 0; //время отображения следующего символа при анимации
// переменные для расчетов
unsigned long timeStart;//время срабатывания первого датчика
unsigned long timeStop;//время срабатывания второго датчика
long T; //интервал в милисекундах
float V; //скорость в м/с
float sV; //масштабная скорость
int selcted_scale = EEPROM.read(0); // ключ массива выбранного масштаба
int selcted_unit = EEPROM.read(1); // ключ массива выбранных единиц измерения
// массивы, которые можно дополнять или изменять
const String scale[][2] = {{" HO", "87"}, {" TT", "120"}, {" N", "160"}}; // массив масштабов (отображение на экране, множитель)
const String units[][2] = {{"KPH", "3.6"}, {"MPH", "2.23694"}}; // массив единиц измерения (отображение на экране, коэффициент перевода из метров в секунду)
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // (RS, E, DB4, DB5, DB6, DB7) инициализируем дисплей
int ir_left = 8; // вход лекого датчика
int ir_right = 9; // вход правого датчика
int scalePin = 10; // вход кноки выбора масштаба
int unitsPin = 11; // вход кнопки выбора единиц измерения скорости
Bounce scaleBouncer = Bounce(); // объект-обработчик антидребезга кнопки выбра масштаба
Bounce unitsBouncer = Bounce(); // объект-обработчик антидребезга кнопки выбора единиц измерения
// переменные для анимации
int r;
char c;
void setup() {
lcd.begin(16, 2);
pinMode(ir_left, INPUT);
pinMode(ir_right, INPUT);
pinMode(scalePin, INPUT);
pinMode(unitsPin, INPUT);
// параметры обработчика антидребезга
scaleBouncer.attach(scalePin);
scaleBouncer.interval(5);
unitsBouncer.attach(unitsPin);
unitsBouncer.interval(5);
}
// расчет скорости
void calculateSpeed(int selcted_unit, int selcted_scale){
T = (timeStop - timeStart);
V = S / T;
sV = V * units[selcted_unit][1].toFloat() * scale[selcted_scale][1].toInt();
char buffer[12];
String tmpStr = dtostrf(sV, 12, 1, buffer);
lcd.setCursor(0, 0);
lcd.print(tmpStr);
flagShow = millis() + resultDelay;
timeStart = 0;
timeStop = 0;
}
//рисуем бегущую строку
void animation() {
lcd.setCursor(r, 0);
lcd.print(c);
if(flagRight > 0) {
r++;
if(r > 11){
r = 1;
if(c == '>') {
c = ' ';
}
else {
c = '>';
}
}
}
else if(flagLeft > 0) {
r--;
if(r < 1){
r = 11;
if(c == '<') {
c = ' ';
}
else {
c = '<';
}
}
}
flagAnimation = millis() + animationDelay;
}
void loop() {
if(selcted_scale > sizeof(scale)/sizeof(scale[0])-1) {
// если считанное значение больше, чем количество элементов в массиве масштабов - обнуляем значение
selcted_scale = 0;
EEPROM.write(0, 0);
}
if(selcted_unit > sizeof(units)/sizeof(units[0])-1) {
// если считанное значение больше, чем количество элементов в массиве единиц измерения скоростей - обнуляем значение
selcted_unit = 0;
EEPROM.write(1, 0);
}
// если флаг отображения результатов меньше текущего времени выполняем код, в противном случае продолжаем отображать результаты измерений
if(flagShow < millis()) {
flagShow = 0;
// если сработал один датчик, но за время timeoutDelay не сработал второй - выводим TIME OUT
if(timeStart > 0 && timeStart + timeoutDelay < millis()) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" TIME OUT ");
flagShow = millis() + resultDelay;
timeStart = 0;
timeStop = 0;
flagLeft = 0;
flagRight = 0;
flagAnimation = 0;
}
// если одновременно сработали оба датчика, но не выставлены флаги начала измерения - выводим ошибку
else if(digitalRead(ir_right) == LOW && digitalRead(ir_left) == LOW && flagLeft == 0 && flagRight == 0) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" ERROR ");
flagShow = millis() + 1000;
}
// если нет сигнала от обоих датчиков и не выставлены флаги обоих датчиков - система готова к измерениям
else if(digitalRead(ir_right) == HIGH && digitalRead(ir_left) == HIGH && flagLeft == 0 && flagRight == 0) {
// если была нажата кнопка масштаба
if(scaleBouncer.update()){
if(scaleBouncer.read() == HIGH) {
selcted_scale++;
if(selcted_scale > sizeof(scale)/sizeof(scale[0])-1){
selcted_scale = 0;
}
}
}
// если была нажата кнопка единиц измерения
if (unitsBouncer.update()){
if(unitsBouncer.read() == HIGH) {
selcted_unit++;
if(selcted_unit > sizeof(units)/sizeof(units[0])-1){
selcted_unit = 0;
}
}
}
lcd.setCursor(13, 0);
lcd.print(units[selcted_unit][0]);
lcd.setCursor(13, 1);
lcd.print(scale[selcted_scale][0]);
lcd.setCursor(1, 0);
lcd.print("READY ");
}
// если измерения не началось и сработал один из датчиков при этом нет флага второго датчика - начинаем замер
else if(timeStart == 0 && ((digitalRead(ir_right) == LOW && flagLeft == 0) || (digitalRead(ir_left) == LOW && flagRight == 0))) {
timeStart = millis();
if(digitalRead(ir_right) == LOW){
flagRight = millis();
c = '>';
r = 1;
}
else {
flagLeft = millis();
c = '<';
r = 11;
}
lcd.setCursor(1, 0);
lcd.print(" ");
animation();
}
// если сработал один из датчиков и выставлен флаг второго - заканчиваем измерения и производим расчет скорости
else if((digitalRead(ir_right) == LOW && flagLeft > 0) || (digitalRead(ir_left) == LOW && flagRight > 0)) {
timeStop = millis();
flagLeft = 0;
flagRight = 0;
flagAnimation = 0;
EEPROM.update(0, selcted_scale);
EEPROM.update(1, selcted_unit);
calculateSpeed(selcted_unit, selcted_scale);
}
// если настало время рисовать новый символ при анимации
else if(flagAnimation < millis()){
animation();
}
}
}