2

I've built an accelerometer based dice. It changes colour based on what side of the dice faces up. All is well and good, except I'd like the RGB to fade from one colour to the next instead of changes instantly.

Here's the code:

include <Wire.h>
include <Adafruit_Sensor.h>
include <Adafruit_LSM303_U.h>
include <Adafruit_9DOF.h>
include <Adafruit_L3GD20_U.h>

/* Assign a unique ID to the sensors */
Adafruit_9DOF                 dof   = Adafruit_9DOF();
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
Adafruit_LSM303_Mag_Unified   mag   = Adafruit_LSM303_Mag_Unified(30302);

// OUTPUT: Use digital pins 9-11, the Pulse-width Modulation (PWM) pins
// LED's cathodes should be connected to digital GND
int redLEDPin = 11;   // Red LED,   connected to digital pin 9
int greenLEDPin = 10;  // Green LED, connected to digital pin 10
int blueLEDPin = 9;  // Blue LED,  connected to digital pin 11

// Program variables
int red = 255;   // Variables to store the values to send to the pins
int green = 255;
int blue = 255;

int lastOrientation = -1; // previous orientation

void initSensors()
{
  if(!accel.begin())
  {
    Serial.println(F("No LSM303 detected"));
    while(1);
  }
  if(!mag.begin())
  {
    Serial.println("No LSM303 detected");
    while(1);
  }
}

void setup() {

  Serial.begin(115200);
  Serial.println(F("RGB Dice... go!")); Serial.println("");

  /* Initialise the sensors */
  initSensors();

  pinMode(redLEDPin, OUTPUT);   // sets the pins as output
  pinMode(greenLEDPin, OUTPUT);   
  pinMode(blueLEDPin, OUTPUT);


  int lastOrientation = -1; // previous orientation
}

void loop() {

  /* Get a new sensor event */
  sensors_event_t event;
  accel.getEvent(&event);

  int orientation = - 1; // dice orientation
  String orientationString; // string for printing orientation
  /*
   * orientation:
   * 1: cyan
   * 2: blue
   * 3: green
   * 4: magenta
   * 5: yellow
   * 6: red
   */

  // read accelerometer
  int x = event.acceleration.x;
  int y = event.acceleration.y;
  int z = event.acceleration.z;

  // calculate absolute values, and determin largest
  int absX = abs(x);
  int absY = abs(y);
  int absZ = abs(z);

  if ( (absZ > absX) && (absZ > absY)) {

    //base orientation Z
    if (z > 0) {
      orientationString = "1, cyan";
      orientation = 1;
      red = 255;
      green = 0;
      blue = 0;

    } else {
      orientationString = "6, red";
      orientation = 6;
      red = 0;
      green = 255;
      blue = 255;
    }

  } else if ( (absY > absX) && (absY > absZ)) {

    //base orientation Y
    if (y > 0) {
      orientationString = "2, blue";
      orientation = 2;
      red = 255;
      green = 255;
      blue = 0;

    } else {
      orientationString = "5, yellow";
      orientation = 5;
      red = 0;
      green = 0;
      blue = 255;
    }
  } else {
    //base orientation X
    if (x > 0) {
      orientationString = "4, magenta";
      orientation = 4;
      red = 0;
      green = 255;
      blue = 0;

    } else {
      orientationString = "3, green";
      orientation = 3;
      red = 255;
      green = 0;
      blue = 255;

    }
  }

  // if orientation changes, print out a description
  if (orientation != lastOrientation) {
    Serial.println(orientationString);
    lastOrientation = orientation;
  }

  analogWrite(redLEDPin, red);   // Write values to LED pins
  analogWrite(greenLEDPin, green); 
  analogWrite(blueLEDPin, blue);  
  delay(100);

}

Every fading example I have adopted has broken the code so far. I guess my initial code isn't set up for it. Any ideas for a workaround?

3
  • Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question.
    – sa_leinad
    Commented Nov 25, 2016 at 4:32
  • What have you tried so far? In each case, how did it break your code? What prevented you from modifying your code to get it to work?
    – sa_leinad
    Commented Nov 25, 2016 at 4:35
  • You might get some inspiration here: hackingmajenkoblog.wordpress.com/2016/10/02/flexible-led-fading
    – Majenko
    Commented Nov 25, 2016 at 10:48

1 Answer 1

1

You need to be a little clearer about the time that can exist between the sensor values changing. If you fade to slowly the correct value might never be reached before the sensor changes again. Maybe that's what you want?

You can't use a delay().

You need a currentRed, currentGreen and currentBlue so you can fade to the values of red, green and blue.

unsigned long last_reading_ms;
int red = 255;   // Variables to store the TARGET rgb target values for each pin
int green = 255;
int blue = 255;
int currentRed = 0;   // Variables to store the current rgb values of the pins
int currentGreen = 0;
int currentBlue = 0;

void setup()
{
   last_reading_ms=millis();
}

void loop()
{
  /* read sensor every 100ms and set red, green,blue if changed */
  if (millis()-last_reading_ms > 100)
  {
    /* Get a new sensor event */
    sensors_event_t event;
    accel.getEvent(&event);
    //the rest of your sensor reading code here

    last_reading_ms=millis()
  }

  /* if orientation changes, print out a description */
  if (orientation != lastOrientation) {
   Serial.println(orientationString);
   lastOrientation = orientation;
  }

  calculateRGBFadeTo();

  analogWrite(redLEDPin, currentRed);   // Write values to LED pins
  analogWrite(greenLEDPin, currentGreen); 
  analogWrite(blueLEDPin, currentBlue);  


  //*** do not use a delay or use a very small one to slow the transition ***
  //delay(100);

}

/* get values 1 step closer to target red, green, blue */ 
void calculateRGBFadeTo()
{
  /* fade to required rgb values */
  if (currentRed<red)
   currentRed ++;
  else if (currentRed>red)
   currentRed --;

  if (currentGreen<green)
    currentGreen ++;
  else if (currentGreen>green)
    currentGreen --;

  if (currentBlue<blue)
    currentBlue ++;
  else if (currentBlue>blue)
    currentBlue --;    

}

Notes:

The fade can be slowed down either with a small delay such as 5 ms. or by using another millis() variable to determine the time since the led was last set.

For example: if (millis()-last_rgb_write_ms > 5)

It's also possible to speed up the fade by using a larger increment. An increment of two, for example, currentRed+=2; instead of currentRed++;

Reminder, the comparison of when the target red, green, blue has been reached will be more complex with an increment greater than 1.

An increment of 2 and a sensor read frequency of 128ms (instead of 100) would ensure the correct RGB values are always reached. Alternatively reading the sensor every 255ms (4-5 times per second) would also ensure the correct RGB values has been reached.

Using millis() and avoiding delays allows many more tasks to be performed with more accurate timings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.