Introduction: Smart UV Index Display
Ultraviolet light is invisible to the human eye but has some very important aspects to it. I wanted to create a display that provided a better way to track the UV index throughout the day. Utilizing the Open Weather API, the display is capable of displaying real time weather information. As a bonus, it also doubles as a beautiful art piece that changes with the sun. I chose purple for my design because it represents the color of UV light
Supplies
Most of the project's cosmetic design revolves around 3d printing. However, with the necessary electronics and a little bit of creativity, this project can be replicated with household items.
MATERIALS
- Plastic Poster Board - Used to diffuse light. Can be found at most craft stores.
- ESP8266 Microcontroller - D1 mini style
- Addressable Led Strip - 60 pixels per meter
- Power Source - 5v USB
TOOLS
- Adhesive - Tape or glue to adhere components
- Craft knife/Scissors
- Printout Stencil
- Basic Soldering Supplies
Step 1: 3D Printing
Everything was modeled using Fusion 360.
All parts can be printed on the Creality Ender 3 pro using PLA with a 0.2mm layer height.
Since some of the models have minimal overhangs, enabling supports will yield the best result.
Step 2: Diffusion
This project utilizes Plastic Poster Board as a way to diffuse the individual LEDs. It is a semi translucent sheet made out of 12 point polyethylene material. Aside from its durability, these sheets are generally very inexpensive.
Print out the provided stencil at actual size. It is crucial that the printout is not scaled, otherwise the pieces will not fit.
Transfer the pattern onto the diffusion material and cut out the individual pieces.
Insert each piece into its respective spot and check to see that each one fits well.
Use adhesive to secure each piece from behind. - Be careful to only apply the adhesive around the edges of each pane. Any glue that creeps into the line of sight will become extremely visible when backlit.
- Paper is another great way to diffuse light. As an alternative, the stencil cutouts can be used in place of the plastic.
Step 3: Electronics
The electronics for this project are very straight forward.
Since internet connectivity is required, an ESP8266 was chosen for this project. Unfortunately, the ESP8266 operates at 3.3 volts, while the addressable LEDs run at 5 volts. The D1 mini is equipped with a 5v output, but there will still be a discrepancy in the logic levels. To prevent any related damage, a logic level shifter should be included for safety. The schematic above shows the complete wiring diagram for the display. To simplify the design, I decided to forgo the level shifter and risk the possibility of future issues.
Following the schematic, cut the led strip so that there is a strip of eleven pixels.
Solder power and data wires to the start of the strip, checking that they are long enough to comfortably reach the microcontroller in the base. The data pins for addressable LEDs typically only work in one direction. Check for a Data In (DI) labeled soldering pad or for arrows signifying the correct data direction (Data In to Data Out).
If connectors are available, crimp them between the wires and the microcontroller. Otherwise, slot the wires through the base and solder them directly to the microcontroller.
Step 4: Assembly
With the diffusion panels already secured to the front plate, slot the LEDs into the designated groove positioning one pixel per pane. Adhere the strip to the slot with glue or tape.
Snap the back plate on and glue as desired. The two pieces will be secured together by the base so they do not need to seat snugly.
Press fit the upper assembly into the designated slots on the base guiding the LED wires through the passthrough hole.
Insert the USB cable into the hole in the fit, securing with glue if needed.
Finally, connect all wires and plug the microcontroller into the USB, coupling it to the assembly.
Step 5: API Setup
Weather data, necessary for this project, will be collected using the OpenWeatherMap API. The base subscription of this resource is completely free and provides plenty of interesting data. In order to collect this data, a user specific API request must be generated.
First, visit the OpenWeatherMap website and set up a new account.
Navigate to the 'API keys' tab on the account page to find the Default API key. This key is required to access user specific data. - API key activation does take some time after generation. Expect a delay before it becomes usable.
The One Call API Document provides plenty of information on generating a custom API call and parameters. The API call can be tested by manually typing it into an internet browser.
Parameters go as follows:
http://api.openweathermap.org/data/3.0/onecall?lat={lat}&lon={lon}&exclude={part}&appid={API key}
{Lat}/{Lon} : Enter respective user specific Latitude and Longitude geographical coordinates
{API key} : Enter unique API key found in the previous step.
{Part} : Excludes specific parts of the weather data not needed in the project. Since this project relies on the current weather data, Minutely, Hourly, Daily, and Alert data can be excluded to optimize the call.
Filling out the parameters as needed should yield the following link:
http://api.openweathermap.org/data/2.5/onecall?lat=38.8895&lon=-77.0353&exclude=minutely,hourly,daily,alerts&appid=46819096f5b3caddd467604d94671893
Typing the API call into a browser should return an html readout providing current local weather information. In the case of error codes, review the reasoning, adjust the link, and test again.
A current weather API response should return similar JSON data:
{ "lat": 38.8895, "lon": -77.0353, "timezone": "America/New_York", "timezone_offset": -14400, "current": { "dt": 1658212453, "sunrise": 1658224680, "sunset": 1658277029, "temp": 296.8, "feels_like": 297.57, "pressure": 1009, "humidity": 90, "dew_point": 295.06, "uvi": 0, "clouds": 40, "visibility": 10000, "wind_speed": 1.54, "wind_deg": 260, "weather": [ { "id": 802, "main": "Clouds", "description": "scattered clouds", "icon": "03n" } ] } }
Depending on the browser and user extensions, the API data may be formatted differently:
{"lat":38.8895,"lon":-77.0353,"timezone":"America/New_York","timezone_offset":-14400,"current":{"dt":1658212453,"sunrise":1658224680,"sunset":1658277029,"temp":296.8,"feels_like":297.57,"pressure":1009,"humidity":90,"dew_point":295.06,"uvi":0,"clouds":40,"visibility":10000,"wind_speed":1.54,"wind_deg":260,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}]}}
As long as data is returning, the API is in working condition. The category titled "UVI" will be relied on for this project.
Step 6: Code Setup
The code for this project is written in C++ using the Arduino integrated development environment. In order for it to run properly, the following libraries must be installed:
- FastLED.h
- ESP8266WiFi.h
- ESP8266HTTPClient.h
- WiFiClient.h>
- Arduino_JSON.h
When setting up the code several inputs are required. First, the local wireless internet information must be entered between the quotation marks, in the designated locations.
Next, enter the entire API call generated in the previous Instructables step. - Most browsers use Hypertext Transfer Protocol Secure (https). Using the https protocol will not allow the API data to be collected: http must be used NOT https.
////////////WIFI/////////// const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASSWORD"; String API_ADRESS = "YOUR_API"; //HTTP NOT HTTPS ///////////////////////////
Don't forget that the ESP8266 runs on standard 2.4Ghz networks and will not work on a 5Ghz bandwidth.
With the code flashed, the ESP will print updates through serial. During the setup, the text "Connecting..." will be printed while it is attempting to connect to the local Wi-Fi. A successful connection will cause the ESP to print its local IP Address.
Following the setup, the ESP will attempt to call the API. First, to confirm there are no typos, the link will be printed. This printout will be followed by the HTTP Response Code. Ideally, the response '200' indicates that the request has succeeded. If successful, the significant information will be displayed. In the case of a different response code, a quick Google search will be able to interpret the issue.
Step 7: Reading the Display
The display simultaneously conveys ultraviolet information in two different ways.
- Through a designated pixel color
- Through display brightness
Each lighted section of the display is considered to be one UVI zone. Zone one represents all UVI values between 0-1, while zone 11 consists of all values falling between 10-11. Each mediating zone accounts for each iterating integer value between. An UV index of 8.33 would result in the display highlighting the tenth zone (8-9) with a solid color. This color is decided in the code and can be altered depending on user preferences.
Additionally, the display more accurately conveys the current UVI value through brightness. As the index increases throughout the day, the LED strip brightness will also continue to increase. Subsequently, after the sun has set and the UVI value is back to zero, the display will be off during the nighttime, until the sun rises again.
Step 8: Code Walkthrough
The project uses the Fast LED library to control the addressable pixels. Fast LED is an extremely powerful resource and this project only brushes the surface of its capabilities. A section in the code is dedicated to initializing Fast LED's compatibility with the LED lights.
/////////FASTLED/////////// FASTLED_USING_NAMESPACE #define DATA_PIN 2 #define LED_TYPE WS2812B #define COLOR_ORDER GRB #define NUM_LEDS 11 CRGB leds[NUM_LEDS]; #define FRAMES_PER_SECOND 120 ///////////////////////////
Prior to the code's loop is a pattern list, used to organize an array of different light animations. Each list item is linked to a void located at the end of the code. The voids run a unique LED pattern to change up the display throughout the day The program is designed to cycle through each of the sequences through the day. Additional items can be included to add more animated lighting effects. The last sequence in the list is reserved for a nighttime effect. This is set to only run while there is no sunlight and the UV Index is zero.
// List of patterns to cycle through. Each is defined as a separate function below. typedef void (*SimplePatternList[])(); SimplePatternList gPatterns = {rainbow, juggle, night}; //Last List Item Only Occurs When UVI = 0
Within the code loop, there are several functions that alter the appearance of the display.
To change the color of the UV indication light 'White' can be replaced with a very long list of Predefined Colors by altering the following line of code:
if (sunLight == true) leds[uvi] = CRGB::White;
The EVERY_N_SECONDS and EVERY_N_MINUTES functions set the amount of time that elapses before the section of code is run again. The time between animation patterns can be directly altered within the loop. The time between API calls is assigned though the 'callDelay' integer and can be adjusted within the indexUV void.
The indexUV void runs following the acquisition of a new data JSON file from the API call. It extracts and interprets the UV data.
Here the variable brightness can be adjusted:
float brightness = (index/10)*255; //Varies Brightness Depending On UVI
The current code calculates the current index value out of 10 and corresponds that percentage to pixel brightness. For example, a UV Index of 4 would result in a pixel brightness of 102 out of 255 (max brightness). In other words, since 4 is 40% of 1, the LEDs will be set to 40% brightness. Although the UV Index commonly exceeds 11 in some regions, using a lower percentage base value, such as 10, allows the full pixel brightness to be enjoyed more often, while still conveying the appropriate information.
An if statement is used to set the time delay between between API calls by changing the 'callDelay' value. The time between API calls is set to be changed depending on the time of day to prevent exceeding the API allotment. As shown below, there are three different results that can occur when executing the if statement.
if (index > 0) { ... } else if (index == 0) { ... } else { ... }
- If the UV Index is found to be above zero, it is assumed that the sun is out and the time between API calls is shortened to increase the resolution of the data output.
- Once the UV Index is at zero, it is assumed that the sun has set, the time between API calls is extended, and the nighttime sequence void begins to run.
- Finally a third case exists as a catch all safety measure to prevent any accidental overages. If an unexpected value, such as 'NULL' is returned, the program will decide that there is a 'Possible Data Error' and maximize the time between calls. The display conditions prior to the possible error will continue to execute during this time. There is a good chance that everything will return to normal following the next API call. If an issue continues to persist, the HTTP Response Code can be used to help decipher the issues.