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

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

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

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

kisoft пишет:

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

Ну этт понятно, тут нужен титановый мопед от великого Клапауция где можно настраивать необходимые опции. Типа ДаблБабл = фальш //двойной клик не ловим

kisoft пишет:

Док, чего ты паришься, проверяй сначала дабл, а потом просто клик. Всё равно switch в такой реализации нельзя использовать.

см. мое саапчение за номером 37 :)

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

JollyBiber пишет:

Ну этт понятно, тут нужен титановый мопед от великого Клапауция где можно настраивать необходимые опции. Типа ДаблБабл = фальш //двойной клик не ловим

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

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Обоснуй. У меня ни разу не возникало необходимости ловить двойной клик, а вот 2 подряд с замером времени - да.

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

JollyBiber пишет:

Обоснуй. У меня ни разу не возникало необходимости ловить двойной клик, а вот 2 подряд с замером времени - да.

реально троллишь. а, разве двойной клик - это не замер времени между двумя кликами?

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

или тебе нужно знать nm - m - ну, да - у тебя всё приват, тебе этого знать не положено.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

"По этому вопросу есть 2 мнения: мое и неправильное"

Как то так твоя реакция выглядит. Но мне в принципе фиолетово :)

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

JollyBiber пишет:

"По этому вопросу есть 2 мнения: мое и неправильное"

Как то так твоя реакция выглядит. Но мне в принципе фиолетово :)

ну, ок - моё мнение следующее: ты увидел пять if'ов в лупе моего примера применения класса и ты как опытный программист-оптимизатор быдлокода не смог пройти мимо - заехал на булдозере со свитчем...

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

...как-то так вижу.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

"Ой все!"

чтобы лучше видеть можно посмотреть оригинал: "а если приделать вместо иф-ов свитч?"

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

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

JollyBiber пишет:

"Ой все!"

чтобы лучше видеть можно посмотреть оригинал: "а если приделать вместо иф-ов свитч?"

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

т.е. ты написал один каммент в этой теме, за что был обвинён, обозван и оклеветан?

*а, так "да" - ни один опытный программист не может спокойно пройти мимо более чем одного if'а быдлокода. 

JollyBibe, как это засунуть в свитч?:

if (if >1) {switch}

 

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

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

У тебя началась неделя самобичевания?  Прирост if-ов увеличивается втрое?

Скетч твой - можешь засовывать свои ифы в него хоть с разбега ;)

Вот теперь - троллю.

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

JollyBiber пишет:

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

таки, "да" - моя ошибка, что сразу не послал тебя нахрен.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Ты еще молод, у тебя все еще спереди - посылай, как вариант отредактируй свой первый ответ.

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

JollyBiber пишет:

Ты еще молод, у тебя все еще спереди - посылай, как вариант отредактируй свой первый ответ.

ок. если я старше тебя, ты пойдёшь нахрен без вариантов?

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Ты выложишь скан паспорта с фамилией Клапауций999?

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

JollyBiber пишет:

Ты выложишь скан паспорта с фамилией Клапауций999?

не - я весь список if'ов твоих условий ухода нахрен перепишу в один switch.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Начинай

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

JollyBiber пишет:

Начинай

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

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

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

// работаем с переменными кнопки 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) {}

}
Клапауций 999, для работы по событиям в классе заводятся виртуальные методы, которые задают обработку событий по умолчанию, а юзер может их переопределеить как ему надо (на то они и виртуальные). Тогда в кодне основной программы нет таких огромных if'ов (впрочем, как и switch'ей) и он (код) намного чище и прозрачнее.
Клапауций 999
Offline
Зарегистрирован: 06.06.2015

ЕвгенийП пишет:

Клапауций 999, для работы по событиям в классе заводятся виртуальные методы, которые задают обработку событий по умолчанию, а юзер может их переопределеить как ему надо (на то они и виртуальные). Тогда в кодне основной программы нет таких огромных if'ов (впрочем, как и switch'ей) и он (код) намного чище и прозрачнее. 

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

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

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

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

static const byte doubleclick_         =  200; // длительность отслеживания двойного клика.

т.е. каждое следующее за первым одиночным нажатием одиночное нажатие у тебя будет тупить 200 миллисекунд, что концептуально не правильно, т.к. здесь ещё раз внимательно!:

200 миллисекунд - это 5 раз в секунду
Прочитал внимательно и не понял:  у нас что, пальцы настолько быстронажимающий дэвайс
что нам надо упорно бороться за то чтобы обеспечить программную поддержку этих "быстрых пальцев"?
Или мы собираемся приделать эту кнопку вместо гашетки на SU-27-999 где от времени реакции зависит жизнь ?

 

 

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

trembo пишет:

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

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

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

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

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

Нокия 720 ( NMT), Моторола (GSM) - не помню, но карточка была размером с банковскую кредитку.....

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

trembo пишет:

Нокия 720 ( NMT), Моторола (GSM) - не помню, но карточка была размером с банковскую кредитку.....

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

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

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

ЕвгенийП пишет:

Клапауций 999, для работы по событиям в классе заводятся виртуальные методы, которые задают обработку событий по умолчанию, а юзер может их переопределеить как ему надо (на то они и виртуальные). Тогда в кодне основной программы нет таких огромных if'ов (впрочем, как и switch'ей) и он (код) намного чище и прозрачнее. 

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

Может быть и ничего. Пустая функция - просто название обработчика, который юзер напишет сам. Зато это позволяет спрятать внутрь класса весь огромный разборщик событий (не важно, на IF'ах он или на SWITCH'е).

Давайте посмотрим на примере. Итак, Ваш класс остаётся таким же, но в него добавляется функция разбора событий (и вызова обработчиков). Обработчики событий - пустые виртуальные функции. Смотрите:

class BUTTON {
	public:
	/*
	 * .............
	 * здесь всё как Вы написали
	 * .............
	 */

	 void DispatchEvents(void) {
	 	if (/* обнаружено нажатие */ 0) OnButtonDown();
	 	else if (/* обнаружено отпускание */ 0) OnButtonUp();
	 	else if (/* обнаружено двойное нажатие */ 0) OnDoubleClick();
	 	//	........
	 	// и так далее, какие там ещё у Вас события бывают
	 	// ........
	 }

protected:
	virtual void OnButtonDown(void) {}
	virtual void OnButtonUp(void) {}
	virtual void OnDoubleClick(void) {}
};

В IF'ах должны быть ВАШИ условия - как Вы там эти события распознаёте (Вы это делаете и без меня, потому я ограничислся комментариями). На нули в моих IF'ах забейте. Мне они нужны, чтобы пример компиировался, а когда там будут Ваши настоящие условия, то никаких нулей не нужно.

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

Теперь, когда Вы так написали, Вы говорите Юзеру: "Мужик, ты должен сделать всего две вещи: 1) ставить в loop() вызов DispatchEvents() и 2) дописать свои обработчики ТОЛЬКО для тех событий, которые реально собираешься обрабатывать".

Допустим, юзера интересует ТОЛЬКО событие "двойное нажатие", а остальные ему пофиг. Тогда он пишет такой код:

#include "BUTTON.h"

class MyButton : public BUTTON {
protected:
	void OnDoubleClick(void) {
		/*
		 * здесь нужная юзеру обработка события DoubleClick
		 */
	}
};

MyButton TheButton;

void loop() {
	TheButton.DispatchEvents();
}

void setup() {
}

И это всё! Заметьте, страшный разборщик с кучей IF'ов навсегда спрятан в Вашем классе и его никто не видит. Юзер же только описал единственную нужную ему функцию-обработчик.

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

#include "BUTTON.h"

class MyButton : public BUTTON {
protected:
	void OnButtonUp(void) {
		/*
		 * здесь нужная юзеру обработка отпускания кнопки
		 */
	}
	void OnButtonDown(void) {
		/*
		 * здесь нужная юзеру обработка нажатия кнопки
		 */
	}
};

MyButton TheButton;

void loop() {
	TheButton.DispatchEvents();
}

void setup() {
}

Если остались вопросы, не стесняйтесь.

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

ЕвгенийП пишет:

Вот, такой же пример с двумя обработчиками:

#include "BUTTON.h"

class MyButton : public BUTTON {
protected:
	void OnButtonUp(void) {
		/*
		 * здесь нужная юзеру обработка отпускания кнопки
		 */
	}
	void OnButtonDown(void) {
		/*
		 * здесь нужная юзеру обработка нажатия кнопки
		 */
	}
};

MyButton TheButton;

void loop() {
	TheButton.DispatchEvents();
}

void setup() {
}

Если остались вопросы, не стесняйтесь.

ок. покажите мне, пожалуйста, пример "И это всё!", когда нажатием кнопки BUTTON_01 нужно зажечь светодиод LED_01, а нажатием кнопки BUTTON_02 нужно погасить светодиод LED_01.

спасибо.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

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

ок. покажите мне, пожалуйста, пример "И это всё!", когда нажатием кнопки BUTTON_01 нужно зажечь светодиод LED_01, а нажатием кнопки BUTTON_02 нужно погасить светодиод LED_01.

спасибо.

Здесь можно сделать по-разному, но всякие "короткие" пути мне не нравятся. Во-первых, короткие они только по записи, а не по коду, а во-вторых, они затушёвывают главную идею использования классов.

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

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

Вот, смотрите:

#include "BUTTON.h"

/*
 * Класс для кнопок, которые "связаны" с неким пином
 * и зажигают LED на своём пине по нажатию
 */
class HighButton : public BUTTON {
public:
	HighButton(const int8_t pin = 13) { m_pin = pin; }
protected:
	int8_t m_pin; 
	virtual void OnButtonDown(void) { digitalWrite(m_pin, HIGH); }
};

/*
 * Класс для кнопок, которые "связаны" с неким пином
 * и гасят LED на своём пине по нажатию
 */
class LowButton : public HighButton {
public:
	LowButton(const int8_t pin) : HighButton(pin) {}
protected:
	void OnButtonDown(void) { digitalWrite(m_pin, LOW); }
};

HighButton 
	button1 (9),	// по нажатию, зажигает на 9-м пине
	button2 (10);	// по нажатию, зажигает на 10-м пине

LowButton 
	button3 (11),	// по нажатию, гасит на 11-м пине
	button4 (12);	// по нажатию, гасит на 12-м пине


void loop() {
	button1.DispatchEvents();
	button2.DispatchEvents();
	button3.DispatchEvents();
	button4.DispatchEvents();
}

void setup() {
}

(это то, что Вы просили, только здесь две кнопки зажигают и две гасят)

В строках 7-13 я определяю класс HighButton, который 1) при инициализации запоминает пин и 2) при нажатии кнопки зажигает LED на запомненном пине.

А в строках 19-24 я определю дочерний от первого класс LowButton. Он точно также при инициализации запоминает пин, хотя в нём это явно не прописано (это свойство он унаследовал у родительского класса!!!), А вот функция OnButtonDown у него своя (в строке 23), которая заменяет "родительскую" (из строки 12).

Теперь, если я опишу некие переменные с типом "HighButton", они будут работать в точности как переменные родительского класса BUTTON, только при инициализации они будут запоминать пин, а при нажатии кнопки выдавать на этот пин HIGH.

А если я опишу переменные с типом "LowButton", они будут работать в точности как переменные родительского класса HighButton, только при нажатии кнопки выдавать на свой пин LOW.

При этом ВСЕ программные коды хранятся в памяти только один раз - ничего не дублируется!

Кстати, проблема с дуюлированием кода ради добавления кнопки (с чего мы начали) она ведь даже не столько в занимаемой памяти. Вот представьте, у Вас три кнопки и Вы, соответственно трижды продублировали код. А теперь, не дай Бог, потребовалось в коде что-то поравить! Оппааа! Не забыть поправить в трёх местах! Да, везде одинаково! Дублирование - большая головная боль.

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

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

поясню просто - я написал класс для тактовой кнопки.

если для работы с этим классом вам или кому-то ещё, для каждого частного случая(в данном случае зажигания/гашение светодиода), требуется использование чего-то ещё:

/*
 * Класс для кнопок, которые "связаны" с неким пином
 * и зажигают LED на своём пине по нажатию
 */
/*
 * Класс для кнопок, которые "связаны" с неким пином
 * и гасят LED на своём пине по нажатию
 */

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

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

*лично я использую if'ы - они меня не парят абсолютно.

спасибо.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

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

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

Я ничего не имею против того, что Вы написали, всё нормально, симпатичненько так. Просто Вы попросили замечаний профессионала (помните. Вы просили?) вот и пытался рассказать Вам как это делается профессионально.

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

ЕвгенийП пишет:

Вы не даёте человеку возможности добавить что-то ещё.

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

void loop() {
BUTTON_01.read();
BUTTON_02.read();
if (BUTTON_01.click_down) {зажечь светодиод}
if (BUTTON_02.click_down) {погасить светодиод}
}

то напиши для этого класс.

спасибо.
ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Понятно, пожалуйста :)

Navi
Offline
Зарегистрирован: 12.08.2015

Подскажите как сделать при нажатии кнопки включается нагрузка например на 5мин. При повторном нажатии нагрузка выключается не дожидаясь окончании времени. Нужно сделать 3 независимые кнопки на разное время. Как включить на время разобрался а как принудительно выключить всю голову сломал.

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

Navi пишет:

Подскажите как сделать при нажатии кнопки включается нагрузка например на 5мин. При повторном нажатии нагрузка выключается не дожидаясь окончании времени. Нужно сделать 3 независимые кнопки на разное время. Как включить на время разобрался а как принудительно выключить всю голову сломал.

boolean l_01 = 1;

void loop() {
BUTTON_01.read();
if (BUTTON_01.click_down) {l_01 = !l_01; digitalWrite(11, l_01);}
}

или

void loop() {
BUTTON_01.read();
if (BUTTON_01.click_down) {digitalWrite(11, !digitalRead(11));}
}

 

Navi
Offline
Зарегистрирован: 12.08.2015

Значит в код boolead нужно добавить?

long previousMillis = 0; 
int val=0; 
int state = 0;
void setup()    
{       
  pinMode(12, OUTPUT);
 } 
void loop() 
{ 
  if(digitalRead(2) == HIGH && state == 0)     //если кнопка нажата ... 
  { 
    state = 1;
  }
  if (state == 1)
  {
    digitalWrite(12,HIGH);                     // вкл. светодиод   
    if (millis() - previousMillis >1000)    
    { 
      previousMillis = millis();     
      val++; 
    } 
    if(val >= 60) 
    { 
      digitalWrite(12,LOW);    // выкл. светодиод
      state = 0;
      val = 0; 
    } 
  }
}

 

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

Navi пишет:

Значит в код boolead нужно добавить?

нет - в этой теме обсуждается ползание кнопкой в контексте использования класса титановый велосипед для тактовой кнопки.

если ты готов, то продолжим...

Navi
Offline
Зарегистрирован: 12.08.2015

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

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

Navi пишет:

Для меня это сложно. Я только начал изучать. В стиральной машине вышло из строя механическое реле времени,хочу заменить на электронное управление(стирка,отжим,слив). С этой кнопкой уже третий день сижу.

ок. вот тебе проще - каждую кнопку можно настраивать по отдельности

http://arduino.ru/forum/programmirovanie/titanovyi-velosiped-dlya-taktov...

Navi
Offline
Зарегистрирован: 12.08.2015

Спасибо,пошёл разбираться.

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");}  
}

 

timer_23
Offline
Зарегистрирован: 06.08.2014

А как можно выйти из такой ситуевины: у меня при нажатии на кнопку как то долго происходит реакция. Вот пример кода. Мне нужно прибавлять и убавлять 1.

 

int r = 0;
//тут ваш класс

BUTTON button(2);
BUTTON button2(3);

void setup()
{
	Serial.begin(9600);
}

void loop()
{
	button.read();
	button2.read()
	if(button.clck_down)
	{
		i += 1;
	}
	if(button2.click_down)
	{
		i-=1;
	}
	
	Serial.Println(i);
	delay(1000);
}

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

 

 

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


int i = 0;

//тут ваш класс

BUTTON button1(2);
BUTTON button2(3);

void setup()
{
	Serial.begin(9600);
}

void loop()
{
	button1.read();
	button2.read();
	if(button1.clck_down)
	{
		i += 1; Serial.Println(i); // что-то делаем только тогда, когда что-то прозошло.
	}
	if(button2.click_down)
	{
		i -= 1; Serial.Println(i); // что-то делаем только тогда, когда что-то прозошло.
	}
	
	// Serial.Println(i);
	// delay(1000);
}

timer_23
Offline
Зарегистрирован: 06.08.2014

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

А вообще даже не в этом дело. В том коде де я это использую так или иначе нужен delay.  Чтобы обновлять данные на дисплее

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

timer_23 пишет:

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

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

почему ты можешь долбить Serial.Println(i); в конце скетча и тебе не удобно напечатать i сразу после её изменения?

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

timer_23 пишет:

В том коде де я это использую так или иначе нужен delay.  Чтобы обновлять данные на дисплее

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

timer_23
Offline
Зарегистрирован: 06.08.2014

Хорошо попробую. Спасибо за. Совет!

Maverik
Offline
Зарегистрирован: 12.09.2012

полезная штучка.

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

Огромное спасибо за велсипед

Вот что обнаружил, скопировал ваш велсипед, все ок, но единственное что смутило и не сразу понял в чем дело это то что функции timer и retention работают наоборот, то есть timer как retention а retention как timer.

вот ссылка на вирт проектик

https://123d.circuits.io/circuits/1223896-titanium-bacycle-button

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

Mr.Privet пишет:

функции timer и retention работают наоборот, то есть timer как retention а retention как timer.

наличие неполадки практически не подтверждено


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// класс титановый велосипед для тактовой кнопки.
// фильтр дребезга, отслеживание событий: нажатие, отпускание, двойное нажатие(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(11, OUTPUT);
digitalWrite(11, 1);
pinMode(14, OUTPUT);
digitalWrite(14, 1);
pinMode(15, OUTPUT);
digitalWrite(15, 1);
/////
}

void loop() {

// *.click_down
// *.click_up
// *.doubleclick
// *.timer
// *.retention

// if (BUTTON_01.click_down) {}
// if (BUTTON_01.click_up) {}
// if (BUTTON_01.doubleclick) {}
// if (BUTTON_01.timer) {}
// if (BUTTON_01.retention) {}

// digitalWrite(11, !digitalRead(11));
// digitalWrite(14, !digitalRead(14));
// digitalWrite(15, !digitalRead(15));

BUTTON_01.read();

// включить/выключить при нажатии.
// if (BUTTON_01.click_down) {digitalWrite(14, !digitalRead(14));}

if (BUTTON_01.click_down) {digitalWrite(14, 0);} // светодиод включается при нажатии кнопки.
if (BUTTON_01.timer) {digitalWrite(14, 1);} // светодиод выключается через timer_ = 10000 (10 сек.) неактивности кнопки.
if (BUTTON_01.retention) {digitalWrite(14, 1);} // светодиод выключается при удержании кнопки через retention_ = 1000 (1 сек.)
//

}
если есть желание, обсудить проблему по сути, то вам нужно опубликовать скетч примера, подтверждающего факт наличия неполадки.
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
примером будет считаться скетч, обрабатывающий одну или более кнопок и изменяющий одну или более  переменных или состояние одного или более пинов.
скетчи с функционалом превышающим вышеуказанное требование рассмариваться не будут.
ссылки на нечто на сторонних ресурсах рассматриваться не будут.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 пишет:

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

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