Components and supplies
Tactile Switch, Top Actuated
Arduino Nano R3
Alphanumeric LCD, 16 x 2
Trimmer Potentiometer, 10 kohm
Jumper wires
Project description
Code
Clock programme
arduino
Accurate clock with date just using and Arduino
1// Paul Brace - Feb 2021 2// Simple Clock with Date created just using a Arduino - no RTC module 3// Program incorporates a time correction adjustment to compensate for the internal 4// clock speed not being 100% accurate. 5// Once correct speed adjustment set the clock is surprisingly accurate. 6// In my test it did not lose or gain any time over a 5 day period. 7// Displays time on a 16x2 LCD display 8// Buttons to set time 9// Mode button (pin 2) toggles set time, set date and run 10// Button 1 (pin 3) Increments Minutes and Month and decreases Year/speed adj 11// Button 2 (pin 4) Increments Hour and Day and increases Year./speed adj 12// 24 Hour display 13 14// Include the library driver for display: 15#include <LiquidCrystal.h> 16 17// LiquidCrystal lcd( RS, EN, D4,D5, D6, D7) 18LiquidCrystal lcd(12, 13, 6, 7, 8, 9); // create an lcd object and assign the pins 19 20// Define buttons and buzzer connections 21#define MODE_BUTTON 2 22#define HOUR_BUTTON 3 // Same button different definitions to 23#define UP_BUTTON 3 // make code easier to understand 24#define DAY_BUTTON 3 25#define MINUTE_BUTTON 4 // Same button different definitions to 26#define DOWN_BUTTON 4 // make code easier to understand 27#define MONTH_BUTTON 4 28 29// Current mode settings 30#define SHOW_TIME 1 // 1 = running - show time 31#define SET_TIME 2 // 2 = time set 32#define SET_YEAR 3 // 3 = year set 33#define SET_DATE 4 // 4 = day/month set 34#define SET_SPEED_ADJ 5 // 5 = amend the speedCorrection variable 35 36int speedCorrection = 3545; // Number of milliseconds my Nano clock runs slow per hour 37// negative number here if it is running fast 38// change to match your Arduino 39 40// Volatile variables as changed in an interrupt and we 41// need to force the system to read the actual variable 42// when used outside the interrupt and not use a cached version 43volatile unsigned long currentTime; // Duration in milliseconds from midnight 44unsigned long lastTime = -1000; // lastTime that ShowTime was called initialised to -1000 so shows immediately 45volatile unsigned long elapsed; // Timer used for delay and hour count 46 47unsigned long millisecondsInADay; // Milliseconds in 24 hours 48unsigned long millisecondsInHour; // Milliseconds in 1 hour 49int currentMode; // 1 = running - show time 50// 2 = time set 51// 3 = year set 52// 4 = day/month set 53 54float currentDate; // Julian date 55float lastDate = 0.0; // last date that ShowDate was called 56int currentDay; 57int currentMonth; 58int currentYear; 59 60char *dayArray[] = { "Tue. ", // Will show a compiler warning but works fine 61 "Wed. ", 62 "Thur. ", 63 "Fri. ", 64 "Sat. ", 65 "Sun. ", 66 "Mon. " 67 }; 68 69void setup() { 70 // Set up time interrupt - millis() rolls over after 50 days so 71 // we are using our own millisecond counter which we can reset at 72 // the end of each day 73 TCCR0A = (1 << WGM01); //Set the CTC mode Compare time and trigger interrupt 74 OCR0A = 0xF9; //Set value for time to compare to ORC0A for 1ms = 249 (8 bits so max is 256) 75 //[(Clock speed/Prescaler value)*Time in seconds] - 1 76 //[(16,000,000/64) * .001] - 1 = 249 = 1 millisecond 77 TIMSK0 |= (1 << OCIE0A); //set timer compare interrupt 78 TCCR0B |= (1 << CS01); //Set the prescale 1/64 clock 79 TCCR0B |= (1 << CS00); // ie 110 for last 3 bits 80 TCNT0 = 0; //initialize counter value to 0 81 sei(); //Enable interrupt 82 83 pinMode(MINUTE_BUTTON, INPUT_PULLUP); 84 pinMode(HOUR_BUTTON, INPUT_PULLUP); 85 pinMode(MODE_BUTTON, INPUT_PULLUP); 86 //pinMode(BUZZER, OUTPUT); 87 currentTime = 0; // Set to current time to mindnight 88 currentDate = JulianDate(1, 1, 2021); // Set base date 89 elapsed = 0; // Set period counter to 0 90 millisecondsInADay = 24ul * 60 * 60 * 1000; 91 millisecondsInHour = 60ul * 60 * 1000; 92 currentMode = SHOW_TIME; // Initial mode is running and showing time and date 93 // Setup LCD 94 lcd.begin(16, 2); 95 lcd.noAutoscroll(); 96 lcd.display(); 97 lcd.clear(); 98 ShowTime(currentTime); 99} 100 101void loop() { 102 // loop runs every 150 milliseconds 103 104 // If at end of the day reset time and increase date 105 if ((currentMode == SHOW_TIME) && 106 (currentTime > millisecondsInADay)) { 107 //Next day 108 // Stop interrupts while reset time 109 noInterrupts(); 110 currentTime -= millisecondsInADay; 111 interrupts(); 112 currentDate++; 113 } 114 // At the end of each hour adjust the elapsed time for 115 // the inacuracy in the Arduino clock 116 if (elapsed >= millisecondsInHour) { 117 noInterrupts(); 118 // Adjust time for slow/fast running Arduino clock 119 currentTime += speedCorrection; 120 // Reset to count the next hour 121 elapsed = 0; 122 interrupts(); 123 } 124 125 // Check if any buttons have been pressed 126 CheckButtons(); 127 128 // Show display based on current mode 129 switch (currentMode) { 130 case SHOW_TIME: 131 // Display current time and date 132 ShowTime(currentTime); 133 ShowDate(currentDate); 134 break; 135 case SET_TIME: 136 // Display screen for setting the time 137 ShowTimeSet(currentTime); 138 break; 139 case SET_YEAR: 140 // Display screen for setting the year 141 ShowYearSet(currentDate); 142 break; 143 case SET_DATE: 144 // Display screen for setting the day and month 145 ShowDDMMSet(currentDate); 146 break; 147 case SET_SPEED_ADJ: 148 // Display screen for adjusting the speed correction 149 ShowSpeedSet(); 150 break; 151 } 152 Wait(150); 153} 154 155// This is interrupt is called when the compare time has been reached 156// hence will be called once a millisecond based on the 157// OCR0A register setting. 158ISR(TIMER0_COMPA_vect) { 159 if (currentMode != SET_TIME) 160 currentTime++; 161 elapsed++; 162} 163 164float JulianDate(int iday, int imonth, int iyear) { 165 // Calculate julian date (tested up to the year 20,000) 166 unsigned long d = iday; 167 unsigned long m = imonth; 168 unsigned long y = iyear; 169 if (m < 3) { 170 m = m + 12; 171 y = y - 1; 172 } 173 unsigned long t1 = (153 * m - 457) / 5; 174 unsigned long t2 = 365 * y + (y / 4) - (y / 100) + (y / 400); 175 return 1721118.5 + d + t1 + t2; 176} 177 178void GregorianDate(float jd, int &iday, int &imonth, int &iyear) { 179 // Note 2100 is the next skipped leap year - compensates for skipped leap years 180 unsigned long f = jd + 68569.5; 181 unsigned long e = (4.0 * f) / 146097; 182 unsigned long g = f - (146097 * e + 3) / 4; 183 unsigned long h = 4000ul * (g + 1) / 1461001; 184 unsigned long t = g - (1461 * h / 4) + 31; 185 unsigned long u = (80ul * t) / 2447; 186 unsigned long v = u / 11; 187 iyear = 100 * (e - 49) + h + v; 188 imonth = u + 2 - 12 * v; 189 iday = t - 2447 * u / 80; 190} 191 192void SplitTime(unsigned long curr, unsigned long &ulHour, 193 unsigned long &ulMin, unsigned long &ulSec) { 194 // Calculate HH:MM:SS from millisecond count 195 ulSec = curr / 1000; 196 ulMin = ulSec / 60; 197 ulHour = ulMin / 60; 198 ulMin -= ulHour * 60; 199 ulSec = ulSec - ulMin * 60 - ulHour * 3600; 200} 201 202unsigned long SetTime(unsigned long ulHour, unsigned long ulMin, 203 unsigned long ulSec) { 204 // Sets the number of milliseconds from midnight to current time 205 return (ulHour * 60 * 60 * 1000) + 206 (ulMin * 60 * 1000) + 207 (ulSec * 1000); 208} 209 210void Wait(unsigned long value) { 211 // Create our own dealy function 212 // We have set our own interrupt on TCCR0A 213 // hence millis() and delay() will no longer work 214 unsigned long startTime = elapsed; 215 while ((elapsed - startTime) < value) { 216 // Just wait 217 } 218} 219 220void CheckButtons() { 221 // If the mode button has been pressed pin will go LOW 222 if (digitalRead(MODE_BUTTON) == LOW) { 223 // Advance to next mode 224 switch (currentMode) { 225 case SHOW_TIME: 226 currentMode = SET_TIME; 227 lcd.clear(); 228 break; 229 case SET_TIME: 230 currentMode = SET_YEAR; 231 lcd.clear(); 232 break; 233 case SET_YEAR: 234 currentMode = SET_DATE; 235 lcd.clear(); 236 break; 237 case SET_DATE: 238 currentMode = SET_SPEED_ADJ; 239 lcd.clear(); 240 break; 241 case SET_SPEED_ADJ: 242 currentMode = SHOW_TIME; 243 lcd.clear(); 244 // Reset variables so that the display will be forced to update 245 // the next time ShowTime and ShowDate are called 246 lastTime = 0; 247 lastDate = 0.0; 248 break; 249 } 250 } 251 if (currentMode != SHOW_TIME) { 252 switch (currentMode) { 253 // If mode anyhting other than SHOW_TIME check buttons 254 // Pin goes LOW when ssociated button pressed 255 case SET_TIME: 256 if (digitalRead(MINUTE_BUTTON) == LOW) { 257 // Advance minute 258 unsigned long iHours; 259 unsigned long iMinutes; 260 unsigned long iSeconds; 261 SplitTime(currentTime, iHours, iMinutes, iSeconds); 262 if (iMinutes < 59) { 263 iMinutes++; 264 } 265 else { 266 iMinutes = 0; 267 } 268 // Set stored milliseconds based on current setting 269 noInterrupts(); 270 currentTime = SetTime(iHours, iMinutes, 0); 271 elapsed = 0; 272 interrupts(); 273 } 274 if (digitalRead(HOUR_BUTTON) == LOW) { 275 // Advance hour 276 unsigned long iHours; 277 unsigned long iMinutes; 278 unsigned long iSeconds; 279 SplitTime(currentTime, iHours, iMinutes, iSeconds); 280 if (iHours < 23) { 281 iHours++; 282 } 283 else { 284 iHours = 0; 285 } 286 // Set stored milliseconds based on current setting 287 noInterrupts(); 288 currentTime = SetTime(iHours, iMinutes, 0); 289 elapsed = 0; 290 interrupts(); 291 } 292 break; 293 case SET_YEAR: 294 if (digitalRead(UP_BUTTON) == LOW) { 295 // Increase year 296 int iDay; 297 int iMonth; 298 int iYear; 299 GregorianDate(currentDate, iDay, iMonth, iYear); 300 iYear++; 301 // Set stored date based on current settings 302 currentDate = JulianDate(iDay, iMonth, iYear); 303 } 304 if (digitalRead(DOWN_BUTTON) == LOW) { 305 // Decrease year 306 int iDay; 307 int iMonth; 308 int iYear; 309 GregorianDate(currentDate, iDay, iMonth, iYear); 310 iYear--; 311 // Set stored date based on current settings 312 currentDate = JulianDate(iDay, iMonth, iYear); 313 } 314 break; 315 case SET_DATE: 316 if (digitalRead(MONTH_BUTTON) == LOW) { 317 // Advance month 318 int iDay; 319 int iMonth; 320 int iYear; 321 GregorianDate(currentDate, iDay, iMonth, iYear); 322 iMonth++; 323 if (iMonth > 12) { 324 iMonth = 1; 325 } 326 // Set stored date based on current settings 327 currentDate = JulianDate(iDay, iMonth, iYear); 328 } 329 if (digitalRead(DAY_BUTTON) == LOW) { 330 // Advance day 331 int iDay; 332 int iMonth; 333 int iYear; 334 GregorianDate(currentDate, iDay, iMonth, iYear); 335 iDay++; 336 if (iDay > 31) { 337 iDay = 1; 338 } 339 if (((iMonth == 4) || (iMonth == 6) || (iMonth == 9) || (iMonth == 11)) 340 && (iDay > 30)) { 341 iDay = 1; 342 } 343 if ((iMonth == 2) && (iDay > 29)) { 344 iDay = 1; 345 } 346 if ((iMonth == 2) && ((iYear % 4) != 0) && (iDay > 28)) { 347 iDay = 1; 348 } 349 // Set stored date based on current settings 350 // If subsequently adjust the month so day is not valid 351 // then display will advance to next valid date 352 currentDate = JulianDate(iDay, iMonth, iYear); 353 } 354 break; 355 case SET_SPEED_ADJ: 356 // increase or decrease correcton by 5 milliseconds 357 if (digitalRead(UP_BUTTON) == LOW) { 358 speedCorrection += 5; 359 } 360 if (digitalRead(DOWN_BUTTON) == LOW) { 361 speedCorrection -= 5; 362 } 363 break; 364 } 365 } 366} 367 368String FormatNumber(int value) { 369 // To add a leading 0 if required 370 if (value < 10) { 371 return "0" + String(value); 372 } 373 else { 374 return String(value); 375 } 376} 377 378void ShowTime(unsigned long value) { 379 // Update display once a second 380 // or when rolls over midnight 381 if ((value > lastTime + 1000) || (value < lastTime)) { 382 lastTime = value; 383 unsigned long iHours; 384 unsigned long iMinutes; 385 unsigned long iSeconds; 386 SplitTime(value, iHours, iMinutes, iSeconds); 387 388 // Display the time on line 0 389 lcd.setCursor(0, 0); 390 lcd.print("Time: " + FormatNumber(iHours) + ":" + 391 FormatNumber(iMinutes) + ":" + 392 FormatNumber(iSeconds)); 393 } 394} 395 396void ShowDate(float value) { 397 // Update display if date has changed since 398 // the date was last displayed 399 if (lastDate != value) { 400 lastDate = value; 401 int iday; 402 int imonth; 403 int iyear; 404 String currentDay; 405 GregorianDate(value, iday, imonth, iyear); 406 int dayOfWeek = (unsigned long)value % 7; 407 // Display the date on line 0 408 lcd.setCursor(0, 1); 409 lcd.print(dayArray[dayOfWeek]); 410 lcd.print(FormatNumber(iday) + ":" + 411 FormatNumber(imonth) + ":" + 412 iyear); 413 } 414} 415 416void ShowDDMMSet(float value) { 417 int iday; 418 int imonth; 419 int iyear; 420 String currentDay; 421 GregorianDate(value, iday, imonth, iyear); 422 // Display day and month for adjusting 423 lcd.setCursor(0, 0); 424 lcd.print("Set day & month:"); 425 lcd.setCursor(0, 1); 426 lcd.print("Day:" + FormatNumber(iday) + " Month:" + 427 FormatNumber(imonth)); 428} 429 430 431void ShowYearSet(float jd) { 432 int iday; 433 int imonth; 434 int iyear; 435 GregorianDate(jd, iday, imonth, iyear); 436 // Display year for adjusting 437 lcd.setCursor(0, 0); 438 lcd.print("Set year:"); 439 lcd.setCursor(0, 1); 440 lcd.print("Year: " + FormatNumber(iyear)); 441} 442 443void ShowTimeSet(unsigned long value) { 444 unsigned long iHours; 445 unsigned long iMinutes; 446 unsigned long iSeconds; 447 // Display time for adjusting 448 SplitTime(value, iHours, iMinutes, iSeconds); 449 lcd.setCursor(0, 0); 450 lcd.print("Set time:"); 451 lcd.setCursor(0, 1); 452 lcd.print("Hours:" + FormatNumber(iHours) + " Mins:" + 453 FormatNumber(iMinutes)); 454} 455 456void ShowSpeedSet() { 457 // Display speed correction figure for adjusting 458 // could be + or - 459 lcd.setCursor(0, 0); 460 lcd.print("Set speed adj:"); 461 lcd.setCursor(0, 1); 462 lcd.print("Millis: "); 463 lcd.print(speedCorrection); 464 lcd.print(" "); 465}
Processing timer testing script
processing
This is the script for Processing that will read the milliseconds sent from the Arduino and compare it to the elapsed milliseconds in processing.
1// Paul Brace Feb 2021 2// Script to accept millis() from Arduino 3// and compare it to internal millis() to 4// assess inaccuracy of the Arduino clock. 5// Assumes that the computer clock is accurate 6// -ve = Arduino is running slow so enter as a +ve adjustment in the clock program 7// +ve = Arduino is running fast so enter as a -ve adjustment to slow the clock down 8 9import processing.serial.*; 10 11Serial theSerialPort; // create the serial port object 12 13int[] serialBytesArray = new int[15]; // array to store incoming bytes 14int bytesCount = 0; // current number of bytes received 15boolean init = false; // false until handshake completed by receiving the character Z 16int fillColor = 255; // defining the initial fill colour 17long mills = 0; // last reading received 18long first = 0; // time of first mills received so we can calculate the difference over an hour 19long now; // number of millis elapsed since first mills received 20long firstReading = 100000; // millis() in processing of first message received from Arduino 21long DiffPerHour = 0; // the difference after the first hour has passed 22int inByte; // last byte read 23 24void setup() { 25 // define some canvas and drawing parameters 26 size(500, 500); 27 background(70); 28 noStroke(); 29 30 // print the list of all serial devices so you know which one to set for the Arduino 31 // will need to run program and edit if the correct port is not set below 32 printArray(Serial.list()); 33 // instantate the Serial Communication 34 String thePortName = Serial.list()[1]; 35 theSerialPort = new Serial(this, thePortName, 9600); 36} 37void draw() { 38 // Display time settings 39 background(70); 40 fill(fillColor); 41 textSize(25); 42 text(hour() + ":" + minute() + ":" + second(), 50, 50); 43 // the last read millis sent by the Arduino 44 text("Incoming elapsed: " + mills, 50, 100); 45 // the current elapsed since first read in Processing 46 text("Local elapsed: " + (now - firstReading), 50, 150); 47 // display the current difference 48 text("Diff: " + (mills - (now - firstReading)), 50, 200); 49 // Check if 1 hour has passed and if the first hour store the difference 50 if (((now - firstReading)>= 3600000) && (DiffPerHour == 0)){ 51 DiffPerHour = mills - (now - firstReading); 52 } 53 // Display the first difference and the difference after the first hour 54 text("Diff after 1 hour: " + DiffPerHour, 50, 300); 55} 56 57void serialEvent(Serial myPort) { 58 // read a byte from the serial port 59 inByte = myPort.read(); 60 if (init == false) { // if not yet handshaked the see if handshake byte 61 if (inByte == 'Z') { // if the byte read is Z 62 myPort.clear(); // clear the serial port buffer 63 init = true; // store the fact we had the first hello 64 myPort.write('Z'); // tell the Arduino to send more 65 if (first == 0){ 66 first = millis(); 67 } 68 } 69 } 70 else { 71 // if there already was the first hello 72 // Add the latest byte from the serial port to array 73 if (inByte != 69) { // Check not the end of message character E 74 if (bytesCount < 14) { 75 serialBytesArray[bytesCount] = inByte; 76 bytesCount++; 77 } 78 } 79 if (inByte == 69) { 80 // End of message 81 // store local time elapsed 82 now = millis(); 83 // calculate incoming millis() 84 mills = 0; 85 for (int i = 1; i <= bytesCount; i++) { 86 mills += (serialBytesArray[i - 1] - 48) * pow(10, (bytesCount - i)); 87 } 88 // Say we are ready to accept next message 89 // if this is the first reading then set the first difference 90 if (firstReading == 100000) { 91 firstReading = now; 92 } 93 myPort.write('Z'); 94 // Reset bytesCount: 95 bytesCount = 0; 96 } 97 } 98}
Arduino timer program
arduino
This program sends the number of elapsed milliseconds to the serial port evert 2 seconds.
1// Paul Brace Feb 2021 2// For use with corresponding Processing script 3// to compare millis() from here to millis() in 4// Processing using the computer clock 5 6int inByte = 0; 7unsigned long firstReading = 100000; // millis() when first reading sent 8 9void setup() { 10 Serial.begin(9600); 11 // Send the hello byte to Processing 12 sayHello(); 13} 14 15void loop() { 16 // if a byte is received on the serial port 17 // then read and discard it and send current 18 // value of millis() 19 if (Serial.available() > 0){ 20 // get incoming byte 21 inByte = Serial.read(); 22 // send time elapsed since first reading processing 23 Serial.print(millis() - firstReading); 24 Serial.print('E'); 25 // repeat every 2 seconds 26 delay(2000); 27 } 28} 29 30void sayHello(){ 31 // Wait until the serial port is available 32 // then send hello byte to start handshake 33 while (Serial.available() <=0){ 34 Serial.print('Z'); // Send Z to processing to say Hello 35 delay(200); 36 } 37 firstReading = millis(); 38}
Processing timer testing script
processing
This is the script for Processing that will read the milliseconds sent from the Arduino and compare it to the elapsed milliseconds in processing.
1// Paul Brace Feb 2021 2// Script to accept millis() from Arduino 3// 4 and compare it to internal millis() to 5// assess inaccuracy of the Arduino clock. 6// 7 Assumes that the computer clock is accurate 8// -ve = Arduino is running slow 9 so enter as a +ve adjustment in the clock program 10// +ve = Arduino is running 11 fast so enter as a -ve adjustment to slow the clock down 12 13import processing.serial.*; 14 15Serial 16 theSerialPort; // create the serial port object 17 18int[] serialBytesArray 19 = new int[15]; // array to store incoming bytes 20int bytesCount = 0; // 21 current number of bytes received 22boolean init = false; // false 23 until handshake completed by receiving the character Z 24int fillColor = 255; // 25 defining the initial fill colour 26long mills = 0; // last 27 reading received 28long first = 0; // time of first mills 29 received so we can calculate the difference over an hour 30long now; // 31 number of millis elapsed since first mills received 32long firstReading = 100000; 33 // millis() in processing of first message received from Arduino 34long 35 DiffPerHour = 0; // the difference after the first hour has passed 36 37int inByte; // last byte read 38 39void setup() 40 { 41 // define some canvas and drawing parameters 42 size(500, 500); 43 background(70); 44 45 noStroke(); 46 47 // print the list of all serial devices so you know which 48 one to set for the Arduino 49 // will need to run program and edit if the correct 50 port is not set below 51 printArray(Serial.list()); 52 // instantate the Serial 53 Communication 54 String thePortName = Serial.list()[1]; 55 theSerialPort = new 56 Serial(this, thePortName, 9600); 57} 58void draw() { 59 // Display time settings 60 61 background(70); 62 fill(fillColor); 63 textSize(25); 64 text(hour() + ":" 65 + minute() + ":" + second(), 50, 50); 66 // the last read millis sent by the 67 Arduino 68 text("Incoming elapsed: " + mills, 50, 100); 69 // the current 70 elapsed since first read in Processing 71 text("Local elapsed: " + (now - firstReading), 72 50, 150); 73 // display the current difference 74 text("Diff: " + (mills - 75 (now - firstReading)), 50, 200); 76 // Check if 1 hour has passed and if the first 77 hour store the difference 78 if (((now - firstReading)>= 3600000) && (DiffPerHour 79 == 0)){ 80 DiffPerHour = mills - (now - firstReading); 81 } 82 // Display 83 the first difference and the difference after the first hour 84 text("Diff after 85 1 hour: " + DiffPerHour, 50, 300); 86} 87 88void serialEvent(Serial myPort) 89 { 90 // read a byte from the serial port 91 inByte = myPort.read(); 92 if 93 (init == false) { // if not yet handshaked the see if handshake byte 94 95 if (inByte == 'Z') { // if the byte read is Z 96 myPort.clear(); 97 // clear the serial port buffer 98 init = true; // store 99 the fact we had the first hello 100 myPort.write('Z'); // tell the Arduino 101 to send more 102 if (first == 0){ 103 first = millis(); 104 } 105 106 } 107 } 108 else { 109 // if there already was 110 the first hello 111 // Add the latest byte from the serial port to array 112 113 if (inByte != 69) { // Check not the end of message character E 114 115 if (bytesCount < 14) { 116 serialBytesArray[bytesCount] = inByte; 117 118 bytesCount++; 119 } 120 } 121 if (inByte == 69) { 122 // 123 End of message 124 // store local time elapsed 125 now = millis(); 126 127 // calculate incoming millis() 128 mills = 0; 129 for (int i = 1; 130 i <= bytesCount; i++) { 131 mills += (serialBytesArray[i - 1] - 48) * pow(10, 132 (bytesCount - i)); 133 } 134 // Say we are ready to accept next message 135 136 // if this is the first reading then set the first difference 137 if 138 (firstReading == 100000) { 139 firstReading = now; 140 } 141 myPort.write('Z'); 142 143 // Reset bytesCount: 144 bytesCount = 0; 145 } 146 } 147}
Clock programme
arduino
Accurate clock with date just using and Arduino
1// Paul Brace - Feb 2021 2// Simple Clock with Date created just using a Arduino - no RTC module 3// Program incorporates a time correction adjustment to compensate for the internal 4// clock speed not being 100% accurate. 5// Once correct speed adjustment set the clock is surprisingly accurate. 6// In my test it did not lose or gain any time over a 5 day period. 7// Displays time on a 16x2 LCD display 8// Buttons to set time 9// Mode button (pin 2) toggles set time, set date and run 10// Button 1 (pin 3) Increments Minutes and Month and decreases Year/speed adj 11// Button 2 (pin 4) Increments Hour and Day and increases Year./speed adj 12// 24 Hour display 13 14// Include the library driver for display: 15#include <LiquidCrystal.h> 16 17// LiquidCrystal lcd( RS, EN, D4,D5, D6, D7) 18LiquidCrystal lcd(12, 13, 6, 7, 8, 9); // create an lcd object and assign the pins 19 20// Define buttons and buzzer connections 21#define MODE_BUTTON 2 22#define HOUR_BUTTON 3 // Same button different definitions to 23#define UP_BUTTON 3 // make code easier to understand 24#define DAY_BUTTON 3 25#define MINUTE_BUTTON 4 // Same button different definitions to 26#define DOWN_BUTTON 4 // make code easier to understand 27#define MONTH_BUTTON 4 28 29// Current mode settings 30#define SHOW_TIME 1 // 1 = running - show time 31#define SET_TIME 2 // 2 = time set 32#define SET_YEAR 3 // 3 = year set 33#define SET_DATE 4 // 4 = day/month set 34#define SET_SPEED_ADJ 5 // 5 = amend the speedCorrection variable 35 36int speedCorrection = 3545; // Number of milliseconds my Nano clock runs slow per hour 37// negative number here if it is running fast 38// change to match your Arduino 39 40// Volatile variables as changed in an interrupt and we 41// need to force the system to read the actual variable 42// when used outside the interrupt and not use a cached version 43volatile unsigned long currentTime; // Duration in milliseconds from midnight 44unsigned long lastTime = -1000; // lastTime that ShowTime was called initialised to -1000 so shows immediately 45volatile unsigned long elapsed; // Timer used for delay and hour count 46 47unsigned long millisecondsInADay; // Milliseconds in 24 hours 48unsigned long millisecondsInHour; // Milliseconds in 1 hour 49int currentMode; // 1 = running - show time 50// 2 = time set 51// 3 = year set 52// 4 = day/month set 53 54float currentDate; // Julian date 55float lastDate = 0.0; // last date that ShowDate was called 56int currentDay; 57int currentMonth; 58int currentYear; 59 60char *dayArray[] = { "Tue. ", // Will show a compiler warning but works fine 61 "Wed. ", 62 "Thur. ", 63 "Fri. ", 64 "Sat. ", 65 "Sun. ", 66 "Mon. " 67 }; 68 69void setup() { 70 // Set up time interrupt - millis() rolls over after 50 days so 71 // we are using our own millisecond counter which we can reset at 72 // the end of each day 73 TCCR0A = (1 << WGM01); //Set the CTC mode Compare time and trigger interrupt 74 OCR0A = 0xF9; //Set value for time to compare to ORC0A for 1ms = 249 (8 bits so max is 256) 75 //[(Clock speed/Prescaler value)*Time in seconds] - 1 76 //[(16,000,000/64) * .001] - 1 = 249 = 1 millisecond 77 TIMSK0 |= (1 << OCIE0A); //set timer compare interrupt 78 TCCR0B |= (1 << CS01); //Set the prescale 1/64 clock 79 TCCR0B |= (1 << CS00); // ie 110 for last 3 bits 80 TCNT0 = 0; //initialize counter value to 0 81 sei(); //Enable interrupt 82 83 pinMode(MINUTE_BUTTON, INPUT_PULLUP); 84 pinMode(HOUR_BUTTON, INPUT_PULLUP); 85 pinMode(MODE_BUTTON, INPUT_PULLUP); 86 //pinMode(BUZZER, OUTPUT); 87 currentTime = 0; // Set to current time to mindnight 88 currentDate = JulianDate(1, 1, 2021); // Set base date 89 elapsed = 0; // Set period counter to 0 90 millisecondsInADay = 24ul * 60 * 60 * 1000; 91 millisecondsInHour = 60ul * 60 * 1000; 92 currentMode = SHOW_TIME; // Initial mode is running and showing time and date 93 // Setup LCD 94 lcd.begin(16, 2); 95 lcd.noAutoscroll(); 96 lcd.display(); 97 lcd.clear(); 98 ShowTime(currentTime); 99} 100 101void loop() { 102 // loop runs every 150 milliseconds 103 104 // If at end of the day reset time and increase date 105 if ((currentMode == SHOW_TIME) && 106 (currentTime > millisecondsInADay)) { 107 //Next day 108 // Stop interrupts while reset time 109 noInterrupts(); 110 currentTime -= millisecondsInADay; 111 interrupts(); 112 currentDate++; 113 } 114 // At the end of each hour adjust the elapsed time for 115 // the inacuracy in the Arduino clock 116 if (elapsed >= millisecondsInHour) { 117 noInterrupts(); 118 // Adjust time for slow/fast running Arduino clock 119 currentTime += speedCorrection; 120 // Reset to count the next hour 121 elapsed = 0; 122 interrupts(); 123 } 124 125 // Check if any buttons have been pressed 126 CheckButtons(); 127 128 // Show display based on current mode 129 switch (currentMode) { 130 case SHOW_TIME: 131 // Display current time and date 132 ShowTime(currentTime); 133 ShowDate(currentDate); 134 break; 135 case SET_TIME: 136 // Display screen for setting the time 137 ShowTimeSet(currentTime); 138 break; 139 case SET_YEAR: 140 // Display screen for setting the year 141 ShowYearSet(currentDate); 142 break; 143 case SET_DATE: 144 // Display screen for setting the day and month 145 ShowDDMMSet(currentDate); 146 break; 147 case SET_SPEED_ADJ: 148 // Display screen for adjusting the speed correction 149 ShowSpeedSet(); 150 break; 151 } 152 Wait(150); 153} 154 155// This is interrupt is called when the compare time has been reached 156// hence will be called once a millisecond based on the 157// OCR0A register setting. 158ISR(TIMER0_COMPA_vect) { 159 if (currentMode != SET_TIME) 160 currentTime++; 161 elapsed++; 162} 163 164float JulianDate(int iday, int imonth, int iyear) { 165 // Calculate julian date (tested up to the year 20,000) 166 unsigned long d = iday; 167 unsigned long m = imonth; 168 unsigned long y = iyear; 169 if (m < 3) { 170 m = m + 12; 171 y = y - 1; 172 } 173 unsigned long t1 = (153 * m - 457) / 5; 174 unsigned long t2 = 365 * y + (y / 4) - (y / 100) + (y / 400); 175 return 1721118.5 + d + t1 + t2; 176} 177 178void GregorianDate(float jd, int &iday, int &imonth, int &iyear) { 179 // Note 2100 is the next skipped leap year - compensates for skipped leap years 180 unsigned long f = jd + 68569.5; 181 unsigned long e = (4.0 * f) / 146097; 182 unsigned long g = f - (146097 * e + 3) / 4; 183 unsigned long h = 4000ul * (g + 1) / 1461001; 184 unsigned long t = g - (1461 * h / 4) + 31; 185 unsigned long u = (80ul * t) / 2447; 186 unsigned long v = u / 11; 187 iyear = 100 * (e - 49) + h + v; 188 imonth = u + 2 - 12 * v; 189 iday = t - 2447 * u / 80; 190} 191 192void SplitTime(unsigned long curr, unsigned long &ulHour, 193 unsigned long &ulMin, unsigned long &ulSec) { 194 // Calculate HH:MM:SS from millisecond count 195 ulSec = curr / 1000; 196 ulMin = ulSec / 60; 197 ulHour = ulMin / 60; 198 ulMin -= ulHour * 60; 199 ulSec = ulSec - ulMin * 60 - ulHour * 3600; 200} 201 202unsigned long SetTime(unsigned long ulHour, unsigned long ulMin, 203 unsigned long ulSec) { 204 // Sets the number of milliseconds from midnight to current time 205 return (ulHour * 60 * 60 * 1000) + 206 (ulMin * 60 * 1000) + 207 (ulSec * 1000); 208} 209 210void Wait(unsigned long value) { 211 // Create our own dealy function 212 // We have set our own interrupt on TCCR0A 213 // hence millis() and delay() will no longer work 214 unsigned long startTime = elapsed; 215 while ((elapsed - startTime) < value) { 216 // Just wait 217 } 218} 219 220void CheckButtons() { 221 // If the mode button has been pressed pin will go LOW 222 if (digitalRead(MODE_BUTTON) == LOW) { 223 // Advance to next mode 224 switch (currentMode) { 225 case SHOW_TIME: 226 currentMode = SET_TIME; 227 lcd.clear(); 228 break; 229 case SET_TIME: 230 currentMode = SET_YEAR; 231 lcd.clear(); 232 break; 233 case SET_YEAR: 234 currentMode = SET_DATE; 235 lcd.clear(); 236 break; 237 case SET_DATE: 238 currentMode = SET_SPEED_ADJ; 239 lcd.clear(); 240 break; 241 case SET_SPEED_ADJ: 242 currentMode = SHOW_TIME; 243 lcd.clear(); 244 // Reset variables so that the display will be forced to update 245 // the next time ShowTime and ShowDate are called 246 lastTime = 0; 247 lastDate = 0.0; 248 break; 249 } 250 } 251 if (currentMode != SHOW_TIME) { 252 switch (currentMode) { 253 // If mode anyhting other than SHOW_TIME check buttons 254 // Pin goes LOW when ssociated button pressed 255 case SET_TIME: 256 if (digitalRead(MINUTE_BUTTON) == LOW) { 257 // Advance minute 258 unsigned long iHours; 259 unsigned long iMinutes; 260 unsigned long iSeconds; 261 SplitTime(currentTime, iHours, iMinutes, iSeconds); 262 if (iMinutes < 59) { 263 iMinutes++; 264 } 265 else { 266 iMinutes = 0; 267 } 268 // Set stored milliseconds based on current setting 269 noInterrupts(); 270 currentTime = SetTime(iHours, iMinutes, 0); 271 elapsed = 0; 272 interrupts(); 273 } 274 if (digitalRead(HOUR_BUTTON) == LOW) { 275 // Advance hour 276 unsigned long iHours; 277 unsigned long iMinutes; 278 unsigned long iSeconds; 279 SplitTime(currentTime, iHours, iMinutes, iSeconds); 280 if (iHours < 23) { 281 iHours++; 282 } 283 else { 284 iHours = 0; 285 } 286 // Set stored milliseconds based on current setting 287 noInterrupts(); 288 currentTime = SetTime(iHours, iMinutes, 0); 289 elapsed = 0; 290 interrupts(); 291 } 292 break; 293 case SET_YEAR: 294 if (digitalRead(UP_BUTTON) == LOW) { 295 // Increase year 296 int iDay; 297 int iMonth; 298 int iYear; 299 GregorianDate(currentDate, iDay, iMonth, iYear); 300 iYear++; 301 // Set stored date based on current settings 302 currentDate = JulianDate(iDay, iMonth, iYear); 303 } 304 if (digitalRead(DOWN_BUTTON) == LOW) { 305 // Decrease year 306 int iDay; 307 int iMonth; 308 int iYear; 309 GregorianDate(currentDate, iDay, iMonth, iYear); 310 iYear--; 311 // Set stored date based on current settings 312 currentDate = JulianDate(iDay, iMonth, iYear); 313 } 314 break; 315 case SET_DATE: 316 if (digitalRead(MONTH_BUTTON) == LOW) { 317 // Advance month 318 int iDay; 319 int iMonth; 320 int iYear; 321 GregorianDate(currentDate, iDay, iMonth, iYear); 322 iMonth++; 323 if (iMonth > 12) { 324 iMonth = 1; 325 } 326 // Set stored date based on current settings 327 currentDate = JulianDate(iDay, iMonth, iYear); 328 } 329 if (digitalRead(DAY_BUTTON) == LOW) { 330 // Advance day 331 int iDay; 332 int iMonth; 333 int iYear; 334 GregorianDate(currentDate, iDay, iMonth, iYear); 335 iDay++; 336 if (iDay > 31) { 337 iDay = 1; 338 } 339 if (((iMonth == 4) || (iMonth == 6) || (iMonth == 9) || (iMonth == 11)) 340 && (iDay > 30)) { 341 iDay = 1; 342 } 343 if ((iMonth == 2) && (iDay > 29)) { 344 iDay = 1; 345 } 346 if ((iMonth == 2) && ((iYear % 4) != 0) && (iDay > 28)) { 347 iDay = 1; 348 } 349 // Set stored date based on current settings 350 // If subsequently adjust the month so day is not valid 351 // then display will advance to next valid date 352 currentDate = JulianDate(iDay, iMonth, iYear); 353 } 354 break; 355 case SET_SPEED_ADJ: 356 // increase or decrease correcton by 5 milliseconds 357 if (digitalRead(UP_BUTTON) == LOW) { 358 speedCorrection += 5; 359 } 360 if (digitalRead(DOWN_BUTTON) == LOW) { 361 speedCorrection -= 5; 362 } 363 break; 364 } 365 } 366} 367 368String FormatNumber(int value) { 369 // To add a leading 0 if required 370 if (value < 10) { 371 return "0" + String(value); 372 } 373 else { 374 return String(value); 375 } 376} 377 378void ShowTime(unsigned long value) { 379 // Update display once a second 380 // or when rolls over midnight 381 if ((value > lastTime + 1000) || (value < lastTime)) { 382 lastTime = value; 383 unsigned long iHours; 384 unsigned long iMinutes; 385 unsigned long iSeconds; 386 SplitTime(value, iHours, iMinutes, iSeconds); 387 388 // Display the time on line 0 389 lcd.setCursor(0, 0); 390 lcd.print("Time: " + FormatNumber(iHours) + ":" + 391 FormatNumber(iMinutes) + ":" + 392 FormatNumber(iSeconds)); 393 } 394} 395 396void ShowDate(float value) { 397 // Update display if date has changed since 398 // the date was last displayed 399 if (lastDate != value) { 400 lastDate = value; 401 int iday; 402 int imonth; 403 int iyear; 404 String currentDay; 405 GregorianDate(value, iday, imonth, iyear); 406 int dayOfWeek = (unsigned long)value % 7; 407 // Display the date on line 0 408 lcd.setCursor(0, 1); 409 lcd.print(dayArray[dayOfWeek]); 410 lcd.print(FormatNumber(iday) + ":" + 411 FormatNumber(imonth) + ":" + 412 iyear); 413 } 414} 415 416void ShowDDMMSet(float value) { 417 int iday; 418 int imonth; 419 int iyear; 420 String currentDay; 421 GregorianDate(value, iday, imonth, iyear); 422 // Display day and month for adjusting 423 lcd.setCursor(0, 0); 424 lcd.print("Set day & month:"); 425 lcd.setCursor(0, 1); 426 lcd.print("Day:" + FormatNumber(iday) + " Month:" + 427 FormatNumber(imonth)); 428} 429 430 431void ShowYearSet(float jd) { 432 int iday; 433 int imonth; 434 int iyear; 435 GregorianDate(jd, iday, imonth, iyear); 436 // Display year for adjusting 437 lcd.setCursor(0, 0); 438 lcd.print("Set year:"); 439 lcd.setCursor(0, 1); 440 lcd.print("Year: " + FormatNumber(iyear)); 441} 442 443void ShowTimeSet(unsigned long value) { 444 unsigned long iHours; 445 unsigned long iMinutes; 446 unsigned long iSeconds; 447 // Display time for adjusting 448 SplitTime(value, iHours, iMinutes, iSeconds); 449 lcd.setCursor(0, 0); 450 lcd.print("Set time:"); 451 lcd.setCursor(0, 1); 452 lcd.print("Hours:" + FormatNumber(iHours) + " Mins:" + 453 FormatNumber(iMinutes)); 454} 455 456void ShowSpeedSet() { 457 // Display speed correction figure for adjusting 458 // could be + or - 459 lcd.setCursor(0, 0); 460 lcd.print("Set speed adj:"); 461 lcd.setCursor(0, 1); 462 lcd.print("Millis: "); 463 lcd.print(speedCorrection); 464 lcd.print(" "); 465}
Arduino timer program
arduino
This program sends the number of elapsed milliseconds to the serial port evert 2 seconds.
1// Paul Brace Feb 2021 2// For use with corresponding Processing script 3// 4 to compare millis() from here to millis() in 5// Processing using the computer 6 clock 7 8int inByte = 0; 9unsigned long firstReading = 100000; // millis() 10 when first reading sent 11 12void setup() { 13 Serial.begin(9600); 14 // Send 15 the hello byte to Processing 16 sayHello(); 17} 18 19void loop() { 20 // 21 if a byte is received on the serial port 22 // then read and discard it and send 23 current 24 // value of millis() 25 if (Serial.available() > 0){ 26 // get 27 incoming byte 28 inByte = Serial.read(); 29 // send time elapsed since first 30 reading processing 31 Serial.print(millis() - firstReading); 32 Serial.print('E'); 33 34 // repeat every 2 seconds 35 delay(2000); 36 } 37} 38 39void sayHello(){ 40 41 // Wait until the serial port is available 42 // then send hello byte to start 43 handshake 44 while (Serial.available() <=0){ 45 Serial.print('Z'); // 46 Send Z to processing to say Hello 47 delay(200); 48 } 49 firstReading = 50 millis(); 51}
Downloadable files
Clock LCD 16x2 Breadboard
Clock LCD 16x2 Breadboard
Clock LCD 16x2 Breadboard
Clock LCD 16x2 Breadboard
Clock LCD 16x2 Schematic
Clock LCD 16x2 Schematic
Comments
Only logged in users can leave comments