Sparkcore Xmas Tree Star
The Spark core christmas tree star (SCXTS) is kind of a leftover project from the instructables build night, hosted during dorkbot.de Aachen in November 2014. I failed there, because at that time I had no means to parse an incoming string with more than one argument inside the Sparkcore function. Only after solving this issue I really got started.
I borrowed my already claimed core from there and a ‘Spark core Spark Internet Button’ (short: the button). I wanted to figure out how to light up all LEDs in individually specified colors, meaning, I wanted to use my pink iPod as a remote control for the button.
Very soon I found out about the next problem. The Spark core has 2 limitations. The number of functions, available by name, is limited to 4. You can declare as much as you like, but then some of them are just swallowed.
The second limitation is the parameter string limited to a length of 64 characters. With this limitation it is impossible to formulate 12 colors in an ordered list as string, even if the values itself are bytes. At the very least, as hex coded with 6 letters per color, it would be 72, without the separators.
The only way is to split the call. At first I created two functions, one for the first 6 colors, one for the last 6. This approach worked fine until I ran into the other limitation of 4 functions in total. I wanted to switch the button on and off, change the overall brightness and so on, but I got only 2 names left.
What I came up with is one single function. The first argument ‘mode’ is used to branch out to a set of then internal functions. A negative float value indicates how the following data should be processed.
A positive values just means full throttle: All 12 values fit into one string: Use it!
Downloads
Parts
Here is the complete list of the mandatory parts:
- 1 x Spark Core
- 1 x Christmas tree
- 1 x pink iPod (5th generation)
- 1 x "Spark Core Spark Internet Button"
- 1 x yellow USB-cable
- 1 x white power supply
Maybe you like to add a Christmas tree stand, candles and some decoration.
Because these parts are not mandatory they are not included here.
Assembling
To assemble the hardware mount the Spark Core onto the big button as indicated. The USB connector of the core must point to the empty slot of LED #12. Because said slot is empty, instead the LED of the core itself is used.
Plug the yellow USB cable into the outlet of the core and connect it. The power supply is for later use only if the computer does not become part of the decoration.
In order to code something on the core, plug the USB-cable into a computer, claim your core, if you haven't done it yet, launch the API and start coding. At first handling 12 colors with 11 LEDs and one LED on the core is an issue.
Declaration:
SparkButton mySparkButton; typedef struct rgbColor {unsigned char r;
unsigned char g; unsigned char b; } RGBColor;
The code in setup():
...
mySparkButton = SparkButton();
mySparkButton.begin();
RGB.control(true); ...
and the function to set all 12 available color-LEDs:
void setOneOfMyLED(int ledIndex, RGBColor col)
{
if(0 == ledIndex || 12 == ledIndex)
{
RGB.color(col.r, col.g, col.b); } else if(ledIndex < 12) { mySparkButton.ledOn(ledIndex, col.r, col.g, col.b); } }
This already has become my standard code for the button.
Remote
Then the app on the remote device is developed. Soloely related parts of the code are presented here.
In an effort to limit the functionality to certain types of devices I decided to restrict my app to only pink iPods.
…
UIDevice *device = [UIDevice currentDevice];
SEL selector = NSSelectorFromString(@"deviceInfoForKey:"); if (![device respondsToSelector:selector]) { selector = NSSelectorFromString(@"_deviceInfoForKey:"); } if ([device respondsToSelector:selector]) { NSCharacterSet *cs = [[NSCharacterSet lowercaseLetterCharacterSet]invertedSet]; NSString *deviceColor = [[device performSelector:selector withObject:@"DeviceColor"]stringByTrimmingCharactersInSet:cs]; NSString *deviceEnclosureColor = [[device performSelector:selector withObject:@"DeviceEnclosureColor"]stringByTrimmingCharactersInSet:cs]; NSLog(@"deviceColor: %@ deviceEnclosureColor: %@", deviceColor, deviceEnclosureColor); if([deviceColor isEqualToString:@"white"] && [deviceEnclosureColor isEqualToString:@"pink"]) { return YES; } } …
At some points in life colors should be useful.
Then I created the interface to set all the colors. I found a little framework, where the usual dance with NSURL was already done and implemented these two methods:
-(void)sendRequestforFunction: (NSString *) theFunction<br>withParameter:(NSString *)parameter { SparkTransactionPost *postTransaction = [[SparkTransactionPost alloc] initWithAccessToken:ACCESS_TOKEN deviceId:DEVICE_ID functionName:theFunction andParameters:parameter]; [SparkCoreConnector connectToSparkAPIWithTransaction:postTransaction andHandler:^(NSURLResponse *response, NSDictionary *responseDictionary, NSError *error) { if(error == nil) { NSLog(@"Response: %@",responseDictionary); }
else { NSLog(@"Error: %@",error); }
}]; // end block NSLog(@"Continuing with app");
} -(void)sendRequestforUnifiedFunctionWithParameter:(NSString *)parameter { [self sendRequestforFunction:UNIFIEDFUNCTION withParameter:parameter]; }
The first method returns a dictionary, which comes directly from the Spark core:
Response:
{
connected = 1;
id = 53xx...;
"last_app" = "";
name = Sushi;
"return_value" = 12;
}
Obviously it is connected, has a device-ID and a given name, and the returned value is '12'. The latter is the integer value returned from the function in the core, in my case the count of the colors.
The second method is simply calling on the first one. It is more interesting. Because I found a technique to use only one single function call on the the core I could reduce my code on the iOS-device to only the parameters' string. I do not handle any names of functions anymore anywhere in my app:
#define UNIFIEDFUNCTION @"theFunction"
The same name is declare in the Spark core's application.
Remote Control
As defined:
#define UNIFIEDFUNCTION "theFunction"
and then in setup():
Spark.function(UNIFIEDFUNCTION, setColorInModeFunction);
This function called from the Internet via the NSURLRequest then branches into severall parts according to the mode and the count values. This part maybe can get more sophisticated, a checksum could be introduced and so on.
int setColorInModeFunction(String args) { unsigned char allValuesMaxCount = 25;
// 64 chars -24 separator = 40 digis;
// 40/25 digits = 1.6 digits per value double allValues[allValuesMaxCount]; unsigned int count = parseArgsIntoDoubles(args, allValues); if(count > allValuesMaxCount) {// How to deal with a once-in-a-lifetime situation? Serial.println("Houston, we have a problem!"); } double mode = allValues[0]; if(THEFEATURE_FUNCTION == mode)// set overall brightness and rotation {// 1 == rpm, 2 = brightness double rotationExponent = allValues[1]; rotationDirectionIsClock = (0 < rotationExponent); float buttonRpmExp = (float)fabs(rotationExponent); rpm = (unsigned int) round(pow(2,buttonRpmExp));// the only case we get fractional exponents, e.g. smooth changes buttonRpmIndex = (unsigned char)round(buttonRpmExp);// to get the buttons as index right brightness = (float)constrain(allValues[2],0.0,1.0); int exp; double fraction = frexp(256.0*brightness, &exp); brightnessIndex = (unsigned char)constrain(exp,0,7); } else if (SINGLECOL_FUNCTION == mode && 3 == count)// set one indexed color { unsigned int aColorIndex = constrain((unsigned long)(floor(allValues[1])),0,11); theColorValues[aColorIndex] = constrain((unsigned long)(floor(allValues[2])),0,0xFFFFFF); } else // some more or all colors will be set { unsigned char startIndex = 0; unsigned char offset = 0; unsigned char colorsCount = 12; // 12: there will be 12 values for all the LEDs, there is no mode, value is positive if( 0 <= mode && 12 == count)// we got all values, no explicit mode { // nothing } else if(INDEXEDCOL_FUNCTION == mode) { // set all values for the loop startIndex = constrain((unsigned long)(floor(allValues[1])),0,11); colorsCount = constrain((unsigned long)(floor(allValues[2])),1,12); offset = 3; } if(13 > startIndex + colorsCount)// no more than 12 for(unsigned int i = 0; i < colorsCount ; i++) {// set the indexed color from offset to end unsigned char readIndex = offset + i; // for readability, start at offset; theColorValues[i + startIndex] = constrain((unsigned long)(floor(allValues[readIndex])),0,0xFFFFFF); }; } return count; }
The images show some special cases. All LED in red shortens the call, because the red color is in hex 0x0000FF, decimal only 256. This is only on call. The next two images show both a lot of LEDs in black, which gives a string of '0'.
The last one shows the interface to set one color with a colorwheel and a brightness slider. From this interface the color can be set as single color.
Source Code for the Spark Core
The iOS-app is not available yet. This version is developed on a beta distribution from Apple, which has not been made public yet. In addition some of the APIs are private.
More important:
This is the code for the Spark core, and it is complete