Go Down

### Topic: Mathematical question (Read 2301 times)previous topic - next topic

#### Eibert_Draisma

##### Apr 29, 2012, 04:18 pm
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?

#### el_supremo

#1
##### Apr 29, 2012, 04:21 pm
delay(n*n/10);

Pete
Don't send me technical questions via Private Message.

#### Eibert_Draisma

#2
##### Apr 29, 2012, 04:36 pm
Hi Pete,

You are right; thank you so much!

BR Eibert.

#### robtillaart

#3
##### Apr 29, 2012, 05:16 pm

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

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### el_supremo

#4
##### Apr 29, 2012, 06:31 pm
Quote
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):
Code: [Select]
`delay( ((long)n)*n/10);`

Pete
Don't send me technical questions via Private Message.

#### robtillaart

#5
##### Apr 29, 2012, 07:41 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)
Code: [Select]
`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}`

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### Morris Dovey

#6
##### Apr 29, 2012, 08:17 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.
There's always a better way!

#### robtillaart

#7
##### Apr 29, 2012, 08:52 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 ..

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### Eibert_Draisma

#8
##### May 11, 2012, 07:17 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?!

Code: [Select]
`/*  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 2int  ledPin = 13;  // Note: the solenoid is also connected to pin 13int 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;}`

#### robtillaart

#9
##### May 11, 2012, 07:52 pm

100 * 100 / 10 = 10000 /10 = 1000 ?

what datatype is N in your code?
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### Eibert_Draisma

#10
##### May 12, 2012, 06:00 pm
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.

#### PeterH

#11
##### May 12, 2012, 07:53 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.
I only provide help via the forum - please do not contact me for private consultancy.

#### robtillaart

#12
##### May 12, 2012, 07:59 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
Code: [Select]
`/*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 2int  ledPin = 13;  // Note: the solenoid is also connected to pin 13int  limitCm = 200; // unsigned long randOffSHORT = 0;unsigned long randOffLONG  = 0;unsigned long duration, distanceInches, distanceCm;#define NEAR 0#define FAR  1int 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;}`
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### Eibert_Draisma

#13
##### May 14, 2012, 03:23 pmLast Edit: May 14, 2012, 04:57 pm by Eibert_Draisma Reason: 1
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.

Code: [Select]
`/*  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 2int  ledPin = 13;  // Note: the solenoid is also connected to pin 13int 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 LEDlong previousMillis = 0;        // will store last time LED was updatedlong interval = 0;              // will store the last intervalvoid blinkSequence(){int blinkNumber = random(1, 11);        // Define how many random blinks in a sequence, in this case 1 to 10for (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;}`

Go Up

Please enter a valid email to subscribe