Sunday, November 13, 2016

Frank, Breathe!

// breathing LED, for @frankippolito
// simplest possible sketch, using only loops and IO, no PWM hardware or libraries.
// copy and paste this into a new Arduino sketch, modify the indicated parameters and upload.
// (v2 some corrections and comment mods)

#define LED_PIN A0 // which pin is the LED on
#define LED_GAMMA 4.4 // gamma correct the brightness curve
// larger values make it "pop" more at the end

#define LED_OFF_STATE LOW // in theory the arduino can sink more current than it can source, 
#define LED_ON_STATE HIGH // so swap these if you're wiring the other side of the LED to VCC rather than GND

#define PWM_MIN 0 // LED brightness minimum (0..255)
#define PWM_MAX 255 // LED brightness maximum (0..255)
// there is no need to use a series resistor for the LED as long as you manage the maximum on time. PWM is More efficient

#define DELTA_RATE 0.001 // LED brightness change per delta step (rate of change)
#define DELTA_LOOPS 2 // how long to spend in the PWM loop per delta step (major divider)

// the rate of the "breathing" is going to be a slightly complication multiplication of
// the processor clock speed, some overheads, and the DELTA_RATE divided by DELTA_LOOPS
// Code is written so the MIN and MAX values can be tweaked later without altering timings.
// There is also potential for "gamma correction" if the log brightness curve isn't sophisticated 
// enough
// update: Changing the DELTA_RATE is now the best way to fine-tune the breathing rate. 
// It scales better and with finer control than the LOOPS, since it's a floating point number.

void setup() {
  Serial.begin(57600); //  needed on some boards
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LED_OFF_STATE);

int direction_up = true;
float linear = 0.0;

void loop() {
  // update our linear value
  if(direction_up) {
    // going up
    if( (linear += DELTA_RATE) >= 1.0 ) {
      // we hit max
      linear = 1.0; direction_up = false;
  } else {
    // going down
    if( (linear -= DELTA_RATE) <= 0.0 ) {
      // we hit max
      linear = 0.0; direction_up = true;
  // turn the linear value into an absolute PWM brightness via the gamma curve
  float gamma = pow(linear, LED_GAMMA);
  gamma = gamma * (float)(PWM_MAX - PWM_MIN) + (float)PWM_MIN;
  int pwm = gamma;
  // if we have PWM hardware available, this next bit would be just a call to set it's value and a wait(),
  // but we're not going to assume any is available on the pin you want, so we just use raw loops to do 
  // rough-and-ready PWM to the LED for a few turns.
  for(int j = DELTA_LOOPS; j>0; j--) {
    int i;
    // spend some time on
    for(i = pwm; i>0; i--) {
      digitalWrite(LED_PIN, LED_ON_STATE);
    // spend some time off
    for(i = (256-pwm); i>0; i--) {
      digitalWrite(LED_PIN, LED_OFF_STATE);