NASA Asteroid Display Lamp
by aatidmarsh in Circuits > Microcontrollers
115 Views, 0 Favorites, 0 Comments
NASA Asteroid Display Lamp
This project is a lamp that displays the number of asteroids near Earth, as reported by NASA's Asteroids Near Earth Objects Web Service API. The lamp turns on automatically at sunset and turns off automatically at midnight, but within that window of time it can be controlled by the potentiometer on its base. When it is on, the color of the orbiting light represents the number of asteroids near Earth. Once completed, the lamp is portable, and can be moved to anywhere as long as it is connected to a computer or portable battery.
Supplies
Electronic components:
- Particle Argon (and breadboard)
- Neopixel lights (apx. 1.5 feet)
- Potentiometer
Tools:
- Hot glue gun
- Knife/box cutter
- Soldering iron and solder
Webhook to Retrieve Sunset Data
Create the URL:
https://api.sunrise-sunset.org/json?lat=(YOUR LATITUDE)&lng=(YOUR LONGITUDE)
Replace (YOUR LATITUDE) with the latitude of your location/another location in your area. Replace (YOUR LONGITUDE) with the longitude of your location/a location in your area. To find your latitude and longitude, you can open Google Maps in your browser, zoom in to your area, and right click on the location you would like the coordinates for.
Create the webhook:
Open the Particle console and go to the "Integrations" tab (https://console.particle.io/integrations).
Click "New Integration" and select "Webhook." Name the event get_sunset. Paste the link you created above that includes your preferred latitude and longitude into the URL field. Set the request type to GET. Save your webhook.
This data comes from the Sunrise and Sunset API - https://sunrise-sunset.org/api.
Webhook to Retrieve NASA Data
Create your NASA API key:
Go to the NASA API Portal (https://api.nasa.gov/) and scroll down to the section titled "Generate API Key." Fill in your information and hit "Signup," then check your email for your API key.
Create the URL:
https://api.nasa.gov/neo/rest/v1/feed?start_date={{{startDate}}}&end_date{{{endDate}}}&api_key=(YOUR API KEY)
Replace (YOUR API KEY) with the API key you created. Do not replace {{{startDate}}} or {{{endDate}}}.
Create the webhook:
Open the Particle console and go to the "Integrations" tab (https://console.particle.io/integrations).
Click "New Integration" and select "Webhook." Name the event get_asteroids. Paste the link you created above that includes your own NASA API key into the URL field. Save your webhook.
Create Pieces of the Base
Create a template:
Draw a 2-inch line on a sheet of graph paper or cardstock. Draw a perpendicular 3-inch line through the midpoint. Use this perpendicular line to align the midpoints of the first 2-inch line and a parallel 4.5-inch line. Connect the right endpoints of the parallel lines and the left endpoints of the parallel lines. This should create an isoceles trapezoid with one 2-inch base, one 4.5-inch base, and a height of 3 inches.
Cut out the cardboard shapes:
Place the paper trapezoid onto a sheet of cardboard. Use a ruler to trace the trapezoid onto the cardboard. Remove the template and cut out the traced shape. Repeat this step 4 times so that you have 4 identical cardboard trapezoids. Carefully cut a small triangle out of the longest base of one of the trapezoids.
Assemble the Base
Use a hot glue gun to line the angled sides of two trapezoids with glue. Press the edges together so that they are aligned and the two trapezoids are stuck together along this edge. Find a surface with a 90 degree corner and press the trapezoids towards the corner (being careful not to stick them to the surface) so that they cool forming a 90 degree angle. Repeat this process with two unglued trapezoids. Then, line each of the unglued edges of the two corners with hot glue and stick the two corners together. This should create a shape like a square pyramid without a point.
Place the Potentiometer
Set the base upright (larger opening down) in front of you. Place the triangle cutout in front of you, then turn the base 90 degrees counterclockwise. On this face, trace your potentiometer. Cut out the traced shape. If the shape is too small, cut out more cardboard or use pliers to push the sides to be larger. Insert the potentiometer into the hole so that it rotates towards the top (narrower side) of the base, not the bottom. Flip the base over so that you can see the inside. Take a piece of masking tape and place it over the back of the potentiometer. Carefully apply pressure so that the pins pierce through the tape. If you bend the pins, take the potentiometer out, straighten them with pliers, and try again. Press the tape onto the inside of the base to hold the potentiometer in place.
Wire Potentiometer
Use a wire to connect the ground pin to the negative rail. Use another wire to connect the 3v3 power pin to the positive rail.
Attach the jack ends of 3 plug-jack wires to the pins of the potentiometer. Pay attention to which wire is attached to which pin. Looking at the inside of the base, the leftmost wire should be connected to the negative rail. The middle wire should be connected to pin A5. The right wire should be connected to the positive rail.
If the connection between the potentiometer plugs and the jacks seems unstable and you are worried that they will fall off, you can wrap a piece of tape around the wires and attach it to the base to keep them in place.
When this step is complete, you should be able to place the base over the top of the Argon. The power cord should go out the cutout on the side, and no other wires should be visible.
Create Pieces of the Shade
Create circles:
Trace a circular object of approximately 4.5-inch diameter onto a sheet of cardboard. Center another circular object of approximately 2-inch diameter within the circle and trace it. Cut around the outer circle and cut out the inner circle to create a cardboard donut shape. Repeat the process with an outer circle of approximately 3-inch diameter and an inner circle of approximately 1.5-inch diameter.
Create rectangles:
Cut out 3 cardboard rectangles with dimensions of 1" x 3".
Assemble the Shade
Take the larger circle and place it on a level surface. Apply 3 short lines of hot glue approximately evenly spaced around the inner circle. Attach the 3 cardboard rectangles, 1/2" base down, to the hot glue. Apply hot glue to the top of each of the rectangles. Attach the smaller circle to the top of the rectangles, trying to keep it centered above the larger circle. Take a strip of cardboard that is approximately 1" wide. Align it with one of the cardboard rectangles so that one end is in the joint where the rectangle meets the top circle. Angle it so that it touches the bottom circle approximately 1/4" from the base of the rectangle. Cut the strip to this length and attach it in this position. Repeat this process with the other 2 cardboard rectangles. At the end of this step, you should have a structure similar to a cone with the top cut off.
Solder Lights
If your lights do not have wires attached to one end, you may need to solder wires to the end of the strip so that they can be connected to the breadboard. This tutorial may be helpful: https://www.youtube.com/watch?v=QYa7MfUcUzc
The lights are directional--of there are arrows on your strip, solder the wires to the end where the arrows are pointing away (shown on diagram). If your strip already has lights in this position, you do not need to do this.
Wind Lights
Note: some photos do not show an actual strip of lights, but a replica made of plastic and paper. These photos were taken after the initial construction of the lamp for added clarity.
Position your lights:
Wind your lights around your cone shape in your preferred positioning. Make sure to keep the end of the lights with the wires near the bottom of the structure (larger circle). When you are satisfied with the position of your lights, take a marker and make a small mark on both the lights and the cardboard at any point where the lights touch the sloped cardboard connections between the top and bottom circles.
Prepare lights to be attached:
At any point on your lights where there is a mark, take a piece of masking tape. Wrap the tape around the lights at that point so that the tape completely encircles the light strip and is stuck to itself.
Attach the lights:
Add hot glue to each of the marker marks on your cardboard frame. Rewind the lights into your preferred positioning so that the hot glue sticks to the tape that was wrapped around the lights. Hold the lights in place while the glue cools. If your light strip is wrapped in such a way that the ends touch, you can also add hot glue to the outside of the casing to stick the ends together.
Wire Lights
Pass the wires of the lights through the center hole of the larger circle of the cone. Pass the wires through the smaller opening in the base. Carefully maneuver the wires within the base and plug them into the Argon. The wire labeled GND should be connected to the negative rail. The wire labeled Din should be connected to pin D2. The wire labeled 5V should be connected to the positive rail.
Set Up Sample Program
Go to build.particle.io and open a new project. Give the project a recognizable title (mine is namedLightTest). Click the folder icon in the left sidebar to save the project.
Open the libraries tab by clicking the bookmark icon in the left sidebar. Type "neopixel" into the search bar. Select the first option, neopixel. It should have a check mark next to it. Click "Include in project," select the project you just created, and confirm your choice. This should create an include statement at the top of your project.
Delete the other code in the project and paste in the code (written below/attached in the .ino file). When this step is complete, click the lightning bolt in the left sidebar to flash the code to the Argon. This should cause the Argon to display blue lights with one rotating orange light when the potentiometer is on, and no lights when the potentiometer is off.
Code:
#define PIXEL_COUNT 17
#define PIXEL_PIN D2
#define PIXEL_TYPE WS2812B
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
int pixelCount = 17;
int analogPin = A5;
bool on = true;
void setup() {
strip.begin();
for (int i = 0; i < pixelCount; i++) {
strip.setPixelColor(i, 10, 0, 0);
}
strip.show();
}
void loop() {
int val = analogRead(analogPin);
if (val < 2000) {
for (int i = 0; i < pixelCount; i++) {
strip.setPixelColor(i, 0, 0, 0);
}
strip.show();
on = false;
}
else {
if (on) {
uint temp1;
uint temp2;
for (int i = 0; i < pixelCount; i++) {
temp2 = strip.getPixelColor(i);
strip.setPixelColor(i, temp1);
temp1 = temp2;
}
strip.setPixelColor(0, temp1);
strip.show();
delay(200);
}
else {
for (int i = 0; i < pixelCount; i++) {
strip.setPixelColor(i, 0, 0, 5);
}
uint color = strip.Color(5, 2, 0);
strip.setPixelColor(0, color);
strip.show();
on = true;
}
}
}
Downloads
Attach the Base and the Shade
While completing steps 13 and 14, I recommend running the above sample program so that you can see that the wires are remaining in place. If you complete these steps and a wire is out of place, it will be difficult to fix it.
Apply hot glue around the top (smaller) opening in the base. Center the larger circle above the opening. Press down to attach the shade and the base and hold in place as it cools.
Add the Bottom of the Base
Place the lamp on a sheet of cardboard and trace the bottom of the base. Cut out this square. Apply hot glue around the edges of the square. Carefully press the base onto the square--make sure that no wires are going to stick out.
Some of the photos of the demonstration lamp show it turned on its side or upside down--after completing this step, unless you have a sticky breadboard that will stick to this base, I would recommend keeping the lamp right side up so that all electronic components remain in place.
Create the Shade
Cut out a strip of tissue paper approximately 1.5-2" wide. Apply hot glue to the edge of the top and bottom circles of the shade over a 1-2" area. Press the tissue paper carefully onto the glue on the top circle. Pulling gently so it is taut, press it onto the glue on the bottom circle. Repeat, moving around the circles, until the entire shade is covered. Cut around the bottom edge to remove excess tissue paper. On the top, fold the tissue paper towards the center and glue it down. Using the same objects you used when you first cut out the top circle, cut out an identical cardboard donut shape. Glue it on top of the tissue paper.
Set Up Firmware
Include libraries:
Go to build.particle.io and open a new project. Give the project a recognizable title (mine is homeAutomation) and hit the folder icon in the left sidebar to save. Open the libraries tab by clicking the bookmark icon in the left sidebar. Type "neopixel" into the search bar. Select the first option, named neopixel. It should have a check mark next to it. Click "Include in project," select the project you just created, and confirm your choice. This should create an include statement at the top of your project. Go back to the libraries tab and type "arduino" into the search bar. Select the ArduinoJson library and go through the steps to include it in your project. You should now have two include statements at the top of your project. Delete the other code in the project.
Determine your time zone:
Determine your time zone relative to UTC. You should be able to represent this as a float between -12.0 and 14.0. For example, CST is 6 hours behind UTC. This time zone would be represented as -6.
Edit the code (below):
Copy and paste the code (written below/attached in the .ino file) into your project, then edit these lines:
In setup(), there is a Time.zone() function. Change the parameter of this function from -6 to whatever float represents the conversion to your time zone.
In loop(), the third line is hour += 6. Change the 6 to your float with the opposite sign (for example: if you were 6 hours behind UTC, your float would be -6, and the line is hour += 6. If you were 6 hours ahead of UTC, your float would be 6, and the line would be hour += -6).
Note: If you have this project up for a long period of time, daylight savings time may mean that your project and the sunset time are no longer aligned. Adjust the lines mentioned above that deal with time to account for this problem.
If you do not have 17 lights on your Neopixel strip, change PIXEL_COUNT to the number of lights you have.
If you know that your Neopixel lights are not type WS2812B, change PIXEL_TYPE to the type of lights you have.
Code:
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
//setting up variables for lights
#define PIXEL_COUNT 17
#define PIXEL_PIN D2
#define PIXEL_TYPE WS2812B
//lights strip
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
//stores time.day to tell when the day is over + when to turn lights off
int currentDay;
//stores data retreived by sunset webhook
String sunset;
String sunsetHour;
String sunsetMinute;
//determine light behavior
bool on = false;
bool night = false;
bool lastOn = false;
bool triggeredToday = false;
bool gotColor = false;
//light strip variable so i can use it for for-loops
int pixelCount = 17;
//stores number of asteroids
int numAs;
//potentiometer
int analogPin = A5;
//date variables--end date has a default so that there are not Problems
String startDate;
String endDate = "2023-01-19";
//doc for sunset response
//StaticJsonDocument<512> doc;
//color of circling light
uint color;
void setup() {
// Subscribe to the webhook response event
Particle.subscribe("hook-response/get_sunset", myHandler);
Particle.subscribe("hook-response/get_asteroids", singleValueHandler);
Serial.begin(9600);
//set time zone so that it turns off at midnight our time
Time.zone(-6);
//lights setup
strip.begin();
//turn all lights off
for (int i = 0; i < pixelCount; i++) {
strip.setPixelColor(i, 0, 0, 0);
}
on = false;
strip.show();
currentDay = Time.day() - 1;
}
void loop() {
//setting up time to compare sunset time to
String currentHour = "0";
int hour = Time.hourFormat12();
hour += 6;
if (hour > 12) {
hour -= 12;
}
if (hour <= 0) {
hour += 12;
}
String hourString = String(hour);
currentHour.concat(hourString);
String currentMin = "0";
int minute = Time.minute();
String minString = String(minute);
currentMin.concat(minString);
int val = analogRead(analogPin);
on = (val > 2000);
//using particle time functions, check if it is currently the currentDay
if (Time.day() != currentDay) {
//if it is not, get the sunset time
triggeredToday = false;
Particle.publish("get_sunset", PRIVATE);
//and update the current day to the new day
currentDay = Time.day();
//and turn off the lights
for (int i = 0; i < pixelCount; i++) {
strip.setPixelColor(i, 0, 0, 0);
}
strip.show();
on = false;
night = false;
//setting up date for url--going from yesterday to today
startDate = endDate;
String year = String(Time.year());
endDate = year;
endDate.concat("-");
String month = String(Time.month());
if (month.length() < 2) {
month = "0" + month;
}
endDate.concat(month);
endDate.concat("-");
String day = String(Time.day());
if (day.length() < 2) {
day = "0" + day;
}
endDate.concat(day);
lastOn = false;
}
sunsetHour = sunset.substring(0, 2);
sunsetMinute = sunset.substring(3, 5);
//check if it is currently past today's sunset time
if (currentHour.endsWith(sunsetHour) && currentMin.endsWith(sunsetMinute) && Time.isPM() && (!night) && triggeredToday) {
//set up date to send to webhook
String data = String::format("{ \"startDate\": \"%s\" } , { \"endDate\": \"%s\" }", startDate.c_str(), endDate.c_str());
//get data. set up the number of colored lights required.
Particle.publish("get_asteroids", data, PRIVATE);
night = true;
gotColor = false;
}
if (night && on && (!lastOn) && gotColor) {
//set up the initial array and turn the lights on
for (int i = 0; i < pixelCount; i++) {
strip.setPixelColor(i, 0, 0, 2);
}
//uint color = strip.Color(10, 0, 10);
strip.setPixelColor(0, color);
strip.show();
lastOn = true;
}
if (night && on && lastOn) {
//if they are on, move the lights by one light
uint temp1;
uint temp2;
for (int i = 0; i < pixelCount; i++) {
temp2 = strip.getPixelColor(i);
strip.setPixelColor(i, temp1);
temp1 = temp2;
}
strip.setPixelColor(0, temp1);
strip.show();
delay(200);
lastOn = true;
}
if (!on) {
for (int i = 0; i < pixelCount; i++) {
strip.setPixelColor(i, 0, 0, 0);
}
strip.show();
lastOn = false;
}
}
//sunset webhook handler
void myHandler(const char *event, const char *data) {
sunset = String(data);
triggeredToday = true;
}
//asteroid webhook handler
void singleValueHandler(const char *event, const char *data) {
String dataStr = String(data);
numAs = atoi(dataStr);
if (numAs > 100) {
color = strip.Color(10, 0, 0);
}
else if (numAs > 75) {
color = strip.Color(8, 1, 1);
}
else if (numAs > 50) {
color = strip.Color(7, 2, 1);
}
else if (numAs > 25) {
color = strip.Color(6, 2, 2);
}
else {
color = strip.Color(5, 3, 2);
}
gotColor = true;
}