Go Down

### Topic: How to: calculate a period of time? (Read 6011 times)previous topic - next topic

##### Nov 10, 2009, 06:15 pm
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!!

#### mowcius

#1
##### Nov 10, 2009, 06:40 pmLast Edit: Nov 10, 2009, 08:52 pm by mowcius Reason: 1
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

Mowcius

#### TeamMCS

#2
##### Nov 10, 2009, 08:36 pm
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

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)

#### TeamMCS

#3
##### Nov 10, 2009, 08:37 pm
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)

#### mowcius

#4
##### Nov 10, 2009, 08:52 pmLast Edit: Nov 10, 2009, 09:06 pm by mowcius Reason: 1
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?

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

#5
##### Nov 14, 2009, 01:30 am
Thanks mowcius,

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

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
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: [Select]
`// these constants won't change:const int ledPin = 13;      // led connected to digital pin 13const int knockSensor = 0;  // the piezo is connected to analog pin 0const 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 pinint ledState = LOW;         // variable used to store the last LED status, to toggle the lightunsigned long knockcount=0; // start the knock counterunsigned 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?

#### PaulS

#6
##### Nov 14, 2009, 01:42 am
Code: [Select]
`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.

#7
##### Nov 14, 2009, 01:45 amLast Edit: Nov 14, 2009, 01:46 am by madepablo Reason: 1
Hi there PaulS,

Thanks!

So, i suppose that you mean something like that?

Code: [Select]
` if (millis() - previousMillis > interval) {     previousMillis = millis();   `

This i from the example of blinkwithoutdelay example of arduino webpage

#### PaulS

#8
##### Nov 14, 2009, 02:07 am
Something like that, yes. Not exactly like that, though. More like this:

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

#9
##### Nov 14, 2009, 02:10 am
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,

#10
##### Nov 14, 2009, 02:53 amLast Edit: Nov 14, 2009, 02:54 am by bcook Reason: 1
This...
Code: [Select]
`while(millis() - startTime < 30000)`
...will work a bit better when millis approaches its maximum value.

#11
##### Nov 14, 2009, 03:11 am
Ok, now the code looks like this:
Code: [Select]
`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: [Select]
`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: [Select]
`Serial.println(readknocks(),DEC);`
2.-
Code: [Select]
`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!

#### PaulS

#12
##### Nov 14, 2009, 03:18 am
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.

#13
##### Nov 14, 2009, 03:27 amLast Edit: Nov 14, 2009, 03:52 am by bcook Reason: 1
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: [Select]
`  unsigned long startTime = 0xFFFFFFF0;  // millis();  while(millis() < 0xFFFFFFF0 /*startTime*/ + 30000)`

...which reduces to this...

Code: [Select]
`  while(0xFFFFFFF0 < 0x00007520)`

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

The code I posted works like this...

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

...which reduces to this...

Code: [Select]
`  while(0 < 30000)`

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

Code: [Select]
`  while(0x0000751F/*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000)`

...which reduces to this...

Code: [Select]
`  while(29999 < 30000)`

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

Code: [Select]
`  while(0x00007520/*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000)`

...which reduces to this...

Code: [Select]
`  while(30000 < 30000)`

And the loop is finished.

Oh, and startTime needs to be declared "unsigned long" instead of "long".

#### Mitch_CA

#14
##### Nov 14, 2009, 05:43 amLast Edit: Nov 14, 2009, 06:15 pm by mitch_79 Reason: 1
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: [Select]
`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]

Go Up

Please enter a valid email to subscribe