Форум ModelldepO

Форум ModelldepO (http://forum.modelldepo.ru/index.php)
-   Arduino (http://forum.modelldepo.ru/forumdisplay.php?f=220)
-   -   масштабное измерение скорости V2.0 (http://forum.modelldepo.ru/showthread.php?t=18201)

BNSF9399 12.09.2016 12:19

масштабное измерение скорости V2.0
 
Вложений: 1
Чтобы не "мусорить" в чужой теме...
Давно блуждали мысли, "поковырять" arduino, но так как реального применения не находилось, то так на уровне мыслей все и оставалось.
И тут наткнулся на тему xu56857 масштабное измерение скорости
Первоначально планировалось только доработать авторский код с целью добавления возможности выбора масштаба и единиц измерения, но выслушав советы BR95009 и Qvan22, основательно "перелопатил" исходный скетч.
В чем отличие от авторского - как таковые убраны задержки обработки кода, т.е. программа работает постоянно, все задержки реализованы при помощи флагов.
Все пины и константы вынесены в переменные, что позволяет подключать датчики и кнопки к любому выходу с минимальными правками кода.
Добавлен выбор масштаба (HO, TT, N). При необходимости можно добавить любой масштаб
Добавлен выбор единиц измерения (км/ч, MPH). При необходимости можно добавить любые единицы (дюймы в сек, миллиметры в сек... Все, что душе угодно).
Для сохранения выбранных настроек используется энергонезависимая память контроллера (EEPROM), что позволяет при повторном включении вернутся к последним выбранным единицам измерения и масштабу
Добавлена анимация во время измерения. Вместо надписей GO LEFT/GO RIGHT - реализована бегущая строка из символов > и < движущихся в соответствующем направлении
https://www.youtube.com/watch?v=_TGgUmm8zWE#t=14

Пока все реализовано на макетке на UNO, жду нужные компоненты от наших китайских товарищей и буду реализовывать в "железе".
Схема
Вложение 130518
скетч
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(765432); // (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(162);
  
pinMode(ir_leftINPUT);
  
pinMode(ir_rightINPUT);
  
pinMode(scalePinINPUT);
  
pinMode(unitsPinINPUT);
  
// параметры обработчика антидребезга
  
scaleBouncer.attach(scalePin);
  
scaleBouncer.interval(5);
  
unitsBouncer.attach(unitsPin);
  
unitsBouncer.interval(5);
}

// расчет скорости
void calculateSpeed(int selcted_unitint selcted_scale){
  
= (timeStop timeStart);
  
T;
  
sV units[selcted_unit][1].toFloat() * scale[selcted_scale][1].toInt();
  
char buffer[12];
  
String tmpStr dtostrf(sV121buffer);
  
lcd.setCursor(00);
  
lcd.print(tmpStr);
  
flagShow millis() + resultDelay;
  
timeStart 0;
  
timeStop 0;
}

//рисуем бегущую строку
void animation() {
  
lcd.setCursor(r0);
  
lcd.print(c);
  if(
flagRight 0) {
    
r++;
    if(
11){
      
1;
      if(
== '>') {
        
' ';
      }
      else {
        
'>';
      }
    }
  }
  else if(
flagLeft 0) {
    
r--;
    if(
1){
      
11;
      if(
== '<') {
        
' ';
      }
      else {
        
'<';
      }
    }
  }
  
flagAnimation millis() + animationDelay;
}

void loop() {
  if(
selcted_scale sizeof(scale)/sizeof(scale[0])-1) {
    
// если считанное значение больше, чем количество элементов в массиве масштабов - обнуляем значение
    
selcted_scale 0;
    
EEPROM.write(00);
  }
  if(
selcted_unit sizeof(units)/sizeof(units[0])-1) {
    
// если считанное значение больше, чем количество элементов в массиве единиц измерения скоростей - обнуляем значение
    
selcted_unit 0;
    
EEPROM.write(10);
  }
  
// если флаг отображения результатов меньше текущего времени выполняем код, в противном случае продолжаем отображать результаты измерений
  
if(flagShow millis()) {
    
flagShow 0;
    
// если сработал один датчик, но за время timeoutDelay не сработал второй - выводим TIME OUT
    
if(timeStart && timeStart timeoutDelay millis()) {
      
lcd.clear();
      
lcd.setCursor(00);
      
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 == && flagRight == 0) {
      
lcd.clear();
      
lcd.setCursor(00);
      
lcd.print("     ERROR      ");
      
flagShow millis() + 1000;
    }
    
// если нет сигнала от обоих датчиков и не выставлены флаги обоих датчиков - система готова к измерениям
    
else if(digitalRead(ir_right) == HIGH && digitalRead(ir_left) == HIGH && flagLeft == && 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(130);
      
lcd.print(units[selcted_unit][0]);
      
lcd.setCursor(131);
      
lcd.print(scale[selcted_scale][0]);
      
lcd.setCursor(10);
      
lcd.print("READY      ");
    }
    
// если измерения не началось и сработал один из датчиков при этом нет флага второго датчика - начинаем замер
    
else if(timeStart == && ((digitalRead(ir_right) == LOW && flagLeft == 0) || (digitalRead(ir_left) == LOW && flagRight == 0))) {
      
timeStart millis();
      if(
digitalRead(ir_right) == LOW){
        
flagRight millis();
        
'>';
        
1;
      }
      else {
        
flagLeft millis();
        
'<';
        
11;
      }
      
lcd.setCursor(10);
      
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(0selcted_scale);
      
EEPROM.update(1selcted_unit);
      
calculateSpeed(selcted_unitselcted_scale);
    }
    
// если настало время рисовать новый символ при анимации
    
else if(flagAnimation millis()){
      
animation();
    }
  }



Alexmit 12.09.2016 14:59

Цитата:

Сообщение от BNSF9399 (Сообщение 281612)
Схема

Я бы датчики повесил на выводы 2 и 3, а то вдруг захочется на прерываниях сделать.

BNSF9399 12.09.2016 15:27

Цитата:

Сообщение от Alexmit (Сообщение 281628)
а то вдруг захочется на прерываниях сделать

боюсь мне до этого как до Китая пешком, но совет учту, лучше предусмотреть сразу, чем потом переделывать.

xu56857 12.09.2016 19:57

Попробовал установить Ваш скетч - супер! Очень понравилось , что добавили время ожидания в 20 секунд на срабатывания второго датчика, я об этом думал, но руки не дошли ((, проще было нажать "резет". Вот что значит софт написанный специалистом, спасибо!

BR95009 12.09.2016 20:30

Цитата:

Сообщение от BNSF9399 (Сообщение 281612)
Для сохранения выбранных настроек используется энергонезависимая память контроллера (EEPROM)

В EEPROM нужно стараться писать как можно реже, так как ее ресурс на запись на ардуине серьезно ограничен. Чтение - без проблем.

---------- Сообщение добавлено в 22:30 ---------- Предыдущие сообщение было в 22:24 ----------

Шикарно, очень элегантно!

Кстати, если будете паять на навесном монтаже, то я тут для себя открыл вот такие макетки. На ней собирается, также как на бредборде и паяется, с перемычками. Красота.

http://g02.a.alicdn.com/kf/HTB1ikLfK...0x24-5cm-2.jpg

BNSF9399 12.09.2016 21:07

Цитата:

Сообщение от BR95009 (Сообщение 281659)
я тут для себя открыл вот такие макетки

Хорошие, но резать надо дорожки. Хотя, это удобнее чем лишние перемычки делать. Но я заказал обычные, благо не так много соединений.

Цитата:

Сообщение от BR95009 (Сообщение 281659)
В EEPROM нужно стараться писать как можно реже

ну у меня реально только при изменении значения запись идет. Можно доработать, делать запись если состояние не менялось какоето время, тем самым исключив запись "прощелкиваемых" позиций.
подумаю над этим.

Цитата:

Сообщение от xu56857 (Сообщение 281655)
Вот что значит софт написанный специалистом

Я такой же специалист, как и вы. Это вообще мое первое "творение" на ардуине.
Так, php средненько знаю, лет 10 назад на перле немного упражнялся. Спасибо BR95009, показал красивый "финт" с millis(), иначе ябы тоже залез в цикл прерываемый вторым датчиком.

BNSF9399 12.09.2016 21:23

Цитата:

Сообщение от xu56857 (Сообщение 281655)
время ожидания в 20 секунд

Это время можно менять на свое усмотрение. у меня с MD LSH micro 1 см за 20 сек проезжает. Стало быть 7см - 140 сек.
Тут можно или дробную часть измерений увеличить (в моем варианте только десятые отображает), либо смириться с тем, что маленькие скорости не меряем.
за получение форматированного результата отвечает функция dtostrf(sV,12,1,buffer); Где 12 - это минимальное количество символов в возврате (включая точку) и 1 количество знаков в дробной части. Почему я сделал так - все просто. при обработке мне не надо считать длину получившегося числа, чтобы прижать его вправо к единицам измерения. эта функция дополнит число ведущими пробелами до 12 знаков. итого, я печатаю с нулевой позиции число и точно знаю, что оно у меня всегда закончится в 11 позиции, а с 13 позиции я печатаю единицы измерения. Если поменять 1 на 3, то получим 3 знака после запятой в возврате. Тогда и таймаут можно побольше сделать.

BR95009 12.09.2016 22:23

Цитата:

Сообщение от BNSF9399 (Сообщение 281666)
Хорошие, но резать надо дорожки.

Зачем резать? Паяйте так же, как ставите на бредбоард. Все очень культурно. Правда не так компактно, но для компактных решений один хрен свою PCB нужно делать нормальну двухстороннюю.

BNSF9399 12.09.2016 23:30

Цитата:

Сообщение от BR95009 (Сообщение 281675)
Правда не так компактно

вот чтобы компактно было - и резать. свою плату имеет смысл делать если много обвеса.

BNSF9399 13.09.2016 11:26

Цитата:

Сообщение от BR95009 (Сообщение 281659)
В EEPROM нужно стараться писать как можно реже

вынес чтение в отдельную функцию, так как оно нужно только при старте.
запись сделал перед началом отображения результатов.
т.е. в процессе работы можно менять масштаб и единицы, но в EEPROM они будут записаны только после очередного замера скорости.
а вернее, даже не записаны, а обновлены. Т.е. запись произойдет только в том случае, если значение в EEPROM отличается от текущего выбранного.

Еще бы с округлением дробного числа разобраться. Чтото мне подсказывает, что dtostrf просто отрезает лишние знаки. В результате 0.19 км/ч будет отображаться как 0.1, а надо 0.2
round только до целого округляет. Делать финт ушами с округлением значения умноженного на 10, а затем делить на 10?
красивее пока в голову ничего не лезет.

BR95009 13.09.2016 12:03

Цитата:

Сообщение от BNSF9399 (Сообщение 281713)
В результате 0.19 км/ч будет отображаться как 0.1, а надо 0.2

А зачем замерять до десятых вообще?

Мы же для настройки меряем Мах скорость, а она обычно в десятах киллометров у локомотивов (у немецких, например). По этому округелние до целых вполне себе годится.

А так то:
Цитата:

floor() — округление вниз
ceil() — округление вверх
round() — округление в ближайшую сторону

BNSF9399 13.09.2016 14:38

Цитата:

Сообщение от BR95009 (Сообщение 281716)
Мы же для настройки меряем Мах скорость

в принципе - да, а вдруг минимальную захочется померять? у меня 0.188 она.

Цитата:

Сообщение от BR95009 (Сообщение 281716)
А так то:

ну так оно до целого округляет. в php в round второй параметр указывает дробную часть, т.е. round(0.188, 2) вернет 0.19, round(0.188,1) - 0.2, а тут round(0.188) вернет 0.
Поэтому и получается, что нужно делать round(0.188*10)/10 ?

shalex 13.09.2016 15:31

оффтопик

BR95009 13.09.2016 15:39

Цитата:

Сообщение от shalex (Сообщение 281733)
Возможно не в кассу и лишнее, но лучше не грузить МК дробными вычислениями и переменными, а оперировать целочисленными типами.

В данном случае - не важно, в вообще - абсолютно согласен. Флоата надо стараться избегать.

---------- Сообщение добавлено в 17:36 ---------- Предыдущие сообщение было в 17:34 ----------

Цитата:

Сообщение от shalex (Сообщение 281733)
Serial.print(Speed % 100);

А что нам даст остаток от деления?

---------- Сообщение добавлено в 17:39 ---------- Предыдущие сообщение было в 17:36 ----------

Цитата:

Сообщение от shalex (Сообщение 281733)
Пример вывода скорости в отладочный порт, в человеческом виде:

Я решил проблему проще:

int a = 7; int b = 4;
Serial.print(float(a/b)); // выводит 1,75

shalex 13.09.2016 15:52

Цитата:

Сообщение от BR95009 (Сообщение 281734)
А что нам даст остаток от деления?

Даст 2 знака после запятой, без применения типов с плав.запятой.
Только надо выводить в формате "00", чтобы лидирующий 0 не терялся, если результат получится меньше 10 (меньше 0.1)

Цитата:

Сообщение от BR95009 (Сообщение 281734)
int a = 7; int b = 4;
Serial.print(float(a/b)); // выводит 1,75

Тут опять float используется, можно и без этого сделать, только на целочисленных операциях.

BR95009 13.09.2016 16:07

Ну можно все умножить на 1000. А потом выводить по условиям.

BNSF9399 13.09.2016 16:38

Цитата:

Сообщение от BR95009 (Сообщение 281734)
Serial.print(float(a/b)); // выводит 1,75

красиво, но теперь нужно посчитать, с какой позиции печатать это на экрене

Цитата:

Сообщение от BR95009 (Сообщение 281734)
Флоата надо стараться избегать.

коэффициенты перевода и метры в секунду с разной точностью.
лишняя операция деления - меньше грузит процессор чем умножение на float?
И, в очередной раз, спасибо
Arduino код:

  t 0.001
  
0.001 t

явно можно убрать умножение ибо
S / T даст тотже результат.

shalex 13.09.2016 17:05

Цитата:

Сообщение от BNSF9399 (Сообщение 281742)
лишняя операция деления - меньше грузит процессор чем умножение на float?

Почитал вот эту статью и что-то операции с float не кажутся такими уж тормозными )))

BNSF9399 14.09.2016 13:45

Вложений: 1
Шальные руки голове покоя не дают.
в процессе экспериментов выяснилось, что функция dtostrf превосходно округляет сама.
также выяснилось, что при расчете скорости в м/с V = S / T при большом значении T, зачастую получаем на выходе 0.
проблему решил умножением S на 1000, с последующим умножением на 0.001 результата масштабной скорости
Arduino код:

  V 1000.0 T;
  
sV units[selected_unit][1].toFloat() * scale[selected_scale][1].toInt() * 0.001

Ну и универсальности хочется... Добавил кнопку выбора количества знаков после запятой при выводе результата.
Ниже два скетча. Первый без кнопки, количество знаков задается в переменной
Arduino код:

int decimals 1

переставил датчики на 2-й и 3-й выходы
схема
Вложение 130605

скетч v2.10 - без кнопки
Arduino код:

/*
 scale speed meter v2.10 2016.09.14 13:14 GMT
 */
#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> 

// переменные, которые можно менять
int S 70//дистанция (расстояние между датчиками) в миллиметрах
int resultDelay 5000// время показа результата измерений в миллисекундах
int animationDelay 50// время задержки анимации в миллисекундах
unsigned long timeoutDelay 160000// время ожидания срабатывания второго датчика в миллисекундах

// флаги
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 selected_scale EEPROM.read(0); // ключ массива выбранного масштаба
int selected_unit EEPROM.read(1); // ключ массива выбранных единиц измерения
int decimals 1// точности отображения результата

// массивы, которые можно дополнять или изменять
const String scale[][2] = {{" HO""87"}, {" TT""120"}, {"  N""160"}}; // массив масштабов (отображение на экране, множитель)
const String units[][2] = {{"KPH""3.6"}, {"MPH""2.23694"}}; // массив единиц измерения (отображение на экране, коэффициент перевода из метров в секунду)

LiquidCrystal lcd(456789); // (RS, E, DB4, DB5, DB6, DB7) инициализируем дисплей
int ir_left 2// вход лекого датчика
int ir_right 3// вход правого датчика
int scalePin 10// вход кноки выбора масштаба
int unitsPin 11// вход кнопки выбора единиц измерения скорости
int decimalsPin 12// вход кнопки выбора точночти отображения результатов

Bounce scaleBouncer Bounce(); // объект-обработчик антидребезга кнопки выбра масштаба
Bounce unitsBouncer Bounce(); // объект-обработчик антидребезга кнопки выбора единиц измерения

// переменные для анимации
int r;
char c;

void setup() {
  
lcd.begin(162);
  
pinMode(ir_leftINPUT);
  
pinMode(ir_rightINPUT);
  
pinMode(scalePinINPUT);
  
pinMode(unitsPinINPUT);
  
// параметры обработчика антидребезга
  
scaleBouncer.attach(scalePin);
  
scaleBouncer.interval(5);
  
unitsBouncer.attach(unitsPin);
  
unitsBouncer.interval(5);
}

// расчет скорости
void calculateSpeed(int selected_unitint selected_scaleint decimals){
  
= (timeStop timeStart);
  
1000.0 T;
  
sV units[selected_unit][1].toFloat() * scale[selected_scale][1].toInt() * 0.001;
  
char buffer[12];
  
String tmpStr dtostrf(sV12decimalsbuffer);
  
lcd.setCursor(00);
  
lcd.print(tmpStr);
  
flagShow millis() + resultDelay;
  
timeStart 0;
  
timeStop 0;
}

//рисуем бегущую строку
void animation() {
  
lcd.setCursor(r1);
  
lcd.print(c);
  if(
flagRight 0) {
    
r++;
    if(
11){
      
1;
      if(
== '>') {
        
' ';
      }
      else {
        
'>';
      }
    }
  }
  else if(
flagLeft 0) {
    
r--;
    if(
1){
      
11;
      if(
== '<') {
        
' ';
      }
      else {
        
'<';
      }
    }
  }
  
flagAnimation millis() + animationDelay;
}

void loop() {
  if(
selected_scale sizeof(scale)/sizeof(scale[0])-1) {
    
// если считанное значение больше, чем количество элементов в массиве масштабов - обнуляем значение
    
selected_scale 0;
    
EEPROM.write(00);
  }
  if(
selected_unit sizeof(units)/sizeof(units[0])-1) {
    
// если считанное значение больше, чем количество элементов в массиве единиц измерения скоростей - обнуляем значение
    
selected_unit 0;
    
EEPROM.write(10);
  }
  
// если флаг отображения результатов меньше текущего времени выполняем код, в противном случае продолжаем отображать результаты измерений
  
if(flagShow millis()) {
    
flagShow 0;
    
// если сработал один датчик, но за время timeoutDelay не сработал второй - выводим TIME OUT
    
if(timeStart && timeStart timeoutDelay millis()) {
      
lcd.clear();
      
lcd.setCursor(00);
      
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 == && flagRight == 0) {
      
lcd.clear();
      
lcd.setCursor(00);
      
lcd.print("     ERROR      ");
      
flagShow millis() + 1000;
    }
    
// если нет сигнала от обоих датчиков и не выставлены флаги обоих датчиков - система готова к измерениям
    
else if(digitalRead(ir_right) == HIGH && digitalRead(ir_left) == HIGH && flagLeft == && flagRight == 0) {
      
// если была нажата кнопка масштаба
      
if(scaleBouncer.update()){
        if(
scaleBouncer.read() == HIGH) {
          
selected_scale++;
          if(
selected_scale sizeof(scale)/sizeof(scale[0])-1){
            
selected_scale 0;
          }
        }
       }
      
// если была нажата кнопка единиц измерения
      
if (unitsBouncer.update()){
        if(
unitsBouncer.read() == HIGH) {
          
selected_unit++;
          if(
selected_unit sizeof(units)/sizeof(units[0])-1){
            
selected_unit 0;
          }
        }
      }
      
// если была нажата кнопка единиц измерения
      
if (unitsBouncer.update()){
        if(
unitsBouncer.read() == HIGH) {
          
selected_unit++;
          if(
selected_unit sizeof(units)/sizeof(units[0])-1){
            
selected_unit 0;
          }
        }
      }
      
char buffer[12];
      
String tmpStr dtostrf(0.00012decimalsbuffer);
      
lcd.setCursor(00);
      
lcd.print(tmpStr);
      
lcd.setCursor(130);
      
lcd.print(units[selected_unit][0]);
      
lcd.setCursor(131);
      
lcd.print(scale[selected_scale][0]);
      
lcd.setCursor(11);
      
lcd.print("READY      ");
    }
    
// если измерения не началось и сработал один из датчиков при этом нет флага второго датчика - начинаем замер
    
else if(timeStart == && ((digitalRead(ir_right) == LOW && flagLeft == 0) || (digitalRead(ir_left) == LOW && flagRight == 0))) {
      
timeStart millis();
      if(
digitalRead(ir_right) == LOW){
        
flagRight millis();
        
'>';
        
1;
      }
      else {
        
flagLeft millis();
        
'<';
        
11;
      }
      
lcd.setCursor(11);
      
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(0selected_scale);
      
EEPROM.update(1selected_unit);
      
lcd.setCursor(11);
      
lcd.print("RESULT     ");
      
calculateSpeed(selected_unitselected_scaledecimals);
    }
    
// если настало время рисовать новый символ при анимации
    
else if(flagAnimation millis()){
      
animation();
    }
  }


во втором варианте, (v3.10) выбор кнопкой от одного до трех знаков
http://www.youtube.com/watch?v=D-SIrYlvdGo

Arduino код:

/*
 scale speed meter v3.10 2016.09.14 13:14 GMT
 */
#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> 

// переменные, которые можно менять
int S 70//дистанция (расстояние между датчиками) в миллиметрах
int resultDelay 5000// время показа результата измерений в миллисекундах
int animationDelay 50// время задержки анимации в миллисекундах
unsigned long timeoutDelay 160000// время ожидания срабатывания второго датчика в миллисекундах

// флаги
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 selected_scale EEPROM.read(0); // ключ массива выбранного масштаба
int selected_unit EEPROM.read(1); // ключ массива выбранных единиц измерения
int selected_decimals EEPROM.read(2); // ключ массива точности отображения результата

// массивы, которые можно дополнять или изменять
const String scale[][2] = {{" HO""87"}, {" TT""120"}, {"  N""160"}}; // массив масштабов (отображение на экране, множитель)
const String units[][2] = {{"KPH""3.6"}, {"MPH""2.23694"}}; // массив единиц измерения (отображение на экране, коэффициент перевода из метров в секунду)
int decimals[] = {123}; // массив точности отображения результата.

LiquidCrystal lcd(456789); // (RS, E, DB4, DB5, DB6, DB7) инициализируем дисплей
int ir_left 2// вход лекого датчика
int ir_right 3// вход правого датчика
int scalePin 10// вход кноки выбора масштаба
int unitsPin 11// вход кнопки выбора единиц измерения скорости
int decimalsPin 12// вход кнопки выбора точночти отображения результатов

Bounce scaleBouncer Bounce(); // объект-обработчик антидребезга кнопки выбра масштаба
Bounce unitsBouncer Bounce(); // объект-обработчик антидребезга кнопки выбора единиц измерения
Bounce decimalsBouncer Bounce(); // объект-обработчик антидребезга кнопки выбора точночти отображения результата

// переменные для анимации
int r;
char c;

void setup() {
  
lcd.begin(162);
  
pinMode(ir_leftINPUT);
  
pinMode(ir_rightINPUT);
  
pinMode(scalePinINPUT);
  
pinMode(unitsPinINPUT);
  
// параметры обработчика антидребезга
  
scaleBouncer.attach(scalePin);
  
scaleBouncer.interval(5);
  
unitsBouncer.attach(unitsPin);
  
unitsBouncer.interval(5);
  
decimalsBouncer.attach(decimalsPin);
  
decimalsBouncer.interval(5);
}

// расчет скорости
void calculateSpeed(int selected_unitint selected_scaleint selected_decimals){
  
= (timeStop timeStart);
  
1000.0 T;
  
sV units[selected_unit][1].toFloat() * scale[selected_scale][1].toInt() * 0.001;
  
char buffer[12];
  
String tmpStr dtostrf(sV12decimals[selected_decimals], buffer);
  
lcd.setCursor(00);
  
lcd.print(tmpStr);
  
flagShow millis() + resultDelay;
  
timeStart 0;
  
timeStop 0;
}

//рисуем бегущую строку
void animation() {
  
lcd.setCursor(r1);
  
lcd.print(c);
  if(
flagRight 0) {
    
r++;
    if(
11){
      
1;
      if(
== '>') {
        
' ';
      }
      else {
        
'>';
      }
    }
  }
  else if(
flagLeft 0) {
    
r--;
    if(
1){
      
11;
      if(
== '<') {
        
' ';
      }
      else {
        
'<';
      }
    }
  }
  
flagAnimation millis() + animationDelay;
}

void loop() {
  if(
selected_scale sizeof(scale)/sizeof(scale[0])-1) {
    
// если считанное значение больше, чем количество элементов в массиве масштабов - обнуляем значение
    
selected_scale 0;
    
EEPROM.write(00);
  }
  if(
selected_unit sizeof(units)/sizeof(units[0])-1) {
    
// если считанное значение больше, чем количество элементов в массиве единиц измерения скоростей - обнуляем значение
    
selected_unit 0;
    
EEPROM.write(10);
  }
  if(
selected_decimals sizeof(decimals)/sizeof(int)-1) {
    
// если считанное значение больше, чем количество элементов в массиве точночти отображения результата - обнуляем значение
    
selected_decimals 0;
    
EEPROM.write(20);
  }
  
// если флаг отображения результатов меньше текущего времени выполняем код, в противном случае продолжаем отображать результаты измерений
  
if(flagShow millis()) {
    
flagShow 0;
    
// если сработал один датчик, но за время timeoutDelay не сработал второй - выводим TIME OUT
    
if(timeStart && timeStart timeoutDelay millis()) {
      
lcd.clear();
      
lcd.setCursor(00);
      
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 == && flagRight == 0) {
      
lcd.clear();
      
lcd.setCursor(00);
      
lcd.print("     ERROR      ");
      
flagShow millis() + 1000;
    }
    
// если нет сигнала от обоих датчиков и не выставлены флаги обоих датчиков - система готова к измерениям
    
else if(digitalRead(ir_right) == HIGH && digitalRead(ir_left) == HIGH && flagLeft == && flagRight == 0) {
      
// если была нажата кнопка масштаба
      
if(scaleBouncer.update()){
        if(
scaleBouncer.read() == HIGH) {
          
selected_scale++;
          if(
selected_scale sizeof(scale)/sizeof(scale[0])-1){
            
selected_scale 0;
          }
        }
       }
      
// если была нажата кнопка единиц измерения
      
if (unitsBouncer.update()){
        if(
unitsBouncer.read() == HIGH) {
          
selected_unit++;
          if(
selected_unit sizeof(units)/sizeof(units[0])-1){
            
selected_unit 0;
          }
        }
      }
      
// если была нажата кнопка единиц измерения
      
if (unitsBouncer.update()){
        if(
unitsBouncer.read() == HIGH) {
          
selected_unit++;
          if(
selected_unit sizeof(units)/sizeof(units[0])-1){
            
selected_unit 0;
          }
        }
      }
      
// если была нажата кнопка выбора точночти отображения результата
      
if (decimalsBouncer.update()){
        if(
decimalsBouncer.read() == HIGH) {
          
selected_decimals++;
          if(
selected_decimals sizeof(decimals)/sizeof(int)-1){
            
selected_decimals 0;
          }
        }
      }
      
char buffer[12];
      
String tmpStr dtostrf(0.00012decimals[selected_decimals], buffer);
      
lcd.setCursor(00);
      
lcd.print(tmpStr);
      
lcd.setCursor(130);
      
lcd.print(units[selected_unit][0]);
      
lcd.setCursor(131);
      
lcd.print(scale[selected_scale][0]);
      
lcd.setCursor(11);
      
lcd.print("READY      ");
    }
    
// если измерения не началось и сработал один из датчиков при этом нет флага второго датчика - начинаем замер
    
else if(timeStart == && ((digitalRead(ir_right) == LOW && flagLeft == 0) || (digitalRead(ir_left) == LOW && flagRight == 0))) {
      
timeStart millis();
      if(
digitalRead(ir_right) == LOW){
        
flagRight millis();
        
'>';
        
1;
      }
      else {
        
flagLeft millis();
        
'<';
        
11;
      }
      
lcd.setCursor(11);
      
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(0selected_scale);
      
EEPROM.update(1selected_unit);
      
EEPROM.update(2selected_decimals);
      
lcd.setCursor(11);
      
lcd.print("RESULT     ");
      
calculateSpeed(selected_unitselected_scaleselected_decimals);
    }
    
// если настало время рисовать новый символ при анимации
    
else if(flagAnimation millis()){
      
animation();
    }
  }



BR95009 14.09.2016 14:47

Цитата:

Сообщение от BNSF9399 (Сообщение 281777)
Ну и универсальности хочется... Добавил кнопку выбора количества знаков после запятой

Теперь осталось сделать, что бы все это менялось по хлопку. :)))

---------- Сообщение добавлено в 16:47 ---------- Предыдущие сообщение было в 16:42 ----------

А что LCD такой шняжный? C кучей проводков? Я купил такой же, тока I2C шный. 4 провода: GND, Vcc, SCL, SDA. Все.

http://www.elecrow.com/wiki/images/t...2c_lcd1602.png

BNSF9399 14.09.2016 17:04

Цитата:

Сообщение от BR95009 (Сообщение 281787)
А что LCD такой шняжный?

ну как у xu56857 был, такой и купил. Я уже потом вникать когда начал, про как у вас узнал.
с другой стороны, выводов хватает для решения поставленной задачи.

Цитата:

Сообщение от BR95009 (Сообщение 281787)
что бы все это менялось по хлопку

ну это уже излишество :)

мистер И.К.С. 14.09.2016 20:00

Цитата:

Сообщение от BR95009 (Сообщение 281787)
LCD такой шняжный? C кучей проводков? Я купил такой же, тока I2C шный.

экран то как раз одинаковый. Разница только в экономии, либо деньги, либо контакты на ардуине. Если контактов в проекте хватает, то легче припаять "лишние" 12 проводков вместо 4, чем переплачивать: дополнительная платка I2C, стоит почти как экран, а если припаяна китайцами, то как два экрана ;D

Qvan22 14.09.2016 22:04

Интересный проект! Скажите, а что вы думаете о варианте добавления такого "функционала" как измерение скорости на кругу. Все как в вашем видео на ютубе, где вы подстраиваете cv декодера, а ваш прибор будет помогать в калибровке?
Хотим посмотреть скорость на отрезке пути - используем оба датчика, хотим настроить декодер - используем один на кругу.
Даже "железную" часть не надо изменять, только дописать программу...

BR95009 14.09.2016 23:43

Цитата:

Сообщение от мистер И.К.С. (Сообщение 281824)
Разница только в экономии, либо деньги, либо контакты на ардуине.

Ахренеть! Без платки-100 руб, с платкой - 151! Вот это экономия!

BNSF9399 15.09.2016 10:54

Цитата:

Сообщение от Qvan22 (Сообщение 281835)
хотим настроить декодер - используем один на кругу.

а что мешает этим же спидометром на кругу мерять? если спидометра нет, мы математическим путем вычисляем скорость исходя их длины круга и времени его прохождения. а тут сразу получаем результат :)
я его не кругу и планирую использовать. замерил скорость - подстроил, пока локомотив проезжает круг и по "приезду" к спидометру он уже полным ходом с новыми настройками будет ехать и нам не надо будет пропускать круг.

---------- Сообщение добавлено в 10:54 ---------- Предыдущие сообщение было в 10:50 ----------

Цитата:

Сообщение от BR95009 (Сообщение 281857)
Без платки-100 руб, с платкой - 151! Вот это экономия!

меня всегда поражали такие сравнения :)
"да че там, 10 рублей или 15... копейки разница."
а теперь зайдем с другой стороны. 150 и 100 - это на 50% дороже! При том, что эти 50 рублей ну ни как не влияют на проект в целом, так как выводов контроллера вполне хватает для подключения дисплея напрямую. а на 50 рублей можно 20 кнопок купить или лишний ИК датчик.


Текущее время: 06:22. Часовой пояс GMT +3.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2024, vBulletin Solutions, Inc. Перевод: zCarot
Copyright © ModelldepO.ru 2006 -