ソースを参照

Added library examples

Lixie Labs 6 年 前
コミット
112f760b5e

+ 7 - 7
LICENSE

@@ -1,7 +1,7 @@
                     GNU GENERAL PUBLIC LICENSE
                        Version 3, 29 June 2007
 
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
 state the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
+    {one line to give the program's name and a brief idea of what it does.}
+    Copyright (C) {year}  {name of author}
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -645,14 +645,14 @@ the "copyright" line and a pointer to where the full notice is found.
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 Also add information on how to contact you by electronic and paper mail.
 
   If the program does terminal interaction, make it output a short
 notice like this when it starts in an interactive mode:
 
-    <program>  Copyright (C) <year>  <name of author>
+    {project}  Copyright (C) {year}  {fullname}
     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
   You should also get your employer (if you work as a programmer) or school,
 if any, to sign a "copyright disclaimer" for the program, if necessary.
 For more information on this, and how to apply and follow the GNU GPL, see
-<https://www.gnu.org/licenses/>.
+<http://www.gnu.org/licenses/>.
 
   The GNU General Public License does not permit incorporating your program
 into proprietary programs.  If your program is a subroutine library, you
 may consider it more useful to permit linking proprietary applications with
 the library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.  But first, please read
-<https://www.gnu.org/licenses/why-not-lgpl.html>.
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 8 - 26
README.md

@@ -1,28 +1,10 @@
-![Lixie II](https://i.imgur.com/G5l9iJk.png)
+<<<<<<< HEAD
+# EDGELIT
 
-# Lixie II for Arduino
+Hey Lixie fans! *This is a work-in-progress library*, please refer to src/Edgelit.h for functions it contains, and stay tuned for updates!
 
-This library is a work-in-progress, but almost all of the functionality from the original Lixie 1/Edgelit libraries is here, and will have software examples within the next day or so! For now, you can [refer to the original library documentation](https://github.com/connornishijima/Lixie-arduino) for its usage is almost identical. Just change any ocurrance of "Lixie" in your code with "Lixie_II".
-
-# WARNING
-
-## UNLIKE THE ORIGINAL LIXIE LIBRARY, THIS ONE RELIES ON THE ESPRESSIF/XTENSA ARCHITECTURE. (ESP8266 / ESP32 - NOT AVR)
-
-This decision was made to allow for non-blocking animation code, as the Ticker library or ESP32's second core are great for this. The vast majority of Lixie 1 users were using Espressif controllers anyways for a cheap gateway for the Lixies to fetch internet time, stocks, analytics, etc. This means that any Arduino Unos, Micros, Pro Minis, Megas, those old controllers are out. This isn't an "Apple killing the headphone jack" moment, for me this was a "Apple retiring the iPod Classic" moment. We have better stuff now, and just like web developers not wanting to hold back potential to make sure a site can run on Internet Explorer, I didn't want to hold back what Lixies were capable of either. The Espressif microcontrollers are amazing, and unless you're going for low-power, (Lixies are not) AVR isn't really right for something like this anymore. Farewell, AVR architecture. 
-
-----------
-# Installation
-
-***The Lixie library relies on [the FastLED library from Daniel Garcia](https://github.com/FastLED/FastLED), so make sure you have that installed as well!***
-
-~**With Arduino Library Manager:**~ COMING SOON!
-
-~1. Open *Sketch > Include Library > Manage Libraries* in the Arduino IDE.~
-~2. Search for "Lixie", and select the latest version.~
-~3. Click the Install button and Arduino will prepare the library and examples for you!~
-
-**Manual Install:**
-
-1. Click "Clone or Download" above to get an "Lixie_II-master.zip" file.
-2. Extract its contents to the libraries folder in your Arduino sketchbook. (".../Documents/Arduino/libraries" on Windows)
-3. Rename the folder from "Lixie_II-master" to "Lixie_II".
+More info here:
+[HACKADAY.IO](https://hackaday.io/project/18633-lixie-an-led-alternative-to-the-nixie-tube/log/150072-lixie-is-now-3499-production-updates)
+=======
+Hey Lixie fans! This library is a work in progress, stay tuned for documentation and examples!
+>>>>>>> parent of cac07ae... Merge branch 'master' of https://github.com/connornishijima/Edgelit

+ 48 - 0
examples/color_functions/color_functions.ino

@@ -0,0 +1,48 @@
+#include <Lixie_II.h>           // https://github.com/connornishijima/Lixie_II
+#define DATA_PIN        13      // Lixie DIN connects to this pin (D7 on Wemos)
+#define NUM_DIGITS      4
+Lixie_II lix(DATA_PIN, NUM_DIGITS);
+
+void setup() {
+	lix.begin();
+}
+
+void loop() {
+  lix.write(222222); // Any number works
+  
+  // Sets all currently used LEDs (ON) to cyan
+  lix.color_all(ON, CRGB(0,255,255));
+  delay(3000);
+ 
+  // Sets all "unused" LEDs (OFF) to a dim red
+  lix.color_all(OFF, CRGB(32,0,0));
+  delay(3000);
+
+  // These "OFF" LEDs can be used creatively, such as mimicking LED underlights of a Nixie Tube:
+  lix.color_all(ON, CRGB(255, 70, 7));
+  lix.color_all(OFF, CRGB(0, 3, 8));  
+  delay(3000);
+  // (However, save yourself the time and just use lix.nixie(); as a shortcut for this!)
+
+  // Conversely, all LEDs can be colored the same color for things such as notification effects.
+  lix.color_all(ON, CRGB(255, 0, 0));
+  lix.color_all(OFF, CRGB(255, 0, 0));  
+  delay(3000);
+
+  // Two colors can also be used in a single digit
+  lix.color_all(OFF, CRGB(0,0,0)); // This clears the "OFF" color from before
+  lix.color_all_dual(ON, CRGB(255,0,0), CRGB(0,0,255));
+  delay(3000);
+
+  // You can also color individual displays ("1" is second from the right, your first Lixie in the chain is "0"
+  lix.color_display(1, ON, CRGB(0,255,0));
+  delay(3000);
+
+  // Gradients between a left and righthand color are possible as well
+  lix.gradient_rgb(ON, CRGB(255,255,0), CRGB(0,255,255));
+  delay(3000);
+  
+  // Gradients between a left and righthand color are possible as well
+  lix.gradient_rgb(ON, CRGB(255,255,0), CRGB(0,255,255));
+  delay(3000);
+}

+ 18 - 0
examples/count_up/count_up.ino

@@ -0,0 +1,18 @@
+#include <Lixie_II.h>           // https://github.com/connornishijima/Lixie_II
+#define DATA_PIN        13      // Lixie DIN connects to this pin (D7 on Wemos)
+#define NUM_DIGITS      4
+Lixie_II lix(DATA_PIN, NUM_DIGITS);
+
+uint16_t counter = 0;
+
+void setup() {
+	lix.begin();                         // Mandatory, sets up animation timer
+	lix.color_all(ON, CRGB(255, 70, 7)); // (Optional) Nixie tube coloring
+	lix.transition_time(125);            // (Optional) 125 milliseconds crossfade time 
+}
+
+void loop() {
+	lix.write(counter);
+	delay(1000);
+	counter = counter + 1;
+}

+ 17 - 0
examples/floating_point/floating_point.ino

@@ -0,0 +1,17 @@
+#include <Lixie_II.h>           // https://github.com/connornishijima/Lixie_II
+#define DATA_PIN        13      // Lixie DIN connects to this pin (D7 on Wemos)
+#define NUM_DIGITS      4
+Lixie_II lix(DATA_PIN, NUM_DIGITS);
+
+void setup() {
+  lix.begin();
+  lix.transition_type(INSTANT); // Defaults to CROSSFADE of 250ms
+  lix.gradient_rgb(ON, CRGB(0,255,0), CRGB(0,255,255)); // Gradient from green to cyan
+}
+
+void loop() {
+  uint32_t time_millis = millis();
+  float seconds = time_millis / 1000.0;
+  lix.write_float(seconds, 2); // Writes a floating point number (of the seconds elapsed) with 2 decimal places.
+  delay(1);
+}

+ 151 - 0
examples/introduction_tour/introduction_tour.ino

@@ -0,0 +1,151 @@
+/*
+   ____ ___  ____ _  _     ____ ____ ____ _ ____ _        _  _ ____ _  _ _ ___ ____ ____   /
+   |  | |__] |___ |\ |     [__  |___ |__/ | |__| |        |\/| |  | |\ | |  |  |  | |__/  / 
+   |__| |    |___ | \|     ___] |___ |  \ | |  | |___     |  | |__| | \| |  |  |__| |  \ .  
+
+  (115200 Baud)
+*/                                                                                                                
+
+void title(){
+  Serial.println(F("----------------------------------"));
+  Serial.println(F(" Lixie II Introduction Tour       "));
+  Serial.println(F(" by Connor Nishijima              "));
+  Serial.println(F(" November 1st, 2019               "));
+  Serial.println();
+  Serial.println(F(" Released under the GPLv3 License "));
+  Serial.println(F("----------------------------------"));
+}
+
+#include "Lixie_II.h" // Include Lixie Library
+
+#define DATA_PIN   13 // Pin to drive Lixies (D7 on Wemos)
+#define NUM_LIXIES 4  // How many Lixies you have
+Lixie_II lix(DATA_PIN, NUM_LIXIES);
+
+uint16_t function_wait = 8000;
+
+void setup() {
+  Serial.begin(115200);
+  lix.begin(); // Initialize LEDs
+  lix.white_balance(Tungsten100W); // Default
+    // Can be: Tungsten40W, Tungsten100W,  
+    //         Halogen,     CarbonArc,
+    //         HighNoonSun, DirectSunlight,
+    //         OvercastSky, ClearBlueSky  
+    // 2,600K - 20,000K
+	delay(1000);
+}
+
+
+void loop() {
+  title();
+  run_demo();
+}
+
+void run_demo(){
+  lix.color_all(ON, CRGB(0,255,255)); // Start with cyan color
+  delay(1000);
+  
+  Serial.println("This is a tour of the basic and advanced features of the Lixie II library!\n");
+  delay(2000);
+  
+  Serial.println("Let's begin!\n");
+  delay(3000);
+  
+  Serial.println("Lixie II can take integers, char arrays, or floats as input with lix.write() and lix.write_float(). (Any non-numeric chars are ignored)\n");
+  lix.write_float(2.0, 1);
+  delay(function_wait);
+  
+  Serial.println("For the Nixie fans, Lixie II offers the lix.nixie() function to shortcut you to pleasant Nixie tube colors!\n");
+  lix.nixie();
+  lix.write(222222);
+  delay(function_wait);
+  
+  Serial.println("Lixie II operates with 'ON' and 'OFF' colors.");
+  Serial.println("Currently, the ON color is orange, and the OFF");
+  Serial.println("color is a dark blue.\n");
+  delay(function_wait);
+  
+  Serial.println("They can be configured independently in the lix.color_all(<ON/OFF>, CRGB col) function.");
+  Serial.println("I've now used color_all(OFF, CRGB(0,8,0)); to change the blue to a dark green!\n");
+  lix.color_all(OFF, CRGB(0,10,0));
+  delay(function_wait);
+  
+  Serial.println("Lixies can also have two colors per digit by using lix.color_all_dual(ON, CRGB col1, CRGB col2);!\n");
+  lix.color_all_dual(ON, CRGB(255,0,0), CRGB(0,0,255));
+  delay(function_wait);
+  
+  Serial.println("Individual displays can also be colored with lix.color_display(index, <ON/OFF>, CRGB col)!\n");
+  lix.color_display(1, ON, CRGB(255,0,0));
+  lix.color_display(1, OFF, CRGB(0,0,10));
+  delay(function_wait);
+  
+  Serial.println("Gradients are possible with lix.gradient_rgb(<ON/OFF>, CRGB col1, CRGB col2)!\n");
+  lix.color_all(OFF, CRGB(0,0,0)); // Remove OFF color
+  lix.gradient_rgb(ON, CRGB(255,0,255), CRGB(0,255,255));
+  delay(function_wait);
+  
+  Serial.println("Lixies can also show 'streaks' at any position between left (0.0) and");
+  Serial.println("right (1.0) with lix.streak(CRGB col, float pos, uint8_t blur);\n");
+  lix.stop_animation(); // Necessary to prevent rendering of current numbers
+  float iter = 0;
+  uint32_t t_start = millis();
+  while(millis() < t_start+function_wait){
+    float pos = (sin(iter) + 1)/2.0;
+    lix.streak(CRGB(0,255,0), pos, 8);
+    iter += 0.02;
+    delay(1);
+  }
+
+  Serial.println("Here's lix.streak() with 'blur' of '1' (none)\n");
+  t_start = millis();
+  while(millis() < t_start+function_wait){
+    float pos = (sin(iter) + 1)/2.0;
+    lix.streak(CRGB(0,255,0), pos, 1);
+    iter += 0.02;
+    delay(1);
+  }
+
+  Serial.println("These can be used to show a progress percentage, or 'busy' indicator while WiFi connects.\n");
+  delay(3000);
+  Serial.println("One shortcut to this functionality is lix.sweep_color(CRGB col, uint16_t speed, uint8_t blur);\n");
+  for(uint8_t i = 0; i < 5; i++){
+    lix.sweep_color(CRGB(0,0,255), 20, 5);
+  }
+  lix.start_animation(); // This resumes "number mode" after we stopped it above
+  
+  lix.nixie();
+  Serial.println("Transitions between numbers can also be modified.");
+  Serial.println("By default Lixie II uses a 250ms crossfade, but this can");
+  Serial.println("be changed with lix.transition_time(ms) or lix.transition_type(<INSTANT/CROSSFADE>);\n");
+  
+  Serial.println("CROSSFADE...");
+  for(uint8_t i = 0; i < 30; i++){
+    lix.write(i);
+    delay(250);
+  }
+  Serial.println("INSTANT!\n");
+  lix.transition_type(INSTANT);
+  for(uint8_t i = 0; i < 30; i++){
+    lix.write(i);
+    delay(250);
+  }
+
+  Serial.println("And to end our guided tour, here's a floating point of seconds elapsing, using lix.rainbow(uint8_t hue, uint8_t separation);");
+  delay(3000);
+  
+  t_start = millis();
+  float hue = 0;
+  while(millis() < t_start+10000){
+    uint32_t millis_passed = millis()-t_start;
+    float seconds = millis_passed / 1000.0;
+    lix.write_float(seconds, 2);
+    lix.rainbow(hue, 20);
+    hue += 0.1;
+    delay(1);
+  }
+  lix.write_float(10.0, 2);
+  delay(function_wait);
+  
+  Serial.println("\n\n");
+}

+ 30 - 23
keywords.txt

@@ -1,46 +1,53 @@
 ###################################
-# Syntax Coloring Map for Edgelit
+# Syntax Coloring Map for Lixie II
 ###################################
 
 ###################################
 # Datatypes (KEYWORD1)
 ###################################
 
-Edgelit	KEYWORD1
+Lixie_II	KEYWORD1
 
 ###################################
 # Methods and Functions (KEYWORD2)
 ###################################
 
+build_controller	KEYWORD2
 begin	KEYWORD2
+transition_type	KEYWORD2
+transition_time	KEYWORD2
+max_power	KEYWORD2
+color_all	KEYWORD2
+color_all_dual	KEYWORD2
+color_display	KEYWORD2	
+gradient_rgb	KEYWORD2
+start_animation	KEYWORD2
+stop_animation	KEYWORD2
 write	KEYWORD2
+write_float	KEYWORD2
+clear_all	KEYWORD2
+write_digit	KEYWORD2
 push_digit	KEYWORD2
-clear	KEYWORD2
-max_power	KEYWORD2
-nixie	KEYWORD2
-vfd_green	KEYWORD2
-vfd_blue	KEYWORD2
-progress	KEYWORD2
-animation_callback	KEYWORD2
-led_count	KEYWORD2
-empty_displays	KEYWORD2
+clear_digit	KEYWORD2
+mask_update	KEYWORD2
+fade_in	KEYWORD2
+fade_out	KEYWORD2
 brightness	KEYWORD2
-transition_type	KEYWORD2
-transition_time	KEYWORD2
-color	KEYWORD2
-color_pixel	KEYWORD2
-color_display	KEYWORD2
+run	KEYWORD2
+wait	KEYWORD2
+streak	KEYWORD2
+sweep_color	KEYWORD2
+sweep_gradient	KEYWORD2
+nixie	KEYWORD2
+white_balance	KEYWORD2
+rainbow	KEYWORD2
 
 ###################################
 # Constants (LITERAL1)
 ###################################
 
-LIXIE_1	LITERAL1
-NIXIE_PIPE	LITERAL1
-
-FRONT	LITERAL1
-BACK	LITERAL1
+ON	LITERAL1
+OFF	LITERAL1
 
 INSTANT	LITERAL1
-CROSSFADE	LITERAL1
-FADE_TO_BLACK	LITERAL1
+CROSSFADE	LITERAL1

+ 24 - 0
library.json

@@ -0,0 +1,24 @@
+{
+  "name": "Lixie II",
+  "description": "Library for controlling Lixie II displays!",
+  "keywords": "fastled, lixie, nixie, magic, pwm, edgelit, clock",
+  "authors":
+  {
+    "name": "Connor Nishijima",
+    "email": "connornishijima@gmail.com"
+  },
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/connornishijima/Lixie_II.git"
+  },
+  "version": "1.2.0",
+  "examples": "examples/*.ino",
+  "frameworks": "arduino",
+  "platforms":
+  [
+	"avr",
+    "esp8266",
+	"esp32"
+  ]
+}

+ 5 - 5
library.properties

@@ -1,9 +1,9 @@
 name=Lixie II
-version=1.0.0
+version=1.2.0
 author=Connor Nishijima
 maintainer=Connor Nishijima <connornishijima@gmail.com>
-sentence=ESP32 Library for controlling the Lixie II!
-paragraph=ESP32 Library for controlling the Lixie II!
+sentence=Library for controlling Lixie II displays!
+paragraph=Library for controlling Lixie II displays!
 category=Signal Input/Output
-url=https://github.com/connornishijima/Lixie_II-arduino
-architectures=esp32
+url=https://github.com/connornishijima/Lixie_II
+architectures=*

+ 525 - 0
src/Edgelit.cpp

@@ -0,0 +1,525 @@
+/*
+	Edgelit.cpp - Library for controlling any WS2812B-based edgelit
+	numeric display, such as the Lixie or NixiePipe.
+	
+	Created by Connor Nishijma April 17, 2018
+	Released under the GPLv3 License
+*/
+
+#include "Edgelit.h"
+
+// ##################################################################################
+// CUSTOM DISPLAY TYPES CAN BE ADDED HERE! ------------------------------------------
+// Just make sure to update the array lengths! (The bracketed numbers)
+// You'll also need to add a #define in the Edgelit.h file that points to
+// the index of your display in these arrays. For example:
+
+// #define CUSTOM_TYPE 2
+
+// How many e_leds are on each type of display?
+const uint8_t e_led_count_pcb[2] = {
+	20, // Lixie 1
+	10, // NixiePipe
+};
+
+// Going through the LED strip from beginning to end, what numerals do these lights pertain to?
+// -1 == IGNORE LED (if led count per board is lower than 22
+// -2 == AUXILARY PANE for non-numeric use if your display has this (decimal, for example)
+const int16_t e_numeral_positions[2][22] = {
+	{3, 9, 2, 0, 1, 6, 5, 7, 4, 8, 3, 9, 2, 0, 1, 6, 5, 7, 4, 8,-1,-1}, // Lixie 1
+	{9, 8, 7, 6, 5, 4, 3, 2, 1, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} // NixiePipe
+};
+
+// ----------------------------------------------------------------------------------
+// ##################################################################################
+
+// Huge mess of variables starts here ----------------------------
+
+uint8_t e_disp_type = 0;  // Stores display type (Lixie, NixiePipe, Etc.) for reference during control
+uint16_t e_NumLEDs;       // Keeps the number of LEDs based on board type and display quantity.
+uint8_t e_NumDigits;      // Defined by the user at compile time, set in Edgelit::Edgelit() below
+uint8_t e_LEDsPerDisplay; // It's... the e_LEDsPerDisplay.
+
+uint8_t e_trans_type = INSTANT;	// Transition type when writing a new number to displays.
+								// Defaults to INSTANT (no transition)
+
+uint16_t e_trans_time = 1;		// Default transition time (ms) is 1, to avoid possible division by zero.
+
+CLEDController *e_controller; // FastLED 
+
+float e_master_brightness = 1.0;
+
+bool *e_numeral_changed;
+float *e_numeral_brightness;
+float *e_numeral_brightness_target;
+float e_numeral_brightness_push = 1;
+
+CRGB *e_leds;
+CRGB *e_on_colors;
+CRGB *e_off_colors;
+uint8_t *e_mask0;
+uint8_t *e_mask1;
+uint8_t *e_mask_temp;
+float e_mask_fader = 1.0;
+float e_mask_push = 0.0;
+
+uint8_t e_fade_step = 2; // DONE
+uint8_t e_new_fade_step = 2; // DONE
+uint8_t *e_current_digits;
+uint8_t *e_new_digits;
+bool *e_digits_used;
+
+uint8_t e_current_mask = 0;
+
+bool e_enable_empty_displays = false;
+bool e_halt_updating = false;
+
+os_timer_t edge_isr;
+
+// Huge mess of variables ends here, thank god that's over -------
+
+void nothing(){
+	// Used as default animation callback
+	// It does nothing, but it "does nothing" really well!
+}
+
+void (*animation_func)() = nothing; // Default can't be NULL. Sad!
+
+void edge_run(void *parameters){
+	if(!e_halt_updating){		
+		if(e_mask_fader < 1 && e_trans_type != FADE_TO_BLACK){
+			e_mask_fader+=e_mask_push;
+		}
+		
+		if(e_mask_fader < 0){
+			e_mask_fader = 0;
+		}
+		else if(e_mask_fader > 1){
+			e_mask_fader = 1;
+		}
+		
+		bool done_fading = true;
+		for(uint8_t i = 0; i < e_NumDigits; i++){
+			if(e_numeral_brightness[i] != e_numeral_brightness_target[i]){
+				if(e_numeral_brightness[i] < e_numeral_brightness_target[i]){
+					e_numeral_brightness[i] += e_numeral_brightness_push;
+				}
+				else if(e_numeral_brightness[i] > e_numeral_brightness_target[i]){
+					e_numeral_brightness[i] -= e_numeral_brightness_push;
+				}
+			}
+			
+			if(e_numeral_brightness[i] < 0){
+				e_numeral_brightness[i] = 0;
+			}
+			
+			if(e_numeral_brightness[i] > 1){
+				e_numeral_brightness[i] = 1;
+			}
+			
+			if(e_numeral_brightness[i] != e_numeral_brightness_target[i]){
+				done_fading = false;
+			}
+			
+			if(e_current_digits[i] != e_new_digits[i]){
+				e_numeral_changed[i] = true;
+			}
+			else{
+				e_numeral_changed[i] = false;
+			}
+
+			if(e_trans_type == FADE_TO_BLACK){
+				// In charge of FADE_TO_BLACK animation steps
+				if(e_numeral_changed[i]){
+					if(e_fade_step == 0 && done_fading){
+						e_mask_fader = 0.0;
+						e_numeral_brightness_target[i] = 0.0;
+						e_new_fade_step = 1;
+					}
+					else if(e_fade_step == 1 && done_fading){
+						e_mask_fader = 1.0;
+						if(e_digits_used[i]){
+							e_numeral_brightness_target[i] = 1.0;
+						}
+						e_new_fade_step = 2;
+					}
+					else if(e_fade_step == 2 && done_fading){
+						e_current_digits[i] = e_new_digits[i];
+					}
+				}
+			}
+			else{
+				e_current_digits[i] = e_new_digits[i];
+			}
+		}
+		
+		e_fade_step = e_new_fade_step;
+		
+		for(uint16_t i = 0; i < e_NumLEDs; i++){
+			if(e_current_mask == 0){
+				e_mask_temp[i] = e_mask0[i]*e_mask_fader + e_mask1[i]*(1-e_mask_fader);
+			}
+			else if(e_current_mask == 1){
+				e_mask_temp[i] = e_mask1[i]*e_mask_fader + e_mask0[i]*(1-e_mask_fader);
+			}
+			
+			float mask_float = (e_mask_temp[i]/255.0) * e_numeral_brightness[i/e_LEDsPerDisplay];
+			
+			if(e_digits_used[i/e_LEDsPerDisplay] == false){
+				e_numeral_brightness_target[i/e_LEDsPerDisplay] = 0.0;
+				if(e_trans_type == INSTANT){
+					e_numeral_brightness[i/e_LEDsPerDisplay] = 0.0;
+				}
+			}
+			else{
+				if(e_trans_type != FADE_TO_BLACK){
+					e_numeral_brightness[i/e_LEDsPerDisplay] = 1.0;
+					e_numeral_brightness_target[i/e_LEDsPerDisplay] = 1.0;
+				}
+			}	
+
+			// temp_brightness is used to keep "off colors" from appearing on
+			// displays without a numeral currently shown on them. Using
+			// empty_displays(true) will force temp_brightness to 1.
+			float temp_brightness;
+			if(e_enable_empty_displays){
+				temp_brightness = 1.0;
+			}
+			else{
+				temp_brightness = e_numeral_brightness[i/e_LEDsPerDisplay];
+			}
+			
+			e_leds[i].r = (e_on_colors[i].r * mask_float + e_off_colors[i].r * (1-mask_float))*temp_brightness*e_master_brightness;
+			e_leds[i].g = (e_on_colors[i].g * mask_float + e_off_colors[i].g * (1-mask_float))*temp_brightness*e_master_brightness;
+			e_leds[i].b = (e_on_colors[i].b * mask_float + e_off_colors[i].b * (1-mask_float))*temp_brightness*e_master_brightness;
+		}
+		
+		animation_func();
+		e_controller->showLeds(); // Show our work!		
+	}
+}
+
+Edgelit::Edgelit(const uint8_t pin, uint8_t nDigits, uint8_t d_type){
+	e_disp_type = d_type;
+	e_LEDsPerDisplay = e_led_count_pcb[e_disp_type];
+	e_NumDigits = nDigits;
+	e_NumLEDs = nDigits*e_LEDsPerDisplay;
+	
+	e_leds = new CRGB[e_NumLEDs];
+	e_on_colors = new CRGB[e_NumLEDs];
+	e_off_colors = new CRGB[e_NumLEDs];
+	e_mask0 = new uint8_t[e_NumLEDs];
+	e_mask1 = new uint8_t[e_NumLEDs];
+	e_mask_temp = new uint8_t[e_NumLEDs];
+	
+	e_current_digits = new uint8_t[e_NumDigits];
+	e_new_digits = new uint8_t[e_NumDigits];
+	e_digits_used = new bool[e_NumDigits];
+	e_numeral_changed = new bool[e_NumDigits];
+	e_numeral_brightness = new float[e_NumDigits];
+	e_numeral_brightness_target = new float[e_NumDigits];
+	
+	for(uint16_t i = 0; i < e_NumLEDs; i++){
+		e_leds[i] = CRGB(0,0,0);
+		e_on_colors[i] = CRGB(255,255,255);
+		e_off_colors[i] = CRGB(0,0,0);
+		e_mask0[i] = 0;
+		e_mask1[i] = 0;
+		e_mask_temp[i] = 0;
+	}
+	
+	for(uint8_t i = 0; i < e_NumDigits; i++){
+		e_numeral_changed[i] = false;
+		e_numeral_brightness[i] = 1.0;
+		e_numeral_brightness_target[i] = 1.0;
+		
+		e_digits_used[i] = false;
+		e_current_digits[i] = 10; // Not a valid number to start with
+		e_new_digits[i] = 10;
+	}
+	
+	build_controller(pin);
+}
+
+void Edgelit::begin() {
+	#ifdef ESP8266
+		os_timer_setfn(&edge_isr, edge_run, NULL);
+		os_timer_arm(&edge_isr, 10, true); // ~100 FPS
+
+	#else
+		// TIMER 1 for interrupt frequency 100 Hz:
+		cli(); // stop interrupts
+		TCCR1A = 0; // set entire TCCR1A register to 0
+		TCCR1B = 0; // same for TCCR1B
+		TCNT1  = 0; // initialize counter value to 0
+		// set compare match register for 100 Hz increments
+		OCR1A = F_CPU / (8 * 100) - 1; // (must be <65536)
+		// turn on CTC mode
+		TCCR1B |= (1 << WGM12);
+		// Set CS12, CS11 and CS10 bits for 8 prescaler
+		TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
+		// enable timer compare interrupt
+		TIMSK1 |= (1 << OCIE1A);
+		sei(); // allow interrupts
+	#endif
+	
+	max_power(5,500);
+}
+
+void Edgelit::max_power(uint8_t volts, uint16_t milliamps){
+	FastLED.setMaxPowerInVoltsAndMilliamps(volts,milliamps);
+}
+
+void Edgelit::transition_type(uint8_t type){
+	e_trans_type = type;
+}
+
+void Edgelit::transition_time(uint16_t ms){ // Supports down to 50ms
+	e_trans_time = ms;
+	e_mask_push = 1 / float(e_trans_time/10.0);
+	
+	e_numeral_brightness_push = e_mask_push*2; // Used for FADEOUT animation
+}
+
+uint8_t Edgelit::get_size(uint32_t input) const{
+	uint8_t places = 1;
+	while(input > 9){
+		places++;
+		input /= 10;
+	}
+	return places;
+}
+
+void Edgelit::clear() {
+	if(e_current_mask == 0){
+		memset(e_mask0, 0, e_NumLEDs);
+	}
+	else if(e_current_mask == 1){
+		memset(e_mask1, 0, e_NumLEDs);
+	}
+
+	memset(e_new_digits, 0, e_NumDigits);
+	memset(e_digits_used, 0, e_NumDigits);
+	memset(e_mask_temp, 0, e_NumLEDs);
+}
+
+void Edgelit::push_digit(int16_t number) {
+	if(number > 9) return;
+
+	// If multiple displays, move all LED states forward one
+	if (e_NumDigits > 1) {
+		for (uint16_t i = e_NumLEDs - 1; i >= e_LEDsPerDisplay; i--) {
+			if(e_current_mask == 0){
+				e_mask0[i] = e_mask0[i - e_LEDsPerDisplay];
+			}
+			else{
+				e_mask1[i] = e_mask1[i - e_LEDsPerDisplay];
+			}
+		}
+		for(uint8_t i = e_NumDigits-1; i >= 1; i--){
+			e_new_digits[i] = e_new_digits[i-1];
+			e_digits_used[i] = e_digits_used[i-1];
+		}
+	}
+	
+	e_new_digits[0] = number;
+	e_digits_used[0] = true;
+ 
+	// Clear the LED states for the first display
+	for (uint16_t i = 0; i < e_LEDsPerDisplay; i++) {
+		if(e_current_mask == 0){
+			e_mask0[i] = e_mask0[i - e_LEDsPerDisplay];
+		}
+		else{
+			e_mask1[i] = e_mask1[i - e_LEDsPerDisplay];
+		}
+	}
+	
+	for(uint8_t i = 0; i < e_LEDsPerDisplay; i++){
+		if(e_numeral_positions[e_disp_type][i] == number){
+			if(e_current_mask == 0){
+				e_mask0[i] = 255;
+			}
+			else{
+				e_mask1[i] = 255;
+			}
+		}
+		else{
+			if(e_current_mask == 0){
+				e_mask0[i] = 0;
+			}
+			else{
+				e_mask1[i] = 0;
+			}
+		}
+	}	
+}
+
+void queue_animation(){
+	if(e_trans_type == INSTANT){
+		e_mask_fader = 1.0;
+		e_trans_time = 1;
+	}
+	else if(e_trans_type == CROSSFADE){
+		if(e_mask_fader == 1.0){ // If previous animation is done
+			e_mask_fader = 0.0;
+		}
+	}
+	else if(e_trans_type == FADE_TO_BLACK){
+		e_mask_fader = 0;
+		e_fade_step = 0;
+		e_new_fade_step = 0;
+	}
+}
+
+void Edgelit::write(uint32_t input){
+	e_current_mask = !e_current_mask; // switch between masks
+	uint32_t nPlace = 1;
+
+	clear();
+
+	// Powers of 10 while avoiding floating point math
+	for(uint8_t i = 1; i < get_size(input); i++){
+		nPlace *= 10;
+	}
+
+	for(nPlace; nPlace > 0; nPlace /= 10){
+		push_digit(input / nPlace);
+		if(nPlace > 1) input = (input % nPlace);
+	}
+	
+	queue_animation();
+}
+
+void Edgelit::color(CRGB col, uint8_t layer){
+	for(uint16_t i = 0; i < e_NumLEDs; i++){
+		if(layer == FRONT){
+			e_on_colors[i] = col;
+		}
+		else if(layer == BACK){
+			e_off_colors[i] = col;
+		}
+	}
+}
+
+void Edgelit::color_pixel(uint16_t led, CRGB col, uint8_t layer){
+	if(layer == FRONT){
+		e_on_colors[led] = col;
+	}
+	else if(layer == BACK){
+		e_off_colors[led] = col;
+	}
+}
+
+CRGB Edgelit::color_pixel(uint16_t led, uint8_t layer){
+	if(layer == FRONT){
+		return e_on_colors[led];
+	}
+	else if(layer == BACK){
+		return e_off_colors[led];
+	}
+}
+
+void Edgelit::color_display(uint16_t disp, CRGB col, uint8_t layer){
+	for(uint8_t i = 0; i < e_LEDsPerDisplay; i++){
+		if(layer == FRONT){
+			e_on_colors[(e_LEDsPerDisplay*disp)+i] = col;
+		}
+		else if(layer == BACK){
+			e_off_colors[(e_LEDsPerDisplay*disp)+i] = col;
+		}
+	}
+}
+
+void Edgelit::nixie(uint8_t argon_intensity){
+	for(uint16_t i = 0; i < e_NumLEDs; i++){
+		e_on_colors[i] = CRGB(255,70,7);    // NEON
+		e_off_colors[i] = CRGB(50,150,255); // ARGON
+		
+		e_off_colors[i].r *= (argon_intensity/255.0);
+		e_off_colors[i].g *= (argon_intensity/255.0);
+		e_off_colors[i].b *= (argon_intensity/255.0);
+	}
+}
+
+void Edgelit::vfd_green(uint8_t aura_intensity){
+	for(uint16_t i = 0; i < e_NumLEDs; i++){
+		e_on_colors[i] = CRGB(100,255,100);  // ION
+		e_off_colors[i] = CRGB(100,255,100); // AURA
+		
+		e_off_colors[i].r *= (aura_intensity/255.0);
+		e_off_colors[i].g *= (aura_intensity/255.0);
+		e_off_colors[i].b *= (aura_intensity/255.0);
+	}
+}
+
+void Edgelit::vfd_blue(uint8_t aura_intensity){
+	for(uint16_t i = 0; i < e_NumLEDs; i++){
+		e_on_colors[i] = CRGB(150,255,255);  // ION
+		e_off_colors[i] = CRGB(0,100,255);   // AURA
+		
+		e_off_colors[i].r *= (aura_intensity/255.0);
+		e_off_colors[i].g *= (aura_intensity/255.0);
+		e_off_colors[i].b *= (aura_intensity/255.0);
+	}
+}
+
+void Edgelit::progress(float percent, float brightness){
+	memset(e_digits_used, 1, e_NumDigits);
+	memset(e_numeral_brightness-1, brightness, e_NumDigits);
+	memset(e_numeral_brightness_target-1, brightness, e_NumDigits);	// Not sure why e_numeral_brightness has to be offset by 1 here
+	e_mask_fader = 1.0;
+	e_fade_step = 0;
+	
+	uint16_t index = e_NumLEDs-(e_NumLEDs*(percent/100.0));
+	
+	for(uint16_t i = 0; i < e_NumLEDs; i++){
+		if(i <= index){
+			e_mask0[i] = 0;
+			e_mask1[i] = 0;
+		}
+		else{
+			e_mask0[i] = 255;
+			e_mask1[i] = 255;
+		}
+		yield();
+	}
+}
+
+void Edgelit::animation_callback(void (*func)()) {
+	 animation_func = func;
+}
+
+uint16_t Edgelit::led_count(){
+	return e_NumLEDs;
+}
+
+void Edgelit::empty_displays(bool state){
+	e_enable_empty_displays = state;
+}
+
+void Edgelit::brightness(float bright){
+	e_master_brightness = bright;
+}
+
+// Sets white_balance correction for the displays, defaults to Tungsten100W.
+// http://fastled.io/docs/3.1/group___color_enums.html#gadf6bcba67c9573665af20788c4431ae8
+void Edgelit::white_balance(CRGB correction){
+	e_controller->setCorrection(correction);
+}
+
+
+void Edgelit::build_controller(const uint8_t pin){
+	//FastLED control pin has to be defined as a constant, (not just const, it's weird) this is a hacky workaround.
+	// Also, this stops you from defining non existent pins with your current board architecture
+	if (pin == 0)
+		e_controller = &FastLED.addLeds<LED_TYPE, 0, COLOR_ORDER>(e_leds, e_NumLEDs);
+	else if (pin == 2)
+		e_controller = &FastLED.addLeds<LED_TYPE, 2, COLOR_ORDER>(e_leds, e_NumLEDs);
+	else if (pin == 4)
+		e_controller = &FastLED.addLeds<LED_TYPE, 4, COLOR_ORDER>(e_leds, e_NumLEDs);
+	else if (pin == 5)
+		e_controller = &FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(e_leds, e_NumLEDs);
+	else if (pin == 12)
+		e_controller = &FastLED.addLeds<LED_TYPE, 12, COLOR_ORDER>(e_leds, e_NumLEDs);
+}

+ 85 - 0
src/Edgelit.h

@@ -0,0 +1,85 @@
+/*
+	Edgelit.h - Library for controlling any WS2812B-based edgelit
+	numeric display, such as the Lixie or NixiePipe.
+	
+	Created by Connor Nishijma April 17, 2018
+	Released under the GPLv3 License
+*/
+
+// This stops the biggest fans of my hacky code
+// from including it more than once.
+#ifndef edgelit_h
+#define edgelit_h
+
+// CUSTOM DISPLAY TYPES ARE DEFINED HERE! ------------------------
+// ----Check Edgelit.cpp for details!
+#define LIXIE_1		0
+#define NIXIE_PIPE	1
+// CUSTOM DISPLAY TYPES ARE DEFINED HERE! ------------------------ 
+
+// Every library has this. I've never gotten around to
+// researching what the hell this is. Better play it safe.
+#include "Arduino.h"
+
+// For os_timer_t interrupts
+extern "C" {
+	#include "user_interface.h"
+}
+
+// FastLED has issues with the ESP8266, especially
+// when used with networking, so we fix that here.
+#define FASTLED_ESP8266_RAW_PIN_ORDER
+#define FASTLED_ALLOW_INTERRUPTS 		0
+#define FASTLED_INTERRUPT_RETRY_COUNT	0
+
+// Aside from those issues, it's my tool of choice for WS2812B
+#include "FastLED.h"
+
+// FastLED info for the LEDs
+#define LED_TYPE WS2812B
+#define COLOR_ORDER GRB
+
+// Transition types
+#define INSTANT			0
+#define CROSSFADE		1
+#define FADE_TO_BLACK	2
+
+// Layer types
+#define FRONT	0
+#define BACK	1
+
+// Functions
+class Edgelit
+{
+	public:
+		Edgelit(const uint8_t pin, uint8_t nDigits, uint8_t d_type);
+		void begin();
+		void transition_type(uint8_t type);
+		void transition_time(uint16_t ms);
+		void write(uint32_t input);
+		void push_digit(int16_t number);
+		void clear();
+		void max_power(uint8_t volts, uint16_t milliamps);
+		void nixie(uint8_t argon_intensity = 3);
+		void vfd_green(uint8_t aura_intensity = 3);
+		void vfd_blue(uint8_t aura_intensity = 3);
+
+		void color(CRGB col, uint8_t layer = 0);
+		void color_pixel(uint16_t led, CRGB col, uint8_t layer = 0);
+		CRGB color_pixel(uint16_t led, uint8_t layer = 0);
+
+		void color_display(uint16_t disp, CRGB col, uint8_t layer = 0);
+
+		void progress(float percent, float brightness);
+		void animation_callback(void (*func)());
+		uint16_t led_count();
+		void empty_displays(bool state);
+		void brightness(float bright);
+		void white_balance(CRGB correction);
+		
+	private:
+		uint8_t get_size(uint32_t input) const;
+		void build_controller(uint8_t DataPin);		
+};
+
+#endif

+ 282 - 47
src/Lixie_II.cpp

@@ -23,8 +23,8 @@ const uint8_t led_assignments[leds_per_digit] = {	1, 9, 4, 6, 255, 7, 3, 0, 2, 8
 const uint8_t x_offsets[leds_per_digit] 	  = {	0, 0, 0, 0, 1,   1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4,   5, 5, 5, 5	};
 uint8_t max_x_pos = 0;
 
-CRGB *color_on;
-CRGB *color_off;
+CRGB *col_on;
+CRGB *col_off;
 
 uint8_t *led_mask_0;
 uint8_t *led_mask_1;
@@ -34,10 +34,15 @@ float mask_fader = 0.0;
 float mask_push = 1.0;
 bool mask_fade_finished = false;
 
-uint8_t transition_type = CROSSFADE;
+uint8_t trans_type = CROSSFADE;
+uint16_t trans_time = 250;
 bool transition_mid_point = true;
 
-Ticker lixie_animation;
+bool background_updates = true;
+
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
+	Ticker lixie_animation;
+#endif
 
 uint16_t led_to_x_pos(uint16_t led){
 	uint8_t led_digit_pos = x_offsets[led%22];
@@ -73,7 +78,7 @@ void animate(){
 		uint8_t mask_input_0 = led_mask_0[i];
 		uint8_t mask_input_1 = led_mask_1[i];
 
-		if(transition_type == INSTANT || transition_type == CROSSFADE){
+		if(trans_type == INSTANT || trans_type == CROSSFADE){
 			if(current_mask == 0){	
 				mask_float = ((mask_input_0*(1-mask_fader)) + (mask_input_1*(mask_fader)))/255.0;
 			}
@@ -82,9 +87,9 @@ void animate(){
 			}
 		}
 		
-		new_col.r = (color_on[i].r*mask_float) + (color_off[i].r*(1-mask_float));
-		new_col.g = (color_on[i].g*mask_float) + (color_off[i].g*(1-mask_float));
-		new_col.b = (color_on[i].b*mask_float) + (color_off[i].b*(1-mask_float));
+		new_col.r = (col_on[i].r*mask_float) + (col_off[i].r*(1-mask_float));
+		new_col.g = (col_on[i].g*mask_float) + (col_off[i].g*(1-mask_float));
+		new_col.b = (col_on[i].b*mask_float) + (col_off[i].b*(1-mask_float));
 		
 		lix_leds[i] = new_col;	
 		
@@ -96,12 +101,55 @@ void animate(){
 	lix_controller->showLeds();	
 }
 
+void Lixie_II::transition_type(uint8_t type){
+	trans_type = type;
+}
+
+void Lixie_II::transition_time(uint16_t ms){
+	trans_time = ms;
+}
+
+void Lixie_II::run(){
+	animate();
+}
+
+void Lixie_II::wait(){
+	while(mask_fader < 1.0){
+		animate();
+	}
+}
+
 void Lixie_II::start_animation(){
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
 	lixie_animation.attach_ms(20, animate);
+#elif defined(__AVR__)	
+	// TIMER 1 for interrupt frequency 50 Hz:
+	cli(); // stop interrupts
+	TCCR1A = 0; // set entire TCCR1A register to 0
+	TCCR1B = 0; // same for TCCR1B
+	TCNT1  = 0; // initialize counter value to 0
+	// set compare match register for 50 Hz increments
+	OCR1A = 39999; // = 16000000 / (8 * 50) - 1 (must be <65536)
+	// turn on CTC mode
+	TCCR1B |= (1 << WGM12);
+	// Set CS12, CS11 and CS10 bits for 8 prescaler
+	TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
+	// enable timer compare interrupt
+	TIMSK1 |= (1 << OCIE1A);
+	sei(); // allow interrupts
+#endif
+}
+
+#if defined(__AVR__)	
+ISR(TIMER1_COMPA_vect){
+   animate();
 }
+#endif
 
 void Lixie_II::stop_animation(){
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
 	lixie_animation.detach();
+#endif
 }
 
 Lixie_II::Lixie_II(const uint8_t pin, uint8_t number_of_digits){
@@ -113,15 +161,15 @@ Lixie_II::Lixie_II(const uint8_t pin, uint8_t number_of_digits){
 	led_mask_0 = new uint8_t[n_LEDs];
 	led_mask_1 = new uint8_t[n_LEDs];
 		
-	color_on = new CRGB[n_LEDs];
-	color_off = new CRGB[n_LEDs];
+	col_on = new CRGB[n_LEDs];
+	col_off = new CRGB[n_LEDs];
 	
 	for(uint16_t i = 0; i < n_LEDs; i++){
 		led_mask_0[i] = 0;
 		led_mask_1[i] = 0;
 		
-		color_on[i] = CRGB(255,255,255);
-		color_off[i] = CRGB(0,0,0);
+		col_on[i] = CRGB(255,255,255);
+		col_off[i] = CRGB(0,0,0);
 	}
 	
 	build_controller(pin);
@@ -194,6 +242,26 @@ void Lixie_II::write(uint32_t input){
 	mask_update();
 }
 
+bool char_is_number(char input){
+	if(input <= 57 && input >= 48) // if between ASCII '9' and '0'
+		return true;
+	else
+		return false;
+}
+
+void Lixie_II::write(char* input){
+	char temp[20] = "";
+	byte index = 0;
+	for(uint8_t i = 0; i < 20; i++){
+		if(char_is_number(input[i])){
+			temp[index] = input[i];
+			index++;
+		}
+	}
+	uint32_t output = atol(temp);
+	write(output);
+}
+
 void Lixie_II::write_float(float input_raw, uint8_t dec_places){
 	uint16_t dec_places_10s = 1;
 	float input_mult = input_raw;
@@ -316,23 +384,26 @@ void Lixie_II::clear_digit(uint8_t digit, uint8_t num){
 void Lixie_II::mask_update(){
 	mask_fader = 0.0;
 	
-	if(transition_type == INSTANT){
+	if(trans_type == INSTANT){
 		mask_push = 1.0;
 	}
 	else{
-		mask_push = 0.1;
+		float trans_multiplier = trans_time / float(1000);
+		mask_push = 1 / (50.0 * trans_multiplier);
 	}
 	mask_fade_finished = false;
 	transition_mid_point = false;
+	
+	// WAIT GOES HERE
 }
 
 void Lixie_II::color_all(uint8_t layer, CRGB col){
 	for(uint16_t i = 0; i < n_LEDs; i++){
 		if(layer == ON){
-			color_on[i] = col;
+			col_on[i] = col;
 		}
 		else if(layer == OFF){
-			color_off[i] = col;
+			col_off[i] = col;
 		}
 	}
 }
@@ -346,18 +417,18 @@ void Lixie_II::color_all_dual(uint8_t layer, CRGB col_left, CRGB col_right){
 		
 		if(layer == ON){
 			if(side){
-				color_on[i] = col_left;
+				col_on[i] = col_left;
 			}
 			else{
-				color_on[i] = col_right;
+				col_on[i] = col_right;
 			}
 		}
 		else if(layer == OFF){
 			if(side){
-				color_off[i] = col_left;
+				col_off[i] = col_left;
 			}
 			else{
-				color_off[i] = col_right;
+				col_off[i] = col_right;
 			}
 		}
 	}
@@ -367,10 +438,10 @@ void Lixie_II::color_display(uint8_t display, uint8_t layer, CRGB col){
 	uint16_t start_index = leds_per_digit*display;
 	for(uint16_t i = 0; i < leds_per_digit; i++){
 		if(layer == ON){
-			color_on[start_index+i] = col;
+			col_on[start_index+i] = col;
 		}
 		else if(layer == OFF){
-			color_off[start_index+i] = col;
+			col_off[start_index+i] = col;
 		}
 	}
 }
@@ -385,30 +456,47 @@ void Lixie_II::gradient_rgb(uint8_t layer, CRGB col_left, CRGB col_right){
 		col_out.b = (col_right.b*(1-progress)) + (col_left.b*(progress));
 		
 		if(layer == ON){
-			color_on[i] = col_out;
+			col_on[i] = col_out;
 		}
 		else if(layer == OFF){
-			color_off[i] = col_out;
+			col_off[i] = col_out;
 		}
 	}
 }
 
-void Lixie_II::streak(CRGB col, int16_t pos, uint8_t blur){
+void Lixie_II::brightness(uint8_t level){
+	FastLED.setBrightness(level);
+}
+
+void Lixie_II::fade_in(){
+	for(int16_t i = 0; i < 255; i++){
+		brightness(i);
+		FastLED.delay(1);
+	}
+	brightness(255);
+}
+
+void Lixie_II::fade_out(){
+	for(int16_t i = 255; i > 0; i--){
+		brightness(i);
+		FastLED.delay(1);
+	}
+	brightness(0);
+}
+
+void Lixie_II::streak(CRGB col, float pos, uint8_t blur){
+	float pos_whole = pos*n_digits*6; // 6 X-positions in a single display
+	
 	for(uint16_t i = 0; i < n_LEDs; i++){
-		
-		uint16_t pos_delta = abs(led_to_x_pos(i) - pos);
+		uint16_t pos_delta = abs(led_to_x_pos(i) - pos_whole);
 		if(pos_delta > blur){
 			pos_delta = blur;
 		}
 		float pos_level = 1-(pos_delta/float(blur));
 		
-		pos_level*=pos_level;
+		pos_level *= pos_level; // Squared for sharper falloff
 		
-		if(i == 0){
-			//Serial.println(pos_level);
-		}
 		lix_leds[i] = CRGB(col.r * pos_level, col.g * pos_level, col.b * pos_level);
-
 	}
 	lix_controller->showLeds();
 }
@@ -438,7 +526,7 @@ void Lixie_II::sweep_gradient(CRGB col_left, CRGB col_right, uint16_t speed, uin
 			col_out.g = (col_right.g*(1-progress)) + (col_left.g*(progress));
 			col_out.b = (col_right.b*(1-progress)) + (col_left.b*(progress));
 			
-			streak(col_out, sweep_pos, blur);
+			streak(col_out, 1-progress, blur);
 			FastLED.delay(speed);
 		}
 	}
@@ -458,7 +546,7 @@ void Lixie_II::sweep_gradient(CRGB col_left, CRGB col_right, uint16_t speed, uin
 			col_out.g = (col_right.g*(1-progress)) + (col_left.g*(progress));
 			col_out.b = (col_right.b*(1-progress)) + (col_left.b*(progress));
 			
-			streak(col_out, sweep_pos, blur);
+			streak(col_out, progress, blur);
 			FastLED.delay(speed);
 		}
 	}
@@ -466,19 +554,166 @@ void Lixie_II::sweep_gradient(CRGB col_left, CRGB col_right, uint16_t speed, uin
 }
 
 uint8_t Lixie_II::get_size(uint32_t input){
-  uint8_t places = 1;
-  while(input > 9){
-    places++;
-    input /= 10;
-  }
-  return places;
+	uint8_t places = 1;
+	while(input > 9){
+		places++;
+		input /= 10;
+	}
+	return places;
 }
 
-/*
-void fill_all(CRGB col){
-  for(uint16_t i = 0; i < n_LEDs; i++){
-    led_mask[i] = 255;
-	color_on[i] = col;
-  }
+void Lixie_II::nixie(){
+	color_all(ON, CRGB(255, 70, 7));
+	color_all(OFF, CRGB(0, 3, 8));  
+}
+
+void Lixie_II::white_balance(CRGB c_adj){
+	lix_controller->setTemperature(c_adj);
+}
+
+void Lixie_II::rainbow(uint8_t r_hue, uint8_t r_sep){
+	for(uint8_t i = 0; i < n_digits; i++){
+		color_display(i, ON, CHSV(r_hue,255,255));
+		r_hue+=r_sep;
+	}
+}
+
+void Lixie_II::clear(bool show_change){
+	for(uint16_t i = 0; i < n_LEDs; i++){
+		led_mask_0[i] = 0.0;
+		led_mask_1[i] = 0.0;
+	}
+	if(show_change){
+		mask_fader = 0.0;
+		mask_push  = 1.0;
+		mask_fade_finished = false;
+	}
+}
+
+void Lixie_II::clear_digit(uint8_t index, bool show_change){
+	uint16_t start_index = index*leds_per_digit;	
+	for(uint16_t i = start_index; i < leds_per_digit; i++){
+		led_mask_0[i] = 0.0;
+		led_mask_1[i] = 0.0;
+	}
+}
+
+void Lixie_II::show(){
+	mask_update();
+}
+
+
+
+
+// BEGIN LIXIE 1 DEPRECATED FUNCTIONS
+
+
+
+
+void Lixie_II::write_flip(uint32_t input, uint16_t flip_time, uint8_t flip_speed){
+	// This animation no longer supported, crossfade is used instead
+	transition_type(CROSSFADE);
+	transition_time(flip_time);
+	write(input);
+}
+
+void Lixie_II::write_fade(uint32_t input, uint16_t fade_time){
+	transition_type(CROSSFADE);
+	transition_time(fade_time);
+	write(input);
+}
+
+void Lixie_II::sweep(CRGB col, uint8_t speed){
+	sweep_color(col, speed, 3, false);
+}
+
+void Lixie_II::progress(float percent, CRGB col1, CRGB col2){
+	uint16_t crossover_whole = percent * n_digits;
+	for(uint8_t i = 0; i < n_digits; i++){
+		if(n_digits-i-1 > crossover_whole){
+			color_display(n_digits-i-1, ON, col1);
+			color_display(n_digits-i-1, OFF, col1);
+		}
+		else{
+			color_display(n_digits-i-1, ON, col2);
+			color_display(n_digits-i-1, OFF, col2);
+		}
+	}
+}
+
+void Lixie_II::fill_fade_in(CRGB col, uint8_t fade_speed){
+	for(float fade = 0.0; fade < 1.0; fade += 0.05){
+		for(uint16_t i = 0; i < n_LEDs; i++){
+			lix_leds[i].r = col.r*fade;
+			lix_leds[i].g = col.g*fade;
+			lix_leds[i].b = col.b*fade;
+		}
+		
+		FastLED.show();
+		delay(fade_speed);
+	}
+}
+
+void Lixie_II::fill_fade_out(CRGB col, uint8_t fade_speed){
+	for(float fade = 1; fade > 0; fade -= 0.05){
+		for(uint16_t i = 0; i < n_LEDs; i++){
+			lix_leds[i].r = col.r*fade;
+			lix_leds[i].g = col.g*fade;
+			lix_leds[i].b = col.b*fade;
+		}
+		
+		FastLED.show();
+		delay(fade_speed);
+	}
+}
+
+void Lixie_II::color(uint8_t r, uint8_t g, uint8_t b){
+	color_all(ON,CRGB(r,g,b));
+}
+void Lixie_II::color(CRGB c){
+	color_all(ON,c);
+}
+void Lixie_II::color(uint8_t r, uint8_t g, uint8_t b, uint8_t index){
+	color_display(index, ON, CRGB(r,g,b));
+}
+void Lixie_II::color(CRGB c, uint8_t index){
+	color_display(index, ON, c);
+}
+void Lixie_II::color_off(uint8_t r, uint8_t g, uint8_t b){
+	color_all(OFF,CRGB(r,g,b));
+}
+void Lixie_II::color_off(CRGB c){
+	color_all(OFF,c);
+}
+void Lixie_II::color_off(uint8_t r, uint8_t g, uint8_t b, uint8_t index){
+	color_display(index, OFF, CRGB(r,g,b));
+}
+void Lixie_II::color_off(CRGB c, uint8_t index){
+	color_display(index, OFF, c);
+}
+
+void Lixie_II::color_fade(CRGB col, uint16_t duration){
+	// not supported
+	color_all(ON,col);
+}
+void Lixie_II::color_fade(CRGB col, uint16_t duration, uint8_t index){
+	// not supported
+	color_display(index,ON,col);
+}
+void Lixie_II::color_array_fade(CRGB *cols, uint16_t duration){
+	// support removed
+}
+void Lixie_II::color_array_fade(CHSV *cols, uint16_t duration){
+	// support removed
+}
+void Lixie_II::color_wipe(CRGB col1, CRGB col2){
+	gradient_rgb(ON,col1,col2);
+}
+void Lixie_II::nixie_mode(bool enabled, bool has_aura){
+	// enabled removed
+	// has_aura removed
+	nixie();
 }
-*/
+void Lixie_II::nixie_aura_intensity(uint8_t val){
+	// support removed
+}

+ 51 - 10
src/Lixie_II.h

@@ -9,13 +9,15 @@
 #define lixie_II_h
 
 #include "Arduino.h"
-#include <Ticker.h>	// ESP ONLY
 
-// FastLED has issues with the ESP8266, especially
-// when used with networking, so we fix that here.
-#define FASTLED_ESP8266_RAW_PIN_ORDER
-#define FASTLED_ALLOW_INTERRUPTS 		0
-#define FASTLED_INTERRUPT_RETRY_COUNT	0
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
+	#include <Ticker.h>	// ESP ONLY
+	// FastLED has issues with the ESP8266, especially
+	// when used with networking, so we fix that here.
+	#define FASTLED_ESP8266_RAW_PIN_ORDER
+	#define FASTLED_ALLOW_INTERRUPTS 		0
+	#define FASTLED_INTERRUPT_RETRY_COUNT	0
+#endif
 
 // Aside from those issues, it's my tool of choice for WS2812B
 #include "FastLED.h"
@@ -25,7 +27,6 @@
 
 #define INSTANT   		0
 #define CROSSFADE 		1
-#define FADE_TO_BLACK	2
 
 // Functions
 class Lixie_II
@@ -34,23 +35,63 @@ class Lixie_II
 		Lixie_II(const uint8_t pin, uint8_t n_digits);
 		void build_controller(const uint8_t pin);
 		void begin();
+		void transition_type(uint8_t type);
+		void transition_time(uint16_t ms);
 		void max_power(uint8_t V, uint16_t mA);
 		void color_all(uint8_t layer, CRGB col);
 		void color_all_dual(uint8_t layer, CRGB col_left, CRGB col_right);
 		void color_display(uint8_t display, uint8_t layer, CRGB col);
 		void gradient_rgb(uint8_t layer, CRGB col_left, CRGB col_right);
-		void streak(CRGB col, int16_t pos, uint8_t blur);
-		void sweep_color(CRGB col, uint16_t speed, uint8_t blur, bool reverse = false);
-		void sweep_gradient(CRGB col_left, CRGB col_right, uint16_t speed, uint8_t blur, bool reverse);
 		void start_animation();
 		void stop_animation();
 		void write(uint32_t input);
+		void write(char* input);
 		void write_float(float input, uint8_t dec_places = 1);
 		void clear_all();
 		void write_digit(uint8_t digit, uint8_t num);
 		void push_digit(uint8_t number);
 		void clear_digit(uint8_t digit, uint8_t num);
 		void mask_update();
+		void fade_in();
+		void fade_out();
+		void brightness(uint8_t level);
+		void run();
+		void wait();
+		void streak(CRGB col, float pos, uint8_t blur);
+		void sweep_color(CRGB col, uint16_t speed, uint8_t blur, bool reverse = false);
+		void sweep_gradient(CRGB col_left, CRGB col_right, uint16_t speed, uint8_t blur, bool reverse = false);
+		void nixie();
+		void white_balance(CRGB c_adj);
+		void rainbow(uint8_t r_hue, uint8_t r_sep);
+		
+		// ----------------------------------------------
+		// Deprecated Lixie 1 functions and overloads:
+		// ----------------------------------------------
+		
+		void clear(bool show_change = true);
+		void clear_digit(uint8_t index, bool show_change = true);
+		void show();
+		void write_flip(uint32_t input, uint16_t flip_time = 100, uint8_t flip_speed = 10);
+		void write_fade(uint32_t input, uint16_t fade_time = 250);
+		void sweep(CRGB col, uint8_t speed = 15);
+		void progress(float percent, CRGB col1, CRGB col2);
+		void fill_fade_in(CRGB col, uint8_t fade_speed = 20);
+		void fill_fade_out(CRGB col, uint8_t fade_speed = 20);
+		void color(uint8_t r, uint8_t g, uint8_t b);
+		void color(CRGB c);
+		void color(uint8_t r, uint8_t g, uint8_t b, uint8_t index);
+		void color(CRGB c, uint8_t index);
+		void color_off(uint8_t r, uint8_t g, uint8_t b);
+		void color_off(CRGB c);
+		void color_off(uint8_t r, uint8_t g, uint8_t b, uint8_t index);
+		void color_off(CRGB c, uint8_t index);
+		void color_fade(CRGB col, uint16_t duration);
+		void color_fade(CRGB col, uint16_t duration, uint8_t index);
+		void color_array_fade(CRGB *cols, uint16_t duration);
+		void color_array_fade(CHSV *cols, uint16_t duration);
+		void color_wipe(CRGB col1, CRGB col2);
+		void nixie_mode(bool enabled, bool has_aura = true);
+		void nixie_aura_intensity(uint8_t val);
 		
 	private:
 		uint8_t get_size(uint32_t input);