Final Project Development #1

Neopixel control with accelerometer

Speed change according to device rotation.
Speed change according to device rotation.
Changing brightness Y axis
Changing brightness Y axis.

For the first stage of my project development, I decided to test the Neopixels attached to some kind of input. I wanted to get a sense of how they behaved, what was their limits and possibilities. I found the Neopixels to be absolutely mesmerizing, bright as hell, and made me fall in love with light again in the process. Though I am mostly concerned about being able to perform my original idea due to the planet’s current situation, working through this process will clear up a lot of things in terms of power usage, communication, timing, and generative pattern creation in general.

Accelerometer

I decided to implement an accelerometer to have some kind of control over the lights in realtime. Fortunately, I had an Adafruit LIS3DH laying around and the documentation for it is good.

To use it you require the Adafruit LIS3DH and Adafruit Unified Sensor from the Arduino Library Manager. I decided to use I2C wiring since it required fewer cables than through SPA.

I2C Wiring

Use this wiring if you want to connect via I2C interface ( Taken from the documentation website)

  • Connect Vin to the power supply, 3-5V is fine. Use the same voltage that the microcontroller logic is based off of.
  • Connect GND to common power/data ground
  • Connect the SCL pin to the I2C clock SCL pin on your Arduino. On an UNO & ‘328 based Arduino, this is also known as A5,.
  • Connect the SDA pin to the I2C data SDA pin on your Arduino. On an UNO & ‘328 based Arduino, this is also known as A4.
LIS3DH Accelerometer.
LIS3DH Accelerometer.

The accelerometer’s main components seem to be the chip in the center of the breakout board made by Adafruit, which also takes care of things like regulating voltage and logic level-shifting. I did find the LIS3DH online datasheet in order to attach it later by itself.

Readings

The Adafruit LIS3DH library has an example called `acceldemo’ which has a clean code with raw readings and normalized values in an m/s^2 format. After taking away the clutter I got the values I needed. The minimum value is -9.8~-10 and the max value is 9.8~10 for each axis, with 0 being the value when the sensor is parallel to the direction of the axis. I later applied the values from the X axis as the animation speed for the Rainbow Cycle.

Accelerometer Readings
Accelerometer Readings

IMC

For testing purposes I used an Arduino UNO, which has an ATMega328 IMC. For my purposes, which would be to control 300+ LEDs, it seems the ATTiny can’t handle that much according to some forum posts. In this repository, for example, the user gets ~80 pixels at 3 bytes for LED. However I also found this article where the developer uses 1k RAM to handle 1000 Neopixels…so maybe with the ATTiny’s 1/2K RAM I can handle 500? Testing, testing, and testing. In any case, I decided if the ATTiny85 is not enough the ATMega328 might be a good choice as it is 4 times more powerful. This video by Adafruit in which they describe their Trinket product was helpful for me to understand this difference.

ATTiny85

  • 8k flash
  • 1/2k ram

ATMega328

  • 32k flash
  • 2k flash

Animations

I believe the way to proceed with the animations is to think of them as generative systems. Storing images or gifs into the microcontroller would be memory- draining unless I attach an SD card to it and convert each of the images into byte arrays to later on load into it. However, this seems counter-intuitive with my main idea of having the sensors I attach control the parameters in the animation. So generative systems seems like the approach there.

What I’m thinking about now

  • Well, even though I liked the neopixels, the 8×8 grid made by Adafruit is too big, and the LEDs are a bit too far apart. I would love to fabricate my own when it’s possible. I also bought the strip but the strip is still too thick, it doesn’t make much of a difference.
  • I made some experiments with a magnifying glass and the pixels and it looked amazing. Maybe somewhere to branch off? Maybe a portable VJ System?
  • I still think power is going to be an issue. If I want this to be portable it’s going to need a bigger power source for each pixel I add. I want to test how much they would last with a Lithium-Ion battery. or a small powerbank?
  • I’m still confused about what I want it to do. Do I want to play with it? Do I think of it as a data viz device?
  • Maybe consider making it a screen? Not so much a cube but probably more of a frame which can update it’s animations using my wifi network? Example, if it’s night time dim it a bit and make soothing patterns vs morning bright patterns?
  • The tap feature in the accelerometer had me thinking. What if each of the people who have the device can sense when someone else tapped with intent? I could imagine sitting in my desk and having the cube having some calm pattern, and suddenly ripples come off of it. I would know the person who has the other cube sent a tap that changed the pattern and is communicating with me. This idea occured to me when people started calling me more often when all of this covid situation started, and I wanted to let them know everything was ok without having to actually answer my phone/ message them.

Code

Github Repository

The code below is the one called “lightingAccelerometer”. I have several other scripts I made in order to understand each library correctly.

// Neopixels
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

// ACCELEROMTER LIS3DH
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>

#define PIN 6
#define NUMPIXELS 64
#define PIXELTYPE NEO_GRB + NEO_KHZ800

#define CLICKTHRESHHOLD 80


//define the neopixels as pixels
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, PIXELTYPE);

// define the Accelerometer 12C as lis
Adafruit_LIS3DH lis = Adafruit_LIS3DH();

void setup() {

  Serial.begin(9600);

  // NEOPIXELS
  pixels.begin(); // Initialize the pixels
  pixels.setBrightness(10); // Set brightness. Max is 255
  pixels.show(); // Initialize all pixels to 'off'

  // ACCELEROMETER
  lis.begin(0x18); // begins the accelerometer
  lis.setClick(4, CLICKTHRESHHOLD); // this sets up the Accelerometer to read taps
  lis.setRange(LIS3DH_RANGE_4_G);   // 2, 4, 8 or 16 G!
}

void loop() {
  //Get a new accelerometer event, normalized 
  sensors_event_t event;
  lis.getEvent(&event);

  // Run the animation
  animation_rainbow(2, event.acceleration.x, event.acceleration.z);
}


void tapBrightness() {

}
// Fill the dots in a sequence one by one
void colorWipe(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < pixels.numPixels(); i++) {
    pixels.setPixelColor(i, c);
    pixels.show();
    delay(wait);
  }
}

void animation_rainbow(int wait, int accX, int accZ) {
  uint16_t i, j;
  int speedVal = abs(int(accX) + 1);
  int brightVal = abs(accZ * 20) + 10;
  speedVal = map(speedVal, 0, 10, 0, 7);
  pixels.setBrightness(brightVal); // Set brightness. Max is 255
  for (j = 0; j < 255; j++) {
    for (i = 0; i < pixels.numPixels(); i++) {
      pixels.setPixelColor(i, Wheel(((i + j)*speedVal) & 255));
    }
    pixels.show();
    delay(wait);//this delay makes it work a bit better
  }
}


// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) {
    //The lower the number, the more red it is
    // When it gets higher, Blue starts going upwards and Red goes down
    // When Wheelpos is 0, (255,0,0), when it's 85, (0,0,255)
    //RED TO BLUE
    return pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170) {
    WheelPos -= 85;
    //BLUE TO GREEN
    //0, (0,0,255) // 170, (0,255,0)
    return pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  //GREEN TO RED
  WheelPos -= 170;
  return pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}