Характеристики и подключение датчиков DHT11 и DHT22
Датчик состоит из двух частей – емкостного датчика температуры и гигрометра. Первый используется для измерения температуры, второй – для влажности воздуха. Находящийся внутри чип может выполнять аналого-цифровые преобразования и выдавать цифровой сигнал, который считывается посредством микроконтроллера.
В большинстве случаев DHT11 или DHT22 доступен в двух вариантах: как отдельный датчик в виде пластикового корпуса с металлическими контактами или как готовый модуль с датчиком и припаянными элементами обвязки. Второй вариант гораздо проще использовать в реальных проектах и крайне рекомендуется для начинающих.
Датчик DHT11
- Потребляемый ток – 2,5 мА (максимальное значение при преобразовании данных);
- Измеряет влажность в диапазоне от 20% до 80%. Погрешность может составлять до 5%;
- Применяется при измерении температуры в интервале от 0 до 50 градусов (точность – 2%)
- Габаритные размеры: 15,5 мм длина; 12 мм широта; 5,5 мм высота;
- Питание – от 3 до 5 Вольт;
- Одно измерение в единицу времени (секунду). То есть, частота составляет 1 Гц;
- 4 коннектора. Между соседними расстояние в 0,1 ”.
Датчик DHT22
- Питание – от 3 до 5 Вольт;
- Максимальный ток при преобразовании – 2,5 мА;
- Способен измерять влажность в интервале от 0% до 100%. Точность измерений колеблется от 2% до 5%;
- Минимальная измеряемая температура – минус 40, максимальная – 125 градусов по Цельсию (точность измерений – 0,5);
- Устройство способно совершать одно измерение за 2 секунд. Частота – до 0,5 ГЦ;
- Габаритные размеры: 15,1 мм длина; 25 мм широта; 5,5 мм высота;
- Присутствует 4 коннектора. Расстояние между соседними – 0,1 ‘;
Очевидно, что при использовании в ардуино датчика температуры и влажности DHT11 устройство выдаст менее точные значения, чем DHT22. У аналога больший диапазон измеряемых значений, но и цена соответствующая. Датчик температуры и влажности DHT22, как и его аналог, имеет один цифровой выход, соответственно снимать показания можно не чаще, чем один раз в 1-2 секунды.
Circuit #
- Both the I2C display and the real-time clock board communicate via I2C. To ease the connection, you can create the I2C bus on the breadboard by wiring the and I2C pins from the Arduino board to the contact lines on the breadboard. The display and RTC have different I2C addresses, they do not interfere with each other, so you can connect them to the same I2C bus.
- Connect the and pins from the Arduino board to the corresponding rails on the breadboard.
- Plug the I2C display. Wire its and with the I2C bus on the breadboard. Wire its and pins with the line on the breadboard. Wire pin with the line on the breadboard.
- Plug the I2C RTC breakout board. Wire its and with the I2C bus on the breadboard. Wire its and pins with the corresponding lines on the breadboard.
- Power the RTC board with the battery to keep running even when the external power source is disconnected.
Note
If you are using another model of the I2C display or RTC module, look at their pinout and datasheet to connect them correctly.
Использование функции arduino delay
Синтаксис
Ардуино delay является самой простой командой и её чаще всего используют новички. По сути она является задержкой, которая приостанавливает работу программы, на указанное в скобках число миллисекунд. (В одной секунде 1000 миллисекунд.) Максимальное значение может быть 4294967295 мс, что примерно ровняется 50 суткам. Давайте рассмотрим простой пример, наглядно показывающий работу этой команды.
В методе setup прописываем, что пин 13 будет использоваться, как выход. В основной части программы сначала на пин подается высокий сигнал, затем делаем задержку в 10 секунд. На это время программа как бы приостанавливается. Дальше подается низкий сигнал и опять задержка и все начинается сначала. В итоге мы получаем, что на пин поочередно подается, то 5 В, то 0.
Пример delay с миганием светодиодом
Пример схемы для иллюстрации работы функции delay. Можно построить схему со светодиодом и резистором. Тогда у нас получится стандартный пример – мигание светодиодом. Для этого на пин, который мы обозначили как выходной, необходимо подключить светодиод плюсовым контактом. Свободную ногу светодиода через резистор приблизительно на 220 Ом (можно немного больше) подключаем на землю. Определить полярность можно, если посмотреть на его внутренности. Большая чашечка внутри соединена с минусом, а маленькая ножка с плюсом. Если ваш светодиод новый, то определить полярность можно по длине выводов: длинная ножка – плюс, короткая – минус.
Мигаем светодиодом без delay()
или всегда ли официальный примеру учат «хорошему».
Обычно это одна из первых проблем с которой сталкивается навичок в микроконтроллерх. Помигал диодом, запустил стандартный скетч blink(), но как только он него возникает желание что-бы контроллер «занимался чем-то еще» его ждет неприятный сюрприз — тут нет многопоточности. Как только он написали где-то что-то типа delay(1000) — обнаруживается что на это строчке «контроллер остановился» и ничего больше не делает (кнопки не опрашивает, датчики не читает, вторым диодом «помигать» не может).
Новичок лезет с этим вопросом на форум и тут же получает ушат поучений: «отказывайтесь от delay()», учитесь работать с millis() и в прочитайте, в конце концов базовые примеры. В частности Мигаем светодиодом без delay()
Приведу его код:
/* Blink without Delay 2005 by David A. Mellis modified 8 Feb 2010 by Paul Stoffregen */ const int ledPin = 13; // номер выхода, подключенного к светодиоду // Variables will change: int ledState = LOW; // этой переменной устанавливаем состояние светодиода long previousMillis = 0; // храним время последнего переключения светодиода long interval = 1000; // интервал между включение/выключением светодиода (1 секунда) void setup() { // задаем режим выхода для порта, подключенного к светодиоду pinMode(ledPin, OUTPUT); } void loop() { // здесь будет код, который будет работать постоянно // и который не должен останавливаться на время между переключениями свето unsigned long currentMillis = millis(); //проверяем не прошел ли нужный интервал, если прошел то if(currentMillis - previousMillis > interval) { // сохраняем время последнего переключения previousMillis = currentMillis; // если светодиод не горит, то зажигаем, и наоборот if (ledState == LOW) ledState = HIGH; else ledState = LOW; // устанавливаем состояния выхода, чтобы включить или выключить светодиод digitalWrite(ledPin, ledState); } }
В принципе отправка к этому примеру — вполне правильна. В нем действительно виден стандартный паттерн как нужно выполнять какое-то переодическое действие (или отложенное):
1. Сохраняем время в какую-то переменную
2. В loop() все время смотрим на разницу «текущие-время — сохраненное»
3. Когда эта разница превысила какое-то значение (нужный нам «интервал переодичности»)
4. Выполняем какое-то действие (меняем состояние диода, заново запоминаем «стартовое время и т.п.»)
С задачей «объяснить идею» — пример справляется. Но, с моей точки зрения, в нем есть несколько методологических ошибок. Написание скетчек в подобно стиле — рано или поздно приведет к проблемам.
Итак, что же тут не так?
1. Не экономно выбираем тип переменных
Переменная ledPin у нас объявлена как тип int. Зачем? Разве номер пина может быть очень большим числом? Или может быть отрицательным числом? Зачем под его хранение выделять два байта, когда вполне хватит одного. Тип byte может хранить числа от 0 до 255. Для номера пина — этого вполне хватит.
const byte ledPIN = 13; // номер выхода, подключенного к светодиоду
Этого будет вполне достаточно.
2. А зачем нам переменная для малых чисел?
А зачем нам тут вообще переменая? (пусть и объявленная как const). Зачем тратить такты процессора на чтение переменной? И расходовать еще один байт? Воспользуемся директивой препроцессора #define
#define LED_PIN 13 // номер выхода, подключенного к светодиоду
Тогда еще на этапе компиляции компилятор просто подставить 13-ть везде где в коде используется LED_PIN и не будет выделять отдельных переменных.
3. Тип int опять выбрали как «первое что в голову пришло»?
И опять спотыкаемся на объявлении следующей же переменной. Почему ledState опять int? Кроме того что снова «два байта там где можно один использовать», так еще и «смысловая нагрузка» теряется. Что у нас хранится в переменной? Состояние светодиода. Включен/выключен. Горит/Не горит. Явно же напрашивается тип boolean. По крайней мере до тех пор, пока светодиод у нас может принимать два состояния.
Резюме
Платформа Arduino предоставляет нам несколько способов выполнения задержки в своем проекте. С помощью delay вы можете быстро поставить на паузу выполнение скетча, но при этом заблокируете работу микроконтроллера. Использование команды millis позволяет обойтись в ардуино без delay, но для этого потребуется чуть больше программировать. Выбирайте лучший способ в зависимости от сложности вашего проекта. Как правило, в простых скетчах и при задержке меньше 10 секунд используют delay. Если логика работы сложнее и требуется большая задержка, то вместо delay лучше использовать millis.
«>
Функция micros()
Функция micros() является аналогом функции millis(), разница заключается в точности измерения. С помощью функции micros() мы получим время, прошедшее от запуска текущей программы в микросекундах. Счетчик подсчитанных микросекунд будет сброшен по истечении 70 минут. Ниже приведен пример использования функции micros():
unsigned long time; void setup(){ Serial.begin(9600); } void loop(){ Serial.print(«Время с момента запуска: «); time = micros(); Serial.print(time); Serial.println(» мкс «); delay(1000); }
Так же, как и в примере с функцией millis(), здесь каждую секунду в монитор порта будет отправляться информация об измеренном времени, разница состоит только в том, что в данном случае время измеряется в микросекундах.
Принципы работы устройства
GPS модуль передает данные в NMEA формате, пример этих данных можно посмотреть на рисунке ниже. NMEA формат состоит из нескольких строк (предложений), из которых нам нужно будет извлечь только время и дату. Эти данные содержатся в строке $GPRMC, которая содержит время, дату, координаты и другую полезную информацию. Длина строки $GPRMC составляет примерно 70 символов. Мы уже рассматривали пример извлечения нужных нам GPS данных из строки $GPGGA в проекте определения местоположения автомобиля, здесь же мы будем извлекать данные из строки $GPRMC.
Строка $GPRMC, в основном, содержит скорость, время, дату и местоположение:
$GPRMC,123519.000,A, 7791.0381,N, 06727.4434,E,022.4,084.4,230394,003.1,W*6A
$GPRMC,HHMMSS.SSS,A,latitude,N,longitude,E,speed,angle,date,MV,W,CMD
В следующей таблице представлен перевод (описание) данных GPS из строки $GPRMC.
Идентификатор | Описание |
RMC | Recommended Minimum sentence C |
HHMMSS.SSS | Время в формате: час минута секунда и миллисекунда |
A | Статус // A=active (активный) and V= void (пустой, недействительный) |
Latitude | Широта (координата) |
N | Направление: N=North (север), S=South (юг) |
Longitude | Долгота (координата) |
E | Направление: E= East (восток), W=West (запад) |
Speed | Скорость в узлах (1 узел= 1,87 км в час) |
Angle | Угол места в градусах |
Date | Временная отметка (дата в UTC, Universal Time Coordinated — всеобщее скоординированное время) |
MV | Магнитное возмущение |
W | Direction of variation E/W (направление изменения E/W) |
CMD (*6A) | Данные контрольной суммы |
В большинстве случаев эта строка ($GPRMC) используется для извлечения данных времени, даты и скорости.
Мы можем извлечь время и дату из строки $GPRMC при помощи подсчета запятых в этой строке. Таким образом, с помощью Arduino мы будем находить в GPS данных строку $GPRMC и сохранять ее в массиве, в котором время (в 24-часовом формате) может быть найдено после одной запятой, а дата – после 9 запятых. Время и дата потом сохраняются в соответствующих строках.
GPS спутники передают время и дату в формате UTC (Universal Time Coordinated — всеобщее скоординированное время), поэтому мы должны конвертировать его в местное время. В нашем проекте мы добавили к этому времени 5:30 чтобы получить время, соответствующее времени в Индии, вы для своего региона можете легко узнать соответствующую поправку.
Приложение для Android для передачи данных Arduino при помощи Bluetooth
Специально для проекта этих умных часов мы создали приложение для Android в среде Android Studio, которое можно скачать по этой ссылке. После скачивания установите это приложение в свой смартфон на Android, включите Bluetooth и установите связь с модулем HC-06. По умолчанию пароль для HC-06 — 1234 или 0000. При желании вы можете использовать любое другое подобное приложение из магазина PlayStore.
На следующем рисунке показан пример работы данного приложения (оно называется OLED) когда оно установило связь с модулем HC-06.
Это приложение может показывать все основные параметры работы смартфона как показано на следующем рисунке.
Особенности измерения скорости движения и скорости вращения.
При измерении скорости вращения бензинового двигателя надо обязательно учесть величину К, которая совсем не очевидна. Например, вы намотали провод на кабель свечи и ожидаете, что там будет одна искра на один оборот. Это совсем не так. Во-первых, у 4-тактного двигателя вспышка происходит один раз на два оборота, у 2-тактного один раз на оборот коленвала. Во-вторых, для упрощения системы зажигания коммутатор подаёт искру на неработающие в данный момент цилиндры, типа на выпуске. Для получения правильного К надо почитать документацию на двигатель или подсмотреть показания эталонного тахометра.
При измерении скорости движения частота обновления дисплея не имеет большого значения, особенно, если вы рисуете цифры, а не двигаете стрелку. Даже обновление информации раз в секунду не вызовет отторжения. С оборотами двигателя всё наоборот, индикатор должен откликаться гораздо быстрее на изменение оборотов.
Вывод информации
Типичная обида начинающего разработчика автомобильной и мотоциклетной электроники «стрелки дёргаются, цифры нечитабельны» лечится простым способом — надо обманывать клиента. Вы что думаете, автомобильный тахометр всегда показывает вам правду? Конечно же нет! Хотя вам этот обман нравится и вы хотите, чтобы ваш прибор дурил голову так же.
Стрелки
Если включить зажигание на новом модном автомобиле или мотоцикле, стрелки приборов сделают красивый вжух до максимума и медленнее опадут до нуля. Вот! Вот это нам и надо сделать. Надо, чтобы при показе максимальной величины стрелка не метнулась к ней мгновенно и не упала как акции лохотрона в ноль.
Итак, нам надо учитывать максимальную скорость стрелки на увеличение и максимальную на уменьшение показаний. Совсем хорошо сделать эти скорости нелинейными, чтобы стрелка сначала двигалась быстрее, а потом чуть помедленнее приближалась к заданному значению.
Вот пример с нелинейным выводом показаний:
Цифры
С цифрами всё намного сложнее. Быстрые изменения показаний приводят к тому, что несколько порядков сливаются в мутное пятно. Для скорости, как и писал выше, можно задать интервал раз в секунду и глаз успеет прочитать три цифры.
В мототехнике не зря делают аналоговые индикаторы оборотов, точные цифры не нужны, важна относительная близость к оборотам максимального крутящего момента, к максимальным вообще и холостые.
Я предлагаю менять частоту вывода информации на дисплей в зависимости от степени изменения величины. Если обороты меняются, скажем, на 5% от последнего подсчёта, а не показа — можно затупить и показывать раз в 300-500мс. Если на 20%, то показывать раз в 100мс.
Можно огрубить шкалу и показывать только две значащие цифры
С учётом мототематики, можно довольно точно показывать обороты холостого хода как описано чуть выше и огрублять вывод на оборотах от двух холостых. На высоких оборотах для гонщиков важнее делать блинкеры типа «передачу вниз», «передачу вверх» и «ты спалишь движок». То есть держать двигатель около максимального крутящего момента и не дать ему крутиться выше максимальных разрешённых оборотов. Блинкеры замечательно делаются с помощью SmartDelay когда можно унаследовать от этого класса свой с заданной ногой контроллера и частотой мигания, там есть методы для переопределения и они вызываются раз в заданное время.
Функции счёта времени
Данные функции возвращают время, прошедшее с момента запуска микроконтроллера. Таких функций у нас две:
- millis() – Возвращает количество миллисекунд, прошедших с запуска. Возвращает unsigned int, от 1 до 4 294 967 295 миллисекунд (
50 суток), имеет разрешение 1 миллисекунда, после переполнения сбрасывается в 0. Работает на системном таймере Timer 0micros() – Возвращает количество микросекунд, прошедших с запуска. Возвращает unsigned int, от 4 до 4 294 967 295 микросекунд (
70 минут), имеет разрешение в 4 микросекунды, после переполнения сбрасывается в 0. Работает на системном таймере Timer 0
Вы спросите, а как время со старта МК поможет нам организовать действия по времени? Очень просто, схема вот такая:
- Выполнили действие
- Запомнили текущее время со старта МК (в отдельную переменную)
- Ищем разницу между текущим временем и запомненным
- Как только разница больше нужного нам времени “Таймера” – выполняем действие и так по кругу
Реализация такого “таймера на millis()” выглядит вот так:
Напомню, что uint32_t это второе название типа данных unsigned long, просто оно короче в записи. Почему переменная должна быть именно такого типа? Потому что функция millis() возвращает именно этот тип данных, т.е. если мы сделаем нашу переменную например типа int, то она переполнится через 32.7 секунды. Но миллис тоже ограничен числом 4 294 967 295, и при переполнении тоже сбросится в 0. Сделает он это через 4 294 967 295 / 1000 / 60 / 60 / 24 = 49.7 суток. Значит ли это, что наш таймер “сломается” через 50 суток? Нет, данная конструкция спокойно переживает переход через 0 и работает дальше, не верьте диванным экспертам, проверьте =)
Вернёмся к вопросу многозадачности: хотим выполнять одно действие два раза в секунду, второе – три, и третье – 10. Нам понадобится 3 переменные таймера и 3 конструкции с условием:
И вот так мы можем например 10 раз в секунду опросить датчик, фильтровать значения, и два раза в секунду выводить показания на дисплей. И три раза в секунду мигать лампочкой. Почему нет?
Конструкция громоздкая, но используется очень часто, у меня в крупном проекте может быть десяток таких таймеров. Поэтому для ускорения написания кода я сделал библиотечку GyverTimer.
Работа схемы
Схема устройства представлена на следующем рисунке.
Плата Arduino управляет всеми процессами на этой схеме: она принимает данные от GPS модуля, извлекает дату и время из строки $GPRMC и показывает их на экране ЖК дисплея.
Контакты данных ЖК дисплея D4, D5, D6, D7 подсоединены к контактам 5, 4 , 3, 2 Arduino, а контакты управления ЖК дисплея RS и EN подсоединены к контактам 7 и 6 Arduino. Контакт передачи Tx GPS модуля подсоединен к контакту Rx (pin 10) платы Arduino (мы этот контакт сделаем входом последовательного порта с помощью соответствующей бибблиотеки). Контакты земли GPS модуля и платы Arduino соединены вместе. В данной схеме мы использовали GPS модуль SKG13BL, функционирующий на скорости 9800 бод/с, плату Arduino (ее последовательный порт) также можно сконфигурировать на работу со скоростью 9800 бод/с с помощью команды “Serial.begin(9800)”.
Работа программного обеспечения
Интерфейс DS5000
Программа, приведённая в Приложении, написана для взаимодействия DS5000 с DS1307 с помощью двухпроводного интерфейса. DS5000 запрограммирован с использованием макетной платы DS5000T фирмы Dallas Semiconductor, которая позволяет использовать ПК в качестве терминала ввода/вывода. Программные средства KIT5K поставляемые вместе с макетной платой DS5000T обеспечивают высокоуровневый интерфейс для загрузки программных приложений в DS5000 или установки его параметров через Program command. Программное обеспечение KIT5K содержит эмулятор терминала ввода/вывода, чтобы позволить пользователю запускать программные приложения в микроконтроллер DS5000, который связан с пользователем через COM порт ПК.
Исходный код DS1307
Первый раздел исходного кода, расположенный в Приложении, используется при конфигурации DS5000 для последовательного соединения с ПК. Также в начале кода находится подпрограмма MASTER_CONTROLLER, которая используется для управления демонстрационной программой.
Подпрограммы, которые следуют непосредственно за подпрограммой MASTER_CONTROLLER, являются драйверами низкого уровня и служат для управления двухпроводным интерфейсом. Они не являются индивидуальными для DS1307, а могут быть использованы с любым совместимым с двухпроводным интерфейсом «ведомым» устройством. Вот эти подпрограммы:
SEND_START
Подпрограмма используется для генерации состояния START на двухпроводной шине.
SEND_STOP
Подпрограмма используется для генерации состояния STOP на двухпроводной шине.
SEND_BYTE
Подпрограмма посылает 8-разрядное слово (первым является старший значащий бит (MSB)) по двухпроводной шине и девятый тактовый импульс для импульса подтверждения приёма.
READ_BYTE
Подпрограмма читает 8-разрядное слово с двухпроводной шины. Она проверяет очищен ли флаг LASTREAD после того, как считан последний байт из «ведомого» устройства. Если это был не последний байт, то DS5000 посылает импульс подтверждения по девятому тактовому импульсу, а если это был последний считанный байт из «ведомого» устройства, то DS5000 посылает «неподтверждение».
SCL_HIGH
Подпрограмма осуществляет переход линии SCL из низкого в высокое состояние и обеспечивает высокое состояние линии SCL перед продолжением.
DELAY и DELAY_4
Эти две подпрограммы включены для обеспечения сохранения временной диаграммы двухпроводной шины.
Остальная часть кода, включённая в приложение, специально предназначена для демонстрации функций DS1307. Продемонстрированы следующие функции:
Setting Time
Время считывается с клавиатуры и сохраняется в сверхоперативной памяти DS5000. Затем оно передаётся по двухпроводной шине в DS1307.
Set RAM
Одиночный байт в шестнадцатеричном виде считывается с клавиатуры и записывается в RAM DS1307.
Read Date/Time
Дата и время считываются по двухпроводной шине и сохраняются в сверхоперативной памяти DS5000. Затем они выводятся на экран. Это продолжается до тех пор, пока не будет нажата кнопка на клавиатуре.
OSC On/OSC Off
Тактовый генератор DS1307 может быть включен или выключен.
SQW/OUT On/SQW/OUT Off
Функция SQW/OUT может быть включена или выключена. Она будет переключаться на частоте 1 Гц.
Таблица 1. AC электрические характеристики
Параметр | Символ | Эффективноезначение | Единицы |
Тактовая частота SCL | fSCL | 59 | кГц |
Время свободного состояния шины между состояниями STOP и START | tBUF | 5.7 | мкс |
Время удержания(повторенного) состояния START | tHD:STA | 6.2 | мкс |
Период низкого состояния тактового импульса SCL | tLOW | 10.5 | мкс |
Период высокого состояния тактового импульса SCL | tHIGH | 6.5 | мкс |
Время установки для повторного состояния START | tSU:STA | 5.3 | мкс |
Время удержания данных | tHD:DAT | 5.5 | мкс |
Время установки данных | tSU:DAT | 3.1 | мкс |
Время установки для состояния STOP | tSU:STO | 5.4 | мкс |
Заключение
Было показано, как правильно подсоединять напрямую DS1307 или любое двухпроводное «ведомое» устройство к 8051-совместимому микроконтроллеру. Соединение должно быть таким, чтобы временная диаграмма двухпроводного интерфейса на микроконтроллере не нарушалась драйверами низкого уровня. Для этого в программный код должны быть включены подпрограммы задержки. Приведённых в таблице 1 эффективных значений, придерживались при конфигурации аппаратной части, описанной в данном техническом руководстве.
Документация
Rus Пример программы на языке Асемблер | ||
100 Kb Engl Исходный фаил | ||
Rus Описание интерфейса I2C | ||
Програмное обеспечение микроконтроллеров MCS-51 | ||
200 Kb Engl Описание DS1307 — часы реального времени с IIC интерфейсом |
Главная —
Микросхемы —
DOC —
ЖКИ —
Источники питания —
Электромеханика —
Интерфейсы —
Программы —
Применения —
Статьи
Кухонный таймер Ардуино с энкодером
Сейчас рассмотрим, как сделать таймер на Ардуино своими руками с энкодером и LCD. Принцип управления, подобен предыдущему варианту. Поворотом ручки энкодера можно задать необходимый временной интервал, а нажатием на ручку можно запускать и останавливать обратный отсчет времени. Далее размещена схема сборки проекта на Arduino Nano, этот проект можно собрать и на плате Arduino Uno.
Скетч таймера обратного отсчета времени
#include <Wire.h> // библиотека для протокола I2C #include <LiquidCrystal_I2C.h> // библиотека для LCD 1602 LiquidCrystal_I2C LCD(0x27, 20, 2); // присваиваем имя дисплею #include <RotaryEncoder.h> // библиотека для энкодера RotaryEncoder encoder(4, 2); // пины подключение энкодера (DT, CLK) // задаем шаг энкодера, максимальное и минимальное значение #define STEPS 1 #define POSMIN 0 #define POSMAX 30 int lastPos, newPos; boolean buttonWasUp = true; byte w = 0; int SEC = 0; int MIN = 0; unsigned long timer; void setup() { pinMode(6, INPUT_PULLUP); // пин для кнопки энкодера encoder.setPosition(0 / STEPS); pinMode(10, OUTPUT); // подключаем светодиод и зуммер pinMode(12, OUTPUT); digitalWrite(10, HIGH); LCD.init(); // инициализация дисплея LCD.backlight(); // включение подсветки LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); } void loop() { // проверяем положение ручки энкодера encoder.tick(); newPos = encoder.getPosition() * STEPS; if (newPos < POSMIN) { encoder.setPosition(POSMIN / STEPS); newPos = POSMIN; } else if (newPos > POSMAX) { encoder.setPosition(POSMAX / STEPS); newPos = POSMAX; } // если положение изменилось - меняем переменную MIN и выводим на дисплей if (lastPos != newPos) { MIN = newPos; lastPos = newPos; LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); } // если была нажата кнопка энкодера запускаем отсчет времени boolean buttonIsUp = digitalRead(6); if (buttonWasUp && !buttonIsUp && MIN > 0) { delay(10); buttonIsUp = digitalRead(6); if (!buttonIsUp) { if (SEC == 0) { SEC = 60; MIN = MIN - 1; } if (MIN < 0 ) { MIN = 0; } digitalWrite(10, LOW); w = 1; } } buttonWasUp = buttonIsUp; // запоминаем состояние кнопки while (w == 1 ) { // если прошло 995 мс - вычитаем одну секунду от переменной SEC if (millis() - timer > 993) { timer = millis(); SEC = SEC - 1; // если отсчет закончился - обнуляемся, включаем сигнал и выходим из цикла if (SEC == 0 && MIN == 0) { lastPos = 0; newPos = 0; MIN = 0; SEC = 0; LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); digitalWrite(10, HIGH); tone(12, 100); delay(500); noTone(12); w = 0; } // если секунды дошли до нуля - вычитаем одну минуту if (SEC == 0 && w==1) { SEC = 59; MIN = MIN - 1; if (MIN < 0 ) { MIN = 0; } } // если из цикла while еще не вышли - выводим информацию на дисплей if (w == 1) { LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER START"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); } } // если была нажата кнопка - обнуляем переменные и выходим из цикла buttonIsUp = digitalRead(6); if (buttonWasUp && !buttonIsUp) { delay(10); buttonIsUp = digitalRead(6); if (!buttonIsUp) { lastPos = 0; newPos = 0; MIN = 0; SEC = 0; LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); digitalWrite(10, HIGH); w = 0; } } buttonWasUp = buttonIsUp; // запоминаем состояние кнопки } }
Пояснения к коду:
- частоту звукового сигнала можно изменить через команду tone();
- для скетча потребуется установить библиотеку RotaryEncoder.