The KOTOR Crate is the perfect bedside storage unit, complete with a night light and "sweet" melodies to wake you up in the morning.
To start I went back and forth on the best way to design this storage unit. After trying one way and realizing it really wasn't practical, I went back to the drawing board and stumbled upon the second design.
This is my first interdependent hardware and software build, and it has been a great experience.
Set UpTo build this project, I broke it up into different stages:
1) Servo
2) Adafruit NeoPixel
3) Web App
4) Pushbutton & LED
5) Piezo Buzzer
6) Putting together the container
//The Code is explained separately
1) Servo
Before working with Particle Photon, I wrote all the code for an Arduino UNO, since I had the starter kit and had done some of the projects from the kit.
The Opening and Closing of the top was the most important part, thus being able to build it first was important. I used a black box with a thin film on it to start, by cutting out the circle and using its as a guide for others. The best type of cardboard is from specialty boxes, since they hold up better then normal shipping boxes.
Getting the "door" to slide between the top and bottom proved to be quite tricky, and adding a few washers between helped it slide.
2) NeoPixel
Next was the Adafruit NeoPixel, since I wanted a light to either shine inside the container or around it. Also being able to change the color was important to set different moods. (*See Later)
Adafruit has a wonderful guide that helped me understand how to use the NeoPixel's and change the color. I was still doing this all on the Arduino to get a better understating, since I hadn't built the Web App yet.
3) Web App
This was my biggest challenge, since before this challenge my web work was limited to HTML and I never had to get it to talk to any hardware.
Thankfully I was directed to this tutorial on the Particle Forum, which helped me understand how to get the Web App to talk to the Photon.
A lot of how I designed the code for the Photon and web app is based on the code in the tutorial.
I used JQuery Mobile and w3schools to build the web app GUI.
After finally getting the servo and NeoPixel to turn on and off, I then moved into changing the NeoPixels color. I mean WOW this proved to be a challenge. Since the only way I know to change the color is through a RGB code, and I didn't want to have a "million" sliders on the app. I decided to input a number from the slider into a function that would convert it from a HSL code to a HEX code, then pass it to the Photon, to be converted from HEX to RGB. Quite a process, but I don't know how to POST large viable or use JSON. Thus it proved to be the best option and I learnt a lot from it.
4) Pushbutton & LED
This was an add on that I felt was needed. Since we don't always have our phones out and being able to turn the light on/off at anytime would be nice. However, again another challenge, since getting a physical button to work with a digital one was not easy (still not quite right). Maybe I was just doing something wrong, but I started out with a simple push button, and wasn't able to get it to work. So then I got a click button and had better luck. Currently, the physical button still over rides the digital, but it works for what I want.
The LED within the button indicates when the NeoPixel is off (Solid blue LED), NeoPixel is turned on with the Web App (LED Flashes), and NeoPixel is turned on with the physical button (LED is off).
5) Piezo Button
This was my last minute add on. In the spirit of Star Wars I wanted the piezo to play both the SW Theme song and the Imperial March. The Arduino Melody page formed the basis for which to play the notes. (I'm currently building out a complete scale system with my girlfriend to be able to input any song). I then put in the notes and beats for each song, fined tuned, added lights, and voila.
Now on the web app if you choose a side it will play the song and light up accordingly.
6) Putting together the container
The last part was putting it all together and building a housing unit to go with it all. The prototype 1 was my original idea, however after putting it together roughly, it just didn't feel right. So, I went back to the drawing board and after throwing around a few ideas, my girlfriend said she just liked it flat and simple with the NeoPixel under it. Thus we went with that design. The base is made up of stacked rings of foam board. Since I don't have access to a 3D printer, it was the best next thing. Each ring is cut and sanded to make sure that the inner rings fit in nice and snug. Legs were mounted to allow the light to flow out around the base.
ElectronicsList of parts and board design can be found in the sections below.
I have broken down each part of the build by code. Since I built it in parts, I found it helpful to have each part separated. Please note that not all the code may be included, so if you copy-paste it, it's would be best to check it over first.
- Servo
//Servo SetUp
Servo myServo;
int const serWrite = D2;
int potVal;
int angle;
void setup() {
//setup REST service for servo control
Particle.function("setServo", serControl);
myServo.attach(serWrite);
}
int serControl (String state) {
if (state == "open") {
potVal = 0;
angle = map(potVal, 0, 4095, 0, 179);
myServo.write(angle);
return 1;
}
if (state == "close") {
potVal = 4000;
angle = map(potVal, 0, 4095, 0, 179);
myServo.write(angle);
return 1;
}
//return 0;
}
- Adafruit NeoPixel
// This #include statement was automatically added by the Particle IDE.
#include "neopixel/neopixel.h"
//NeoPixel SetUp
#define NEO_PIN D1
#define NEO_COUNT 7
#define NEO_TYPE WS2812B
//Light State On/Off
String lightState;
//NeoPixel Color
String hexCode;
int red;
int green;
int blue;
Adafruit_NeoPixel neoRing = Adafruit_NeoPixel(NEO_COUNT, NEO_PIN, NEO_TYPE);
void setup() {
neoRing.begin();
neoRing.setBrightness(128); //NeoPixel Brightness set to half
neoRing.show(); // Initialize all pixels to 'off'
//setup REST service for NeoPixel control
Particle.function("setSwitch", neoLight);
Particle.variable("getSwitch", &lightState, STRING);
//setup REST service for NeoPixel colour
Particle.function("setColor", pixelColor);
Particle.variable("getColor", &hexCode, STRING);
Particle.variable("getRed", &red, INT);
Particle.variable("getGreen", &green, INT);
Particle.variable("getBlue", &blue, INT);
}
int neoLight (String state) {
if (state == "on") {
lightState = "On";
//pixelColor(hexCode);
//neoRing.show(); //refresh the NeoPixel color
return 1;
} else if (state == "off") {
lightState = "Off";
//lightOff ();
return 1;
}
//return 0;
}
int pixelColor (String color) {
hexCode = color;
char tempInt;
//HEX char to INT - Red color
tempInt = color[1];
red = hexToDec(tempInt,1);
tempInt = color[2];
red = (hexToDec(tempInt,0)) + red;
Serial.println(red);
//HEX char to INT - Green color
tempInt = color[3];
green = hexToDec(tempInt,1);
tempInt = color[4];
green = (hexToDec(tempInt,0)) + green;
Serial.println(green);
//HEX char to INT - Blue color
tempInt = color[5];
blue = hexToDec(tempInt,1);
tempInt = color[6];
blue = (hexToDec(tempInt,0)) + blue;
Serial.println(blue);
for (int i = 0; i < 7; i++){
neoRing.setPixelColor(i, red, green, blue); //Red Colour
if (lightState == "On" || buttonState == 1) {
neoRing.show(); //refresh the NeoPixel color
}
}
}
//Color Hex to Dec converter
int hexToDec(char hexString,int x) {
int decValue = 0;
int tempvalue = 0;
if ((hexString != 'a') && (hexString != 'b') && (hexString != 'c') && (hexString != 'd') && (hexString != 'e') && (hexString != 'f'))
{
int tempvalue = hexString - '0';
if (x==1){
decValue=tempvalue * 16;
} else {
decValue = tempvalue + decValue;
}
}
else {
if (hexString == 'a'){
tempvalue=10; }
if (hexString == 'b'){
tempvalue=11; }
if (hexString == 'c'){
tempvalue=12; }
if (hexString == 'd'){
tempvalue=13; }
if (hexString == 'e'){
tempvalue=14; }
if (hexString == 'f'){
tempvalue=15; }
if (x==1){
decValue = tempvalue * 16;
} else {
decValue = tempvalue + decValue;
}
}
return decValue;
}
void lightOff () {
for (int i = 0; i < 7; i++){
neoRing.setPixelColor(i, 0, 0, 0);
}
neoRing.show(); //refresh the NeoPixel color
}
- Button & LED
//Physical Button SetUp
int const ledPin = A3;
int const buttonPin = D4;
int ledState = LOW;
int buttonState;
int breath = 10;
int i = 0;
int d = 0;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0; // will store last time LED was updated
// constants won't change :
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
//Button Connect
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLDOWN);
}
void loop() {
buttonState = digitalRead(buttonPin);
// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if (buttonState == 1 && lightState == "On") {
lightState = "Off";
//pixelColor(hexCode);
//digitalWrite(ledPin, LOW);
}
if (buttonState == 1 && lightState == "Off") {
//lightState = "On";
pixelColor(hexCode);
digitalWrite(ledPin, LOW);
}
if (buttonState == 0 && lightState == "On") {
if (buttonState == 0 && lightState == "On") {
pixelColor(hexCode);
if (buttonState == 1) {
lightState = "Off";
}
}
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
//digitalWrite(ledPin, LOW);
}
if (buttonState == 0 && lightState == "Off") {
lightOff();
digitalWrite(ledPin, HIGH);
}
}
- Piezo Buzzer
//Piezo Buzzer
int const speakerPin = D3;
void ssetup () {
Particle.function("setForce", force);
//Sound Output Pin
pinMode(speakerPin, OUTPUT);
}
int force (String side) {
String tempHex = hexCode;
if (side == "light") {
//Theme Song
int length = 17; // the number of notes
String themeNotes[] = {"g","D/D5","C","b","a/A4","G/G5","D/D5","C","b","a/A4","G/G5","D/D5","C","b","C","a/A4","rest"};
int themeBeats[] = { 4, 4, 1, 1, 1, 4, 4, 1, 1, 1, 4, 4, 1, 1, 1, 5, 2 };
int tempo = 150;
lightState = "On";
pixelColor("#0000FF");
for (int i = 0; i < length; i++) {
if (themeNotes[i] == "rest") {
delay(themeBeats[i] * tempo);
} else {
playNote(themeNotes[i], themeBeats[i] * tempo);
}
delay(tempo / 2);
}
lightOff();
lightState = "Off";
hexCode = tempHex;
return 1;
}
if (side == "dark") {
//Imperial March
int length = 10;
String marchNotes[] = {"G4","G4", "G4", "D#4/Eb4", "A#4/Bb4", "G4", "D#4/Eb4","A#4/Bb4", "G4", "rest"};
int marchBeats[] = { 8,8,8,6,2,8,6,2,16,2 };
int tempo = 100;
lightState = "On";
pixelColor("#FF0000");
for (int i = 0; i < length; i++) {
if (marchNotes[i] == "rest") {
delay(marchBeats[i] * tempo);
} else {
playNote(marchNotes[i], marchBeats[i] * tempo);
}
delay(tempo / 2);
}
lightOff();
lightState = "Off";
hexCode = tempHex;
return 1;
}
}
//Sound Engine
void playTone(int tone, int duration) {
for (long i = 0; i < duration * 1000L; i += tone * 2) {
digitalWrite(speakerPin, HIGH);
delayMicroseconds(tone);
digitalWrite(speakerPin, LOW);
delayMicroseconds(tone);
}
}
void playNote(String note, int duration) {
String noteNames[] = { "G6","F#6/Gb6","F6","E6","D#6/Eb6","D6","C#6/Db6","C6","B5","A#5/Bb5","A5","G#5/Ab5","G/G5","F#5/Gb5","F5","E5","D#5/Eb5","D/D5","C#5/Db5","C5","C","B4","b","A#4/Bb4","a/A4","G#4/Ab4","g","G4","F#4/Gb4","F4","E4","D#4/Eb4"};
int tones[] = { 318, 337, 357, 379, 401, 425, 450, 477, 506, 536, 568, 601, 637, 675, 715, 758, 803, 851, 901, 955, 956, 1012, 1014, 1072, 1136, 1203, 1274, 1275, 1351, 1431, 1516, 1607 };
for (int i = 0; i < 32; i++) {
if (noteNames[i] == note) {
playTone(tones[i], duration);
}
}
}
The KOTOR Web App will run in a browser or in DropBox on a smartphone.
The top two buttons control the servo and NeoPixel light. The button slider controls the color of the NeoPixel and the button below it displays the chosen color. As the slider moves the bottom button will change color. You can push it to see the HEX code.
The Light Side and Dark Side nav tabs take you to pages that control the peize. By pushing either the "Jedi" or "Sith" button, the piezo will start to play either The Imperial March or Star Wars Theme song for a few seconds, with a red or blue light.
Prototype 1 was my original idea behind building this. By having a long cylinder shaped unit, it would be able to store more item. However, it proved to be very awkward and top heavy. Not to mention that getting my hand in the top was not easy. Thus a better design was needed.
This design happened by accident since I couldn't work out how best to set it all up. Finally my girlfriend said to just leave it like that, since it was cute. The NeoPixel gives a nice glow to the base and brings the whole set up together.
Pushing the on/off button to activate the under light
Imperial March
Star Wars Theme Song
NeoPixel Color ChangeI have included this topic heading since it was my biggest challenge and may be useful to others.
Web App - HSL to HEX
When the hslToHex () function is called and a value is passed from the slider, it will convert it to the HEX code by passing it as a RGB code first and then converting to HEX. If you want more control of the colors produced, the s and l variables can be passed in as numbers between 0 and 1. I have fixed them, so to have only one slider in the app.
The HEX code is then posted to the Photon.
function hslToHex (h) {
var s = 1;
var l = 0.5;
var r, g, b;
var red, green, blue;
var hue = document.getElementById('colorBoxId').value;
hue = hue/100;
console.log(hue);
if(s == 0){
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, hue + 1/3);
g = hue2rgb(p, q, hue);
b = hue2rgb(p, q, hue - 1/3);
}
red = Math.round(r * 255);
green = Math.round(g * 255);
blue = Math.round(b * 255);
console.log(r + " RED " + red);
console.log(g + " GREEN " + green);
console.log(b + " BLUE " + blue);
hexCode = "#" + componentToHex(red) + componentToHex(green) + componentToHex(blue);
console.log("Hex " + hexCode);
var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + setColor + "/";
$.post( requestURL, { params: hexCode, access_token: accessToken });
//return hexCode;
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
Particle Photon - HEX to RGB
Once the Photon receives the HEX code as a string, pixelColor () breaks it down into the individual numbers, since HEX represent #RRGGBB, the function hexToDec () converts each hex number into the decimal it represents. This is then passed to the NeoPixel and the color is displayed.
int pixelColor (String color) {
hexCode = color;
char tempInt;
//HEX char to INT - Red color
tempInt = color[1];
red = hexToDec(tempInt,1);
tempInt = color[2];
red = (hexToDec(tempInt,0)) + red;
Serial.println(red);
//HEX char to INT - Green color
tempInt = color[3];
green = hexToDec(tempInt,1);
tempInt = color[4];
green = (hexToDec(tempInt,0)) + green;
Serial.println(green);
//HEX char to INT - Blue color
tempInt = color[5];
blue = hexToDec(tempInt,1);
tempInt = color[6];
blue = (hexToDec(tempInt,0)) + blue;
Serial.println(blue);
for (int i = 0; i < 7; i++){
neoRing.setPixelColor(i, red, green, blue); //Red Colour
if (lightState == "On" || buttonState == 1) {
neoRing.show(); //refresh the NeoPixel color
}
}
}
//Color Hex to Dec converter
int hexToDec(char hexString,int x) {
int decValue = 0;
int tempvalue = 0;
if ((hexString != 'a') && (hexString != 'b') && (hexString != 'c') && (hexString != 'd') && (hexString != 'e') && (hexString != 'f'))
{
int tempvalue = hexString - '0';
if (x==1){
decValue=tempvalue * 16;
} else {
decValue = tempvalue + decValue;
}
}
else {
if (hexString == 'a'){
tempvalue=10; }
if (hexString == 'b'){
tempvalue=11; }
if (hexString == 'c'){
tempvalue=12; }
if (hexString == 'd'){
tempvalue=13; }
if (hexString == 'e'){
tempvalue=14; }
if (hexString == 'f'){
tempvalue=15; }
if (x==1){
decValue = tempvalue * 16;
} else {
decValue = tempvalue + decValue;
}
}
<br/>
return decValue;
}
Final RemarksFinally I would like to thank Particle and Hackster for this opportunity. As my first true build, I have really enjoyed myself and look forward to others in the future.
If you have any questions or comments please let me know.
Comments