Algebra nightmare - breathing aid for my son (not sick or dying just sleepless)

Hey all. I'm dead rotten at Algebra. Dyxlexia: It's wilding like spreadfire.

My 8 year old son is an insomniac. Hours of playing, reading, games etc will just NOT help him sleep. I've tried just about everything I can to exhaust him before bed, but to no avail. On that note - while advice on how to get the little one to sleep is more than welcome, if possible, an answer to my math related question would still be really great because he knows about this project and wants to try it!

I've created a lamp for my son's nightstand that is a 1w blue HB led driven with a lovely constant current driver / PWM that I had laying about. I'm using analogWrite() to the driver's PWM pin to dim and illuminate the LED. When the LED is getting brighter, he's to breathe in. When it's getting dimmer, he's to exhale. At the valley between breaths, he's to not breathe (you know... like we all do all the time including you as you're reading this now. yes, you.)

The issue I have is that I've decided to go too big for my skills on it. I can make it work if I just control the brightness, but leave the timing of the breathing exactly the same because I know all the variables. I can make it work if I change the timing of the breath but the brightness is set.

The big issue is the increment in my analogWrite loop for inhaling and exhaling.

I need to get from 5 (my brightness floor) to X (my max brightness) in Y (in breath) milliseconds. I can simply change the delay to do this, but with SHORTER breaths, I might not get all the way to Y before running out of time if the increment is too small on my fade up loop. Alternately, if I increase the increment, but then increase the in-breath length, the steps might be HUGE and really noticeable on a long in-breath. We want the increments as small as possible to make it possible for 5 to get to X in Y milliseconds with a delay long enough for the PWM to register but not so long that it looks choppy. I'm going to throw up. I'll restate:

I need to get a formula that will give me the smallest increment and lowest delay necessary to get from 5 to X in as close to exactly Y ms as possible.
I'll use the same formula to calculate the outbreath. The pauses are not an issue.

I have a pot for brightness that is mapped to return 5-200.
I have a pot for breath length that is mapped to return 2000-9000 (as in the seconds necessary to complete one breath cycle)

In the end, this would run as a macro of sorts that will start him at a nice 5000ish range, then slowly slow down and help him get to sleep. This technique has been used in cases like his as an alternative to drugs (EEEK!).

I would appreciate any assistance you might be able to give on this problem. I know this is long and I'm almost certainly missing an important piece of information that will frustrate you folks. I'm just brain dead on this and I missed the cutoff to give it to him tonight, but would love it if he had it for tomorrow.

I've attached a photo - it looks much better in person. No you may not have my box of M/M jumpers.

Assume:

  1. The human breath from start of in-breath 1 to the start of in-breath 2 can take between 2000 milliseconds and 9000 milliseconds.
  2. The breath is made up of:
  3. in-breath
  4. pause and hold breath
  5. out-breath
  6. pause with no breathing
  7. The following values are assumed for the ideal breath:
  8. in-breath = total breath * .50;
  9. pause and hold breath = totalbreath *.05
  10. out-breath = totalbreath * .35
  11. pause between breaths = totalbreath * .10

Code blocks with missing chunks:

int peak = 80; // max brightness
int start = 5; // min brightness (it looks creepy when it goes all the way off)


int totalBreath = 7500; // from start of in-breath to start of in-breath
int inbreathIncrement = 2; // steps to increase brightness on in-breath with analogWrite()
int outbreathIncrement = 2;// steps to decrease brightness on in-breath with analogWrite()
int inbreathDelay = 20; // delay in ms between increases in brightness 
int outbreathDelay = 30;// delay in ms bet... The comments are too verbose, aren't they?
int pause = 1000; // the time to wait at the bottom of a breath before starting a new one.

  
}

void loop(){
  setBrightness(); //see below
  setSpeed(); //ditto
  
// at this point, it's assumed we know the speed of the breath and the brightness.
// that should give us inbreathIncrement and inbreathDelay

  for(int i=start; i<=peak;i=i + inbreathIncrement){
   analogWrite(3,i); 
     delay(inbreathDelay);

  }

  for(int i=peak; i>=start;i=i- outbreathIncrement){
    
   analogWrite(3,i); 
   delay(outbreathDelay);
  }  
  
  delay(pause); 
}

void setBrightness(){
  peak = map(analogRead(A1),0,1024,255,start);

}

void setSpeed(){
  
  
  
 totalBreath = map(analogRead(A0),0,1024,9000,2000);
 int inbreathtime = totalBreath *.55;
 int outbreathtime = totalBreath * .35;
 pause = totalBreath * .1;
 
  
inbreathDelay = ??????
outbreathDelay = ??????


}

Honestly, thanks everyone who made it this far. It's a thin line between not giving enough information for you to help and molesting you with so much mind-barf that you don't want to read this far. Half of you probably only read this far assuming that by looking this deep you'd find Carmen Sandiego. She's not here, but I'll tell you where I last saw her if you help me with my math issues!

Well the first thing that springs to mind is that you are using ints, which do not have fractional parts, and you are working with a lot of decimal places.

A first change I would make is either use float instead of int (which handles fractions) or scale stuff up (eg. use long instead of int and then divide). Probably using float is simpler.

Otherwise if you are not careful, you will end up with (say for a long breath but a dim light) something like max light = 100 divided by 200 ms giving 0, and then keep adding 0 and nothing will happen.

Once you use floats I think your theory is OK, it should work for any values. I haven't fully checked the arithmetic, but heck, it can't be too hard to work out the increment to get the light from 0 to x luminence in 0 to y seconds. It would be one divided by the other, and then scaled down to milliseconds. In fact, I think you are better of not varying the delay but simply working out how much to change per delay.

So as an example, you want 80 brightness, and you want 6 seconds (6000 ms). So you increment the light by 80 / 6000 every millisecond (that is, by 0.0133). That's why you need the floats, otherwise the answer becomes zero. So after 75 ms the brightness will hit 1, and then another 75 ms later it will become 2, and so on up to 80 after 6000 ms.

My 8 year old son is an insomniac. Hours of playing, reading, games etc will just NOT help him sleep

Does he need the sleep? Many people don't (at least during some periods) I have a nephew who needs ~4 hours/night since childhood, and once and a while half a day. If your son doesn't need the sleep - he has enough energy during the day - your main concern might be to give him enough to read, tinker, do, think, etc, and clear instructions(!) every night so he don't wake the others.

I'm no doctor, I don't know all the ins/outs of your son, just another point of view to consider

robtillaart:

My 8 year old son is an insomniac. Hours of playing, reading, games etc will just NOT help him sleep

Does he need the sleep?

I'm utterly fine with him staying up as long as he likes and reading etc. He gets up and draws and writes etc... The issue is that he then has to be dragged out of bed in the morning. If he was leaping out of bed at 7am like he used to, I'd be fine. It's just clear he's not getting the sleep he needs!

OK, I understand

Fiddled a bit with your code,

  • added a pause at the TOP (smaller than at the bottom)
  • changed comments
  • removed initialization as these are never used
  • removed the incrementsteps, made the stepsize 1 in the loops,
    ==> this makes the code simpler and smooths the transitions of the light even more
  • added the math for the delayIn /out based upon totalTime and percentage and #steps in the loops
  • changed int to long to prevent overflow
  • added setup and some "debug" statements

Give it a try

// 
//    FILE: sleepLight.pde
//  AUTHOR: 
// VERSION: 0.1.00
// PURPOSE: configurable dimming light for breath control
//
// HISTORY: 
// 0.1.00 - 2011-05-14  initial version
// 
// LICENCE: ???
//

// BREATHCYCLE
// 1 - BREATH IN
// 2 - PAUSE TOP
// 3 - BREATH OUT
// 4 - PAUSE BOTTOM


int peak = 80; 		// max brightness
int start = 5; 		// min brightness (it looks creepy when it goes all the way off)

// No need to initialize as it is done in the loop() every time
// some vars made global for debug purpose
long totalBreath; 	// from start of in-breath to start of in-breath
long inbreathtime;
long outbreathtime;
long inbreathDelay;
long outbreathDelay;	
long pauseTop; 		// the time to wait at the TOP of a breath.
long pauseBottom; 	// the time to wait at the BOTTOM of a breath before starting a new one.


void setup()
{
  Serial.begin(115200);
  Serial.println("Goodnight ...");
  pinMode(13, OUTPUT);
}

void loop()
{
  setBrightness();
  setSpeed();

  // debug statements
  /*
  Serial.print("IB: ");  Serial.println(inbreathtime);
  Serial.print("ID: ");  Serial.println(inbreathDelay);
  Serial.print("PT: ");  Serial.println(pauseTop);
  Serial.print("OB: ");  Serial.println(outbreathtime);
  Serial.print("OD: ");  Serial.println(outbreathDelay);
  Serial.print("PB: ");  Serial.println(pauseBottom);
  */

  Serial.println("Breath in...");
  digitalWrite(13, HIGH);
  for(int i=start; i<=peak; i++)
  {
    analogWrite(3,i); 
    delay(inbreathDelay);
  }

  // IS A PAUSE NEEDED between IN and OUT ?????
  delay(pauseTop);

  Serial.println("Breath out...");
  digitalWrite(13, LOW);
  for(int i=peak; i>=start; i--)
  {
    analogWrite(3,i); 
    delay(outbreathDelay);
  }  
  //delay(pauseBottom); 
}

void setBrightness()
{
  peak = map(analogRead(A1), 0, 1024, 255, start);
}

void setSpeed()
{
  totalBreath = map(analogRead(A0), 0, 1024, 9000, 2000);   // 2 - 9 seconds

  // Divide total time over the phases	total == 100%
  inbreathtime = totalBreath * 55 / 100;
  outbreathtime = totalBreath * 35 / 100;
  pauseTop = totalBreath * 3 /100;
  pauseBottom = totalBreath * 7 /100;

  // calculate the delaytimes for breathin/out
  int steps = peak-start;
  inbreathDelay =  inbreathtime / steps;
  outbreathDelay = outbreathtime / steps;
}
// END OF FILE

robtillaart:
Fiddled a bit with your code,
...
Give it a try

You sure did! I did uncommment the pause at the bottom, after trying it without. It works beautifully (though I'll reverse my mapping on my speed pot now).

I can't thank you enough for your help! He's fiddling with the knobs now and says (in true 8 year old fashion) that he's tired already :slight_smile:

THANKS!!!!

Nick, though I used robtillaart's code (and am now working to make sure I understand it), your point about floats is very well taken. Can you tell I came from the do-whatever-you-like world of PHP most recently? Blech.

Thank you both very much - he's so excited!

though I'll reverse my mapping on my speed pot now

just change the red stuff in the map statements

void setBrightness()
{
peak = map(analogRead(A1), 0, 1024, start, 255);
}
....
totalBreath = map(analogRead(A0), 0, 1024, 2000, 9000); // 2 - 9 seconds

you should change 2000 and 9000 by named constants or defines
#define MINTIME 2000
#define MAXTIME 9000

totalBreath = map(analogRead(A0), 0, 1024, MINTIME, MAXTIME);

idem

#define MINBRIGHT 5
#define MAXBRIGHT 255

peak = map(analogRead(A1), 0, 1024, MINBRIGHT, MAXBRIGHT);


If there are things unclear, just let me know,

Furthermore I was thinking of the following idea,

  • if the potmeters don't change for some time (30 minutes) the peakBrightness should slowly dim to prevent that the light wakes him up.
  • Adding a RealTimeClock could wake him with light too. Search for DS1307

Well the first thing that springs to mind is that you are using ints, which do not have fractional parts, and you are working with a lot of decimal places...

Nick has a point here. Floats (in Arduino sketches) take a lot more of the sparse memory and performance. However in your app this was no problem. You were using them as percentages of totalBreathTime and as the time was between 2000 and 9000 milisec the resulting values were OK. In the rewrite I replaced the float math with long integer math - smaller and faster - so it would be more clear how the totalBreathTime was divided. And the saved memory can be used for future expansion :wink:

Hope your son sleeps well now, (maybe we should productize the lamp :wink:

robtillaart,

Changes made. I'm (though it clearly doesn't show in my late night sample code yesterday) not too unfamiliar with c++ - I made my living with it all of 10 years ago. Just gotten lazy! Thank you for your continued interest in this little project!

A couple things... Sadly, this product already exists in the USA, it just has a few problems. It's patented, closed source and designed for ADULT sleeping patterns. Children have a slightly higher respiratory rate, so I needed to make my own.

A couple interesting things about this one...
I'm using a 3W RGB common-anode LED for this. The red on this LED burned out a few months back, so I've been waiting for a project that would benefit from JUST blue or green (or a mix). It's mounted (see pic) to a bar of aluminum and then heatsinks are mounted to IT. This is VAST overkill, but the box is actually cardboard... Just being careful. As an example, it's been running at max peak and max breath for over an hour and the bar directly under the LED is at 73F according to my IR thermometer. That's 1 degree above ambient.

I'm driving the LED with this constant current driver:
http://www.dealextreme.com/p/mr16-1-1w-320-350ma-constant-current-regulated-led-driver-8-40v-input-13553
The fun bit about this driver is that you can get VERY good PWM results by applying output from the Arduino to the PT4511 pin 1. For less than $2, it's a really great driver. I remove the MR-15 pins and the H-bridge, solder on some .1 pins and toss it right on my perfboard! If I ever turn this into a more permanent product, I'll likely make it a single board solution with a SMD atmega, the ON SEMI CAT4109 RGB driver (LOVE this chip - best design I've seen in years), a white/brown/pink noise maker/mixer and a clock with a low power LCD built in (the low power version of this: http://www.mdfly.com/index.php?main_page=product_info&cPath=40_60&products_id=201). I'd throw it all into this little 150x150x50mm box I have with a li-ion battery and turn it into a nice travel sized unit :slight_smile:

I'd also like to take this moment to thank Modern Device (http://www.moderndevice.com) for what has VERY quickly become my favorite Arduino. The BBB is just amazing. It's cheap enough that I don't mind deploying it, has enough thoughtful additions (nobody includes the dang inductor!), and is flexible. I proto on one in my breadboard then often (not in this case, just because I will be adding more) solder directly on to the board. I will usually cut small holes in a piece of acrylic for each of my wires, cut mounting holes in it, mount my BBB to the acrylic then use the Dale Wheat stress relief technique (http://www.dalewheat.com/) to make sure my connections last forever.

edit: note that the kapton tape is only there to hold the heatsink in place while the adhesive cures.

Rob,
SUCCESS!!! More success than we imagined, in fact. My 3 year old sleeps in the same room and for the first time any of us can remember, they were BOTH asleep within 10 minutes! Without fail, every night in the past they've both been up for 30-45 minutes past lights out.

I'm making 4 more using Modern Device's RBBBs today!

I think these next versions will use the tlc5940 and 4 pirhana LEDs so that can use a lower powered adapter.

Thanks again!!!!

So to hear you had a good night too :wink: