I want to change a 3W RGB LED's colours depending on x and y rotation (180°, only upper hemisphere) with x rotation from Magenta to Green and y rotation from Yellow Orange to Cyan Blue.
I got it to work well, without jitter, but only EITHER blending from Magenta to Green (x) OR Yellow Orange to Cyan Blue. I guess I would need some conditional checking that toggles between the two, depending on which axix is currently dominant, depending on how the sensor is oriented.
Thanks for some ideas!
// Library required to read from this sensor; use the latest version
#include <Wire.h>
// Variables that remain constant
#define DEVICE (0x53) // ADXL345 I2C address (fixed)
byte _buff[6];
char POWER_CTL = 0x2D;
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32; // x axis data register byte 0
char DATAX1 = 0x33; // x axis data register byte 1
char DATAY0 = 0x34;
char DATAY1 = 0x35;
char DATAZ0 = 0x36;
char DATAZ1 = 0x37;
const int xMax = 254; // Averaged readings from 2-point calibration with 6-point-tumble method (values particular to each IC)
const int xMin = -264;
const int yMax = 258;
const int yMin = -256;
const int zMax = 256;
const int zMin = -240;
const float xOffset = 0.5 * (xMax + xMin); // p. 8 http://www.analog.com/media/en/technical-documentation/application-notes/AN-1057.pdf
const float xGain = 0.5 * (xMax - xMin);
const float yOffset = 0.5 * (yMax + yMin);
const float yGain = 0.5 * (yMax - yMin);
const float zOffset = 0.5 * (zMax + zMin);
const float zGain = 0.5 * (zMax - zMin);
const float alphaEMA = 0.5; // Smoothing factor 0 < α < 1 (smaller = smoother = less responsive)
const byte xRotColours[][3] = { // xRot -> from M 255, 0, 255 to G 0, 255, 0
{255, 0, 255},
{0, 255, 0},
};
const byte yRotColours[][3] = { // yRot -> from YR 255, 127, 0 to CB 0, 127, 255
{255, 32, 0},
{0, 127, 255},
};
const byte pinLEDR = 9; // Pins for common cathode RGB LED, see https://dronebotworkshop.com/rgb-leds/, later bright 3W Adafruit Pixie
const byte pinLEDG = 10;
const byte pinLEDB = 11;
// Variables that can change
float xEMA = 0; // Initialise with an arbitrary reading (0 = oriented in the horizontal plane at start)
float yEMA = 0;
float zEMA = 0;
void setup()
{
Wire.begin();
Serial.begin(57600);
registerWrite(DATA_FORMAT, 0x00); // Set to 2g mode, typical output -256 +256 per axis, see p. 4 http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
registerWrite(POWER_CTL, 0x08); // Set to measuring mode
pinMode (pinLEDR, OUTPUT);
pinMode (pinLEDG, OUTPUT);
pinMode (pinLEDB, OUTPUT);
}
void loop()
{
readSensor();
float xRot = atan(xEMA / sqrt((pow(yEMA, 2)) + pow(zEMA, 2))) * 57.2957 + 90; // See p. 7 http://www.analog.com/media/en/technical-documentation/application-notes/AN-1057.pdf
float yRot = atan(yEMA / sqrt((pow(xEMA, 2)) + pow(zEMA, 2))) * 57.2957 + 90;
float zRot = atan(sqrt((pow(xEMA, 2)) + pow(yEMA, 2)) / zEMA) * 57.2957;
if (zEMA > 0.003)
{ // Instability when x and y are exactly in line with earth's gravity, see p. 12 https://www.nxp.com/files-static/sensors/doc/app_note/AN3461.pdf
double xFraction = (xRot - 0) / (double) (180 - 0); // Convert 0 -> 180 into 0.0 -> 1.0 (percentage)
double yFraction = (yRot - 0) / (double) (180 - 0);
byte xR = (xRotColours [1][0] - xRotColours [0][0]) * xFraction + xRotColours [0][0];
byte xG = (xRotColours [1][1] - xRotColours [0][1]) * xFraction + xRotColours [0][1];
byte xB = (xRotColours [1][2] - xRotColours [0][2]) * xFraction + xRotColours [0][2];
byte yR = (yRotColours [1][0] - yRotColours [0][0]) * yFraction + yRotColours [0][0];
byte yG = (yRotColours [1][1] - yRotColours [0][1]) * yFraction + yRotColours [0][1];
byte yB = (yRotColours [1][2] - yRotColours [0][2]) * yFraction + yRotColours [0][2];
/*Serial.print(xFraction, 2);
Serial.print(" ");
Serial.println(yFraction, 2);*/
setLED (xR, xG, xB); // Or use y-values... but how to use both x and y together?
//setLED (yR, yG, yB);
}
//delay(500); // For CoolTerm/Excel use only
}
void readSensor()
{ // Read from the sensor, calibrate readings, smooth output
uint8_t bytesToRead = 6; // Burst read (preferential as per Analog Devices)
registerRead( DATAX0, bytesToRead, _buff); // Read from the 6 registers
int xRaw = (((int)_buff[1]) << 8) | _buff[0]; // 10 bit (2 bytes), LSB first, convert into integer
int yRaw = (((int)_buff[3]) << 8) | _buff[2];
int zRaw = (((int)_buff[5]) << 8) | _buff[4];
float x = (xRaw - xOffset) / xGain; // See p. 8 http://www.analog.com/media/en/technical-documentation/application-notes/AN-1057.pdf
float y = (yRaw - yOffset) / yGain;
float z = (zRaw - zOffset) / zGain;
//Serial.print(xRaw);
//Serial.print(", ");
//Serial.print(yRaw);
//Serial.print(", ");
//Serial.println(zRaw);
xEMA = (alphaEMA * x) + ((1 - alphaEMA) * xEMA); // See https://en.wikipedia.org/wiki/Exponential_smoothing
yEMA = (alphaEMA * y) + ((1 - alphaEMA) * yEMA);
zEMA = (alphaEMA * z) + ((1 - alphaEMA) * zEMA);
return;
}
void registerWrite(byte address, byte val)
{
Wire.beginTransmission(DEVICE);
Wire.write(address);
Wire.write(val);
Wire.endTransmission();
return;
}
void registerRead(byte address, byte num, byte _buff[])
{ // Reads num bytes into _buff array
Wire.beginTransmission(DEVICE);
Wire.write(address);
Wire.endTransmission();
Wire.beginTransmission(DEVICE);
Wire.requestFrom(DEVICE, num);
byte i = 0;
while (Wire.available())
{
_buff[i] = Wire.read(); // Read 1 byte
i++;
}
Wire.endTransmission();
return;
}
void setLED (byte r, byte g, byte b)
{
analogWrite(pinLEDR, r);
analogWrite(pinLEDG, g);
analogWrite(pinLEDB, b);
return;
}

