Project tutorial
Motion Lamp with Arduino 101

Motion Lamp with Arduino 101 © GPL3+

Motion lamp is a prototype that allows controlling an RGB light lamp through movement with Arduino 101 and NeoPixel Strip.

  • 1,805 views
  • 0 comments
  • 3 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)

About this project

Introduction

One of the most interesting facets that brings us Arduino 101, is the possibility of working with a gyroscope and an accelerometer.

These two sensors give us the possibility to locate a device in 3 diemsions in Arduino. With this project we intend to experience one of the possibilities when it comes to interacting with objects in our house, in this case a color lamp.

The idea is to assign each space dimension (X, Y, Z) to a color component. As we will see throughout this article, the chosen color space is the HSV as it provides Hue, Saturation and Value independently.

We do not know how we will interact with objects, appliances and electrical appliances in the future. What we do know is that thanks to Open Hardware and Open Software, we can investigate and imagine what that future might look like.

Finally, we have to say that this is just a prototype, we would like to be able to develop some device that would allow us any light of the house with a single remote control. We will continue investigating, of that there is no doubt, and everything that we discover we will share it with you :)

The final objective of this project is to be able to investigate how an IMU unit can be used to handle the operation of different objects of our daily life.

The components

In this project we have used an Arduino 101, a NeoPixel Strip (only 11 + 1 LEDs) and a transparent cube printed in 3D.

The software

To program the sketch we have used the Arduino IDE. Regarding the libraries we have used the free Intel CurieIMU, the algorithm adaptation of Madgwick AHRS for Arduino, the Adafruit library to control a NeoPixel Strip and the library to convert between RGBConverter color spaces.

The device

Consists of cube with 5 visible faces. Inside will counter a NeoPixel Strip with 11 pixels (you can use another number of pixels) and a strip of a single pixel.

By moving the cube, we will be able to change the color of the NeoPixel Strip according to the components of the HSV color space. When we have chosen the right color, we can leave it fixed with a movement of the cube.

The strip of a single pixel, will notify us which color component we are changing and when the color has stayed fixed.

Step 1: Investigating the Madgwick Algorithm

You can find all the information on how to use the algorithm of Madgwick with an Arduino 101 in the official web of Arduino.

In addition you have at your disposal an extensive information of what it does and how it does it in case you want to investigate.

Basically, this algorithm calculates the Euler angles from the information obtained by the gyroscope and the accelerometer. These angles are used to determine the orientation of the object in the X, Y and Z axes.

The three angles with which we are going to work are Yaw, Roll and Pitch.

  • Yaw represents the angle of rotation around the Z axis.
  • Roll represents the angle of rotation around the X axis.
  • Pitch represents the angle of rotation around the Y axis.

While Yaw angle ranges from 0 ° to 360 °, Roll and Pitch angles range from -180 ° to 180 ° according to the original Arduino implementation.

One of the most complicated things, which I have not yet come to understand, is the range of values ​​of each angle. For lack of time, I have not been able to thoroughly investigate what I have been able to obtain is the ranges through testing.

Yaw

It is the simplest, it goes from 0º to 360º so no conversion is necessary.

Roll

If you place the USB connector on the front, and rotate around the axis would go parallel to that connector, when turning and put the analog pins in horizontal would have a value of -90º.

If you turn it completely and turn it upside down, this angle is 180º.

If you now position the digital pins horizontally, the angle will be 90 °.

Therefore, the range goes from 0º, to -90º, to 180º, to 90º and 0º. This implies that it is displaced -180 °.

Pitch

Perhaps it is the most complex angle with respect to the range of values. Imagine an airplane and start from the rest position, horizontal position 0º.

If you climb the nose of the airplane you will advance from 0º to -90º, vertical position with the nose upwards. If you continue to move horizontally but face down, you will go to 0º. Therefore in this sense, the range of values ​​goes from 0º, to -90º and 0º.

If you lower the nose of the airplane you will advance from 0º to 90º, vertical position with the nose downwards. If you continue to move horizontally but face down, you will return to 0º. Therefore in this sense, the range of values goes from 0º, a90º and 0º.

With all this, we were already clear how we could use those ranges to change the color of the lamp. Now it was about implementing the NeoPixel Strip inside the cube.

Step 2: Implementing the NeoPixel Strip

The option chosen by us has been to use the library provided by Adafruit.

We have experience in using this type of addressable LEDs in different projects.

We already presented a project to the contest The Arduino Internet of Holiday Things, where we are third by the way :), and we use 3 addressable LEDs to show the state of the device.

In addition to this, we have made a chapter of our podcast in Spanish (La Tecnología para Todos) where we talk about how to use NeoPixel to decorate a Christmas tree.

We had different options when programming the NeoPixel, but we have finally opted for the Adafruit library.

It does not have much mystery, it is a very simple library and it has not really been complicated to implement it in this project.

As a matter of fact, when working in the RGB color space, the number of components and the dimensions of the space coincided, 3.

The disadvantage was that when doing the first tests and associating a dimension to each RGB component, the results were not satisfactory.

For this reason we decided to use another color space, the HSV. This color space allows us to modify the Hue, Saturation and Value separately obtaining quite satisfactory results.

The relationship between the dimensions of space and the color components are as follows:

  • Angle Yaw: associated to the component Value
  • Angulo Roll: associated with the Saturation component
  • Angle Pitch: associated with the Hue component

Therefore, whenever we rotate the cube around the Z axis we will be changing the color of the Motion Lamp. When we rotate the cube around the X axis we will be changing the saturation and a rotation around the Y axis will change the illumination.

The Adafruit library for controlling NeoPixel Strip uses RGB color space. However, as I said earlier, the results when using the HSV color space allow us to independently change the hue, saturation and intensity.

Therefore, we had to add a library that allows us to do these conversions. The library is called RGBConverter and you can find it in GitHub.

The idea is to transform each angle of rotation in the 3D space into a value of the HSV color space.

In the library for each component a range of 0 to 1 is used, so a mapping must be done between the rotation angles and the values of each component.

Once the value of Hue, Saturation and Value is obtained, the conversion is made to the RGB color space to be applied to the NeoPixel Strip.

Because it is uncomfortable to rotate around the X axis and the Y axis, we have made the decision to reduce the range where a change in rotation is detected on these axes.

For the Pitch angle, we detect from -90º to 90º what gives us a range of 180º.

For the Roll angle, we detect from -90 ° to 90 ° which gives us a range of 180 °.

To work in the range of 0º to 180º, we simply add 90º to the obtained angle.

All this you can see in the code associated with this project. Now let's look at the hardware-level implementation and the logic for the different states.

Step 3: Mounting the circuit

The circuit is very simple, we will have 2 NeoPixel Strip. One with 11 pixels and another with 1 pixel.

The two strips will share 5V power provided by the Arduino 101. They will be connected to ground and to two different digital pins.

The 11-pixel NeoPixel Strip will be connected to the digital pin 6 and the 1-pixel pin to the digital pin 7.

Everything will go inside the printed cube, where the Arduino fits perfectly. We have taken care to leave two holes to connect the USB port and external power.

In the following versions we will take into account the heat that accumulates inside the printed cube and we will make a ventilation holes :).

The 11-pixel strip does the same as it sits inside the cube. However, the strip of a single pixel should be stuck on the cover.

This will allow us to know at all times in what state we are. Later we will see the different states in which we can be.

And with this we would have finished the assembly. If you can think of any improvement, please share it with all of us, sharing is loving;)

Step 3: The code

In order to change the value of the components we had a doubt, change all the components at once with a free movement or change each component separately.

In the end we have chosen the second option for ease of use of the device. So, we are going to have 4 states.

  • State 1: it is this state we can change the value of the components H (Hue).
  • State 2: this state we can change the value of the components S (Saturation).
  • State 3: this state we can change the value of the components V (Value).
  • State 4: we leave fixed the chosen color.

The question now is, how do we change state? To do this, we have chosen the detection implementation of a tap detection that comes as an example with the CurieIMU library.

You have the example very well explained in the Arduino web. We have only adapted and integrated it into the general sketch of this project.

The only thing to keep in mind is the sensitivity. First, let's work with 2G for the accelerometer range.

The detection threshold we have left as it is in example 750 and for now has given us our results.

If you lower this parameter you will have more sensitivity but you may have false positives.

Due to the difficulty of knowing in what state we are, we decided to add a pixel to inform us.

The pixel will represent the 4 states as follows:

  • State 1: turns red
  • State 2: turns green
  • State 3: turns blue
  • State 4: turns off

With this we would have finished the whole logic of the project. You can check all the code in the corresponding section of this article.

Step 4: Experimentation

Let's start by testing each of the states. We recorded a video to show how the device would work.

We still have some false positive and negative when detecting taps. Little by little we will improve the project.

Conclusions

Being able to investigate and design projects involving components such as an IMU unit or the BLE was unthinkable not so long ago.

However, the advance of technology and above all, Open Hardware, has given us all the opportunity to imagine what the devices of the future will be like.

However, this requires two things. The first is that we take time to investigate these components and algorithms, often removing time from other hobbies or obligations.

The second is that we decide to share it. It is important that little by little we are transmitting all this information to the rest of the world and the only way is to open our projects and leave them free.

Any questions or suggestions please, leave them below in the comments. Thank you very much :)

Code

Motion Lamp with Arduino 101Arduino
It is the main sketch where the whole logic of the project is implemented.
#include <CurieIMU.h>
#include <MadgwickAHRS.h>
#include <Adafruit_NeoPixel.h>
#include <RGBConverter.h>

#define PIN       6  // 11 pixels NeoPixel Strip
#define PIN1      7  // 1 pixel NeoPixel Strip
#define NUMPIXELS 11 // Numer of píxels
#define SAMPLE_RATE 25 // Sampling rate for accelerometer and gyroscope

// Madgwick configuration
Madgwick filter;
unsigned long microsPerReading, microsPrevious;
float accelScale, gyroScale;

// NeoPixel configuration
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixelsStatus = Adafruit_NeoPixel(1, 7, NEO_GRB + NEO_KHZ800);

// Color spaces
RGBConverter rgbConverter;
double h = 1;
double s = 1;
double v = 1;
byte rgb[3];

// Status Motion Lamp
// State 0 -> Select Hue - Pitch
// State 1 -> Select Saturation - Roll
// State 2 -> Select Value - Yaw
// State 3 -> Fix color
volatile int statusLamp = 0;

void setup() {
  Serial.begin(9600);

  // start the IMU and filter
  CurieIMU.begin();
  CurieIMU.setGyroRate(SAMPLE_RATE);
  CurieIMU.setAccelerometerRate(SAMPLE_RATE);
  filter.begin(SAMPLE_RATE);

  // Set the accelerometer range to 2G
  CurieIMU.setAccelerometerRange(2);
  // Set the gyroscope range to 250 degrees/second
  CurieIMU.setGyroRange(250);

  CurieIMU.autoCalibrateAccelerometerOffset(X_AXIS, 0);
  CurieIMU.autoCalibrateAccelerometerOffset(Y_AXIS, 0);
  CurieIMU.autoCalibrateAccelerometerOffset(Z_AXIS, 1);
  CurieIMU.autoCalibrateGyroOffset();

  CurieIMU.attachInterrupt(eventCallback);
  CurieIMU.setDetectionThreshold(CURIE_IMU_TAP, 950);
  CurieIMU.interrupts(CURIE_IMU_TAP);

  // initialize variables to pace updates to correct rate
  microsPerReading = 1000000 / SAMPLE_RATE;
  microsPrevious = micros();

  // Init NeoPixel 11
  pixels.begin();
  pixels.show();

  // Init NeoPixel 1
  pixelsStatus.begin();
  pixels.show();

  // Show status in px
  setStatusPixel(statusLamp);
}

void loop() {
  int aix, aiy, aiz; //accelerometer
  int gix, giy, giz;
  float ax, ay, az;
  float gx, gy, gz;
  float roll, pitch, yaw;
  static unsigned long microsNow;

  // check if it's time to read data and update the filter
  microsNow = micros();
  if (microsNow - microsPrevious >= microsPerReading) {

    // read raw data from CurieIMU
    CurieIMU.readMotionSensor(aix, aiy, aiz, gix, giy, giz);

    // convert from raw data to gravity and degrees/second units
    ax = convertRawAcceleration(aix);
    ay = convertRawAcceleration(aiy);
    az = convertRawAcceleration(aiz);
    gx = convertRawGyro(gix);
    gy = convertRawGyro(giy);
    gz = convertRawGyro(giz);

    // update the filter, which computes orientation
    filter.updateIMU(gx, gy, gz, ax, ay, az);

    // print the heading, pitch and roll
    roll = filter.getRoll();
    pitch = filter.getPitch();
    yaw = filter.getYaw();

    // increment previous time, so we keep proper pace
    microsPrevious = microsPrevious + microsPerReading;

    // Only if change Hue, Saturation or Value
    if (statusLamp < 3)
    {
      // pitch only -90º to 90º = 180º
      // State 0 -> select Hue
      if (pitch >= -90 && pitch <= 90 && statusLamp == 0)
      {
        // Transform angle
        pitch = pitch + 90;
        // Obtains color cordinates from angles
        h = pitch / 180.0;
      }

      // Angles restrictions
      // roll only -90º to 90º = 180º
      // State 1 -> select Saturation
      if (roll >= -90 && roll <= 90 && statusLamp == 1)
      {
        // Transform angle
        roll = roll + 90;
        // Obtains color cordinates from angles
        s = roll / 180.0;
      }

      // State 2 -> select Value
      if (statusLamp == 2)
      {
        // yaw 0º to 360º
        v = yaw / 360.0;
      }

      // Convert to rgb
      rgbConverter.hsvToRgb(h, s, v, rgb);
      
      /*
        Serial.print("Color: ");
        Serial.print(h);
        Serial.print(" - ");
        Serial.print(s);
        Serial.print(" - ");
        Serial.print(v);
        Serial.println(" ");


        Serial.print("Orientation: ");
        Serial.print(yaw);
        Serial.print(" ");
        Serial.print(pitch);
        Serial.print(" ");
        Serial.println(roll); */

      // Change color of the pixels
      for (int px = 0; px < NUMPIXELS; px++)
      {
        pixels.setPixelColor(px, pixels.Color(rgb[0], rgb[1], rgb[2]));
        pixels.show();
      }
    }

    // Show status in px
    setStatusPixel(statusLamp);
  }
}

float convertRawAcceleration(int aRaw) {
  // since we are using 2G range
  // -2g maps to a raw value of -32768
  // +2g maps to a raw value of 32767

  float a = (aRaw * 2.0) / 32768.0;
  return a;
}

float convertRawGyro(int gRaw) {
  // since we are using 250 degrees/seconds range
  // -250 maps to a raw value of -32768
  // +250 maps to a raw value of 32767

  float g = (gRaw * 250.0) / 32768.0;
  return g;
}

static void eventCallback()
{
  // Detect tap in all axis
  if (CurieIMU.getInterruptStatus(CURIE_IMU_TAP)) {
    Serial.print("Tap detected statusLamp: ");
    Serial.println(statusLamp);

    // Change state
    statusLamp++;

    // Init state
    if (statusLamp > 3)
    {
      statusLamp = 0;
    }
  }
}

void setStatusPixel(int statusPx)
{
  switch (statusPx)
  {
    case 0:
      pixelsStatus.setPixelColor(0, pixelsStatus.Color(150, 0, 0));
      pixelsStatus.show();
      break;
    case 1:
      pixelsStatus.setPixelColor(0, pixelsStatus.Color(0, 150, 0));
      pixelsStatus.show();
      break;
    case 2:
      pixelsStatus.setPixelColor(0, pixelsStatus.Color(0, 0, 150));
      pixelsStatus.show();
      break;
    case 3:
      pixelsStatus.setPixelColor(0, pixelsStatus.Color(0, 0, 0));
      pixelsStatus.show();
      break;

  }
}
CurieIMU
Library to obtain information from the IMU unit. We use it to get the motion sensor and to detect the tap.
MadgwickAHRS
Library to get the Euler angles and determine the cube spatial position.
Adafruit NeoPixel
This library controls the NeoPixel Strip.
RGBConverter
This library converts between HSV and RGB color spaces.

Custom parts and enclosures

Cube 3D
This is the cube design we have used for this project.

Schematics

Motiom Lamp Schematics
Conections Arduino 101 with NeoPixel Strip
Circuit 101 rzna1q8t1c

Comments

Similar projects you might like

Arduino 101 BLE RGB Lamp

Project tutorial by Gustavo Reynaga

  • 2,400 views
  • 2 comments
  • 10 respects

Motion Detection Lamp

Project tutorial by Izzati Azryna

  • 6,264 views
  • 0 comments
  • 11 respects

My First Arduino 101 Car Controlled by BLE

Project tutorial by Alexis Santiago Allende

  • 2,545 views
  • 2 comments
  • 12 respects

Smart Garbage Monitoring System Using Arduino 101

Project tutorial by Technovation

  • 35,581 views
  • 13 comments
  • 52 respects

Arduino 101 - Intel Curie Pattern Matching Dress

Project tutorial by Kitty Yeung

  • 11,430 views
  • 17 comments
  • 57 respects

Control RGB LED by Dragging – Arduino 101 & App Inventor

Project tutorial by CAVEDU Education and DFRobot

  • 7,143 views
  • 0 comments
  • 14 respects
Add projectSign up / Login