0

I try to convert an accelerometer's upper hemisphere x and y rotations - meaning clamping output only to z positive orientations - into two pre-defined colour transitions of an RGB LED, where x and y range from 0° - 180°.

enter image description here

The x rotation shall transition from M {255, 0, 255} to G {0, 255, 0} and the y rotation from YR {255, 127, 0} to CB {0, 127, 255}; these value triplets are arbitrary.

The accelerometer, calibration, etc. work as intended and I have the code working for colour changes for either x or y rotations - but don't know how both can/should be combined.

Hardware used: Adafruit Metro Mini, ANALOG DEVICES ADXL345, common cathode RGB LED.

Any help very much appreciated!

/******* xyz tilt indicator *******/

/******* Adafruit Metro Mini, ANALOG DEVICES ADXL345, common cathode RGB LED *******/

/******* LIBRARIES *******/

#include <Wire.h>

/******* VARIABLES *******/

#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 =  247; // Averaged readings from 2-point calibration with 6-point-tumble method (values particular to each IC)
const int xMin = -266;
const int yMax =  254;
const int yMin = -261;
const int zMax =  233;
const int zMin = -258;

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.3; // Smoothing factor 0 < α < 1 (smaller = smoother = less responsive)
float xEMA = 0;  // Initialise with an arbitrary reading (0 = oriented in the horizontal plane at start)
float yEMA = 0;
float zEMA = 0;

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, 127, 0},
  {0, 127, 255},
};

const byte pinLEDR = 9; // Pins for common cathode RGB LED, see https://dronebotworkshop.com/rgb-leds/
const byte pinLEDG = 10;
const byte pinLEDB = 11;

/******* FUNCTIONS *******/

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);

} // End of setup

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.002) {  // 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];

    // HERE IS WHERE MY QUESTION IS: HOW TO COMBINE THE TWO TRANSITIONS?

    setLED (xR, xG, xB);

  }

  //delay(100); // For CoolTerm/Excel use only

} // End of loop

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;

  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;

} // End of readSensor

void registerWrite(byte address, byte val) {

  Wire.beginTransmission(DEVICE);
  Wire.write(address);
  Wire.write(val);
  Wire.endTransmission();

  return;

} // End of registerWrite

void registerRead(byte address, int num, byte _buff[]) { // Reads num bytes into _buff array

  Wire.beginTransmission(DEVICE);
  Wire.write(address);
  Wire.endTransmission();
  Wire.beginTransmission(DEVICE);
  Wire.requestFrom(DEVICE, num);

  int i = 0;

  while (Wire.available())
  {

    _buff[i] = Wire.read(); // Read 1 byte
    i++;
  }

  Wire.endTransmission();

  return;

} // End of registerRead

void setLED (byte r, byte g, byte b) {

  analogWrite(pinLEDR, r);
  analogWrite(pinLEDG, g);
  analogWrite(pinLEDB, b);

  return;

} // End of setLED

Thanks for chiming in.

Not enough reputation to comment directly under your comment.

Yes, at the moment, I can either have the x rotation to transition linearly (xFraction) between M and G or the y rotation to transition linearly (yFraction) between YR and GB. As I attempted to show in the schematic on top, I want to be able to do both simultaneously. So, somehow xR/xG/xB and yR/yG/yB need to be linearly mixed as well. That's what I'm struggling with.

  • When wanting to combine multiple sensor values to one set of result values, you must have a plan, what the code should be doing. This is not clear. You seem to be mixing colors linear with xRot and yRot. Maybe you want to combine the resulting xR, ... and yR ... values also linearly. For example half x, half y. This depends on what result you want to reach. – chrisl Aug 20 '18 at 13:39

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Browse other questions tagged or ask your own question.