Introduction: Smart Map of Idaho With LED Data + Art
I've always wanted a way to artistically and dynamically display geographic data by "painting" a map with light. I live in Idaho and love my state so I thought this would be a great place to start! In addition to being a piece of art with cool illumination effects, it provides useful information as well. For example, you can show a "heat map" per count of population density, precipitation levels, elevation maximums/minimums, number of acres of wilderness area, etc. After doing this map of Idaho, I'm motivated to do something similar on a global scale!
For this instructable you will need the following:
- (2) 2'x4' sheet of 1/4MDF
- (1) 10' piece 1"x8" Pine board
- (1) sheet of light diffusing acrylic
- 2 Strings of (50) ws2812B prewired indexible LED
- 5 volt power supply
- Stain, paint, glue
- Arduino Micro or equivalent
Tools Needed
- CNC Machine
- Soldering iron
- Clamps
- Sand Paper
Step 1: Glue Up the Wood
Whenever I glue up panels of wood, I always biscuit join them together. This prevents splitting due to shrinkage as the wood dries. This is especially important on this project as the surface area connecting the pieces together will be reduced due to the CNC pocketing operations. After getting a good bead of glue in on both sides as well as in both halves of the biscuit cavities, clamp and leave for 24 hours.
After pulling the clamps apart, use a palm sander (or if you are brave a belt sander) and sand the joints smooth. Invariably you will have glue squeeze out the joints and you want to sand the board down to be as flat and blemish free as possible.
now that we have the three panels we will be needing let's move on to the CNC work!
Step 2: CNC the Three Panels (Border Panel, Pine Core and LED Panel)
There are three panels that make up the project. You can see the models in the software I use. The map data was purchased from the excellent royalty free maptorian map packs. Amazing detail and value here! The CAD files are attached in the next step if you'd like either the DXF for the CAD or the Vector files.
The LED core panel is basically a machined 1/4" MDF sheet that holds the LEDs with a tight friction fit. You will notice on this panel a large "pocket" around the LED. This is to allow the light to diffuse as quickly as possible so to avoid hot spots on the acrylic.
The core is the pine panel we glued up in the previous step and represents the backdrop for the project. In order for the light to reach the acrylic panels we machined away each county.
Finally the top panel is machined with just the outlines of the counties and the state boarder. Each county has a small shelf that will receive the 1/8" light diffusing acrylic.
Speaking of acrylic, time to machine these next.
Step 3: Machine the Counties From an Acrylic Sheet
Machining the counties from acrylic took a little trial and error. Acrylic can melt if machined to slowly so a proper feedrate is necessary to get good results. Another tip is to use as large a tool as possible with good suction to clear away the chips. Small tools tend to not clear the chips as easily and build up heat that produces that undesirable melting.
I was able to get the resolution I needed with an 1/8" upcut two flute spiral bit at 18,500rpm and a feedrate of 200ipm. A good feeds and speeds calculator is useful here! I'd recommend the one at cnccookbook.com. A single flute bit would have worked even better but I didn't have one on hand. Keeping small tabs on these pieces in the CAM work is important to keep the finished pieces from breaking away and getting projected into the room!
The magic offset for making counties the right size, turned out to be .075 setback from the centerline on the cad drawing. This made allowance for 1/2 of the 1/8" border plus a little extra for the panel to drop in place. A small amount of sanding was required on certain pieces to get them to fall into place. Again, a bunch of friction fit pieces made this quick and easy work.
Getting all the counties to fit onto a single piece of acrylic was easy work with my vectric software that has a nesting feature to maximize sheet usage.
Just for fun I started test fitting some pieces. Starting to come along. Cool!
Want the files for machining the counties. Sure! See attachment.
Step 4: Paint and Stain
Before we assemble all our pieces, we should paint and stain first. I used a combination of stains for the wood panel, spray paint for the border layer and a reflective white for the LED layer. Quick work and we are on to the assembly. Having fun!
Step 5: Glue Up Panels
Now it's time to glue up the bottom panel to the bottom of the pine core and then the MDF state border panel to the to of the pine core. I just used a series of clamps to do this.
Step 6: Wire Up the LED's With Friction Fit and Connect Arduino
This nightmare job was super simple with the friction fit tolerances here. I used the back end of a pen to press them into place . The virtually snapped in and will not come out without significant force. No glue of any sort was used for this part of the project. This makes assembly, WAY easy! I've done many projects where I've had to wrangle wiring for hours and this literally took 10 minutes. This is by far the easiest way. I tried to wire up the state in zigzag order keeping the groupings such that each county was sequential along the string.
Connecting to the arduino was simple through the use of a small breadboard and connecting wires. The power supply was an ebay purchase. 5v and 8amps is overkill for this project but gives plenty of overhead. Wiring these things is dead simple. +5v to the VCC pin, Ground to the ground pin and then power the strand with the same 5v source. The only remaining pin is the data pin that powers the string! In my case, I used D7 for data. Now on to programming!
Step 7: Coding the Arduino
The LED's are powered by an arduino which makes coding cake. Some of the initial routines were borrowed (ie. stolen) from the excellent ws2813fx library on github. It was easy to modify these routines to do what I needed them to do. The full scope of the code would be difficult to explain in entirety but here's a few highlights!
Here's the available demonstration routines:
<p>#define FX_MODE_STATIC 0<br>#define FX_MODE_BLINK 1 #define FX_MODE_BREATH 2 #define FX_MODE_COLOR_WIPE 3 #define FX_MODE_COLOR_WIPE_INV 4 #define FX_MODE_COLOR_WIPE_REV 5 #define FX_MODE_COLOR_WIPE_REV_INV 6 #define FX_MODE_COLOR_WIPE_RANDOM 7 #define FX_MODE_RANDOM_COLOR 8 #define FX_MODE_SINGLE_DYNAMIC 9 #define FX_MODE_MULTI_DYNAMIC 10 #define FX_MODE_RAINBOW 11 #define FX_MODE_RAINBOW_CYCLE 12 #define FX_MODE_SCAN 13 #define FX_MODE_DUAL_SCAN 14 #define FX_MODE_FADE 15 #define FX_MODE_THEATER_CHASE 16 #define FX_MODE_THEATER_CHASE_RAINBOW 17 #define FX_MODE_RUNNING_LIGHTS 18 #define FX_MODE_TWINKLE 19 #define FX_MODE_TWINKLE_RANDOM 20 #define FX_MODE_TWINKLE_FADE 21 #define FX_MODE_TWINKLE_FADE_RANDOM 22 #define FX_MODE_SPARKLE 23 #define FX_MODE_FLASH_SPARKLE 24 #define FX_MODE_HYPER_SPARKLE 25 #define FX_MODE_STROBE 26 #define FX_MODE_STROBE_RAINBOW 27 #define FX_MODE_MULTI_STROBE 28 #define FX_MODE_BLINK_RAINBOW 29 #define FX_MODE_CHASE_WHITE 30 #define FX_MODE_CHASE_COLOR 31 #define FX_MODE_CHASE_RANDOM 32 #define FX_MODE_CHASE_RAINBOW 33 #define FX_MODE_CHASE_FLASH 34 #define FX_MODE_CHASE_FLASH_RANDOM 35 #define FX_MODE_CHASE_RAINBOW_WHITE 36 #define FX_MODE_CHASE_BLACKOUT 37 #define FX_MODE_CHASE_BLACKOUT_RAINBOW 38 #define FX_MODE_COLOR_SWEEP_RANDOM 39 #define FX_MODE_RUNNING_COLOR 40 #define FX_MODE_RUNNING_RED_BLUE 41 #define FX_MODE_RUNNING_RANDOM 42 #define FX_MODE_LARSON_SCANNER 43 #define FX_MODE_COMET 44 #define FX_MODE_FIREWORKS 45 #define FX_MODE_FIREWORKS_RANDOM 46 #define FX_MODE_MERRY_CHRISTMAS 47 #define FX_MODE_FIRE_FLICKER 48 #define FX_MODE_FIRE_FLICKER_SOFT 49 #define FX_MODE_FIRE_FLICKER_INTENSE 50 #define FX_MODE_CIRCUS_COMBUSTUS 51 #define FX_MODE_HALLOWEEN 52 #define FX_MODE_BICOLOR_CHASE 53 #define FX_MODE_TRICOLOR_CHASE 54 #define FX_MODE_ICU 55</p>
And a look into one of the sample routines.
<p>uint16_t WS2812FX::mode_breath(void) {<br> // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // step uint16_t breath_delay_steps[] = { 7, 9, 13, 15, 16, 17, 18, 930, 19, 18, 15, 13, 9, 7, 4, 5, 10 }; // magic numbers for breathing LED uint8_t breath_brightness_steps[] = { 150, 125, 100, 75, 50, 25, 16, 15, 16, 25, 50, 75, 100, 125, 150, 220, 255 }; // even more magic numbers!</p><p> if(SEGMENT_RUNTIME.counter_mode_call == 0) { SEGMENT_RUNTIME.aux_param = breath_brightness_steps[0] + 1; // we use aux_param to store the brightness }</p><p> uint8_t breath_brightness = SEGMENT_RUNTIME.aux_param;</p><p> if(SEGMENT_RUNTIME.counter_mode_step < 8) { breath_brightness--; } else { breath_brightness++; }</p><p> // update index of current delay when target brightness is reached, start over after the last step if(breath_brightness == breath_brightness_steps[SEGMENT_RUNTIME.counter_mode_step]) { SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (sizeof(breath_brightness_steps)/sizeof(uint8_t)); }</p><p> int lum = map(breath_brightness, 0, 255, 0, _brightness); // keep luminosity below brightness set by user uint8_t w = (SEGMENT.colors[0] >> 24 & 0xFF) * lum / _brightness; // modify RGBW colors with brightness info uint8_t r = (SEGMENT.colors[0] >> 16 & 0xFF) * lum / _brightness; uint8_t g = (SEGMENT.colors[0] >> 8 & 0xFF) * lum / _brightness; uint8_t b = (SEGMENT.colors[0] & 0xFF) * lum / _brightness; for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { Adafruit_NeoPixel::setPixelColor(i, r, g, b, w); }</p><p> SEGMENT_RUNTIME.aux_param = breath_brightness; return breath_delay_steps[SEGMENT_RUNTIME.counter_mode_step]; }</p>
Full source can be downloaded from the ws2812fx github repository.
Step 8: Enjoy the Artistic Light Display
I was very happy with the result! It really is a joy to watch and I'm excited to continue to play around with various data display configurations! Feel free to ask any questions or hit me up for any information I missed.

Second Prize in the
LED Contest 2017

Runner Up in the
Arduino Contest 2017

Participated in the
Homemade Gifts Contest 2017
19 Comments
5 years ago
I'm from Idaho! So cool. Would love to have one in our school library.
5 years ago
That is very cool. I live in New Zealand now but lived in Latah County for seven years. Your project is even more special because of the state you chose but everyone would love there own state.
5 years ago
This looks great and has inspired a project idea for me already.
But your title says data… Where's the data?
Reply 5 years ago
I have visions of incorporating more sophisticated data in the firmware. For now, all I have is a static "screen" that shows population density per county and some water statistics. It would be cool to have it interactive and pulling data from the cloud real-time.
Reply 5 years ago
You should check out the ESP8266 or the ESP32, both have wi-fi. You could probably query the DarkSky API for county-level weather data (temperature, precip probability, ect): https://darksky.net/dev
Also, I would probably remove the Arduino from Owyhee County, it cast too much of a shadow.
5 years ago
I love it. That was an awesome idea! I could see you getting request for other states/countries.
Reply 5 years ago
Thanks. I am considering doing a kickstarter campaign with a world map version. That could have a lot of potential for artistic data visualization!
Reply 5 years ago
Yes! I would back that campaign. With the right software you could encourage a user community to build different visualizations. Maybe it would need to be Pi or Yun based so it could pull from live data sources?
Build related question, would laser cut acrylic be easier than cnc? I dont have cnc experience but found that laser cut ‘puzzle piece’ projects like this were really easy. Just $0.02.
Excellent work and a brilliant idea. Idaho ftw!
5 years ago
Thank you for the great instructable! Got a DIY cnc but always had problems with cutting cast acrylic and destroyed quite a lot of my bits.
Unfortunately I can only go up to 100 ipm. Is there any bit that you would suggest for cutting acrylic?
Oh and maybe you should have a look at an ESP8266 instead of the Arduino.
There is a GitHub project with a web interface out there:
https://github.com/jasoncoon/esp8266-fastled-webse...
I currently use it for all my projects!
Reply 5 years ago
That's actually really helpful! Thank you for the tip. Very inexpensive - amazingly so.
Reply 5 years ago
The larger the bit the better and single flute upcut spiral bit will do wonders. Also if you can shoot a bit of air to keep things cool, that will also help. I'm cutting closer to 250ipm.
5 years ago
Great project! Thanks for sharing. What I do not understand is - what for is the mdf-layer? You could have routed a recess into the wooden board to fit the acrylic. The way you did it you had to cut the map twice. Merry Christmas from Germany!
Reply 5 years ago
Great question. The top layer could have definitely been worked into the pine core but there are two reasons that prevented me from doing this. First, I wanted the raised section black and that would have required tedious painting. It was easier to just paint it separate and glue it on. Second, I actually needed that extra 1/4 inch to help diffuse the light. Putting the LEDs too close to the acrylic makes undesirable hot spots. The extra 1/4 helps a lot.
5 years ago
great work
5 years ago
That is really cool. Especially since I am from Idaho myself.
5 years ago
Very cool! Can't wait til I get a CNC so I can start taking inspiration from builds like this
Reply 5 years ago
p.s. Good luck in the LED contest, I have a feeling we'll both be in the finals :)
5 years ago
looks awesome!
5 years ago
Kootenai County checking in! Pretty cool. I'd like to see it used to display some sort of geographic or demographic data. I'd say to color code the net influx of people from California on a per county basis, but that might be too depressing. ;-)