Offline
Sr. Member
Karma: 0
Posts: 379
Arduino rocks
|
 |
« on: November 10, 2009, 12:15:14 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!!
|
|
|
|
|
Logged
|
|
|
|
|
North Yorkshire, UK
Offline
Faraday Member
Karma: 104
Posts: 5531
|
 |
« Reply #1 on: November 10, 2009, 12:40:28 pm » |
You could use millis() eg: 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
|
|
|
|
« Last Edit: November 10, 2009, 02:52:47 pm by mowcius »
|
Logged
|
|
|
|
|
The Big Smoke
Offline
Sr. Member
Karma: 0
Posts: 259
Hacking and Slashing
|
 |
« Reply #2 on: November 10, 2009, 02:36:26 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)
|
|
|
|
|
Logged
|
|
|
|
|
The Big Smoke
Offline
Sr. Member
Karma: 0
Posts: 259
Hacking and Slashing
|
 |
« Reply #3 on: November 10, 2009, 02:37:53 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)
|
|
|
|
|
Logged
|
|
|
|
|
North Yorkshire, UK
Offline
Faraday Member
Karma: 104
Posts: 5531
|
 |
« Reply #4 on: November 10, 2009, 02:52:19 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 How many spaces did I do wrong then? ;D Well a few after thoughts, probably should have done: 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
Sr. Member
Karma: 0
Posts: 379
Arduino rocks
|
 |
« Reply #5 on: November 13, 2009, 07:30:58 pm » |
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: // 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... // 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
Brattain Member
Karma: 334
Posts: 36453
Seattle, WA USA
|
 |
« Reply #6 on: November 13, 2009, 07:42:13 pm » |
You have this in readKnocks: 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
Sr. Member
Karma: 0
Posts: 379
Arduino rocks
|
 |
« Reply #7 on: November 13, 2009, 07:45:56 pm » |
Hi there PaulS, Thanks! So, i suppose that you mean something like that? 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
Brattain Member
Karma: 334
Posts: 36453
Seattle, WA USA
|
 |
« Reply #8 on: November 13, 2009, 08:07:25 pm » |
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
Sr. Member
Karma: 0
Posts: 379
Arduino rocks
|
 |
« Reply #9 on: November 13, 2009, 08:10:40 pm » |
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
Shannon Member
Karma: 129
Posts: 10379
|
 |
« Reply #10 on: November 13, 2009, 08:53:19 pm » |
This... 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
Sr. Member
Karma: 0
Posts: 379
Arduino rocks
|
 |
« Reply #11 on: November 13, 2009, 09:11:36 pm » |
Ok, now the code looks like this: 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: 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.- Serial.println(readknocks(),DEC); 2.- 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 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
Brattain Member
Karma: 334
Posts: 36453
Seattle, WA USA
|
 |
« Reply #12 on: November 13, 2009, 09:18:54 pm » |
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
Shannon Member
Karma: 129
Posts: 10379
|
 |
« Reply #13 on: November 13, 2009, 09:27:30 pm » |
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... unsigned long startTime = 0xFFFFFFF0; // millis(); while(millis() < 0xFFFFFFF0 /*startTime*/ + 30000) ...which reduces to this... while(0xFFFFFFF0 < 0x00007520) Oops, the condition is already false. The while loop is never executed. The code I posted works like this... unsigned long startTime = 0xFFFFFFF0; // millis(); while(0xFFFFFFF0 /*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000) ...which reduces to this... while(0 < 30000) The first time it works. 29999 milliseconds later we have this... while(0x0000751F/*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000) ...which reduces to this... while(29999 < 30000) Still good. One millisecond later we have this... while(0x00007520/*millis()*/ - 0xFFFFFFF0 /*startTime*/ < 30000) ...which reduces to this... 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
Full Member
Karma: 1
Posts: 242
Engineer
|
 |
« Reply #14 on: November 13, 2009, 11:43:19 pm » |
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... 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
|
|
|
|
|
|