Full Instructions

User Manual: Pdf

Open the PDF directly: View PDF PDF.
Page Count: 29

DownloadFull Instructions
Open PDF In BrowserView PDF
Animated LED Sand
Created by Ruiz Brothers

Last updated on 2018-02-06 04:55:15 PM UTC

Guide Contents
Guide Contents
Overview

2
3

Simulated Glowy Physics!
Prerequisite Guides

3
3

Adafruit Feather M0 Basic Proto - ATSAMD21 Cortex M0
Adafruit 15x7 CharliePlex LED Matrix Display FeatherWings
Lithium Ion Polymer Battery - 3.7v 350mAh
Adafruit LIS3DH Triple-Axis Accelerometer (+-2g/4g/8g/16g)
Breadboard-friendly SPDT Slide Switch
Ultimaker 2+ 3D Printer
PLA Filament for 3D Printers - 1.75mm Diameter - Teal - 1KG

4
4
5
5
5
5
6

Circuit Diagram
Code
3D Printing

7
8
13

Slice Settings
Clean up

14
14

Assembly

15

Measure Wires
Assemble Slide Switch
Wire Boards
Solder Slide Switch
Solder Accelerometer
Accelerometer Mount
Spacer
Attach boards
Bundle wires
Mount Battery
Attach Spacer
Plug in battery
Mount inside enclosure
Tuck wires
Align mounting holes
Face cover
Slide Switch Tolerances
Slide Switch Mount
Attach Lid
Gimbal Assemble
Teeter totter

© Adafruit Industries

15
15
17
17
18
19
20
20
21
22
23
24
24
24
24
25
27
27
27
27
28

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

Page 2 of 29

Overview
Simulated Glowy Physics!
These LEDs interact with motion and looks like they’re affect by gravity. An Adafruit LED matrix displays the LEDs as
little grains of sand which are driven by sampling an accelerometer with an Adafruit Feather.
The code, written by Phillip Burgess, simulates physics by calculating collisions and terminal velocity.
We designed 3d printed enclosures to take advantage of sensor readings by allowing it teeter totter as well as a
gimbal to create interesting simulations.

Prerequisite Guides
I suggest walking through the following guides to get a better understanding of the electronics.
Adafruit LIS3DH Triple-Axis Accelerometer
Adafruit 15x7 CharliePlex LED Matrix
Adafruit Feather M0 Basic

© Adafruit Industries

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

Page 3 of 29

6 x M2X8mm Screws
M2x8mm screws

2 x M2X4mm screws
M2X4mm screws

Adafruit Feather M0 Basic Proto - ATSAMD21 Cortex M0
PRODUCT ID: 2772

https://adafru.it/s1d

$19.95
IN STOCK

Your browser does not support the video tag.

Adafruit 15x7 CharliePlex LED Matrix Display FeatherWings
PRODUCT ID: 2965

https://adafru.it/wft

$0.00

OUT OF STOCK

© Adafruit Industries

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

Page 4 of 29

Lithium Ion Polymer Battery - 3.7v 350mAh
PRODUCT ID: 2750

https://adafru.it/kBj

$6.95
IN STOCK

Adafruit LIS3DH Triple-Axis Accelerometer (+2g/4g/8g/16g)
PRODUCT ID: 2809

https://adafru.it/uBq

$4.95
IN STOCK

Breadboard-friendly SPDT Slide Switch
PRODUCT ID: 805

https://adafru.it/drN

$0.95
IN STOCK

Ultimaker 2+ 3D Printer
PRODUCT ID: 2673

https://adafru.it/zef

$2,499.00
IN STOCK

© Adafruit Industries

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

Page 5 of 29

PLA Filament for 3D Printers - 1.75mm Diameter - Teal 1KG
PRODUCT ID: 3069

https://adafru.it/AeV

$39.95
IN STOCK

© Adafruit Industries

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

Page 6 of 29

Circuit Diagram

Take a moment to review the components in the circuit diagram. This illustration is meant for referencing wired
connections - The length of wire, position and size of components are not exact.
The Slide switch will connect to the Feather board and will need to be 70mm for the GND and 50mm for the EN pin
CharliePlex Featherwing connects the Feather.
The connections for SDA and SCL wires will need to be 70mm long.
3V and GND connections can both be 80mm long
LIS3DH connects to the top of the Feather via the 3d printed mount and will needto be 40mm long for 3V, GND, SDA
and SCL connections.

© Adafruit Industries

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

Page 7 of 29

Code
To use the LED Sand sketch you'll want to make sure you're using the latest version of the Arduino IDE (1.6.5 at the
time of this writing).
If you're totally new to Arduino take a little time to go through some introductory tutorials like how to make a LED blink.
This will help you understand how to use the IDE, load a sketch, and upload code.
Next you'll need to make sure the libraries used by the sketch are installed. With the latest Arduino IDE you can use its
library manager to easily install libraries, or check out this guide on how to manually install a library. You'll want to
install the following libraries:
Adafruit LIS3DH
Adafruit Unified Sensor
Adafruit IS31FL3731 Charlieplex LED
Search for the libraries in the library manager and they should be easy to find and install.
If you already have one or more of these libraries installed then make sure to update it to the latest version.
//-------------------------------------------------------------------------// 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!)
//
- LIS3DH accelerometer (2809)
//
- 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 
// For I2C communication
#include  // For accelerometer
#define
#define
#define
#define
#define
#define

DISP_ADDR 0x74 // Charlieplex FeatherWing I2C address
ACCEL_ADDR 0x18 // Accelerometer I2C address
N_GRAINS
20 // Number of grains of sand
WIDTH
15 // Display width in pixels
HEIGHT
7 // Display height in pixels
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();
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
© Adafruit Industries

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

Page 8 of 29

img[WIDTH * HEIGHT]; // Internal 'map' of pixels
const uint8_t PROGMEM
0, 90, 75, 60, 45,
0, 0, 0, 0,
0, 91, 76, 61, 46,
14, 29, 44, 59,
0, 92, 77, 62, 47,
13, 28, 43, 58,
0, 93, 78, 63, 48,
12, 27, 42, 57,
0, 94, 79, 64, 49,
11, 26, 41, 56,
0, 95, 80, 65, 50,
10, 25, 40, 55,
0, 96, 81, 66, 51,
9, 24, 39, 54,
0, 97, 82, 67, 52,
8, 23, 38, 53,
};

remap[] = {
// In order to redraw the screen super
30, 15, 0,
// fast, this sketch bypasses the
0, 0, 0, 0, // Adafruit_IS31FL3731 library and
31, 16,
1,
// writes to the LED driver directly.
74, 89,104, 0, // But this means we need to do our
32, 17, 2,
// own coordinate management, and the
73, 88,103, 0, // layout of pixels on the Charlieplex
33, 18, 3,
// Featherwing is strange! This table
72, 87,102, 0, // remaps LED register indices in
34, 19, 4,
// sequence to the corresponding pixel
71, 86,101, 0, // indices in the img[] array.
35, 20, 5,
70, 85,100, 0,
36, 21, 6,
69, 84, 99, 0,
37, 22, 7,
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...
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
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...
© Adafruit Industries

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

Page 9 of 29

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= 3) ? 1 : 4 - az;
ax -= az;
ay -= az;
int16_t az2 = az * 2 + 1;

//
//
//
//
//

Transform accelerometer axes
to grain coordinate space
Random motion factor
Clip & invert
Subtract motion factor from X, Y

// 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 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 nottechnically-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 MAX_X) {
newx
= MAX_X;
grain[i].vx /= -2;
} 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;
}

// New position in grain space
// If grain would go out of bounds
// keep it inside, and
// give a slight bounce off the wall

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
© Adafruit Industries

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

Page 11 of 29

// (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
grain[i].y
img[oldidx]
img[newidx]

=
=
=
=

newx; // Update grain position
newy;
0;
// Clear old spot (might be same as new, that's OK)
255; // Set new spot

}
// Update pixel data in LED driver
pageSelect(backbuffer); // Select background buffer
for(i=bytes=0; i= 32) bytes = Wire.endTransmission();
}
if(bytes) Wire.endTransmission();
}

© Adafruit Industries

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

Page 12 of 29

3D Printing

The 3D printed parts are fairly easy to make with most common home desktop 3D printers that are on the market.
And if you don’t have access a 3D printer, you can order our parts by visiting our Thingiverse page and have someone
local 3D print the parts and ship them to you.

Download the Fusion360 files (dome version)
https://adafru.it/ArR

Download the Fusion 360 files (gimbal version)
https://adafru.it/ArS

Download from Thingiverse
https://adafru.it/ArT

Download from Youmagine
https://adafru.it/svF

Download from Pinshape
https://adafru.it/ArU

© Adafruit Industries

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

Page 13 of 29

Slice Settings
Download the STL file and import it into your 3D printing
slicing software. You'll need to adjust your settings
accordingly if you're using material different than PLA.

230C Extruder Temp
No heated bed (65C for heated)
1.0 Extrusion Multiplier
.4mm Nozzle
0.38 Extrusion Width
.2mm Layer Height
30% infill
No Supports
skirt
60mm/s | 120mm travel speed

Clean up
We used a flush diagonal cutter to clean up any stringing and overhangs around the port openings and around the
standoffs inside the enclosure.
Make sure the openings for the USB ports are cleaned before mounting components. Use a hobby knife to help cut
away stringing that could block components from mounting.

© Adafruit Industries

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

Page 14 of 29

Assembly
Measure Wires
First we'll need to measure wires for each component so
its long enough to reach each mount inside the
enclosure.

The Slide Switch will connect to the Feather board and
will need to be 70mm for the GND and 50mm for the
EN pin.

CharliePlex Featherwing connects the Feather.
Connections for SDA and SCL wires will need to be
70mm long. Measure the 3v and GND connections to
both be 80mm long.

LIS3DH connects to the Feather and will need all four
connections to be 40mm long.

Assemble Slide Switch
A slide switch is used to tie the enable and ground pins
so we can turn off the 3 volt regulator, while still
enabling you to charge the lipo battery through a USB
cable.

Use heat shrink to prevent the pins from touching other
components when inserted into the enclosure.

We used 1x45mm sized heat shrink tubes. You can use
the side of the soldering iron to heat up and shrink the
tubes around the solder connections.

Use a flush diagonal cutter to remove the unused third
© Adafruit Industries

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

Page 15 of 29

pin.

© Adafruit Industries

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

Page 16 of 29

Wire Boards
The LED matrix is free-wired to the Adafruit Feather to
make the assembly as slim as possible.

We can solder the wires from the bottom side of the
boards to make them easy to attach to each other. This
also gives room for the battery to fit between the two
boards.

We used 30AWG silicone coated wires to provide
flexibility and durable handling.

Solder Slide Switch
Next, we'll solder the slide switch to the EN and GND
pin on the top of the Feather board.

© Adafruit Industries

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

Page 17 of 29

Solder Accelerometer
The LIS3DH is soldered in the same way, with the wires
underneath the board.

We can then solder connections for SDA, SCL, GND and
3V to the top side of the Feather board.

Later, will mount it to the top of the Feather board with
the help of a 3d printed mount.

© Adafruit Industries

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

Page 18 of 29

Accelerometer Mount
This 3d printed mount will secure the LIS3DH to the top
of the Feather board.

We'll use M2x4mm screws to connect the LIS3DH to the
3d printed mount.

Next we'll fasten the the 3d printed mount on top of the
feather board. We'll use the two mounts close to the
prototyping area.

Use two M2x8mm screws so they can reach through
the Feather and in the 3d printed spacer.

© Adafruit Industries

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

Page 19 of 29

Spacer
This 3d printed spacer will join the Feather and
CharliePlex board together.

This will allow a 350mAh lipo battery to safely sit
between the two boards, with just enough space to fit
the wires from both boards.

Attach boards
First fasten the two screws on the accelerometer mount.
Leave the other two screws unfastened until we fit the
battery cable through the opening on the spacer.

© Adafruit Industries

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

Page 20 of 29

Bundle wires
Next, we'll want to tidy up the wires by bundling them up
with a small pieces of kapton tape. This will make it
easier to arrange the wires allowing the battery to fit
between the boards.

© Adafruit Industries

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

Page 21 of 29

Mount Battery
Now we can pass the battery wire through the opening
on the side of the 3d printed spacer.

Reference the picture so the wires wraps around the
board with enough space to plug into the Feather board.

© Adafruit Industries

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

Page 22 of 29

Attach Spacer
Now we can fasten the other two M2x8mm screws
through the Feather board. Tighten them in from the top
of the Feather, into the spacer.

The Feather board should now be firmly attached to the
3d printed spacer. The CharliePlex board will attach
after we mount it into the enclosure.

© Adafruit Industries

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

Page 23 of 29

Plug in battery
First we'll need to plug in the battery before we insert it
into the enclosure.

Reference the picture to route the wire along the side of
the spacer, to the JST PH-2 connector on the Feather
board.

If the LEDs light up, use the slide switch to turn the
circuit off.

Mount inside enclosure
Align the CharliePlex to the enclosure, making sure the
display cutout matches the LED matrix. (We're showing
the box enclosure, but all of the design variants mount
the same).

Tuck wires

© Adafruit Industries

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

Page 24 of 29

One side of the enclosure is wider to accommodate the
JST connection on the side of the Feather board.
Carefully fit the wires inside the enclosure. You can use
tweezers to arrange them next to the spacer.

Align mounting holes
Flip the enclosure to the display cutout. Move the
CharliePlex board so it aligns with the mounting holes
on the front face of the enclosure.

Use four M2x8mm screws on the front face of the
enclosure to attach the CharliePlex to the spacer.

Face cover
To hide the screws on the front of the enclosure, we
used a small amount of tac to adhere one of the printed
face cover.

© Adafruit Industries

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

Page 25 of 29

© Adafruit Industries

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

Page 26 of 29

Slide Switch Tolerances
To adjust for different printer tolerances and to ensure
the slide switch tightly press fits into the enclosure, we
might need to gently spread the two metal sides. This
will help the slide switch snap fit into place on the
printed part.

Slide Switch Mount
First, carefully flip the slide the switch into the middle
position. This will make it easier to pass through the
opening in the enclosure.

Now we can insert the slide switch into the mount. Tilt
the slide switch at an angle and then press fit into place.

Attach Lid
The lid press fits onto the enclosure by aligning the nubs
on both parts. Insert the protruding nubs on the lid into
its matching cavity on the enclosure.

Gimbal Assemble
To assemble the gimbal version of the enclosure, simply
insert the points on the sides, one by one, at an angle.

Use by holding the outer ring close, but not over the
points that attach them to each other ring. Gently move
in an up and down motion to spin the center of the
gimbal!

© Adafruit Industries

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

Page 27 of 29

Teeter totter
The boards are offset so the display can mount to the
center of the enclosure. This make it unstable on a
dome, making the "grains of sand" bounce in
unexpected fun ways.

Simply push on the side close to the USB port to
randomly spin the enclosure on a flat table.

© Adafruit Industries

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

Page 28 of 29

© Adafruit Industries

Last Updated: 2018-02-06 04:55:12 PM UTC

Page 29 of 29



Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.4
Linearized                      : No
Title                           : 
Creator                         : wkhtmltopdf 0.12.4
Producer                        : Qt 4.8.7
Create Date                     : 2018:02:06 16:55:16Z
Page Count                      : 29
Page Mode                       : UseOutlines
EXIF Metadata provided by EXIF.tools

Navigation menu