Home AMX User Forum AMX Control Products

Analog Clock

On another thread there was discussion of an analog clock. I happened to be in the process of making one. So, here's all the ingredients for it. enjoy.


post:
ericmedley wrote: »
Okay, here it is.

I've included all the stuff you'd need to make it yourself to. I made pretty simple clock hands for this sample. It's much easier to create the different positions of the hands in a 3D amination program. But you can do it just fine with Photoshop.

To move the hand from one second/minute just select the layer of the hand and then rotate it by 6 degrees. You'll need to reposition the center of the hand with the spindle. Expoert, wash rinse repeeat...


Be careful not to move the guide layer.

The clock is made with 3 multi-state levels. In this example level 1 is hours. level 2 - minutes and level 3 seconds.

minutes and seconds are simple enough. just send the numberic value of the second and/or minute to the level and it'll work.

Hours is a little more complicated. Here's the formula to make that work. My version doesn't round. I honestly don't care that much. But you may want to round the calculation to get a more honest looking hour hand.
send_level dev_AW_TP_01,3,SECOND_CURR // second hand
	send_level dev_AW_TP_01,2,MINUTE_CURR // minute Hand
	if(HOUR_CURR<13)
		{
		send_level dev_AW_TP_01,1,(((HOUR_CURR)*60)/12 )+ ((MINUTE_CURR*5)/60)   // hour hand
		}
	else
		{
		send_level dev_AW_TP_01,1,(((HOUR_CURR-12)*60)/12 )+ ((MINUTE_CURR*5)/60)  // hour hand
		}

Have fun.
e


PS: Okay, it appears that I cannot upload the file to the website. Must be too big or something. I'll put it on my website.

http://www.ericmedley.com/analog_clock.zip right-click and save as.

Comments

  • Joe HebertJoe Hebert Posts: 2,159
    Cool Clock

    Eric,

    Now that?s a cool clock!
    Two thumbs way up.

    I just had to make a minor adjustment to the TP file by changing the level ranges to 0-59 instead of 0-255.
    Other than that the sample file does an excellent job showing all the pieces.

    One suggestion for the hours calculation. Instead of doing the IF and ELSE, you can use the mod operator with one line of code and accomplish the same thing, like so:
    SEND_LEVEL dvTP,1,(((hour % 12)*60)/12) + ((minute*5)/60)
    
  • ericmedleyericmedley Posts: 4,177
    Joe Hebert wrote: »
    Eric,

    Now that’s a cool clock!
    Two thumbs way up.

    I just had to make a minor adjustment to the TP file by changing the level ranges to 0-59 instead of 0-255.
    Other than that the sample file does an excellent job showing all the pieces.

    One suggestion for the hours calculation. Instead of doing the IF and ELSE, you can use the mod operator with one line of code and accomplish the same thing, like so:
    SEND_LEVEL dvTP,1,(((hour &#37; 12)*60)/12) + ((minute*5)/60)
    

    OOPS!!! my bad on the levels. I made a new TP file with brand new faders. I forgot that one for sure. To all who use it. Make sure to check that one. Thanks for the math fix too. I have to admit that I'm pretty old-skool and don't spend much time tweaking my math for better performance. If I find a way that works, I just go with it. My poor little brain won't hold much nowadays... :)

    e


    (EDITED TO NOTE) I fixed the level problem in the sample TP file. It's now updated on the site. Sorry about that...
  • kbeattyAMXkbeattyAMX Posts: 358
    Great Stuff!

    Here's the code I used to make it run.
    DEFINE_CONSTANT
    SEC_timeline = 1
    
    DEFINE_VARIABLE
    long SEC_timeline_Time[] = {1000}
    
    DEFINE_START
    timeline_create(SEC_timeline,SEC_timeline_time,1,timeline_absolute,timeline_repeat)
    
    timeline_event[SEC_timeline]
    {
      send_level dvTP,3,atoi(mid_string(time,7,2))
      send_level dvTP,2,atoi(mid_string(time,4,2))
      send_level dvTP,1,(((atoi(mid_string(time,1,2)) % 12)*60)/12)+ ((atoi(mid_string(time,4,2))*5)/60)
    }
    
    
  • JohnMichnrJohnMichnr Posts: 279
    OK - that is really fun!

    Now all I need is the ticking sound to keep playing on the touchpanel. I'm sure that wouldn't cause any problems :)
  • AMXJeffAMXJeff Posts: 450
    Cool TP Pages...

    More then one way to skin that cat...

    I also changed the min max levels on the TP to 0-59 No ifs, no timelines... Just in DEFINE_PROGRAM

    Remember SEND_LEVELS only get sent when the VALUE changes. So the second level only gets sent once a second, the min only once a min, and the hour only a few times an hour....

    // Code I used...
    DEFINE_PROGRAM

    // oops made a typo...
    SEND_LEVEL dvTP, 1, (((TIME_TO_HOUR(TIME) % 12) * 60) / 12) + ((TIME_TO_MINUTE(TIME) * 5) / 60)
    SEND_LEVEL dvTP, 2, TIME_TO_MINUTE(TIME)
    SEND_LEVEL dvTP, 3, TIME_TO_SECOND(TIME)
  • kbeattyAMXkbeattyAMX Posts: 358
    A possible problem I see with this is that these statements are evaluated every pass thru mainline. It may add some unwanted overhead in runtime processing.
  • ericmedleyericmedley Posts: 4,177
    kbeattyAMX wrote: »
    A possible problem I see with this is that these statements are evaluated every pass thru mainline. It may add some unwanted overhead in runtime processing.

    Actually that's just a snipet from the code just to illustrate the math. I figured y'all would program the thing the way you wanted.

    I have mine in a trap that only comes on when someone is viewing the clock. In my program I try to minimize the network traffic as much as possible.
  • ericmedleyericmedley Posts: 4,177
    JohnMichnr wrote: »
    OK - that is really fun!

    Now all I need is the ticking sound to keep playing on the touchpanel. I'm sure that wouldn't cause any problems :)


    You say that as a joke but I did do that for fun. I put one of those obnoxious mechanical wind-up clock ticking samples.

    When the fun was over I had to turn it off, of course...
  • kbeattyAMXkbeattyAMX Posts: 358
    ericmedley wrote: »
    Actually that's just a snipet from the code just to illustrate the math. I figured y'all would program the thing the way you wanted.

    I have mine in a trap that only comes on when someone is viewing the clock. In my program I try to minimize the network traffic as much as possible.

    Good Stuff! Minimize the traffic as much as possible. Yes there are many ways to tackle this task. It's good to see all of the ways we can dream up to make this work.
  • viningvining Posts: 4,368
    kbeattyAMX wrote:
    A possible problem I see with this is that these statements are evaluated every pass thru mainline.

    Here's a little function that I use that's either called by a timeline or as a quick fix in define_program behind a wait 2 or wait 5 depending on what else is runing in there. When its called it will only test for a change in the current second. If the second has changed it will then check to see if the minute has changed, so on and so forth. It's the most logical and efficient way I've found to update time vars.
    DEFINE_FUNCTION fnTimeUpdateTimes()
         
         {
         if(nMY_Second != TIME_TO_SECOND(TIME))
    	  {
    	  nMY_Second = TIME_TO_SECOND(TIME) ;
    	  SEND_LEVEL dvTP, 3, nMY_Second ;
    	  cSysTime = TIME ;
    	  if(nMY_Minute != TIME_TO_MINUTE(TIME))
    	       {
    	       nMY_Minute = TIME_TO_MINUTE(TIME) ;
    	       SEND_LEVEL dvTP, 2, nMY_Minute ;
    	       if(nMY_Hour != TIME_TO_HOUR(TIME))
    		    {
    		    nMY_Hour = TIME_TO_HOUR(TIME) ;
    		    SEND_LEVEL dvTP, 1, (((nMY_Hour % 12) * 60) / 12) + ((nMY_Minute * 5) / 60) ;
    		    if (cMyDay != Day)
    			 {
    			 cMyDay = Day ;
    			 if (cMyDate != Date)
    			      {
    			      cMyDate = Date ;
    			      }
    			 }
    		    }
    	       }
    	  }
         }
    
  • JohnMichnrJohnMichnr Posts: 279
    ericmedley wrote: »
    You say that as a joke but I did do that for fun. I put one of those obnoxious mechanical wind-up clock ticking samples.

    When the fun was over I had to turn it off, of course...

    How annoying was it - and how much network traffic did it generate. You would have had to keep calling it correct?
  • ericmedleyericmedley Posts: 4,177
    JohnMichnr wrote: »
    How annoying was it - and how much network traffic did it generate. You would have had to keep calling it correct?

    Ha!

    It's a sample loop of about 5 ticks. I didn't really time it that well as it was meant to be a joke. But I was sending the 'SOU-' command every 3 seconds to make it sound continuous.
  • AMXJeffAMXJeff Posts: 450
    Overhead
    kbeattyAMX wrote: »
    A possible problem I see with this is that these statements are evaluated every pass thru mainline. It may add some unwanted overhead in runtime processing.

    Food for thought:

    All very good things to think about to minimize overhead. But keep in mind that just because you use IFs and Timelines and Conditions, all that has to be checked and takes time. The facts are that the interpreter handles math a lot faster than conditionals. Sometimes when doing NOT conditionals the interpreter has to do two to three checks before it decides the condition is true or false. Also Timelines add tons of overhead, so pick your poisons.

    I am not saying that any of these methods are better than the other. But according to my testing the three send levels in mainline takes less than one millisecond to complete, I do not know how much overhead I added to just get the actual time, but very minimal of time. Plus the send levels do not create any traffic until the actual levels change.
  • kbeattyAMXkbeattyAMX Posts: 358
    AMXJeff wrote: »
    Food for thought:

    All very good things to think about to minimize overhead. But keep in mind that just because you use IFs and Timelines and Conditions, all that has to be checked and takes time. The facts are that the interpreter handles math a lot faster than conditionals. Sometimes when doing NOT conditionals the interpreter has to do two to three checks before it decides the condition is true or false. Also Timelines add tons of overhead, so pick your poisons.

    I am not saying that any of these methods are better than the other. But according to my testing the three send levels in mainline takes less than one millisecond to complete, I do not know how much overhead I added to just get the actual time, but very minimal of time. Plus the send levels do not create any traffic until the actual levels change.

    I'm not sure how you are testing performance.
    MainlinePass = MainlinePass +1
    wait 10
    {
      send_string 0,"'PASS=',itoa(MainlinePass)"
    	MainlinePass = 0
    }
    
    send_level dvTP,3,atoi(mid_string(time,7,2))
    send_level dvTP,2,atoi(mid_string(time,4,2))
    send_level dvTP,1,(((atoi(mid_string(time,1,2)) % 12)*60)/12)+ ((atoi(mid_string(time,4,2))*5)/60)
    

    Moving this code to mainline slowed Mainline passes from 13760 passes to 1480 passes. Looks like a major slowdown to me.
  • JohnMichnrJohnMichnr Posts: 279
    kbeattyAMX wrote: »
    Moving this code to mainline slowed Mainline passes from 13760 passes to 1480 passes. Looks like a major slowdown to me.

    OK - I have a question on this - and I am speaking from ignorance, not spite. So be nice y'all:)

    Does it really matter that define_program runs only 1480 passes per second as opposed to 13760?

    As I understand it (this may be the point of error on my part) mainline is the lowest on the priority and will only run if there are no other event in the stack. So is it a measure of system performance, or a measure of system spare time? (hey i'm board now - let's run mainline)

    If I am parsing a bunch of data back from a device and going through a couple of thousand bytes with a while loop, isn't mainline also going to slow down even if i have nothing in mainline?

    I fit is a measure of system performance, then are there hard numbers to maintain? ie your mainline must run between 5000 and 15000 times per second.
  • AMXJeffAMXJeff Posts: 450
    I tested the amount of time that it took to peform the three lines of code. The ones you tested are not the three lines of code I used...

    MIN = TIME_TO_MINUTE(TIME);

    SEND_LEVEL dvTP, 1, (((TIME_TO_HOUR(TIME) % 12) * 60) / 12) + ((MIN * 5) / 60)
    SEND_LEVEL dvTP, 2, MIN
    SEND_LEVEL dvTP, 3, TIME_TO_SECOND(TIME)
  • AMXJeffAMXJeff Posts: 450
    JohnMichnr wrote: »
    OK - I have a question on this - and I am speaking from ignorance, not spite. So be nice y'all:)

    Does it really matter that define_program runs only 1480 passes per second as opposed to 13760?

    As I understand it (this may be the point of error on my part) mainline is the lowest on the priority and will only run if there are no other event in the stack. So is it a measure of system performance, or a measure of system spare time? (hey i'm board now - let's run mainline)

    If I am parsing a bunch of data back from a device and going through a couple of thousand bytes with a while loop, isn't mainline also going to slow down even if i have nothing in mainline?

    I fit is a measure of system performance, then are there hard numbers to maintain? ie your mainline must run between 5000 and 15000 times per second.

    You are correct... mainline only runs when needed and is not a measurement of performance... By actually having a counter in mainline forces it to run...
  • kbeattyAMXkbeattyAMX Posts: 358
    AMXJeff wrote: »
    You are correct... mainline only runs when needed and is not a measurement of performance... By actually having a counter in mainline forces it to run...

    No I don't think so...

    "Mainline is the section of the program that is executed continuously by the NetLinx Master Controller. DEFINE_PROGRAM contains the code known as mainline that is executed continuously as long as the Controller has power. Mainline is where all input (button events, level changes, command/string input, etc.) is processed. After each pass through mainline, NetLinx services the NetLinx bus and updates its internal structures." from AMX

    If mainline slows down the interpretor slows down then you start getting Max Que threshold messages.
  • DHawthorneDHawthorne Posts: 4,584
    All event-driven program environments must have some manner of mainline loop, if only to check hardware interrupts for events. We often refer to the "DEFINE_PROGRAM" section as mainline, but it technically isn't the same thing, it just gets executed as part of the mainline loop. Most of mainline happens behind the scenes from our vantage. Since NetLinx is an interpreted language, it makes sense that compiler optimization passes would just skip the DEFINE_PROGRAM segment if nothing is in there, but just that segment; the mainline loop itself still has to run to check the event tables, WAITs, etc.

    I'm very pragmatic when it comes to such considerations ... the bottom line is not if the processor is doing any extra work, but if my running program is affected. I'm not going to go out of my way to keep the DEFINE_PROGRAM section empty if it makes sense to have code there, though I do try, for the most part to keep my code event-driven. It's just not always the most graceful way to handle things, and if it costs me a few ticks, well, so be it. If it noticeably bogs the program down, that's another story. I've only once had a case where running code made any discernible difference; it's nearly always the message queue and device response that causes trouble.
  • kbeattyAMXkbeattyAMX Posts: 358
    DHawthorne wrote: »
    All event-driven program environments must have some manner of mainline loop, if only to check hardware interrupts for events. We often refer to the "DEFINE_PROGRAM" section as mainline, but it technically isn't the same thing, it just gets executed as part of the mainline loop. Most of mainline happens behind the scenes from our vantage. Since NetLinx is an interpreted language, it makes sense that compiler optimization passes would just skip the DEFINE_PROGRAM segment if nothing is in there, but just that segment; the mainline loop itself still has to run to check the event tables, WAITs, etc.

    I'm very pragmatic when it comes to such considerations ... the bottom line is not if the processor is doing any extra work, but if my running program is affected. I'm not going to go out of my way to keep the DEFINE_PROGRAM section empty if it makes sense to have code there, though I do try, for the most part to keep my code event-driven. It's just not always the most graceful way to handle things, and if it costs me a few ticks, well, so be it. If it noticeably bogs the program down, that's another story. I've only once had a case where running code made any discernible difference; it's nearly always the message queue and device response that causes trouble.

    Agreed. On the other hand, tracking passes through mainline can be an indicator of system health and stability. Code in DEFINE_EVENT and DEFINE_PROGRAM both affect the passes in mainline. 99% of projects I complete have no issue with the system getting bogged down. The problems occur when i'm working with a very large system and processing has to be distributed among masters. Checking the health of the master is important to having a stable system. Passes through mainline helps me understand where I am in processing load. (Of course the interpretor buffer must not be getting back logged.)
  • ipssheldonipssheldon Posts: 106
    Why not create a timeline that fires every second or even every half second and only update the clock when the timeline triggers? That keeps this out of mainline altogether. The timeline could run forever or simply start the timeline loop when the analog clock is active.

    Just a thought.
  • kbeattyAMXkbeattyAMX Posts: 358
    ipssheldon wrote: »
    Why not create a timeline that fires every second or even every half second and only update the clock when the timeline triggers? That keeps this out of mainline altogether. The timeline could run forever or simply start the timeline loop when the analog clock is active.

    Just a thought.

    Check out the discussion at the beginning of the thread.
  • ericmedleyericmedley Posts: 4,177
    My clock, which was intended to bring joy to everyone's world has only resulted in debate... :(
  • viningvining Posts: 4,368
    ipssheldon wrote:
    Why not create a timeline that fires every second or even every half second and only update the clock when the timeline triggers?
    Timelines like waits are still part of the mainline loop it's just just not in DEFINE_PROGRAM so in a sense it still gets checked on every pass of the main system timeline or every pass of the mainline to check the repitition rate to see if it's time to fire off a time_line event. I would think the system timeline runs at a 1 ms repition to check user defined time_line and decrement their stored time value on every pass until it reaches 0 at which point it executes or fires the event. Same for waits just at 100ms intervals.

    I personally would use a time_line but only create it when "on page" and then kill it when all TPs are "off page" that way there is only overhead when there needs to be. So basically the clock code doesn't exist if you're not on the clock page. Typically I would do this for everything a module does form feedback to processing queues or sending queries. If on page fire the time_line ad let it control everything for that module and let it die when nobody's watching.

    I still however have modules that runs code define_program and doesn't use time_lines but those are generally older code.
    ericmedley wrote:
    My clock, which was intended to bring joy to everyone's world has only resulted in debate...
    You should be a proud parent of your forked child.
  • All this talk about using up processor power to generate the code for the clock...when you could just give two of the three levels a setup port of 0, then make one level code Time level: Hour; make #2 level code Time level: minute; and the last, if you want seconds, is the only one that needs any code.

    Or am I missing something... :-)
  • ericmedleyericmedley Posts: 4,177
    All this talk about using up processor power to generate the code for the clock...when you could just give two of the three levels a setup port of 0, then make one level code Time level: Hour; make #2 level code Time level: minute; and the last, if you want seconds, is the only one that needs any code.

    Or am I missing something... :-)
    You assume that I want to send the system time...

    :D
  • Aaaaahaaaaaaaaa.......... :-)
  • travtrav Posts: 188
    Corrections

    Sorry,it was getting to me...

    [php]
    DEFINE_VARIABLE

    INTEGER nThe_bird
    INTEGER nThe_word
    DEVCHAN dcEverybody_Heard[] = { {10001:1:0,1} }


    DEFINE_EVENT

    BUTTON_EVENT[dcEverybody_Heard]
    {
    PUSH:
    {
    the_word = GET_LAST (dcEverybody_Heard)
    nThe_bird = nThe_word
    }
    RELEASE:
    {
    nThe_word = nThe_bird
    IF(button.input.device<>(dcEverybody_Heard[nThe_word]))
    {
    SEND_STRING 0,"'PA PA PA OO MOW MOW PA PA PA OO MOW MOW MOW'"
    }
    }
    }
    [/php]
  • ericmedleyericmedley Posts: 4,177
    trav wrote: »
    Sorry,it was getting to me...

    [php]
    DEFINE_VARIABLE

    INTEGER nThe_bird
    INTEGER nThe_word
    DEV dcEverybody_Heard[] = { {10001:1:0,1} }


    DEFINE_EVENT

    BUTTON_EVENT[dcEverybody_Heard]
    {
    PUSH:
    {
    the_word = GET_LAST (dcEverybody_Heard)
    nThe_bird = nThe_word
    }
    RELEASE:
    {
    nThe_word = nThe_bird
    IF(button.input.device<>(dcEverybody_Heard[nThe_word]))
    {
    SEND_STRING 0,"'PA PA PA OO MOW MOW PA PA PA OO MOW MOW MOW'"
    }
    }
    }
    [/php]

    Ha! I have to do that too. My eyesight is getting so poor that I even have to put spaces between lots of things so I can read them. Like my_Functin ( var_1 , var_2 , var_3 ) instead of (var_1 ,var_2 ,var_3)


    My for loops all look like

    for ( nloop = length ; nloop > 0 ; nloop -- )

    I'm real sure I won't be able to do this stuff much longer... I'll have to go back to full-time music production again. (or run my 22" monitor at 640 X 480 )
  • @trav


    ROFLMAO!!


    I'll nick that if you don't mind :-D
Sign In or Register to comment.