Pages: [1]   Go Down
Author Topic: Mathematical question  (Read 1293 times)
0 Members and 1 Guest are viewing this topic.
Amsterdam
Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 Offline
Edison Member
*
Karma: 48
Posts: 1616
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

delay(n*n/10);

Pete
Logged

Where are the Nick Gammons of yesteryear?

Amsterdam
Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Pete,

You are right; thank you so much!

BR Eibert.
Logged

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 216
Posts: 13673
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

Rob Tillaart

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

Offline Offline
Edison Member
*
Karma: 48
Posts: 1616
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
delay( ((long)n)*n/10);

Pete
Logged

Where are the Nick Gammons of yesteryear?

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 216
Posts: 13673
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ha ha, funny - I removed the cast to not overcomplicate it -  should be unsigned long BTW smiley-wink

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:
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

Rob Tillaart

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

West Des Moines, Iowa USA
Offline Offline
Sr. Member
****
Karma: 2
Posts: 428
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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!

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 216
Posts: 13673
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

Rob Tillaart

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

Amsterdam
Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
/*
  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

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 216
Posts: 13673
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

what datatype is N in your code?
Logged

Rob Tillaart

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

Amsterdam
Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

I only provide help via the forum - please do not contact me for private consultancy.

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 216
Posts: 13673
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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 smiley
Code:
/*
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

Rob Tillaart

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

Amsterdam
Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
/*
  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

Pages: [1]   Go Up
Jump to: