Кухонный таймер на ардуино,

Простейший таймер

Реле времени (схема ниже) производит подключение нагрузки к питанию на время 1-60 сек. Транзисторный ключ управляет электронным реле К1, который подключает потребитель к сети контактом К1.1.

В исходном состоянии переключатель S1 замыкает конденсатор С1 на сопротивление R2, который поддерживает его разряженным. Электромагнитный переключатель К1 при этом не работает, поскольку транзистор заперт. При подключении конденсатора к питающей сети (верхнее положение контакта S1) начинается его зарядка. Через базу протекает ток, который открывает транзистор и включается К1, замыкая цепь нагрузки. Напряжение питания на реле времени — 12 вольт.

В процессе зарядки конденсатора базовый ток постепенно уменьшается. Соответственно падает величина коллекторного тока, пока К1 своим отключением не разомкнет цепь нагрузки контактом К1.1.

Чтобы снова подключить нагрузку к сети на заданный период работы, схему следует снова перезапустить. Для этого переключатель устанавливается в нижнее положение «выключено», что приводит к разрядке конденсатора. Затем устройство снова включается с помощью S1 в течение заданного временного промежутка. Задержка регулируется с помощью установки резистора R1, а также может быть изменена, если конденсатор заменить на другой.

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

Кухонный таймер Ардуино с энкодером

Сейчас рассмотрим, как сделать таймер на Ардуино своими руками с энкодером и 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; // запоминаем состояние кнопки
  }
}

Пояснения к коду:

  1. частоту звукового сигнала можно изменить через команду tone();
  2. для скетча потребуется установить библиотеку RotaryEncoder.

Базовые вызовы

/**
 * Init ISR (Interrupt service routine) for the timer and start timer.
 * 
 * General algorithm
 * http://www.robotshop.com/letsmakerobots/arduino-101-timers-and-interrupts
 * 1. CPU frequency 16Mhz for Arduino
 * 2. maximum timer counter value (256 for 8bit, 65536 for 16bit timer)
 * 3. Divide CPU frequency through the choosen prescaler (16000000 / 256 = 62500)
 * 4. Divide result through the desired frequency (62500 / 2Hz = 31250)
 * 5. Verify the result against the maximum timer counter value (31250 < 65536 success).
 *    If fail, choose bigger prescaler.
 * 
 * Example: to set timer clock period to 20ms (50 operations per second == 50Hz)
 * 
 * 1) on 16MHz CPU (AVR Arduino)
 *   use prescaler 1:8 (TIMER_PRESCALER_1_8) and adjustment=40000-1:
 *   16000000/8/50=40000, minus 1 cause count from zero.
 * 
 * 2) on 80MHz CPU (PIC32MX ChipKIT)
 *   use prescaler 1:64 (TIMER_PRESCALER_1_64) and adjustment=25000-1:
 *   80000000/64/50=25000, minus 1 cause count from zero.
 *
 * 3) on 84MHz CPU (SAM Arduino Due)
 *   use prescaler 1:128 (TIMER_PRESCALER_1_128) and adjustment=13125-1:
 *   80000000/128/50=13125, minus 1 cause count from zero.
 * 
 * Timer interrupt handler timer_handle_interrupts would be called every 20ms
 * (50 times per second == 50Hz freq) in this case.
 * 
 * @param timer
 *   system timer id: use TIMER_DEFAULT for default timer
 *   or _TIMER1...TIMER9, _TIMER1_32BIT..._TIMER9_32BIT for specific timer.
 *   note: availability of specific timer depends on the platform.
 * @param prescaler
 *   timer prescaler (1, 2, 4, 8, 16, 32, 64, 128, 256, 1024),
 *   use constants: PRESCALER_1_1, PRESCALER_1_2, PRESCALER_1_8,
 *   PRESCALER_1_16, PRESCALER_1_32, PRESCALER_1_64, PRESCALER_1_128
 *   PRESCALER_1_256, PRESCALER_1_1024
 *   note: availability of specific prescaler depends on the platform.
 * @param adjustment
 *   adjustment divider after timer prescaled - timer compare match value.
 */
void timer_init_ISR(int timer, int prescaler, int adjustment);

/**
 * Stop ISR (Interrupt service routine) for the timer.
 * 
 * @param timer
 *     system timer id for started ISR
 */
void timer_stop_ISR(int timer);

/**
 * Timer ISR (Interrupt service routine) handler.
 * Implementation must be provided in module with user code.
 * 
 * @param timer
 *     system timer id for started ISR 
 */
void timer_handle_interrupts(int timer);

“Универсальное” электромагнитное реле

Электромагнитное реле является по сути управляемым механическим выключателем: подали на него ток – оно замкнуло контакты, сняли ток – разомкнуло. Контакты являются именно контактами: металлическими “пятаками”, которые прижимаются друг к другу. Именно поэтому такое реле может управлять как нагрузкой постоянного, так и переменного тока.

Сама катушка реле является неслабой индуктивной нагрузкой, что приводит к дополнительным проблемам (читай ниже), поэтому для управления “голым” реле нам понадобится дополнительная силовая и защитная цепь.

После изучения данного урока вы сами сможете её составить (транзистор и диод), а сейчас мы поговорим о модулях реле: готовая плата, на которой стоит само реле, а также цепи коммутации, защиты и даже оптическая развязка. Такие модули бывают “семейными” – с несколькими реле на борту. Спасибо китайцам за это! Смотрите варианты у меня в каталоге ссылок на Али.

Такое реле сделано специально для удобного управления с микроконтроллера: пины питания VCC (Vin, 5V) и GND подключаются к питанию, а далее реле управляется логическим сигналом, поданным на пин IN. С другой стороны стоит клеммник для подключения проводов, обычно контакты подписаны как NO, NC и COM. Это общепринятые названия пинов кнопок, переключателей и реле:

  • COM – Common, общий. Реле является переключающим, и пин COM является общим.
  • NO – Normal Open, нормально открытый. При неактивном реле данный контакт не соединён с COM. При активации реле он замыкается с COM.
  • NC – Normal Closed, нормально закрытый. При неактивном реле данный контакт соединён с COM. При активации реле он размыкается с COM.

Подключение нагрузки через реле думаю для всех является очевидным:

Важный момент: катушка реле в активном режиме потребляет около 60 мА, то есть подключать больше одного модуля реле при питании платы от USB не рекомендуется – уже появятся просадки по напряжению и помехи:

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

На плате, справа от надписи High/Low trigger есть перемычка, при помощи которой происходит переключение уровня. Электромагнитное реле имеет ряд недостатков перед остальными рассмотренными ниже способами, вы должны их знать и учитывать:

  • Ограниченное количество переключений: механический контакт изнашивается, особенно при большой и/или индуктивной нагрузке.
  • Противно щёлкает!
  • При большой нагрузке реле может “залипнуть”, поэтому для больших токов нужно использовать более мощные реле, которые придётся включать при помощи… маленьких реле. Или транзисторов.
  • Необходимы дополнительные цепи для управления реле, так как катушка является индуктивной нагрузкой, и нагрузкой самой по себе слишком большой для пина МК (решается использованием китайского модуля реле).
  • Очень большие наводки на всю линию питания при коммутации индуктивной нагрузки.
  • Относительно долгое переключение (невозможно поставить детектор нуля, читай ниже), при управлении индуктивными цепями переменного тока можно попасть на большой индуктивный выброс, необходимо ставить искрогасящие цепи.

Важный момент связан с коммутацией светодиодных светильников и ламп, особенно дешёвых: у них прямо на входе стоит конденсатор, который при резком подключении в цепь становится очень мощным потребителем и приводит к скачку тока. Скачок может быть настолько большим, что 15-20 Ваттная светодиодная лампа буквально сваривает контакты реле и оно “залипает”! Данный эффект сильнее выражен на дешёвых лампах, будьте с ними аккуратнее (за инфу спасибо DAK). При помощи реле можно плавно управлять сильно инерционной нагрузкой, такой как большой обогреватель. Для этого нужно использовать сверхнизкочастотный ШИМ сигнал, у меня есть готовая библиотека. Не забываем, что реле противно щёлкает и изнашивается, поэтому для таких целей лучше подходит твердотельное реле, о котором мы поговорим ниже.

Сборка

При реализации проекта я столкнулся с двумя основными трудностями:

  • Как определить, работает ли помпа, чтобы знать, когда начинать и заканчивать отсчет.
  • Как коммутировать 170В на 20 анодов (по 10 на каждую лампу).

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

Модифицированные библиотеки от Paul Stoffregen

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

Плата ШИМ выводы TimerOne ШИМ выводы TimerThree
Teensy 3.1 3, 4 25, 32
Teensy 3.0 3, 4  
Teensy 2.0 4, 14, 15 9
Teensy++ 2.0 25, 26, 27 14, 15, 16
Arduino Uno 9, 10  
Arduino Leonardo 9, 10, 11 5
Arduino Mega 11, 12, 13 2, 3, 5
Wiring-S 4, 5  
Sanguino 12, 13  

Методы модифицированных библиотек аналогичны описанным выше, но добавлен еще один метод управления запуском таймера:

Возобновляет работу остановленного таймера. Новый период не начинается.

Исходный код программы

Далее приведен полный текст программы. Работа нашего проекта продемонстрирована на видео, приведенном в конце статьи.

Arduino

#include<LiquidCrystal.h> //подключение библиотеки для работы с ЖК дисплеем
#define ledPin 7
LiquidCrystal lcd(8,9,10,11,12,13);
float value = 3035; //Preload timer value (3035 for 4 seconds)
void setup()
{
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print(«ARDUINO TIMERS»);
delay(2000);
lcd.clear();

pinMode(ledPin, OUTPUT);
pinMode(2,INPUT);
pinMode(4,INPUT);

noInterrupts(); // отключаем все прерывания

TCCR1A = 0;
TCCR1B = 0;
TCNT1 = value; // preload timer
TCCR1B |= (1 << CS10)|(1 << CS12); // 1024 prescaler (коэффициент деления предделителя)
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt ISR (разрешаем вызов процедуры обработки прерывания переполнения счетчика)
interrupts(); // разрешаем все прерывания
}
ISR(TIMER1_OVF_vect) // процедура обработки прерывания переполнения счетчика
{
TCNT1 = value; // preload timer
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //включаем и выключаем светодиод
}
void loop()
{
if(digitalRead(2) == HIGH)
{
value = value+10; //Incement preload value
}
if(digitalRead(4)== HIGH)
{
value = value-10; //Decrement preload value
}
lcd.setCursor(0,0);
lcd.print(value);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include<LiquidCrystal.h>            //подключение библиотеки для работы с ЖК дисплеем
#define ledPin 7

LiquidCrystallcd(8,9,10,11,12,13);

floatvalue=3035;//Preload timer value (3035 for 4 seconds)

voidsetup()

{

lcd.begin(16,2);

lcd.setCursor(,);

lcd.print(«ARDUINO TIMERS»);

delay(2000);

lcd.clear();

pinMode(ledPin,OUTPUT);

pinMode(2,INPUT);

pinMode(4,INPUT);

noInterrupts();// отключаем все прерывания

TCCR1A=;

TCCR1B=;

TCNT1=value;// preload timer

TCCR1B|=(1<<CS10)|(1<<CS12);// 1024 prescaler (коэффициент деления предделителя)

TIMSK1|=(1<<TOIE1);// enable timer overflow interrupt ISR (разрешаем вызов процедуры обработки прерывания переполнения счетчика)

interrupts();// разрешаем все прерывания

}

ISR(TIMER1_OVF_vect)// процедура обработки прерывания переполнения счетчика

{

TCNT1=value;// preload timer

digitalWrite(ledPin,digitalRead(ledPin)^1);//включаем и выключаем светодиод

}

voidloop()

{

if(digitalRead(2)==HIGH)

{

value=value+10;//Incement preload value

}

if(digitalRead(4)==HIGH)

{

value=value-10;//Decrement preload value

}

lcd.setCursor(,);

lcd.print(value);

}

Защита от помех DC

Раздельное питание

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

Искрогасящие цепи DC

При размыкании контактов в цепи питания индуктивной нагрузки происходит так называемый индуктивный выброс, который резко подбрасывает напряжение в цепи вплоть до того, что между контактами реле или выключателя может проскочить электрическая дуга (искра). В дуге нет ничего хорошего – она выжигает частички металла контактов, из за чего они изнашиваются и со временем приходят в негодность. Также такой скачок в цепи провоцирует электромагнитный выброс, который может навести в электронном устройстве сильные помехи и привести к сбоям или даже поломке! Самое опасное, что индуктивной нагрузкой может являться сам провод: вы наверняка видели, как искрит обычный выключатель света в комнате. Лампочка – не индуктивная нагрузка, но идущий к ней провод имеет индуктивность. Для защиты от выбросов ЭДС самоиндукции в цепи постоянного тока используют обыкновенный диод, установленный встречно-параллельно нагрузке и максимально близко к ней. Диод просто закоротит на себя выброс, и все дела:

Где VD – защитный диод, U1 – выключатель (транзистор, реле), а R и L схематично олицетворяют индуктивную нагрузку. Диод нужно ОБЯЗАТЕЛЬНО ставить при управлении индуктивной нагрузкой (электромотор, соленоид, клапан, электромагнит, катушка реле) при помощи транзистора, то есть вот так:

При управлении ШИМ сигналом рекомендуется ставить быстродействующие диоды (например серии 1N49xx) или диоды Шоттки (например серии 1N58xx), максимальный ток диода должен быть больше или равен максимальному току нагрузки.

Фильтры

Если силовая часть питается от одного источника с микроконтроллером, то помехи по питанию неизбежны. Простейший способ защитить МК от таких помех – конденсаторы по питанию как можно ближе к МК: электролит 6.3V 470 uF (мкФ) и керамический на 0.1-1 мкФ, они сгладят короткие просадки напряжения. Кстати, электролит с низким ESR справится с такой задачей максимально качественно.

Ещё лучше с фильтрацией помех справится LC фильтр, состоящий из индуктивности и конденсатора. Индуктивность нужно брать с номиналом в районе 100-300 мкГн и с током насыщения больше, чем ток нагрузки после фильтра. Конденсатор – электролит с ёмкостью 100-1000 uF в зависимости опять же от тока потребления нагрузки после фильтра. Подключается вот так, чем ближе к нагрузке – тем лучше:

Подробнее о расчёте фильтров можно почитать здесь.

Объяснение программы для Arduino

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

Arduino

#include <LiquidCrystal.h>
#include <Keypad.h>

long int set1;
long int set2;
long int set3;
long int set4;
long int j;
int t1, t2, t3, t4, t5, t6;
int r1, r2, r3;
char key;
String r;
String hours;
String minutes;
String seconds;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#include <LiquidCrystal.h>
#include <Keypad.h>
 

longintset1;

longintset2;

longintset3;

longintset4;

longintj;

intt1,t2,t3,t4,t5,t6;

intr1,r2,r3;

charkey;

Stringr8;

Stringhours;

Stringminutes;

Stringseconds;

Далее в программе мы сообщаем количество строк и столбцов в используемой нами клавиатуре и задаем карту клавиш клавиатуры.

Arduino

const byte ROWS = 4; // четыре строки
const byte COLS = 4; // четыре столбца

char keys = {
{‘1′,’2′,’3′,’A’},
{‘4′,’5′,’6′,’B’},
{‘7′,’8′,’9′,’C’},
{‘*’,’0′,’#’,’D’}
};

1
2
3
4
5
6
7
8
9

constbyteROWS=4;// четыре строки

constbyteCOLS=4;// четыре столбца

charkeysROWSCOLS={

{‘1′,’2′,’3′,’A’},

{‘4′,’5′,’6′,’B’},

{‘7′,’8′,’9′,’C’},

{‘*’,’0′,’#’,’D’}

};

Затем мы сообщаем плате Arduino к каким ее контактам подключены контакты строк и столбцов клавиатуры, а также контакты ЖК дисплея.

Arduino

byte rowPins = { 6, 7, 8, 9 }; // подсоедините контакты клавиатуры ROW0, ROW1, ROW2 и ROW3 к этим контактам Arduino
byte colPins = { 10, 11, 12, 13 };// подсоедините контакты клавиатуры COL0, COL1, COL2 и COL3 к этим контактам Arduino
LiquidCrystal lcd(A0, A1, 5, 4, 3, 2); // создаем объект ЖК дисплея с параметрами (rs, enable, d4, d5, d6, d7)

1
2
3

byterowPinsROWS={6,7,8,9};// подсоедините контакты клавиатуры ROW0, ROW1, ROW2 и ROW3 к этим контактам Arduino

bytecolPinsCOLS={10,11,12,13};// подсоедините контакты клавиатуры COL0, COL1, COL2 и COL3 к этим контактам Arduino

LiquidCrystallcd(A0,A1,5,4,3,2);// создаем объект ЖК дисплея с параметрами (rs, enable, d4, d5, d6, d7)

Следующая команда создает объект клавиатуры в программе.

Arduino

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

1 Keypadkpd=Keypad(makeKeymap(keys),rowPins,colPins,ROWS,COLS);

В функции setFeedingTime() после нажатия кнопки мы можем ввести время, с которого начнется отсчет, для начала счета после этого необходимо нажать клавишу D на клавиатуре.

Arduino

void setFeedingTime()
{
feed = true;
int i=0;

lcd.clear();
lcd.setCursor(0,0);
lcd.print(«Set feeding Time»);
lcd.clear();
lcd.print(«HH:MM:SS»);
lcd.setCursor(0,1);

while(1){
key = kpd.getKey();
char j;

if(key!=NO_KEY){
lcd.setCursor(j,1);
lcd.print(key);
r = key-48;
i++;
j++;

if (j==2 || j == 5)
{
lcd.print(«:»); j++;
}
delay(500);
}

if (key == ‘D’)
{key=0; break; }
}
lcd.clear();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

voidsetFeedingTime()

{

feed=true;

inti=;

lcd.clear();

lcd.setCursor(,);

lcd.print(«Set feeding Time»);

lcd.clear();

lcd.print(«HH:MM:SS»);

lcd.setCursor(,1);

while(1){

key=kpd.getKey();

charj;

if(key!=NO_KEY){

lcd.setCursor(j,1);

lcd.print(key);

ri=key-48;

i++;

j++;

if(j==2||j==5)

{

lcd.print(«:»);j++;

}

delay(500);

}

if(key==’D’)

{key=;break;}

}

lcd.clear();

}

В функции void setup() мы инициализируем ЖК дисплей и последовательную связь, а также задаем режим работы (на ввод или вывод данных) для используемых контактов.

Arduino

void setup()
{
lcd.begin(16,2);
Serial.begin(9600);
pinMode(A0, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A3, INPUT);
pinMode(A4, OUTPUT);
}

1
2
3
4
5
6
7
8
9

voidsetup()

{

lcd.begin(16,2);

Serial.begin(9600);

pinMode(A0,OUTPUT);

pinMode(A1,OUTPUT);

pinMode(A3,INPUT);

pinMode(A4,OUTPUT);

}

Встроенные варианты частот

/**
 * freq: 500KHz = 500000 ops/sec
 * period: 1sec/500000 = 2us
 */
void timer_init_ISR_500KHz(int timer);

/**
 * freq: 200KHz = 200000 ops/sec
 * period: 1sec/200000 = 5us
 */
void timer_init_ISR_200KHz(int timer);s

/**
 * freq: 100KHz = 100000 ops/sec
 * period: 1sec/100000 = 10us
 */
void timer_init_ISR_100KHz(int timer);

/**
 * freq: 50KHz = 50000 ops/sec
 * period: 1sec/50000 = 20us
 */
void timer_init_ISR_50KHz(int timer);

/**
 * freq: 20KHz = 20000 ops/sec
 * period: 1sec/20000 = 50us
 */
void timer_init_ISR_20KHz(int timer);

/**
 * freq: 10KHz = 10000 ops/sec
 * period: 1sec/10000 = 100us
 */
void timer_init_ISR_10KHz(int timer);

/**
 * freq: 5KHz = 5000 ops/sec
 * period: 1sec/5000 = 200us
 */
void timer_init_ISR_5KHz(int timer);

/**
 * freq: 2KHz = 2000 ops/sec
 * period: 1sec/2000 = 500us
 */
void timer_init_ISR_2KHz(int timer);

/**
 * freq: 1KHz = 1000 ops/sec
 * period: 1sec/1000 = 1ms
 */
void timer_init_ISR_1KHz(int timer);

/**
 * freq: 500Hz = 500 ops/sec
 * period: 1sec/500 = 2ms
 */
void timer_init_ISR_500Hz(int timer);

/**
 * freq: 200Hz = 200 ops/sec
 * period: 1sec/200 = 5ms
 */
void timer_init_ISR_200Hz(int timer);

/**
 * freq: 100Hz = 100 ops/sec
 * period: 1sec/100 = 10ms
 */
void timer_init_ISR_100Hz(int timer);

/**
 * freq: 50Hz = 50 ops/sec
 * period: 1sec/50 = 20ms
 */
void timer_init_ISR_50Hz(int timer);

/**
 * freq: 20Hz = 20 ops/sec
 * period: 1sec/20 = 50ms
 */
void timer_init_ISR_20Hz(int timer);

/**
 * freq: 10Hz = 10 ops/sec
 * period: 1sec/10 = 100ms
 */
void timer_init_ISR_10Hz(int timer);

/**
 * freq: 5Hz = 5 ops/sec
 * period: 1sec/5 = 200ms
 */
void timer_init_ISR_5Hz(int timer);

/**
 * freq: 2Hz = 2 ops/sec
 * period: 1sec/2 = 500ms
 */
void timer_init_ISR_2Hz(int timer);

/**
 * freq: 1Hz = 1 ops/sec
 * period: 1sec
 */
void timer_init_ISR_1Hz(int timer);

Функция millis вместо delay

Функция millis() позволит выполнить задержку без delay на ардуино, тем самым обойти недостатки предыдущих способов. Максимальное значение параметра millis такое же, как и у функции delay (4294967295мс или 50 суток). При переполнении значение просто сбрасывается в 0, не забывайте об этом.

С помощью millis мы не останавливаем выполнение всего скетча, а просто указываем, сколько времени ардуино должна просто “обходить” именно тот блок кода, который мы хотим приостановить. В отличие от delay millis сама по себе ничего не останавливает. Данная команда просто возвращает нам от встроенного таймера микроконтроллера количество миллисекунд, прошедших с момента запуска. При каждом вызове loop Мы сами измеряем время, прошедшее с последнего вызова нашего кода и если разница времени меньше желаемой паузы, то игнорируем код. Как только разница станет больше нужной паузы, мы выполняем код, получаем текущее время с помощью той же millis и запоминаем его – это время будет новой точкой отсчета. В следующем цикле отсчет уже будет от новой точки и мы опять будем игнорировать код, пока новая разница millis и нашего сохраненного прежде значения не достигнет вновь желаемой паузы.

Вот пример, наглядно иллюстрирующий работу команды:

Сначала мы вводим переменную timing, в ней будет храниться количество миллисекунд. По умолчанию значение переменной равно 0. В основной части программы проверяем условие: если количество миллисекунд с запуска микроконтроллера минус число, записанное в переменную timing больше, чем 10000, то выполняется действие по выводу сообщения в монитор порта и в переменную записывается текущее значение времени. В результате работы программы каждые 10 секунд в монитор порта будет выводиться надпись 10 seconds. Данный способ позволяет моргать светодиодом без delay.

Library Reference

  • — Constructor

  • — Destructor

  • — Starts the timer

  • — Stops and resets the timer

  • — Pauses the timer. It can continue where it paused after calling start method

  • — Resets the timer elapsed time. If the timer is running, it will continue to do so

  • — Returns the elapsed time in milliseconds

  • — Sets the interval time. The callback function will be called every «interval» ms. The repeat count will prevent the callback from being called after a number of repetetions (if it’s > 0). WARNING: The timer will stop after that (if repeat count > 0)

  • — Sets the timeout time. The callback function will be called just once. WARNING: The timer will stop after that

  • — Removes the interval, so the timer can be used as a normal timer

  • — Sets the callback function

  • — Updates the timer. It must be called in a loop in order to make it work

  • — Check if the timer is paused

  • — Check if the timer is stopped

  • — Check if the timer is running

  • protected — Calls the callback function

  • private — Stores the start time

  • private — Stores the current time

  • private — Stores if the timer is running

  • private — Stores if the timer is paused

  • private — Stores the interval, if setted

  • private — Stores the last time the callback was called

  • private — Stores the number of times remaining to call the callback function

  • private — Stores the number of times to call the callback function

  • private — Stores if the interval was setted

  • private — Stores the callback function

  • — Constructor

  • — Calls the callback method

  • private — Stores the object to be used

  • private — Stores the method to be called

  • — Get the TimerManager instance
  • — Updates all timers
  • — Starts all timers
  • — Stops and resets all timers
  • — Pauses all timers
  • — Resets elapsed time of all timers. If any timer was running, it will continue to do so
  • private — Adds a timer to the TimerManager
  • private — Removes a timer from the TimerManager. Return true if the timer was removed, false otherwise
  • private — Constructor
  • private — Constructor
  • private — Copy operator
  • private — A node of Timers used to implement a simple linked list
  • private — A pointer to the first TimerNode
  • private — A pointer to the last TimerNode

Задержки

Простейшей с точки зрения использования функцией времени является задержка, их у нас две:

  • delay(time) – “Приостанавливает” выполнение кода на time миллисекунд. Дальше функции delay выполнение кода не идёт, за исключением прерываний. Использовать рекомендуется только в самых крайних или тех случаях, когда delay не влияет на скорость работы устройства. time принимает тип данных unsigned long и может приостановить выполнение на срок от 1 мс до

50 суток (4 294 967 295 миллисекунд) с разрешением в 1 миллисекунду. Работает на системном таймере Timer 0delayMicroseconds(time) – Аналог delay(), приостанавливает выполнение кода на time микросекунд. time принимает тип данных unsigned long и может приостановить выполнение на срок от 4 мкс до

70 минут (4 294 967 295 микросекунд) с разрешением 4 микросекунды. Работает на системном таймере Timer 0

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

И вот мы можем делать какое-то действие два раза в секунду. А что делать, если нам нужно выполнять одно действие два раза в секунду, а другое – три? А третье – 10 раз в секунду например. Сразу привыкаем к той мысли, что задержки лучше вообще не использовать в реальном коде. Разве что delayMicroseconds, он бывает нужен для генерации каких-то протоколов связи. Нормальным инструментом для тайм-менеджмента своего кода являются функции, которые считают время со старта МК.

Видео. Освещение дома на Ардуино

— Для питания платы и LED ленты используется блок питания на 12 Вольт с выходным напряжением 1 Ампер. При этом общее потребление электроэнергии, если включить все освещение сразу, не превышает 15 Ватт, что сравнимо с одной энергосберегающей лампочкой. Но при этом освещение на Arduino работает в санузле (туалет совмещен с ванной), в прихожей, в коридоре на 2 этаже, в гостиной (зона отдыха и зона кухни). Для управления освещением на Arduino используется пульт от телевизора. Кроме того, что автоматическое включение подсветки в ванной и коридоре — это очень удобно, можно еще и сэкономить на оплате за электроэнергию. Также в скетче прописан цикл для создания эффекта присутствия, когда вы уезжаете из дома на несколько дней. Освещение включается и выключается в разных комнатах по заданному алгоритму.

Реле Ардуино: распиновка, характеристики

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

Реле SRD-05VDC-SL-C Ардуино: распиновка, характеристики

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

Согласно характеристикам реле SRD-05VDC-SL-C, для переключения контактов достаточно около 5 Вольт 20 мА, выводы на Ардуино способны выдавать до 40 мА. Таким образом с помощью Ардуино мы можем управлять не только лампой накаливания, но и любым бытовым прибором — обогревателем, холодильником и т.д. Полевые транзисторы на Ардуино могут управлять токами только до 100 Вольт.