Kid Proof Smart LEDStrip with SparkCore, Adafruit NeoPixels and SmartThings

I love to get my son excited every time I make or hack something so this time I created a SmartThings controlled Adafruit NeoPixels LED Strip for his bed. The plan was simple, LED Strip that can change colors and also be controlled by a simple and resistant button.

original

I will try to make it as simple as I can but please leave a comment if think that anything needs clarification.

On the Spark side:

You can find instructions on how to connect the SparkCore and the Neo Pixels on Spark’s community forums or you can follow this drawing provided by BDub 🙂

LGRR4CO-1

After you have all the connections ready:

  • Log in to Spark IDE
  • Assuming that the SparkCore is already paired with your account, select the core that you are going to use
  • Create a new APP
  • Paste following code
  • Go to Libraries, click on NEOPIXEL and include the library into your APP by clicking “INCLUDE IN APP” button.
// This #include statement was automatically added by the Spark IDE.
#include "neopixel/neopixel.h"

#include "math.h"


#define NEOPIXEL_PIN A1 //data pin for the neopixel LEDs
#define NEOPIXEL_COUNT 30 //# of LEDs
#define NEOPIXEL_TYPE WS2812B //type of LEDs

double lightState = 0; //variable for keeping track of the brightness value for the lamp.
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEOPIXEL_TYPE); //setup neopixel strip
uint32_t color = strip.Color(255, 255, 255); //set color of LEDs to imitate tungsten light via RGB 
int top = D3; // Define encoder pin A (The top pin)
int bottom = D4;// Define encoder pin B (The bottom pin)
int count = 0; // pre-initiate the count of the encoder to zero for debugging
int topVal = LOW; // pre-initiate the state of pin A to low
int topLast = LOW; // make its last value the same
int bottomVal = LOW; // make the bottom value low 
int inputPin = D5; //pin for reading value of pushbutton fucntionality
int led = D7; 
int point = 0; //index for array based dim value
int val = 0; //pushbutton state value
double brightness[] = {0.0, 0.11493046921721886, 0.250704760561023, 0.411103115457414, 0.6005913609774866, 0.8244452478422774, 1.0888973384030416, 1.401310534266823, 1.7703830749408593, 2.206390715088621, 2.721472823120924, 3.3299703667077485, 4.048825195437263, 4.898051737489861, 5.901294243353279, 7.086485091414136, 8.486622484036182, 10.14068918681737, 12.094737890656232, 14.403173415386968, 17.130267454225375, 20.351948032692242, 24.157913504308894, 28.654129941144937, 33.965781451816035, 40.24075556998111, 47.653760753874806, 56.41119063670973, 66.75687045889588, 78.97884567481861, 93.41740174324569, 110.4745383891715, 130.62516212044608, 154.43030862235864, 182.55276316941087, 215.77551395934938, 255.0};

/*
 * setup() - this function runs once when you turn your Arduino one
 * We set the motors pin to be an output (turning the pin high (+5v) or low (ground) (-))
 * rather than an input (checking whether a pin is high or low)
 */
void setup(){
    
    strip.begin();
    pinMode(top, INPUT);  // setup the pins as inputs
    pinMode(bottom, INPUT);
    pinMode(inputPin, INPUT);
    pinMode(led, OUTPUT);    
    
    for(int i = 0; i < strip.numPixels(); i++) {//set pixels to given RGB color
        strip.setPixelColor(i, color);
    }
    
    strip.setBrightness(lightState);//set LEDs to lightState(init. 0)
    strip.show();
    Spark.function("ledstate", turnTo); //Setup Spark turnTo function for remote dimmability
    Serial.begin(9600); //Serial for debugging
}


/*
 * loop() - this function will start after setup finishes and then repeat
 * we call a function called motorOnThenOff()
 */

void loop(){
    // Nothing here
}

/*
turnTo() is a handler function for the Spark.function "turnTo" turning the lamp to a given state.
The sole parameter "command" is supposed to be a string representation of an integer between 0 and 255 that
represents the state to write to the lamp.

*/
int turnTo(String command){
    String aux1 = getValue(command, ',', 0);
    String aux2 = getValue(command, ',', 1);
    String aux3 = getValue(command, ',', 2);
    String aux4 = getValue(command, ',', 3);
    
    int color1 = atoi(aux1.c_str());
    int color2 = atoi(aux2.c_str());
    int color3 = atoi(aux3.c_str());    
  
    int dim = atoi(aux4.c_str())*2.55; ;//parse the string into an integer
    
    color = strip.Color(color1, color2, color3); //set color of LEDs to imitate tungsten light via RGB 
    
    for(int i = 0; i < strip.numPixels(); i++) {//set LEDs to color
        strip.setPixelColor(i, color);
    }
    
    if (dim < 0 || dim > 255) {//if state out of bounds, return
        return 1;
    }
    
    lightState = dim;//else set local state equal to state
    point = indexRet(lightState);//set the index of the array to the closest brightness to current brightness #see indexRet().
    Serial.println("lightState: " + String(lightState));
    delay(10);
    strip.setBrightness(lightState);//set brightness to lightState    
    Serial.print("Color: (" + String(color1)); //print for debugging
    Serial.print(", " + String(color2)); //print for debugging
    Serial.print(", " + String(color3)); //print for debugging
    Serial.println("), dim value: " + String(dim)); //print for debugging    
    strip.show();
    
    if (dim == 0) {
        digitalWrite(led, LOW);    // Turn OFF the LED        
    } else {
        digitalWrite(led, HIGH);    // Turn ON the LED        
    }
    
    return dim;
}

/*
turnUp() is a function for turning the lamp up, using a point value to index the brightness state from the brightness array.

*/
int turnUp(){
    if (lightState < 247) {//if light less than full
        point++; //increment index
        lightState = brightness[point]; //set local state brightness to new value
    } else if (lightState >= 247){ // if light full
        point = 37; //set index to max value
        lightState = 255; //set local state to max value
    }
    for(int i = 0; i < strip.numPixels(); i++) {//set LEDs to color
        strip.setPixelColor(i, color);
    }
    
    strip.setBrightness(lightState); //set brightness of LEDs to lightState
    strip.show();
    Serial.println(lightState); //print for debugging
    return 1;
}

/*
turnDown() is a function for turning the lamp down, using a point value to index the brightness state from the brightness array.

*/
int turnDown(){
    if (lightState > 0) {//if lamp on
        point--;//decrement index
        lightState = brightness[point];//set local state brightness to new value
    } else if (lightState <= 0) { // if lamp off
        lightState = 0;//set local brightness state to 0
        point = 5; //set index to 5, boundary between visible light and none for dimming function.
    }
    
    for(int i = 0; i < strip.numPixels(); i++) {
        strip.setPixelColor(i, color);
    }
    
    strip.setBrightness(lightState);
    strip.show();
    Serial.println(lightState);
    return 1;
}

/*
indexRet() takes in a given lightState and returns the index of the array that is closest to a given brightness. 
This ensures a smooth transition between physical and cloud dimming; no sporadic jumps between brightness.
*/
int indexRet(double lightState){
    double index = log(1.578*lightState + 1); //conversion function for returning raw value between 0 and 6 (1/6 steps)
    int point = (int) 6*index; //adjustment for returning index between 0 and 36
    return point;
}

String getValue(String data, char separator, int index)
{
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length()-1;

  for(int i=0; i<=maxIndex && found<=index; i++){
    if(data.charAt(i)==separator || i==maxIndex){
        found++;
        strIndex[0] = strIndex[1]+1;
        strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }

  return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}

Now on SmartThings:

  • Log in to SmartThings IDE
  • Go to “My Device Type” and click on “+New SmartDevice”
  • In the following page click on tab “From code” and paste the code below
/**
 *  Spark Core
 *
 *  Author: juano23@gmail.com
 *  Date: 2014-01-23
 */
 
 preferences {
    input("deviceId", "text", title: "Device ID")
    input("token", "text", title: "Access Token")
}
 
 // for the UI
metadata {
    // Automatically generated. Make future change here.
	definition (name: "Spark LED Strip", author: "juano23@gmail.com") {
    	capability "Switch"
        capability "Switch Level"
        
		attribute "level", "string"        
		attribute "color", "string"
		attribute "whitestate", "string"        

		command "fade"
		command "red"
		command "blue"
		command "green"
		command "yellow"
		command "violet"
		command "orange"
		command "flash"
		command "cool"
		command "warm"
		command "white1"        
		command "white2"
		command "white3"
		command "white4"

	}

	// tile 
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: false) {
			state "on", label:'${name}', action:"switch.off", icon:"st.Lighting.light21", backgroundColor:"#79b821", nextState:"off"
			state "off", label:'${name}', action:"switch.on", icon:"st.Lighting.light21", backgroundColor:"#ffffff", nextState:"on"
		}
		controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
			state "level", label: "Dimmer", action:"switch level.setLevel"
		}
		standardTile("tempup", "device.switch", width: 1, height: 1, canChangeIcon: false, decoration: "flat") {
			state "cool", label:'${name}', action:"cool", icon:"st.thermostat.thermostat-down"
		}
		standardTile("tempdown", "device.switch", width: 1, height: 1, canChangeIcon: false, decoration: "flat") {
			state "warm", label:'${name}', action:"warm", icon:"st.thermostat.thermostat-up"
		}
        standardTile("White", "device.whitestate", height:1, width:1) {
            state "1", label:'White', action:"white1", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#ffffff", nextState: "1"
            state "2", label:'White 1', action:"white2", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#ffffff", nextState: "2"
            state "3", label:'White 2', action:"white3", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#ffffff", nextState: "3"
            state "4", label:'White 3', action:"white4", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#ffffff", nextState: "4"           
        }       
        standardTile("Red", "device.red", height:1, width:1) {
            state "default", label:'Red', action:"red", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#EE2C2C"
        }    
        standardTile("Yellow", "device.yellow", height:1, width:1) {
            state "default", label:'Yellow', action:"yellow", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#FFE303"
        }    
        standardTile("Orange", "device.orange", height:1, width:1) {
            state "default", label:'Orange', action:"orange", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#FFAA00"
        }    
        standardTile("Green", "device.green", height:1, width:1) {
            state "default", label:'Green', action:"green", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#66CD00"
        }    
        standardTile("Blue", "device.blue", height:1, width:1) {
            state "default", label:'Blue', action:"blue", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#0276FD"
        } 
        standardTile("Violet", "device.violet", height:1, width:1) {
            state "default", label:'Purple', action:"violet", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#BF5FFF"
        }     
        standardTile("Fade", "device.fade", height:1, width:1, decoration: "flat") {
            state "default", label:"Fade", action:"fade", unit:"", icon:"st.illuminance.illuminance.light", backgroundColor: "#ffffff"
        }          
		standardTile("Flash", "device.flash", inactiveLabel: false, decoration: "flat") {
			state "default", label:"Flash", action:"flash",  icon:"st.illuminance.illuminance.bright"
		}

		main(["switch"])
		details(["switch","tempdown","tempup", "levelSliderControl","White","Red","Orange","Yellow","Green","Blue","Violet"])
	}
}

def parse(String description) {
	log.error "This device does not support incoming events"
	return null
}

def white1() {
    sendEvent(name: "color", value: "255,255,255,");
	put();
}

def white2() {
    sendEvent(name: "color", value: "255,150,150,");
	put();
}

def white3() {
    sendEvent(name: "color", value: "255,150,100,");
	put();
}

def white4() {
    sendEvent(name: "color", value: "255,150,50,");   
	put();
}

def red() {
    sendEvent(name: "color", value: "255,0,0,")
	put();
}

def blue() {
    sendEvent(name: "color", value: "0,0,255,")
	put();
}

def green() {
    sendEvent(name: "color", value: "0,255,0,")
	put();
}

def yellow() {
    sendEvent(name: "color", value: "255,255,0,")
	put();
}

def orange() {
    sendEvent(name: "color", value: "255,100,0,")
	put();
}

def violet() {
    sendEvent(name: "color", value: "255,0,255,")
	put();
}

def warm() {
	def white = device.currentValue("whitestate") != null ? device.currentValue("whitestate") : "1"
	log.debug "Next white...$white"
    if (white == "1") {white2()};   
    if (white == "2") {white3()};  
	if (white == "3") {white4()};     
    if (white != "4") {sendEvent(name:"whitestate", value: Integer.parseInt(white) + 1)};        
    
}

def cool() {
	def white = device.currentValue("whitestate") != null ? device.currentValue("whitestate") : "1"
    log.debug "Previus white... $white"
    if (white == "4") {white3()};   
    if (white == "3") {white2()}; 
    if (white == "2") {white1()};       
    if (white != "1") {sendEvent(name:"whitestate", value: Integer.parseInt(white) - 1)};    
}

def off() {
    setLevel(0) 
}

def on() {
    setLevel(100)
}

def setLevel(value) {
    def level = value as Integer    
	if (level <= 0){
    	sendEvent(name: "switch", value: "off");    
    } else {
    	sendEvent(name: "switch", value: "on");       
	}    
	sendEvent(name: "level", value: value)   
    put();
	sendEvent(name: "level", value: value)   
    put();    
}

private put() {
    //Spark Core API Call
	httpPost(
		uri: "https://api.spark.io/v1/devices/${deviceId}/ledstate",
        body: [access_token: token, command: device.currentValue("color") + device.currentValue("level")],  
	) {response -> log.debug (response.data)}
    log.debug device.currentValue("color") + device.currentValue("level") + ',' +device.currentValue("whitestate");
}

  • Go to “My Devices” and click on “+New Device”
  • Create the Device type with the following values
  • Name: Spark LED Strip
  • Device Network Id: 2323
  • Type: Spark LED Strip

Restart the App and you should be able to see the new device 🙂

IMG_3117

To connect you SparkCore with your SmartThings interface go to preference and enter your Device ID and Access Token.

Finally, I also used a SmartThings Multi and put it inside a Staples Easy button. By pressing the button I let my son toggle on and off the strip.s0105150_sc7

If you like to collaborate please contact me at juano23@gmail.com