# Dotstar + CircuitPython Digital Sand

## Overview

Something that has gotten popular recently is [Phil Burgess' _digital sand_ demo](../../../../animated-led-sand).&nbsp; It's been riffed on a few times now, and since I have been playing around with the Dotstar Featherwing, I thought I'd have a go at it as well.

I really wanted to do this in CircuitPython, partly as a learning exercise since I'm still fairly new to (Circuit)Python, and partly to see how far it could be pushed on a rather constrained environment like the SAMD21G18.

The code is a fairly literal port of Phil's code as listed in the Ruiz Brothers' [Animated LED Sand](../../../../animated-led-sand) learning guide. I did have to make some changes, and do some optimization to get it all to fit and to run at a reasonable speed. I'm pretty happy with how it turned out. I'll go over those optimization on the code page.

See the GIF below to see a short demo - its slower than C code, maybe better-called _LED Snow_ than sand?

![](https://cdn-learn.adafruit.com/assets/assets/000/050/330/medium800thumb/sensors_sand_demo.jpg?1516559394)

# Dotstar + CircuitPython Digital Sand

## An LSM303 FeatherWing

I wanted to build this project as compactly as possible, meaning I wanted it as a stack of feather/wing boards. And that meant I needed a accelerometer FeatherWing.&nbsp; Alas there isn't such a thing available so I had to make my own. Since I didn't want to undertake a custom wing PCB, I looked for a breakout I could put onto a protowing.

Whatever accelerometer breakout I chose would need to fit between the header strips. In the end I found that the Flora accelerometer breakouts would fit perfectly. I chose the LSM303 as it was a bit smaller and cheaper. Additionally, all I needed for this project was an accelerometer and the LSM303 is an accelerometer/magnetometer combo. For another project I will be using the LSM9DS0 Flora breakout which includes a gyroscope as well.

Since the LSM303 didn't have a CircuitPython library yet, it gave me the opportunity to delve into writing a CircuitPython I2C driver.

Since I mounted the Flora breakout directly on the protowing I carefully cut the traces between the header holes and their duplicate holes on the bottom of the board; just the few on either side near the center of the board where the breakout would be. This avoided the chance of any undesirable shorts/connections with the signals from the Feather. While I could have mounted it with some non-conductive foam/rubber (I've used bumpers/feet for this in other projects) I wanted a solid, unmoving mounting for this.

![](https://cdn-learn.adafruit.com/assets/assets/000/050/459/medium800/sensors_trace-cut.png?1516747074)

The Flora breakout is especially nice as there are just 3.3v, ground, SDA, and SCL to connect. That made wiring quick and simple.

Be very careful to get the LSM303 breakout centered on the wing, and with the X and Y axies aligned with the sides. I oriented the X axis along the length of the wing, with the Y axis aligned along the width.

Wiring is simply a matter of connecting 3v & ground to the breakout along with SDA and SCL. The Flora breakout boards are beautifully minimal.

![](https://cdn-learn.adafruit.com/assets/assets/000/050/324/medium800/sensors_accel_top.jpg?1516416168)

![](https://cdn-learn.adafruit.com/assets/assets/000/050/325/medium800/sensors_accel_bottom.jpg?1516416188)

## API

The API follows the unified sensor approach used in Adafruit's sensor APIs, both in C as well as CircuitPython. This means using standardized naming, as well as implementing as properties in CircuitPython.&nbsp; Here's an example that uses the core of the API: reading raw and processed accelerometer and magnetometer values.

https://github.com/adafruit/Adafruit_CircuitPython_LSM303/blob/master/examples/raw_and_cooked/raw_and_cooked.py

# Dotstar + CircuitPython Digital Sand

## An aside: shorty stackers

I really like the shorty headers from Adafruit for working with Feather/wings. They save quite a bit of space between boards that would otherwise be wasted and just make the assembly bigger than needed. After trying them on a few projects, I now keep a drawer full in stock for use in most, if not all, my Feather based work.

### Short Headers Kit for Feather - 12-pin + 16-pin Female Headers

[Short Headers Kit for Feather - 12-pin + 16-pin Female Headers](https://www.adafruit.com/product/2940)
These two&nbsp; **Short** &nbsp; **Female&nbsp;Headers** &nbsp;alone are, well, lonely. But pair them with any of our&nbsp;[Feather](https://www.adafruit.com/categories/777)&nbsp;boards and you're in business!

These headers are particularly cute and...

Out of Stock
[Buy Now](https://www.adafruit.com/product/2940)
[Related Guides to the Product](https://learn.adafruit.com/products/2940/guides)
![Angled shot of two stacking headers.](https://cdn-shop.adafruit.com/640x480/2940-01.jpg)

### Short Feather Male Headers - 12-pin and 16-pin Male Header Set

[Short Feather Male Headers - 12-pin and 16-pin Male Header Set](https://www.adafruit.com/product/3002)
These two&nbsp; **Short** &nbsp; **Male&nbsp;Headers** &nbsp;alone are, well, lonely. But pair them with any of our&nbsp;[Feather](https://www.adafruit.com/categories/777)&nbsp;boards and you're in business!

<p...></p...>In Stock
[Buy Now](https://www.adafruit.com/product/3002)
[Related Guides to the Product](https://learn.adafruit.com/products/3002/guides)
![Angled shot of a Short Feather Male Headers - 12-pin and 16-pin Male Header Set. ](https://cdn-shop.adafruit.com/640x480/3002-00.jpg)

There are great if you just need to stack a wing on top of a Feather, but to go beyond one wing you need stacking headers. However, it seems that short stacking headers aren't a thing. So I did as makers do when they can't find what they want... I made my own. This project turned into a series of these "make it yourself" sub-projects.

First, do like you would when using the male headers. Before you start soldering, slip the pieces of female header in place from the top. Be sure that each header is on the same side of the matching pins that go through the board so that the spacing will be correct. I.e. **don't** put both on the outside or on the inside; you'll want one on the outside and the other on the inside. It doesn't matter which is which.

![](https://cdn-learn.adafruit.com/assets/assets/000/050/327/medium800/sensors_inserting.jpg?1516477717)

![](https://cdn-learn.adafruit.com/assets/assets/000/050/328/medium800/sensors_seated.jpg?1516477749)

Now the tricky part. Using a iron with a thin tip (an SMT tip works well) carefully poke in between the female header strip and the board to heat the header pins. Then touch solder to them and let it wick into the hole and join the two pieces of header together.&nbsp; It takes a bit of practice but it's pretty easy to get a good connection. Be careful not to melt the header too much; you're bound to melt a bit here & there. It might look a bit nasty, but as long as it works, right?

![](https://cdn-learn.adafruit.com/assets/assets/000/050/329/medium800/sensors_soldered.jpg?1516477779)

# Dotstar + CircuitPython Digital Sand

## Assembly

![](https://cdn-learn.adafruit.com/assets/assets/000/050/326/medium800/sensors_assembled.jpg?1516416497)

Now the boards snap together: the Dotstar wing on top of the LSM303 wing, which is on top of the Feather M0.

That arrangement puts the accelerometer chip pretty much smack-dab in the middle of the entire thing. Which is exactly where it should be.

The white? I put a couple pieces of white label stickers (the ones I use to label the drawers in my parts cabinets) over the Dotstars as a slight diffusion layer to make them show better on camera.

# Dotstar + CircuitPython Digital Sand

## Code

This is a fairly literal port of Phil's C code. Of course, management of the display is quite different since it's different hardware. The major optimizations are using an array of Boolean to track where grains are, and replacing as much multiplication and division as possible with bit shifting. Other than that it is a pretty faithful port of Phil's code. So much so that I've reused his comments verbatim.

The code is complex and not immediately obvious, but the comments do a good job of walking you through it.

It doesn't always act exactly as you might expect, but keep in mind that this isn't a physics simulation, it's more of an approximation. The rules are quite simple and, as Phil said on Show & Tell, it's an example of fairly complex emergent behavior.

I'm quite pleased and impressed with how much I could get CircuitPython to do and how well it performs given the speed and memory constraints it's running under.

## Division & multiplication by powers of 2

The optimizations around division and multiplication are based on the mapping between base 10 numbers and binary numbers. Especially where 2 and powers of 2 are concerned.

If we take the number 10 and represent it in binary, we have 00001010 (we'll stick to 8 bits for these examples). If we divide 10 by 2 we get 5. In binary that's 00000101. So if we keep dividing by 2 (integer division)

`10        00001010`  
` 5        00000101`  
` 2        00000010`  
` 1        00000001`

In decimal it's a free for all without any noticeable pattern. But look at the binary representations. Each time we divide by 2, the digits move one place to the right (with the rightmost one being discarded and a 0 begin shifted into the left.

Another thing to notice is, that since each shift right is a dividing by 2, shifting multiple times divides by a power of 2. Shifting twice is dividing by 4 (2 squared), 3 times is dividing by 8 (2 cubed), etc.&nbsp; In general, shifting right n digits is dividing by 2 to the power of n.

Multiplying by powers of 2 works the same way, but by shifting left rather than right.

So why is this relevant? Shifting is a lot faster than division. A good compiler (like GCC used by the Arduino IDE) or a hardware integer math unit will make this optimization (i.e. shifting for factors that are powers of 2)&nbsp; without the programmer having to worry about it. However, CircuitPython and MicroPython don't.

This comes up quite a bit in this code. Grain positions are kept in a coordinate system that has 256 times the resolution of the pixel coordinate system. To get the pixel location of a grain, both x and y need to be divided by 256. Fortunately that's just shifting 8 times.

## Remove unnecessary code

One optimization that had a significant impact was getting rid of my Dotstar Featherwing library in favor of using the underlying Dotstar library directly. When it struck me that the display was just being updated all at once, in one place, getting rid of the library was an obvious win.&nbsp; Not only did it free memory, but it also removed a non-trivial amount of work that was being done each time through the loop.

Consider the Dotstar update code. In early versions I started by clearing the display out of habit. That's completely unnecessary when it's updating each pixel. Look for easy wins like this. The code should only do what it absolutely needs to. Anything more is wasting memory and/or time.

I expect I could get another jump in performance if I stripped down the Dotstar library and made a version customized for this application. One that did just what is required.

## Optimize carefully

This was a good exercise in optimization. The first version of the python code could barely move 5 grains around in anything approaching a smooth speed. The current version does a reasonable job with 10 grains.

I do want to warn you about trying to optimize in an all or nothing way. Be very deliberate and cautious. Pick one thing to optimize and do it.&nbsp; Measure the results. If it didn't help significantly, remove it and try something else. The thing about optimizing is that it generally makes the code harder to understand so only do optimizations that make a difference.

## The code

Here is the code in its entirety.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Digital_Sand_Dotstar_Circuitpython_Edition/code.py

# Dotstar + CircuitPython Digital Sand

## Downloads

[Download the LSM303 CircuitPython library](https://github.com/adafruit/Adafruit_CircuitPython_LSM303)
[Download the digital sand demo code](https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/master/Digital_Sand_Dotstar_Circuitpython_Edition)
You will also need the CircuitPython library bundle that matches the version of CircuitPython you are using (I used 2.2.0 when working on this).

After downloading and unzipping, you can just drag the `adafruit_lsm303.py` file into `CIRCUITPY/lib`.

For the demo code, just rename it `main.py` and drag onto `CIRCUITPY`.


## Featured Products

### Adafruit Feather M0 Express

[Adafruit Feather M0 Express](https://www.adafruit.com/product/3403)
At the Feather M0's heart is an ATSAMD21G18 ARM Cortex M0+ processor, clocked at 48 MHz and at 3.3V logic, the same one used in the new&nbsp;[Arduino Zero](https://www.adafruit.com/products/2843). This chip has a whopping 256K of FLASH (8x more than the Atmega328 or 32u4) and...

Out of Stock
[Buy Now](https://www.adafruit.com/product/3403)
[Related Guides to the Product](https://learn.adafruit.com/products/3403/guides)
### Adafruit DotStar FeatherWing - 6 x 12 RGB LEDs

[Adafruit DotStar FeatherWing - 6 x 12 RGB LEDs](https://www.adafruit.com/product/3449)
A Feather board without ambition is a Feather board without FeatherWings! This is the **DotStar FeatherWing, a 6x12 RGB LED Add-on For All Feather Boards**!&nbsp;Using our&nbsp;[Feather Stacking Headers](https://www.adafruit.com/products/2830)&nbsp;or&nbsp;<a...></a...>

No Longer Stocked
[Buy Now](https://www.adafruit.com/product/3449)
[Related Guides to the Product](https://learn.adafruit.com/products/3449/guides)
### FLORA Accelerometer/Compass Sensor - LSM303

[FLORA Accelerometer/Compass Sensor - LSM303](https://www.adafruit.com/product/1247)
Add motion and direction sensing to your wearable FLORA project with this high precision 3-axis Accelerometer+Compass sensor. Inside are two sensors, one is a classic 3-axis accelerometer, which can tell you which direction is down towards the Earth (by measuring gravity) or how fast the board...

No Longer Stocked
[Buy Now](https://www.adafruit.com/product/1247)
[Related Guides to the Product](https://learn.adafruit.com/products/1247/guides)
### FeatherWing Proto - Prototyping Add-on For All Feather Boards

[FeatherWing Proto - Prototyping Add-on For All Feather Boards](https://www.adafruit.com/product/2884)
A Feather board without ambition is a Feather board without FeatherWings!

This is the **FeatherWing Proto** - a prototyping add-on for all Feather boards. Using our [Feather Stacking Headers](https://www.adafruit.com/products/2830) or <a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/2884)
[Related Guides to the Product](https://learn.adafruit.com/products/2884/guides)

## Related Guides

- [Adafruit Feather M0 Express](https://learn.adafruit.com/adafruit-feather-m0-express-designed-for-circuit-python-circuitpython.md)
- [Using DS18B20 Temperature Sensor with CircuitPython](https://learn.adafruit.com/using-ds18b20-temperature-sensor-with-circuitpython.md)
- [CircuitPython 101: Basic Builtin Data Structures](https://learn.adafruit.com/basic-datastructures-in-circuitpython.md)
- [CircuitPython Hardware: PCA9685 DC Motor & Stepper Driver](https://learn.adafruit.com/micropython-hardware-pca9685-dc-motor-and-stepper-driver.md)
- [Deco Two-Key Feather Macro Pad](https://learn.adafruit.com/deco-two-key-keypad-macropad-circuitpython-feather.md)
- [Homefruit FeatherWing Tester](https://learn.adafruit.com/homefruit-featherwing-tester.md)
- [¡Bienvenido a CircuitPython!](https://learn.adafruit.com/bienvenido-a-circuitpython-2.md)
- [RPi Stock Alert Alarm](https://learn.adafruit.com/rpi-stock-alert-alarm.md)
- [Blahaj Alarm and Lamp](https://learn.adafruit.com/blahaj-alarm.md)
- [Using LoraWAN and The Things Network with CircuitPython](https://learn.adafruit.com/using-lorawan-and-the-things-network-with-circuitpython.md)
- [CircuitPython Basics: Analog Inputs & Outputs](https://learn.adafruit.com/circuitpython-basics-analog-inputs-and-outputs.md)
- [Square NeoPixel Display with Black LED Acrylic](https://learn.adafruit.com/sqaure-neopixel-display-with-black-led-acrylic.md)
- [Extending CircuitPython: An Introduction](https://learn.adafruit.com/extending-circuitpython.md)
- [How to Program SAMD Bootloaders](https://learn.adafruit.com/how-to-program-samd-bootloaders.md)
- [CircuitPython Hardware: ILI9341 TFT & FeatherWing](https://learn.adafruit.com/micropython-hardware-ili9341-tft-and-featherwing.md)
- [CircuitPython Hardware: SSD1306 OLED Display](https://learn.adafruit.com/micropython-hardware-ssd1306-oled-display.md)
