Accurate measurment of long time intervals
DylanB
Posts: 24
Hi,
We are designing a fraction collector (automated collection of effluent from soil columns) which is based around a BS2 and serial stepper motor controller. A simple outline of how the device is supposed to work is as follows:
1. init motor controller
main:
2. poll for user input (4 buttons)
3. display contents of timing variables (time between cycling a collection vessel) on serial LCD
4. if the user has pushed the 'start' key, start counting out time intervals specified in the program, and optionally modified by buttons.
5. check elapsed time, and rotate fraction collector carriage a single step.
goto main
The main problem is that while counting out time (with PAUSE or SLEEP) the BS2 can't really do anything else, and we would want to constantly be polling for user input on some buttons, and updating the LCD.
I have taken a look at the "pocket watch b" module, and wonder if polling this device in every loop of the main subroutine might work for couting out long time intervals (10mins to 2 hours)...?
Note that the single motor controller will be used to address 3 stepper motors, thus three timing variables will be used to decide when it is time to rotate the fraction collector carriage.
Any thoughts or ideas would be appreciated!
Cheers,
Dylan
We are designing a fraction collector (automated collection of effluent from soil columns) which is based around a BS2 and serial stepper motor controller. A simple outline of how the device is supposed to work is as follows:
1. init motor controller
main:
2. poll for user input (4 buttons)
3. display contents of timing variables (time between cycling a collection vessel) on serial LCD
4. if the user has pushed the 'start' key, start counting out time intervals specified in the program, and optionally modified by buttons.
5. check elapsed time, and rotate fraction collector carriage a single step.
goto main
The main problem is that while counting out time (with PAUSE or SLEEP) the BS2 can't really do anything else, and we would want to constantly be polling for user input on some buttons, and updating the LCD.
I have taken a look at the "pocket watch b" module, and wonder if polling this device in every loop of the main subroutine might work for couting out long time intervals (10mins to 2 hours)...?
Note that the single motor controller will be used to address 3 stepper motors, thus three timing variables will be used to decide when it is time to rotate the fraction collector carriage.
Any thoughts or ideas would be appreciated!
Cheers,
Dylan
Comments
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Jon Williams
Applications Engineer, Parallax
Another method I've seen used is to "roll your own" pause command by creating a subroutine and then placing the polled item in a loop. So instead of placing a "pause 100" command in the middle of your program, set a variable and call a loop with the sensor check and a pause together. Here's some pseudo-code to (poorly) demonstrate what I'm getting at:
The idea is to make the sensing or "work" portion of the code "interleaved" with the pause statement so instead of a "Pause 100" use a "pause 1 and Check Sensor" in a 100 count loop and call the loop instead using a "pause" command.... Not really elegant, but it can avoid having the stamp "sleeping" when it could be doing work...
Vern
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Post Edited (Vern) : 2/8/2006 6:07:41 PM GMT
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
This device will be running 3 motors (fraction collector carriages) simultaneously. The time interval in between "steps" in each of the motor assemblies will often be different. For example the delta-time (dt) for the three motors might look something like the following:
Ideally, we need to trust that the accuracy of the dt for each motor is about +- 1 second for time intervals < a couple of minutes.
My initial plan on how to accomplish this goes something like this
...and use some method of either reseting the clock at a certain point, or using the MOD operator to get an integral number of times that a motor_increment time interval has elapsed.
I am worried that it will be hard to maintain an accurate tracking of real time and the time at which commands are run by the basic stamp--- but given the speed of the BS2, should I be?
Thanks again,
Dylan
Also, because of the timing intervals, you might want to stagger the initial motor starts by a minute or so. Otherwise, I think worst case scenario would be all three motors conflicting every 40 minutes or so.
Motor 1 comes on after 5 minutes, then Motor 2 comes on after 8 minutes, Motor 3 comes on after 20 minutes.· If there is a shut down time for each, it too can easily be implemented.· Some time ago I built a Digital Thermostat with multiple schedules for events.· It wasn't limited like the ones you buy at Wal-Mart.· You could input several on/off times for each event on different days.· This applies here as well.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
Here is a little bit more info on the project. We have 3 stepper motors, each attached to a circular test tube rack. A single test tube is positioned under a soil column, and effluent is collected in the test tube. Based on the nature of the soil, and thus the rate of effluent discharge, the test tube rack under the soil column must be advanced 1 test tube every time interval. this time interval can be specified in the program sent to the BS2 (based on some initial guesses), and then later adjusted up or down with some buttons connected to the BS2. Since the three soils are not quite the same, the time interval between advancing the test tube rack for each setup may be different.
Therefore, once the device has been started, motor1 will need to be advaced every dt1 interval, motor 2 will need to be advanced every dt2 intervatl, and motor 3 will need to be advanced every dt3 interval.
a simplified flowchart might look like this:
advancing the motors is accomplished by sending some serial data to a motor controller, which addresses each motor.
My thoughts on using a single clock: it would be more elegant if we can do it this way, but i am worried about losing precision due to integer math on time durations- but maybe this is not an issue. It sounds like the smallest time interval that we need is 1 minute.
thanks again,
Dylan
dyan
·· Because you are only using minutes you should be able to use a single DS1302.· You could even further simplify the whole thing by converting ALL time (hrs, mins, secs) to just minutes.· There are 1440 minutes in a single day and the code to handle a roll-over is easy although in your application I'm not sure it would apply.·
·· Now, for simplicity, if you DO want to use three DS1302 chips you can share the clock/data lines so that three DS1302 chips will take up 5 I/O pins.· You can use the same constants for clock and data simplifying the control of the chip.· This would be very easy to implement.·
·· This also makes more sense if you need to be able to adjust your interval on the fly, so to speak.· Each DS1302 has RAM which is backed-up with the time when using a backup battery.· You could store interval values on there and retrieve them at startup.· When they're changed you could update the value on the chip.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
Thank you for the helpful hints. We ordered 3 DS1302 units + crystals. The sample code for interfacing to a single RTC seems like it will be a good starting point. Which inputs/outputs can be shared between all three RTC units? Is there any sample code for this floating around somewhere perhaps... ?
Thanks!
Dylan
·· As in my previous post you can share the clock and data lines.· Each chip will need it's own chip select (RST) line so you're looking at using 5 I/O lines total in DS1302s.· Once you have the chips, get one working and I will show you an example of using more than one.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
Your code and hints have been most helpful!
We were successful in implementing the 3 independent DS1302 chips, using only 5 I/O lines as you suggested.
Now we have a new bug to squash... When the three clocks are started at the same time... well almost the same time with a FOR loop:
... the clocks appear to stay in sync:
...however, we are resetting individual clock units when a specific delta-time has elapsed
I have noticed that each time i reset a clock, it loses a small amount of time relative to other clocks that are still running. - possibly due to the fact that resetting a clack takes a small amount of time.
i am wondering if there is perhaps a more efficient way of accomplishing this task, i.e. some math that can be done to determine if a delta-time unit has elapsed, without having to reset the clock units.
attached is a complete listing of the program
Thanks!
Dylan
·· How is it that you mean the clock is out of sync with the others?· If you could better explain that I would know what I am looking for.· Also, understanding your need to read 3 clocks at the same time I might recommend forgoing a little code space and actually directly SHIFTOUT/SHIFTIN the data in that routine instead of using a FOR...NEXT loop.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
Starting all clocks at 00:00.
if i reset:
clock 0 every 15 sec
clock 1 every 30 sec
clock 2 every 45 sec
...then clock 0 will lag behind clock 1 which will lag behind clock 2.
i.e. near 15 seconds this is what the clocks should read
00:14 00:14 00:14
00:00 00:00 00:30
and they do.
i.e. near 30 seconds this is what the clocks should read
00:14 00:29 00:29
00:00 00:00 00:30
and they do.
i.e. near 45 seconds this is what the clocks should read
00:14 00:14 00:44
00:00 00:15 00:00
and they almost do: clock 0 appears to increment _just after_ clock 1 and 2
i.e. near 60 seconds this is what the clocks should read
00:14 00:29 00:14
00:00 00:00 00:15
and they almost do: clock 0 appears to increment _slightly after_ clock 1 and 2
i.e. after 75 seconds this is what the clocks should read
00:14 00:14 00:29
00:00 00:15 00:30
and they almost do: clock 0 appears to increment _slightly after_ clock 1 and 2
i.e. after 90 seconds this is what the clocks should read
00:14 00:29 00:44
00:00 00:00 00:00
and they almost do: clock 0 appears to increment _slightly after_ clock 1 and 2
.... and so on. the longer i let it run, the more "behind" clock 0 and then clock 1 become relative to clock 2 .
in summary, after a total time duration of 100 minutes = 6000 seconds, the clocks have been reset:
clock 0: 400 times
clock 1: 200 times
clock 2: 133 times
seems that every clock reset operation makes each one of these three clocks slightly slower than an outside time source, porportional to the number of reset operations.
I wonder if there is some way to compensate for this...
As far as savinf some space goes. How exactly would I implement a single SHIFTIN operation such that it read in data from each clock ?
Thanks!
Dylan
·· Do you have a measurement of how far off they are and at what interval?· If so, instead of loading them with $00 each time you could maybe load seconds with $01 or something to compensate for lost time.· I wonder if the problem could be when the other operations are happening before you reset the clock.
·· Okay, I see one thing that could slow it down a little...When the clock reaches its trip point you should be resetting it immediately, but instead you gosub to the mx routine and SEROUT and DEBUG first.· That will cost a little time.· Remember, the clock will keep going once reset so you can do that other stuff right afterward.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
I agree with the earlier posts about using only one single clock chip. Then you read the time and convert to a sequential time of the proper granularity. For exmple minutes of day from 0 to1439:
minOfDay = hours.nib1 * 10 + hours.nib0 * 6 + minutes.nib1 * 10 + minutes.nib0
Then the different event timings are a matter of period and phase:
motor1flag = minOfDay +1440 + phase1 // period1 max 1 ' = 0 when it is time to advance motor 1, =1 otherwise
motor2flag = minOfDay +1440 + phase2 // period2 max 1 ' = 0 when it is time to advance motor 1, = 1 otherwise
etc.
1440 is added to the minute of day so that it will work properly over the midnight boundary. The phase is a number from 0 to (period-1), to allow for the start times to be offset from an integer multiple of the period with respect to real time.
The granularity could be taken down to 5 seconds and stil fit in one word variable.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
Thank you for the tips. Programming in the PBASIC environment is still a bit new to me- especially after spending most of my time using scripting languages like AWK and PHP.
I don't quite follow the logic of how your example code would work.
For example, my delta-time value for motor 1 (dt_1) is equal to 10 minutes. I would like to run a subroutine every dt_1 minutes.
Looking at your example:
It looks like I would implement this such that:
However, I am not quite sure what the variables: phase1 and period1 "mean" ....
As for the granularity of this project, I think that a single minute would be good enough- as long as it is consistant.
Thanks!
Dylan
http://169.237.35.250/~dylan/fraction_collector/
Cheers,
Nice project!
For the math, the first thing need to know is the "//" operator. In Stampese, that means, "remainder after division". In my example, period1 is equivalent to your dt_1, that is, how often you want the motor to turn on.
in this code
the three independent flags will equal zero once every 10, 12 and 15 minutes respectively, and 1 at all other times. That is because minOfDay // period is the remainder of the divisions, e.g.,
29 // 15 = 14
30 // 15 = 0
31 // 15 = 1
32 // 15 = 2
The MAX operator restricts the value to either zero or 1.
The actions as written occur exactly matched with clock time. That is, with 10 minutes it would happen on the hour, 10 minutes after the hour, and so on, and with 12 minutes, it would also be on the hour, 12 minutes after the hour and so on. That might be what you want.
However, if you want an arbitrary origin for starting each interval, then you need phase1, phase2, phase3. Say you want to start a 10 minute interval for motor 1 at 7 minutes after the hour of 2am. The value of minOfDay at that time is 127, and the remainder when divided by 10 is 7. Set phase1 equal to that value. Subsequently, the motor1flag will equal zero at 137, 147, 157,...
motor1flag = minOfDay + 7 // 10
which you would use with variables instead of constants for 7 and 10. The phase1=7 is the starting phase with respect to the 10 minute cycle.
If you restrict your time intervals to numbers that evenly divide 1440 minutes, then that is all you need. You don't even have to add the extra 1440 I showed in my example. Those numbers that divide evenly into 1440 are,
1,2,3,4,5,6,8,10,12,15,18,20,24,30,32,45,60,... and others.
If you need to use numbers like 13 that do not divide evenly into 1440, then the system will have to do an extra bit of calculation as the clock rolls back at midnight. It is a simple operation to adjust the phase with respect to clock time. Otherwise you would have a glitch at midnight and a motor would run too short or to long. Let me know if you need help with code to do that.
The advantage of this approach is that you can use a single RTC, and you let it run, without the subsecond error of resetting it.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
·· However you solve this minor issue, once it is done you should definately post it in the Projects Section with a few of the pictures.· You've done some nice work there and I see the StampCI Board in the mix as well.·
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
thanks for the great explanation -- I am starting to get a handle on your examples now.
After talking with the person who is actually going to be using this system, it sounds like she would like to have control over the starting time, and dt values for each of the motors independently: i.e. setup motor 1 and start it .... a little later setup motor 2 and then start it . In this way the absolute starting time of any one motor will most likely not be the same for the other 2. However, i have three RTC units in place, so perhaps a slight modification to your example might do the trick:
I will start a new thread for the couple questions I have regarding that aspect of the project.
A couple notes:
an RTC will be started from 00:00:00 when a motor is "enabled" . We will need to be able to set the dt value for this motor in arbitrary amounts of minutes, probably in the range of 1-300 minutes . At the maxmum dt of 300 minutes * 30 test tubes, this would be 6.25 days. While I doubt that we would actually run a single sample for that long, i think that building in support for total time durations of >24 hours would be a good idea (just in case). might need some pointers on this aspect.
Thanks,
Dylan
·· A tip on working with the days of the week...The defauly value is 0, but the valid days are 1 through 7.· Obviously the other values are literal, but the days thing had me confused for a while when I first started using this chip.· I guess the main question is, does the unit always work in full minutes or is there a time when you will want to go for xx minutes and xx seconds?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
I imagine that this device will only operate in increments of minutes-- i.e. we need second-leve precision, but only minute-level time step intervals.
thanks,
Dylan
thanks,
Dylan
·· The reason you use HEX is because the values are in BCD within the DS1302.· That means there are 2 digits packed into a single byte.· When you specify a 2 digit decimal value it will often not equal the BCD value.· HEX formatting is as close as you're going to get as long as each digit doesn't exceed 9.· For example, $99 is the largest BCD value while $FF is the largest HEX value, otherwise the packed format is similar.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
ok, i have done some more work-- and it almost works!
here is a code snippet
however, the part of the loop which checks to see if we should increment a motor evaluates to TRUE about 5 times per loop in which motorflag = 0 .... i think that this is due to the fact that this loop is running about 5 times per second -- thus there are 5 loops where the follwoing expression is TRUE:
Any thoughts on how to only run the motor advance routines *once* per TRUE evaluation of the about code?
Thanks -- we are almost there...
Dylan
·· It's easier to deal with code as an attachment...That way it can be loaded into the editor and viewed properly.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com