Multitasking with Arduino | Relay Timer Controller | using millis

Introduction

In this demonstration, I show a Relay Timer Controller. The multitasking is achieve using the power of millis function.

The following task are achieve:

  1. The On state of the relay (simulated through the LED).
  2. Navigating the LCD menu using buttons.

Circuit Diagram

Video Demonstration

Call To Action

If you like this article, please give me thumbs up and share this to your friends.

If you have question, please write it in the comment box.

Please do not forget to Subscribe:
Click this to Subscribe

Thank you and have a good day.

Source Code

  1#include "LiquidCrystal.h"
  2
  3// LED definitions for easy reference
  4#define RED_LED   A0
  5#define YEL_LED   A1
  6#define BLU_LED   A2
  7#define LEFT_SW   A3
  8#define ENTR_SW   A4
  9#define RGHT_SW   A5
 10
 11// Variables for the LCD
 12LiquidCrystal lcd(13, 12, 11, 10, 9, 8);
 13byte charUp[8] = {
 14        B00100,
 15        B01110,
 16        B11111,
 17        B00000,
 18        B00000,
 19        B00000,
 20        B00000,
 21        B00000
 22};
 23byte charDown[8] = {
 24        B00000,
 25        B00000,
 26        B00000,
 27        B00000,
 28        B00000,
 29        B11111,
 30        B01110,
 31        B00100
 32};
 33byte charUpDown[8] = {
 34        B00100,
 35        B01110,
 36        B11111,
 37        B00000,
 38        B00000,
 39        B11111,
 40        B01110,
 41        B00100
 42};
 43
 44// Variables for the button switch
 45#define DEFAULT_DELAY 300
 46char buttonPressed = '0';
 47byte menu = 1;
 48byte sub = 1;
 49byte menuLevel = 0;     // Level 0: no menu display, display anything you like
 50                        // Level 1: display main menu
 51                        // Level 2: display sub menu
 52                        // Level 3: display sub menu of sub menu
 53unsigned long relay_val_1 = 0;
 54unsigned long relay_val_2 = 0;
 55unsigned long relay_val_3 = 0;
 56
 57// Variables for the button debouncing
 58bool currState_L = HIGH;
 59bool currState_E = HIGH;
 60bool currState_R = HIGH;
 61bool prevState_L = HIGH; 
 62bool prevState_E = HIGH; 
 63bool prevState_R = HIGH; 
 64unsigned long prevTime_L = 0;
 65unsigned long prevTime_E = 0;
 66unsigned long prevTime_R = 0;
 67unsigned long waitTime_L = 50;
 68unsigned long waitTime_E = 50;
 69unsigned long waitTime_R = 50;
 70
 71// Variables for the LED
 72bool ledState_RED = LOW;            // this holds the current state of the LED
 73bool ledState_YEL = LOW;      
 74bool ledState_BLU = LOW;            
 75unsigned long prevMillis_RED = 0;   // this holds the start time of millis for computing the elapse time
 76unsigned long prevMillis_YEL = 0;   
 77unsigned long prevMillis_BLU = 0;   
 78unsigned long interval_RED = 3000;  // this holds the interval of each toggle of LED
 79unsigned long interval_YEL = 5000;  
 80unsigned long interval_BLU = 7000;  
 81unsigned long currMillis_ALL = 0;   // this holds the current time captured by the millis() function
 82                                    // it is use in general for synchronization purpose
 83unsigned long elapsed_RED = 0;
 84unsigned long elapsed_YEL = 0;
 85unsigned long elapsed_BLU = 0;
 86
 87bool run_RED = false;
 88bool run_YEL = false;
 89bool run_BLU = false;
 90
 91void setup() {
 92  lcd.begin(16,2);
 93  lcd.createChar(0, charUp);
 94  lcd.createChar(1, charDown);
 95  lcd.createChar(2, charUpDown);
 96  lcd.println("  TechToTinker  ");
 97  
 98  // Set the pin directions for LEDs
 99  pinMode(RED_LED, OUTPUT);
100  pinMode(YEL_LED, OUTPUT);
101  pinMode(BLU_LED, OUTPUT);
102
103  pinMode(LEFT_SW, INPUT_PULLUP);
104  pinMode(ENTR_SW, INPUT_PULLUP);
105  pinMode(RGHT_SW, INPUT_PULLUP);
106
107  updateLevel_0();
108}
109
110void loop() {
111  // Store the currently time
112  // Notice: it use a single current time for synchonization with other function
113  currMillis_ALL = millis();
114  checkButton();
115  // Virtually process all the task
116  process_RED();
117  process_YEL();
118  process_BLU();
119}
120
121void checkButton() {
122  // Button Debouncing
123  bool currRead_L = digitalRead(LEFT_SW);
124  bool currRead_E = digitalRead(ENTR_SW);
125  bool currRead_R = digitalRead(RGHT_SW);
126  if (currRead_L != prevState_L) {
127    prevTime_L = millis();
128  }
129  if (currRead_E != prevState_E) {
130    prevTime_E = millis();
131  }
132  if (currRead_R != prevState_R) {
133    prevTime_R = millis();
134  }
135
136  if ((millis() - prevTime_L) > waitTime_L) {
137    if (currRead_L != currState_L) {
138      currState_L = currRead_L;
139      if (currState_L == LOW) {
140        buttonPressed = 'L';
141      } 
142    }
143  } else buttonPressed = '0';
144  if ((millis() - prevTime_E) > waitTime_E) {
145    if (currRead_E != currState_E) {
146      currState_E = currRead_E;
147      if (currState_E == LOW) {
148        buttonPressed = 'E';
149      } 
150    }
151  } else buttonPressed = '0';
152  if ((millis() - prevTime_R) > waitTime_R) {
153    if (currRead_R != currState_R) {
154      currState_R = currRead_R;
155      if (currState_R == LOW) {
156        buttonPressed = 'R';
157      } else {
158        //buttonPressed = '0';
159      }
160    }
161  } else buttonPressed = '0';
162
163  prevState_L = currRead_L;
164  prevState_E = currRead_E;
165  prevState_R = currRead_R;
166  processButton(buttonPressed);
167}
168
169void processButton(char buttonPressed) {
170  switch(menuLevel) {
171    case 0:                     // Level 0, home screen
172      switch ( buttonPressed ) {
173        case 'E':   // Enter
174          menu = 1;
175          menuLevel = 1;        // go to main menu
176          updateLevel_1();      // show main menu
177          delay(DEFAULT_DELAY);
178          break;
179        case 'L':   // Left button
180          break;
181        case 'R':   // Right button
182          break;
183        default:
184          break;
185      }
186      break;
187    case 1:                       // Level 1, main menu
188      switch ( buttonPressed ) {
189        case 'E':                 // Enter
190          if (menu == 4) {        // Hide menu is selected
191            menuLevel = 0;        // go to home screen
192            updateLevel_0();      // show home screen
193          } else {
194            sub = 1;
195            menuLevel = 2;        // go to sub menu
196            updateLevel_2();      // show sub menu
197          }
198          delay(DEFAULT_DELAY);
199          break;
200        case 'L':                 // Left
201          menu--;
202          updateLevel_1();        // show main menu
203          delay(DEFAULT_DELAY);
204          break;
205        case 'R':                 // Right
206          menu++;
207          updateLevel_1();        // show main menu
208          delay(DEFAULT_DELAY);
209          break;
210        default:
211          break;
212      } 
213      break;
214    case 2:                     // Level 2, sub menu
215      switch ( buttonPressed ) {
216        case 'E':               // Enter
217          if (sub == 2) {         // Edit
218            menuLevel = 3;        // go to sub menu of sub menu
219            updateLevel_3();      // show sub menu of sub menu
220          } else if (sub == 3) {  // Execute
221            executeAction();
222            delay(1000);
223            menuLevel = 2;        // go to sub menu
224            updateLevel_2();      // show sub menu
225          } else if (sub == 4) {  // Back
226            menuLevel = 1;        // go back to level 1
227            updateLevel_1();      // show main menu
228          }
229          delay(DEFAULT_DELAY);
230          break;
231        case 'L':               // Left button
232          sub--;
233          updateLevel_2();
234          delay(DEFAULT_DELAY);
235          break;
236        case 'R':               // Right button
237          sub++;
238          updateLevel_2();      // show main menu
239          delay(DEFAULT_DELAY);
240          break;
241        default:
242          break;
243      } 
244      break;
245    case 3:                     // Level 3, sub menu of sub menu
246      switch ( buttonPressed ) {
247        case 'E':               // Enter
248          menuLevel = 2;        // go back to level 2
249          updateLevel_2();      // show sub menu
250          delay(DEFAULT_DELAY);
251          break;
252        case 'L':               // Left
253          if (sub == 2) {       // edit value
254            if        (menu == 1) {
255              if (relay_val_1 == 0) {
256                relay_val_1 = 0;
257              } else {
258                relay_val_1 = relay_val_1 - 1;
259              }
260            } else if (menu == 2) {
261              if (relay_val_2 == 0) {
262                relay_val_2 = 0;
263              } else {
264                relay_val_2 = relay_val_2 - 1;
265              }
266            } else if (menu == 3) {
267              if (relay_val_3 == 0) {
268                relay_val_3 = 0;
269              } else {
270                relay_val_3 = relay_val_3 - 1;
271              }
272            }
273          }
274          updateLevel_3();      // show sub menu
275          delay(DEFAULT_DELAY);
276          break;
277        case 'R':               // Right
278          if (sub == 2) {       // edit value
279            if        (menu == 1) {
280              if (relay_val_1 < 3600000) {  // 1 hour max
281                relay_val_1 = relay_val_1 + 1;
282              } else {
283                relay_val_1 = 3600000;
284              }
285            } else if (menu == 2) {       
286              if (relay_val_2 < 3600000) {  // 1 hour max
287                relay_val_2 = relay_val_2 + 1;
288              } else {
289                relay_val_2 = 3600000;
290              }
291            } else if (menu == 3) {
292              if (relay_val_3 < 3600000) {  // 1 hour max  
293                relay_val_3 = relay_val_3 + 1;
294              } else {
295                relay_val_3 = 3600000;
296              }
297            }
298          }
299          updateLevel_3();      // show sub menu
300          delay(DEFAULT_DELAY);
301          break;
302        default:  
303          break;
304      } 
305      break;
306    default:
307      break;
308  }
309}
310
311void updateLevel_0() {
312  lcd.clear();
313  lcd.println("  TechToTinker  ");
314  lcd.setCursor(0,1);
315  lcd.println("  - E for menu  ");
316}
317
318void updateLevel_1 () {
319  switch (menu) {
320    case 0:
321      menu = 1;
322      break;
323    case 1:
324      lcd.clear();
325      lcd.print(">Relay RED       ");
326      lcd.setCursor(0, 1);
327      lcd.print(" Relay YEL       ");
328      lcd.setCursor(15,1);
329      lcd.write((byte)1);     // down arrow
330      break;
331    case 2:
332      lcd.clear();
333      lcd.print(" Relay RED       ");
334      lcd.setCursor(0, 1);
335      lcd.print(">Relay YEL       ");
336      lcd.setCursor(15,1);
337      lcd.write((byte)2);     // up and down arrow
338      break;
339    case 3:
340      lcd.clear();
341      lcd.print(">Relay BLU      ");
342      lcd.setCursor(0, 1);
343      lcd.print(" Hide Menu      ");
344      lcd.setCursor(15,1);
345      lcd.write((byte)2);     // up and down arrow
346      break;
347    case 4:
348      lcd.clear();
349      lcd.print(" Relay BLU      ");
350      lcd.setCursor(0, 1);
351      lcd.print(">Hide Menu      ");
352      lcd.setCursor(15,1);
353      lcd.write((byte)0);     // up arrow
354      break;
355    case 5:
356      menu = 4;
357      break;
358  }
359}
360
361void updateLevel_2 () {
362  switch (menu) {
363    case 0:
364      break;
365    case 1:                                 // Relay RED
366      switch (sub) {
367        case 0:
368          break;
369        case 1:
370          lcd.clear();
371          lcd.print(" Relay RED:     ");
372          lcd.setCursor(0, 1);
373          lcd.print("  Duration = ");
374          lcd.print(relay_val_1);
375          lcd.setCursor(15,1);
376          lcd.write((byte)1);     // down arrow
377          break;
378        case 2:
379          lcd.clear();
380          lcd.print(" Relay RED:     ");
381          lcd.setCursor(0, 1);
382          lcd.print("  Edit          ");
383          lcd.print(relay_val_1);
384          lcd.setCursor(15,1);
385          lcd.write((byte)2);     // up and down arrow
386          break;
387        case 3:
388          lcd.clear();
389          lcd.print(" Relay RED:     ");
390          lcd.setCursor(0, 1);
391          lcd.print("  Execute       ");
392          lcd.setCursor(15,1);
393          lcd.write((byte)2);     // up and down arrow
394          break;
395        case 4:
396          lcd.clear();
397          lcd.print(" Relay RED:     ");
398          lcd.setCursor(0, 1);
399          lcd.print("  Back          ");
400          lcd.setCursor(15,1);
401          lcd.write((byte)0);      // up arrow
402        default:
403          break;
404      }
405      break;
406    case 2:                                 // Relay YEL
407      switch (sub) {
408        case 0:
409          break;
410        case 1:
411          lcd.clear();
412          lcd.print(" Relay YEL:     ");
413          lcd.setCursor(0, 1);
414          lcd.print("  Duration = ");
415          lcd.print(relay_val_2);
416          lcd.setCursor(15,1);
417          lcd.write((byte)1);     // down arrow
418          break;
419        case 2:
420          lcd.clear();
421          lcd.print(" Relay YEL:     ");
422          lcd.setCursor(0, 1);
423          lcd.print("  Edit          ");
424          lcd.setCursor(15,1);
425          lcd.write((byte)2);     // up and down arrow
426          break;
427        case 3:
428          lcd.clear();
429          lcd.print(" Relay YEL:     ");
430          lcd.setCursor(0, 1);
431          lcd.print("  Execute       ");
432          lcd.setCursor(15,1);
433          lcd.write((byte)0);     // up arrow
434          break;
435        case 4:
436          lcd.clear();
437          lcd.print(" Relay YEL:     ");
438          lcd.setCursor(0, 1);
439          lcd.print("  Back          ");
440          lcd.setCursor(15,1);
441          lcd.write((byte)0);      // up arrow
442        default:
443          break;
444      }
445      break;
446    case 3:                               // Relay BLU
447      switch (sub) {
448        case 0:
449          break;
450        case 1:
451          lcd.clear();
452          lcd.print(" Relay BLU      ");
453          lcd.setCursor(0, 1);
454          lcd.print("  Duration = ");
455          lcd.print(relay_val_3);
456          lcd.setCursor(15,1);
457          lcd.write((byte)1);     // down arrow
458          break;
459        case 2:
460          lcd.clear();
461          lcd.print(" Relay BLU:     ");
462          lcd.setCursor(0, 1);
463          lcd.print("  Edit          ");
464          lcd.setCursor(15,1);
465          lcd.write((byte)2);     // up and down arrow
466          break;
467        case 3:
468          lcd.clear();
469          lcd.print(" Relay BLU:     ");
470          lcd.setCursor(0, 1);
471          lcd.print("  Execute       ");
472          lcd.setCursor(15,1);
473          lcd.write((byte)2);     // up and down arrow
474          break;
475        case 4:
476          lcd.clear();
477          lcd.print(" Relay BLU:     ");
478          lcd.setCursor(0, 1);
479          lcd.print("  Back          ");
480          lcd.setCursor(15,1);
481          lcd.write((byte)0);      // up arrow
482          break;
483        default:
484          break;
485      }
486      break;
487    case 4:
488      //sub = 3;
489      break;
490  }
491}
492
493void updateLevel_3 () {
494  switch (menu) {
495    case 0:
496
497      break;
498    case 1:
499      lcd.clear();
500      lcd.print(" Relay RED:     ");
501      lcd.setCursor(0, 1);
502      lcd.print("  Duration = ");
503      lcd.print(relay_val_1);
504      break;
505    case 2:
506      lcd.clear();
507      lcd.print(" Relay YEL:     ");
508      lcd.setCursor(0, 1);
509      lcd.print("  Duration = ");
510      lcd.print(relay_val_2);
511      break;
512    case 3:
513      lcd.clear();
514      lcd.print(" Relay BLU:     ");
515      lcd.setCursor(0, 1);
516      lcd.print("  Duration = ");
517      lcd.print(relay_val_3);
518      break;
519    case 4:
520      sub = 3;
521      break;
522  }
523}
524
525void executeAction () {
526  switch (menu) {
527    case 0:
528
529      break;
530    case 1:
531      lcd.clear();
532      lcd.print(" Executing RED  ");
533      run_RED = true;                   // execute the timer
534      prevMillis_RED = millis();        // record the start time
535      interval_RED = relay_val_1 * 1000;// duration in seconds
536      digitalWrite(RED_LED, HIGH);
537      break;
538    case 2:
539      lcd.clear();
540      lcd.print(" Executing YEL  ");
541      run_YEL = true;
542      prevMillis_YEL = millis();        // record the start time
543      interval_YEL = relay_val_2 * 1000;// duration in seconds
544      digitalWrite(YEL_LED, HIGH);
545      break;
546    case 3:
547      lcd.clear();
548      lcd.print(" Executing BLU  ");
549      run_BLU = true;
550      prevMillis_BLU = millis();        // record the start time
551      interval_BLU = relay_val_3 * 1000;// duration in seconds
552      digitalWrite(BLU_LED, HIGH);
553      break;
554    case 4:
555      sub = 3;
556      break;
557  }
558}
559
560void process_RED() {
561  if (run_RED) {
562    if (millis() - prevMillis_RED >= interval_RED) {
563      // Elapse time has reached the interval
564      // Save the current time as previous time
565      //prevMillis_RED = currMillis_ALL;
566      run_RED = false;
567      digitalWrite(RED_LED, LOW);
568    }
569  }
570}
571
572void process_YEL() {
573  if (run_YEL) {
574    if (millis() - prevMillis_YEL >= interval_YEL) {
575      // Elapse time has reached the interval
576      // Save the current time as previous time
577      //prevMillis_RED = currMillis_ALL;
578      run_YEL = false;
579      digitalWrite(YEL_LED, LOW);
580    }
581  }
582}
583
584void process_BLU() {
585  if (run_BLU) {
586    if (millis() - prevMillis_BLU >= interval_BLU) {
587      // Elapse time has reached the interval
588      // Save the current time as previous time
589      //prevMillis_RED = currMillis_ALL;
590      run_BLU = false;
591      digitalWrite(BLU_LED, LOW);
592    }
593  }
594}


Posts in this series



No comments yet!

GitHub-flavored Markdown & a sane subset of HTML is supported.