Amsterdam
Offline
Newbie
Karma: 0
Posts: 18
|
 |
« on: April 29, 2012, 09:18:16 am » |
Hi all,
I would like to use a specific delay formula, but don't know how to write it. This function: delay(distanceCm * 10) I would like to replace by the function below: delay ((N/100) raised to power 2) * 1000
So: Devide N by 100, than raise the result to power 2 than multiply this by 1000
Does anyoine of you know how to do this? Thanks in advance, Eibert
|
|
|
|
|
Logged
|
|
|
|
|
Offline
God Member
Karma: 14
Posts: 912
|
 |
« Reply #1 on: April 29, 2012, 09:21:55 am » |
delay(n*n/10);
Pete
|
|
|
|
|
Logged
|
|
|
|
|
Amsterdam
Offline
Newbie
Karma: 0
Posts: 18
|
 |
« Reply #2 on: April 29, 2012, 09:36:39 am » |
Hi Pete,
You are right; thank you so much!
BR Eibert.
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 91
Posts: 9441
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #3 on: April 29, 2012, 10:16:33 am » |
be carefull!
what is the datatype and range of n? if n is an integer overflow can happen quite fast
delay (n*n/10) overflows at n=256
delay(n*(n/10)); is robuster against overflow. at n=810
|
|
|
|
|
Logged
|
|
|
|
|
Offline
God Member
Karma: 14
Posts: 912
|
 |
« Reply #4 on: April 29, 2012, 11:31:30 am » |
delay(n*(n/10)); is robuster against overflow but for values of n less than 10 it will "underflow". E.g. n=9 will give delay(0); Since delay takes a long integer, the best thing to do is force the calculation (assuming that n is an integer): delay( ((long)n)*n/10); Pete
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 91
Posts: 9441
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #5 on: April 29, 2012, 12:41:36 pm » |
ha ha, funny - I removed the cast to not overcomplicate it - should be unsigned long BTW  you're right I didn't count in underflow. My formula gives also some staircasing effects. (just interested where this simple function would end) so two issues - overflow and underflow we want to protect for , maybe we even need rounding ? I'll try to capture it all in the most optimized function preventing overflows (as 10 = 2*5 there are different ways to do the division to minimize the error) in theory: (numbers not double checked)
myDelay(unsigned long n) { if (n < 65536) delay((n*n)/10); // delay((n*n+5)/10); if you want to add rounding ; or maybe even +9 ? else if (n < 92681) delay(((n/2)*n)/5); else if (n < 146544) delay(((n/5)*n)/2); else if (n < 201245) delay((n/2)*(n/5)); else if (n < 207247) delay((n/10)*n); }
in practice:
myDelay(unsigned long n) { if (n < 65536) delay((n*n)/10); // small values of n else delay((n/10)*n); // large values of n }
|
|
|
|
|
Logged
|
|
|
|
|
West Des Moines, Iowa USA
Offline
Sr. Member
Karma: 2
Posts: 429
|
 |
« Reply #6 on: April 29, 2012, 01:17:40 pm » |
Rob, it seems to me that division may reduce the number of significant bits - in which case there is an accuracy advantage to not doing division until after the multiplication.
If the division were done first, then bits would be lost - and a subsequent multiplication would magnify the error introduced by discarding those bits.
I suggest that (((unsigned long)n * n) / 10) might be a good solution.
|
|
|
|
|
Logged
|
There's always a better way!
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 91
Posts: 9441
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #7 on: April 29, 2012, 01:52:30 pm » |
very true, integer division does truncate so the trick should be to keep the truncating minimal, thats why i did it in 2 steps in the above theoretical exercise.
however for large values of n the rounding error is far less than the inaccuracy of the build in clock I think ..
|
|
|
|
|
Logged
|
|
|
|
|
Amsterdam
Offline
Newbie
Karma: 0
Posts: 18
|
 |
« Reply #8 on: May 11, 2012, 12:17:27 pm » |
Wow, thanks for all the interesting comments! Actually I was happy with the first answer, by Pete (el_supremo), and in the end the formula I used was: delay(distanceCm*distanceCm/20) This is works really fine; I haven't encountered any problems -with this formula...- so far. But in the mean time, I have been struggeling a lot with the sketch I was working on: it did not work properly because of something else... The problem is (see sketch below): during the randOffLONG period, the Ping))) sensor is not working as "everything is paused" due to using relay(). Therefor, I wanted to replace function delay(randOffLONG); by something that is based upon millis. But I can't find the right way to do this! Note: All other pauses that use relay() are too short to cause any problems, so I only need to find a solution for the randOffLONG period. I hope someone of you can help me out?! /* As long as the Ping))) sensor doesn't detect anything at a distance closer than 200cm: - a solenoid is randomly triggered for 1 to 10 times (easy snap sequence); - when triggered, the solenoid stays powered for 0,04 seconds, than is turned off for a SHORT period, randomly between 0,05 to 1 second until the next trigger pulse - after the last pulse of a sequence, the solenoid is turned off for a LONG period, randomly between 15 minutes (900 sec) and 60 minutes (3600 sec) As soon as the Ping))) sensor detects an object at a distance of 200cm or closer: - the trigger rate of the solenoid is changed depending on detection distance (the shorter the distance, the higher the trigger frequency) - the minimum time needed to pull the solenoid core completely into its coil is 40 (0,04 seconds) Note: solenoid is connected to LedPin 13. Ping))) instructions are based upon http://www.arduino.cc/en/Tutorial/Ping (Sketch created by David A. Mellis 3 Nov 2008, modified 30 Aug 2011 by Tom Igoe) */ int pingPin = 2; // Connect SIG from the Ping))) sensor to Arduino's Digital pin 2 int ledPin = 13; // Note: the solenoid is also connected to pin 13 int limitCm = 200; // long randOffSHORT = 0; long randOffLONG = 0; long int duration, distanceInches, distanceCm;
void setup() { Serial.begin(9600); pinMode(ledPin, OUTPUT); // initialize the digital pin as an output. }
void loop() { pinMode(pingPin, OUTPUT); // initialize the pingPin as an OUTPUT. digitalWrite(pingPin, LOW); delayMicroseconds(2); // a short LOW pulse is given beforehand, to ensure a clean HIGH pulse: digitalWrite(pingPin, HIGH); delayMicroseconds(5); // the PING))) is triggered by a HIGH pulse of 5 microseconds. digitalWrite(pingPin, LOW); pinMode(pingPin, INPUT); // initialize the pingPin as an INPUT. duration = pulseIn(pingPin, HIGH);
distanceInches = microsecondsToInches(duration); distanceCm = microsecondsToCentimeters(duration);
checkLimit(); delay(100); }
void checkLimit() { if (distanceCm > limitCm){ // randomised 'easy snap sequence' is activated randOffLONG = random(900000, 3600000); int blinkNumber = random(1, 11); for (int a = 0; a < blinkNumber; a ++) { randOffSHORT = random(50, 1000); // Define length of random Off period, in this case between 0,05 and 1 second digitalWrite(ledPin, HIGH); // when ledPin = HIGH, solenoid is activated delay(40); // 40 millisec is minimum time needed, to pull solenoid core completely into its coil digitalWrite(ledPin, LOW); // turn off LED, un-trigger Solenoid delay(randOffSHORT); // wait for short random period } delay(randOffLONG); // wait for long random period: DURING THIS PERIOD THE PING SENSOR DOES NOT RESPOND.... } else // start of snap frequency that is depending on detection distance { Serial.println(distanceCm); // "increase blinking rate depending on detection distance" digitalWrite(ledPin, HIGH); // when ledPin = HIGH, solenoid is activated delay(40); // 40 millisec is minimum time necessary to pull core completely into coil digitalWrite(ledPin, LOW); // turn off LED, un-trigger Solenoid delay(40); // safety delay, as 40 millisec is the shortest possible time between triggers delay(distanceCm*distanceCm/20); // extra delay; duration will decrease quickly when approaching sensor } }
long microsecondsToInches(long microseconds) { return microseconds / 74 / 2; }
long microsecondsToCentimeters(long microseconds) { return microseconds / 29 / 2; }
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 91
Posts: 9441
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #9 on: May 11, 2012, 12:52:21 pm » |
100 * 100 / 10 = 10000 /10 = 1000 ?
what datatype is N in your code?
|
|
|
|
|
Logged
|
|
|
|
|
Amsterdam
Offline
Newbie
Karma: 0
Posts: 18
|
 |
« Reply #10 on: May 12, 2012, 11:00:01 am » |
Hi Rob,
I did not use N in my code (see my post below, dated May 11), I just used it in my first post to illustrate what I was trying to do.
I wanted the blinking speed of the Led (with a solenoid connected to the ledPin) to increase quickly, when someone approaches the sensor. To achieve this, the delay (related to the distance) had to decrease quickly when someone approaches the sensor. This code does the trick: delay(distanceCm*distanceCm/20); // extra delay; duration will decrease quickly when approaching sensor
Eibert.
|
|
|
|
|
Logged
|
|
|
|
|
UK
Offline
Tesla Member
Karma: 89
Posts: 6400
-
|
 |
« Reply #11 on: May 12, 2012, 12:53:00 pm » |
Therefor, I wanted to replace function delay(randOffLONG); by something that is based upon millis. But I can't find the right way to do this! Note: All other pauses that use relay() are too short to cause any problems, so I only need to find a solution for the randOffLONG period.
I hope someone of you can help me out?!
You're right to want to get away from using delay(). It's a very common problem, and the solution is demonstrated in the 'blink without delay' example.
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 91
Posts: 9441
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #12 on: May 12, 2012, 12:59:57 pm » |
I refactored the code a bit, you will recognize snippets of your code in it - remodeled the functions() - removed the long delay (other delays are less worrysome) - introduced a minimal state-machine that can differentiate between NEAR and FAR and remembers its previous state Read the code of loop() carefully (use paper and pencil and 'be the arduino' ) as is is mainly the statemachine code in there. YOu will see that only when the state changes to FAR a random pattern is played only once and then it is "dark" until the distance is lower than limit again. (Code compiles, but not tested - no ping nearby  /* As long as the Ping))) sensor doesn't detect anything at a distance closer than 200cm: - a solenoid is randomly triggered for 1 to 10 times (easy snap sequence); - when triggered, the solenoid stays powered for 0,04 seconds, than is turned off for a SHORT period, randomly between 0,05 to 1 second until the next trigger pulse - after the last pulse of a sequence, the solenoid is turned off for a LONG period, randomly between 15 minutes (900 sec) and 60 minutes (3600 sec) As soon as the Ping))) sensor detects an object at a distance of 200cm or closer: - the trigger rate of the solenoid is changed depending on detection distance (the shorter the distance, the higher the trigger frequency) - the minimum time needed to pull the solenoid core completely into its coil is 40 (0,04 seconds) Note: solenoid is connected to LedPin 13. Ping))) instructions are based upon http://www.arduino.cc/en/Tutorial/Ping (Sketch created by David A. Mellis 3 Nov 2008, modified 30 Aug 2011 by Tom Igoe) */
int pingPin = 2; // Connect SIG from the Ping))) sensor to Arduino's Digital pin 2 int ledPin = 13; // Note: the solenoid is also connected to pin 13 int limitCm = 200; // unsigned long randOffSHORT = 0; unsigned long randOffLONG = 0; unsigned long duration, distanceInches, distanceCm;
#define NEAR 0 #define FAR 1 int prevState = NEAR; int state = NEAR;
void setup() { Serial.begin(9600); pinMode(ledPin, OUTPUT); // initialize the digital pin as an output. }
void loop() { readDistance();
if (distanceCm > limitCm) { state = FAR; } else { state = NEAR; }
if (state == FAR && prevState == NEAR) { blinkRandom(); } if (state == NEAR) { Serial.println(distanceCm); blinkOnce(); delay(distanceCm*distanceCm/20); } prevState = state; }
void blinkOnce() { digitalWrite(ledPin, HIGH); // when ledPin = HIGH, solenoid is activated delay(40); // 40 millisec is minimum time necessary to pull core completely into coil digitalWrite(ledPin, LOW); // turn off LED, un-trigger Solenoid delay(40); // safety delay, as 40 millisec is the shortest possible time between triggers }
void blinkRandom() { int blinkNumber = random(1, 11);
for (int a = 0; a < blinkNumber; a ++) { blinkOnce(); randOffSHORT = random(50, 1000); delay(randOffSHORT); } }
void readDistance() { pinMode(pingPin, OUTPUT); // initialize the pingPin as an OUTPUT. digitalWrite(pingPin, LOW); delayMicroseconds(2); // a short LOW pulse is given beforehand, to ensure a clean HIGH pulse: digitalWrite(pingPin, HIGH); delayMicroseconds(5); // the PING))) is triggered by a HIGH pulse of 5 microseconds. digitalWrite(pingPin, LOW); pinMode(pingPin, INPUT); // initialize the pingPin as an INPUT. duration = pulseIn(pingPin, HIGH); distanceInches = microsecondsToInches(duration); distanceCm = microsecondsToCentimeters(duration); }
long microsecondsToInches(long microseconds) { return microseconds / 74 / 2; }
long microsecondsToCentimeters(long microseconds) { return microseconds / 29 / 2; }
|
|
|
|
|
Logged
|
|
|
|
|
Amsterdam
Offline
Newbie
Karma: 0
Posts: 18
|
 |
« Reply #13 on: May 14, 2012, 08:23:10 am » |
Ok, I used the BlinkWithoutDelay example to find a way to get rid of the 'long delay problem': the sketch seems to work fine now. Rob, thank you for refactoring the code: it looks nicely structured now. Will insert the new randOffLONG-interval function in it again. /* As long as the Ping))) sensor doesn't detect anything at a distance closer than 200cm: - a solenoid is randomly triggered for 1 to 10 times (easy snap sequence); - when triggered, the solenoid stays powered for 0,04 seconds, than is turned off for a SHORT period, randomly between 0,05 to 1 second until the next trigger pulse - after the last pulse of a sequence, the solenoid is turned off for a LONG period, randomly between 15 minutes (900 sec) and 60 minutes (3600 sec) As soon as the Ping))) sensor detects an object at a distance of 200cm or closer: - the trigger rate of the solenoid is changed depending on detection distance (the shorter the distance, the higher the trigger frequency) - the minimum time needed to pull the solenoid core completely into its coil is 40 (0,04 seconds) Note: solenoid is connected to LedPin 13. Ping))) instructions are based upon http://www.arduino.cc/en/Tutorial/Ping (Sketch created by David A. Mellis 3 Nov 2008, modified 30 Aug 2011 by Tom Igoe) */
// Constants won't change: int pingPin = 2; // Connect SIG from the Ping))) sensor to Arduino's Digital pin 2 int ledPin = 13; // Note: the solenoid is also connected to pin 13 int limitCm = 200; // Store the limit distance
// Variables will change: long randOffSHORT = 0; // long randOffLONG = 0; long int duration, distanceCm; int ledState = LOW; // ledState used to set the LED long previousMillis = 0; // will store last time LED was updated long interval = 0; // will store the last interval
void blinkSequence() { int blinkNumber = random(1, 11); // Define how many random blinks in a sequence, in this case 1 to 10 for (int a = 0; a < blinkNumber; a ++) { randOffSHORT = random(50, 1000); // Define length of SHORT random Off period, in this case between 0,05 and 1 second digitalWrite(ledPin, HIGH); // when ledPin = HIGH, solenoid is activated delay(40); // 40 millisec is minimum time needed, to pull solenoid core completely into its coil digitalWrite(ledPin, LOW); // turn off LED, un-trigger Solenoid delay(randOffSHORT); // wait for short random period }}
void setup() { Serial.begin(9600); pinMode(ledPin, OUTPUT); // initialize the digital pin as an output. }
void loop() { pinMode(pingPin, OUTPUT); // initialize the pingPin as an OUTPUT. digitalWrite(pingPin, LOW); delayMicroseconds(2); // a short LOW pulse is given beforehand, to ensure a clean HIGH pulse: digitalWrite(pingPin, HIGH); delayMicroseconds(5); // the PING))) is triggered by a HIGH pulse of 5 microseconds. digitalWrite(pingPin, LOW); pinMode(pingPin, INPUT); // initialize the pingPin as an INPUT. duration = pulseIn(pingPin, HIGH);
distanceCm = microsecondsToCentimeters(duration);
checkLimit(); delay(100); }
void checkLimit() { if (distanceCm > limitCm){ // randomised 'easy snap sequence' is activated interval = random(900000, 3600000); // Define length of LONG random Off period, in this case between 15 (900 sec) and 60 minutes (3600 sec) unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval){ // save the last time you blinked the LED previousMillis = currentMillis; blinkSequence(); }} else // start of snap frequency that is depending on detection distance { Serial.println(distanceCm); // "increase blinking rate depending on detection distance" digitalWrite(ledPin, HIGH); // when ledPin = HIGH, solenoid is activated delay(40); // 40 millisec is minimum time necessary to pull core completely into coil digitalWrite(ledPin, LOW); // turn off LED, un-trigger Solenoid delay(40); // safety delay, as 40 millisec is the shortest possible time between triggers delay(distanceCm*distanceCm/20); // extra delay; duration will decrease quickly when approaching sensor } } long microsecondsToCentimeters(long microseconds) { return microseconds / 29 / 2; }
|
|
|
|
« Last Edit: May 14, 2012, 09:57:59 am by Eibert_Draisma »
|
Logged
|
|
|
|
|
|