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.
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 🙂
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 🙂
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.
If you like to collaborate please contact me at juano23@gmail.com