A User-Friendly Interface in a Simple Timer
Make a user-friendly interface with an alphanumeric display, a rotary encoder and a finite state machine.
Components and supplies
2
Logic Level FET N-Channel
1
Trimmer Potentiometer, 10 kohm
1
speaker 50 ohms
1
Standard LCD - 16x2 White on Blue
1
Arduino Nano Every
1
Rotary Encoder with Push-Button
1
Through Hole Resistor, 10 ohm
1
Breadboard (generic)
Project description
Code
Timer
c_cpp
1[code] 2/****************************************/ 3/************ A simple timer ************/ 4/**** with a user-friendly interface ****/ 5/****************************************/ 6 7/* Libraries and their licence files */ 8#include <LiquidCrystal.h> 9 10// arduino-fsm is free software: you can redistribute it and/or modify it under 11// the terms of the GNU Lesser General Public License as published by the Free 12// Software Foundation, either version 3 of the License, or (at your option) 13// any later version. 14// 15// arduino-fsm is distributed in the hope that it will be useful, but WITHOUT 16// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 18// for more details. 19// 20// You should have received a copy of the GNU Lesser General Public License 21// along with arduino-fsm. If not, see <http://www.gnu.org/licenses/>. 22 23#include <Fsm.h> 24 25/* 26 EEPROM.h - EEPROM library 27 Original Copyright (c) 2006 David A. Mellis. All right reserved. 28 New version by Christopher Andrews 2015. 29 30 This library is free software; you can redistribute it and/or 31 modify it under the terms of the GNU Lesser General Public 32 License as published by the Free Software Foundation; either 33 version 2.1 of the License, or (at your option) any later version. 34 35 This library is distributed in the hope that it will be useful, 36 but WITHOUT ANY WARRANTY; without even the implied warranty of 37 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 38 Lesser General Public License for more details. 39 40 You should have received a copy of the GNU Lesser General Public 41 License along with this library; if not, write to the Free Software 42 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 43*/ 44 45#include <EEPROM.h> 46 47/* Hardware connections */ 48#define LCDRs 12 49#define LCDEn 11 50#define LCDD4 5 51#define LCDD5 4 52#define LCDD6 3 53#define LCDD7 2 54#define backLightPin 10 55#define rotaryAPin 6 56#define rotaryBPin 7 57#define rotarySwitchPin 8 58#define beeperPin A0 59 60/* Global variables */ 61unsigned long remainingTime=0; // seconds 62bool timerRunning=false; 63int dimming, toneFrequencyIndex; 64String lines[2]; 65 66/* Frequencies of notes from C4 to B7 */ 67int toneFrequencies[48]= 68{ 69 262, 277, 294, 311, 330, 349, 370, 392, 415, 70 440, 466, 494, 523, 554, 587, 622, 659, 698, 71 740, 784, 831, 880, 932, 988, 1047, 1109, 72 1175, 1245, 1319, 1397, 1480, 1568, 1661, 73 1760, 1865, 1976, 2093, 2217, 2349, 2489, 74 2637, 2794, 2960, 3136, 3332, 3520, 3729, 75 3951 76}; 77 78/* Addresses in the EEPROM */ 79enum EEPROMAddresses 80{ 81 EEPROMId0, 82 EEPROMId1, 83 EEPROMId2, 84 EEPROMDimming, 85 EEPROMTone 86}; 87 88/* Events generated by the rotary switch */ 89enum potentialEvents 90{ 91 noEvent, 92 cwRot, // clockwise rotation 93 ccwRot, // counterclockwise rotation 94 shortSwitchPress, longSwitchPress, 95 timeOut 96} event; 97 98#define maxTime 30000 99#define longPressTime 2500 100 101LiquidCrystal lcd(LCDRs, LCDEn, LCDD4, LCDD5, 102 LCDD6, LCDD7); 103 104 105/* Display objects and functions */ 106 107/* An object of the class flash can make an 108 area of contiguous characters on a line 109 of the LCD blink at any frequency and 110 any duty cycle 111*/ 112class flash 113{ 114public : 115 bool on; 116 int line, row, number, period, cycle; 117 void setFlash(bool flashOn, 118 int flashLine=0, 119 int flashRow=0, 120 int flashNumber=0, 121 int flashPeriod=800, 122 int flashCycle=60) 123 { 124 on=flashOn; 125 line=flashLine; 126 row=flashRow; 127 number=flashNumber; 128 period=flashPeriod; 129 cycle=flashCycle; 130 } 131 132 flash() // Constructor 133 { 134 on=false; 135 } 136}; 137 138#define flashingAreasNumber 2 139flash displayFlash[flashingAreasNumber]; 140 141/* The text to be displayed on the LCD is 142 stored in the variables lines[0] and 143 lines[1]. This fonction displays it 144 on the LCD while blinking the areas 145 defined in the objects of class flash. 146*/ 147void LCDShow(void) 148{ 149 String line; 150 for(int i=0; i<2; i++) 151 { 152 line=lines[i]; 153 for(int j=0; j<flashingAreasNumber; j++) 154 { 155 if(displayFlash[j].on && 156 displayFlash[j].line==i && 157 (millis()%displayFlash[j].period > 158 ((long)displayFlash[j].period * 159 (long)displayFlash[j].cycle)/100)) 160 { 161 for(int k=0; k<displayFlash[j].number; k++) 162 line.setCharAt 163 (displayFlash[j].row+k, ' '); 164 } 165 } 166 lcd.setCursor(0, i); 167 lcd.print(line); 168 } 169} 170 171void clearDisplay(void) 172{ 173 for(int i=0; i<flashingAreasNumber; i++) 174 displayFlash[i].setFlash(false); 175 lines[0]=" "; 176 lines[1]=" "; 177 LCDShow(); 178} 179 180/* Functions called by the finite state 181 machine. They display and set the different 182 values accessible to the user : delay of 183 the timer, brightness of the LCD and 184 frequency of the beeper. Some values are 185 stored into the EEPROM after setting. 186*/ 187 188void timerDisplay(void) 189{ 190 int hour, minute, second; 191 192 hour=remainingTime/3600; 193 minute=(remainingTime-hour*3600L)/60; 194 second=remainingTime%60; 195 196 lines[0]="Timer "; 197 lines[1]=" "; 198 if(hour<10) lines[1]+=" "; 199 lines[1]+=String(hour); 200 lines[1]+=":"; 201 if(minute<10) lines[1]+="0"; 202 lines[1]+=String(minute); 203 lines[1]+=":"; 204 if(second<10) lines[1]+="0"; 205 lines[1]+=String(second); 206 LCDShow(); 207} 208 209void dimmingDisplay(void) 210{ 211 lines[0]="Brightness "; 212 lines[1]=" "+String(dimming); 213 LCDShow(); 214} 215 216void toneDisplay(void) 217{ 218 lines[0]="Tone "; 219 lines[1]=" "; 220 if (toneFrequencies[toneFrequencyIndex]<1000) 221 lines[1]+=" "; 222 lines[1]+=String(toneFrequencies 223 [toneFrequencyIndex]); 224 lines[1]+=" Hz "; 225 LCDShow(); 226} 227 228void timerHourSetEnter(void) 229{ 230 clearDisplay(); 231/* 232 The two following lines just for showing 233 that it is possible to program two areas 234 blinking at different frequencies. 235 */ 236 displayFlash[0].setFlash(true, 0, 0, 5, 1000, 237 50); 238 displayFlash[1].setFlash(true, 1, 3, 2); 239 timerDisplay(); 240 timerRunning=false; 241} 242 243void timerHourSet(void) 244{ 245 int hour; 246 hour=remainingTime/3600; 247 if (event==cwRot && hour<23) hour++; 248 else 249 if (event==ccwRot && hour>0) hour--; 250 remainingTime=remainingTime%3600+hour*3600L; 251 timerDisplay(); 252} 253 254void timerMinuteSetEnter(void) 255{ 256 clearDisplay(); 257 displayFlash[0].setFlash(true, 0, 0, 5, 1000, 258 50); 259 displayFlash[1].setFlash(true, 1, 6, 2); 260 timerDisplay(); 261} 262 263void timerMinuteSet(void) 264{ 265 int minute; 266 minute=(remainingTime%3600)/60; 267 if (event==cwRot && minute<59) minute++; 268 else 269 if (event==ccwRot && minute>0) minute--; 270 remainingTime=(remainingTime/3600)*3600+ 271 remainingTime%60 + 272 minute*60L; 273 timerDisplay(); 274} 275 276void timerSecondSetEnter(void) 277{ 278 clearDisplay(); 279 displayFlash[0].setFlash(true, 0, 0, 5, 1000, 280 50); 281 displayFlash[1].setFlash(true, 1, 9, 2); 282 timerDisplay(); 283} 284 285void timerSecondSet(void) 286{ 287 int second; 288 second=remainingTime%60; 289 if (event==cwRot && second<59) second++; 290 else 291 if (event==ccwRot && second>0) second--; 292 remainingTime=(remainingTime/60)*60+second; 293 timerDisplay(); 294} 295 296void timerSecondSetExit(void) 297{ 298 if (remainingTime!=0) timerRunning=true; 299} 300 301/* This function stops the beeper when it is 302 beeping, and suspends or restarts the 303 counting of the timer. 304*/ 305void toggleTimerRunning(void) 306{ 307 if (remainingTime==0) 308 { 309 timerRunning=false; 310 noTone(beeperPin); 311 } 312 else timerRunning=!timerRunning; 313} 314 315void dimmingSetEnter(void) 316{ 317 displayFlash[0].setFlash(true, 1, 6, 1); 318 dimmingDisplay(); 319} 320 321void dimmingSet(void) 322{ 323 if(event==cwRot && dimming<9) dimming++; 324 else 325 if(event==ccwRot && dimming>0) dimming--; 326 dimmingDisplay(); 327} 328 329void dimmingSetExit(void) 330{ 331 EEPROM.update(EEPROMDimming, dimming); 332} 333 334void toneSetEnter(void) 335{ 336 displayFlash[0].setFlash(true, 1, 4, 4); 337 toneDisplay(); 338 noTone(beeperPin); 339 tone(beeperPin, 340 toneFrequencies[toneFrequencyIndex]); 341} 342 343void toneSet(void) 344{ 345 if(event==cwRot && toneFrequencyIndex<47) 346 toneFrequencyIndex++; 347 else 348 if (event==ccwRot && toneFrequencyIndex>0) 349 toneFrequencyIndex--; 350 toneDisplay(); 351 if(event==cwRot || event==ccwRot) 352 { 353 noTone(beeperPin); 354 tone(beeperPin, 355 toneFrequencies[toneFrequencyIndex]); 356 } 357} 358 359 void toneSetExit(void) 360{ 361 noTone(beeperPin); 362 EEPROM.update(EEPROMTone, 363 toneFrequencyIndex); 364} 365 366/* 367 States of the finite state machine 368*/ 369State timerDisplayState(&clearDisplay, 370 &timerDisplay, NULL); 371State dimmingDisplayState(&clearDisplay, 372 &dimmingDisplay, 373 NULL); 374State toneDisplayState(&clearDisplay, 375 &toneDisplay, 376 NULL); 377State timerHourSetState(&timerHourSetEnter, 378 &timerHourSet, 379 NULL); 380State timerMinuteSetState(&timerMinuteSetEnter, 381 &timerMinuteSet, 382 NULL); 383State timerSecondSetState(&timerSecondSetEnter, 384 &timerSecondSet, 385 &timerSecondSetExit); 386State dimmingSetState(&dimmingSetEnter, 387 &dimmingSet, 388 &dimmingSetExit); 389State toneSetState(&toneSetEnter, 390 &toneSet, 391 &toneSetExit); 392 393Fsm UIFsm(&timerDisplayState); 394 395/* 396 This function defines all the transitions 397 between the different states of the 398 finite state machine. 399 */ 400void setTransitions(void) 401{ 402 UIFsm.add_transition(&timerDisplayState, 403 &dimmingDisplayState, 404 cwRot, NULL); 405 UIFsm.add_transition(&dimmingDisplayState, 406 &toneDisplayState, 407 cwRot, NULL); 408 UIFsm.add_transition(&toneDisplayState, 409 &timerDisplayState, 410 cwRot, NULL); 411 412 UIFsm.add_transition(&timerDisplayState, 413 &toneDisplayState, 414 ccwRot, NULL); 415 UIFsm.add_transition(&toneDisplayState, 416 &dimmingDisplayState, 417 ccwRot, NULL); 418 UIFsm.add_transition(&dimmingDisplayState, 419 &timerDisplayState, 420 ccwRot, NULL); 421 422 UIFsm.add_transition(&timerDisplayState, 423 &timerHourSetState, 424 longSwitchPress, NULL); 425 UIFsm.add_transition(&timerHourSetState, 426 &timerMinuteSetState, 427 shortSwitchPress, NULL); 428 UIFsm.add_transition(&timerHourSetState, 429 &timerDisplayState, 430 timeOut, NULL); 431 UIFsm.add_transition(&timerMinuteSetState, 432 &timerSecondSetState, 433 shortSwitchPress, NULL); 434 UIFsm.add_transition(&timerMinuteSetState, 435 &timerDisplayState, 436 timeOut, NULL); 437 UIFsm.add_transition(&timerSecondSetState, 438 &timerDisplayState, 439 shortSwitchPress, NULL); 440 441 UIFsm.add_transition(&dimmingDisplayState, 442 &dimmingSetState, 443 longSwitchPress, NULL); 444 UIFsm.add_transition(&dimmingSetState, 445 &dimmingDisplayState, 446 shortSwitchPress, NULL); 447 UIFsm.add_transition(&dimmingSetState, 448 &dimmingDisplayState, 449 timeOut, NULL); 450 451 UIFsm.add_transition(&toneDisplayState, 452 &toneSetState, 453 longSwitchPress, NULL); 454 UIFsm.add_transition(&toneSetState, 455 &toneDisplayState, 456 shortSwitchPress, NULL); 457 UIFsm.add_transition(&toneSetState, 458 &toneDisplayState, 459 timeOut, NULL); 460/* 461 This is a special transition. It does not 462 change the current state, but it calls the 463 function toggleTimerRunning when the 464 current state is timerDisplayState and the 465 user shortly presses the rotary switch. This 466 is done to suspend or restart the count 467 when the timer is counting, and to stop the 468 beeper after the end of the count. 469*/ 470 UIFsm.add_transition(&timerDisplayState, 471 &timerDisplayState, 472 shortSwitchPress, 473 &toggleTimerRunning); 474} 475 476/* 477 This function manages the counting of the 478 timer and the beep when the count has 479 reached zero. 480*/ 481void timerManagement(void) 482{ 483 static unsigned long memoTime, actualTime; 484 static bool toneStarted=false; 485 486 if (timerRunning) 487 { 488 if(remainingTime==0) 489 { 490 if((millis()%1000)>500) 491 { 492 if(toneStarted==false) 493 { 494 tone(beeperPin, 495 toneFrequencies[toneFrequencyIndex]); 496 toneStarted=true; 497 } 498 } 499 else 500 { 501 if(toneStarted==true) 502 { 503 noTone(beeperPin); 504 toneStarted=false; 505 } 506 } 507 } 508 else // remaining time not zero 509 { 510 actualTime=millis(); 511 if(actualTime-memoTime>=1000) 512 { 513 memoTime=actualTime; 514 remainingTime--; 515 } 516 } 517 } 518} 519 520/* 521 This function reads the actions of the user 522 on the rotary switch and converts them into 523 events. 524 */ 525void rotaryEncoderRead(void) 526{ 527 unsigned long pressedSwitchDelay=0; 528 unsigned long pressedSwitchTime=0; 529 static unsigned long lastEventTime; 530 static bool longPressTreated=false; 531 532 event=noEvent; 533 if(digitalRead(rotaryAPin)==LOW) 534 { 535 if(digitalRead(rotaryBPin)==LOW) 536 event=ccwRot; 537 else 538 event=cwRot; 539 while(digitalRead(rotaryAPin)==LOW); 540 lastEventTime=millis(); 541 } 542 543 if(digitalRead(rotarySwitchPin)==LOW) 544 { 545 if (!longPressTreated) 546 { 547 pressedSwitchTime=millis(); 548 while(digitalRead(rotarySwitchPin)==LOW 549 && (millis()-pressedSwitchTime)< 550 longPressTime); 551 lastEventTime=millis(); 552 pressedSwitchDelay=millis()- 553 pressedSwitchTime; 554 if(pressedSwitchDelay>=longPressTime) 555 { 556 event=longSwitchPress; 557 longPressTreated=true; 558 } 559 else if(pressedSwitchDelay>50) 560 event=shortSwitchPress; 561 } 562 } 563 else 564 longPressTreated=false; 565 if(millis()-lastEventTime>maxTime) 566 event=timeOut; 567} 568 569/* 570 The settings of brightness and tone frquency 571 are stored into the EEPROM to be kept when 572 the power is off. This function is called at 573 power on to get these values back. To be sure 574 that they are the right ones, the first 575 three bytes of the EEPROM must contain "tim". 576 If it is not the case, the values are 577 replaced by the default values. 578*/ 579void readEEPROM(void) 580{ 581 if(EEPROM.read(EEPROMId0)==short('t') && 582 EEPROM.read(EEPROMId1)==short('i') && 583 EEPROM.read(EEPROMId2)==short('m')) 584 { 585 dimming=EEPROM.read(EEPROMDimming); 586 toneFrequencyIndex=EEPROM.read(EEPROMTone); 587 } 588 else 589 { 590 EEPROM.update(EEPROMId0, 't'); 591 EEPROM.update(EEPROMId1, 'i'); 592 EEPROM.update(EEPROMId2, 'm'); 593 dimming=5; 594 EEPROM.update(EEPROMDimming, dimming); 595 toneFrequencyIndex=21; 596 EEPROM.update(EEPROMTone, 597 toneFrequencyIndex); 598 } 599} 600 601void setup() 602{ 603 pinMode(rotaryAPin, INPUT); 604 pinMode(rotaryBPin, INPUT); 605 pinMode(rotarySwitchPin, INPUT_PULLUP); 606 pinMode(beeperPin, OUTPUT); 607 digitalWrite(beeperPin, LOW); 608 lines[0].reserve(17); 609 lines[1].reserve(17); 610 lcd.begin(16, 2); 611 lcd.clear(); 612/* 613 If the rotary switch is pressed during power 614 on, the values in the EEPROM are replaced 615 by the default values. This is achived by 616 deleting the chain "tim" in the first 617 three bytes of the EEPROM. 618*/ 619 if(digitalRead(rotarySwitchPin)==LOW) 620 { 621 EEPROM.update(EEPROMId0, 0xFF); 622 EEPROM.update(EEPROMId1, 0xFF); 623 EEPROM.update(EEPROMId2, 0xFF); 624 } 625 readEEPROM(); 626 setTransitions(); 627} 628 629void loop() 630{ 631 rotaryEncoderRead(); 632 timerManagement(); 633 UIFsm.run_machine(); 634 UIFsm.trigger(event); 635 analogWrite(backLightPin, dimming*27); 636} 637[/code]
Timer
c_cpp
1[code] 2/****************************************/ 3/************ A simple timer ************/ 4/**** with a user-friendly interface ****/ 5/****************************************/ 6 7/* Libraries and their licence files */ 8#include <LiquidCrystal.h> 9 10// arduino-fsm is free software: you can redistribute it and/or modify it under 11// the terms of the GNU Lesser General Public License as published by the Free 12// Software Foundation, either version 3 of the License, or (at your option) 13// any later version. 14// 15// arduino-fsm is distributed in the hope that it will be useful, but WITHOUT 16// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 18// for more details. 19// 20// You should have received a copy of the GNU Lesser General Public License 21// along with arduino-fsm. If not, see <http://www.gnu.org/licenses/>. 22 23#include <Fsm.h> 24 25/* 26 EEPROM.h - EEPROM library 27 Original Copyright (c) 2006 David A. Mellis. All right reserved. 28 New version by Christopher Andrews 2015. 29 30 This library is free software; you can redistribute it and/or 31 modify it under the terms of the GNU Lesser General Public 32 License as published by the Free Software Foundation; either 33 version 2.1 of the License, or (at your option) any later version. 34 35 This library is distributed in the hope that it will be useful, 36 but WITHOUT ANY WARRANTY; without even the implied warranty of 37 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 38 Lesser General Public License for more details. 39 40 You should have received a copy of the GNU Lesser General Public 41 License along with this library; if not, write to the Free Software 42 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 43*/ 44 45#include <EEPROM.h> 46 47/* Hardware connections */ 48#define LCDRs 12 49#define LCDEn 11 50#define LCDD4 5 51#define LCDD5 4 52#define LCDD6 3 53#define LCDD7 2 54#define backLightPin 10 55#define rotaryAPin 6 56#define rotaryBPin 7 57#define rotarySwitchPin 8 58#define beeperPin A0 59 60/* Global variables */ 61unsigned long remainingTime=0; // seconds 62bool timerRunning=false; 63int dimming, toneFrequencyIndex; 64String lines[2]; 65 66/* Frequencies of notes from C4 to B7 */ 67int toneFrequencies[48]= 68{ 69 262, 277, 294, 311, 330, 349, 370, 392, 415, 70 440, 466, 494, 523, 554, 587, 622, 659, 698, 71 740, 784, 831, 880, 932, 988, 1047, 1109, 72 1175, 1245, 1319, 1397, 1480, 1568, 1661, 73 1760, 1865, 1976, 2093, 2217, 2349, 2489, 74 2637, 2794, 2960, 3136, 3332, 3520, 3729, 75 3951 76}; 77 78/* Addresses in the EEPROM */ 79enum EEPROMAddresses 80{ 81 EEPROMId0, 82 EEPROMId1, 83 EEPROMId2, 84 EEPROMDimming, 85 EEPROMTone 86}; 87 88/* Events generated by the rotary switch */ 89enum potentialEvents 90{ 91 noEvent, 92 cwRot, // clockwise rotation 93 ccwRot, // counterclockwise rotation 94 shortSwitchPress, longSwitchPress, 95 timeOut 96} event; 97 98#define maxTime 30000 99#define longPressTime 2500 100 101LiquidCrystal lcd(LCDRs, LCDEn, LCDD4, LCDD5, 102 LCDD6, LCDD7); 103 104 105/* Display objects and functions */ 106 107/* An object of the class flash can make an 108 area of contiguous characters on a line 109 of the LCD blink at any frequency and 110 any duty cycle 111*/ 112class flash 113{ 114public : 115 bool on; 116 int line, row, number, period, cycle; 117 void setFlash(bool flashOn, 118 int flashLine=0, 119 int flashRow=0, 120 int flashNumber=0, 121 int flashPeriod=800, 122 int flashCycle=60) 123 { 124 on=flashOn; 125 line=flashLine; 126 row=flashRow; 127 number=flashNumber; 128 period=flashPeriod; 129 cycle=flashCycle; 130 } 131 132 flash() // Constructor 133 { 134 on=false; 135 } 136}; 137 138#define flashingAreasNumber 2 139flash displayFlash[flashingAreasNumber]; 140 141/* The text to be displayed on the LCD is 142 stored in the variables lines[0] and 143 lines[1]. This fonction displays it 144 on the LCD while blinking the areas 145 defined in the objects of class flash. 146*/ 147void LCDShow(void) 148{ 149 String line; 150 for(int i=0; i<2; i++) 151 { 152 line=lines[i]; 153 for(int j=0; j<flashingAreasNumber; j++) 154 { 155 if(displayFlash[j].on && 156 displayFlash[j].line==i && 157 (millis()%displayFlash[j].period > 158 ((long)displayFlash[j].period * 159 (long)displayFlash[j].cycle)/100)) 160 { 161 for(int k=0; k<displayFlash[j].number; k++) 162 line.setCharAt 163 (displayFlash[j].row+k, ' '); 164 } 165 } 166 lcd.setCursor(0, i); 167 lcd.print(line); 168 } 169} 170 171void clearDisplay(void) 172{ 173 for(int i=0; i<flashingAreasNumber; i++) 174 displayFlash[i].setFlash(false); 175 lines[0]=" "; 176 lines[1]=" "; 177 LCDShow(); 178} 179 180/* Functions called by the finite state 181 machine. They display and set the different 182 values accessible to the user : delay of 183 the timer, brightness of the LCD and 184 frequency of the beeper. Some values are 185 stored into the EEPROM after setting. 186*/ 187 188void timerDisplay(void) 189{ 190 int hour, minute, second; 191 192 hour=remainingTime/3600; 193 minute=(remainingTime-hour*3600L)/60; 194 second=remainingTime%60; 195 196 lines[0]="Timer "; 197 lines[1]=" "; 198 if(hour<10) lines[1]+=" "; 199 lines[1]+=String(hour); 200 lines[1]+=":"; 201 if(minute<10) lines[1]+="0"; 202 lines[1]+=String(minute); 203 lines[1]+=":"; 204 if(second<10) lines[1]+="0"; 205 lines[1]+=String(second); 206 LCDShow(); 207} 208 209void dimmingDisplay(void) 210{ 211 lines[0]="Brightness "; 212 lines[1]=" "+String(dimming); 213 LCDShow(); 214} 215 216void toneDisplay(void) 217{ 218 lines[0]="Tone "; 219 lines[1]=" "; 220 if (toneFrequencies[toneFrequencyIndex]<1000) 221 lines[1]+=" "; 222 lines[1]+=String(toneFrequencies 223 [toneFrequencyIndex]); 224 lines[1]+=" Hz "; 225 LCDShow(); 226} 227 228void timerHourSetEnter(void) 229{ 230 clearDisplay(); 231/* 232 The two following lines just for showing 233 that it is possible to program two areas 234 blinking at different frequencies. 235 */ 236 displayFlash[0].setFlash(true, 0, 0, 5, 1000, 237 50); 238 displayFlash[1].setFlash(true, 1, 3, 2); 239 timerDisplay(); 240 timerRunning=false; 241} 242 243void timerHourSet(void) 244{ 245 int hour; 246 hour=remainingTime/3600; 247 if (event==cwRot && hour<23) hour++; 248 else 249 if (event==ccwRot && hour>0) hour--; 250 remainingTime=remainingTime%3600+hour*3600L; 251 timerDisplay(); 252} 253 254void timerMinuteSetEnter(void) 255{ 256 clearDisplay(); 257 displayFlash[0].setFlash(true, 0, 0, 5, 1000, 258 50); 259 displayFlash[1].setFlash(true, 1, 6, 2); 260 timerDisplay(); 261} 262 263void timerMinuteSet(void) 264{ 265 int minute; 266 minute=(remainingTime%3600)/60; 267 if (event==cwRot && minute<59) minute++; 268 else 269 if (event==ccwRot && minute>0) minute--; 270 remainingTime=(remainingTime/3600)*3600+ 271 remainingTime%60 + 272 minute*60L; 273 timerDisplay(); 274} 275 276void timerSecondSetEnter(void) 277{ 278 clearDisplay(); 279 displayFlash[0].setFlash(true, 0, 0, 5, 1000, 280 50); 281 displayFlash[1].setFlash(true, 1, 9, 2); 282 timerDisplay(); 283} 284 285void timerSecondSet(void) 286{ 287 int second; 288 second=remainingTime%60; 289 if (event==cwRot && second<59) second++; 290 else 291 if (event==ccwRot && second>0) second--; 292 remainingTime=(remainingTime/60)*60+second; 293 timerDisplay(); 294} 295 296void timerSecondSetExit(void) 297{ 298 if (remainingTime!=0) timerRunning=true; 299} 300 301/* This function stops the beeper when it is 302 beeping, and suspends or restarts the 303 counting of the timer. 304*/ 305void toggleTimerRunning(void) 306{ 307 if (remainingTime==0) 308 { 309 timerRunning=false; 310 noTone(beeperPin); 311 } 312 else timerRunning=!timerRunning; 313} 314 315void dimmingSetEnter(void) 316{ 317 displayFlash[0].setFlash(true, 1, 6, 1); 318 dimmingDisplay(); 319} 320 321void dimmingSet(void) 322{ 323 if(event==cwRot && dimming<9) dimming++; 324 else 325 if(event==ccwRot && dimming>0) dimming--; 326 dimmingDisplay(); 327} 328 329void dimmingSetExit(void) 330{ 331 EEPROM.update(EEPROMDimming, dimming); 332} 333 334void toneSetEnter(void) 335{ 336 displayFlash[0].setFlash(true, 1, 4, 4); 337 toneDisplay(); 338 noTone(beeperPin); 339 tone(beeperPin, 340 toneFrequencies[toneFrequencyIndex]); 341} 342 343void toneSet(void) 344{ 345 if(event==cwRot && toneFrequencyIndex<47) 346 toneFrequencyIndex++; 347 else 348 if (event==ccwRot && toneFrequencyIndex>0) 349 toneFrequencyIndex--; 350 toneDisplay(); 351 if(event==cwRot || event==ccwRot) 352 { 353 noTone(beeperPin); 354 tone(beeperPin, 355 toneFrequencies[toneFrequencyIndex]); 356 } 357} 358 359 void toneSetExit(void) 360{ 361 noTone(beeperPin); 362 EEPROM.update(EEPROMTone, 363 toneFrequencyIndex); 364} 365 366/* 367 States of the finite state machine 368*/ 369State timerDisplayState(&clearDisplay, 370 &timerDisplay, NULL); 371State dimmingDisplayState(&clearDisplay, 372 &dimmingDisplay, 373 NULL); 374State toneDisplayState(&clearDisplay, 375 &toneDisplay, 376 NULL); 377State timerHourSetState(&timerHourSetEnter, 378 &timerHourSet, 379 NULL); 380State timerMinuteSetState(&timerMinuteSetEnter, 381 &timerMinuteSet, 382 NULL); 383State timerSecondSetState(&timerSecondSetEnter, 384 &timerSecondSet, 385 &timerSecondSetExit); 386State dimmingSetState(&dimmingSetEnter, 387 &dimmingSet, 388 &dimmingSetExit); 389State toneSetState(&toneSetEnter, 390 &toneSet, 391 &toneSetExit); 392 393Fsm UIFsm(&timerDisplayState); 394 395/* 396 This function defines all the transitions 397 between the different states of the 398 finite state machine. 399 */ 400void setTransitions(void) 401{ 402 UIFsm.add_transition(&timerDisplayState, 403 &dimmingDisplayState, 404 cwRot, NULL); 405 UIFsm.add_transition(&dimmingDisplayState, 406 &toneDisplayState, 407 cwRot, NULL); 408 UIFsm.add_transition(&toneDisplayState, 409 &timerDisplayState, 410 cwRot, NULL); 411 412 UIFsm.add_transition(&timerDisplayState, 413 &toneDisplayState, 414 ccwRot, NULL); 415 UIFsm.add_transition(&toneDisplayState, 416 &dimmingDisplayState, 417 ccwRot, NULL); 418 UIFsm.add_transition(&dimmingDisplayState, 419 &timerDisplayState, 420 ccwRot, NULL); 421 422 UIFsm.add_transition(&timerDisplayState, 423 &timerHourSetState, 424 longSwitchPress, NULL); 425 UIFsm.add_transition(&timerHourSetState, 426 &timerMinuteSetState, 427 shortSwitchPress, NULL); 428 UIFsm.add_transition(&timerHourSetState, 429 &timerDisplayState, 430 timeOut, NULL); 431 UIFsm.add_transition(&timerMinuteSetState, 432 &timerSecondSetState, 433 shortSwitchPress, NULL); 434 UIFsm.add_transition(&timerMinuteSetState, 435 &timerDisplayState, 436 timeOut, NULL); 437 UIFsm.add_transition(&timerSecondSetState, 438 &timerDisplayState, 439 shortSwitchPress, NULL); 440 441 UIFsm.add_transition(&dimmingDisplayState, 442 &dimmingSetState, 443 longSwitchPress, NULL); 444 UIFsm.add_transition(&dimmingSetState, 445 &dimmingDisplayState, 446 shortSwitchPress, NULL); 447 UIFsm.add_transition(&dimmingSetState, 448 &dimmingDisplayState, 449 timeOut, NULL); 450 451 UIFsm.add_transition(&toneDisplayState, 452 &toneSetState, 453 longSwitchPress, NULL); 454 UIFsm.add_transition(&toneSetState, 455 &toneDisplayState, 456 shortSwitchPress, NULL); 457 UIFsm.add_transition(&toneSetState, 458 &toneDisplayState, 459 timeOut, NULL); 460/* 461 This is a special transition. It does not 462 change the current state, but it calls the 463 function toggleTimerRunning when the 464 current state is timerDisplayState and the 465 user shortly presses the rotary switch. This 466 is done to suspend or restart the count 467 when the timer is counting, and to stop the 468 beeper after the end of the count. 469*/ 470 UIFsm.add_transition(&timerDisplayState, 471 &timerDisplayState, 472 shortSwitchPress, 473 &toggleTimerRunning); 474} 475 476/* 477 This function manages the counting of the 478 timer and the beep when the count has 479 reached zero. 480*/ 481void timerManagement(void) 482{ 483 static unsigned long memoTime, actualTime; 484 static bool toneStarted=false; 485 486 if (timerRunning) 487 { 488 if(remainingTime==0) 489 { 490 if((millis()%1000)>500) 491 { 492 if(toneStarted==false) 493 { 494 tone(beeperPin, 495 toneFrequencies[toneFrequencyIndex]); 496 toneStarted=true; 497 } 498 } 499 else 500 { 501 if(toneStarted==true) 502 { 503 noTone(beeperPin); 504 toneStarted=false; 505 } 506 } 507 } 508 else // remaining time not zero 509 { 510 actualTime=millis(); 511 if(actualTime-memoTime>=1000) 512 { 513 memoTime=actualTime; 514 remainingTime--; 515 } 516 } 517 } 518} 519 520/* 521 This function reads the actions of the user 522 on the rotary switch and converts them into 523 events. 524 */ 525void rotaryEncoderRead(void) 526{ 527 unsigned long pressedSwitchDelay=0; 528 unsigned long pressedSwitchTime=0; 529 static unsigned long lastEventTime; 530 static bool longPressTreated=false; 531 532 event=noEvent; 533 if(digitalRead(rotaryAPin)==LOW) 534 { 535 if(digitalRead(rotaryBPin)==LOW) 536 event=ccwRot; 537 else 538 event=cwRot; 539 while(digitalRead(rotaryAPin)==LOW); 540 lastEventTime=millis(); 541 } 542 543 if(digitalRead(rotarySwitchPin)==LOW) 544 { 545 if (!longPressTreated) 546 { 547 pressedSwitchTime=millis(); 548 while(digitalRead(rotarySwitchPin)==LOW 549 && (millis()-pressedSwitchTime)< 550 longPressTime); 551 lastEventTime=millis(); 552 pressedSwitchDelay=millis()- 553 pressedSwitchTime; 554 if(pressedSwitchDelay>=longPressTime) 555 { 556 event=longSwitchPress; 557 longPressTreated=true; 558 } 559 else if(pressedSwitchDelay>50) 560 event=shortSwitchPress; 561 } 562 } 563 else 564 longPressTreated=false; 565 if(millis()-lastEventTime>maxTime) 566 event=timeOut; 567} 568 569/* 570 The settings of brightness and tone frquency 571 are stored into the EEPROM to be kept when 572 the power is off. This function is called at 573 power on to get these values back. To be sure 574 that they are the right ones, the first 575 three bytes of the EEPROM must contain "tim". 576 If it is not the case, the values are 577 replaced by the default values. 578*/ 579void readEEPROM(void) 580{ 581 if(EEPROM.read(EEPROMId0)==short('t') && 582 EEPROM.read(EEPROMId1)==short('i') && 583 EEPROM.read(EEPROMId2)==short('m')) 584 { 585 dimming=EEPROM.read(EEPROMDimming); 586 toneFrequencyIndex=EEPROM.read(EEPROMTone); 587 } 588 else 589 { 590 EEPROM.update(EEPROMId0, 't'); 591 EEPROM.update(EEPROMId1, 'i'); 592 EEPROM.update(EEPROMId2, 'm'); 593 dimming=5; 594 EEPROM.update(EEPROMDimming, dimming); 595 toneFrequencyIndex=21; 596 EEPROM.update(EEPROMTone, 597 toneFrequencyIndex); 598 } 599} 600 601void setup() 602{ 603 pinMode(rotaryAPin, INPUT); 604 pinMode(rotaryBPin, INPUT); 605 pinMode(rotarySwitchPin, INPUT_PULLUP); 606 pinMode(beeperPin, OUTPUT); 607 digitalWrite(beeperPin, LOW); 608 lines[0].reserve(17); 609 lines[1].reserve(17); 610 lcd.begin(16, 2); 611 lcd.clear(); 612/* 613 If the rotary switch is pressed during power 614 on, the values in the EEPROM are replaced 615 by the default values. This is achived by 616 deleting the chain "tim" in the first 617 three bytes of the EEPROM. 618*/ 619 if(digitalRead(rotarySwitchPin)==LOW) 620 { 621 EEPROM.update(EEPROMId0, 0xFF); 622 EEPROM.update(EEPROMId1, 0xFF); 623 EEPROM.update(EEPROMId2, 0xFF); 624 } 625 readEEPROM(); 626 setTransitions(); 627} 628 629void loop() 630{ 631 rotaryEncoderRead(); 632 timerManagement(); 633 UIFsm.run_machine(); 634 UIFsm.trigger(event); 635 analogWrite(backLightPin, dimming*27); 636} 637[/code]
Downloadable files
Timer schematics
Timer schematics

Comments
Only logged in users can leave comments