Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Timing mechanism in C

  1. Jul 2, 2012 #1
    I'm using arduino to make a water sensor. The sensor needs to go off after a certain period of time of not having come into contact with any water. The difficulty is that i cant use a delay() mechanism as it would mess with other functions of the sensor. I had thought i found a solution but i cant get it to work. what i came up with was

    int seconds = 0;
    long secPrev = 0;
    long secInt = 1000;

    void loop() {
    ∴long timer = millis();
    ∴if (timer - secPrev > 1000000) //to compensate for roll over
    ∴∴secPrev = 0;

    ∴if (timer - secPrev > secInt) { //should increment seconds by 1
    ∴∴seconds += 1;
    ∴∴secPrev = timer;
    ∴}
    }


    For some reason the seconds arent increasing. the secPrev value is behaving as it should, im just not sure why the seconds arent. would anyone know why this is?
     
  2. jcsd
  3. Jul 2, 2012 #2

    Mark44

    Staff: Mentor

    If you post code, put it between [noparse]
    Code (Text):
     and
    [/noparse] tags. I did this for you, below.
    If seconds isn't incrementing, it must be that timer - secPrev <= secInt. I don't understand your logic well enough to guess why this is happening. I haven't worked with the Arduino board at all, or its development environment. Do you have a debugger to work with? If so, that would make it easy to find why your code is doing what it's doing.

    If no debugger is available, put in printf statements here and there so you can see the value of your variables.
     
    Last edited: Jul 2, 2012
  4. Jul 2, 2012 #3
    arduino uses C primarily, with a few libraries of its own. what i was going for was for every 1000 miliseconds (timer - secPrev) the value of seconds should increase by one and the new value of secPrev should become the current time in milliseconds (secPrev = timer). the value of secPrev behaves how it should, its like the code just skips over seconds all together. I think the problem is with the scope, because when i have it print the value of seconds outside of the if statement it only returns 0 but when inside the if statement it returns 1. I attempted to use pointers but I'm not entirely sure if i used them correctly. to set pointers, it would look like this, right?

    Code (Text):
     long *secPtr = &seconds;
    but it had given me the same result.
     
    Last edited by a moderator: Jul 2, 2012
  5. Jul 2, 2012 #4

    Mark44

    Staff: Mentor

    Are seconds, secPrev, and secInt global variables (declared outside of any function)? If so, they should be visible within your loop() function.

    I don't understand why you would get different values for seconds depending on whether you were inside the if statement (the second one) or not.

    Are you using pointers in the code you're looking at? The code you posted doesn't use pointers at all.
     
  6. Jul 2, 2012 #5
    No, though i did try pointers. that didnt seem to help any. with the pointers, the code looked like

    Code (Text):

    long seconds = 0;
    long *secPtr = &seconds;
    long secInt = 1000;
    long secPrev = 0;

    void loop() {
      timer = millis();
      if ((timer - secPrev) > 1000000) {
        secPrev = 0;
      }
      if ((timer-secPrev) > secInt) {
        *secPtr += 1;
        secPrev = timer;
      }
    }
     
    with this, i get the same result. And the variables are declared outside of any function. they are declared at the very beginning of the sketch, before setup or loop. atleast, i thought thats how you declare global variables, i could be wrong.
     
  7. Jul 2, 2012 #6

    AlephZero

    User Avatar
    Science Advisor
    Homework Helper

    You haven't told us eaxctly what function millis() does, but if it "rolls over" to 0, or to a big negative number, I think you can get timer - secprev < 0. In that case your test for (timer - secPrev > 1000000) looks wrong.

    Or maybe timer and secprev should be declared unsigned long, not long?
     
  8. Jul 2, 2012 #7

    yes, they should be unsigned, but with that change it still doesnt allow seconds to increase any. the test (timer - secprev)> 1000000 is to compensate for the rollover.

    And millis() counts from 0 the number of milliseconds that have passed since the program started running. in this case, i want seconds to increase by 1 every 1000 milliseconds.
     
  9. Jul 2, 2012 #8

    Mark44

    Staff: Mentor

    Do you have printf? You didn't say whether you had a debugger available.
    You could try putting in some output statements, which I suggested before. If your variables are visible outside the if statements, they should be visible inside as well.

    If they aren't, you need to take a close look at the documentation for the Arduino C compiler, because it doesn't seem to be working like other C systems.

    Code (Text):

    void loop() {
      timer = millis();
      printf("timer: %d\n", timer);  // Or %ld if timer is type long.
      printf("secPrev: %ld\n", secPrev);
      if ((timer - secPrev) > 1000000) {
        secPrev = 0;
      }
      if ((timer-secPrev) > secInt) {
        *secPtr += 1;
        secPrev = timer;
      }
    }
     
     
  10. Jul 2, 2012 #9

    Mark44

    Staff: Mentor

    How big is an int on this system? If it's a 16-bit system, it could be that an int and a long are the same size (16 bits), in which case the largest signed int or long would be 32,767. And unsigned, the largest values would be 65,535.
     
  11. Jul 2, 2012 #10
    I do not have a debugger available. and having it print the values, for seconds its constantly zero, however secPrev does behave how it should. every second its value increases by 1000 as it should. when i remove the test and replace it with 1

    Code (Text):


    if (1) {
      seconds += 1;
      secPrev = timer;
    }

     
    the seconds do incriment as they should, but it does so at a much greater rate (the 1000 millisecond restriction is gone). also, when i run the original code with the print inside of the if statement, it shows that seconds does increase by 1 but the value is lost immediately after the end of the if statement. so when it prints from within the if statement, it constantly returns 1.
     
  12. Jul 2, 2012 #11
    it is 8 bit
     
  13. Jul 2, 2012 #12

    Mark44

    Staff: Mentor

    How big is a long? I'm guessing that it is 16 bits, but I don't know that.
    The largest value of a signed long (if 16 bits) is 32,767, so there's no chance of timer - secPrev ever getting anywhere close to a million, as you have in this code.
    Code (Text):

    if ((timer - secPrev) > 1000000) {
        secPrev = 0;
      }

     
     
  14. Jul 2, 2012 #13
    a long is 32 bits. a signed long would be around 2000000.
     
  15. Jul 2, 2012 #14

    Mark44

    Staff: Mentor

    You're off by a factor of 1000. A signed long would be more than 2,000,000,000. Are you sure that a long is 32 bits? That's a surprising jump from 8 bit integers.
     
  16. Jul 2, 2012 #15

    Mark44

    Staff: Mentor

    If you're using the Arduino programming language, their online documentation says that an int is 16 bits, not 8.

    Also, the millis() function returns an unsigned long, but your variable is declared as a long. What might be happening is that millis() is returning a value that is too large for a signed long, but not an unsigned long, and timer interprets that value as being negative. You should declare time as unsigned long so that it matches the return type of the millis() function.

    Their docs are here: http://arduino.cc/en/Reference/HomePage
     
  17. Jul 2, 2012 #16

    chiro

    User Avatar
    Science Advisor

    To build on other advice, try printing to some output device the actual value of the timer at each loop (i.e. the one given by millis()).

    If this is screwing up, has values that don't make sense, has a rollover aspect not what you are expecting, or even is contingent on some other behaviour that you haven't described (contingent on interrupt behaviour, calling specified functions, setting system variables, etc), then you will see this when it is printed out.

    If you can't do this on your board for some reason, see if you can pipe the output to another device.
     
  18. Jul 4, 2012 #17
    I haven't read all of the replies so I apologize if this has already been addressed...

    In general there are three ways to time things; using a hardware timer built into the processors event manager block, an external real time clock chip, or counting clocks... Counting clocks is the easiest, if you want a delay for 100ms and you're operating at 100mhz you know that each clock is 10ns so you use asm NOP instructions in a 10,000 count loop... of course this is blocking, meaning no other code can run while you are delaying. If you must run code on the same processor while you are keeping track of the time I'm afraid I know of no other solution than to use a hardware based timer. Many processors, DSP's, microcontrollers etc have built in hardware timers, and if they don't you can purchase a real time clock chip for pennies and interface with it using any standard bus (SPI, UART, I2C, etc...).
     
  19. Jul 4, 2012 #18

    Hurkyl

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    C standard guarantees that an int is at least 16 bits, and a long is at least 32 bits. Of course, any specific compiler could be can be non-conforming, but it's generally a safe bet to assume this much.


    Show the code. I think it's most likely you think you did the change right but did not.

    Among other things, you really mean 1000000u, not 1000000.


    The second most likely, I think, is that you are using threading but didn't think to mention it.


    EDIT: whoops, you aren't even programming in C/C++, but instead a language based on it. This opens up even more possibilities for integer conversions and promotions to behave in strange and confusing ways.

    And also for integer constants to behave badly. C guarantees that 1000000 would be interpreted as a signed long value (on systems where 1000000 would overflow an unsigned int), but I don't see any such guarantee in Arduino's documentation: in fact, it suggests that Arduino will interpret it as a signed int! So you need both the U and the L suffix to tell it you want an unsigned long value.
     
    Last edited: Jul 4, 2012
  20. Jul 5, 2012 #19
    here is the complete code for what i am doing

    Code (Text):

     int ledstate1=LOW;
     long seconds = 0;
     long *secPtr = &seconds;
     long minutes = 0;
     long hours = 0;
     long days = 0;
     long weeks = 0;
     long previous = 0;
     long interval = 4900;
     unsigned long secInt = 1000;
     unsigned long secPrev = 0;
     unsigned long num = 100000;
     
     void setup(){
       pinMode(6,OUTPUT);
       pinMode(10,OUTPUT);
       pinMode(11,OUTPUT);
       pinMode(12,OUTPUT);
       pinMode(13,OUTPUT);
       Serial.begin(9600);
     }

     void loop() {
       //Timing Mechanism

       
       unsigned long timer = millis();
       if ((timer - secPrev) > num)
         secPrev = 0;
         
       if ((timer - secPrev)>secInt) {
         *secPtr += 1;
         secPrev = timer;
       }
       Serial.println(seconds);
       if (seconds == 60) {
         minutes += 1;
         seconds = 0;
       }
       if (minutes == 60) {
         hours += 1;
         minutes = 0;
       }
       if (hours == 24){
         days += 1;
         hours = 0;
       }
       if (days == 7) {
         weeks += 1;
         days = 0;
       }
       
       int sensorValue = analogRead(A1);
       //light control
        long current = millis();
        if (current - previous > interval) {
            ledstate1 = HIGH;
           previous = current;
        }
        else
        ledstate1 = LOW;
         if (seconds%2!=0) {
           if (analogRead(A0) > 900)
             digitalWrite(13,ledstate1);
           else
             digitalWrite(13,LOW);
           if (analogRead(A0) < 900){
              if (analogRead(A2) < 900){
                seconds = 0;
                minutes = 0;
                hours = 0;
                days = 0;
                weeks = 0;
              }
              if (seconds > 30)
                digitalWrite(12,ledstate1);
              else
                digitalWrite(12,LOW);
              if(seconds <= 1)
                digitalWrite(6,ledstate1);
              else
                digitalWrite(6,LOW);
              if (seconds > 1 && seconds <= 10)
                digitalWrite(10, ledstate1);
              else
                digitalWrite(10,LOW);
              if (seconds > 10 && seconds <= 30)
                digitalWrite(11,ledstate1);
              else
                digitalWrite(11,LOW);
              }
           else{
              digitalWrite(6,LOW);
              digitalWrite(10,LOW);
              digitalWrite(11,LOW);
           }
         }
         if (seconds%2==0) {
              if (analogRead(A3) < 900){
                seconds = 0;
                minutes = 0;
                hours = 0;
                days = 0;
                weeks = 0;
              }
           if (analogRead(A1) > 900)
             digitalWrite(13, ledstate1);
           else
             digitalWrite(13,LOW);
                if (analogRead(A1) < 900){
              if (seconds > 30)
                digitalWrite(12,ledstate1);
              else
                digitalWrite(12,LOW);
              if(seconds <= 1)
                digitalWrite(6,ledstate1);
              else
                digitalWrite(6,LOW);
              if (seconds > 1 && seconds <= 10)
                digitalWrite(10, ledstate1);
              else
                digitalWrite(10,LOW);
              if (seconds > 10 && seconds <= 30)
                digitalWrite(11,ledstate1);
              else
                digitalWrite(11,LOW);
              }
           else{
              digitalWrite(6,LOW);
              digitalWrite(10,LOW);
              digitalWrite(11,LOW);
           }
         }
       //Analog input/output
       if (seconds % 2 == 0){
         pinMode(A0, OUTPUT);
         pinMode(A1,OUTPUT);
         pinMode(A2, OUTPUT);
         pinMode(A3,OUTPUT);
         digitalWrite(A0,HIGH);
         digitalWrite(A1,LOW);
         digitalWrite(A2,HIGH);
         digitalWrite(A3,LOW);    
       }
       else{
         pinMode(A0,OUTPUT);
         pinMode(A1,OUTPUT);
         pinMode(A2,OUTPUT);
         pinMode(A3,OUTPUT);
         digitalWrite(A0,LOW);
         digitalWrite(A1,HIGH);
         digitalWrite(A2,LOW);
         digitalWrite(A3,HIGH);
       }
     }
     
    the issue i am having occurs in the timing mechanism section. it happens just below that. everything else is pretty irrelevant.
     
  21. Jul 5, 2012 #20

    chiro

    User Avatar
    Science Advisor

    One thing that sticks out is this Serial.begin(9600).

    According to the arduino API reference (http://arduino.cc/en/Reference/SoftwareSerial), this sets the data rate for the board.

    Now if the data-rate is affected by every system call and you are flooding the system with such calls, then this is going to affect how the system functions and perhaps will affect you even being able to get anything useful for millis(), especially if the interrupt service routines are being affected by this 9600 parameter and the fact that you are flooding the system with calls to millis().

    It says that you can attach interrupts, and the best thing IMO that you can do is to attach your own custom interrupt to the interrupt vector for the timer mechanism and simply store the results in some specially allocated word which is accessed by another routine. In other words, you have a global word to store this clock data from your new interrupt service routine, and you have a function that just returns the contents of the word.

    This way, the ISR will activate every time a timer event has occured and instead of calling millis(), you just get a copy of the value used in the ISR.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook




Similar Discussions: Timing mechanism in C
  1. Time Delay in C++. (Replies: 18)

  2. C++ time (Replies: 4)

  3. C++ Time Delay (Replies: 4)

Loading...