├── .gitignore ├── Curve.cpp ├── Curve.h ├── LEDFader.cpp ├── LEDFader.h ├── LICENSE ├── README.md ├── examples ├── multipleLEDs │ └── multipleLEDs.ino └── singleLED │ └── singleLED.ino ├── keywords.txt └── library.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | -------------------------------------------------------------------------------- /Curve.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: Curve.cpp 3 | * Author: cameron 4 | * 5 | * Created on October 22, 2013, 1:07 AM 6 | */ 7 | 8 | #include "Curve.h" 9 | 10 | // If this wasn't in program memory, we'd run out of SRAM way to fast. 11 | const uint8_t Curve::etable[256] PROGMEM = { 12 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13 | 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 15 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 16 | 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 17 | 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 18 | 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 19 | 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 16, 20 | 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21, 21, 22, 22, 21 | 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 28, 29, 30, 30, 31, 32, 22 | 32, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 23 | 46, 47, 48, 49, 50, 51, 52, 53, 55, 56, 57, 58, 59, 61, 62, 63, 24 | 65, 66, 68, 69, 71, 72, 74, 76, 77, 79, 81, 82, 84, 86, 88, 90, 25 | 92, 94, 96, 98,100,102,105,107,109,112,114,117,119,122,124,127, 26 | 130,133,136,139,142,145,148,151,155,158,162,165,169,172,176,180, 27 | 184,188,192,196,201,205,210,214,219,224,229,234,239,244,250,255, 28 | }; 29 | 30 | 31 | uint8_t Curve::exponential(uint8_t i) { 32 | // If the compiler could build the above table for us, we could have "simply": 33 | //return !i ? 0 : round(exp(log(MAXOUTPUT) * i / MAXINPUT)); 34 | 35 | // Need to read the table in a special way! 36 | return pgm_read_byte(&etable[i]); 37 | } 38 | 39 | uint8_t Curve::linear(uint8_t i) { 40 | return i; 41 | } 42 | 43 | uint8_t Curve::reverse(uint8_t i) { 44 | return 255-i; 45 | } 46 | -------------------------------------------------------------------------------- /Curve.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: Curve.h 3 | * Author: cameron 4 | * 5 | * Created on October 22, 2013, 1:07 AM 6 | */ 7 | 8 | #ifndef CURVE_H 9 | #define CURVE_H 10 | 11 | #if (defined(__AVR__)) 12 | #include 13 | #else 14 | #include 15 | #endif 16 | 17 | class Curve { 18 | static const uint8_t etable[] PROGMEM; 19 | public: 20 | static uint8_t exponential(uint8_t); 21 | static uint8_t linear(uint8_t); 22 | static uint8_t reverse(uint8_t); 23 | }; 24 | 25 | #endif /* CURVE_H */ 26 | 27 | -------------------------------------------------------------------------------- /LEDFader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LED.cpp 3 | * 4 | * Created on: Sep 24, 2013 5 | * Author: jeremy 6 | */ 7 | 8 | #include "LEDFader.h" 9 | 10 | LEDFader::LEDFader(uint8_t pwm_pin) { 11 | pin = pwm_pin; 12 | color = 0; 13 | to_color = 0; 14 | last_step_time = 0; 15 | interval = 0; 16 | duration = 0; 17 | percent_done = 0; 18 | curve = (curve_function)0; 19 | } 20 | 21 | void LEDFader::set_pin(uint8_t pwm_pin) { 22 | pin = pwm_pin; 23 | } 24 | uint8_t LEDFader::get_pin(){ 25 | return pin; 26 | } 27 | 28 | 29 | void LEDFader::set_value(int value) { 30 | color = (uint8_t)constrain(value, 0, 255); 31 | if (curve) 32 | analogWrite(pin, curve(color)); 33 | else 34 | analogWrite(pin, color); 35 | } 36 | 37 | uint8_t LEDFader::get_value() { 38 | return color; 39 | } 40 | 41 | uint8_t LEDFader::get_target_value() { 42 | return to_color; 43 | } 44 | 45 | // Set curve to transform output 46 | void LEDFader::set_curve(curve_function c) { 47 | curve = c; 48 | } 49 | 50 | // Get the current curve function pointer 51 | LEDFader::curve_function LEDFader::get_curve() { 52 | return curve; 53 | } 54 | 55 | void LEDFader::slower(int by) { 56 | float cached_percent = percent_done; 57 | duration += by; 58 | fade(to_color, duration); 59 | percent_done = cached_percent; 60 | } 61 | 62 | void LEDFader::faster(int by) { 63 | float cached_percent = percent_done; 64 | 65 | // Ends the fade 66 | if (duration <= by) { 67 | stop_fade(); 68 | set_value(to_color); 69 | } 70 | else { 71 | duration -= by; 72 | fade(to_color, duration); 73 | } 74 | percent_done = cached_percent; 75 | } 76 | 77 | void LEDFader::fade(uint8_t value, unsigned int time) { 78 | // Color hasn't changed or fader is fading to the requested value already 79 | if (value == color || value == to_color) { 80 | return; 81 | } 82 | 83 | stop_fade(); 84 | percent_done = 0; 85 | 86 | if (time <= MIN_INTERVAL) { 87 | set_value(value); 88 | return; 89 | } 90 | 91 | duration = time; 92 | to_color = (uint8_t)constrain(value, 0, 255); 93 | 94 | // Figure out what the interval should be so that we're chaning the color by at least 1 each cycle 95 | // (minimum interval is MIN_INTERVAL) 96 | float color_diff = abs(color - to_color); 97 | interval = round((float)duration / color_diff); 98 | if (interval < MIN_INTERVAL) { 99 | interval = MIN_INTERVAL; 100 | } 101 | 102 | last_step_time = millis(); 103 | } 104 | 105 | bool LEDFader::is_fading() { 106 | if (duration > 0) 107 | return true; 108 | return false; 109 | } 110 | 111 | void LEDFader::stop_fade() { 112 | percent_done = 100; 113 | duration = 0; 114 | } 115 | 116 | uint8_t LEDFader::get_progress() { 117 | return round(percent_done); 118 | } 119 | 120 | bool LEDFader::update() { 121 | // No fade 122 | if (duration == 0) { 123 | return false; 124 | } 125 | 126 | unsigned long now = millis(); 127 | unsigned int time_diff = now - last_step_time; 128 | 129 | // Interval hasn't passed yet 130 | if (time_diff < interval) { 131 | return true; 132 | } 133 | 134 | // How far along have we gone since last update 135 | float percent = (float)time_diff / (float)duration; 136 | percent_done += percent; 137 | 138 | // We've hit 100%, set LED to the final color 139 | if (percent >= 1) { 140 | stop_fade(); 141 | set_value(to_color); 142 | return false; 143 | } 144 | 145 | // Move color to where it should be 146 | int color_diff = to_color - color; 147 | int increment = round(color_diff * percent); 148 | 149 | set_value(color + increment); 150 | 151 | // Update time and finish 152 | duration -= time_diff; 153 | last_step_time = millis(); 154 | return true; 155 | } 156 | -------------------------------------------------------------------------------- /LEDFader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LED.h 3 | * 4 | * Created on: Sep 24, 2013 5 | * Author: jeremy 6 | */ 7 | 8 | #include "Arduino.h" 9 | 10 | #ifndef LEDFader_H_ 11 | #define LEDFader_H_ 12 | 13 | // The minimum time (milliseconds) the program will wait between LED adjustments 14 | // adjust this to modify performance. 15 | #define MIN_INTERVAL 20 16 | 17 | class LEDFader { 18 | public: 19 | // Who likes dealing with function pointers? (Ok, I do, but no one else does) 20 | typedef uint8_t (*curve_function)(uint8_t); 21 | private: 22 | uint8_t pin; 23 | unsigned long last_step_time; 24 | unsigned int interval; 25 | uint8_t color; 26 | uint8_t to_color; 27 | unsigned int duration; 28 | float percent_done; 29 | curve_function curve; 30 | 31 | public: 32 | 33 | // Create a new LED Fader for a pin 34 | LEDFader(uint8_t pwm_pin=0); 35 | 36 | // Set the PWM pin that the LED is connected to 37 | void set_pin(uint8_t pwm_pin); 38 | uint8_t get_pin(); 39 | 40 | // Set an LED to an absolute PWM value 41 | void set_value(int pwm); 42 | 43 | // Get the current LED PWM value 44 | uint8_t get_value(); 45 | 46 | // Get the PWM value we're fading to 47 | uint8_t get_target_value(); 48 | 49 | // Set curve to transform output 50 | void set_curve(curve_function); 51 | 52 | // Get the current curve function pointer 53 | curve_function get_curve(); 54 | 55 | // Fade an LED to a PWM value over a duration of time (milliseconds) 56 | void fade(uint8_t pwm, unsigned int time); 57 | 58 | 59 | // Returns TRUE if there is an active fade process 60 | bool is_fading(); 61 | 62 | // Stop the current fade where it's at 63 | void stop_fade(); 64 | 65 | // Update the LEDs along the fade 66 | // Returns TRUE if a fade is still in process 67 | bool update(); 68 | 69 | // Decrease the current fading speed by a number of milliseconds 70 | void slower(int by_seconds); 71 | 72 | // Increase the current fading speed by a number of milliseconds 73 | void faster(int by_seconds); 74 | 75 | // Returns how much of the fade is complete in a percentage between 0 - 100 76 | uint8_t get_progress(); 77 | }; 78 | 79 | #endif /* LEDFader_H_ */ 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Gillick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Background LED Fading Library for Arduino 2 | ======================================== 3 | 4 | This arduino library can fade individual LEDs in the background without blocking your main program. 5 | 6 | Instead of using a for loop and `delay` to fade an LED from one PWM value to another -- which will block all other processes -- this will check the time each loop cycle to see if the value should be adjusted. 7 | 8 | For example, to fade from 0 - 200 in 1 second, it would increment the PWM value by 1 every 50 milliseconds -- without stopping your program while it waits. There are also other optimizations to make the animations smooth and accurate without eating too many chip resources. 9 | 10 | Install 11 | ------- 12 | Move the arduino-LEDFader folder into the [arduino library directory](http://arduino.cc/en/Guide/Libraries) and rename it from `arduino-LEDFader` to `LEDFader`. 13 | 14 | Then in your sketch you just need to add this to the top of your file: 15 | 16 | ```cpp 17 | #include 18 | ``` 19 | 20 | 21 | Simple Examples 22 | --------------- 23 | 24 | Fade an LED on pin 3 up in 3 seconds. 25 | 26 | ```cpp 27 | #include 28 | 29 | // Create new LED Fader on pin 3 30 | LEDFader led = LEDFader(3); 31 | 32 | void setup() { 33 | // Fade from 0 - 255 in 3 seconds 34 | led.fade(255, 3000); 35 | } 36 | 37 | void loop() { 38 | // Need to call update each loop cycle to adjust the PWM if needed 39 | led.update(); 40 | } 41 | ``` 42 | 43 | Building on that example, we can alternate by fading up and down 44 | 45 | ```cpp 46 | #include 47 | 48 | // Create new LED Fader on pin 3 49 | LEDFader led = LEDFader(3); 50 | 51 | void setup() { 52 | led.fade(255, 3000); 53 | } 54 | 55 | void loop() { 56 | led.update(); 57 | 58 | if (led.is_fading() == false) { 59 | 60 | // Fade from 255 - 0 61 | if (led.get_value() == 255) { 62 | led.fade(0, 3000); 63 | } 64 | // Fade from 0 - 255 65 | else { 66 | led.fade(255, 3000); 67 | } 68 | } 69 | } 70 | ``` 71 | 72 | 73 | Multiple LEDs or RGB 74 | --------------------- 75 | 76 | This example will fade 6 LEDs, all to random PWM values and durations. You could, instead, replace 6 LEDs with 2 RGB LEDs. 77 | 78 | ```cpp 79 | #include 80 | 81 | #define LED_NUM 6 82 | 83 | // 6 LEDs (perhaps 2 RGB LEDs) 84 | LEDFader leds[LED_NUM] = { 85 | LEDFader(3), 86 | LEDFader(5), 87 | LEDFader(6), 88 | LEDFader(9), 89 | LEDFader(10), 90 | LEDFader(11) 91 | }; 92 | 93 | void setup() { 94 | } 95 | 96 | void loop() { 97 | 98 | // Update all LEDs and start new fades if any are done 99 | for (byte i = 0; i < LED_NUM; i++) { 100 | LEDFader *led = &leds[i]; 101 | led->update(); 102 | 103 | // Set new fade 104 | if (led->is_fading() == false) { 105 | int duration = random(1000, 3000); // between 1 - 3 seconds 106 | 107 | // Up 108 | if (led->get_value() == 0) { 109 | byte color = random(100, 255); 110 | led.fade(color, duration); 111 | } 112 | // Down 113 | else { 114 | led.fade(0, duration); 115 | } 116 | } 117 | } 118 | } 119 | ``` 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /examples/multipleLEDs/multipleLEDs.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | Fades 6 LEDs, all to random PWM values and durations. 5 | This uses all the PWM pins on the UNO board. 6 | 7 | Circuit 8 | ------- 9 | * Connect the anode (flat side/long leg) of each LED to one of the PWM pins listed below on the arduino, via a 220 ohm resistor. 10 | * Connect the cathode (short leg) of each LED to ground. 11 | 12 | * PWM Pins on the UNO board: 13 | - 3 14 | - 5 15 | - 6 16 | - 9 17 | - 10 18 | - 11 19 | 20 | RGB LEDs 21 | -------- 22 | You can also use this code with 2 RGB LEDs. This will fade each of the colors on an off at 23 | random durations and intensities. Check the LED datasheet to see if it's a common cathode or anode and 24 | which legs are which. 25 | 26 | For common anode RGB LEDs, attach the anode to ground and each cathode leg to a PWM pin through a 27 | resistor. 28 | 29 | For common cathode RGB LEDs, the cathode will be connected straight to 5+ and each 30 | of the anode legs will be connected to a PWM pin through a resistor. Since you're basically attaching 31 | the ground pins to the arduino, the brightness values are reversed: 0 is 100% and 255 is completely off. 32 | This is called current sinking. 33 | */ 34 | 35 | 36 | #define LED_NUM 6 37 | 38 | // 6 LEDs (perhaps 2 RGB LEDs) 39 | LEDFader leds[LED_NUM] = { 40 | LEDFader(3), 41 | LEDFader(5), 42 | LEDFader(6), 43 | LEDFader(9), 44 | LEDFader(10), 45 | LEDFader(11) 46 | }; 47 | 48 | void setup() { 49 | } 50 | 51 | void loop() { 52 | 53 | // Update all LEDs and start new fades if any are done 54 | for (byte i = 0; i < LED_NUM; i++) { 55 | LEDFader *led = &leds[i]; 56 | led->update(); 57 | 58 | // Set new fade 59 | if (led->is_fading() == false) { 60 | int duration = random(1000, 3000); // between 1 - 3 seconds 61 | 62 | // Up 63 | if (led->get_value() == 0) { 64 | byte color = random(100, 255); 65 | led->fade(color, duration); 66 | } 67 | // Down 68 | else { 69 | led->fade(0, duration); 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /examples/singleLED/singleLED.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | Fades a single LED up and down using LED Fader. 5 | Connect an LED to pin 3 6 | */ 7 | 8 | #define LED_PIN 3 9 | 10 | #define FADE_TIME 2000 11 | 12 | #define DIR_UP 1 13 | #define DIR_DOWN -1 14 | 15 | LEDFader led; 16 | int direction = DIR_UP; 17 | 18 | void setup() { 19 | led = LEDFader(LED_PIN); 20 | led.fade(255, FADE_TIME); 21 | } 22 | 23 | void loop() { 24 | led.update(); 25 | 26 | // LED no longer fading, switch direction 27 | if (!led.is_fading()) { 28 | 29 | // Fade down 30 | if (direction == DIR_UP) { 31 | led.fade(0, FADE_TIME); 32 | direction = DIR_DOWN; 33 | } 34 | // Fade up 35 | else { 36 | led.fade(255, FADE_TIME); 37 | direction = DIR_UP; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for OneButton 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | LEDFader KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | set_pin KEYWORD2 16 | set_value KEYWORD2 17 | get_value KEYWORD2 18 | get_target_value KEYWORD2 19 | set_curve KEYWORD2 20 | get_curve KEYWORD2 21 | fade KEYWORD2 22 | is_fading KEYWORD2 23 | stop_fade KEYWORD2 24 | update KEYWORD2 25 | slower KEYWORD2 26 | faster KEYWORD2 27 | get_progress KEYWORD2 28 | 29 | ####################################### 30 | # Constants (LITERAL1) 31 | ####################################### 32 | 33 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=arduino-LEDFader 2 | version=0.0.5 3 | author=jgillick 4 | maintainer=jgillick 5 | sentence=An arduino library to fade individual LEDs in the background without blocking your main program. 6 | paragraph=Instead of using a for loop and delay to fade an LED from one PWM value to another -- which will block all other processes -- this will check the time each loop cycle to see if the value should be adjusted. For example, to fade from 0 - 200 in 1 second, it would increment the PWM value by 1 every 50 milliseconds -- without stopping your program while it waits. There are also other optimizations to make the animations smooth and accurate without eating too many chip resources. 7 | category=Utility 8 | url=https://github.com/jgillick/arduino-LEDFader 9 | architectures=avr 10 | includes=LEDFader.h 11 | --------------------------------------------------------------------------------