Project tutorial
Uke on fire

Uke on fire © GPL3+

Ukulele illuminated by LED elements w/ illumination depending on result of FFT analysis of ukulele's sound.

  • 2,259 views
  • 0 comments
  • 4 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)
4966285
Solder Wire, Lead Free
70y8518 40
Drill / Driver, Cordless
Saw (tiny, generic)
Wood glue
Rubber, foam
Craft glue

About this project

The story

I was wondering how an illuminated music instrument might look like in the dark.

To allow for a certain dependence of the illumination pattern and the instrument's sound, audio spectrum analysis seemed to be a reasonable approach.

As instrument I chose a ukulele since there are DIY (do it yourself) construction kits available at a reasonable price and I had a crude idea of how to get the LEDs in.

Here a quick glance at the result: A concert ukulele w/ 3 optical elements (2 LED strips in the fretboard, 1 LED ring around the sound hole).

UPDATE 1: Meanwhile I built a 2nd (larger) LED Ukulele and added some burning fire effect, see below.

Ideas for the future:

  • Use LEDs do display grip patterns for chords or even simple solo lines based on input from a mobil device wirelessly coupled. Update 2020-07-15: Stumbled across something like this here: https://www.instructables.com/id/LED-Ukulele-Level-One/

Hardware

The Ukulele

It all starts with buying an appropriate DIY ukulele construction kit. The following aspects seem most important to me:

  • "Size" of the fretboard. Since a certain mounting space for the LED PCBs and the wires is necessary, I went for a concert ukulele (aka "23 inch". Lenght of measure ~38 cm. The most common ukulele, the soprano ukulele, is definitely too small for this kind of project using the electronic hardware described below.). From my "theoretical studies" I thought that the width of the fretboard is ~4 cm at its smallest extent and the distance between the first 10-12 frets (which I wanted to use for displaying the result of the audio spectrum analysis) should also be ~>1 cm for the closest frets (i.e., frets 10, 11, 12). However, if I would repeat this project, I would probably go for the next larger type of Ukulele, i.e., a tenor ukulele, since it turned out that - probably due to manufacturing tolerances and my limited tool set and craftsmen skills - I was not able to follow my original plan. I needed more construction space to get all the LED PCBs in - or smaller LED PCBs. (The LED PCBs I used were about 9-10 mm in diameter and ~3 mm in height).
  • UPDATE 1: Meanwhile I found a suitable "larger" (i.e., 23" aka tenor ukelele DIY kit. However it turned out that the width of the fretboard at its upper end is still too narrow to put 4 LEDs in line. Nevertheless it was possible to fit 4 LEDs w/i each of the frets 1 to 11.
  • Make sure that you buy a DIY ukulele construction kit that comes with separated neck and fretboard (since in between you will have to put the LED PCBs). W/ respect to the fretboard make sure its thickness (typically 4mm) matches the LED PCBs you choose (in my case LED PCB heigth ~3mm) so that sufficient installation space is provided. Maybe getting 6 mm thick fretboard would also be a good idea mitigating some of the manufacturing challenges.
  • Most of the DIY construction kits come along w/ rather poor tuning pegs. Those typically easily detune and so you might end up with a nicely illuminated but awfully sounding ukulele. So it might be a good idea to get appropriate tuning pegs along with your DIY ukulele construction kit. What do I mean by appropriate ? There are tuning pegs that basically are straight and other ones that go around by 90° (using worm gear and pinion) in the headstock of the ukulele. Latter ones I prefer. Both types are shown in one of the pictures below. Attention: There is up to my experience no standard for the diameter of the holes for tuning pegs in the headstock. So make sure you get matching ones. I was lucky since I had some old tuning pegs lying around at home which fitted well enough.
  • Last but not least you also might want to get suitable strings, since the ones shipped with some of the construction kits are rather cheap, resulting in - again - frequent detuning and/or poor sound.

Electronics

Due to the limited space available (its a ukulele, not a guitar) I chose an Arduino Nano. Its memory and speed should be sufficient for the audio spectrum analysis and it works w/ the FastLED library used for control of the LED PCBs I connected to WS2812B type strips.

To get the ukulele's sound to the Arduino Nano I decided to use a brakeout board by Adafruit (Adafruit MAX4466 Electret Microphone) which records the sound using a broad band electret microhpone and allows adjustment of the gain. Therefore it is possible to adjust the input sound level to the Arduino (since I have no clue how the sound distribution w/i the ukulele's body looks like). Also the package is small enough to meet the requirements w/ respect to limited construction space w/i the ukulele. The 470µF capacitor between Aruino Nano's 3.3 V and GND serves to reduce noise in the 3.3 V power supply line (otherwise resulting in flickering of the 1st two LEDs in each LED strip in the fretboard. Just remove it in your breadboard test setup and you will understand what I am talking about.).

I also added a potentiometer that allows to adjust the brightness of the LEDs. This might come in handy to adjust the LEDs brightness to surrounding light level. Too bright LEDs might be disturbing in a rather dark room. Furthermore, in case your power supply runs empty or all LEDs are lit at maximum brightness, the voltage might drop to a level that leads to unstable operation of the Arduino.

A simple push button was added to facilitate selection of different lightning patterns/operating modes. This can be simply changed (e.g., new combinations added or removed) w/i the software. In case the push button is pressed during execution of the setup part of the code (practically just push it before you power your electronics) all LEDs will turned on and off once to provide a quick way to check functionality of the LEDs/connections to the LEDs.

Typical available LED strings (RGB, separately addressable) have equally spaced LEDs. Due to the changing distance between the fret bars this was not a useful approach for my project. I stumbled across single LEDs of WS2812b type (i.e., RGB LEDs, w/ on board WS2811 IC, see picture below) that can be easily controlled using FastLED library just as typical WS2812B LED strings. Make sure the height of these LED PCBs is appropriate for the thickness of your fretboard. Unfortunately I cannot provide a link to the seller since he does not offer such LEDs anymore. :-(

The rest ist wire, solder - and patience.... (Why patience ? 30 LED PCBs w/ 6 closely spaced solder connections each combined w/ limited tool set and craftsmen skills... you'll find out by yourself...)

Manufacturing Part 1: Add LEDs to the ukulele

The most crucial and uncertain part to me was whether I could manufacture the fretboard containing the LED PCBs, so...

...I started with marking the positions for the LED PCBs on the backside of the fretboard. After that I drilled some dimpels centered at the marked positions and just deep enough so that they can accomodate the LED PCBs from/into the rear side of the fretboard (~3mm in my case). Here a thicker (as above mentioned 6 mm fretboard) would have given me the option to provide some installation space for the solder and wires, however, I only had a 4 mm thick fretboard available.

This is when I also had to realize that especially in the upper, narrow part of the fretboard (frets 1 to 5) I could not achieve the necessary precision to put 4 LED PCBs next o each other w/i one fret. This is why I finally ended up with only 2 LED PCBs in frets 1 to 5 and 4 LED PCBs in frets 6 to 10 (giving a total of 10 + 20 = 30 LED PCBs).

Next, I glued the LED PCBs into the dimples using ordinary handicraft glue. Since the LED PCBs had 6 solder pads (2x GND, 2x VCC, 1x Din, 1x Dout), I decided on the orientation shown in the picture below. Pay attention to the direction of data flow ! Since there are 2 interconnected GND and VCC pads per each LED PCB, respectively, there is only 1 DataIn and 1 DataOut solder pad, so here the order/direction of connection / orientation of the LED PCB is important. The chosen orientation and alignment allows for straightforward connection of GND and VCC of each LED PCB.

Furthermore (due to the fact mentioned above that I did not succeed in having 4 LEDs in all the frets) I split up the LEDs in 2 seperate optical elements/strings. To save precious installation space GND and VCC for the 2nd LED string are connected directly to an appropriate position of the 1st LED string (thereby saving separate VCC and GND wires for the 2nd string).

Now the fretboard with the LED PCBs has to be mounted to the neck. If one is able to connect the LEDs with wires in a perfect straight line, one could think of milling channels for the cables into the neck. I also tried using copper tape, however due to the topology I did not manage to get sufficient electric contact.

However, as you can see in the picture below, in my case the wire connections are far from being lined up in a straight way. Furthermore, while the dimples could well accomodate the LED PCBs, the solder however reached out of those dimples. Therefore I decided to use some foam rubber ("Moosgummi") ~2 mm thick. I used multipurpose glue/handicraft glue to glue that foam ruber in between the fretboard and the neck. This is one of the most delicate manufacturing steps, since it is not reversible. Like in case you would just manufacture the ukulele (w/o any LEDs) using the DIY construction kit, you have to compress this triple stack (neck, foam, fretboard) for quite a while and with just the right pressure. Numbers ? No idea. Listen to the craftsman inside you. Also check out the video tutorials if you need information about how to put the pieces together to finally yield a ukulele.

I was quite happy to see that I could still control all the LEDs after this challenging manufacturing step. About the glue: I recommend to make some tests before you do the above mentioned non-reversible manufacturing step. Wood glue did not do the job in my case. It did not stick to the rubber foam. Another "office desk" glue I tried did not stick to the neck. Finally my wife came up with the right glue. Thanks! Also make sure your glue is neither conductive (this might short circuit some parts of the LED PCBs or cables) nor has aggressive solvents that might damage the LED PCBs. At first I was worrying that the foam rubber might decoule/damp the ukulele's strings vibration too much leading to a silent sound of the ukulele. This came not true, at least not to my impression (although I did not compare it to a 2nd ukulele manufactured based on the same DIY construction kit w/o optical elements and/or foam rubber).

After having achieved this, the assembly of the rest of the ukulele is pretty much straight forward. Tutorials, videos and alike can be found in the www. This is where I bought my DIY kit and they also link to tutorial videos: (https://www.kirstein.de/Konzert-Ukulelen/Classic-Cantabile-UC-240-DIY-Ukulele-Bausatz-Konzert.html?userInput=ukuele&ignoreForCache[]=userInput&queryFromSuggest=true&ignoreForCache[]=queryFromSuggest).

I also added a third optical element: An LED ring surrounding the soundhole of the ukulele. This LED ring has similar tye of LEDs (probably also WS2812B, but nit 100% sure anymore) and therefore can be controlled by same software routines as the two LED stripes in the fretboard. Details see software description below. My impression is that this LED ring does have an influence on the ukulele's sound. This is plausible since a string instrument's sound is - at least to my understanding - largely influenced by the vibration of the upper and lower part of the body. Currently I glued the LED ring to the body w/ double sided sticky tape. Maybe it would improve the situation if attach it only w/, e.g., 4 screws instead of the tape.

Manufacturing Part 2: add electronics & circuitry to the ukulele

The mounting positions I chose are shown in some pictures below. My boundary conditions were:

  • Possibility to repair/bug fix hardware: Therefore all the components are fixed by screws (nothing glued or clamped or alike) to allow unmounting/disassembly. Also only 1 end of each cable was soldered. The other end was connected to the Arduino Nano using female jumper wire connectors.
  • Arduino's USB connector should be easily accessible for future software updates.
  • "Large area components" such as the microphone breakout and the Arduino Nano should be mounted to and fixed at the ukulele's body without any tilt (-> requires usage of surface of the ukulele's body with as little curvature as possible.)
  • Push button for lighting mode selection should be easyly accessible during playing the ukulele.
  • Wires (e.g., power and data to LED elements or power supply of the whole system) shoud not be where "moving parts" (i.e., the strumming or picking hand or the hand pushing the ukulele's strings onto the fretboard) wil be during usage.

While for some elements the mounting holes could be established simply by drilling, for other parts a combination of drilling and sawing had to be used.

Finally a few words about the power supply. Typically the WS2812B LEDs operate already at pretty low voltages between 3.5 and 5 V. While 3.5 V is too low for the Arduino to operate reliable, more than 5.something V will damage the LEDs. Since I wanted to use rechargeable batteries I decided to use 4x rechargeable NiMH accumulators of AA size. ATTENTION: In case someone puts in non-rechargeable batteries (which typically supply 1.5 V each) the power supply might then kill your LEDs. (I personally stronlgly dislike such a scenario since most of the work went into getting the LEDs into the fretboard. Any ideas for simple protection circuitry ?)

About current sonsumption: Assuming all 3 LED elements are fully lit at medium (~0.5) brightness and "half white" (i.e., mixture of various colors) and a single fully white lit WS2812B LED consumes 60 mA, a current of about (10 + 20 LEDs) * ( 0.5 x 60 mA) * 0.5 ~ 450 mA is necessary, which can be supplied by the batteries chosen. Cranking up the brightness (using the potentiometer) leads to significantly higher power consumption and sometimes (I assume) instable operation of the Arduino Nano (due to voltage drop) and "strange" or "stuck" lighting patterns.

The power supply will be attached to the backside of the ukulele's body by some hook-and-loop-fastener ("Klettverschluss") to achieve really mobile application.

That's it for the hardware. Anything else is software.

Software

I learned a lot about FFT and WS2812B LED control when trying to reproduce Antoine Rochebois project (https://create.arduino.cc/projecthub/AntoineKia/interactive-led-table-for-50-650b83?ref=platform&ref_id=424_trending___&offset=84) using an Arduino Uno.

So I basically plugged things together and modified a few parts and added a few lines of code. The comments in the code below should contain all the necessary explanations.

General

For each LED strip/ring a separate array is used (to allow easy access and programming).

Control (Hue, saturation and value) of each LED strip/ring is done by separate subroutines - which only differ in the names of the relevant variables (array name, number of LEDs in array and alike). This could probably be improved by using only 1 subroutine/function which is called w/ appropriate set of parameters. However I am not that experienced in programming, especially when it comes to handing over arrays to functions. And due to the limited number of LEDs luckily this is not necessary.

Setup

In the setup routine each LED is lit once to check if each of them is still working.

Main

In the main part, first the audio data is collected, followed by FFT.

After that the LED brightness is determined by evaluating the potentiometer setting. This as well as the audio data collection is done in the so called Free Running Mode of the ADCs in the Arduino by directly accessing the microprocessor's registers.

Next the lightning pattern is determined based on the number of times the push button was hit. (If button has not been hit, all optical elements are off !)

Finally the corresponding values are written to the LED arrays and everything is shown by calling FastLED.show().

Some final remarks

While I still like the changing illumination of the ukulele while playing, I have the impression that the pattern is not really the result of an FFT. This might, however be a consequence of mapping the audio spectrum from 20 Hz - 20 kHz to only 2 (frets 1 to 5) or 4 (frets 6 to 10) LED bins, respectively. In addition each LED bin has a very limited height of only 5 LEDs. On the other hand it might be true that the sound prouced by the ukulele - epsecially after the project specific modifications - is far from sine waves, leading to broad band FFT response that makes it hard to see any differences for low or high frequencies played. Or maybe the stiff mechanical coupling of the microphone brakeout board to the ukulele adds some "mechanical" noise/vibrations/frequencies to the intended audio input. Something to maybe follow up in the future....

Nevertheless, I learned a few things during my project and the result still satisfies me. Construcive comments and improvement suggestions welcome!

Hope you enoyed reading this tutorial and it helps you in case you do a similar project

Good luck, lots of insight and especially fun!

Code

Arduino code for "UkeOnFire" projectArduino
Sketch used for illuminating a modified ukulele w/ different patterns based on FFT analysis of audio signal.
Here code for "smaller" ukulele w/ 20 + 10 LEDs. Can be easily adjusted to more LEDs, see corresponding sections in code.
/*  
 *   
 * Control of several LED strips mounted to a Ukulele (tuning: G - C - E - A)
 *   - NR1: D3: LED ring @ sound hole of Ukulele (16 LEDs)
 *   - NR2: D5: LED strip @ lower part of fretboad (Frets 6 to 10, lower left LED @ fret 10 (i.e., on A-string) = #1 for adressing. 4 "rows", 5 LEDs per row => 20 LEDs in total)
 *          - lowest row ("A-string") displays intensity of lowest ("bass") frequencies, ..., highest row ("G-string") displays intensity of highest frequencies
 *   - NR3: D6: LED strip @ upper part of fretboad (Frets 1 to 5, lower left LED @ fret 5 (i.e., on G-string) = #1 for adressing. 2 "rows", 5 LEDs per row => 10 LEDs in total)
 *          - lowest row ("E-string") displays intensity of lowest ("bass") frequencies, ..., highest row ("C-string") displays intensity of highest frequencies
 *   
 *   Inputs:
 *   - A0: Audio signal (recorded from a microphone pointing inside the Ukulele) (--> ADMUX = 0x40)
 *   - A1: Brightness of (all) LEDs (selectable via a potentiometer) (--> ADMUX = 0x41)
 *   - D2: Mode selection (determined by counting how often a switch button is pressed)
 *   
 *  Inputs/ideas and (modified) software parts w/ respect to FFT and LED control with Arduino based
 *   - on project by Antoine Rochebois. Thanks ! https://create.arduino.cc/projecthub/AntoineKia/interactive-led-table-for-50-650b83?ref=platform&ref_id=424_trending___&offset=84
 *   - LED fire effect inspired by various examples in the www, e.g., Mark Kriegsman's code under https://pastebin.com/xYEpxqgq
 * Written by T. Geppert, April/May 2020
 */

/*
 * Constants and alike
 */

// LED stuff =============================================================================
// LED Ring / Stripe ---------------------------------------------------------------------
#include <FastLED.h>
#define NUM_LEDS_NR1 16                       // Number of LEDs in ring (aka "NR1")
#define LED_DATA_PIN_NR1 3                    // Data Pin for control of in NR1
#define NUM_LEDS_NR2 20                       // Number of LEDs in strip 1 ("lower" LED strip="closer to body") (aka "NR2")
#define LED_DATA_PIN_NR2 5                    // Data Pin for control of LEDs in NR2
#define NUM_LEDS_NR3 10                       // Number of LEDs in strip 2 ("upper" LED strip="closer to headstock")(aka "NR3")
#define LED_DATA_PIN_NR3 6                    // Data Pin for control of LEDs in NR3

#define NUM_LED_BINS_NR1 1                     // number of LED stripes=LED bins in LED strip Nr1
#define NUM_LED_BINS_NR2 4                     // number of LED stripes=LED bins in LED strip Nr2
#define NUM_LED_BINS_NR3 2                     // number of LED stripes=LED bins in LED strip Nr3
#define NUM_LEDS_PER_LED_BIN_NR1 16            // number of LEDs per FFT bin ("height" of LED_bin)
#define NUM_LEDS_PER_LED_BIN_NR2 5             // number of LEDs per FFT bin ("height" of LED_bin)
#define NUM_LEDS_PER_LED_BIN_NR3 5             // number of LEDs per FFT bin ("height" of LED_bin)

CRGB leds_NR1[NUM_LEDS_NR1];                    // create array for LED ring (NR1)
CRGB leds_NR2[NUM_LEDS_NR2];                    // create array for strip NR2
CRGB leds_NR3[NUM_LEDS_NR3];                    // create array for strip NR3

#define LED_HUE_STD 13                      // STandarD HUE = "color" of LED; 0~red, 32~orange, 64~yellow
#define LED_SAT_STD 255                     // STandarD SATuration = saturation of color of LED; 0~neutral grey, 255~pure color
#define LED_VAL_STD 75 

#define LED_BRIGHT_PIN A1                   // controls brightness of LEDs;  in code below A0 is addressed via "ADMUX = 0x41" (since operating ADC in special mode)
int LED_HUE_NR1 = LED_HUE_STD;              // HUE used for strip Nr1
int LED_HUE_NR2 = LED_HUE_STD;              // HUE used for strip Nr2
int LED_HUE_NR3 = LED_HUE_STD;              // HUE used for strip Nr3
int LED_SAT = LED_SAT_STD;                  // SAT used for FastLED depending on input conditions
int LED_VAL = LED_VAL_STD;                  // VAL used for FastLED depending on input conditions

int switchOnDelay = 50;                     // time  (ms) between switchin on subsequent leds

// "LED fire" effect related
#define COOLING  100                         // How much does the air cool as it rises? Less cooling = taller flames.  More cooling = shorter flames. Default 55, suggested range 20-100
#define SPARKING 90                        // What chance (out of 255) is there that a new spark will be lit? Higher chance = more roaring fire.  Lower chance = more flickery fire. Default 120, suggested range 50-200.

// Audio stuff =============================================================================
#define AUDIO_IN_PIN A0                   // Audio signal input; in code below A0 is addressed via "ADMUX = 0x40" (since operating ADC in special mode)

// FFT stuff =============================================================================
#define LOG_OUT 0         //set output of FFT library to linear not logarithmical
#define LIN_OUT 1         //set output of FFT library to linear not logarithmical
#define FFT_N 64          //set to 64 point FFT => 32 frequency bins (assuming input frequencies from ~0...22 000 Hz => ~ 2 x 343 Hz/bin

#include <FFT.h>          //include the FFT library; doc found e.g., https://github.com/imagest108/arduino/blob/master/libraries/ArduinoFFT/FFT/fft_read_me.txt
#include <math.h>         //include library for mathematic functions

byte mapFFTBinToLEDBin_NR1[NUM_LED_BINS_NR1+1] = {4, 16};           // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  5-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)
//byte mapFFTBinToLEDBin_NR2[NUM_LED_BINS_NR2+1] = {1, 3, 5, 7, 16};  // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  2-3, 4-5, 6-7, 8-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)
byte mapFFTBinToLEDBin_NR2[NUM_LED_BINS_NR2+1] = {1, 4, 8, 12, 16};  // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  2-3, 4-5, 6-7, 8-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)

byte mapFFTBinToLEDBin_NR3[NUM_LED_BINS_NR3+1] = {1, 3, 16};        // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  2-3, 4-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)
byte currentBinHeight_NR1[NUM_LED_BINS_NR1] = {0};                  // heigth (=numbers of LEDs ON in LED bin in LED strip 1)
byte currentBinHeight_NR2[NUM_LED_BINS_NR2] = {0, 0, 0, 0};         // heigth (=numbers of LEDs ON in LED bin in LED strip 2)
byte currentBinHeight_NR3[NUM_LED_BINS_NR3] = {0, 0};               // heigth (=numbers of LEDs ON in LED bin in LED strip 3)
float faktoren_NR1[NUM_LED_BINS_NR1] = {1};                       //factors to increase the height of each column in strip NR1
float faktoren_NR2[NUM_LED_BINS_NR2] = {0.7, 0.9, 1.1, 1.3};            //factors to increase the height of each column in strip NR2
float faktoren_NR3[NUM_LED_BINS_NR3] = {0.8, 1.1};                    //factors to increase the height of each column in strip NR3

// Mode selection stuff =============================================================================
#define modeSelectorPin 2                                           // use digital pin 2 for mode selection
int modeSelectorPinSwitchState = LOW ;                                 // HIGH if external mode selector button is pressed
int operationMode = 0;                                              // operation mode (e.g., "0" = "all off", "1" = only NR2 controlled by FFT, ...)
int operationModeMax = 7;                                           // maximum number of operation modes (counting starts w/ "0")
int operationModeSelectionDelay = 250;                              // time between subsequent readings of external mode selection button

/*
 * Subroutines and alike ===================================================================================
 */
 
void blinkTest () {                            // Light up all LEDs in each ring/strip subsequently (kind of a function check...)
  //=============
  for (int i=0; i<NUM_LEDS_NR1; i++) {
    LED_HUE_NR1 = ( 2 +  10 * i) % 255;
    // Turn ON leds_NR1
    leds_NR1[i]=CHSV(LED_HUE_NR1, LED_SAT, LED_VAL);
    FastLED.show();
    delay(switchOnDelay);

    // Turn OFF leds_NR1
    leds_NR1[i]=CHSV(LED_HUE_NR1, LED_SAT, 0);
    FastLED.show();
    delay(switchOnDelay);
  }

  for (int i=0; i<NUM_LEDS_NR2; i++) {
    LED_HUE_NR2 = ( 2 +  10 * i) % 255;
    // Turn ON leds_NR2
    leds_NR2[i]=CHSV(LED_HUE_NR2, LED_SAT, LED_VAL);
    FastLED.show();
    delay(switchOnDelay);

    // Turn OFF leds_NR1
    leds_NR2[i]=CHSV(LED_HUE_NR2, LED_SAT, 0);
    FastLED.show();
    delay(switchOnDelay);
  }

  for (int i=0; i<NUM_LEDS_NR3; i++) {
    LED_HUE_NR3 = ( 2 +  10 * i) % 255;
    // Turn ON leds_NR3
    leds_NR3[i]=CHSV(LED_HUE_NR3, LED_SAT, LED_VAL);
    FastLED.show();
    delay(switchOnDelay);

    // Turn OFF leds_NR3
    leds_NR3[i]=CHSV(LED_HUE_NR3, LED_SAT, 0);
    FastLED.show();
    delay(switchOnDelay);
  }
}

void allOFF () {                               // actively turn off all LEDs (set their color to black)
  for ( int a = 0; a < NUM_LEDS_NR1; a++) {
    leds_NR1[a] = CRGB::Black;
  }
  for ( int a = 0; a < NUM_LEDS_NR2; a++) {
    leds_NR2[a] = CRGB::Black;
  }
  for ( int a = 0; a < NUM_LEDS_NR3; a++) {
    leds_NR3[a] = CRGB::Black;
  }
  FastLED.show();
}

void setLEDStrip_NR1 (byte LED_column, byte LED_columnHeight, byte brightness) {
  byte h = map(LED_columnHeight, 0, 255, 0, NUM_LEDS_PER_LED_BIN_NR1);
  h = (unsigned char)(h * faktoren_NR1[LED_column]);
  if (h < currentBinHeight_NR1[LED_column]) {
    currentBinHeight_NR1[LED_column]--;
  }
  else if (h > currentBinHeight_NR1[LED_column]) {
    currentBinHeight_NR1[LED_column] = h;
  }
  if (LED_columnHeight > 150) {
    LED_HUE_NR1 += 20;                                            //CHANGE THIS VALUE IF YOU WANT THE DIFFERENCE BETWEEN THE COLORS TO BE BIGGER
    if (LED_HUE_NR1 > 250) LED_HUE_NR1 = 0;
  }

  for (byte y = 0; y < NUM_LEDS_PER_LED_BIN_NR1; y++) {            //set colors of pixels according to column and hue
    if (currentBinHeight_NR1[LED_column] > y) {
      if (LED_column % 2 == 0) {
        leds_NR1[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CHSV((LED_HUE_NR1 * NUM_LEDS_PER_LED_BIN_NR1) + (LED_column * NUM_LEDS_PER_LED_BIN_NR1), 255, brightness);
      } else {
        leds_NR1[NUM_LEDS_PER_LED_BIN_NR1 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CHSV((LED_HUE_NR1 * NUM_LEDS_PER_LED_BIN_NR1) + (LED_column * NUM_LEDS_PER_LED_BIN_NR1), 255, brightness);
      }
    } else {
      if (LED_column % 2 == 0) {
        leds_NR1[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CRGB::Black;
      } else {
        leds_NR1[NUM_LEDS_PER_LED_BIN_NR1 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CRGB::Black;
      }
    }
  }
}

void setLEDStrip_NR2 (byte LED_column, byte LED_columnHeight, byte brightness) {
  byte h = map(LED_columnHeight, 0, 255, 0, NUM_LEDS_PER_LED_BIN_NR2);
  h = (unsigned char)(h * faktoren_NR2[LED_column]);
  if (h < currentBinHeight_NR2[LED_column]) {
    currentBinHeight_NR2[LED_column]--;
  }
  else if (h > currentBinHeight_NR2[LED_column]) {
    currentBinHeight_NR2[LED_column] = h;
  }
  if (LED_columnHeight > 150) {
    LED_HUE_NR2 += 20;                                            //CHANGE THIS VALUE IF YOU WANT THE DIFFERENCE BETWEEN THE COLORS TO BE BIGGER
    if (LED_HUE_NR2 > 250) LED_HUE_NR2 = 0;
  }

  for (byte y = 0; y < NUM_LEDS_PER_LED_BIN_NR2; y++) {            //set colors of pixels according to column and hue
    if (currentBinHeight_NR2[LED_column] > y) {
      if (LED_column % 2 == 0) {
        leds_NR2[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CHSV((LED_HUE_NR2 * NUM_LEDS_PER_LED_BIN_NR2) + (LED_column * NUM_LEDS_PER_LED_BIN_NR2), 255, brightness);
      } else {
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CHSV((LED_HUE_NR2 * NUM_LEDS_PER_LED_BIN_NR2) + (LED_column * NUM_LEDS_PER_LED_BIN_NR2), 255, brightness);
      }
    } else {
      if (LED_column % 2 == 0) {
        leds_NR2[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CRGB::Black;
      } else {
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CRGB::Black;
      }
    }
  }
}

void setLEDStrip_NR3 (byte LED_column, byte LED_columnHeight, byte brightness) {
  byte h = map(LED_columnHeight, 0, 255, 0, NUM_LEDS_PER_LED_BIN_NR3);
  h = (unsigned char)(h * faktoren_NR3[LED_column]);
  if (h < currentBinHeight_NR3[LED_column]) {
    currentBinHeight_NR3[LED_column]--;
  }
  else if (h > currentBinHeight_NR3[LED_column]) {
    currentBinHeight_NR3[LED_column] = h;
  }
  if (LED_columnHeight > 150) {
    LED_HUE_NR3 += 20;                                            //CHANGE THIS VALUE IF YOU WANT THE DIFFERENCE BETWEEN THE COLORS TO BE BIGGER
    if (LED_HUE_NR3 > 250) LED_HUE_NR3 = 0;
  }

  for (byte y = 0; y < NUM_LEDS_PER_LED_BIN_NR3; y++) {            //set colors of pixels according to column and hue
    if (currentBinHeight_NR3[LED_column] > y) {
      if (LED_column % 2 == 0) {
        leds_NR3[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CHSV((LED_HUE_NR3 * NUM_LEDS_PER_LED_BIN_NR3) + (LED_column * NUM_LEDS_PER_LED_BIN_NR3), 255, brightness);
      } else {
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CHSV((LED_HUE_NR3 * NUM_LEDS_PER_LED_BIN_NR3) + (LED_column * NUM_LEDS_PER_LED_BIN_NR3), 255, brightness);
      }
    } else {
      if (LED_column % 2 == 0) {
        leds_NR3[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CRGB::Black;
      } else {
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CRGB::Black;
      }
    }
  }
}

void NR1_CtrlViaFFT() {
  // LED object NR1 (ring) controlled by FFT result
  // for each of the LED stripes=LED bins, check highest intensity w/i the respective bundle of FFT bins. Based on that result set numbers of LEDs w/i the LED bin to be lit
    for (byte i =0; i < NUM_LED_BINS_NR1; i++) {
    byte maxIntensity = 0;
    for (byte x = mapFFTBinToLEDBin_NR1[i]; x < mapFFTBinToLEDBin_NR1[i+1]; x++) {
      if ( (unsigned char) fft_lin_out[x] > maxIntensity ) {
        maxIntensity = (unsigned char) fft_lin_out[x];
      }
    }
    // set height for each LED strip based on determined maximum intensity w/i each bundle
    setLEDStrip_NR1 (i, maxIntensity, LED_VAL);
  }
}

void NR2_CtrlViaFFT() {
  // LED object NR2 controlled by FFT result  
  // for each of the LED stripes=LED bins, check highest intensity w/i the respective bundle of FFT bins. Based on that result set numbers of LEDs w/i the LED bin to be lit
  for (byte i =0; i < NUM_LED_BINS_NR2; i++) {
    byte maxIntensity = 0;
    for (byte x = mapFFTBinToLEDBin_NR2[i]; x < mapFFTBinToLEDBin_NR2[i+1]; x++) {
      if ( (unsigned char) fft_lin_out[x] > maxIntensity ) {
        maxIntensity = (unsigned char) fft_lin_out[x];
      }
    }
    // set height for each LED strip based on determined maximum intensity w/i each bundle
    setLEDStrip_NR2 (i, maxIntensity, LED_VAL);
  }
}

void NR3_CtrlViaFFT() {
  // LED object NR2 controlled by FFT result  
  // for each of the LED stripes=LED bins, check highest intensity w/i the respective bundle of FFT bins. Based on that result set numbers of LEDs w/i the LED bin to be lit
  for (byte i =0; i < NUM_LED_BINS_NR3; i++) {
    byte maxIntensity = 0;
    for (byte x = mapFFTBinToLEDBin_NR3[i]; x < mapFFTBinToLEDBin_NR3[i+1]; x++) {
      if ( (unsigned char) fft_lin_out[x] > maxIntensity ) {
        maxIntensity = (unsigned char) fft_lin_out[x];
      }
    }
    // set height for each LED strip based on determined maximum intensity w/i each bundle
    setLEDStrip_NR3 (i, maxIntensity, LED_VAL);
  }   
}

void NR2_LEDFire() {

  static byte heat_NR2 [NUM_LEDS_NR2];        // array for heat values
  
  // Step 1: Cool down all cells by a somewhat random amount, except the sparking ones (each LED in each LED Bin; no differentiation between different LED bins)
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    for ( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR2; b++) {       // each LED
      heat_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b] = qsub8 ( heat_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b], random8 (0, (COOLING / NUM_LEDS_PER_LED_BIN_NR2 * 80 ))); // "*4" seems to be a reasonable factor. Play around to get the cooling rate you need to get a nice effect
    }
  }

  // Step 2: spread the heat (each LED in each LED Bin); consider only 1 neighbouring LED due to very low number of LEDs (<<10) in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    for ( int b = NUM_LEDS_PER_LED_BIN_NR2-1; b > 0; b--) {    // each LED
      if (a %2 ==0) {
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + b ] = ( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + b ] + heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + b - 1 ] ) / 1.75;
      }
      else {
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * (a+1) - b - 1 ] = ( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * (a+1) - b - 1 ] + heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * (a+1) - b ] ) / 1.75;    
      }
    }
  }
  
  // Step 3: Randomly ignite new flame sources of heat at the very bottom of each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    if( random8() < SPARKING ) {           
      if( a %2 == 0) {                                          // for even LED bins
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + 0 ] = qadd8( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + 0 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } else {                                                  // for odd LED bins
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + NUM_LEDS_PER_LED_BIN_NR2 - 1 ] = qadd8( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + NUM_LEDS_PER_LED_BIN_NR2 - 1 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } 
    }
  }    

  // Step 4: Map heat array to LED array. Decrease brightness for higher LEDs in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    for( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR2; b++) {
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b] = HeatColor( heat_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b]);
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b].nscale8_video(LED_VAL);
    }
  }
}

void NR3_LEDFire() {

  static byte heat_NR3 [NUM_LEDS_NR3];        // array for heat values
  
  // Step 1: Cool down all cells by a somewhat random amount, except the sparking ones (each LED in each LED Bin; no differentiation between different LED bins)
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    for ( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR3; b++) {       // each LED
      heat_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b] = qsub8 ( heat_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b], random8 (0, (COOLING / NUM_LEDS_PER_LED_BIN_NR3 * 80 ))); // "*4" seems to be a reasonable factor. Play around to get the cooling rate you need to get a nice effect
    }
  }

  // Step 2: spread the heat (each LED in each LED Bin); consider only 1 neighbouring LED due to very low number of LEDs (<<10) in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    for ( int b = NUM_LEDS_PER_LED_BIN_NR3-1; b > 0; b--) {    // each LED
      if (a %2 ==0) {
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + b ] = ( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + b ] + heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + b - 1 ] ) / 1.75;
      }
      else {
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * (a+1) - b - 1 ] = ( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * (a+1) - b - 1 ] + heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * (a+1) - b ] ) / 1.75;    
      }
    }
  }
  
  // Step 3: Randomly ignite new flame sources of heat at the very bottom of each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    if( random8() < SPARKING ) {           
      if( a %2 == 0) {                                          // for even LED bins
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + 0 ] = qadd8( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + 0 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } else {                                                  // for odd LED bins
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + NUM_LEDS_PER_LED_BIN_NR3 - 1 ] = qadd8( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + NUM_LEDS_PER_LED_BIN_NR3 - 1 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } 
    }
  }    

  // Step 4: Map heat array to LED array. Decrease brightness for higher LEDs in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    for( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR3; b++) {
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b] = HeatColor( heat_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b]);
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b].nscale8_video(LED_VAL);
    }
  }
}

/*
 * End of Subroutines and alike ===================================================================================
 */

/*
 * Setup ===================================================================================
 */
void setup() {
  FastLED.addLeds<NEOPIXEL, LED_DATA_PIN_NR1>(leds_NR1, NUM_LEDS_NR1);    // setup LED strip NR1
  FastLED.addLeds<NEOPIXEL, LED_DATA_PIN_NR2>(leds_NR2, NUM_LEDS_NR2);    // setup LED strip NR2
  FastLED.addLeds<NEOPIXEL, LED_DATA_PIN_NR3>(leds_NR3, NUM_LEDS_NR3);    // setup LED strip NR3

  pinMode(LED_DATA_PIN_NR1, OUTPUT);
  pinMode(LED_DATA_PIN_NR2, OUTPUT);
  pinMode(LED_DATA_PIN_NR3, OUTPUT);

  pinMode(LED_BRIGHT_PIN, INPUT);
  pinMode(AUDIO_IN_PIN, INPUT);

  pinMode(modeSelectorPin, INPUT);

  // setup Arduino Nano registers
  ADCSRA = 0xe5;                                                    //set the ADC to free running mode
  ADMUX = 0x40;                                                     //use pin A0
  DIDR0 = 0x03;                                                     //turn off the digital input for Pins A0 (audio signal input) and A1 (LED brightness)
  analogReference(DEFAULT);                                         //set aref to DEFAULT (was EXTERNAL in example, however, due to lack of schematics, I guess analogReference was connected to +5V, however not sure. In order to avoid potential damage to the ATmega - since I am not quite sure whats's going on inside and since I'm a novice to this Arduino topics, I chose to go for DEFAULT - although wasting some bits

  //check if all LEDs are controllable (only in case switch button is pressed during startup)
  modeSelectorPinSwitchState = digitalRead(modeSelectorPin);
  if ( modeSelectorPinSwitchState == HIGH) {
    blinkTest();
  }
  
  LED_HUE_NR1 = 0;                    // reset LED HUE value
  LED_HUE_NR2 = 0;                    // reset LED HUE value  
  LED_HUE_NR3 = 0;                    // reset LED HUE value
  
  allOFF();                           // actively turn off all LEDs (set their color to black)

  delay(500);  

}

/*
 * End of Setup ===================================================================================
 */



/*
 * Main Loop ===================================================================================
 */
 
void loop() {
  // Data acquisition and FFT ----------------------------------------------------------------------------------------------
  // -----------------------------------------------------------------------------------------------------------------------
  // Collect FFT data using input signal @ Pin A0
  for (int i = 0 ; i < 2 * FFT_N ; i += 2) {                      //save FFT_N samples
    while (!(ADCSRA & 0x10));                                     //wait for adc to be ready (i.e., current sampling finished)
    ADMUX = 0x40;                                                 //use pin A0
    ADCSRA = 0xf5;                                                //restart adc
    byte m = ADCL;                                                //fetch adc data
    byte j = ADCH;
   int k = (j << 8) | m;                                         //form into an int
   k -= 0x0200;                                                  //form into a signed int
   k <<= 6;                                                      //form into a 16b signed int
   fft_input[i] = k;                                             //put real data into even bins
   fft_input[i+1] = 0;                                           //set odd bins to 0   
  }  
  
  // Do FFT processing
  fft_window();                                                   // window the data for better frequency response
  fft_reorder();                                                  // reorder the data before doing the fft
  fft_run();                                                      // process the data in the fft
  fft_mag_lin();                                                  // take the output of the fft
  
  fft_lin_out[0] = 0;
  fft_lin_out[1] = 0;

  // set LED brightness ----------------------------------------------------------------------------------------------
  // -----------------------------------------------------------------------------------------------------------------
  // since ADC is in free running mode, switch input pin via ADMUX. Also add slight delay (otherwise - for whatever reason - data will not be passed to variable)
  while (!(ADCSRA & 0x10));                                     //wait for adc to be ready (i.e., current sampling finished)  
  ADMUX = 0x41;                                                 //use pin A1
  delay(1);
  byte m = ADCL;                                                //fetch adc data
  byte j = ADCH;
  int k = (j << 8) | m;                                         //form into an int
  LED_VAL = map (k, 0, 1023, 255, 0);                           // map potentiometer to allowed brightness for FastLED
  sei();                                                        // turn on interrupts

  // select behaviour based on operating mode ------------------------------------------------------------------------
  // -----------------------------------------------------------------------------------------------------------------
  modeSelectorPinSwitchState = digitalRead(modeSelectorPin);    // HIGH = button for selection of operating mode has been pressed
  
  if ( modeSelectorPinSwitchState == HIGH ) {
    operationMode = operationMode+1;                            // switch to next operation mode
    if (operationMode > operationModeMax) {
      operationMode = 0;
    }       
    delay (operationModeSelectionDelay);                        // prevent increasing operationMode in case of external mode selection button is pressed too long

    // Turn OFF all LEDs (to ensure only LEDs according to selected operation mode are on
    // actively turn off all LEDs (set their color to black)
    allOFF();
  }
  
  switch (operationMode) {
    /*
     * # button pressed |         |         |         |          |          |          |
     * (= case below)   | FFT NR1 | FFT NR2 | FFT NR3 | FIRE NR1 | FIRE NR2 | FIRE NR3 |
     * --------------------------------------------------------------------------------
     *        0         |    -    |    -    |    -    |    -     |    -     |    -
     *        1         |    -    |    X    |    -    |    -     |    -     |    -
     *        2         |    -    |    X    |    X    |    -     |    -     |    -
     *        3         |    X    |    X    |    X    |    -     |    -     |    -
     *        4         |    X    |    -    |    -    |    -     |    -     |    -
     *        5         |    -    |    -    |    -    |    -     |    X     |    -     
     *        6         |    -    |    -    |    -    |    -     |    -     |    X     
     *        7         |    -    |    -    |    -    |    -     |    X     |    X     
     */
    case 0:
      break;
    case 1:
      NR2_CtrlViaFFT();
      break;
    case 2:
      NR2_CtrlViaFFT();
      NR3_CtrlViaFFT();
      break;
    case 3:
      NR1_CtrlViaFFT();
      NR2_CtrlViaFFT();
      NR3_CtrlViaFFT();
      break;  
    case 4:
      NR1_CtrlViaFFT();
      break;
    case 5:
      NR2_LEDFire();
      delay(50);
      break;
    case 6:
      NR3_LEDFire();
      delay(50);
      break;
    case 7:
      NR2_LEDFire();
      delay(20);
      NR3_LEDFire();
      delay(30);
      break;      
  }
   
  FastLED.show();

delay(10); 

}
/*
 * End of Main Loop  ===================================================================================
 */

Schematics

Schematics for UkeOnFire

Comments

Author

Default
torstengeppert
  • 3 projects
  • 2 followers

Additional contributors

  • Idea of using arduino + ws2812b leds together with fft for audio spectrum analysis & display by Antoine Rochebois

Published on

June 16, 2020

Members who respect this project

AulaDefault

and 2 others

See similar projects
you might like

Similar projects you might like

Modern Jukebox

Project tutorial by thisoldgeek

  • 7,041 views
  • 8 comments
  • 33 respects

DIY Arduino Musical Instrument-Theremin with 4 Sound Modes

Project tutorial by Mirko Pavleski

  • 1,644 views
  • 0 comments
  • 1 respect

Countdown Voice Timer & Fire Starter

Project tutorial by Shahariar

  • 1,729 views
  • 0 comments
  • 3 respects

Minimal MIDI Drum Kit with 3D Printer

Project tutorial by ryokosaka

  • 30,364 views
  • 6 comments
  • 52 respects

Play with Fire Over Wi-Fi (ESP8266, NeoPixels & Android App)

Project tutorial by ElectroPeak

  • 13,797 views
  • 5 comments
  • 49 respects

Touchless Thermometer

Project tutorial by WU_HuiQiao

  • 7,005 views
  • 0 comments
  • 19 respects
Add projectSign up / Login