Hello!
I am currently working on Master clock for small clock network but currently run into issue with accuracy. Slave clocks require one pulse each minute with alternating polarity of pulses. I am using attiny45 to drive h-bridge using two pins (thanks to other guys here, the bridge do not try to blowup itself now ) and to keep whole thing simple there is no other extrenal parts. Time information is taken from time loop based of 'blink without delay' example that should fire once each 60000ms. Thing is that it goes off sooner, about 600-700 (calculated and measured) before 60k mark, which results in clocks going faster then real time. I am looking at my code but I don't see where I made mistake, it should work as intended.
/* Clock Driver Module V3 - For new H-Bridge
Updates
V3.1 = Fixed 'Minute reset' function
*/
unsigned long TimeMain;
unsigned long TimePulse;
unsigned long TimePulsePause;
unsigned long TimeHeartbeat;
unsigned long TimeMain_last;
unsigned long TimePulse_last;
unsigned long TimePulsePause_last;
unsigned long TimeHeartbeat_last;
unsigned long TimeMain_value = 60000;
unsigned int TimePulse_value = 1500;
unsigned int TimePulsePause_value = 100;
unsigned int TimeHeartbeat_value = 1000;
int pot_value;
bool polarity = false; //false is positive pulse, true is negative pulse
bool pulseGo = false;
bool pulsePause = false;
bool but_rstminute = false;
bool but_pulse = false;
bool HB_led = false;
//pins ATtiny45
int led_rstminute = 3; // pin 2 (PB 3)
int pot_input = A2; // pin 3 (PB 4)
int led_pulse = 0; // pin 5 (PB 0)
int output_positive = 1; // pin 6 (PB 1)
int output_negative = 2; // pin 7 (PB 2)
void setup() {
pinMode (led_rstminute, OUTPUT);
pinMode (pot_input, INPUT);
pinMode (led_pulse, OUTPUT);
pinMode (output_positive, OUTPUT);
digitalWrite(output_positive, LOW);
pinMode (output_negative, OUTPUT);
digitalWrite(output_negative, LOW);
}
void loop() {
//============= MAIN TIMER LOOP AND PULSE GENERATION ==============================
TimeMain = millis(); //Main timer
if (TimeMain - TimeMain_last >= TimeMain_value) {
TimeMain_last = TimeMain;
pulseGo = true;
TimePulse_last = millis(); // write actual time used for count pulse lenght delay
}
if (pulseGo == true && pulsePause == false) {
if (polarity == false) {
digitalWrite(output_positive, HIGH);
} else {
digitalWrite(output_negative, HIGH);
}
TimePulse = millis(); //pulse lenght time
if (TimePulse - TimePulse_last >= TimePulse_value) {
TimePulse_last = TimePulse; // not needed?
digitalWrite(output_positive, LOW);
digitalWrite(output_negative, LOW);
pulseGo = false;
pulsePause = true;
TimePulsePause_last = millis();
if (polarity == false) {
polarity = true;
} else {
polarity = false;
}
}
}
if (pulsePause == true) {
TimePulsePause = millis(); //pulse safety deleay time
if (TimePulsePause - TimePulsePause_last >= TimePulsePause_value) {
TimePulsePause_last = TimePulsePause; // not needed too?
pulsePause = false;
}
}
// =========== CONTROL AND INDICATIONS ================================
pot_value = analogRead(pot_input);
if (pot_value < 200) { // minute reset
digitalWrite(led_rstminute, HIGH);
if (but_rstminute == false) {
but_rstminute = true;
TimeMain = millis();
TimeMain_last = TimeMain + 1;
}
}
if (pot_value > 800) { //manual pulse mode
but_pulse = true;
digitalWrite(led_pulse, HIGH);
pulseGo = true;
TimePulse_value = 650;
}
if (pot_value > 200 && pot_value < 800 ) {
but_rstminute = false;
but_pulse = false;
TimePulse_value = 1500;
}
//=============== HEARTHBEAT =================================
TimeHeartbeat = millis(); //HB loop
if (TimeHeartbeat - TimeHeartbeat_last >= TimeHeartbeat_value) {
TimeHeartbeat_last = TimeHeartbeat;
if (HB_led == false) {
HB_led = true;
digitalWrite(led_rstminute, HIGH);
digitalWrite(led_pulse, LOW);
} else {
HB_led = false;
digitalWrite(led_rstminute, LOW);
digitalWrite(led_pulse, HIGH);
}
}
// =================== OVERFLOW CHECK ==========================
//If TimeMain value became smaller then TimeMain_last we encounterd an overflow of millis register. Now we need to reset TimeMain_last.
//Note: Manual reset of current minute and time adjust is likely needed after overflow reset.
if (TimeMain < TimeMain_last) {
TimeMain_last = TimeMain;
}
}
Can someone with better skill then me look at my horrible programing and tell me if something is wrong there please? I am clueless. And do not laught at my bad editing, thanks Only 'fix' so far was simply add 600 to main time and make it 60600
A good starting point would be to use an RTC chip (like the DS323x family)… then if the long term accuracy is important to you, look into using NTP as a means of correcting for any drift.
These can be easy or hard, and low-cost or expensive to implement…
For example, I use devices scattered in inaccessible regional locations - using cellular network time to keep accuracy - and they also offer automatic DST adjustments provided by the cell carriers.
If you have any network capability - the work is almost trivial.
I would try to simplify it, just set a pulse timer every 60 seconds:
if (TimeMain - TimeMain_last >= TimeMain_value) {
TimeMain_last += TimeMain_value;
TimePulse_last = TimeMain; // write actual time used for count pulse length delay
pulseGo = true;
// set pulse active here
}
if (pulseGo == true and TimeMain - TimePulse_last >= TimePulse_value) {
pulseGo = false;
// set pulse inactive here
}
Probably needs some brush up, it's only 10 minutes of work...
The clock on a typical Arduino board is not particularly accurate. I believe it is not stable enough for time keeping either.
You can use one of the RTC or GPS solutions for a better results OR you could simply count the AC line frequency (actually count the zero crossings).
The AC line frequency is very stable and best of all if it does wander a small amount the power grid will balance this error off by an opposite error so after a long time the error is essentially zero.
However this method requires resetting if you loose power.
If running fast, adjustTimeMain_value = 60000; upward.
Let it run for one hour, if it's, say, 5 seconds fast adjust TimeMain_value = 60000; to:
TimeValue * Arduino time in seconds / clock time in seconds.
Ex; 60000 * 3605 / 3600 = 60083.
Best to us RTC like DS3231, as @ JohnRob said.
Thanks for the code, I ll look into it. I have tendency to make simple tasks over complicated
About accuracy, I did hooked up Uno to attiny's outputs to measure time between pulses and value is all over the place, we are talking about +/- 60ms difference in pulse lenghts. I did not expect huge accuracy and it is not even goal of project, up to one minute per week is still acceptable, now it's about half hour per day, lol.
GPS or RTC is great solution, I used those in different projects, reason not to use them now was/is to keep it simple and cheap.
So my code does work and inaccuracy is just hardware based?
Btw, I am running 8Mhz international clock, no pins for external sadly enough.
Assuming you mean "internal", the internal clock on AVR chips is way less stable and accurate than the ceramic resonators on some Arduino, which are way less stable and accurate than the crystal resonators on other Arduino, which are way less stable and accurate than an RTC/GPS/NTC/... Essentially you have chosen just about the least stable and accurate clock source used in the Arduino world... except perhaps a 555 timer (maybe I'm doing 555 timers an injustice there).
Maybe consider upgrading to an attiny44/84. It's not a beginner level project, but I think you could then connect a 32KHz watch crystal (+ 2 caps) to the chip's crystal inputs to get something as stable and accurate as a basic RTC such as those based on DS1307, but not as good as DS3231. A simple and cheap circuit, unfortunately the sketch may be less simple.
I think I should point out here that @Michelle090's application doesn't need to know the actual time. The slave clocks in the network can be set to display any time, right or wrong. Indeed, they could be set to show the time in different cities around the world.
All that's required is an accurate-as-possible timebase for generating the alternating 1pulse-per-minute signal. Yes, you can get that from an RTC chip, but using the microcontroller's own clock crystal seems simpler and more elegant, provided you use a time-keeping grade crystal.
Important: you shouldn't just get a watch or quartz clock crystal and solder it to the microcontroller. Watches and clocks run at 1.5V; a 3.3V or 5V wave across such a crystal might cause it to malfunction - operate away from its specified frequency, run at a harmonic, or just mechanically break. So you need to make sure the chosen crystal will handle the operating voltage of the MCU.
Another option would be to use a GPS receiver - they produce a 1pps output which can be divided appropriately. Ditto the receiver circuit from a cheap radio-controlled quartz clock movement. But honestly, for this application a 20ppm-or-better crystal for the microcontroller would seem the simplest.
I've done an almost identical application, but mine had to generate a pulse every 30s for a different type of slave clock. I used a PIC chip - a 12F-something-or-other.
In place of the Attiny45 , can you use an ESP8266?
I built an RTC using ESP8266, and it's been 20 days since I started testing and so far the deviation from NTP was 0 (none) seconds.
It won't be a problem. Just connect an ordinary watch crystal to the appropriate pins on the MCU, along with the appropriate caps, as shown in the MCU's datasheet. Your sketch will need to set the MCU's internal registers correctly to use an external, "external low frequency oscillator", and one of the internal timers to generate an interrupt every 32,768 cycles == 1 second.
How do you know it won't be a problem? Have you done it? I mention it simply because I read a warning about it in an electronics publication (Elektor? - can't remember for sure).
Anyway, regardless - a suitably accurate crystal is definitely the way to go. RTCs, GPS receivers, etc, are way over the top.
For such application a RTC looks 'too fancy' in my opinion. I didn't expect such inaccuracy, partially because the first development version that was running on Uno board reported back delay between pulses precisely 60000ms no matter what. But Uno have external crystal so that would explain why final version running on bare attiny45 with internal clock is so bad.
I was looking through my salvage parts and was able to find board containing a RTC 72421A ic. By datasheet it looks okay, but it require lots of pins and I would have to figure it out how to talk to it in the firstplace.
Seem like any solution will require a change in project and ordering of more parts. And most likely different processor.
Not quite. See reply #9. A good RTC is a good idea, because a good RTC is temperature compensated, and the usual quartz crystal oscillators that are used as system clocks, are not.
But a calibrated oscillator, in an environment where the temperature is very stable, can be very accurate when considering system complexity and cost.
A good RTC might be temperature compensated, but most of the cheap ones aren't. (Unless you mean what I describe next...?)
In any case, what you say is questionable because the quartz crystals designed for use in clocks and watches are engineered to have an almost-flat temperature coefficient curve in the room-temperature-to-body-temperature range.
Over-engineering is never good. A proper quartz crystal designed for timekeeping purposes will be just fine for a network of electromagnetic slave clocks. It is already orders of magnitude better than the electric master clock that originally drove them.