Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Breakout boards, sensors, other Adafruit kits, etc.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
jjdeprisco
 
Posts: 38
Joined: Tue Apr 09, 2013 8:09 pm

Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by jjdeprisco »

I'm working with this project from Phillip Burgess:

https://learn.adafruit.com/animated-led-sand/code

I didn't have a LIS3DH, but instead figured it wouldn't be too difficult to rework the code for a ADXL343. And actually, I have the Adafruit ADXL343 + ADT7410 Sensor FeatherWing (Product ID: 4147). I'm just not using the temperature section here.

I've made some tweaks to the original sketch, and the ADXL343 sensor is recognized, my code compiles, and I get grain motion, but no gravity or accelerometer interaction. Kind of like bugs running around, with no change when the device is tipped or turned around.

I'm using ADXL343 at address 0x53 (because 0x1D wouldn't compile). The biggest changes I made were in the accelerometer reading section. The original, which uses accel.read(); doesn't work for the ADXL343, nor does the accel.y, accel.x designations for the variables. So I reworked that based on my understanding of what the ADXL343 wants to see.

See full code below. I've commented the sections I modified. Any thoughts on how to get the ADXL343 to respond better?

Code: Select all

// 3/24/23 JJD - Modified for use with Adafruit ADXL343 + ADT7410 Sensor FeatherWing Product ID: 4147
// (instead of LIS3DH accelerometer)



// SPDX-FileCopyrightText: 2017 Phillip Burgess for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// https://learn.adafruit.com/animated-led-sand/code

//--------------------------------------------------------------------------
// Animated 'sand' for Adafruit Feather.  Uses the following parts:
//   - Feather 32u4 Basic Proto (adafruit.com/product/2771)
//   - Charlieplex FeatherWing (adafruit.com/product/2965 - any color!)
//   - 350 mAh LiPoly battery (2750)
//   - SPDT Slide Switch (805)
//
// This is NOT good "learn from" code for the IS31FL3731; it is "squeeze
// every last byte from the microcontroller" code.  If you're starting out,
// download the Adafruit_IS31FL3731 and Adafruit_GFX libraries, which
// provide functions for drawing pixels, lines, etc.
//--------------------------------------------------------------------------

#include <Wire.h>            // For I2C communication
#include <Adafruit_Sensor.h>  

// #include <Adafruit_LIS3DH.h> // For accelerometer  // JJD replacing with Adafruit_ADXL343
#include <Adafruit_ADXL343.h> // JJD
//#include "Adafruit_ADT7410.h" // JJD ADT7410 Temp sensor (0x48 0x49 0x4A or 0x4B) Not used for now

#define DISP_ADDR   0x74   // Charlieplex FeatherWing I2C address // 0x74 is from original sketch
// IS31FL3731 144-LED CharliePlex driver (0x74 0x75 0x66 or 0x77)

//#define ACCEL_ADDR  0x18  // Accelerometer I2C address  // 0x18 is LIS3DH accelerometer
#define ACCEL_ADDR  0x53  // Accelerometer I2C address // ADXL343 3-axis accelerometer (0x1D or 0x53)

#define N_GRAINS     20 // Number of grains of sand
#define WIDTH        15 // Display width in pixels
#define HEIGHT        7 // Display height in pixels
#define MAX_FPS      45 // Maximum redraw rate, frames/second

// The 'sand' grains exist in an integer coordinate space that's 256X
// the scale of the pixel grid, allowing them to move and interact at
// less than whole-pixel increments.
#define MAX_X (WIDTH  * 256 - 1) // Maximum X coordinate in grain space
#define MAX_Y (HEIGHT * 256 - 1) // Maximum Y coordinate
struct Grain {
  int16_t  x,  y; // Position
  int16_t vx, vy; // Velocity
} grain[N_GRAINS];

// Adafruit_LIS3DH accel      = Adafruit_LIS3DH(); // replace
// Create the ADXL343 accelerometer sensor object
Adafruit_ADXL343 accel = Adafruit_ADXL343(12345); // JJD

uint32_t        prevTime   = 0;      // Used for frames-per-second throttle
uint8_t         backbuffer = 0,      // Index for double-buffered animation
                img[WIDTH * HEIGHT]; // Internal 'map' of pixels

const uint8_t PROGMEM remap[] = {    // In order to redraw the screen super
   0, 90, 75, 60, 45, 30, 15,  0,    // fast, this sketch bypasses the
       0,  0,  0,  0,  0,  0,  0, 0, // Adafruit_IS31FL3731 library and
   0, 91, 76, 61, 46, 31, 16,   1,   // writes to the LED driver directly.
      14, 29, 44, 59, 74, 89,104, 0, // But this means we need to do our
   0, 92, 77, 62, 47, 32, 17,  2,    // own coordinate management, and the
      13, 28, 43, 58, 73, 88,103, 0, // layout of pixels on the Charlieplex
   0, 93, 78, 63, 48, 33, 18,  3,    // Featherwing is strange! This table
      12, 27, 42, 57, 72, 87,102, 0, // remaps LED register indices in
   0, 94, 79, 64, 49, 34, 19,  4,    // sequence to the corresponding pixel
      11, 26, 41, 56, 71, 86,101, 0, // indices in the img[] array.
   0, 95, 80, 65, 50, 35, 20,  5,
      10, 25, 40, 55, 70, 85,100, 0,
   0, 96, 81, 66, 51, 36, 21,  6,
       9, 24, 39, 54, 69, 84, 99, 0,
   0, 97, 82, 67, 52, 37, 22,  7,
       8, 23, 38, 53, 68, 83, 98
};

// IS31FL3731-RELATED FUNCTIONS --------------------------------------------

// Begin I2C transmission and write register address (data then follows)
uint8_t writeRegister(uint8_t n) {
  Wire.beginTransmission(DISP_ADDR);
  Wire.write(n); // No endTransmission() - left open for add'l writes
  return 2;      // Always returns 2; count of I2C address + register byte n
}

// Select one of eight IS31FL3731 pages, or the Function Registers
void pageSelect(uint8_t n) {
  writeRegister(0xFD); // Command Register
  Wire.write(n);       // Page number (or 0xB = Function Registers)
  Wire.endTransmission();
}

// SETUP - RUNS ONCE AT PROGRAM START --------------------------------------

void setup(void) {
  uint8_t i, j, bytes;

  if(!accel.begin(ACCEL_ADDR)) {  // Init accelerometer.  If it fails...  ACCEL_ADDR fails
    pinMode(LED_BUILTIN, OUTPUT);    // Using onboard LED
    for(i=1;;i++) {                  // Loop forever...
      digitalWrite(LED_BUILTIN, i & 1); // LED on/off blink to alert user
      delay(250);                       // 1/4 second
    }
  }
  //accel.setRange(LIS3DH_RANGE_4_G); // Select accelerometer +/- 4G range // replace

  accel.setRange(ADXL343_RANGE_4_G); // Select accelerometer +/- 4G range // JJD


  Wire.setClock(400000); // Run I2C at 400 KHz for faster screen updates

  // Initialize IS31FL3731 Charlieplex LED driver "manually"...
  pageSelect(0x0B);                        // Access the Function Registers
  writeRegister(0);                        // Starting from first...
  for(i=0; i<13; i++) Wire.write(10 == i); // Clear all except Shutdown
  Wire.endTransmission();
  for(j=0; j<2; j++) {                     // For each page used (0 & 1)...
    pageSelect(j);                         // Access the Frame Registers
    for(bytes=i=0; i<180; i++) {           // For each register...
      if(!bytes) bytes = writeRegister(i); // Buf empty? Start xfer @ reg i
      Wire.write(0xFF * (i < 18));         // 0-17 = enable, 18+ = blink+PWM
      if(++bytes >= 32) bytes = Wire.endTransmission();
    }
    if(bytes) Wire.endTransmission();      // Write any data left in buffer
  }

  memset(img, 0, sizeof(img)); // Clear the img[] array
  for(i=0; i<N_GRAINS; i++) {  // For each sand grain...
    do {
      grain[i].x = random(WIDTH  * 256); // Assign random position within
      grain[i].y = random(HEIGHT * 256); // the 'grain' coordinate space
      // Check if corresponding pixel position is already occupied...
      for(j=0; (j<i) && (((grain[i].x / 256) != (grain[j].x / 256)) ||
                         ((grain[i].y / 256) != (grain[j].y / 256))); j++);
    } while(j < i); // Keep retrying until a clear spot is found
    img[(grain[i].y / 256) * WIDTH + (grain[i].x / 256)] = 255; // Mark it
    grain[i].vx = grain[i].vy = 0; // Initial velocity is zero
  }
}

// MAIN LOOP - RUNS ONCE PER FRAME OF ANIMATION ----------------------------

void loop() {
  // Limit the animation frame rate to MAX_FPS.  Because the subsequent sand
  // calculations are non-deterministic (don't always take the same amount
  // of time, depending on their current states), this helps ensure that
  // things like gravity appear constant in the simulation.
  uint32_t t;
  while(((t = micros()) - prevTime) < (1000000L / MAX_FPS));
  prevTime = t;

  // Display frame rendered on prior pass.  It's done immediately after the
  // FPS sync (rather than after rendering) for consistent animation timing.
  pageSelect(0x0B);       // Function registers
  writeRegister(0x01);    // Picture Display reg
  Wire.write(backbuffer); // Page # to display
  Wire.endTransmission();
  backbuffer = 1 - backbuffer; // Swap front/back buffer index


/* JJD reworked this section for ADXL343 */

  // Read accelerometer...
  sensors_event_t event;    // accel.read(); from original sketch didn't work for ADXL343
  accel.getEvent(&event);
  int16_t ax = -event.acceleration.y / 256,      // Transform accelerometer axes
          ay =  event.acceleration.x / 256,      // to grain coordinate space
          az = abs(event.acceleration.z) / 2048; // Random motion factor

  // 3/24/23 Having issues getting accel to work properly; gravity effect not visible
          
/*  End JJD tweaks  */
          
  az = (az >= 3) ? 1 : 4 - az;      // Clip & invert
  ax -= az;                         // Subtract motion factor from X, Y
  ay -= az;
  int16_t az2 = az * 2 + 1;         // Range of random motion to add back in

  // ...and apply 2D accel vector to grain velocities...
  int32_t v2; // Velocity squared
  float   v;  // Absolute velocity
  for(int i=0; i<N_GRAINS; i++) {
    grain[i].vx += ax + random(az2); // A little randomness makes
    grain[i].vy += ay + random(az2); // tall stacks topple better!
    // Terminal velocity (in any direction) is 256 units -- equal to
    // 1 pixel -- which keeps moving grains from passing through each other
    // and other such mayhem.  Though it takes some extra math, velocity is
    // clipped as a 2D vector (not separately-limited X & Y) so that
    // diagonal movement isn't faster
    v2 = (int32_t)grain[i].vx*grain[i].vx+(int32_t)grain[i].vy*grain[i].vy;
    if(v2 > 65536) { // If v^2 > 65536, then v > 256
      v = sqrt((float)v2); // Velocity vector magnitude
      grain[i].vx = (int)(256.0*(float)grain[i].vx/v); // Maintain heading
      grain[i].vy = (int)(256.0*(float)grain[i].vy/v); // Limit magnitude
    }
  }

  // ...then update position of each grain, one at a time, checking for
  // collisions and having them react.  This really seems like it shouldn't
  // work, as only one grain is considered at a time while the rest are
  // regarded as stationary.  Yet this naive algorithm, taking many not-
  // technically-quite-correct steps, and repeated quickly enough,
  // visually integrates into something that somewhat resembles physics.
  // (I'd initially tried implementing this as a bunch of concurrent and
  // "realistic" elastic collisions among circular grains, but the
  // calculations and volument of code quickly got out of hand for both
  // the tiny 8-bit AVR microcontroller and my tiny dinosaur brain.)

  uint8_t        i, bytes, oldidx, newidx, delta;
  int16_t        newx, newy;
  const uint8_t *ptr = remap;

  for(i=0; i<N_GRAINS; i++) {
    newx = grain[i].x + grain[i].vx; // New position in grain space
    newy = grain[i].y + grain[i].vy;
    if(newx > MAX_X) {               // If grain would go out of bounds
      newx         = MAX_X;          // keep it inside, and
      grain[i].vx /= -2;             // give a slight bounce off the wall
    } else if(newx < 0) {
      newx         = 0;
      grain[i].vx /= -2;
    }
    if(newy > MAX_Y) {
      newy         = MAX_Y;
      grain[i].vy /= -2;
    } else if(newy < 0) {
      newy         = 0;
      grain[i].vy /= -2;
    }

    oldidx = (grain[i].y/256) * WIDTH + (grain[i].x/256); // Prior pixel #
    newidx = (newy      /256) * WIDTH + (newx      /256); // New pixel #
    if((oldidx != newidx) && // If grain is moving to a new pixel...
        img[newidx]) {       // but if that pixel is already occupied...
      delta = abs(newidx - oldidx); // What direction when blocked?
      if(delta == 1) {            // 1 pixel left or right)
        newx         = grain[i].x;  // Cancel X motion
        grain[i].vx /= -2;          // and bounce X velocity (Y is OK)
        newidx       = oldidx;      // No pixel change
      } else if(delta == WIDTH) { // 1 pixel up or down
        newy         = grain[i].y;  // Cancel Y motion
        grain[i].vy /= -2;          // and bounce Y velocity (X is OK)
        newidx       = oldidx;      // No pixel change
      } else { // Diagonal intersection is more tricky...
        // Try skidding along just one axis of motion if possible (start w/
        // faster axis).  Because we've already established that diagonal
        // (both-axis) motion is occurring, moving on either axis alone WILL
        // change the pixel index, no need to check that again.
        if((abs(grain[i].vx) - abs(grain[i].vy)) >= 0) { // X axis is faster
          newidx = (grain[i].y / 256) * WIDTH + (newx / 256);
          if(!img[newidx]) { // That pixel's free!  Take it!  But...
            newy         = grain[i].y; // Cancel Y motion
            grain[i].vy /= -2;         // and bounce Y velocity
          } else { // X pixel is taken, so try Y...
            newidx = (newy / 256) * WIDTH + (grain[i].x / 256);
            if(!img[newidx]) { // Pixel is free, take it, but first...
              newx         = grain[i].x; // Cancel X motion
              grain[i].vx /= -2;         // and bounce X velocity
            } else { // Both spots are occupied
              newx         = grain[i].x; // Cancel X & Y motion
              newy         = grain[i].y;
              grain[i].vx /= -2;         // Bounce X & Y velocity
              grain[i].vy /= -2;
              newidx       = oldidx;     // Not moving
            }
          }
        } else { // Y axis is faster, start there
          newidx = (newy / 256) * WIDTH + (grain[i].x / 256);
          if(!img[newidx]) { // Pixel's free!  Take it!  But...
            newx         = grain[i].x; // Cancel X motion
            grain[i].vy /= -2;         // and bounce X velocity
          } else { // Y pixel is taken, so try X...
            newidx = (grain[i].y / 256) * WIDTH + (newx / 256);
            if(!img[newidx]) { // Pixel is free, take it, but first...
              newy         = grain[i].y; // Cancel Y motion
              grain[i].vy /= -2;         // and bounce Y velocity
            } else { // Both spots are occupied
              newx         = grain[i].x; // Cancel X & Y motion
              newy         = grain[i].y;
              grain[i].vx /= -2;         // Bounce X & Y velocity
              grain[i].vy /= -2;
              newidx       = oldidx;     // Not moving
            }
          }
        }
      }
    }
    grain[i].x  = newx; // Update grain position
    grain[i].y  = newy;
    img[oldidx] = 0;    // Clear old spot (might be same as new, that's OK)
    img[newidx] = 255;  // Set new spot
  }

  // Update pixel data in LED driver
  pageSelect(backbuffer); // Select background buffer
  for(i=bytes=0; i<sizeof(remap); i++) {
    if(!bytes) bytes = writeRegister(0x24 + i);
    Wire.write(img[pgm_read_byte(ptr++)] / 3); // Write each byte to matrix
    if(++bytes >= 32) bytes = Wire.endTransmission();
  }
  if(bytes) Wire.endTransmission();
}


User avatar
jjdeprisco
 
Posts: 38
Joined: Tue Apr 09, 2013 8:09 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by jjdeprisco »

PS: When I say address "0x1D wouldn't compile", it was because I simply didn't have the pullup set. So it's probably more correct for me to say that I'm just using the default address (0x53).

User avatar
adafruit_support_carter
 
Posts: 29457
Joined: Tue Nov 29, 2016 2:45 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by adafruit_support_carter »

Try running the basic ADXL343 example from the library:
https://github.com/adafruit/Adafruit_AD ... ortest.ino

See if that at least prints out values that change when you move the ADXL343 around.

User avatar
jjdeprisco
 
Posts: 38
Joined: Tue Apr 09, 2013 8:09 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by jjdeprisco »

Yeah, I know the sensor itself works. Just not in this project so far, with the code I am using above.

User avatar
adafruit_support_carter
 
Posts: 29457
Joined: Tue Nov 29, 2016 2:45 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by adafruit_support_carter »

Unknown then. If the sensor is returning the expected values, then it should just work.

Can try adding some prints of actual values in sand code as another way to sanity check:

Code: Select all

  // Read accelerometer...
  sensors_event_t event;    // accel.read(); from original sketch didn't work for ADXL343
  accel.getEvent(&event);
  int16_t ax = -event.acceleration.y / 256,      // Transform accelerometer axes
          ay =  event.acceleration.x / 256,      // to grain coordinate space
          az = abs(event.acceleration.z) / 2048; // Random motion factor
  Serial.print("ax = "); Serial.print(ax);
  Serial.print("ax = "); Serial.print(ay);
  Serial.print("ax = "); Serial.println(az);

User avatar
jjdeprisco
 
Posts: 38
Joined: Tue Apr 09, 2013 8:09 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by jjdeprisco »

Ah, I'm getting zeros...

ax = 0ay = 0az = 0
ax = 0ay = 0az = 0

So obviously something isn't right... will need to go back to the drawing board on this one. I know the sensor works with other CharlieWing projects using my HUZZAH board, so the issue must be with this code somewhere.

User avatar
adafruit_support_carter
 
Posts: 29457
Joined: Tue Nov 29, 2016 2:45 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by adafruit_support_carter »

If the library example works, but the sand code is returning 0's, then it should just be some minor software thing that's being overlooked.

If the library example is also returning 0's, then that would indicate a hardware issue. Like wiring or some other connection.

User avatar
jjdeprisco
 
Posts: 38
Joined: Tue Apr 09, 2013 8:09 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by jjdeprisco »

Yeah, has to be coding... there are no wires in this one...
20230329_114828.jpg
20230329_114828.jpg (906.66 KiB) Viewed 151 times

User avatar
jjdeprisco
 
Posts: 38
Joined: Tue Apr 09, 2013 8:09 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by jjdeprisco »

Small update...

I've set up a serial print of the raw values from the accelerometer as well as the previous values that were being derived from the accelerometer that return only zeros:

-9.73, 0.71, 0.24.
ax = 0, ay = 0, az = 0
.
-9.69, 0.78, 0.43.
ax = 0, ay = 0, az = 0

So this proves once again that the accel is fine, but the issue is in how the values are then being used to compute the grain response for the gravity effect.

User avatar
adafruit_support_carter
 
Posts: 29457
Joined: Tue Nov 29, 2016 2:45 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by adafruit_support_carter »

It could be a scaling issue. The division by 256 and 2048 being done in the original code:

Code: Select all

  // Read accelerometer...
  accel.read();
  int16_t ax = -accel.y / 256,      // Transform accelerometer axes
          ay =  accel.x / 256,      // to grain coordinate space
          az = abs(accel.z) / 2048; // Random motion factor
Those values are probably specific to the raw x,y,z values being used from the LIS3DH.

There's some code comment here about how those values get converted to G's, which might help:
https://github.com/adafruit/Adafruit_LI ... #L198-L219

User avatar
jjdeprisco
 
Posts: 38
Joined: Tue Apr 09, 2013 8:09 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by jjdeprisco »

Yeah, I've run into this before with other sensors.

Here's what I came up with to make it respond a bit better.

int16_t ax = (-event.acceleration.y * 10000) / 256; // Transform accelerometer axes 256
int16_t ay = (event.acceleration.x * 10000) / 256; // to grain coordinate space 256
int16_t az = (abs(event.acceleration.z) * 10000) / 2048; // Random motion factor 2048

It's not quite perfect, and it's hard to tell which way is "up" on the sensor, but I am now seeing the sand flow.

User avatar
adafruit_support_carter
 
Posts: 29457
Joined: Tue Nov 29, 2016 2:45 pm

Re: Replacing LIS3DH with ADXL343 accelerometer for LED Sand project

Post by adafruit_support_carter »

There's a little coordinate system diagram on the Wing to help indicate to directions:
coord.png
coord.png (31.97 KiB) Viewed 129 times
Z points up/down relative to the FeatherWing, with positive Z up. But the readings can be confusing in terms of sign. The sensed acceleration due to gravity will be in the negative Z for example.

Locked
Please be positive and constructive with your questions and comments.

Return to “Other Products from Adafruit”