Skip to content

dehne/Escapement

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The "Escapement" library for Arduino

Escapement Library Copyright 2014 - 2016 by D. L. Ehnebuske License terms: [Creative Commons Attribution-ShareAlike 3.0 United States (CC BY-SA 3.0 US)] (http://creativecommons.org/licenses/by-sa/3.0/us/ "CC BY-SA 3.0 US")

Introduction

In pendulum clocks, the escapement is the mechanism that does two things. First, it drives the gear-works one step forward for each tick or tock of the pendulum. Second, it gives the pendulum a tiny push with each tick and each tock to keep it going. The clock's gear-works are designed to move the clock's hands to display the time. The gear ratios in the clock mechanism are such that the fixed duration of time that passes between a tick and a tock (and between a tock and a tick) is exactly correct to have the clocks hands mark the passing of time.

This Escapement library implements the electronic equivalent of a mechanical escapement and gear-works for a properly equipped pendulum or bendulum. (A "bendulum" is a long, slender, springy piece of material, held vertically, whose lower end is fixed and whose upper end has a weight that is free to move back and forth -- a sort of upside-down pendulum.) By a "properly equipped" pendulum or bendulum I mean that one that terminates in a strong magnet that moves across a many-turn coil of fine wire positioned at the center of its swing.

The basic idea behind the Escapement is that the motion of the magnet is detected as it passes over the coil by the current the magnet induces. Just after the magnet passes, the Escapement object produces a pulse of current through the coil. This induces a magnetic field in the coil. The field gives the magnet a small push, keeping the pendulum or bendulum going. Each time this happens, the Escapement object returns the number of microseconds that have passed since the last tick or tock. The successively returned values can be used to drive a time-of-day display.

Even carefully designed and built mechanical clocks don't keep perfect time. A major reason for this is that various properties of the materials from which they are built depend on temperature. For example, the length of the pendulum in a pendulum clock changes slightly with temperature, slightly changing its period. Similarly, in a clock that uses a balance wheel, temperature changes change the spring constant of the hairspring making it slightly more or less springy, which slightly changes the balance wheel's ticking rate. Over the years, mechanical clock designers have invented many ways of compensating for such temperature-induced changes.

The pendulum or bendulum the Escapement object drives is subject to the same sorts of temperature effects. To compensate for them, the Escapement object implements optional temperature compensation using the SparkFun TMP102 temperature sensor.

Operation

Typically, an Escapement object is instantiated as a global in an Arduino sketch. It is then initialized using the enable() method in the sketch's setup() function. Then, the sketch's loop() function repeatedly invokes the beat() method. When the magnet passes the coil, beat() returns the number of microseconds that have passed since the magnet passed the last time. In this way, the Escapement can be used to drive a time-of-day clock display. The Escapement object is able to turn pendulum or bendulum ticks and tocks into microseconds because it adjusts to the period of whatever pendulum or bendulum it is driving. It does this through an automatic, optionally temperature-compensated, calibration process.

The beat() method has several internal operational modes that, together, operate as a state machine to calibrate and temperature-compensate the ticking pendulum or bendulum. It works like this.

When the Escapement is started by the enable() method, it attempts to read its persistent parameters from EEPROM. Depending on what it finds, the Escapement enters one of three operational modes, COLDSTART, WARMSTART, or CALIBRATE.

COLDSTART is entered if good calibration data can't be read or if it is forced by enable()'s initialMode parameter. In this mode all of the calibration data, including the Arduino clock's correction data is reset and then WARMSTART mode is entered.

WARMSTART is entered if good calibration data is read and temperature sensing hasn't been added or removed from the configuration since the data was written. With a WARMSTART, the Escapement runs using the corrected Arduino clock as a time reference for TGT_WARMUP beats. It then transitions to MODEL mode.

CALIBRATE is entered if good calibration data is read but temperature sensing has been added or removed. In this mode, the calibration information, but not the Arduino clock's correction data is reset and then WARMSTART mode is entered. This mode may be used by the sketch to force a recalibration. In CALIBRATE mode, beat() returns is measured using the (corrected) Arduino real-time clock.

MODEL uses the currently collected calibration information, if it exists, to create a least-squares fit model of the length of a beat as a function of temperature. If temperature compensation is not being used, the model is calculated as though we had information on only one temperature. If the information collected is insufficient to create a model, COLLECT mode is entered to collect more data. Once the model is created, the Escapement object switches to RUN mode. In MODEL mode, beat() returns the duration measured using the (corrected) Arduino real-time clock.

RUN mode is used for normal operation. During RUN mode, beat() returns the beat length as calculated by the linear least squares model defined during MODEL mode or, if the temperature is outside the model's range, the value measured using the (corrected) Arduino real-time clock. If RUN mode detects that no model has been calculated, it switches to MODEL mode. If it detects that the temperature is one for which we have not completed data collection, it switches to COLLECT mode.

COLLECT mode collects information about the duration of TGT_SAMPLES beats for each of TEMP_STEPS half-degree C "buckets" of temperature. Buckets are indexed by the variable tempIx. The 0th bucket is centered on TEMP_MIN. This bucket, like all the buckets, runs for a half degree C. The highest temperature bucket is centered at TEMP_MIN + (TEMP_STEPS - 1) / 2. Calibration information is not collected for temperatures outside this range. If temperature sensing is not available, temperature compensation cannot be done so the temperature is assumed to always be TEMP_MIN.

For each bucket there are two pieces of information, eeprom.uspb[] and eeprom.sampleCount[]. The first, eeprom.uspb[] is the average beat duration (in microseconds) at the temperature of that bucket. The second, eeprom.sampleCount[], is the number of samples that went into the average so far. A sample is collected if the temperature is within 1/8 degree C of the center-temperature of the bucket when the beat takes place. Data collection for a bucket consists of collecting TGT_SAMPLES samples for that bucket.

If, during collection, the temperature changes enough to fall into a different bucket before TGT_SAMPLES samples are collected, the progress made in collecting samples for the old temperature bucket is maintained in eeprom.uspb[] and eeprom.sampleCount[], and collecting at the new temperature bucket is started or resumed. When TGT_SAMPLES samples have been taken for a bucket, the Escapement object stores the contents of the eeprom structure -- Escapement's persistent parameters -- in the Arduino's EEPROM and switches to MODEL mode. During COLLECT mode, beat() returns the duration measured using the (corrected) Arduino real-time clock.

The net effect of the COLLECT and MODEL modes is that the Escapement object automatically characterizes the bendulum or pendulum it is driving by determining the average duration of beats at half-degree intervals as it encounters different temperatures. It uses this information to calcualte a linear least-squares model of beat duration as a function of temperature. It uses the model to calculate beat duration during RUN mode.

This would work nearly perfectly except that, as hinted at above, the real-time clock in most Arduinos is stable but not too accurate (it's a ceramic resonator, not a crystal). That is, real-time clock ticks are essentially equal to one another in duration but their durations are not exactly the number of microseconds they should be. To correct for this, we use a correction factor, eeprom.bias. The value of eeprom.bias is the number of tenths of a second per day by which the real-time clock in the Arduino must be compensated in order for it to be accurate. Positive eeprom.bias means the real-time clock's "microseconds" are shorter than real microseconds. Since the real-time clock is the standard that's used for calibration, automatic calibration won't work well unless eeprom.bias is set correctly. To help with setting eeprom.bias Escapement has one more mode: CALRTC.

In CALRTC mode, the duration beat() returns is the value measured by the (corrected) Arduino real-time clock. The CALRTC mode persists until changed by the Arduino sketch using setRunMode(). Because the value returned is the real-time-clock-measured value, in this mode the Escapement is effectively driven by the real-time clock, not the pendulum or bendulum, despite its ticking and tocking. The idea is to use the mode to adjust the real-time clock calibration (via the setBias() and incrBias() methods) so that the clock driven by the Escapement keeps perfect time. Once the real-time clock is calibrated, COLLECT mode should collect a good information about beat duration.

About

Arduino escapement library

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages