So - in the previous version, I put together some code to set a seed time, and then to manually increment it on a periodic basis. I started that period off at 1 second - adding 1 to my seconds counter, resetting it at 60 and adding 1 to the minutes counter, and similarly resetting the minutes at 60 and incrementing the hour. Hours are reset to 0 when they reach 24.
Obviously, there is a whole bunch of code going on, doing all the incrementing and comparisons, and that takes up time which is being added to the 1 second delay, meaning each iteration through my loop is taking slightly more than 1 second, and hence my clock would lose time. I was quite shocked to see that it lost about 10 minutes in just 6 hours though - obviously there is room for improvement...
The first step in Mk II, was to add a Serial.print to the end of the loop, to show the millis() value. This revealed that each loop appeared to be taking 1031 or 1032ms, so I reduced the delay at the end of the loop to 968 to start with.
Now the loops were taking about 999 ms, meaning the clock would run fast and gain time. I increased the delay to 969, but now found the loop was running at 1001 ms! Go figure... I decided to put the delay back to 968, and then to check the millis() value each minute - perhaps I could apply a second delay once a minute to act as a compensating adjustment.
It appeared that I was gaining about 44ms per minute, so in the part of my code that resets the seconds and increments the minutes, I added another delay operation - this time for 44ms. This initially seemed to do the trick - I was now gaining just about 1ms per minute which worked out to be 1 second every 16 hours and 40 minutes. Pretty good for my needs - maybe in the next version, I could also put in an hourly adjustment to control the drift even more.
Here's the modified code, with new and amended lines highlighted yellow (actual code - not comments).
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | /* * Mk. II - 25/06/2017 * Checked the value of millis() at the end of each loop from Mk. I * to fine tune the delay. */ // Uses Serial.parseInt() to seed the clock. // The required string is hh:mm:ss which is 8 chars long, but we need to allow for a 'newline' char on the end // so declare a string at least 9 chars long char seedTime[9] = ""; char buffer[21] = ""; // for sprintf output // declare variables. int hh=0, mm=0, ss=0; // include LCD library #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x3F,20,4); void setup() { // Put prompt on display lcd.begin(); lcd.setCursor(0,1); lcd.print("Start Serial Monitor"); lcd.setCursor(0,2); lcd.print("Enter seed time"); lcd.setCursor(0,3); lcd.print(" (hh:mm:ss)"); // Start Serial comms with monitor Serial.begin(9600); Serial.println("Please enter the seed time in the format hh:mm:ss"); // Method 1 - parseInt - finds int values in CSV type string. Can use ":" as separator. // wait for input to be typed while (Serial.available() == 0) {} // just loops until Serial input is available // parse the input string hh = Serial.parseInt(); mm = Serial.parseInt(); ss = Serial.parseInt(); // Only expecting three values - no need to check for newline if (Serial.read() == "\n") { // end of input } // Clear the lcd lcd.clear(); } void loop() { int x = sprintf(buffer, "The time is %02d:%02d:%02d", hh, mm, ss); Serial.println(buffer); Serial.println(millis()); lcd.setCursor(0,2); lcd.print(buffer); // Using delay(1000) resulted in loop times of 1031 to 1032. // Adjusted delay to 1000-32=968. // Loop is now slightly quicker than 1 second so 'time' gains about 45ms in 1 minute. // Added an extra delay in the minute change as a correction. delay(968); ss++; if(ss==60) { ss=0; mm++; delay(44); // compensates for total loop time being very slightly less than 1 second. } if(mm==60) { mm=0; hh++; } if(hh==24) { hh=0; } } |
I left the sketch running overnight and checked again this morning, but... things are not going as I had hoped! After just 7 hours, the clock was about 3 seconds adrift - a lot better than the Mk I's 10 minutes, but considerably worse than the 1/2 second I was expecting. I checked back in the log on the serial monitor, and found that after running for about 8 or 9 minutes with the drift being a constant 1ms per minute, the drift suddenly started jumping to 4ms, then 6ms, then 9ms or more..! No wonder it's 3 seconds adrift already.
I am not sure exactly what to make of this. As far as I was aware - running the same set of operations in the loop repeatedly, should take exactly the same time for each loop. Obviously, if the processing has to take a different route through the logic - such as the extra processing when the seconds or minutes or hours (or all three) get reset, then the timing would be different, but if the same set of operations is run each time (as it is 59 times each minute - or the same cumulative code for an entire minute, as it is 59 times per hour), then there should be no variation in the timing.
With hindsight, I can see some very slight differences - between some loops, such as when the value is < 10, there is additional code to print an extra '0', but even that follows a distinct and predictable pattern each minute. It would not account for the variances I suddenly started getting after a few minutes, that just got bigger and bigger with no apparent pattern (other than just keeping on getting bigger).
I don't know if there are external factors that could affect this, such as voltage to the Arduino (I was running it from the USB port on my ageing laptop, and part of the time I was streaming a movie - but most of the night, it was sitting on its own in the dark, doing nothing but providing power to the Arduino). Perhaps the amount of entries in the serial monitor buffer, gradually increasing through the night is a factor (though I would have thought Arduino.exe would have been handling that independently on the PC)?
So, what next? I think I need to run a few more tests... and maybe post a question on the Arduino forum? Watch this space (or if anyone knows why the exact same code iterated a number of times can take such varying elapsed times, please let me know).
No comments:
Post a Comment