Solving Timing Mechanism for C & Arduino Water Sensor

  • Thread starter Thread starter thedude36
  • Start date Start date
  • Tags Tags
    Mechanism
Click For Summary

Discussion Overview

The discussion revolves around troubleshooting a timing mechanism for an Arduino-based water sensor. Participants are exploring how to increment a seconds counter without using the delay() function, which would interfere with other sensor operations. The focus is on understanding the behavior of the code and identifying why the seconds variable is not incrementing as expected.

Discussion Character

  • Technical explanation
  • Debate/contested
  • Mathematical reasoning

Main Points Raised

  • One participant describes their code and the issue of the seconds variable not increasing, despite the secPrev variable functioning correctly.
  • Another participant suggests that if seconds isn't incrementing, it may be due to the condition timer - secPrev <= secInt, and recommends using debugging techniques to identify the issue.
  • There is a discussion about the use of pointers, with one participant expressing uncertainty about their implementation and whether they are necessary in this context.
  • Concerns are raised about the potential for the millis() function to roll over, which could affect the calculations involving timer and secPrev.
  • Participants discuss the data types used, questioning whether timer and secPrev should be declared as unsigned long instead of long to avoid negative values.
  • One participant notes that when testing with a constant condition, the seconds variable increments correctly, indicating that the issue lies within the conditional logic of the original code.
  • There is a query about the size of data types on the system, with participants speculating on the implications of using 8-bit integers and 16-bit longs.

Areas of Agreement / Disagreement

Participants express uncertainty and propose various hypotheses regarding the code's behavior. There is no consensus on the exact cause of the issue, and multiple competing views on potential solutions and debugging strategies remain present.

Contextual Notes

Limitations include the lack of a debugger and uncertainty regarding the behavior of the millis() function and the data types used in the code. The discussion also highlights the potential for variable scope issues, although participants generally agree that the variables are declared globally.

thedude36
Messages
30
Reaction score
0
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 can't use a delay() mechanism as it would mess with other functions of the sensor. I had thought i found a solution but i can't 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, I am just not sure why the seconds arent. would anyone know why this is?
 
Technology news on Phys.org
If you post code, put it between [noparse]
Code:
 and
[/noparse] tags. I did this for you, below.
thedude36 said:
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 can't use a delay() mechanism as it would mess with other functions of the sensor. I had thought i found a solution but i can't get it to work. what i came up with was
Code:
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, I am just not sure why the seconds arent. would anyone know why this is?

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:
Mark44 said:
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.

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:
 long *secPtr = &seconds;

but it had given me the same result.
 
Last edited by a moderator:
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.
 
Mark44 said:
Are you using pointers in the code you're looking at? The code you posted doesn't use pointers at all.

No, though i did try pointers. that didnt seem to help any. with the pointers, the code looked like

Code:
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 that's how you declare global variables, i could be wrong.
 
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?
 
AlephZero said:
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?


yes, they should be unsigned, but with that change it still doesn't 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.
 
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:
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;
  }
}
 
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.
 
  • #10
Mark44 said:
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]

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:
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.
 
  • #11
Mark44 said:
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.

it is 8 bit
 
  • #12
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:
if ((timer - secPrev) > 1000000) {
    secPrev = 0;
  }
 
  • #13
Mark44 said:
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:
if ((timer - secPrev) > 1000000) {
    secPrev = 0;
  }

a long is 32 bits. a signed long would be around 2000000.
 
  • #14
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.
 
  • #15
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
 
  • #16
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.
 
  • #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...).
 
  • #18
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.
yes, they should be unsigned, but with that change it still doesn't allow seconds to increase any. the test (timer - secprev)> 1000000 is to compensate for the rollover.
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:
  • #19
here is the complete code for what i am doing

Code:
 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.
 
  • #20
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 occurred and instead of calling millis(), you just get a copy of the value used in the ISR.
 

Similar threads

  • · Replies 2 ·
Replies
2
Views
3K
  • · Replies 2 ·
Replies
2
Views
3K
  • · Replies 14 ·
Replies
14
Views
2K
  • · Replies 13 ·
Replies
13
Views
2K
  • · Replies 1 ·
Replies
1
Views
6K
  • · Replies 9 ·
Replies
9
Views
2K
  • · Replies 1 ·
Replies
1
Views
3K
Replies
17
Views
5K
  • · Replies 22 ·
Replies
22
Views
4K
  • · Replies 7 ·
Replies
7
Views
18K