Pages: [1] 2   Go Down
Author Topic: How to: calculate a period of time?  (Read 2044 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Sr. Member
****
Karma: 0
Posts: 423
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I would like to change a little bit the example of knocks with a piezoelectric to measure the number of knocks in a selected time (one minute, for example).

However, i don´t know how to write it for calculate a period of time. what should be the syntax?

Do you understand what i try to do?

Any idea? Thanks!!
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You could use millis()

eg:
Quote
int count = 0;

if(millis() < 60000){
if(button == HIGH)
  count++;
}

That would then count the number of presses/knocks in 60 seconds.

You could also do it many other ways, which i'm sure someone else will say in a moment, once they've criticised my code for some reason or another  smiley-razz

Mowcius


« Last Edit: November 10, 2009, 02:52:47 pm by mowcius » Logged

The Big Smoke
Offline Offline
Sr. Member
****
Karma: 0
Posts: 260
Hacking and Slashing
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well mowcius, you should know better. That's some hidious code. If I made a penny for each bug I spotted in that code I'd be a millionaire

smiley-wink


Yeah what Mow suggested is perfect for simple applications. If you require any major precision check out the timing ICs from Maxim (A quick search will uncover the IC I speak of)
Logged

The Big Smoke
Offline Offline
Sr. Member
****
Karma: 0
Posts: 260
Hacking and Slashing
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Just a thought, watch out for debounce and various other electronic phenomenon when you're catching "events".

(http://www.labbookpages.co.uk/electronics/debounce.html)
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Well mowcius, you should know better. That's some hidious code. If I made a penny for each bug I spotted in that code I'd be a millionaire

How many spaces did I do wrong then?  ;D

Well a few after thoughts, probably should have done:

Quote
void setup() {
  Serial.begin(9600);           //Start serial at 9600baud
  int count = 0;                   //Set count to 0
  delay(1000);                    //Wait 1 second
  
  while(millis() < 61000){     //While code has been running for less then 60 seconds:                
    if(button == HIGH){         //If button is high/knock etc then:
    count++;                         //Increase count by one
    }
  }
    Serial.println(count);         //Print count to serial
}                                         //End of code - after this, the code does nothing

void loop(){
                                           //Nothing to go here
}

Mowcius
« Last Edit: November 10, 2009, 03:06:18 pm by mowcius » Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 423
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks mowcius,

Your idea runs well. However, it only runs one time.

Thanks also to TeamMCS for your inputs and link.

My idea is to made a measurement in a period. Form example, every five minutes, to measure the number of knocks in 30 seconds; as well to know the measurement from other sensor, for example temperature, but in that case just only punctual measurement at the end of the 30 seconds.

Here is a pseudocode of what i want to do:

Quote
// knocks measurement

Declare the pins
      led pin
      knock pin
Declare the variables
      knockcounter

Procedure: knocks measurement
Reset the knockcounter to 0
Start a period of time (30 seconds, for example)
      if the pin read something higher than the therehold,
               increase 1 the knockscounter
               change the state of the led
at the end of the period of time, know the number of total knocks


prodecure: for example read the temperature with other sensor

void setup()
start the serial

void loop()
start the procedure countknocks
start the procedure read temperature
show the total number of knocks on serial
show the temperature on serial
delay for 5 minutes

and here is the code that i have right now. I only show the procedure related to knocks. The temperature or any other sensor is not a problem. However, it doesn´t work, because the serial even show knocks=0 and the led don´t change at all...

Code:
// these constants won't change:
const int ledPin = 13;      // led connected to digital pin 13
const int knockSensor = 0;  // the piezo is connected to analog pin 0
const int threshold = 1;  // threshold value to decide when the detected sound is a knock or not

// these variables will change:
int sensorReading = 0;      // variable to store the value read from the sensor pin
int ledState = LOW;         // variable used to store the last LED status, to toggle the light
unsigned long knockcount=0; // start the knock counter
unsigned long millis();

unsigned long readknocks(){
  sensorReading = analogRead(knockSensor);
  if (millis()<30000){      
    // if the sensor reading is greater than the threshold:
    if (sensorReading >= threshold) {
    // toggle the status of the ledPin:
      ledState = !ledState;
      knockcount=knockcount+1;  
    // update the LED pin itself:        
      digitalWrite(ledPin, ledState);
    // send the string "Knock!" back to the computer, followed by newline
    }
  }
  return knockcount;
}


void setup() {
 pinMode(ledPin, OUTPUT); // declare the ledPin as as OUTPUT
 Serial.begin(9600);       // use the serial port
}

void loop() {
  // read the sensor and store it in the variable sensorReading:
  readknocks();                      //start the measuring time
  Serial.print("Knocks: ");          // prepare to print the results
  Serial.println(knockcount,DEC);    // print the number of knocks
//in the future here i will also read a temperature sensor and show it
  knockcount=0;                      //reset the knocks counter
  delay(3000);   //in the future it will be 5 minutes or more...
 }

Any idea about how could i improve the code?
On the other hand, do you think that it is better to use the procedure used in the example "blinkwithoutdelay" included in arduino IDE?

Thanks for your ideas!!
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 547
Posts: 46003
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You have this in readKnocks:
Code:
if (millis()<30000)

That will work the first time. After the first 30000 seconds that the arduino has been powered up, millis() will never return a value less than 30000.

You need to store that value that millis returns at the start of readKnocks, and then check that millis returns a value less than that time + 30000.
Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 423
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi there PaulS,

Thanks!

So, i suppose that you mean something like that?

Code:
if (millis() - previousMillis > interval) {
     previousMillis = millis();  

This i from the example of blinkwithoutdelay example of arduino webpage
http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
« Last Edit: November 13, 2009, 07:46:53 pm by madepablo » Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 547
Posts: 46003
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Something like that, yes. Not exactly like that, though. More like this:

int readKnocks()
{
   int knockCount = 0;
   long startTime = millis();
   while(millis() < startTime + 30000)
   {
       // Was that a knock I heard?
   }
   return knockCount;
}
Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 423
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Great thanks PaulS,

I didn´t had on mind that here is possible to use the loops by the use of while and for.... Thanks to focu my attention to that.

I will change my code and show here the results...
Cheers,
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 176
Posts: 12285
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

This...
Code:
while(millis() - startTime < 30000)
...will work a bit better when millis approaches its maximum value.
« Last Edit: November 13, 2009, 08:54:28 pm by bcook » Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 423
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, now the code looks like this:
Code:
int readknocks() {
  sensorReading = analogRead(knockSensor);
  long startTime = millis();
  while(millis() < startTime + 30000) {
     if (sensorReading >= threshold) {
         ledState = !ledState;
         knockcount=knockcount+1;    
      digitalWrite(ledPin, ledState);
     }
    }
  return knockcount;
}

but i have some problems... it compiles, but it doesn´t run. I think that the problem is how i call the procedure:
Code:
void loop() {
  Serial.print("Knocks: ");          // prepare to print the results
  Serial.println(readknocks(),DEC);    // print the number of knocks
  knockcount=0;                      //reset the knocks counter
  delay(30000);                      // wait some time until to start to measure again the knocks
 }

I tryed by both methods:
1.-
Code:
Serial.println(readknocks(),DEC);

2.-
Code:
readknocks();
Serial.println(knockcount,DEC);
i don´t know what i am doing bad... :-[

@Coding Badly, thanks for your code, i just saw it, so i will try it too. However, i don´t know what you mean with
Quote
will work a bit better when millis approaches its maximum value.
and what is the difference with the code proposed by PaulS. Thanks for share your knowledge!
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 547
Posts: 46003
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
it compiles, but it doesn´t run.

That statement is pretty vague. Does loop not execute? Does readKnocks not get called? Does readKnocks always return 0?

In readKnocks, you only read the sensor value once. You need to move the analogRead statement inside the while loop.
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 176
Posts: 12285
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I don't have a clue how to describe the problem so I'll post an example.  Assume millis is nearing the point when it wraps from 0xFFFFFFFF to 0x00000000.  Something like this is what is executed...

Code:
 unsigned long startTime = 0xFFFFFFF0;  // millis();
  while(millis() < 0xFFFFFFF0 /*startTime*/ + 30000)

...which reduces to this...

Code:
 while(0xFFFFFFF0 < 0x00007520)

Oops, the condition is already false.  The while loop is never executed.


The code I posted works like this...

Code:
 unsigned long startTime = 0xFFFFFFF0;  // millis();
  while(0xFFFFFFF0 /*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000)

...which reduces to this...

Code:
 while(0 < 30000)

The first time it works.  29999 milliseconds later we have this...

Code:
 while(0x0000751F/*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000)

...which reduces to this...

Code:
 while(29999 < 30000)

Still good.  One millisecond later we have this...

Code:
 while(0x00007520/*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000)

...which reduces to this...

Code:
 while(30000 < 30000)

And the loop is finished.


Oh, and startTime needs to be declared "unsigned long" instead of "long".
« Last Edit: November 13, 2009, 09:52:39 pm by bcook » Logged

Waterloo, Canada
Offline Offline
Full Member
***
Karma: 1
Posts: 242
Engineer
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

It requires more advanced knowledge of the ATmega168, but for my virgin post I'll propose an alternate method using timer 1 input capture and overflow interrupts.

untested...
Code:
volatile unsigned int knocks;
volatile boolean myState;

ISR(TIMER1_CAPT_vect) { // TIMER1 Input Capture Event, rising edge on PortB0
  knocks++;
  myState != myState;
  digitalWrite(myPin, myState);
};

ISR(TIMER1_OVF_vect) { // Timer1 Overflow
  static unsigned int overflow_counter = 0;
  overflow_counter++;
  
  // timer1 overflows at T1 = 65536
  // T1 increments at 1/8th of system clock (confirm this)
  // 10000 T1 overflows ~= 5 minutes
  if (overflow_counter == 10000) {  // report and reset every 5 minutes
    serial.print("Found ");
    serial.print(knocks, DEC);
    serial.println(" knocks.");
    knocks = 0;
    overflow_counter = 0;
  }
}

void setup() {
  mySerial.begin(115200);
  myState = false;
  TCCR1B = B11000010;  // Input Capture Noise Canceler = ON, Input Capture Edge Select = RISING, Prescaler = clk/8
  TIMSK1 |= B00100001;  // turn on T1 Input Capture and Overflow Interrupts
}

void loop() {
  // do laundry
}

I'm most familiar with ATmega1280 (ArduinoMega) so I'm not sure if PortB0 is pinned out on the 168, nor am I sure if Timer1 is used for anything else at a prescaler other than 1/8.

[Edit: it also presumes that you connect your piezo sensor to the PortB0 digital input instead of analog in... should be easy with circuit design, or this can be modified to use the ADC_vect interrupt when analog conversion is complete.]

[Edit Nov. 14: revised to capture good suggestions from Coding Badly]
« Last Edit: November 14, 2009, 12:15:50 pm by mitch_79 » Logged

Pages: [1] 2   Go Up
Jump to: