Basic Principle
This project was inspired by my wife while out one day Christmas shopping. Since the Star Wars movie had been released there were Star Wars toys everywhere. She called me one day and asked if Hackster was still doing the Star Wars inspired contest because she had seen a few things that could be built into something cool.
Well, we purchased some toys and came up with this fun concept of creating an interactive art piece for the wall.
The board has a sound card and of course many lights. It has a motion sensor and an ultrasonic range sensor to detect when someone is nearby. It also has a light sensor to detect light conditions.
The board is interactive and responds to a person standing in front of it or walking by it. The Darth Vader eyes light up red, the Light Saber will activate making the traditional light saber on and off sounds with the traditional hum. The Death Star also acts as a night light and will activate at night when there is motion detected. Finally, of course, Darth Vader will speak.
The activity is random and timed in a manner so that it does not turn on constantly. The approach was to be "when you least suspect it". A few family members have already reported being startled.
The art piece is also interactive through a Windows 10 Universal application and the Particle.io Cloud Web API.
Here are a few images of the final product.
Design Concept
The design of system is divided into four pieces that communicate over an I2C bus. The Main Controller is powered by a Particle Photon and is responsible for monitoring the sensors, controlling the other boards and interacting over the cloud. There are three sub-controllers powered by the Adafruit Trinket Pro 5V that control the Light Saber, the Death Star/Darth Vader Eyes and the Sound.
The Death Star act as a night light using a backlight white LED. The Darth Vader eyes are two Adafruit NeoPixel Jewels (with seven NeoPixels each). The Light Saber has 100 RGBW NeoPixels taken from a 144 LED's per meter strip. There is a speaker in the mouth on the Darth Vader helmet and another one in the Death Star.
ConstructionBoard
Plywood is usually purchased in a 4' x 8' piece. I purchased this at Home Depot and had them cut it to size which is 36" x 48". I purchased a higher quality plywood to get a nice clean, smooth finish. Even though the board is high quality, proper preparation by lightly sanding the edges and the front surface is important. Finish with a 220 grit sandpaper. After the sanding is complete wipe it down with a slightly damp cloth and let it dry completely.
We painted our board using Glidden Blue Grey Sky (30BB 32/067).
Frame
The frame is constructed from select pine 1" x 2" (measures 3/4" by 1 3/4"). The inside cuts for the long edge were 48" and 36" for the short edge. A 45° miter cut was made on each end to create a box around the board.
After cutting the pieces and ensuring a tight fit around the board, glue the pieces together using wood glue and corner clamps. Glue each corner one at at time ensuring that the entire frame is square.
Reinforced the frame using #18 x 3/4" wire nails in each corner for a total of 8 nails (one in each direction at the corners). Note that a pilot hole must be drilled for each nail before hammering it into place to ensure the wood does not split. Using a small punch, drive the nails slightly below the surface of the wood.
After the glues dries, fill the corners in with wood putty to ensure that there are no gaps in the frame. The wood putty should also be used to cover the nail holes.
The entire frame should be sanded before painting. Start with a 100 grit sandpaper and lightly sand the entire frame. Finish with a 220 grit sandpaper. After the sanding is complete wipe it down with a slightly damp cloth and let it dry completely.
We painted our frame with Glidden Grey Metal (00NN 10/000).
Attaching the Frame
The frame is attached to the board using small brass hinges that can be picked up from any hardware store. There is a small overlap in the front that can be set using a large paint stirring stick (the thickness is about 4-5 mm). Start by laying the board face down with the frame in place and put the pant stick under the board to hold it up the desire height. Attach the hinge to the main board using the supplied screws. Always drill small pilot holes to keep the screws from splitting the wood. Work your way around the board being careful to set the gap in the front of the board evenly each time.
There are a total of twelve hinges connecting the frame to the board, three along each edge. The two outer hinges are spaced five inches from the inside corner while the third hinge is centered along the edge.
Decals/Model Placement
The Darth Vader, the Light Saber and the Death Star (these are all lights from Brookstone) come with decals that make them appear to be breaking through the board. We chose the grey color of the board to enhance this effect.
Start by laying out all three pieces onto the board until they look good. The exact placing is up to the builder. Once placement has been determined apply the decals per the instructions that come with the items. The instructions are easy to follow and will result in good adhesion. Note that the instructions specify that a newly painted wall needs up to two weeks to dry. We did allow our board to sit for two weeks before attempting to adhere the decals.
The extra decals added to the board were purchased from Amazon and came with many more decals in the set than needed. The choice and layout o the decals is up to you.
I added more of a 3D effect by purchasing some model fighters from Target and attaching them to the board using a hot glue gun. This step is optional.
The wall lights come with the necessary screws to attach the lights to the board. This is a good point to drill some pilots holes for these and attach them to the board. Follow the instructions that come with the all lights.
NOTE: We are not ready to permanently attach the wall lights but they can be easily attached and detached.
Darth Vader Helmet
Each wall light unit requires the internal electronics to be removed. Luckily, these come apart with a few screws and are easily dismantled.
Flip the Darth Vader head over so you are looking at the back side and remove the screws. Pull the back cover off and set the main part of the helmet aside. Now remove the screws holding the lights and the switch. Cut the wires to the battery compartment. These components can be removed an saved for another project.
Prepare the NeoPixel Jewels by connecting 22 AWG wires to each of the three terminals. I recommend red for the 5V, black for the ground and green for the data wire. Cut pieces about three feet in length and and solder the red wire to the the 5V pin of the first Jewel. Next, solder the black wire to the same Jewel. Finally, solder the green wire to the Data In pin of this jewel.
The second jewel will be connected to the first jewel. Cut three of the same color wires at about 10 inches each. Solder the red wire to the 5V pad on the first Jewel and solder he other end to the 5V pad on the second Jewel. Connect the black from GND pin on the first jewel to the GND pad on the the second jewel. Finally, solder the green wire from the Data Out pad on the first Jewel and solder the other end to the Data In pin on the second jewel.
To attach these to the helmet plate, cut four pieces of straw about 2 inches each. I used thicker smoothie straws (the straws from Wendy's work well too). In order to see the lights through the dark eyes they need to be close to the top of the helmet, thus, the need for the straws. I used a hot glue gun to adhere the straws to the back of the NeoPixel and to the helmet plate (see the images below). A single straw is enough to hold them in place but I decided to use two on the second one to give it extra strength.
The last piece of the helmet is the speaker. This will be glued to the plate in front of the mouth. There is a small piece of metal blocking the sound that needs be removed from the inside of the helmet. This piece can be removed with a few screws.
Using a hot glue gun, attach the speaker to the helmet plate.
Drill a 1/4" hole in the center of the plate to pass the wires through. Note that you will also need to attach this plate to the board without the front of the helmet and drill through the board using this hole as a guide. The wires from the helmet will pass through this hole in the plate an through the hole in the board so that the wires from the individual pieces can be gathered in the back.
With the lights and speaker attached, pass the wires through the hole in the plate and use a little electrical wire to fold them in place. The electrical wire will prevent them from being pulled to far from the back side when wiring the unit.
Place the plate back onto the helmet and put the screws back in.
Light Saber
The Light Saber is a little harder to dismantle. The entire assembly will result in four separate pieces.
The first piece to remove is the red plastic tube that makes up the Light Saber. This simply unscrews and pulls out. Remove this and set it aside.
The second piece is a long plastic, clear tube found inside of the red tube. This is essentially a rolled piece of plastic and will be very useful for holding the NeoPixel strip in place. Remove this piece and set it aside.
The third piece to remove is the backing plate. This can be done by removing the three screws holding the plate to the assembly.
The fourth piece is the side of the Light Saber shaft. This is removed by locating the five screws that hold this in place. These screws are of various sizes so keep track of their locations.
Once apart, all of the electronic components can be removed.
The NeoPixel strip needs to be cut so that there are 100 NeoPixels on the strip. Follow the directions carefully at Adafruit Uberguide to cut these ensuring that the best practices are followed.
Cut three 22 AWG wires to about a two foot length each. Use a red wire, a black wire and a green wire (I often use green, white or yellow for my NeoPixel data wire). Solder the red wire to the the 5V pad. Solder the black wire to the GND pad and solder the green wire to the Data In pad.
Carefully slide the NeoPixel strip into the clear plastic tube being careful not to bend them excessively as you slide them through the tube.
Next run the wires through the assembly bringing them out through the bottom plate see the images).
With the wires in place fasten the side plate using the five screws. Next attached the back plate and run the wires through the hole where the switch was located.
A 1/4" hole will need to be drilled into the board lined up with the wires coming out from this unit.
Breadboarding
I am using Adafruit Perma-Proto boards for my final circuits so that they can be easily transferred from a breadboard.
Using the circuit diagrams attached in this project prototype each circuit. You need to be familiar with how to bread-board a circuit from a circuit diagram.
Notes:
- I used the extra tall single row headers in the breadboard and then use the standard size headers when soldering in the PCB boards.
- I used a NeoPixel ring in place of the NeoPixel strip (for the Light Saber).
- I used a single NeoPixel in place of the two NeoPixel Jewels (for the eyes).
- I used a miniature breadboard to distribute the 12 V to each board.
This part of the project will take a considerable amount of time and requires careful attention to the circuit and how the components are laid out on the breadboards.
Here are images of my breadboard circuits:
This video shows the breadboard circuit running through a cycle of each component:
PCB's
After all of the circuits are fully bread-boarded and tested they can be transferred to the PCB's (in the case Perma-Proto boards).
The process requires that you take each circuit, one at a time, and transfer the circuit while soldering the components in.
It helps to have an extra set of the components needed so that the circuit can be duplicated instead of transferring. This will cut down on the mistakes being made.
Notes:
- I used standard single row headers for the power connections and the I2C connections. For power, break away two pins each. For the I2C break away 3 pins each.
- I used 3 pin terminal blocks for the two NeoPixel connections. Note that these blocks are 3.5 mm and must be soldered to the board on a diagonal.
- I used 28 pin 0.6" IC Sockets for the Trinket Pro's.
- I used 36-pin 0.1" Female break-away headers to mount the Photon. Cut two sets of 12 pins for the Photon.
- I used 2 pins of the female break-away headers to mount the coin battery breakout to the GND and VBAT pins on the Photon.
- At least two of the circuit boards will require a double set of headers for the 12 V jumpers or one circuit board will require 3 sets. The power will come in from the back of board and will need to be connected to each circuit board
- The I2C bus needs to be connected to each circuit board. To achieve this, solder in two sets of headers on at least two of the circuit boards.
Here are images of my final circuits:
This video shows the circuit running through a cycle of each component:
Death Star
I chose the Death Star for this project to serve as the holder of all of the electronic components. It is a good size, fits nice in the theme and has plenty of space to hide the components.
The first step is to dismantle the Death Star and remove the electronics similar to the other items. Remove the ten screws from the back side and remove the plate. Next remove the screws holding the electronic components in place and remove these components. Last, cut the wires from the battery holder.
The previously created circuit boards can now be attached tot he plate from the death star. Use M3 spacers (or posts) along with an M3 screw, an M3 nut and two M3 washers for each post. There will be a total of eight posts (two per circuit board).
Start by attaching the brass posts to each board using the M3 nut (you may need to remove the Trinkets and Photon for this).
The three smaller boards will be aligned with the pre-existing slots in the plate. The larger circuit board will require to small holes to be drilled. From the outside of the plate use a screw and a washer and push them through the slot and then screw them into the brass post. For the larger board, place it in position and then mark the posts with a pencil. Remove the board and drill a hole big enough to accept the screws. The order of the boards is not important so try laying them out before attaching them to ensure a good fit.
A hole is required in this plate similar to the Darth Vader helmet that will need to align with the same hole in the board. The wires will pass through this hole to the backside of the board where the Light Saber and the Darth Vader helmet will be connected.
The back-light LED need to be prepped by connecting the two wire jumper and securing it with shrink tube. See the images below to prepare the LED.
The second speaker will be mounted inside of the Death Star. It just happens that the mounting holes on the speaker align nicely with some screws inside the Death tar as shown below. Connect the speaker to one of the spear terminal blocks on the sound card.
The back-light LED will be glued using hot glue over the green plastic dome inside of the Death Star.
Similar to the LED, prepare the leads of the photoresistor and slip the leads through the preexisting holes on the right side of the dome (away from the Light Saber). On the inside connect a two wire female jumper to the photoresistor and shrink a piece of tubing over the connector as shown in the images below.
Drill two 1/2" holes at the bottom of the dome where the motion sensor and ultrasonic range sensor will be mounted. Connect a four wire female jumper to each sensor and place them through the hole. Using a hot glue gun secure each sensor, one at a time, to the bottom of the dome on the outside.
Cut two sets of three wires for the NeoPixel connections. Use 22 AWG wire at a length of about 24". Connect each set to the wire terminals on the boards. Use red for 5V, black for GND and green (or yellow) for the data wire. Color coding will help make it easier to wire the components on the back of the board later. I put some electrical tape on these wires and labeled them so I could easily identify them later (mark one Light Saber and the other one Darth Vader).
Cut another set of two wires for the speaker. Use a read and black wire (it may be better to use 26 AWG wire for this but 22 AWG will work too). Connects these two wires to the speaker terminal block.
Connect a two wire female jumper to the 12 V header on the Photon board.
At this point the wires can be cleaned up and secured using zip ties and electrical tape. First run the six NeoPixel wires, the two speakers wires and power wire out through the hole in the plate. using some electrical tape secure these wires so they cannot be pulled out from the back of the board. Use zip ties to wrap up the rest of the wires to keep them neat.
Reassemble the Death Star and slip all the of the wires through the hole in the board.
At this point all of the devices can be attached and the wires run through their respective holes in the board. On the back side of the board connect the wires using small wire caps and fasten them down with either electrical tape or some type of adhesive fasteners. Also be sure to remove as much excess wire as possible but leave enough to allow any of the objects to be removed again if necessary.
The software for this project is divided into three sets. The first is the firmware for the Photon. This software controls the other boards and communicates with them via I2C. The second set is the Arduino code that is loaded on to each of the three Trinket Pro's. The last set is the software Windows 10 UWP software which can run on a Windows 10 device and is used to remotely control the device.
The code below is the firmware running on the Photon. Note the class files that are referenced can be found in the GitHub repository for this project.
#include "Controller.h"
#include "HC-SR04.h"
#include "SensorSmoothing.h"
// ***
// *** These are the two pins the HC-SR04 is connected to.
// ***
#define TRIGGER_PIN D2
#define ECHO_PIN D3
// ***
// *** These are the two pins the motion sensor is connected to.
// ***
#define MOTION_OUT D4
#define MOTION_ENABLED D5
// ***
// *** Photoresistor Pin
// ***
#define PHOTORESISTOR_PIN A0
// ***
// *** Numbers of samples to take for averaging.
// ***
#define NUMBER_OF_SAMPLES 5
// ***
// *** Default volume level
// ***
#define DEFALT_VOLUME 15
// ***
// *** This is the main controller to send commands
// *** to all of the devices.
// ***
retained int _volume = DEFALT_VOLUME;
// ***
// *** Define the controller
// ***
Controller _controller = Controller();
// ***
// *** Create the HcSr04 object for the
// *** HC-SR04 (Ultrasonic Ranging Module).
// ***
HcSr04 _ranging = HcSr04();
// ***
// *** This variable indicates if there is current
// *** motion detected.
// ***
bool _motion = false;
// ***
// *** This variable indicates if there is current
// *** motion detected.
// ***
double _distance = 0.0;
// ***
// *** Analog value on photoresistor pin
// ***
int _light = 0;
// ***
// *** This object will be used to keep a running average
// *** of readings with a sample size of NUMBER_OF_SAMPLES.
// *** This is used to keep the ultrasonic sensor from being
// *** too sensitive or jumpy.
// ***
SensorSmoothing<double> _distanceSample = SensorSmoothing<double>(NUMBER_OF_SAMPLES);
// ***
// *** Timer used to monitor the sensor
// ***
Timer _sensorTimer(1000, onSensorTimer);
// ***
// *** Last time of an event
// ***
int _lastEventTime = 0;
// ***
// *** Light Threshold
// ***
#define DEFAULT_LIGHT_THRESHOLD 1000
retained int _lightThreshold = DEFAULT_LIGHT_THRESHOLD;
bool _lightBelowThreshold = false;
bool _deathStarIsOn = false;
// ***
// *** Motion detecting parameters
// ***
retained int _minimumEventTime = 20; // seconds
retained int _minimumDistance = 127; // (50 inches) parameter is cm
void setup()
{
// ***
// *** Publish a setup message
// ***
Particle.publish("Setup", "Started");
// ***
// *** Enable backup RAM
// ***
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
// ***
// *** Initialize the HC-SR04 (Ultrasonic Ranging Module)
// ***
_ranging.begin(TRIGGER_PIN, ECHO_PIN);
// ***
// *** Setup I2C
// ***
Wire.begin();
// ***
// *** Initialize the controller
// ***
_controller.begin(_volume);
// ***
// *** Use the D7 LED indicate when the Light
// *** Saber is active.
// ***
pinMode(D7, OUTPUT);
digitalWrite(D7, LOW);
// ***
// *** Set of the public variables
// ***
Particle.variable("motion", _motion);
Particle.variable("distance", _distance);
Particle.variable("light", _light);
// ***
// *** Publish functions
// ***
Particle.function("controller", controllerWebApi);
Particle.function("parameter", parameterWebApi);
Particle.function("test", testWebApi);
// ***
// *** Set the last event time to
// *** the current time
// ***
_lastEventTime = Time.now();
// ***
// *** Start the timer
// ***
_sensorTimer.start();
// ***
// *** Gives the other boards time to start
// *** just in case this is a power up.
// ***
delay(3000);
// ***
// *** Reset
// ***
_controller.darthVaderOff();
_controller.lightSaberOff();
_controller.deathStarOff();
// ***
// *** Publish a setup message
// ***
Particle.publish("Setup", "Completed");
}
void loop()
{
// ***
// *** Temporarily cache the _lightBelowThreshold value.
// ***
bool previousLightBelowThreshold = _lightBelowThreshold;
// ***
// *** Check for motion
// ***
if (_motion)
{
// ***
// *** Check the light level and see if
// *** it is dark enough to turn the LED
// *** on.
// ***
if (_light < _lightThreshold)
{
if (!previousLightBelowThreshold)
{
Particle.publish("Light", "Below Threshold");
}
_lightBelowThreshold = true;
// ***
// *** Turn the Death Star LED on
// ***
_deathStarIsOn = true;
_controller.deathStarOn();
}
else
{
if (previousLightBelowThreshold)
{
Particle.publish("Light", "Above Threshold");
}
_lightBelowThreshold = false;
}
// ***
// *** Check if an object is within three feet.
// ***
if (_distance < _minimumDistance)
{
// ***
// *** Check if the minimum amount
// *** of time has passed since the
// *** last update.
// ***
if ((Time.now() - _lastEventTime) > _minimumEventTime)
{
Particle.publish("Object", "Detected");
_lastEventTime = Time.now();
_controller.randomEvent();
}
}
}
else
{
// ***
// *** No motion detected
// ***
if (_deathStarIsOn)
{
// ***
// *** When the light is above the threshold, turn the
// *** Death Star LED off.
// ***
_deathStarIsOn = false;
_controller.deathStarOff();
}
}
// ***
// *** Delay
// ***
delay(150);
}
void onSensorTimer()
{
// ***
// *** Check the motion sensor
// ***
bool previousMotion = _motion;
// ***
// *** Get the current motion sensor value.
// ***
_motion = (digitalRead(MOTION_OUT) == HIGH);
// ***
// *** Only publish and event when the status of the motion
// *** sensor hs changed.
// ***
if (!previousMotion && _motion)
{
Particle.publish("Motion", "Detected");
if (_lightBelowThreshold)
{
Particle.publish("Light Level", "Below Threshold: " + String(_light) + "/" + String(_lightThreshold));
}
else
{
Particle.publish("Light Level", "Above Threshold: " + String(_light) + "/" + String(_lightThreshold));
}
// ***
// *** Publish an event showing the distance of the
// *** object in front of the board.
// ***
Particle.publish("Distance", String(String(_distance / 2.54).toInt()) + " in / " + String(String(_minimumDistance / 2.54).toInt()) + " in");
// ***
// *** Publish an event showing the amount of time until
// *** the next event can be triggered.
// ***
Particle.publish("Time", "Last Event: " + String(Time.now() - _lastEventTime) + " second(s) ago");
}
else if (previousMotion && !_motion)
{
Particle.publish("Motion", "Reset");
}
// ***
// *** Get the distance from the Ultrasonic
// *** sensor.
// ***
_distance = getRangeEx();
// ***
// *** Get the photoresistor value
// ***
_light = analogRead(PHOTORESISTOR_PIN);
}
// ***
// *** Web API
// ***
int testWebApi(String command)
{
// ***
// *** Test
// ***
Particle.publish("Test", "Running");
_controller.setVolume(10);
delay(500);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(0);
delay(18000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(1);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(2);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(3);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(4);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(5);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(6);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(7);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.darthVaderOn();
delay(1000);
_controller.darthVaderVoice(8);
delay(5000);
_controller.darthVaderOff();
delay(3000);
_controller.lightSaberOn(7000);
delay(2000);
_controller.deathStarOn();
delay(5000);
_controller.deathStarOff();
Particle.publish("Test", "Completed");
return 1;
}
// ***
// *** Web API
// ***
int parameterWebApi(String command)
{
int returnValue = 0;
// ***
// *** If the command contains an equal
// *** sign , assume it is trying to
// *** set the value of a parameter.
// ***
int equalIndex = command.indexOf("=");
// ***
// *** Check the value of command for the type
// *** of parameter call.
// ***
if (equalIndex > 0)
{
// ***
// *** This command is to set a variable value
// ***
String parameterName = command.substring(0, equalIndex);
String value = command.substring(equalIndex + 1, command.length());
if (parameterName == "volume")
{
_volume = value.toInt();
_controller.setVolume(_volume);
}
else if (parameterName == "minimumDistance")
{
_minimumDistance = value.toInt();
}
else if (parameterName == "lightThreshold")
{
_lightThreshold = value.toInt();
}
else if (parameterName == "minimumEventTime")
{
_minimumEventTime = value.toInt();
}
// ***
// *** Publish the parameter
// ***
Particle.publish("Set Parameter", parameterName + " = " + value);
// ***
// *** Return the value
// ***
returnValue = value.toInt();
}
else if (equalIndex == -1)
{
// ***
// *** If the command matches a name
// *** return the value.
// ***
if (command == "volume")
{
returnValue = _volume;
}
else if (command == "minimumDistance")
{
returnValue = _minimumDistance;
}
else if (command == "lightThreshold")
{
returnValue = _lightThreshold;
}
else if (command == "minimumEventTime")
{
returnValue = _minimumEventTime;
}
// ***
// *** Publish the parameter
// ***
Particle.publish("Get Parameter", command);
}
else if (command == "reset")
{
// ***
// *** Reset the parameters to default values
// ***
_volume = DEFALT_VOLUME;
_controller.setVolume(_volume);
_minimumDistance = 50 * 2.54;
_lightThreshold = 1000;
_minimumEventTime = 20;
_lightThreshold = DEFAULT_LIGHT_THRESHOLD;
// ***
// *** Publish the parameter
// ***
Particle.publish("Reset Parameters", "");
returnValue = 1;
}
return returnValue;
}
// ***
// *** Web API
// ***
int controllerWebApi(String command)
{
int returnValue = 0;
// ***
// *** Publish the parameter
// ***
Particle.publish("Controller", command);
// ***
// *** Pass the command to the controller.
// ***
returnValue = _controller.executeCommand(command);
return returnValue;
}
double getRangeEx()
{
double returnValue = 0;
// ***
// *** Get the distance in centimeters
// ***
Range range;
if (_ranging.getRange(&range))
{
// ***
// *** Add the sample
// ***
_distanceSample.addSample(range.distance);
// ***
// *** Return the average distance
// ***
returnValue = _distanceSample.average();
}
else
{
// ***
// *** Check the result
// ***
if (range.result == timeFailed)
{
switch (range.pingTime.result)
{
case alreadyActive:
Particle.publish("Result", "The sensor is busy.");
break;
case timeout:
Particle.publish("Result", "The sensor did not respond.");
break;
}
}
else if (range.result == underRange)
{
switch (range.result)
{
case underRange:
Particle.publish("Result", "The object is too close.");
break;
case overRange:
Particle.publish("Result", "No object is in range.");
break;
}
}
else
{
Particle.publish("Result", "Unknown Error.");
}
}
return returnValue;
}
Here are screens shots of the Windows 10 Universal application.
The Windows 10 code calls the Particle.io Cloud API using HttpClient to execute functions defined using the Particle.function() syntax as shown here.
protected async virtual Task<int> Execute(string functionName, string command)
{
int returnValue = 0;
HttpClient httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("args", command)
};
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
request.Headers.Authorization = this.Identity.Authentication;
request.Method = this.Method;
request.RequestUri = new Uri(this.Identity.BaseUri, string.Format(this.Resource, functionName));
request.Content = content;
HttpResponseMessage response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
dynamic obj = JsonConvert.DeserializeObject(json);
string value = obj.return_value;
returnValue = Convert.ToInt32(value);
}
else
{
throw new HttpRequestException(response.ReasonPhrase);
}
return returnValue;
}
The code below demonstrates how to retrieve the value of a variable defined using the Particle.variable() syntax.
protected async override Task<T> Execute(params string[] args)
{
T returnValue = default(T);
HttpClient httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Authorization = this.Identity.Authentication;
request.Method = HttpMethod.Get;
request.RequestUri = new Uri(this.Identity.BaseUri, string.Format(this.Resource, args));
HttpResponseMessage response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
dynamic obj = JsonConvert.DeserializeObject(json);
string value = obj.result;
returnValue = (T)Convert.ChangeType(value, typeof(T));
}
else
{
await this.OnFailed(response);
}
return returnValue;
}
See the full code for all of the details. All of the software is available in the linked GitHub repository.
Video DemoThe following video runs through a demonstration of the various effects on the board. Note that the board is designed to be interactive based on motion detection and this demo of the board is just a representation of the built in effects.
Comments