класс титановый велосипед для тактовой кнопки.

Клапауций 999
Offline
Зарегистрирован: 06.06.2015
класс титановый велосипед для тактовой кнопки.
фильтр дребезга, отслеживание событий: нажатие, отпускание, двойное нажатие(doubleclick), нажато и удерживается в течении определённого времени, отпущено и неактивно в течении определённого времени.
Клапауций 999
Offline
Зарегистрирован: 06.06.2015
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// класс титановый велосипед для тактовой кнопки.
// фильтр дребезга, отслеживание событий: нажатие, отпускание, двойное нажатие(doubleclick), нажато и удерживается в течении определённого времени, отпущено и неактивно в течении определённого времени.

class BUTTON {
public:
//================================================================
static const byte bounce_              =   50; // длительность отслеживания дребезга.
static const byte doubleclick_         =  200; // длительность отслеживания двойного клика.
static const unsigned long timer_      = 5000; // длительность отслеживания неактивности.
static const unsigned int retention_   = 2000; // длительность отслеживания нажатия и удержания.
//================================================================
boolean click_down;
boolean click_up;
boolean doubleclick;
boolean timer;
boolean retention;
//=================================
unsigned long m;
boolean  p;
boolean  b;
boolean dc;
byte     c;
boolean  t;
boolean  r;
//=================================
byte _pb;
//=================================
BUTTON(byte pb) {
_pb = pb;
pinMode(_pb, INPUT);
digitalWrite(_pb, 1);
//====
click_down      = 0;
click_up        = 0;
doubleclick     = 0;
timer           = 0;
retention       = 0;
//====
m  =      millis();
p  = digitalRead(_pb);
b  =                0;
dc =                0;
c  =                0;
t  =                0;
r  =                0;
//====
}

void read() {
//=======================================================
unsigned long nm =      millis();
boolean       np = digitalRead(_pb);
//=================
boolean nb  = 0;
boolean ndc = 0;
boolean nt  = 0;
boolean nr  = 0;
//================
click_down  = 0;
click_up    = 0;
doubleclick = 0;
timer       = 0;
retention   = 0;
//=================
if (np != p) {p = np; m = nm; }
//=======================================================
if (nm - m > bounce_) {nb = 1;}
if (nm - m > doubleclick_) {ndc = 1;}
if (ndc != dc) {dc = ndc; if (dc == 1) {c = 0;}}
if (nb != b) {b = nb;
if (p == 0 && b == 0) {click_down = 1;
++c;      if (c == 2) {c = 0; doubleclick = 1;}
}
if (p == 1 && b == 1) {click_up = 1;}
}
//=======================================================
if (nm - m > timer_) {nt = 1;}
if (nt != t) {t = nt;
if (p == 1 && t == 1) {timer = 1;}
}
//=======================================================
if (nm - m > retention_) {nr = 1;}
if (nr != r) {r = nr;
if (p == 0 && r == 1) {retention = 1;}
}
//=======================================================
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

Клапауций 999
Offline
Зарегистрирован: 06.06.2015
класс генерит события TRUE состояния переменных кнопки в течении одного цикла программы.
т.е. сохраняется дизайн поведения визуального восприятия обращения с кнопкой: что-то сделал один раз реальной кнопкой - что-то произошло один раз программно.
 
каждый объявленный объект класса работает со своим уникальным набором переменных:
click_down - приобретает значение TRUE при нажатии.
click_up - приобретает значение TRUE при отпускании.
doubleclick - приобретает значение TRUE при двойном нажатии(doubleclick).
timer - приобретает значение TRUE, если отпущено и нективно в течении определённого времени.
retention - приобретает значение TRUE, если нажато и удерживается в течении определённого времени.
Клапауций 999
Offline
Зарегистрирован: 06.06.2015

пример, объясняющий отличия работы с кнопкой классическим способом и с использованием предложенного класса:


void loop() {

// классический способ использования кнопки.
if (считываем логическое состояние пина кнопки ==  LOW) {что-то делаем при нажатой кнопке} // стоп! а, зачем что-то делается во время каждого цикла loop многократно, если мы нажали на кнопку только один раз?
if (считываем логическое состояние пина кнопки == HIGH) {что-то делаем при отпущенной кнопке} // так же непорядок - что-то делается многократно, не смотря на то, что событие отпускания кнопки произошло ровно один раз.

// использование кнопки с классом - всё корректно: одно физическое событие генерит одно программное событие.
if (нажатие == TRUE) {что-то делаем при нажатой кнопке}
if (отпускание == TRUE) {что-то делаем при отпущенной кнопке}
if (двойное нажатие == TRUE) {что-то делаем при двойном нажатии}
if (удержание == TRUE) {что-то делаем, если нажато и удерживается в течении определённого времени}
if (неактивность == TRUE) {что-то делаем, если отпущено и неактивно в течении определённого времени}

}

 

Клапауций 999
Offline
Зарегистрирован: 06.06.2015

пример скетча с использованием класса:

//~~~~~~~~~~~~~~~~~~~~
// здесь код приведённого выше класса class BUTTON {}; 
//~~~~~~~~~~~~~~~~~~~~

BUTTON BUTTON_01(3); // объявляем объект класса BUTTON кнопку с именем BUTTON_01, подключенную к пину 3.
BUTTON BUTTON_02(4); // объявляем объект класса BUTTON кнопку с именем BUTTON_02, подключенную к пину 4.

void setup () {}

void loop() {

BUTTON_01.read(); // обновляем состояние переменных кнопки BUTTON_01.
BUTTON_02.read(); // обновляем состояние переменных кнопки BUTTON_02.

// работаем с переменными кнопки BUTTON_01.
if (BUTTON_01.click_down) {что-то делаем при нажатии}
if (BUTTON_01.click_up) {что-то делаем при отпускании}
if (BUTTON_01.doubleclick) {что-то делаем при двойном нажатии(doubleclick)}
if (BUTTON_01.timer) {что-то делаем, если кнопка отпущена и неактивна в течении определённого времени}
if (BUTTON_01.retention) {что-то делаем при нажатии и удержании в течении определённого времени}

// работаем с переменными кнопки BUTTON_02.
if (BUTTON_02.click_down) {}
if (BUTTON_02.click_up) {}
if (BUTTON_02.doubleclick) {}
if (BUTTON_02.timer) {}
if (BUTTON_02.retention) {}

}

UPD: упустил из вида очевидное - надеюсь, ползателям ясно, что эти переменные можно настраивать по своему хотению:

07 //================================================================
08 static const byte bounce_              =   50; // длительность отслеживания дребезга.
09 static const byte doubleclick_         =  200; // длительность отслеживания двойного клика.
10 static const unsigned long timer_      = 5000; // длительность отслеживания неактивности.
11 static const unsigned int retention_   = 2000; // длительность отслеживания нажатия и удержания.
12 //================================================================

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Щас 10-ка ставится, потому много не напишу с телефона, но
Имена локальных переменных - мама роди меня обратно. Автор через неделю забудет что это и для чего. Для либы - это смерть. Утрирую, но так и есть, имена переменных должны пониматься без переводчика. Далее, все, что есть в классе имеет доступ public. Блджад, так нельзя. Нужно исходить из мысли, что члены класса не должны меняться снаружи никогда, только методами set и т.п. А щас, что хочу, то и ворочу. Это неправильно.
private - все, что никто не увидит. protected - это увидят только здесь и в порождённое классе. public - увидят все. Типа так

maxi_10
Offline
Зарегистрирован: 05.01.2012

Огромное человеческое спасибо за данный класс. В своем новом проекте(новый контроллер для пивоварни) я полностью использую события данного класса.

Проект на стадии 50% готовности, кому интересно код прилагаю.

Проект разбит на 6 файлов.

// Last Relise - 05/08/2015 Alpha  //
// Last Relise - 19/08/2015 Betha  //
// Last Relise - 00/00/0000 Relise //
/////////////////////////////////////

// #define ENCODER_DO_NOT_USE_INTERRUPTS

#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <EEPROM.h>
#include <Encoder.h>
#include <OneWire.h>
#include <LiquidCrystal_I2C.h>
#include <DS1307.h>
#include <RTClib.h>
#include <PID_v1.h>
#include <IniFile.h>
#include <TimeHelpers.h>

#define ONE_WIRE_BUS 7    // Шина датчика температуры
#define SD_SELECT 4       // Шина CS SD карты
#define ButtonEnc 8       // Кнопка энкодора
#define TEN1 13           // Порт шины нагревательного элемента 1
#define UPGRADE 1000      // Частота обновления главного экрана в милисикундах

//``````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
byte type_s;               // Создание переменной типа датчика измерения температуры
byte data[12];             // Создание переменной данных датчика температуры
byte addr[8];              // Создание переменной адреса датчика температуры
//
long Pos;                  // Создание переменной нового положения энкодера
long position;             // Создание переменной текущего положения энкодера
long NeoTime = 0;           // Создание переменной хранения значения unix времени конца выполнения программы
//
const size_t bufferLen = 30; // Создания константы размера буфера строк читаемых из ini файла
//
char buffer[bufferLen];    // Создания буфера строк читаемых из ini файла
//
double Setpoint;           // Создание переменной устанавливаемой температуры (и передача данных из памяти)
double Input;              // Создание переменной входящих данных в ПИД регулятор 
double Output;             // Создание переменной исходящих данных из ПИД регулятора
double consKp = 5;         // Создание переменных констант ПИД регулятора
double consKi = 75;        // Создание переменных констант ПИД регулятора
double consKd = 1;         // Создание переменных констант ПИД регулятора
//
String FileiniNAMEAn;
String FileiniNAME;
//                           0         1         2         3         4         5         6         7         8         9
const String Prog[10] = {"PROG_1", "PROG_2", "PROG_3", "PROG_4", "PROG_5", "PROG_6", "PROG_7", "PROG_8", "PROG_9", "PROG_10"};
int Pat[10][3]        = {{20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0} };
int prog = 0;
int mem1 = 0;
int mem2 = 0;
int mem3 = 0;
int Nasos_Start = 30;
int Nasos_Stop  = 80;
int PID_SPEED = 3;
int _HR = 0;                // Создание переменной хранения значения часов
int _MI = 0;                // Создание переменной хранения значения минут
int _SE = 0;                // Создание переменной хранения значения секунд
int A_H = 0;                // Создание переменной хранения значения часов при изменении времени
int A_M = 0;                // Создание переменной хранения значения минут при изменении времени
int maxMenu = 40;           // Отсчет от 0 (количество пунктов -1).
int curMenu = 0;            // Значение текущего пункта меню.
int Moschnost;              // Создание переменной мощности нагревательного элемента
int T_Upr = 0;              // Режим работы.
int P_Numb = 0;             // Создание переменной определения пункта масива програмируемых периодов (общего назначения)
int R_Numb = 0;             // Создание переменной определения пункта масива програмируемых периодов (отрисовка меню)
long unixt;                 // Создание переменной хранения текущего unix времени
long Sec;                   // Создание переменной временивыполнения программы
//
float Mic1 = 0;             // Переменные измерения производительности
float Mic2 = 0;             // Переменные измерения производительности
float celsius;              // Создание переменной значения измерений датчика температуры
//
boolean StartTime = 0;      // Создание переменной начала работы по заданному переоду (Р1 - Р9)
boolean Rabota = 1;         // Создание переменной дополнительная переменная управления переключением периодов (Р1 - Р9)
boolean Alarm = 0;          // Создание переменной значения сигнализации
boolean chgTemp = 0;        // Создание переменной флага изменения значения температуры 
boolean flag = 0;           // Создание переменной флага управления пьезо излучателем
boolean rasTempBol = 0;     // Создание переменной флага необходимости расчета времени
boolean tr_menu = false;    // Признак входа в меню настройки.
boolean dir = false;        // Признак переключения между вводом данных и листанием меню.
boolean keyPres = false;    // Признак исплнния клика по кнопке.
boolean STATUS = false;     // Состояние предохранителя (программа включена/выключенна)
boolean Udate = true;       // Обновление основного экрана
boolean SD_stat = false;    // Определяет наличие или отсутствие SD карты.
//``````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````

RTC_DS1307 rtc;                                                        // Инициализация Часов точного времени
OneWire  ds(ONE_WIRE_BUS);                                             // Инициализация шины датчика измерения температуры
LiquidCrystal_I2C lcd(0x27, 20, 4);                                    // Инициализация дисплея
DateTime now;                                                          // Создания объекта Часов точного времени
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT); // Инициализация ПИД регулятора
Encoder myEnc(2, 3);
File root;
File entry;


class BUTTON {
public:
//================================================================
static const byte bounce_              =    50; // длительность отслеживания дребезга.
static const byte doubleclick_         =   200; // длительность отслеживания двойного клика.
static const unsigned int retention_   =  2000; // длительность отслеживания нажатия и удержания.
static const unsigned long timer_      = 60000; // длительность отслеживания неактивности.
//================================================================
boolean click_down;
boolean click_up;
boolean doubleclick;
boolean timer;
boolean retention;
//=================================
unsigned long m;
boolean  p;
boolean  b;
boolean dc;
byte     c;
boolean  t;
boolean  r;
//=================================
byte _pb;
//=================================
BUTTON(byte pb) {
_pb = pb;
pinMode(_pb, INPUT);
digitalWrite(_pb, 1);
//====
click_down      = 0;
click_up        = 0;
doubleclick     = 0;
timer           = 0;
retention       = 0;
//====
m  =      millis();
p  = digitalRead(_pb);
b  =              0;
dc =              0;
c  =              0;
t  =              0;
r  =              0;
//====
}

void read() {
//=======================================================
unsigned long nm =      millis();
boolean       np = digitalRead(_pb);
//=================
boolean nb  = 0;
boolean ndc = 0;
boolean nt  = 0;
boolean nr  = 0;
//================
click_down  = 0;
click_up    = 0;
doubleclick = 0;
timer       = 0;
retention   = 0;
//=================
if (np != p) {p = np; m = nm; }
//=======================================================
if (nm - m > bounce_) {nb = 1;}
if (nm - m > doubleclick_) {ndc = 1;}
if (ndc != dc) {dc = ndc; if (dc == 1) {c = 0;}}
if (nb != b) {b = nb;
if (p == 0 && b == 0) {click_down = 1;
++c;      if (c == 2) {c = 0; doubleclick = 1;}
}
if (p == 1 && b == 1) {click_up = 1;}
}
//=======================================================
if (nm - m > timer_) {nt = 1;}
if (nt != t) {t = nt; if (p == 1 && t == 1) {timer = 1;}}
//=======================================================
if (nm - m > retention_) {nr = 1;}
if (nr != r) {r = nr; if (p == 0 && r == 1) {retention = 1;}}
//=======================================================
}};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

BUTTON BUTTON_01(ButtonEnc);

/////////////////////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
void clearLine(int line){if (line <= 3 && line >= 0){lcd.setCursor(0, line); lcd.print("                   "); lcd.setCursor(0, line);} else {lcd.clear();}}
void Enc_Zero(int ps = 0){ if (ps == 0) {myEnc.write(0);} else {ps = ps*4; myEnc.write(ps);} position = Pos;}
void EnterCHG(){if (tr_menu == true) {if (dir == true) {Enc_Zero(curMenu); menu(curMenu);} else {lcd.setCursor(13, 0); lcd.print("Chenge"); Enc_Zero(0); ChgData();} dir = !dir;}}
/////////////////////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

int s; // удалить временная парковочная переменная

void setup()
{
  Serial.begin(115200);
//  SavePAT();
  LoadPAT();
  rtc.begin();
  lcd.init(); lcd.backlight();

  pinMode(TEN1, OUTPUT);
  pinMode(ButtonEnc, INPUT); digitalWrite(ButtonEnc, HIGH);

  myPID.SetMode(AUTOMATIC);
  delay(500);
  
  if (!SD.begin(SD_SELECT)) {lcd.setCursor(0, 0); lcd.print("SD init - failed!");} // Тестирование SD карты (карта отсутствует)
  else        {lcd.setCursor(0, 0); lcd.print("SD  init - ok"); root = SD.open("/"); root.rewindDirectory(); SD_stat = true;} // Тестирование SD карты (карта на месте, исправна)
  delay(1000); lcd.setCursor(0, 1); lcd.print("CPU init - ok"); // Приятный красивый мультик (несёт только эстетический хорактер)
  delay(1000); lcd.setCursor(0, 2); lcd.print("SYS init - ok"); // Приятный красивый мультик (несёт только эстетический хорактер)
  delay(1000); lcd.setCursor(0, 3); lcd.print("PID init - ok"); // Приятный красивый мультик (несёт только эстетический хорактер)
  delay(2000);  
  lcd.clear();
}

void loop(){
  Mic1 = millis();
  now = rtc.now(); _HR = now.hour(); _MI = now.minute(); _SE = now.second();
  getTemp();
  Input = celsius;                       // Конвертация типов переменных на вход ПИД регулятора.
  DO_EVERY(PID_SPEED,{ myPID.Compute(); });
  Pos = myEnc.read() / 4;
  Moschnost = map(Output, 0, 255, 0, 100);
    
  if (STATUS) {analogWrite(TEN1, Output);} else {analogWrite(TEN1, 0); Moschnost = 0;}
  
//--------------------------------------------------------  
      BUTTON_01.read();
  if (BUTTON_01.timer)       {lcd.noBacklight(); tr_menu = false; lcd.noBlink(); lcd.clear(); Enc_Zero(0); keyPres = false;} 
  if (BUTTON_01.click_down)  {keyPres = true; lcd.backlight();}
  if (BUTTON_01.retention)   {lcd.backlight(); tr_menu = !tr_menu; if (tr_menu == true) {Enc_Zero(0); menu(0); lcd.blink();} else {lcd.noBlink(); lcd.clear(); Enc_Zero(0); SavePAT();} keyPres = false;}
  if (BUTTON_01.doubleclick) {if (tr_menu == false && position == 1){STATUS = true; Enc_Zero(0);} else {STATUS = false;} Udate = true;}
  if (BUTTON_01.click_up)    {if (keyPres == true) {EnterCHG();} keyPres = false;}
//--------------------------------------------------------

  if (tr_menu == false){ DO_EVERY(UPGRADE,{Udate = true;}); menu(99); }
  
  if (Pos != position) {position = Pos; lcd.backlight();
    if (tr_menu == false)                 {if (position > 1) {Enc_Zero(0);} else if (position < 0) {Enc_Zero(1);} Print_ctrl_main();}
    if (tr_menu == true && dir == false) {if (position > maxMenu) {Enc_Zero(0);} else if (position < 0) {Enc_Zero(maxMenu);} menu(position);}
    if (tr_menu == true && dir == true)  {ChgData();}  
  }

//``````````````````````````````````````````
Mic2 = 1000 / (millis() - Mic1); // Вычисление количества операций(циклов) в секунду 
Serial.print(" CPU-"); Serial.print(Mic2); Serial.print("Lps"); Serial.print(" Status-"); Serial.print(STATUS); Serial.print(" Output-"); Serial.println(Output);
//``````````````````````````````````````````
} // end Loop
void ChgData(){
        if (curMenu == 0) {if (position >= 0 && position <= 3)    {s = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 1) {if (position >= 0 && position <= 100)  {s = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 2) {if (position >= 0 && position <= 100)  {s = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} s = position; lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 3) {lcd.setCursor(6, 1); lcd.print("             "); CHGFile(); lcd.setCursor(6, 1); lcd.print(FileiniNAME); lcd.setCursor(0, 1); lcd.blink();}
        if (curMenu == 4) {ReadINI(); lcd.setCursor(8, 3); lcd.print("ok"); lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 5) {if (position >= 1 && position <= 100)  {PID_SPEED = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 1); lcd.blink();}
        if (curMenu == 6) {if (position >= 0 && position <= 100)  {consKi = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 7) {if (position >= 0 && position <= 100)  {Setpoint = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 8) {if (position >= 0 && position <= 24)   {A_H = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 1); lcd.blink();}
        if (curMenu == 9) {if (position >= 0 && position <= 59)   {A_M = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 10){rtc.adjust(DateTime(2015, 8, 19, A_H, A_M, 0)); lcd.setCursor(12, 3); lcd.print("ok"); lcd.setCursor(0, 3); lcd.blink();}

        if (curMenu == 11) {if (position >= 0 && position <= 100)   {Pat[0][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 12) {if (position >= 0 && position <= 32000) {Pat[0][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 13) {if (position >= 0 && position <= 1)     {Pat[0][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 14) {if (position >= 0 && position <= 100)   {Pat[1][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 15) {if (position >= 0 && position <= 32000) {Pat[1][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 16) {if (position >= 0 && position <= 1)     {Pat[1][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 17) {if (position >= 0 && position <= 100)   {Pat[2][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 18) {if (position >= 0 && position <= 32000) {Pat[2][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 19) {if (position >= 0 && position <= 1)     {Pat[2][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 20) {if (position >= 0 && position <= 100)   {Pat[3][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 21) {if (position >= 0 && position <= 32000) {Pat[3][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 22) {if (position >= 0 && position <= 1)     {Pat[3][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 23) {if (position >= 0 && position <= 100)   {Pat[4][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 24) {if (position >= 0 && position <= 32000) {Pat[4][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 25) {if (position >= 0 && position <= 1)     {Pat[4][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 26) {if (position >= 0 && position <= 100)   {Pat[5][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 27) {if (position >= 0 && position <= 32000) {Pat[5][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 28) {if (position >= 0 && position <= 1)     {Pat[5][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 29) {if (position >= 0 && position <= 100)   {Pat[6][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 30) {if (position >= 0 && position <= 32000) {Pat[6][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 31) {if (position >= 0 && position <= 1)     {Pat[6][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 32) {if (position >= 0 && position <= 100)   {Pat[7][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 33) {if (position >= 0 && position <= 32000) {Pat[7][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 34) {if (position >= 0 && position <= 1)     {Pat[7][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 35) {if (position >= 0 && position <= 100)   {Pat[8][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 36) {if (position >= 0 && position <= 32000) {Pat[8][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 37) {if (position >= 0 && position <= 1)     {Pat[8][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 38) {if (position >= 0 && position <= 100)   {Pat[9][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 39) {if (position >= 0 && position <= 32000) {Pat[9][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 40) {if (position >= 0 && position <= 1)     {Pat[9][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
}

void CHGFile(){
    entry = root.openNextFile(); if (!entry) { root.rewindDirectory(); }
    FileiniNAMEAn = entry.name();
    if (FileiniNAMEAn == "" or FileiniNAMEAn == "SYSTEM~1") {entry = root.openNextFile();}
    else {FileiniNAME = entry.name();}}

void ReadINI(){
if (SD_stat){
  char Str2[FileiniNAME.length()+1]; FileiniNAME.toCharArray(Str2, FileiniNAME.length()+1);
  IniFile ini(Str2);
 
  if (!ini.open()) {clearLine(0); lcd.print("Ini file does not exist"); while (1);}
  if (!ini.validate(buffer, bufferLen)) {clearLine(0); lcd.print("ini file "); lcd.print(ini.getFilename()); lcd.print(" not valid"); while (1);}
//--------------------------------------------------------------------  
  if (ini.open()) { /* clearLine(0); lcd.print("Load: "); lcd.print(ini.getFilename()); */ }
  if (ini.getValue("NAME",  "NAME",      buffer, bufferLen)) {if (curMenu == 4) {clearLine(2); lcd.setCursor(1, 2); lcd.print(buffer);}} //else {clearLine(0); lcd.print("Default Error file");}
  if (ini.getValue("PID",   "consKp",    buffer, bufferLen)) {consKp      = String(buffer).toInt();}
  if (ini.getValue("PID",   "consKi",    buffer, bufferLen)) {consKi      = String(buffer).toInt();}
  if (ini.getValue("PID",   "consKd",    buffer, bufferLen)) {consKd      = String(buffer).toInt();}
  if (ini.getValue("PID",   "PID_SPEED", buffer, bufferLen)) {PID_SPEED   = String(buffer).toInt();}
  if (ini.getValue("NASOS", "start",     buffer, bufferLen)) {Nasos_Start = String(buffer).toInt();}
  if (ini.getValue("NASOS", "stop",      buffer, bufferLen)) {Nasos_Stop  = String(buffer).toInt();}
 
  for (int mesMas=0; mesMas <= 9; mesMas++){
      char Str1[Prog[mesMas].length()+1]; Prog[mesMas].toCharArray(Str1, Prog[mesMas].length()+1);
      if (ini.getValue(Str1, "P_Temp",    buffer, bufferLen)) {Pat[mesMas][0]   = String(buffer).toInt();}
      if (ini.getValue(Str1, "P_Time",    buffer, bufferLen)) {Pat[mesMas][1]   = String(buffer).toInt();}
      if (ini.getValue(Str1, "P_Alar",    buffer, bufferLen)) {Pat[mesMas][2]   = String(buffer).toInt();}
  }
  ini.close();
  entry.close();
//--------------------------------------------------------------------
}}
void LoadPAT(){
  INI_pat_mem();
  for (int pr=50; pr <= 59; pr++){
    prog = pr - 50;
    mem1 = pr;
    mem2 = pr + 10;
    mem3 = pr + 20;
    Pat[prog][0] = EEPROM.read(mem1); Pat[prog][1] = EEPROM.read(mem2); Pat[prog][2] = EEPROM.read(mem3);
  }
  T_Upr     = EEPROM.read(111);
  consKi    = EEPROM.read(112);
  PID_SPEED = EEPROM.read(113);
  myPID.SetTunings(consKp, consKi, consKd);
}

void SavePAT(){
  INI_pat_mem();
  for (int pr=50; pr <= 59; pr++){
    prog = pr - 50;
    mem1 = pr;
    mem2 = pr + 10;
    mem3 = pr + 20;
    EEPROM.write(mem1, Pat[prog][0]); EEPROM.write(mem2, Pat[prog][1]); EEPROM.write(mem3, Pat[prog][2]);
  }
    if (EEPROM.read(111) != T_Upr)     { if (T_Upr != 3){ EEPROM.write(111, T_Upr); }                            } // Сохранение значения режима работы
    if (EEPROM.read(112) != consKi)    { EEPROM.write(112, consKi); myPID.SetTunings(consKp, consKi, consKd);    } // Сохранение значения режима consKi PID регулятора
    if (EEPROM.read(113) != PID_SPEED) { EEPROM.write(113, PID_SPEED); myPID.SetTunings(consKp, consKi, consKd); } // Сохранение значения режима SPEED PID регулятора
}

void INI_pat_mem(){
  prog = 0; mem1 = 0; mem2 = 0; mem3 = 0;
}
void getTemp() {
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);
  ds.reset();
  ds.select(addr);
  ds.write(0xBE);
  for ( int i = 0; i < 9; i++) {data[i] = ds.read();}
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3;
    if (data[7] == 0x10) {raw = (raw & 0xFFF0) + 12 - data[6];}
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw & ~7;
    else if (cfg == 0x20) raw = raw & ~3;
    else if (cfg == 0x40) raw = raw & ~1;
  }
  celsius = (float)raw / 16.0;
  return;
}

void menu(int lvl){
  if (lvl == 0 || lvl == 1 || lvl == 2){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 0 "); 
      lcd.setCursor(0, 1); lcd.print(" -"); lcd.setCursor(12, 1); lcd.print("");
      lcd.setCursor(0, 2); lcd.print(" -"); lcd.setCursor(12, 2); lcd.print("");
      lcd.setCursor(0, 3); lcd.print(" -"); lcd.setCursor(12, 3); lcd.print("");
      switch (lvl){case 0:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 1:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 2:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 3 || lvl == 4){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 1 ");
      lcd.setCursor(0, 1); lcd.print(" File:"); lcd.print(FileiniNAME);
      if (!SD_stat) {lcd.setCursor(0, 2); lcd.print(" SD init - failed!");}
      lcd.setCursor(0, 3); lcd.print(" Load"); 
      switch (lvl){case 3:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 4:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 5 || lvl == 6 || lvl == 7 ){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 2 ");
      lcd.setCursor(0, 1); lcd.print(" PID_SPEED "); lcd.setCursor(12, 1); lcd.print(PID_SPEED);
      lcd.setCursor(0, 2); lcd.print(" consKi ");    lcd.setCursor(12, 2); lcd.print(consKi);
      lcd.setCursor(0, 3); lcd.print(" Setpoint ");  lcd.setCursor(12, 3); lcd.print(Setpoint);
      switch (lvl){case 5:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 6:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 7:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 8 || lvl == 9 || lvl == 10 ){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 3 ");
      lcd.setCursor(0, 1); lcd.print(" hour");   lcd.setCursor(12, 1); lcd.print(A_H);
      lcd.setCursor(0, 2); lcd.print(" minute"); lcd.setCursor(12, 2); lcd.print(A_M);
      lcd.setCursor(0, 3); lcd.print(" set clock"); 
      switch (lvl){case 8: lcd.setCursor(0, 1); lcd.blink(); break;
                   case 9: lcd.setCursor(0, 2); lcd.blink(); break;
                   case 10:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
//```````````````````````````````````````````````````````````````````````````````````````````
  if (lvl == 11 || lvl == 12 || lvl == 13){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.0 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[0][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[0][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[0][2]);
      switch (lvl){case 11:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 12:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 13:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 14 || lvl == 15 || lvl == 16){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.1 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[1][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[1][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[1][2]);
      switch (lvl){case 14:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 15:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 16:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 17 || lvl == 18 || lvl == 19){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.2 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[2][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[2][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[2][2]);
      switch (lvl){case 17:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 18:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 19:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 20 || lvl == 21 || lvl == 22){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.3 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[3][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[3][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[3][2]);
      switch (lvl){case 20:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 21:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 22:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 23 || lvl == 24 || lvl == 25){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.4 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[4][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[4][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[4][2]);
      switch (lvl){case 23:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 24:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 25:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 26 || lvl == 27 || lvl == 28){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.5 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[5][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[5][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[5][2]);
      switch (lvl){case 26:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 27:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 28:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 29 || lvl == 30 || lvl == 31){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.6 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[6][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[6][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[6][2]);
      switch (lvl){case 29:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 30:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 31:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 32 || lvl == 33 || lvl == 34){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.7 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[7][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[7][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[7][2]);
      switch (lvl){case 32:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 33:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 34:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 35 || lvl == 36 || lvl == 37){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.8 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[8][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[8][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[8][2]);
      switch (lvl){case 35:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 36:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 37:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 38 || lvl == 39 || lvl == 40){ lcd.clear(); curMenu = lvl;
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.9 "); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[9][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[9][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[9][2]);
      switch (lvl){case 38:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 39:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 40:lcd.setCursor(0, 3); lcd.blink(); break;}
  }

//```````````````````````````````````````````````````````````````````````````````````````````  
  if (lvl == 99){ 

    if (Udate){ Udate = false;
      lcd.setCursor(0, 0); lcd.print("T.ust "); lcd.print(Setpoint); if (STATUS) {lcd.setCursor(17, 0); lcd.print("RUN");} else {lcd.setCursor(17, 0); lcd.print("OFF");}
      lcd.setCursor(0, 1); lcd.print("T.zid "); lcd.print(Input);
      lcd.setCursor(0, 2); lcd.print("P.ten "); 
     
      if (Moschnost <= 100) {lcd.setCursor(6, 2);lcd.print(Moschnost); lcd.print("% ");}
      if (Moschnost < 10)   {lcd.setCursor(6, 2);lcd.print(Moschnost); lcd.print("%  ");}
      
      Print_ctrl_main();
 
      lcd.setCursor(0, 3); if (_HR < 10) {lcd.print("0"); lcd.print(_HR);} else {lcd.print(_HR);} lcd.print(":"); 
                           if (_MI < 10) {lcd.print("0"); lcd.print(_MI);} else {lcd.print(_MI);} lcd.print(":"); 
                           if (_SE < 10) {lcd.print("0"); lcd.print(_SE);} else {lcd.print(_SE);}
    }
  }
}

void Print_ctrl_main(){
      if (tr_menu == false && position == 0) {lcd.setCursor(14, 1); lcd.print(">>STOP"); 
                                              lcd.setCursor(14, 2); lcd.print("  WORK");}
                                              
      if (tr_menu == false && position == 1) {lcd.setCursor(14, 1); lcd.print("  STOP"); 
                                              lcd.setCursor(14, 2); lcd.print(">>WORK");}  
}

 

vvadim
Offline
Зарегистрирован: 23.05.2012

попробовал и я велосипед

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// класс титановый велосипед для тактовой кнопки.
// фильтр дребезга, отслеживание событий: нажатие, отпускание, двойное нажатие(doubleclick), нажато и удерживается в течении определённого времени, отпущено и неактивно в течении определённого времени.

class BUTTON {
public:
//===================================================================================================
static const byte bounce_              =   50; // длительность отслеживания дребезга.
static const byte doubleclick_         =  200; // длительность отслеживания двойного клика.
static const unsigned long timer_      = 10000; // длительность отслеживания неактивности.
static const unsigned int retention_   = 1000; // длительность отслеживания нажатия и удержания.
//===================================================================================================
boolean click_down;
boolean click_up;
boolean doubleclick;
boolean timer;
boolean retention;
//=================================
unsigned long m;
boolean  p;
boolean  b;
boolean dc;
byte     c;
boolean  t;
boolean  r;
//=================================
byte _pb;
//=================================
BUTTON(byte pb) {
_pb = pb;
pinMode(_pb, INPUT);
digitalWrite(_pb, 1);
//====
click_down      = 0;
click_up        = 0;
doubleclick     = 0;
timer           = 0;
retention       = 0;
//====
m  =      millis();
p  = digitalRead(_pb);
b  =                0;
dc =                0;
c  =                0;
t  =                0;
r  =                0;
//====
}

void read() {
//=======================================================
unsigned long nm =      millis();
boolean       np = digitalRead(_pb);
//=================
boolean nb  = 0;
boolean ndc = 0;
boolean nt  = 0;
boolean nr  = 0;
//================
click_down  = 0;
click_up    = 0;
doubleclick = 0;
timer       = 0;
retention   = 0;
//=================
if (np != p) {p = np; m = nm; }
//=======================================================
if (nm - m > bounce_) {nb = 1;}
if (nm - m > doubleclick_) {ndc = 1;}
if (ndc != dc) {dc = ndc; if (dc == 1) {c = 0;}}
if (nb != b) {b = nb;
if (p == 0 && b == 0) {click_down = 1;
++c;      if (c == 2) {c = 0; doubleclick = 1;}
}
if (p == 1 && b == 1) {click_up = 1;}
}
//=======================================================
if (nm - m > timer_) {nt = 1;}
if (nt != t) {t = nt;
if (p == 1 && t == 1) {timer = 1;}
}
//=======================================================
if (nm - m > retention_) {nr = 1;}
if (nr != r) {r = nr;
if (p == 0 && r == 1) {retention = 1;}
}
//=======================================================
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

////////////////////////
BUTTON BUTTON_01(3);
////////////////////////

void setup() {

/////
pinMode(16, OUTPUT);
digitalWrite(16, 1);
pinMode(14, OUTPUT);
digitalWrite(14, 1);
pinMode(15, OUTPUT);
digitalWrite(15, 1);
pinMode(13, OUTPUT);
}

void loop()
{
 if (BUTTON_01.click_down) {digitalWrite(16, !digitalRead(16));}
 if (BUTTON_01.click_up) {}
 if (BUTTON_01.doubleclick) {digitalWrite(14, !digitalRead(14));}
 if (BUTTON_01.timer) {}
 if (BUTTON_01.retention) {digitalWrite(15, !digitalRead(15));}

BUTTON_01.read();
}

 

при двойном клике или удержании кнопки одиночное нажатие всё равно срабатывает, что иногда не приемлемо

Клапауций 666
Offline
Зарегистрирован: 10.11.2015

vvadim пишет:

при двойном клике или удержании кнопки одиночное нажатие всё равно срабатывает, что иногда не приемлемо

опиши мне словами алгоритм, который будет предсказывать будущее, например:
 
я нажал кнопку и... [здесь девайс смотрит в будущее и он точно знает, что]
 
я отпущу кнопку и больше ничего делать не буду - отработать нажатие.
я сделаю двойной клик - нажатие отрабатывать не нужно, отработать двойной клик.
я буду удерживать кнопку - нажатие отрабатывать не нужно, отработать удержание.
 
и я напишу код по этому алгоритму.
 
теоретическое обоснование дизайна поведения класса:
 
причиной событий, обрабатываемых кодом класса, является одиночное нажатие.
попытка скрыть причину(одиночное нажатие) и повлиять на непрерывность континиума пространство-время вызывает некорректную работу кода в реальном времени.
 
~~~~~~~~~~
*приглашаю в топик магов, колдунов и прорицателей всех мастей для разработки нового алгоритма работы класса.
*пользователям класса, обладающим логическим, а не мистическим мышлением - бесплатный попкорн.
~~~~~~~~~~
 

 

Клапауций 777
Offline
Зарегистрирован: 21.11.2015

в версии Дуино ИДЕ arduino-1.6.6 класс можно подключить как библиотеку:

#include <class_BUTTON.h>
- копируем код #1 в файл class_BUTTON.h
- копируем файл class_BUTTON.h в папку class_BUTTON.
- папку class_BUTTON размещаем в *\Мои документы\Arduino\libraries\

 

Клапауций 123
Offline
Зарегистрирован: 06.12.2015

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

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

если эта схема расово правильная, то почему большинство схем в гугле не имеют сопротивления R1? На что оно влияет?

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Резистор ограничивает ток через контакты кнопки при её нажимании.
Смотреть про второй закон коммутации:

https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D1%8B_%D0%B2_%D1%8D%D0%BB%D0%B5%D0%BA%D1%82%D1%80%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D1%86%D0%B5%D0%BF%D1%8F%D1%85

Хотя при 0.1 без резистора будет и  не  смертельно для кнопки...... какое-то время......
Поставьте для "пробы"   2200.0 - могут и контакты "свариться"
 

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

Tomasina пишет:

если эта схема расово правильная, то почему большинство схем в гугле не имеют сопротивления R1? На что оно влияет?

*большинство схем в гугле имеют сиськи.

R1 , если:

1. если ты по запарке что-то не то зальёшь в дуино и пин у тебя окажется оутпут.

2. см. #128

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

отлично. Беру на вооружение.

Mr.Privet
Mr.Privet аватар
Offline
Зарегистрирован: 17.11.2015

и еще один дурацкий вопрос - а конденсатор зачем? от скачков напряжения?

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

он забирает на себя дребезг контактов. Кое-что, конечно, прорывается, но для МК все же легче.

Клапауций 322
Offline
Зарегистрирован: 31.12.2015

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

исходные данные:
тактовая кнопка классической конструкции(далее "кнопка") - нормально разомкнутые контакты.
процесс дребезга кнопки начинается при нажатии кнопки и длится, обусловленное конструкцией кнопки, время.
процесс дребезга при отпускании кнопки физически невозможен.
 
решение:
программная фиксация события нажатия кнопки в течении времени дребезга.
 
*всё.
Клапауций 322
Offline
Зарегистрирован: 31.12.2015

Клапауций 777 пишет:

в версии Дуино ИДЕ arduino-1.6.6 класс можно подключить как библиотеку:

#include <class_BUTTON.h>
- копируем код #1 в файл class_BUTTON.h
- копируем файл class_BUTTON.h в папку class_BUTTON.
- папку class_BUTTON размещаем в *\Мои документы\Arduino\libraries\

*работает только в версии arduino-1.6.7

что бы работало в версиях младше вплоть до Arduino 1.0, нужно в код #1 добавить первой строкой

#include <Arduino.h>

**проверяем.

sirota
Offline
Зарегистрирован: 18.08.2015

Попробовал класс, понравилось. Но я так и не догнал как сделать повторяющиеся нажатия?

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

sirota пишет:

Но я так и не догнал как сделать повторяющиеся нажатия?

после нажатия кнопки, повторяешь действие.

ruka36-222x160.jpgruka36-222x160.jpg

MaksVV
Offline
Зарегистрирован: 06.08.2015

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

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

MaksVV пишет:

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

а, можно у тебя узнать алгоритм предсказания действий пользователя кнопки и попросить тебя прочитать топик полностью или начиная отсюда #100 ?

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

lelik1414 пишет:

Только вот одно не понятно, почему на хорошую вещь сначала пытаются нагадить.

никто особо и не гадил - вся критика и претензии были не по сути работы, а исключительно косметические:
1. профессионалы уволили меня нафиг из программистов с такими именами переменных и их принципиальной публичностью.
2. чайники, боялись попасть в АдЪ, юзая простой алгоритм подавления дребезга с блек-джеком, чистыми шлюхами и жёстким БДСМ причинно-следственными связями.
Клапауций 232
Offline
Зарегистрирован: 05.04.2016

3. всё оказалось совсем не так, как на самом деле.

желающим познать дзен - странный диалог Клапауций&Logik начало отсюда #62

*подписчикам, срочно отписаться - титановый велосипед подготавливается к переплавке в титановый костыль на три кнопки. :D

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

karl2233 пишет:

делаю зарядное для LI-Io и Ni-Mh ...

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

karl2233 пишет:

1. Подскажите, плз, где глянуть примеры с разными действиями на разное количество нажатий кнопки?

заведи переменную, считающую количество нажатий кнопки - будешь что-то делать не просто по условию нажатия, а по условию нажатия и значения этой переменной.

т.е.:


#include <class_BUTTON.h>

BUTTON BUTTON_01(3); // объявляем объект класса BUTTON кнопку с именем BUTTON_01, подключенную к пину 3.

byte tick_click_down_01 = 0; // счётчик нажатий кнопки BUTTON_01

void setup () {}

void loop() {

BUTTON_01.read(); // обновляем состояние переменных кнопки BUTTON_01.

// работаем с переменными кнопки BUTTON_01.
if (BUTTON_01.click_down) {++tick_click_down_01; if (tick_click_down_01 == 5) {tick_click_down_01 = 4;}} // подсчёт нажатий кнопки до 4.
if (BUTTON_01.timer) {tick_click_down_01 = 0;} // сброс счётчика нажатий в 0 после 5 секунд неактивности кнопки.

if (BUTTON_01.click_down && tick_click_down_01 == 1) {} // что-то делаем по событию первого нажатия.
if (BUTTON_01.click_down && tick_click_down_01 == 2) {} // что-то делаем по событию второго нажатия.
if (BUTTON_01.click_down && tick_click_down_01 == 3) {} // что-то делаем по событию третьго нажатия.
if (BUTTON_01.click_down && tick_click_down_01 == 4) {} // что-то делаем по событию червёртого нажатия.

}

karl2233 пишет:

2. Как организовать запрет опроса кнопки после того, как процесс будет запущен, т.е. на РВ3( Ардуино 2) будет лог 1 и Тини 13 не должна реагировать на нажатие кнопки.

#4

if (!условие) {BUTTON_01.read();} // обновляем состояние переменных кнопки BUTTON_01.

 

karl2233 пишет:

При паузе между нажатиями более 5 сек, фиксируется последнее состояние портов, которое можно изменить следующим нажатием кнопки (если на порту РВ3 лог 0).

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

если ты желаешь сбросить счётчик нажатий, то выше в примере реализовано. и настраивается 

static const unsigned long timer_    = 5000; // длительность отслеживания неактивности.

 

karl2233
Offline
Зарегистрирован: 05.07.2015

ооооо! Клапауций 232, гораздо понятнее стало.

спасибо!

а про тему я понял что ошибся, да уже ничё не исправить.

 

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Клапацуй 232, попробовал Ваш класс (после того как в своём обнаружил баг (при создании более одного экземпляра класса защита от дребезга не производится, в данный момент ищу причину).

Использовал для проверки следующий код

#include <BUTTON.h>
BUTTON BUTTON_01(2);
void setup () {
  pinMode(13,OUTPUT);
}
void loop() {
  BUTTON_01.read(); // обновляем состояние
  if (BUTTON_01.click_down) {
    digitalWrite(13,!digitalRead(13));
  }
}

К удевлению задержку между нажатием кнопки и реакцией на неё в течении длительности отслеживания дребезга я не обнаружил. В чем может быть проблемма? Код класса взят из поста #1.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

yul-i-an пишет:

К удевлению задержку между нажатием кнопки и реакцией на неё в течении длительности отслеживания дребезга я не обнаружил. В чем может быть проблемма? Код класса взят из поста #1

для обнаружения реакции на дребезг нужно симулировать ситуацию дребезга - нажатия кнопки чаще, чем static const byte bounce_:

для проверки вручную и визуально установи

static const byte bounce_              =   1000; // длительность отслеживания дребезга.

нажатия реже, чем один раз в секунду у тебя будут отрабатываться - чаще, чем один раз в секунду будут игнорироваться.

или сгенери аппаратным образом нажатие кнопки чаще, чем static const byte bounce_ = 50 при дефолтных настройках класса, если владеешь осциллографом. 

*код класса работает по следующему алгоритму - если время между нажатиями кнопки меньше static const byte bounce_ , то событие считается дребезгом и игнорируются, если больше, то нажатием и отрабатывается немедленно. 

** начиная отсюда #173 пытаюсь объяснить, почему так, а не иначе.

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

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

Начал с 50Гц и снижал частоту до 10Гц, и Ваш и мой класс отработали одинаково, вплоть до 11Гц на выходе у них ничего небыло, а вот на 10Гц и Ваш и мой начал переодически устанавливать выход т.к. длина импульса при частоте 10Гц около 50мс.

Так что оба решения состоятельны.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

yul-i-an пишет:

Так что оба решения состоятельны.

как же состоятельны, если

yul-i-an пишет:
К удевлению задержку между нажатием кнопки и реакцией на неё в течении длительности отслеживания дребезга я не обнаружил. В чем может быть проблемма? Код класса взят из поста #1.

далее, я тебе объяснил, что бабушку хоронят потому, что она умерла, а не потому, что выкопана могила.

GarryC
Offline
Зарегистрирован: 08.08.2016

Тогда лично я не очень понял, зачем Вы текст тут разместили, если юзать будете сами? Чтобы не забыть? Или все таки для того, чтобы им могли пользоваться другие? Если второй вариант, то тогда надо позаботится об удобстве будущих пользователей и добавить в описание класа одну строчку private: в нужном месте, а не становится в позицию оскорбленного справедливой критикой. 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Тогда лично я не очень понял, зачем Вы текст тут разместили, если юзать будете сами? Чтобы не забыть? Или все таки для того, чтобы им могли пользоваться другие? Если второй вариант, то тогда надо позаботится об удобстве будущих пользователей и добавить в описание класа одну строчку private: в нужном месте, а не становится в позицию оскорбленного справедливой критикой. 

и, кто мне это переведёт на русский язык?

кому я снова недодал личного внимания или недостаточно убедительно послал в пень?

GarryC
Offline
Зарегистрирован: 08.08.2016

Боюсь Вас огорчить, но послать меня достаточно убедительно в пень Вам удасться лет через 15-20 занятий встроенным программированием.

Есть такая очаровательная фраза "В Уставе караульной службы каждая строчка написана кровью людей, которые пытались делать по своему". Вы, похоже, решили пройти путь по хорошо освещенным граблям самостоятельно, в добрый путь.

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

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

Ни в коей мере не претендуя на личное внимание столь продвинутого программиста, тем не менее мог бы порекомендовать посмотреть исходные коды обработки событий мыши в древней системе Turbo Pascal, где то, что Вы считаете невозможным без заглядывания в будущее, реализуется вполне понятными и довольно таки тривиальными конструкциями.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Боюсь Вас огорчить...

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

- ты пишешь хороший код для работы с кнопкой и публикуешь его на этом форуме.

- ты исправляешь ошибки в код класс титановый велосипед для тактовой кнопки.

- ты идёшь в пень, как программист-пустомеля.

*кретинам, обзывающим меня программистом, повторяю: я - радиомонтажник.

Гриша
Offline
Зарегистрирован: 27.04.2014

Уважаемый GarryC, не сочтите за труд прочитать.

GarryC пишет:

Тогда лично я не очень понял, зачем Вы текст тут разместили, если юзать будете сами? ...то тогда надо позаботится об удобстве будущих пользователей и добавить в описание класа одну строчку private: в нужном месте.

Если Вы пришли к нам в "песочницу" из "конструкторского бюро", так помогите, если не затруднит. Если обратите внимание на попытки написать альтернативу этому коду, то увидите, что никто не довел дела до конца и/или его теория оказалась не состоятельной.

GarryC пишет:

Боюсь Вас огорчить, но послать меня достаточно убедительно в пень Вам удасться лет через 15-20 занятий встроенным программированием.

Мда, сколько таких тут мимо проходило, ни один не доказал своей состоятельности ДЕЛОМ.

GarryC пишет:

..., и советы по ее улучшению Вы категорически отвергаете, ..

Достаточно часто проглядываю топики, чего-то не увидел дельных советов.

GarryC пишет:

... Если Вы действительно считаете, что потрясающие в своей эффективности алгоритмы и необычные, не приходящие до сего момента никому в голову решения, примененные в Вашем чудесном коде, вызывают злопыхания завистников, который в бессильной злобе пытаются своими грязными лапами обеспечить Ваше прекрасное детище...

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

GarryC пишет:

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

Мог (я) бы сказать СПАСИБО, ЕСЛИ БЫ такой продвинутый человек за "спасибо" (как это сделал Клапауций 232) подарил БЫ сообществу сие чудо.

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

 /*
 этот код я написал для замка в деревне, чтоб за ключем не бегать. Если работаешь
 не далеко и кто-то пришел, он должен знать код. Устройсто втягивает защелку на замке.
 КОД необходимо набирать последовательно, дождавшись когда КРАСНЫЙ светик потухнет,
 иначе нажитие другой кнопки будет включено в КОД (комбинацию кнопок).
 Каждая кнопка имеет "вес" 1, 2, 4 и 8 соответственно, пока горит КРАСНЫЙ светодиод
 вводится КОД соответствующий зашитому в программе и установленному джамперами.
 Невозможно понять, ошибся ты или нет, пока не набраны 4 комбинации.
 Для повторного нажатия неоходимо дождаться пока потухнет подсветка...
 Ну не хватило у меня кнопок на полную клавиатуру :)))))))))))))))
 Если на А3 (тумблер в доме) лог 0 (тумблер включен) 
 при любой комбинации А0, А1 и А2 (выбор кода) работает "простой код"
силовой "полевик" выдран с компьютерной матери 18N06L (что было, то и ставим) 
 */
    #include <class_noDELAY.h>
    #include <class_BUTTON.h>
    
      // коды доступа :)))------------
    
    byte COD_A[8][5] = {     // [строка] [значение]
                        {0, 0, 0, 0, 0},   // нули. чтоб код не править по инициализации
                        {0, 1, 8, 2, 4},   // код при нажатом тумблере (простой код)
                        {0, 2, 2, 5, 5},   // первый код (по перемычкам кода)
                        {0, 2, 6, 8, 10},  // второй код и т.д.
                        {0, 10, 2, 8, 2},
                        {0, 1, 6, 8, 8},
    
                        };  
    
    //----------------------------
    
    
    noDELAY nD_01; // таймер 1
    noDELAY nD_02; // таймер 2
    noDELAY nD_011; // таймер 11
    noDELAY nD_022; // таймер 22    
    noDELAY nD_03; // таймер 3 - сброс незавершенного кода
    
    BUTTON BT_01(2); // кнопок 1 разряда
    BUTTON BT_02(3); // кнопок 2 разряда
    BUTTON BT_03(4); // кнопок 4 разряда
    BUTTON BT_04(5); // кнопок 8 разряда
    BUTTON BT_05(8); // кнопка в доме
    
    // A0, A1, A2 - джамперы выбора предустановленного кода
    // A3         - упрощенный код доступа 
    
    // an array of pin numbers to which BUTTON or jumper are attached
    int BUTTON_Pins[] = { 2, 3, 4, 5, 8, A0, A1, A2, A3 }; 
    int  i=0, j=0; 
    byte q1=0, q2=0;;                       // перебор кода, очередь нажатия
    byte Bt = 0;                            // код кнопок
    byte Bt1=0, Bt2=0, Bt3=0, Bt4=0, Bt5=0; // результат (факт) нажатия кнопок по весу
    byte AC0=0, AC1=0, AC2=0, AC3=0, AC4=0, AC5=0, AC6=0; // выбор кода
    boolean  Btc = 0;                 // признак обработки кода кнопок, 1 если еще не обработан 
    boolean OL=0; // открыть замок
    
  
    
    
    //----------------------------------------------------
    void setup () 
    {
      for (int k = 0; k < 9; k++) 
      { pinMode(BUTTON_Pins[i], INPUT);  }
      
      pinMode(6,OUTPUT); // белый светик на подсветку 
      pinMode(7,OUTPUT); // красный светик на индикацию
      pinMode(9,OUTPUT); // ШИМ на мотор замка
      
       Serial.begin(9600); // for deBug
    }
    //------------------------------------------------------
      //----------------------------------------------------------
    // процедура открытия замка 
      void OPE_LOCK ()
  {
      if (OL == 1)
                                       
    {  
                                          
      nD_011.start();
      nD_011.read(1000);
      nD_022.start();    // мигалка красным светиком пока открываем
      nD_022.read(100);  //мигает красный светик открытого замка                                      
      if (nD_011.tick) {j++;}
      
      if (j==1)
      {                                             
      digitalWrite(9, 1);   // рывок на открытие, время nD_011.read(1000);
      }   
      if (j>=2) {analogWrite(9, 30);}  // удержание задвижки замка ШИМом
      
      if (nD_022.tick) {digitalWrite(7, !digitalRead(7));} //мигает красный светик открытого замка
      
      if (j>=5) // открыли, обнуляем все переменные
      {            
      digitalWrite(9, 0);
      digitalWrite(7, 0);
      q1=0;
      q2=0;
      j=0;
      Bt  = 0;
      Btc = 0;
      nD_011.stop();
      nD_022.stop();
      OL=0;
      }
    }
  } 
 
    //------------------------------------------------------
    void loop() 
    {
       //--------------------------------- получаем код нажатия кнопок 
      BT_01.read(); // обновляем состояние
      BT_02.read(); // обновляем состояние
      BT_03.read(); // обновляем состояние
      BT_04.read(); // обновляем состояние
      BT_05.read(); // обновляем состояние кнопка в доме
        
        if (BT_01.click_down) { Bt1=1;}
        if (BT_02.click_down) { Bt2=2;}
        if (BT_03.click_down) { Bt3=4;}
        if (BT_04.click_down) { Bt4=8;}
        if (BT_05.click_down) { Bt5=16;}

    //------------------- если нажата кнопка - подсветка и отсчет на сброс переменных кода
    if (Bt1==1 || Bt2==2 || Bt3==4 || Bt4==8 || Bt5==16) 
      { 
//                                                         Serial.println ("nD_03.start()  ");
        nD_03.start();
        digitalWrite(6, 1);               
      }
       nD_03.read(10000); // время до сброса или нажатия
       if (nD_03.tick) 
               {
                 digitalWrite(6, 0);
                 nD_03.stop();
                 Bt=0;
                 Btc=0;
                 q1=0;
                 q2=0;
                } 
      // конец подсветки и ограничения времени
      
      
      // начало проверки набранной комбинации кнопок
    if (Btc == 0) // если предыдущий код обработан, а новый еще нет
    {
    if (Bt1==1 || Bt2==2 || Bt3==4 || Bt4==8 || Bt5==16) //если кнопка нажата
      { 
        digitalWrite(7, 1);
        nD_01.start();
        nD_01.read(200);
        if (nD_01.tick) {i++;}        
        if (i>=3) // интервал определения одновременного нажатия кнопок
           {
             Bt=Bt1 + Bt2 + Bt3 + Bt4 + Bt5 ;   // получен код набора кнопок
             Bt1=0, Bt2=0, Bt3=0, Bt4=0, Bt5=0; // получен код набора кнопок
             Btc = 1; // признак, код еще не обработан
//                                                                Serial.println (Bt);
             i=0;
             nD_01.stop();
             digitalWrite(7, 0);
 //                                          Serial.println ("Bt1=0, Bt2=0, Bt3=0, Bt4=0, Bt5=0;  ");
           }}}
     //========  подучен код кнопок и признак обработки 
 //----------------------------------------
 // обработчик кода нажатия кнопок
  if (Btc == 1)
  {
    q1++; //  перебор очереди нажатия
 //                                                            Serial.print ("q1=   ");
 //                                                            Serial.println (q1);
      if (Bt >= 16)  // если нажата кнопка в доме (независимо от набора на улице)
      {
//                                                      Serial.println ("if (Bt>=16) !!!!!!  ");
        OL=1;
//        OPEN_LOCK (1);  // в конце лупа
        Btc=0;
        q1=0;
        q2=0;
      }
      
      if (Bt<=15) //набираем кнопки на улице
      {
//                                                        Serial.println ("[AC4] =   ");
//                                                        Serial.println (AC4);
//                                                        Serial.println ("[q1] =   ");
//                                                        Serial.println (q1    );
//                                                        Serial.println ("COD_A[AC4][q1]    ");
//                                                        Serial.println (COD_A[AC4][q1]);
        ACCESS_CODE (); // какой код выбран т.е. где стоят джамперы  (работа с массивом)
        
        if (Bt == COD_A[AC4][q1]) // если код совпал 
        {
          q2++;
  //                                                           Serial.print ("q2=   ");
   //                                                          Serial.println (q2);
        }
        else {q2=0;}
        if ( q2 >= 4) //если все 4 цифры верно набрали
        {
          OL=1;    // открыть и обнулить
          Btc=0;
          q1=0;
          q2=0;
        }
        if ( q1 >= 12) // если кто-то брутит, можно еще больше сделать
        {
//          OL=1;
          Btc=0;
          q1=0;
          q2=0;
        }
        
       Btc=0;  // ХБЗ зачем оно тут :)
      }
    
    
  } // конец обработчика кода нажатия кнопок 

 
 //--------------------------------------- 
     
     
//                                                          Serial.print ("OL ");
//                                                         Serial.println (OL);
      OPE_LOCK (); // открыть замок, если OL = 1
    } // loop

    //--------------------------------------------------------------
    
  void ACCESS_CODE ()   // работа с кодом доступа
      {
//                                              Serial.println ("void ACCESS_CODE ()  ");
       AC0 =  !digitalRead(A0);
       AC1 =  !digitalRead(A1);
       AC2 =  !digitalRead(A2);
       AC3 =  !digitalRead(A3);
 
//                                             Serial.print ("AC0... AC2=  ");
 //                                            Serial.print (AC0);  
 //                                            Serial.print (AC1);      
 //                                            Serial.println (AC2);          
       
       if (AC3 == 1) // если код "простой" т.е. вкл. тумблер в доме
       {   // byte COD_A[8][4] =    // [строка] [значение]
       AC4 = 1;
       }
       
       if (AC3 == 0) // собираем код из джамперов и спускаем ниже кода тумблера (+1)
       {
       AC4 = 1 + AC0 + AC1 + AC1 + AC2 + AC2 + AC2 + AC2;
       }
 //                               Serial.print ("AC3=  ");
//                                Serial.println (AC3);
 //                               Serial.print ("AC4=  ");
 //                               Serial.println (AC4);                                
  
      }
    

 

Гриша
Offline
Зарегистрирован: 27.04.2014

UPD

так я и не понял этой фразы....

GarryC пишет:

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

объяснитесь, пожалуйста, так я собираюсь нажать, кликнуть 2 раза или подержать (ну скажем 5 секунд). И как там это реализовано?

пост 100

Клапауций 666 пишет:

vvadim пишет:

при двойном клике или удержании кнопки одиночное нажатие всё равно срабатывает, что иногда не приемлемо

опиши мне словами алгоритм, который будет предсказывать будущее, например:

я нажал кнопку и... [здесь девайс смотрит в будущее и он точно знает, что]....

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

Гриша пишет:

так я и не понял этой фразы....

Гриша, если бы хоть у одного, пострадавшего от класс титановый велосипед для тактовой кнопки., было желание вести конструктивный диалог с целью улучшения, исправления, приведения к некоему стардарту кода класса, то это звучало бы следующим образом - в языке Z++ кнопки обрабатываются таким кодом по такому алгоритму.

т.е. забей на этот серый шум профессиональных безработных программистов - времени у них дофига, а код бесплатно написать им профессиональность не позволяет.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

анонс.

на днях выложу переписанную заново версию кода класса - через более чем год многие вещи вижу иначе.

sirota
Offline
Зарегистрирован: 18.08.2015

Клапауций 232 пишет:

анонс.

на днях выложу переписанную заново версию кода класса - через более чем год многие вещи вижу иначе.

Ждем. Юзаю более или менее данный класс. Немного переписал под себя, убрал лишние примочки.

Гриша
Offline
Зарегистрирован: 27.04.2014

sirota пишет:

Ждем. Юзаю более или менее данный класс. Немного переписал под себя, убрал лишние примочки.

и чего мешает поделиться результатом (ну соответственно, с описание переделок.)? Я пример использования кинул в посте 243 (тока там без переделок, я их не делал)

 

GarryC
Offline
Зарегистрирован: 08.08.2016

Прежде всего, хотелось бы подчеркнуть, что воспитанный человек не может обращаться на ты к незнакомому человеку в русском языке, поскольку это невежливо. В других языках, где понятия ты И Вы сливаются, это вполне допустимо, но даже в них при общении с малознакомыми людьми принято применять безличные Mrs и Ms вместо You. Но это так заметка по поводу, конечно, можно быть хорошим разработчикам и при этом пренебрегать правилами поведения в обществе, но мне подобное сочетания представляется маловероятным в силу ряда причин, излагать которые в данном комменте считаю излишним.

Насчет первого варианта поведение - предложение интересное, своего рода вызов, конечно, не моего уровня, но вполне себе вызов. У меня такой код есть, написан он лет 15 назад на ассемблере для I8051, в ближайшие выходные постараюсь первратить его в программу для среды Ардуино и выложить.

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

Третий вариант, с Вашего позволения, пока не рассматриваем до ближайших выходных.

Ну и последнее, по моему мнению, назвать человека программистом - это не обозвать его, поскольку программирование, как профессия, требует весьма высокого уровня интеллектуального развития и не может быть оскорбительныи. А что касается того, чем Вы зарабатываете в реальной жизни - по Вашему, это существенно? Я должен сделать скидку и относится к Вашей программе, исходя из иных критериев, нежели к своей или к программе коллеги? Это почему, собственно?

Если я Вам принесу криво собранную плату со "слепыми " пайками и в ответ на Ваши замечания гордо заявлю, что я программист, это как-то улучшит качество этой платы?

GarryC
Offline
Зарегистрирован: 08.08.2016

Сразу отвечу на вопрос из следующего Вашего комментария о заглядывании в будущее - Вы увидите это в коле, который я обещал написать за выходные, но сразу изложу основную идею - событие нажатия задерживается до того момента, когда достоверно станет известно, что это не двойной клик и не длительное нажатие - довольно таки несложно. Постараюсь обратить внимание на данный момент в комментариях.

1. В том то и дело, что я прочитал комменты в начале и там уважаемому автору предлагалось другими участниками ввести некоторые элементарные исправления в код, чтобы соблюсти общепринятые соглашения о написании классов. Но, вместо того, чтобы последовать этим несомненно разумным советам, автор начинает вопрошать, зачем это делать (я не буду цитировать содержимое любой книги по ООП, там все наглядно показано с примерами), заявлять, что он и так прекрасно использует свой класс, что и вызвало к жизни мой комментарий относительно намерений, сподвигнувших его на выставление своего кода на всеобщее обозрение. Да, Вы правы, никто из критикующих код не предложил внятной альтернативы; как я и обещал, постараюсь исправить данное упущение.

2. Отосительно проходящих мимо, ничего не могу сказать, надеюсь, что после выходных буду зачислен в другую категорию, пока могу порекомендовать посмотреть мои посты на Хабре, я там пишу исключительно о встроенном программировании и разработке, возможно, они позволят Вам создать первое впечатление о моем уровне.

3. По поводу советов именно они и были в комментах к этому посту - посмотрите внимательнее.

4. Да, это был сарказм, возможно, излишний, наверное, погорячился.

5. Если я выполню свое обещание за эти выходные, то вполне приму Вашу благодарность, ни на что другое я не рассчитываю, мои посты удовлетворяют не нижние уровни пирамиды потребностей.

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

7. Если Вы занялись программированием, то Вы должны ответственно относиться к данному процессу и постоянно повышать свой уровень, какой бы он в данный момент не был.

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

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

почему ты решил, что ты мне не знаком?

твоя порода мне очень даже знакома, и ничего, кроме презрения не вызывает.
я тебе указал на три выхода из этого уютного помещения #242
почему ты решил избрать самый позорный выход - мне не интересно.
 
Гриша
Offline
Зарегистрирован: 27.04.2014

IMHO (лирика) {

Уважаемый GarryC, что касается:

GarryC пишет:

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

Давно заметил, что чем дальше на восток, тем больше люди говорят друг - другу Вы. На западе (России) подавляющее большинство ТЫчет :) такая вот диалектика. Это становится для меня вопросом восприятия и, Я лично,  отношусь к этому так:

И посылал в иные земли [посланников, как правило, перед объявлением войны] со словами: „Иду на Вы!“

Если копнуть историю, несложно нарыть вагон информации, где среди прочего "Как называть родителей на «ты» или на «вы» ".  

Что же касается грубости "Клапауций ХХХ (и его ипостаси)"  комментировать  не буду, тем более воспитывать, он не один на форуме.

} IMHO (лирика)

GarryC пишет:

Сразу отвечу на вопрос из следующего Вашего комментария о заглядывании в будущее ... событие нажатия задерживается до того момента, когда достоверно станет известно, что это не двойной клик и не длительное нажатие - довольно таки несложно.

пост 110

Клапауций 777 пишет:

Гриша пишет:

* возможно!, этот алгоритм поможет следующим пользователям... :

в программе определяется "что" использовать (нажатие, двойное нажатие, удержание) и устанавливается интервал (время) определения - до его окончания никаких действий. Соответственно если использовать только простое нажатие - мгновенное исполнение, во всех других комбинациях - действие исполняется по истечении времени интервала определения - да хоть тройное нажатие! 

ты не оригинален в желании программно затупить работу кнопки на время, превышающее bounce_ - длительность отслеживания дребезга.

пост113

Гриша пишет:

Клапауций 777 пишет:

Гриша пишет:

UPD пост 109. "залипание кнопки" - если  перекрыть все моменты, то необходимо позаботиться в алгоритме о том, что есть "залипание кнопки" и как этот момент обрабатывать!!! 

никак не обрабатывать, т.к. при залипании кнопки(постоянном нажатии) обработается один раз событие удержание кнопки и в дальнейшем кнопка перестанет работать совсем от слова СОВСЕМ.

исходя из условий поставленной задачи, можно вообще не обрабатывать "удержание" если кнопку не отпустили!!! НО, это все должен определить РАЗРАБОТЧИК (програмист) согласно алгоритму по ТЗ.

ИМХО. Написать код достаточно просто,  правильно составить алгоритм почти -  искусство. Это как в школе – правильно составленное условие задачи больше половины решения. А в данном случае, разработчик должен описать все возможные события именно так, как ему это нужно.

Темка понравилась именно тем, что есть код (доступный), который можно допилить под свои нужды – все универсальное слишком громоздко.

 

PS.  тем не менее ждем Вашего расширенного варианта кода

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

Гриша пишет:

Давно заметил, что чем дальше на восток, тем...

...толще партизаны.(с)

Гриша, неужели моё обращение к GarryC на "ты", является главным из перечисленных им, моих преступлений против человечности в общем и ползателями форума в частности, что бы акцентировать внимание именно на этом, вместо того, что бы осудить меня по совокупности моих ужасных деяний?

весь в сомнениях - ведь, весь этот флуд просто обязан как-то коррелировать к кодом для обработки работы тактовой кнопки... но, как и каким местом?

что GarryC хотел мне и нам донести, потратив время для написания таких длинных и полезных для меня и всех нас постов?

Гриша
Offline
Зарегистрирован: 27.04.2014

Клапауций 232 пишет:

весь в сомнениях - ведь, весь этот флуд просто обязан как-то коррелировать к кодом для обработки работы тактовой кнопки... но, как и каким местом?

да никакак - это ИМХО

уже раз 5 писал:

пост 15

Гриша пишет:

 3) Пожалуйста, дайте право ТС дополнять (возможно, и изменять) первый пост всегда!!! 

пост 25

Гриша пишет:

Назрела проблема с офф.топом. Много сообщений убивающих время и не несущих полезной информации....

Клапауций 232
Offline
Зарегистрирован: 05.04.2016
 
класс титановый велосипед для тактовой кнопки. (версия 1.0)
 
исправления:
 
- фильтр дребезга включается, если время между предыдущим и настоящим нажатием/отпусканием кнопки меньше, чем время длительности отслеживания дребезга. во всех остальных случаях нажатие/отпускание обрабатывается немедленно.
было:
- фильтр дребезга включался после отпускания кнопки(что есть костыль).
 
- тихий старт.(но можно настроить p = !digitalRead(_pb); // отключить тихий старт.)
было:
- при старте код генерил событие нажатия/отпускания кнопки, событие неактивности/удержания кнопки.
оба варианта дизайна поведения по сути верны, но отсутствие вариантов выбора - было неверным.
 
добавлено:
- переменная программного состояния кнопки "s". т.е. если p = digitalRead(пин кнопки) - сырые данные, то s = обработанное фильтром дребезга логическое состояние пина кнопки.
 
!!!несовместимость с прошлой версией:
- инвертированы активные значения переменных b, t, r - там, где было TRUE, теперь FALSE и наоборот. юзающим эти переменные, исправьте 0 на 1 и наборот в своём коде.
 
***разъяснения, рекомендации ползателям класса по использованию переменных класса.
 
класс генерит переменные-события, актуальные в течении одного цикла программы:
click_down  // событие нажатия.
click_up    // событие отпускания.
doubleclick // событие двойного нажатия.
timer       // событие неактивности.
retention   // событие нажатия и удержания.
 
так же класс хранит значения переменных-состояний своей работы:
p // состояние пина кнопки.
s // программное состояние кнопки.
t // состояние таймера неактивности.
r // состояние таймера нажатия и удержания.
 
существует возможность выбора:
ждать, когда событие произойдёт и что-то сделать.
в любой момент узнать значение переменной-состояния и что-то сделать.
 
например: нужно включить подсветку на дисплее при начале работы с кнопкой и выключить при неактивности кнопки.
 
вариант с переменными-событиями:
if (BUTTON_01.click_down) {digitalWrite(led, 0);} // зажечь светодиод при нажатии на кнопку.
if (BUTTON_01.timer) {digitalWrite(led, 1);} // погасить светодиод при нективности кнопки.
 
вариант с переменными-состояниями:
digitalWrite(led, !BUTTON_01.t);
 
т.е.
переменные-события актуальны один цикл и привязаны ко времени.
переменные-состояния актуальны всегда и не привязаны ко времени.
 
###совпадение исправлений, дополнений, прочего в коде класса с пожеланиями ползателей класса прошу считать нелепой случайностью.
Клапауций 232
Offline
Зарегистрирован: 05.04.2016
подключение класса как библиотеки:
- скопировать код класса в файл class_BUTTON.h
- скопировать файл class_BUTTON.h в папку class_BUTTON
- скопировать папку class_BUTTON в *\Мои документы\Arduino\libraries\
Клапауций 232
Offline
Зарегистрирован: 05.04.2016
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// класс титановый велосипед для тактовой кнопки. (версия 1.0)
// фильтр дребезга, отслеживание событий: нажатие, отпускание, двойное нажатие(doubleclick), нажато и удерживается в течении определённого времени, отпущено и неактивно в течении определённого времени.

#include <Arduino.h>

class BUTTON {
public:
//============================================================================================
// константы настроек класса.
static const byte bounce_            =   50; // длительность отслеживания дребезга.
static const byte doubleclick_       =  250; // длительность отслеживания двойного нажатия.
static const unsigned long timer_    = 5000; // длительность отслеживания неактивности.
static const unsigned int retention_ = 2000; // длительность отслеживания нажатия и удержания.
//============================================================================================
unsigned long start; // старт отсчёта времени.
boolean p;           // состояние пина кнопки.
boolean s;           // программное состояние кнопки.
boolean b;           // состояние таймера фильтра дребезга.
byte    c;           // переменная счётчика двойного нажатия.
boolean t;           // состояние таймера неактивности.
boolean r;           // состояние таймера нажатия и удержания.
//============================================================
boolean click_down;  // событие нажатия.
boolean click_up;    // событие отпускания.
boolean doubleclick; // событие двойного нажатия.
boolean timer;       // событие неактивности.
boolean retention;   // событие нажатия и удержания.
//============================================================
byte _pb;
//============================================================
BUTTON(byte pb) {
_pb = pb;
pinMode(_pb, INPUT);
digitalWrite(_pb, 1);
//===================
start = millis();
p     = digitalRead(_pb);
// p     = !digitalRead(_pb); // отключить тихий старт.
s     = p;
b     = 0;
c     = 0;
t     = 0;
r     = 0;
//==============
click_down  = 0;
click_up    = 0;
doubleclick = 0;
timer       = 0;
retention   = 0;
//==============
}

void read() {
//==============================================================================
boolean np = digitalRead(_pb); // текущее состояние пина кнопки.
unsigned long stop = millis(); // стоп отсчёта времени.
//==============================================================================
click_down  = 0;
click_up    = 0;
doubleclick = 0;
timer       = 0;
retention   = 0;
//==============================================================================
if (np != p) {p = np; click(); start = stop; b = 1; t = 1; r = 1;} // состояние цифрового пина изменилось.
//==============================================================================
if (b != 0          ) {if (stop - start > bounce_     ) {b = 0; click();      }} // фильтр дребезга.
if (c != 0          ) {if (stop - start > doubleclick_) {c = 0;               }} // обнуление счётчика двойного клика.
if (t != 0 && s == 1) {if (stop - start > timer_      ) {t = 0; timer     = 1;}} // неактивность.
if (r != 0 && s == 0) {if (stop - start > retention_  ) {r = 0; retention = 1;}} // нажатие и удержание.
//==============================================================================
}

void click() { // нажатие, отпускание, двойное нажатие.
if (b == 0 && s != p) {s = p;
if (s == 0) {click_down = 1; ++c; if (c == 2) {c = 0; doubleclick = 1;}}
if (s == 1) {click_up   = 1;                                           }
}
}

};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

Гриша
Offline
Зарегистрирован: 27.04.2014

Уважаемый,  строка 35, случаем не ошибка??????????  или это pullup?

digitalWrite(_pb, 1);