Add required libraries
diff --git a/lib/FastLED/FastLED.cpp b/lib/FastLED/FastLED.cpp
new file mode 100644
index 0000000..c9e2992
--- /dev/null
+++ b/lib/FastLED/FastLED.cpp
@@ -0,0 +1,79 @@
+#include "FastLED.h"
+
+
+CFastLED LEDS;
+CFastLED & FastSPI_LED = LEDS;
+CFastLED & FastSPI_LED2 = LEDS;
+CFastLED & FastLED = LEDS;
+
+uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28);
+
+CFastLED::CFastLED() {
+	// clear out the array of led controllers
+	m_nControllers = NUM_CONTROLLERS;
+	m_nScale = 255;
+	memset8(m_Controllers, 0, m_nControllers * sizeof(CControllerInfo));
+}
+
+CLEDController *CFastLED::addLeds(CLEDController *pLed,
+									   const struct CRGB *data,
+									   int nLedsOrOffset, int nLedsIfOffset) {
+	int nOffset = (nLedsIfOffset > 0) ? nLedsOrOffset : 0;
+	int nLeds = (nLedsIfOffset > 0) ? nLedsIfOffset : nLedsOrOffset;
+
+	int target = -1;
+
+	// Figure out where to put the new led controller
+	for(int i = 0; i < m_nControllers; i++) {
+		if(m_Controllers[i].pLedController == NULL) {
+			target = i;
+			break;
+		}
+	}
+
+	// if we have a spot, use it!
+	if(target != -1) {
+		m_Controllers[target].pLedController = pLed;
+		m_Controllers[target].pLedData = data;
+		m_Controllers[target].nOffset = nOffset;
+		m_Controllers[target].nLeds = nLeds;
+		pLed->init();
+		return pLed;
+	}
+
+	return NULL;
+}
+
+void CFastLED::show(uint8_t scale) {
+	for(int i  = 0; i < m_nControllers; i++) {
+		if(m_Controllers[i].pLedController != NULL) {
+			m_Controllers[i].pLedController->show(m_Controllers[i].pLedData + m_Controllers[i].nOffset,
+												  m_Controllers[i].nLeds, scale);
+		} else {
+			return;
+		}
+	}
+}
+
+void CFastLED::showColor(const struct CRGB & color, uint8_t scale) {
+	for(int i  = 0; i < m_nControllers; i++) {
+		if(m_Controllers[i].pLedController != NULL) {
+			m_Controllers[i].pLedController->showColor(color, m_Controllers[i].nLeds, scale);
+		} else {
+			return;
+		}
+	}
+}
+
+void CFastLED::clear(boolean writeData) {
+	if(writeData) {
+		showColor(CRGB(0,0,0), 0);
+	}
+	for(int i = 0; i < m_nControllers; i++) {
+		if(m_Controllers[i].pLedData != NULL) {
+			memset8((void*)m_Controllers[i].pLedData, 0, sizeof(struct CRGB) * m_Controllers[i].nLeds);
+		} else {
+			return;
+		}
+	}
+}
diff --git a/lib/FastLED/FastLED.h b/lib/FastLED/FastLED.h
new file mode 100644
index 0000000..43bbf18
--- /dev/null
+++ b/lib/FastLED/FastLED.h
@@ -0,0 +1,147 @@
+#ifndef __INC_FASTSPI_LED2_H
+#define __INC_FASTSPI_LED2_H
+
+#include "controller.h"
+#include "fastpin.h"
+#include "fastspi.h"
+#include "clockless.h"
+#include "clockless_trinket.h"
+#include "lib8tion.h"
+#include "hsv2rgb.h"
+#include "chipsets.h"
+#include "dmx.h"
+
+enum ESPIChipsets {
+	LPD8806,
+	WS2801,
+	SM16716
+};
+
+enum EClocklessChipsets {
+	DMX,
+	TM1809,
+	TM1804,
+	TM1803,
+	WS2811,
+	WS2812,
+	WS2812B,
+	WS2811_400,
+	NEOPIXEL,
+	UCS1903
+};
+
+#define NUM_CONTROLLERS 8
+
+class CFastLED {
+	struct CControllerInfo {
+		CLEDController *pLedController;
+		const struct CRGB *pLedData;
+		int nLeds;
+		int nOffset;
+	};
+
+	CControllerInfo	m_Controllers[NUM_CONTROLLERS];
+	int m_nControllers;
+	uint8_t m_nScale;
+
+public:
+	CFastLED();
+
+	CLEDController *addLeds(CLEDController *pLed, const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0);
+
+	template<ESPIChipsets CHIPSET,  uint8_t DATA_PIN, uint8_t CLOCK_PIN > CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		switch(CHIPSET) {
+			case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+			case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+			case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+		}
+	}
+
+	template<ESPIChipsets CHIPSET,  uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER > CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		switch(CHIPSET) {
+			case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+			case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+			case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+		}
+	}
+
+	template<ESPIChipsets CHIPSET,  uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER, uint8_t SPI_DATA_RATE > CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		switch(CHIPSET) {
+			case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+			case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+			case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+		}
+	}
+
+#ifdef SPI_DATA
+	template<ESPIChipsets CHIPSET> CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB>(data, nLedsOrOffset, nLedsIfOffset);
+	}
+
+	template<ESPIChipsets CHIPSET, EOrder RGB_ORDER> CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER>(data, nLedsOrOffset, nLedsIfOffset);
+	}
+
+	template<ESPIChipsets CHIPSET, EOrder RGB_ORDER, uint8_t SPI_DATA_RATE> CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER, SPI_DATA_RATE>(data, nLedsOrOffset, nLedsIfOffset);
+	}
+
+#endif
+
+	template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN>
+	CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		switch(CHIPSET) {
+#ifdef FASTSPI_USE_DMX_SIMPLE
+			case DMX: { static DMXController<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+#endif
+			case TM1809: { static TM1809Controller800Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case TM1803: { static TM1803Controller400Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case UCS1903: { static UCS1903Controller400Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case WS2812:
+			case WS2812B:
+			case WS2811: { static WS2811Controller800Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case NEOPIXEL: { static WS2811Controller800Khz<DATA_PIN, GRB> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case WS2811_400: { static WS2811Controller400Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+		}
+	}
+
+	template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER>
+	CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+		switch(CHIPSET) {
+#ifdef FASTSPI_USE_DMX_SIMPLE
+			case DMX: {static  DMXController<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+#endif
+			case TM1809: { static TM1809Controller800Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case TM1803: { static TM1803Controller400Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case UCS1903: { static UCS1903Controller400Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case WS2812:
+			case WS2812B:
+			case NEOPIXEL:
+			case WS2811: { static WS2811Controller800Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+			case WS2811_400: { static WS2811Controller400Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+		}
+	}
+
+	void setBrightness(uint8_t scale) { m_nScale = scale; }
+	uint8_t getBrightness() { return m_nScale; }
+
+	/// Update all our controllers with the current led colors, using the passed in brightness
+	void show(uint8_t scale);
+
+	/// Update all our controllers with the current led colors
+	void show() { show(m_nScale); }
+
+	void clear(boolean writeData = false);
+
+	void showColor(const struct CRGB & color, uint8_t scale);
+
+	void showColor(const struct CRGB & color) { showColor(color, m_nScale); }
+
+};
+
+extern CFastLED & FastSPI_LED;
+extern CFastLED & FastSPI_LED2;
+extern CFastLED & FastLED;
+extern CFastLED LEDS;
+
+#endif
diff --git a/lib/FastLED/FastSPI_LED2.h b/lib/FastLED/FastSPI_LED2.h
new file mode 100644
index 0000000..55d9a51
--- /dev/null
+++ b/lib/FastLED/FastSPI_LED2.h
@@ -0,0 +1,2 @@
+#warning "This file is going away, please us FastLED.h in the future!"
+#include<FastLED.h>
diff --git a/lib/FastLED/LICENSE b/lib/FastLED/LICENSE
new file mode 100644
index 0000000..ebe4763
--- /dev/null
+++ b/lib/FastLED/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 FastLED
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/FastLED/README.md b/lib/FastLED/README.md
new file mode 100644
index 0000000..bc31e9a
--- /dev/null
+++ b/lib/FastLED/README.md
@@ -0,0 +1,64 @@
+FastLED
+=======
+
+This is a library for easily & efficiently controlling a wide variety of LED chipsets, like the ones
+sold by adafruit (Neopixel, LPD8806), Sparkfun (WS2801), and aliexpress.  In addition to writing to the
+leds, this library also includes a number of functions for high-performing 8bit math for manipulating
+your RGB values, as well as low level classes for abstracting out access to pins and SPI hardware, while
+still keeping things as fast as possible.
+
+We have multiple goals with this library:
+
+* Quick start for new developers - hook up your leds and go, no need to think about specifics of the led chipsets being used
+* Zero pain switching LED chipsets - you get some new leds that the library supports, just change the definition of LEDs you're using, et. voila!  Your code is running with the new leds.
+* High performance - with features like zero cost global brightness scaling, high performance 8-bit math for RGB manipulation, and some of the fastest bit-bang'd SPI support around, FastLED wants to keep as many CPU cycles available for your led patterns as possible
+
+## Simple example
+
+How quickly can you get up and running with the library?  Here's a simple blink program:
+
+	#include "FastLED.h"
+	#define NUM_LEDS 60
+	CRGB leds[NUM_LEDS];
+	void setup() { FastLED.addLeds<NEOPIXEL, 6>(leds, NUM_LEDS); }
+	void loop() {
+		leds[0] = CRGB::White; FastLED.show(); delay(30);
+		leds[0] = CRGB::Black; FastLED.show(); delay(30);
+	}
+
+## Supported LED chipsets
+
+Here's a list of all the LED chipsets are supported.  More details on the led chipsets are included *TODO: Link to wiki page*
+
+* Adafruit's Neopixel - aka the WS2812B (also WS2811/WS2812, also suppored in lo-speed mode) - a 3 wire addressable led chipset
+* TM1809/4 - 3 wire chipset, cheaply available on aliexpress.com
+* TM1803 - 3 wire chipset, sold by radio shack
+* UCS1903 - another 3 wire led chipset, cheap
+* LPD8806 - SPI based chpiset, very high speed
+* WS2801 - SPI based chipset, cheap and widely available
+* SM16716 - SPI based chipset
+* DMX - send rgb data out over DMX using arduino DMX libraries
+
+LPD6803, HL1606, and "595"-style shift registers are no longer supported by the library.  The older Version 1 of the library ("FastSPI_LED") has support for these, but is missing many of the advanced features of current versions and is no longer being maintained.
+
+
+## Supported platforms
+
+Right now the library is supported on a variety of arduino compatable platforms.  If it's ARM or AVR and uses the arduino software (or a modified version of it to build) then it is likely supported.  Note that we have a long list of upcoming platforms to support, so if you don't see what you're looking for here, ask, it may be on the roadmap (or may already be supported).  N.B. at the moment we are only supporting the stock compilers that ship with the arduino software.  Support for upgraded compilers, as well as using AVR studio and skipping the arduino entirely, should be coming in a near future release.
+
+* Arduino & compatibles - straight up arduino devices, uno, duo, leonardo, mega, nano, etc...
+* Teensy 2, Teensy++ 2, Teensy 3 - arduino compataible from pjrc.com with some extra goodies (note the teensy 3 is ARM, not AVR!)
+
+What types of platforms are we thinking about supporting in the future?  Here's a short list:  Arduino Due, MSP430, ChipKit32, Maple, Beagleboard
+
+## What about that name?
+
+Wait, what happend to FastSPI_LED and FastSPI_LED2?  The library was initially named FastSPI_LED because it was focused on very fast and efficient SPI access.  However, since then, the library has expanded to support a number of LED chipsets that don't use SPI, as well as a number of math and utility functions for LED processing across the board.  We decided that the name FastLED more accurately represents the totality of what the library provides, everything fast, for LEDs.
+
+## For more information
+
+Check out the official site http://fastled.io for links to documentation, issues, and news
+
+
+*TODO* - get candy
+
diff --git a/lib/FastLED/chipsets.h b/lib/FastLED/chipsets.h
new file mode 100644
index 0000000..cef33e9
--- /dev/null
+++ b/lib/FastLED/chipsets.h
@@ -0,0 +1,300 @@
+#ifndef __INC_CHIPSETS_H
+#define __INC_CHIPSETS_H
+
+#include "pixeltypes.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// LPD8806 controller class - takes data/clock/select pin values (N.B. should take an SPI definition?)
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB,  uint8_t SPI_SPEED = DATA_RATE_MHZ(24) >
+class LPD8806Controller : public CLEDController {
+	typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
+
+	class LPD8806_ADJUST {
+	public:
+		// LPD8806 spec wants the high bit of every rgb data byte sent out to be set.
+		__attribute__((always_inline)) inline static uint8_t adjust(register uint8_t data) { return (data>>1) | 0x80; }
+		__attribute__((always_inline)) inline static uint8_t adjust(register uint8_t data, register uint8_t scale) { return (scale8(data, scale)>>1) | 0x80; }
+		__attribute__((always_inline)) inline static void postBlock(int len) {
+			SPI::writeBytesValueRaw(0, ((len+63)>>6));
+		}
+
+	};
+
+	SPI mSPI;
+	int mClearedLeds;
+
+	void checkClear(int nLeds) {
+		if(nLeds > mClearedLeds) {
+			clearLine(nLeds);
+			mClearedLeds = nLeds;
+		}
+	}
+
+	void clearLine(int nLeds) {
+		int n = ((nLeds  + 63) >> 6);
+		mSPI.writeBytesValue(0, n);
+	}
+public:
+	LPD8806Controller()  {}
+	virtual void init() {
+		mSPI.init();
+		mClearedLeds = 0;
+	}
+
+	virtual void clearLeds(int nLeds) {
+		mSPI.select();
+		mSPI.writeBytesValueRaw(0x80, nLeds * 3);
+		mSPI.writeBytesValueRaw(0, ((nLeds*3+63)>>6));
+		mSPI.release();
+	}
+
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+		mSPI.select();
+		uint8_t a = 0x80 | (scale8(data[RGB_BYTE0(RGB_ORDER)], scale) >> 1);
+		uint8_t b = 0x80 | (scale8(data[RGB_BYTE1(RGB_ORDER)], scale) >> 1);
+		uint8_t c = 0x80 | (scale8(data[RGB_BYTE2(RGB_ORDER)], scale) >> 1);
+		int iLeds = 0;
+
+		while(iLeds++ < nLeds) {
+			mSPI.writeByte(a);
+			mSPI.writeByte(b);
+			mSPI.writeByte(c);
+		}
+
+		// latch in the world
+		mSPI.writeBytesValueRaw(0, ((nLeds*3+63)>>6));
+		mSPI.release();
+	}
+
+	virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
+		mSPI.template writeBytes3<LPD8806_ADJUST, RGB_ORDER>((byte*)data, nLeds * 3, scale);
+	}
+
+#ifdef SUPPORT_ARGB
+	virtual void show(const struct CARGB *data, int nLeds, uint8_t scale) {
+		checkClear(nLeds);
+		mSPI.template writeBytes3<1, LPD8806_ADJUST, RGB_ORDER>((byte*)data, nLeds * 4, scale);
+	}
+#endif
+};
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// WS2801 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(1)>
+class WS2801Controller : public CLEDController {
+	typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
+	SPI mSPI;
+	CMinWait<500>  mWaitDelay;
+public:
+	WS2801Controller() {}
+
+	virtual void init() {
+		mSPI.init();
+	    mWaitDelay.mark();
+	}
+
+	virtual void clearLeds(int nLeds) {
+		mWaitDelay.wait();
+		mSPI.writeBytesValue(0, nLeds*3);
+		mWaitDelay.mark();
+	}
+
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+		mWaitDelay.wait();
+		mSPI.select();
+		uint8_t a = scale8(data[RGB_BYTE0(RGB_ORDER)], scale);
+		uint8_t b = scale8(data[RGB_BYTE1(RGB_ORDER)], scale);
+		uint8_t c = scale8(data[RGB_BYTE2(RGB_ORDER)], scale);
+
+		while(nLeds--) {
+			mSPI.writeByte(a);
+			mSPI.writeByte(b);
+			mSPI.writeByte(c);
+		}
+		mSPI.waitFully();
+		mSPI.release();
+		mWaitDelay.mark();
+	}
+
+	virtual void show(const struct CRGB *data, int nLeds, uint8_t scale) {
+		mWaitDelay.wait();
+		mSPI.template writeBytes3<0, RGB_ORDER>((byte*)data, nLeds * 3, scale);
+		mWaitDelay.mark();
+	}
+
+#ifdef SUPPORT_ARGB
+	virtual void show(const struct CRGB *data, int nLeds, uint8_t scale) {
+		mWaitDelay.wait();
+		mSPI.template writeBytes3<1, RGB_ORDER>((byte*)data, nLeds * 4, scale);
+		mWaitDelay.mark();
+	}
+#endif
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// SM16716 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(16)>
+class SM16716Controller : public CLEDController {
+	typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
+	SPI mSPI;
+
+	void writeHeader() {
+		// Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes)
+		mSPI.select();
+		mSPI.writeBytesValueRaw(0, 6);
+		mSPI.waitFully();
+		mSPI.template writeBit<0>(0);
+		mSPI.template writeBit<0>(0);
+		mSPI.release();
+	}
+
+public:
+	SM16716Controller() {}
+
+	virtual void init() {
+		mSPI.init();
+	}
+
+	virtual void clearLeds(int nLeds) {
+		mSPI.select();
+		while(nLeds--) {
+			mSPI.template writeBit<0>(1);
+			mSPI.writeByte(0);
+			mSPI.writeByte(0);
+			mSPI.writeByte(0);
+		}
+		mSPI.waitFully();
+		mSPI.release();
+		writeHeader();
+	}
+
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+		mSPI.select();
+		uint8_t a = scale8(data[RGB_BYTE0(RGB_ORDER)], scale);
+		uint8_t b = scale8(data[RGB_BYTE1(RGB_ORDER)], scale);
+		uint8_t c = scale8(data[RGB_BYTE2(RGB_ORDER)], scale);
+
+		while(nLeds--) {
+			mSPI.template writeBit<0>(1);
+			mSPI.writeByte(a);
+			mSPI.writeByte(b);
+			mSPI.writeByte(c);
+		}
+		writeHeader();
+		mSPI.release();
+	}
+
+	virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
+		// Make sure the FLAG_START_BIT flag is set to ensure that an extra 1 bit is sent at the start
+		// of each triplet of bytes for rgb data
+		// writeHeader();
+		mSPI.template writeBytes3<FLAG_START_BIT, RGB_ORDER>((byte*)data, nLeds * 3, scale);
+		writeHeader();
+	}
+
+#ifdef SUPPORT_ARGB
+	virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) {
+		mSPI.writeBytesValue(0, 6);
+		mSPI.template writeBit<0>(0);
+		mSPI.template writeBit<0>(0);
+
+		// Make sure the FLAG_START_BIT flag is set to ensure that an extra 1 bit is sent at the start
+		// of each triplet of bytes for rgb data
+		mSPI.template writeBytes3<1 | FLAG_START_BIT, RGB_ORDER>((byte*)data, nLeds * 4, scale);
+	}
+#endif
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Clockless template instantiations - see clockless.h for how the timing values are used
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if defined(LIB8_ATTINY) && (F_CPU == 8000000)
+// WS2811@8Mhz 2 clocks, 5 clocks, 3 clocks
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class WS2811Controller800Khz : public ClocklessController_Trinket<DATA_PIN, 2, 5, 3, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class WS2811Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 4, 10, 6, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class UCS1903Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 4, 12, 4, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 2, 5, 3, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1803Controller400Khz : public ClocklessController<DATA_PIN, 6, 6, 6, RGB_ORDER> {};
+
+#elif defined(LIB8_ATTINY) && (F_CPU == 16000000)
+
+// WS2811@16Mhz 4 clocks, 10 clocks, 6 clocks
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class WS2811Controller800Khz : public ClocklessController_Trinket<DATA_PIN, 4, 10, 6, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class WS2811Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 8, 20, 12, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class UCS1903Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 8, 24, 8, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 4, 10, 6, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1803Controller400Khz : public ClocklessController<DATA_PIN, 12, 12, 12, RGB_ORDER> {};
+
+#else
+// UCS1903 - 500ns, 1500ns, 500ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class UCS1903Controller400Khz : public ClocklessController<DATA_PIN, NS(500), NS(1500), NS(500), RGB_ORDER> {};
+#if NO_TIME(500, 1500, 500)
+#warning "Not enough clock cycles available for the UCS103"
+#endif
+
+// TM1809 - 350ns, 350ns, 550ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1809Controller800Khz : public ClocklessController<DATA_PIN, NS(350), NS(350), NS(550), RGB_ORDER> {};
+#if NO_TIME(350, 350, 550)
+#warning "Not enough clock cycles available for the TM1809"
+#endif
+
+// WS2811 - 400ns, 400ns, 450ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+//class WS2811Controller800Khz : public ClocklessController<DATA_PIN, NS(400), NS(400), NS(450), RGB_ORDER> {};
+class WS2811Controller800Khz : public ClocklessController<DATA_PIN, NS(250), NS(400), NS(250), RGB_ORDER> {};
+#if NO_TIME(400, 400, 450)
+#warning "Not enough clock cycles available for the WS2811 (800khz)"
+#endif
+
+// WS2811@400khz - 800ns, 800ns, 900ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class WS2811Controller400Khz : public ClocklessController<DATA_PIN, NS(800), NS(800), NS(900), RGB_ORDER> {};
+#if NO_TIME(800, 800, 900)
+#warning "Not enough clock cycles available for the WS2811 (400Khz)"
+#endif
+
+// 750NS, 750NS, 750NS
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1803Controller400Khz : public ClocklessController<DATA_PIN, NS(750), NS(750), NS(750), RGB_ORDER> {};
+#if NO_TIME(750, 750, 750)
+#warning "Not enough clock cycles available for the UCS103"
+#endif
+#endif
+
+#endif
diff --git a/lib/FastLED/clockless.h b/lib/FastLED/clockless.h
new file mode 100644
index 0000000..09ba6dd
--- /dev/null
+++ b/lib/FastLED/clockless.h
@@ -0,0 +1,283 @@
+#ifndef __INC_CLOCKLESS_H
+#define __INC_CLOCKLESS_H
+
+#include "controller.h"
+#include "lib8tion.h"
+#include "delay.h"
+#include <avr/interrupt.h> // for cli/se definitions
+
+// Scaling macro choice
+#if defined(LIB8_ATTINY)
+#  define INLINE_SCALE(B, SCALE) delaycycles<3>()
+#  warning "No hardware multiply, inline brightness scaling disabled"
+#else
+#   define INLINE_SCALE(B, SCALE) B = scale8_LEAVING_R1_DIRTY(B, SCALE)
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Base template for clockless controllers.  These controllers have 3 control points in their cycle for each bit.  The first point
+// is where the line is raised hi.  The second point is where the line is dropped low for a zero.  The third point is where the
+// line is dropped low for a one.  T1, T2, and T3 correspond to the timings for those three in clock cycles.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+	typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
+	typedef typename FastPin<DATA_PIN>::port_t data_t;
+
+	data_t mPinMask;
+	data_ptr_t mPort;
+	CMinWait<WAIT_TIME> mWait;
+public:
+	virtual void init() {
+		FastPin<DATA_PIN>::setOutput();
+		mPinMask = FastPin<DATA_PIN>::mask();
+		mPort = FastPin<DATA_PIN>::port();
+	}
+
+
+	virtual void clearLeds(int nLeds) {
+		showColor(CRGB(0, 0, 0), nLeds, 0);
+	}
+
+	// set all the leds on the controller to a given color
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+		mWait.wait();
+		cli();
+
+		showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
+
+		// Adjust the timer
+		long microsTaken = CLKS_TO_MICROS((long)nLeds * 24 * (T1 + T2 + T3));
+		MS_COUNTER += (microsTaken / 1000);
+		sei();
+		mWait.mark();
+	}
+
+	virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
+		mWait.wait();
+		cli();
+
+		showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata);
+
+		// Adjust the timer
+		long microsTaken = CLKS_TO_MICROS((long)nLeds * 24 * (T1 + T2 + T3));
+		MS_COUNTER += (microsTaken / 1000);
+		sei();
+		mWait.mark();
+	}
+
+#ifdef SUPPORT_ARGB
+	virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
+		mWait.wait();
+		cli();
+
+		showRGBInternal<1, true>(nLeds, scale, (const byte*)rgbdata);
+
+		// Adjust the timer
+		long microsTaken = CLKS_TO_MICROS((long)nLeds * 24 * (T1 + T2 + T3));
+		MS_COUNTER += (microsTaken / 1000);
+		sei();
+		mWait.mark();
+	}
+#endif
+
+#if defined(__MK20DX128__)
+	inline static void write8Bits(register data_ptr_t port, register data_t hi, register data_t lo, register uint32_t & b)  __attribute__ ((always_inline)) {
+		// TODO: hand rig asm version of this method.  The timings are based on adjusting/studying GCC compiler ouptut.  This
+		// will bite me in the ass at some point, I know it.
+		for(register uint32_t i = 7; i > 0; i--) {
+			FastPin<DATA_PIN>::fastset(port, hi);
+			delaycycles<T1 - 5>(); // 5 cycles - 2 store, 1 and, 1 test, 1 if
+			if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
+			b <<= 1;
+			delaycycles<T2 - 2>(); // 2 cycles,  1 store/skip,  1 shift
+			FastPin<DATA_PIN>::fastset(port, lo);
+			delaycycles<T3 - 5>(); // 3 cycles, 2 store, 1 sub, 1 branch backwards
+		}
+		// delay an extra cycle because falling out of the loop takes on less cycle than looping around
+		delaycycles<1>();
+
+		FastPin<DATA_PIN>::fastset(port, hi);
+		delaycycles<T1 - 6>();
+		if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
+		delaycycles<T2 - 2>(); // 4 cycles, 2 store, store/skip
+		FastPin<DATA_PIN>::fastset(port, lo);
+	}
+
+	// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+	// gcc will use register Y for the this pointer.
+	template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
+		register byte *data = (byte*)rgbdata;
+		register data_t mask = FastPin<DATA_PIN>::mask();
+		register data_ptr_t port = FastPin<DATA_PIN>::port();
+		nLeds *= (3 + SKIP);
+		register uint8_t *end = data + nLeds;
+		register data_t hi = *port | mask;
+		register data_t lo = *port & ~mask;
+		*port = lo;
+
+		register uint32_t b;
+		b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
+		b = scale8(b, scale);
+		while(data < end) {
+			// Write first byte, read next byte
+			write8Bits(port, hi, lo, b);
+
+			b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE1(RGB_ORDER)];
+			INLINE_SCALE(b, scale);
+			delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
+
+			// Write second byte
+			write8Bits(port, hi, lo, b);
+
+			b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE2(RGB_ORDER)];
+			INLINE_SCALE(b, scale);
+
+			data += 3 + SKIP;
+			if((RGB_ORDER & 0070) == 0) {
+				delaycycles<T3 - 6>(); // 1 store, 2 load, 1 mul, 1 shift,  1 adds if BRG or GRB
+			} else {
+				delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
+			}
+
+			// Write third byte
+			write8Bits(port, hi, lo, b);
+
+			b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
+			INLINE_SCALE(b, scale);
+
+			delaycycles<T3 - 11>(); // 1 store, 2 load (with increment), 1 mul, 1 shift, 1 cmp, 1 branch backwards, 1 movim
+		};
+	}
+
+#else // AVR loop/implementation
+
+	template <int N, int ADJ>inline static void bitSetLast(register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t b) {
+		// First cycle
+		FastPin<DATA_PIN>::fastset(port, hi); 							// 1 clock cycle if using out, 2 otherwise
+		delaycycles<T1 - (_CYCLES(DATA_PIN))>();					// 1st cycle length minus 1 clock for out, 1 clock for sbrs
+		__asm__ __volatile__ ("sbrs %0, %1" :: "r" (b), "M" (N) :); // 1 clock for check (+1 if skipping, next op is also 1 clock)
+
+		// Second cycle
+		FastPin<DATA_PIN>::fastset(port, lo);							// 1/2 clock cycle if using out
+		delaycycles<T2 - (_CYCLES(DATA_PIN))>(); 						// 2nd cycle length minus 1/2 clock for out
+
+		// Third cycle
+		FastPin<DATA_PIN>::fastset(port, lo);							// 1/2 clock cycle if using out
+		delaycycles<T3 - (_CYCLES(DATA_PIN) + ADJ)>();				// 3rd cycle length minus the passed in adjustment
+	}
+
+	// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+	// gcc will use register Y for the this pointer.
+	template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
+		register byte *data = (byte*)rgbdata;
+		register data_t mask = FastPin<DATA_PIN>::mask();
+		register data_ptr_t port = FastPin<DATA_PIN>::port();
+		nLeds *= (3 + SKIP);
+		register uint8_t *end = data + nLeds;
+		register data_t hi = *port | mask;
+		register data_t lo = *port & ~mask;
+		*port = lo;
+
+#if 0
+		register uint8_t b = *data++;
+		while(data <= end) {
+			bitSetFast<7>(port, hi, lo, b);
+			bitSetFast<6>(port, hi, lo, b);
+			bitSetFast<5>(port, hi, lo, b);
+			bitSetFast<4>(port, hi, lo, b);
+			bitSetFast<3>(port, hi, lo, b);
+			// Leave an extra 2 clocks for the next byte load
+			bitSetLast<2, 2>(port, hi, lo, b);
+			register uint8_t next = *data++;
+			// Leave an extra 4 clocks for the scale
+			bitSetLast<1, 4>(port, hi, lo, b);
+			next = scale8(next, scale);
+			bitSetLast<0, END_OF_LOOP>(port, hi, lo, b);
+			b = next;
+		}
+#else
+		register uint8_t b;
+
+		if(ADVANCE) {
+			b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
+		} else {
+			b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
+		}
+		b = scale8_LEAVING_R1_DIRTY(b, scale);
+
+		register uint8_t c;
+		register uint8_t d;
+		while(data < end) {
+			for(register byte x=5; x; x--) {
+				bitSetLast<7, 4>(port, hi, lo, b);
+				b <<= 1;
+			}
+			delaycycles<1>();
+			bitSetLast<7, 1>(port, hi, lo, b);
+			delaycycles<1>();
+
+			// Leave an extra 4 clocks for the load/scale
+			bitSetLast<6, 6>(port, hi, lo, b);
+			if(ADVANCE) {
+				c = data[SKIP + RGB_BYTE1(RGB_ORDER)];
+			} else {
+				c = rgbdata[SKIP + RGB_BYTE1(RGB_ORDER)];
+				delaycycles<1>();
+			}
+			INLINE_SCALE(c, scale);
+			bitSetLast<5, 1>(port, hi, lo, b);
+
+			for(register byte x=5; x; x--) {
+				bitSetLast<7, 4>(port, hi, lo, c);
+				c <<= 1;
+			}
+			delaycycles<1>();
+			bitSetLast<7, 1>(port, hi, lo, c);
+			delaycycles<1>();
+
+			// Leave an extra 4 clocks for the load/scale
+			bitSetLast<6, 6>(port, hi, lo, c);
+			if(ADVANCE) {
+				d = data[SKIP + RGB_BYTE2(RGB_ORDER)];
+			} else {
+				d = rgbdata[SKIP + RGB_BYTE2(RGB_ORDER)];
+				delaycycles<1>();
+			}
+			INLINE_SCALE(d, scale);
+			bitSetLast<5, 1>(port, hi, lo, c);
+
+			for(register byte x=5; x; x--) {
+				bitSetLast<7, 4>(port, hi, lo, d);
+				d <<= 1;
+			}
+			delaycycles<1>();
+			// Leave an extra clock for the data pointer advance
+			bitSetLast<7, 2>(port, hi, lo, d);
+			data += (SKIP + 3);
+			// Leave an extra 4 clocks for the load/scale
+			bitSetLast<6, 6>(port, hi, lo, d);
+			if(ADVANCE) {
+				b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
+			} else {
+				b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
+				delaycycles<1>();
+			}
+			INLINE_SCALE(b, scale);
+			bitSetLast<5, 6>(port, hi, lo, d);
+		}
+		cleanup_R1();
+#endif
+	}
+#endif
+#ifdef SUPPORT_ARGB
+	virtual void showARGB(struct CARGB *data, int nLeds) {
+		// TODO: IMPLEMENTME
+	}
+#endif
+};
+
+#endif
diff --git a/lib/FastLED/clockless_trinket.h b/lib/FastLED/clockless_trinket.h
new file mode 100644
index 0000000..168ba67
--- /dev/null
+++ b/lib/FastLED/clockless_trinket.h
@@ -0,0 +1,279 @@
+#ifndef __INC_CLOCKLESS_TRINKET_H
+#define __INC_CLOCKLESS_TRINKET_H
+
+#include "controller.h"
+#include "lib8tion.h"
+#include "delay.h"
+#include <avr/interrupt.h> // for cli/se definitions
+
+
+// Scaling macro choice
+#ifndef TRINKET_SCALE
+#define TRINKET_SCALE 1
+#endif
+
+// Variations on the functions in delay.h - w/a loop var passed in to preserve registers across calls by the optimizer/compiler
+template<int CYCLES> inline void _dc(register uint8_t & loopvar);
+
+template<int _LOOP, int PAD> inline void _dc_AVR(register uint8_t & loopvar) {
+	_dc<PAD>(loopvar);
+	asm __volatile__ ( "LDI %[loopvar], %[_LOOP]\n\tL_%=: DEC %[loopvar]\n\t BRNE L_%=\n\t" :
+							[loopvar] "+a" (loopvar) : [_LOOP] "M" (_LOOP) : );
+}
+
+template<int CYCLES> __attribute__((always_inline)) inline void _dc(register uint8_t & loopvar) {
+	_dc_AVR<CYCLES/3,CYCLES%3>(loopvar);
+}
+template<> __attribute__((always_inline)) inline void _dc<0>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc<1>(register uint8_t & loopvar) {asm __volatile__("cp r0,r0":::);}
+template<> __attribute__((always_inline)) inline void _dc<2>(register uint8_t & loopvar) {asm __volatile__("rjmp .+0":::);}
+
+#define D1(ADJ) _dc<T1-(2+ADJ)>(loopvar);
+#define D2(ADJ) _dc<T2-(1+ADJ)>(loopvar);
+#define D3(ADJ) _dc<T3-(1+ADJ)>(loopvar);
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Base template for clockless controllers.  These controllers have 3 control points in their cycle for each bit.  The first point
+// is where the line is raised hi.  The second point is where the line is dropped low for a zero.  The third point is where the
+// line is dropped low for a one.  T1, T2, and T3 correspond to the timings for those three in clock cycles.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int WAIT_TIME = 50>
+class ClocklessController_Trinket : public CLEDController {
+	typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
+	typedef typename FastPin<DATA_PIN>::port_t data_t;
+
+	CMinWait<WAIT_TIME> mWait;
+public:
+	virtual void init() {
+		FastPin<DATA_PIN>::setOutput();
+	}
+
+	virtual void clearLeds(int nLeds) {
+		static byte zeros[3] = {0,0,0};
+		showRGBInternal_AdjTime(0, false, nLeds, 0, zeros);
+	}
+
+	// set all the leds on the controller to a given color
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+		showRGBInternal_AdjTime(0, false, nLeds, scale, (const byte*)&data);
+	}
+
+	virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
+		showRGBInternal_AdjTime(0, true, nLeds, scale, (const byte*)rgbdata);
+	}
+
+#ifdef SUPPORT_ARGB
+	virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
+		showRGBInternal_AdjTime(1, true, nLeds, scale, (const byte*)rgbdata);
+	}
+#endif
+
+	void __attribute__ ((noinline)) showRGBInternal_AdjTime(int skip, bool advance, int nLeds, uint8_t scale,  const byte *rgbdata) {
+		mWait.wait();
+		cli();
+
+		showRGBInternal(skip, advance, nLeds, scale, rgbdata);
+
+
+		// Adjust the timer
+		long microsTaken = CLKS_TO_MICROS((long)nLeds * 24 * (T1 + T2 + T3));
+		MS_COUNTER += (microsTaken / 1000);
+		sei();
+		mWait.mark();
+	}
+
+#define USE_ASM_MACROS
+
+#define ASM_VARS : /* write variables */				\
+				[b0] "+r" (b0),							\
+				[b1] "+r" (b1),							\
+				[b2] "+r" (b2),							\
+				[count] "+x" (count),					\
+				[scale_base] "+r" (scale_base),			\
+				[data] "+z" (data),						\
+				[loopvar] "+a" (loopvar)				\
+				: /* use variables */					\
+				[hi] "r" (hi),							\
+				[lo] "r" (lo),							\
+				[scale] "r" (scale),					\
+				[ADV] "r" (advanceBy),					\
+				[zero] "r" (zero),						\
+				[O0] "M" (RGB_BYTE0(RGB_ORDER)),		\
+				[O1] "M" (RGB_BYTE1(RGB_ORDER)),		\
+				[O2] "M" (RGB_BYTE2(RGB_ORDER)),		\
+				[PORT] "M" (FastPin<DATA_PIN>::port()-0x20)						\
+				: /* clobber registers */
+
+
+// 1 cycle, write hi to the port
+#define HI1 asm __volatile__("out %[PORT], %[hi]" ASM_VARS );
+// 1 cycle, write lo to the port
+#define LO1 asm __volatile__("out %[PORT], %[lo]" ASM_VARS );
+// 2 cycles, sbrs on flipping the lne to lo if we're pushing out a 0
+#define QLO2(B, N) asm __volatile__("sbrs %[" #B "], " #N ASM_VARS ); LO1;
+// load a byte from ram into the given var with the given offset
+#define LD2(B,O) asm __volatile__("ldd %[" #B "], Z + %[" #O "]" ASM_VARS );
+// 3 cycles - load a byte from ram into the scaling scratch space with the given offset, clear the target var
+#define LDSCL3(B,O) asm __volatile__("ldd %[scale_base], Z + %[" #O "]\n\tclr %[" #B "]" ASM_VARS );
+// 2 cycles - increment the data pointer
+#define IDATA2 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]"  ASM_VARS );
+// 2 cycles - decrement the counter
+#define DCOUNT2 asm __volatile__("sbiw %[count], 1" ASM_VARS );
+// 2 cycles - jump to the beginning of the loop
+#define JMPLOOP2 asm __volatile__("rjmp 1b" ASM_VARS );
+// 2 cycles - jump out of the loop
+#define BRLOOP1 asm __volatile__("breq 2f" ASM_VARS );
+// 2 cycles - perform one step of the scaling (if a given bit is set in scale, add scale-base to the scratch space)
+#define SCALE2(B, N) asm __volatile__("sbrc %[scale], " #N "\n\tadd %[" #B "], %[scale_base]" ASM_VARS );
+// 1 cycle - rotate right, pulling in from carry
+#define ROR1(B) asm __volatile__("ror %[" #B "]" ASM_VARS );
+// 1 cycle, clear the carry bit
+#define CLC1 asm __volatile__("clc" ASM_VARS );
+// 1 cycle, move one register to another
+#define MOV1(B1, B2) asm __volatile__("mov %[" #B1 "], %[" #B2 "]" ASM_VARS );
+// 4 cycles, rotate, clear carry, scale next bit
+#define RORSC4(B, N) ROR1(B) CLC1 SCALE2(B, N)
+// 4 cycles, scale bit, rotate, clear carry
+#define SCROR4(B, N) SCALE2(B,N) ROR1(B) CLC1
+// define the beginning of the loop
+#define LOOP asm __volatile__("1:" ASM_VARS );
+#define DONE asm __volatile__("2:" ASM_VARS );
+// delay time
+
+
+	// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+	// gcc will use register Y for the this pointer.
+	static void __attribute__ ((noinline)) showRGBInternal(int skip, bool advance, int nLeds, uint8_t scale,  const byte *rgbdata) {
+		byte *data = (byte*)rgbdata;
+		data_t mask = FastPin<DATA_PIN>::mask();
+		data_ptr_t port = FastPin<DATA_PIN>::port();
+		// register uint8_t *end = data + nLeds;
+		data_t hi = *port | mask;
+		data_t lo = *port & ~mask;
+		*port = lo;
+
+		uint8_t b0, b1, b2;
+		uint16_t count = nLeds;
+		uint8_t scale_base = 0;
+		uint16_t advanceBy = advance ? (skip+3) : 0;
+		const uint8_t zero = 0;
+		b0 = data[RGB_BYTE0(RGB_ORDER)];
+		b0 = scale8(b0, scale);
+		b1 = data[RGB_BYTE1(RGB_ORDER)];
+		b2 = 0;
+		register uint8_t loopvar;
+
+		if(RGB_ORDER == RGB) {
+			// If the rgb order is RGB, we can cut back on program space usage by making a much more compact
+			// representation.
+
+			// multiply count by 3, don't use * because there's no hardware multiply
+			count = count+(count<<1);
+			advanceBy = advance ? 1 : 0;
+			{
+				/* asm */
+				LOOP
+				// Sum of the clock counts across each row should be 10 for 8Mhz, WS2811
+#if TRINKET_SCALE
+				// Inline scaling, RGB ordering matches byte ordering
+				HI1	D1(0) QLO2(b0, 7) LDSCL3(b1,O1) D2(3) 	LO1 SCALE2(b1,0) 	D3(2)
+				HI1 D1(0) QLO2(b0, 6) RORSC4(b1,1) 	D2(4)	LO1 ROR1(b1) CLC1 	D3(2)
+				HI1 D1(0) QLO2(b0, 5) SCROR4(b1,2)	D2(4)	LO1 SCALE2(b1,3)	D3(2)
+				HI1 D1(0) QLO2(b0, 4) RORSC4(b1,4) 	D2(4)	LO1 ROR1(b1) CLC1	D3(2)
+				HI1 D1(0) QLO2(b0, 3) SCROR4(b1,5) 	D2(4)	LO1 SCALE2(b1,6)	D3(2)
+				HI1 D1(0) QLO2(b0, 2) RORSC4(b1,7) 	D2(4)	LO1 ROR1(b1) CLC1	D3(2)
+				HI1 D1(0) QLO2(b0, 1) IDATA2 		D2(2)	LO1 				D3(0)
+				// In the last bit's first block, we decrement and branch to done if we decremented to 0
+				HI1 D1(0) QLO2(b0, 0) DCOUNT2 BRLOOP1 MOV1(b0, b1) D2(4) 	LO1 D3(2) JMPLOOP2
+#else
+				// no inline scaling, RGB ordering matches byte ordering
+				HI1	D1(0) QLO2(b0, 7) LD2(b1,O1)	D2(2)	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 6) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 5) 				D2(0)	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 4) 				D2(0)	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 3) 				D2(0)	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 2) 				D2(0)	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 1) IDATA2 		D2(2)	LO1 D3(0)
+				// In the last bit's first block, we decrement and branch to done if we decremented to 0
+				HI1 D1(0) QLO2(b2, 0) DCOUNT2 BRLOOP1 MOV1(b0,b1) D2(4) LO1 D3(2) JMPLOOP2
+#endif
+				DONE
+				D2(4) LO1 D3(0)
+			}
+		}
+		else
+		{
+			{
+				/* asm */
+				LOOP
+				// Sum of the clock counts across each row should be 10 for 8Mhz, WS2811
+#if TRINKET_SCALE
+				// Inline scaling - RGB ordering
+				HI1 D1(0) QLO2(b0, 7) LDSCL3(b1,O1) 	D2(3)	LO1 SCALE2(b1,0)	D3(2)
+				HI1 D1(0) QLO2(b0, 6) RORSC4(b1,1) 		D2(4)	LO1 ROR1(b1) CLC1	D3(2)
+				HI1 D1(0) QLO2(b0, 5) SCROR4(b1,2)		D2(4)	LO1 SCALE2(b1,3)	D3(2)
+				HI1 D1(0) QLO2(b0, 4) RORSC4(b1,4) 		D2(4)	LO1 ROR1(b1) CLC1	D3(2)
+				HI1 D1(0) QLO2(b0, 3) SCROR4(b1,5) 		D2(4)	LO1 SCALE2(b1,6)	D3(2)
+				HI1 D1(0) QLO2(b0, 2) RORSC4(b1,7) 		D2(4)	LO1 ROR1(b1) CLC1	D3(2)
+				HI1 D1(0) QLO2(b0, 1) 				 	D2(0)	LO1 				D3(0)
+				HI1 D1(0) QLO2(b0, 0) 	 				D2(0)	LO1 				D3(0)
+				HI1	D1(0) QLO2(b1, 7) LDSCL3(b2,O2) 	D2(3)	LO1 SCALE2(b2,0)	D3(2)
+				HI1 D1(0) QLO2(b1, 6) RORSC4(b2,1) 		D2(4)	LO1 ROR1(b2) CLC1	D3(2)
+				HI1 D1(0) QLO2(b1, 5) SCROR4(b2,2)		D2(4)	LO1 SCALE2(b2,3)	D3(2)
+				HI1 D1(0) QLO2(b1, 4) RORSC4(b2,4) 		D2(4)	LO1 ROR1(b2) CLC1	D3(2)
+				HI1 D1(0) QLO2(b1, 3) SCROR4(b2,5) 		D2(4)	LO1 SCALE2(b2,6)	D3(2)
+				HI1 D1(0) QLO2(b1, 2) RORSC4(b2,7) 		D2(4)	LO1 ROR1(b2) CLC1	D3(2)
+				HI1 D1(0) QLO2(b1, 1) 	 				D2(0)	LO1 				D3(0)
+				HI1 D1(0) QLO2(b1, 0) IDATA2 			D2(2) 	LO1 				D3(0)
+				HI1	D1(0) QLO2(b2, 7) LDSCL3(b0,O0) 	D2(3)	LO1 SCALE2(b0,0)	D3(2)
+				HI1 D1(0) QLO2(b2, 6) RORSC4(b0,1) 		D2(4)	LO1 ROR1(b0) CLC1	D3(2)
+				HI1 D1(0) QLO2(b2, 5) SCROR4(b0,2)		D2(4)	LO1 SCALE2(b0,3)	D3(2)
+				HI1 D1(0) QLO2(b2, 4) RORSC4(b0,4) 		D2(4)	LO1 ROR1(b0) CLC1	D3(2)
+				HI1 D1(0) QLO2(b2, 3) SCROR4(b0,5) 		D2(4)	LO1 SCALE2(b0,6)	D3(2)
+				HI1 D1(0) QLO2(b2, 2) RORSC4(b0,7) 		D2(4)	LO1 ROR1(b0) CLC1	D3(2)
+				HI1 D1(0) QLO2(b2, 1) 					D2(0)	LO1 				D3(0)
+				HI1 D1(0) QLO2(b2, 0) DCOUNT2 BRLOOP1 	D2(3) 	LO1 D3(2) JMPLOOP2
+#else
+				// no inline scaling - non-straight RGB ordering
+				HI1	D1(0) QLO2(b0, 7) LD2(b1,O1)	D2(2)	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 6) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 5) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 4) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 3) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 2) 				D2(0)	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 1) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b0, 0) 				D2(0) 	LO1 D3(0)
+				HI1	D1(0) QLO2(b1, 7) LD2(b2,O2) 	D2(2)	LO1 D3(0)
+				HI1 D1(0) QLO2(b1, 6) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b1, 5) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b1, 4) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b1, 3) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b1, 2) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b1, 1) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b1, 0) IDATA2 		D2(2)	LO1 D3(0)
+				HI1	D1(0) QLO2(b2, 7) LD2(b0,O0) 	D2(2)	LO1 D3(0)
+				HI1 D1(0) QLO2(b2, 6) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b2, 5) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b2, 4) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b2, 3) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b2, 2) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b2, 1) 				D2(0) 	LO1 D3(0)
+				HI1 D1(0) QLO2(b2, 0) DCOUNT2 BRLOOP1 D2(3) 		LO1 D3(2) JMPLOOP2
+#endif
+				DONE
+				D2(4) LO1 D3(0)
+			}
+		}
+	}
+
+#ifdef SUPPORT_ARGB
+	virtual void showARGB(struct CARGB *data, int nLeds) {
+		// TODO: IMPLEMENTME
+	}
+#endif
+};
+
+#endif
diff --git a/lib/FastLED/controller.h b/lib/FastLED/controller.h
new file mode 100644
index 0000000..9df96ef
--- /dev/null
+++ b/lib/FastLED/controller.h
@@ -0,0 +1,57 @@
+#ifndef __INC_CONTROLLER_H
+#define __INC_CONTROLLER_H
+
+#include <avr/io.h>
+#include "pixeltypes.h"
+
+
+#define RGB_BYTE0(X) ((X>>6) & 0x3)
+#define RGB_BYTE1(X) ((X>>3) & 0x3)
+#define RGB_BYTE2(X) ((X) & 0x3)
+
+// operator byte *(struct CRGB[] arr) { return (byte*)arr; }
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// LED Controller interface definition
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Base definition for an LED controller.  Pretty much the methods that every LED controller object will make available.
+/// Note that the showARGB method is not impelemented for all controllers yet.   Note also the methods for eventual checking
+/// of background writing of data (I'm looking at you, teensy 3.0 DMA controller!).  If you want to pass LED controllers around
+/// to methods, make them references to this type, keeps your code saner.  However, most people won't be seeing/using these objects
+/// directly at all
+class CLEDController {
+public:
+	// initialize the LED controller
+	virtual void init() = 0;
+
+	// reset any internal state to a clean point
+	virtual void reset() { init(); }
+
+	// clear out/zero out the given number of leds.
+	virtual void clearLeds(int nLeds) = 0;
+
+	// set all the leds on the controller to a given color
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) = 0;
+
+	// note that the uint8_ts will be in the order that you want them sent out to the device.
+	// nLeds is the number of RGB leds being written to
+	virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) = 0;
+
+#ifdef SUPPORT_ARGB
+	// as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
+	virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) = 0;
+#endif
+
+	// is the controller ready to write data out
+	virtual bool ready() { return true; }
+
+	// wait until the controller is ready to write data out
+	virtual void wait() { return; }
+
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/FastLED/delay.h b/lib/FastLED/delay.h
new file mode 100644
index 0000000..2b1deae
--- /dev/null
+++ b/lib/FastLED/delay.h
@@ -0,0 +1,92 @@
+#ifndef __INC_DELAY_H
+#define __INC_DELAY_H
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Clock cycle counted delay loop
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__arm__)
+# define NOP __asm__ __volatile__ ("nop\n");
+# define NOP2 __asm__ __volatile__ ("nop\n\tnop");
+#else
+#  define NOP __asm__ __volatile__ ("cp r0,r0\n");
+#  define NOP2 __asm__ __volatile__ ("rjmp .+0");
+#endif
+
+// predeclaration to not upset the compiler
+template<int CYCLES> inline void delaycycles();
+
+// TODO: ARM version of _delaycycles_
+// worker template - this will nop for LOOP * 3 + PAD cycles total
+template<int LOOP, int PAD> inline void _delaycycles_AVR() {
+	delaycycles<PAD>();
+	// the loop below is 3 cycles * LOOP.  the LDI is one cycle,
+	// the DEC is 1 cycle, the BRNE is 2 cycles if looping back and
+	// 1 if not (the LDI balances out the BRNE being 1 cycle on exit)
+	__asm__ __volatile__ (
+		"		LDI R16, %0\n"
+		"L_%=:  DEC R16\n"
+		"		BRNE L_%=\n"
+		: /* no outputs */
+		: "M" (LOOP)
+		: "r16"
+		);
+}
+
+// usable definition
+#if !defined(__MK20DX128__)
+template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
+	_delaycycles_AVR<CYCLES / 3, CYCLES % 3>();
+}
+#else
+template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
+	NOP; delaycycles<CYCLES-1>();
+}
+#endif
+
+// pre-instantiations for values small enough to not need the loop, as well as sanity holders
+// for some negative values.
+template<> __attribute__((always_inline)) inline void delaycycles<-6>() {}
+template<> __attribute__((always_inline)) inline void delaycycles<-5>() {}
+template<> __attribute__((always_inline)) inline void delaycycles<-4>() {}
+template<> __attribute__((always_inline)) inline void delaycycles<-3>() {}
+template<> __attribute__((always_inline)) inline void delaycycles<-2>() {}
+template<> __attribute__((always_inline)) inline void delaycycles<-1>() {}
+template<> __attribute__((always_inline)) inline void delaycycles<0>() {}
+template<> __attribute__((always_inline)) inline void delaycycles<1>() {NOP;}
+template<> __attribute__((always_inline)) inline void delaycycles<2>() {NOP2;}
+template<> __attribute__((always_inline)) inline void delaycycles<3>() {NOP;NOP2;}
+template<> __attribute__((always_inline)) inline void delaycycles<4>() {NOP2;NOP2;}
+template<> __attribute__((always_inline)) inline void delaycycles<5>() {NOP2;NOP2;NOP;}
+
+// Some timing related macros/definitions
+
+// Macro to convert from nano-seconds to clocks and clocks to nano-seconds
+// #define NS(_NS) (_NS / (1000 / (F_CPU / 1000000L)))
+#if F_CPU < 96000000
+#define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000
+#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 1000000L)
+#else
+#define NS(_NS) ( (_NS * (F_CPU / 2000000L))) / 1000
+#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 2000000L)
+#endif
+
+//  Macro for making sure there's enough time available
+#define NO_TIME(A, B, C) (NS(A) < 3 || NS(B) < 3 || NS(C) < 6)
+
+#if defined(__MK20DX128__)
+   extern volatile uint32_t systick_millis_count;
+#  define MS_COUNTER systick_millis_count
+#else
+#  if defined(CORE_TEENSY)
+     extern volatile unsigned long timer0_millis_count;
+#    define MS_COUNTER timer0_millis_count
+#  else
+     extern volatile unsigned long timer0_millis;
+#    define MS_COUNTER timer0_millis
+#  endif
+#endif
+
+#endif
\ No newline at end of file
diff --git a/lib/FastLED/dmx.h b/lib/FastLED/dmx.h
new file mode 100644
index 0000000..03329d0
--- /dev/null
+++ b/lib/FastLED/dmx.h
@@ -0,0 +1,115 @@
+#ifndef __INC_DMX_H
+#define __INC_DMX_H
+
+//#ifdef DmxSimple_H
+//#if USE_DMX_SIMPLE
+#ifdef FASTSPI_USE_DMX_SIMPLE
+#include<DmxSimple.h>
+// note - dmx simple must be included before FastSPI for this code to be enabled
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXController : public CLEDController {
+public:
+	// initialize the LED controller
+	virtual void init() { DmxSimple.usePin(DATA_PIN); }
+
+	// reset any internal state to a clean point
+	virtual void reset() { init(); }
+
+	// clear out/zero out the given number of leds.
+	virtual void clearLeds(int nLeds) {
+		int count = min(nLeds * 3, DMX_SIZE);
+		for(int iChannel = 1; iChannel <= count; iChannel++) { DmxSimple.write(iChannel, 0); }
+	}
+
+	// set all the leds on the controller to a given color
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+		int count = min(nLeds, DMX_SIZE / 3);
+		int iChannel = 1;
+		for(int i = 0; i < count; i++) {
+			DmxSimple.write(iChannel++, scale8(data[RGB_BYTE0(RGB_ORDER)], scale));
+			DmxSimple.write(iChannel++, scale8(data[RGB_BYTE1(RGB_ORDER)], scale));
+			DmxSimple.write(iChannel++, scale8(data[RGB_BYTE2(RGB_ORDER)], scale));
+		}
+	}
+
+	// note that the uint8_ts will be in the order that you want them sent out to the device.
+	// nLeds is the number of RGB leds being written to
+	virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
+		int count = min(nLeds, DMX_SIZE / 3);
+		int iChannel = 1;
+		for(int i = 0; i < count; i++) {
+			DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE0(RGB_ORDER)], scale));
+			DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE1(RGB_ORDER)], scale));
+			DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE2(RGB_ORDER)], scale));
+		}
+
+	}
+
+#ifdef SUPPORT_ARGB
+	// as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
+	virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) = 0;
+#endif
+
+	// is the controller ready to write data out
+	virtual bool ready() { return true; }
+
+	// wait until the controller is ready to write data out
+	virtual void wait() { return; }
+
+};
+
+#elif defined(FASTSPI_USE_DMX_SERIAL)
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXController : public CLEDController {
+public:
+	// initialize the LED controller
+	virtual void init() { DMXSerial.init(DMXController); }
+
+	// reset any internal state to a clean point
+	virtual void reset() { init(); }
+
+	// clear out/zero out the given number of leds.
+	virtual void clearLeds(int nLeds) {
+		int count = min(nLeds * 3, DMX_SIZE);
+		for(int iChannel = 0; iChannel < count; iChannel++) { DmxSimple.write(iChannel, 0); }
+	}
+
+	// set all the leds on the controller to a given color
+	virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+		int count = min(nLeds, DMX_SIZE / 3);
+		int iChannel = 0;
+		for(int i = 0; i < count; i++) {
+			DMXSerial.write(iChannel++, scale8(data[RGB_BYTE0(RGB_ORDER)], scale));
+			DMXSerial.write(iChannel++, scale8(data[RGB_BYTE1(RGB_ORDER)], scale));
+			DMXSerial.write(iChannel++, scale8(data[RGB_BYTE2(RGB_ORDER)], scale));
+		}
+	}
+
+	// note that the uint8_ts will be in the order that you want them sent out to the device.
+	// nLeds is the number of RGB leds being written to
+	virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
+		int count = min(nLeds, DMX_SIZE / 3);
+		int iChannel = 0;
+		for(int i = 0; i < count; i++) {
+			DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE0(RGB_ORDER)], scale));
+			DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE1(RGB_ORDER)], scale));
+			DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE2(RGB_ORDER)], scale));
+		}
+
+	}
+
+#ifdef SUPPORT_ARGB
+	// as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
+	virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) = 0;
+#endif
+
+	// is the controller ready to write data out
+	virtual bool ready() { return true; }
+
+	// wait until the controller is ready to write data out
+	virtual void wait() { return; }
+
+};
+
+#endif
+
+#endif
\ No newline at end of file
diff --git a/lib/FastLED/examples/Blink/Blink.ino b/lib/FastLED/examples/Blink/Blink.ino
new file mode 100644
index 0000000..bc5c84d
--- /dev/null
+++ b/lib/FastLED/examples/Blink/Blink.ino
@@ -0,0 +1,44 @@
+#include "FastLED.h"
+
+// How many leds in your strip?
+#define NUM_LEDS 6
+
+// For led chips like Neopixels, which have a data line, ground, and power, you just
+// need to define DATA_PIN.  For led chipsets that are SPI based (four wires - data, clock,
+// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN
+#define DATA_PIN 3
+#define CLOCK_PIN 13
+
+// Define the array of leds
+CRGB leds[NUM_LEDS];
+
+void setup() {
+      // Uncomment/edit one of the following lines for your leds arrangement.
+      // FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
+  	  FastLED.addLeds<NEOPIXEL, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
+
+      // FastLED.addLeds<WS2801, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<SM16716, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<LPD8806, RGB>(leds, NUM_LEDS);
+
+      // FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+}
+
+void loop() {
+  // Turn the LED on, then pause
+  leds[0] = CRGB::Red;
+  FastLED.show();
+  delay(500);
+  // Now turn the LED off, then pause
+  leds[0] = CRGB::Black;
+  FastLED.show();
+  delay(500);
+}
diff --git a/lib/FastLED/examples/Cylon/Cylon.ino b/lib/FastLED/examples/Cylon/Cylon.ino
new file mode 100644
index 0000000..bec878c
--- /dev/null
+++ b/lib/FastLED/examples/Cylon/Cylon.ino
@@ -0,0 +1,43 @@
+#include "FastLED.h"
+
+// How many leds in your strip?
+#define NUM_LEDS 6
+
+// For led chips like Neopixels, which have a data line, ground, and power, you just
+// need to define DATA_PIN.  For led chipsets that are SPI based (four wires - data, clock,
+// ground, and power), like the LPD8806, define both DATA_PIN and CLOCK_PIN
+#define DATA_PIN 11
+#define CLOCK_PIN 13
+
+// Define the array of leds
+CRGB leds[NUM_LEDS];
+
+void setup() {
+	FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
+}
+
+void loop() {
+	// First slide the led in one direction
+	for(int i = 0; i < NUM_LEDS; i++) {
+		// Set the i'th led to red
+		leds[i] = CRGB::Red;
+		// Show the leds
+		FastLED.show();
+		// now that we've shown the leds, reset the i'th led to black
+		leds[i] = CRGB::Black;
+		// Wait a little bit before we loop around and do it again
+		delay(30);
+	}
+
+	// Now go in the other direction.
+	for(int i = NUM_LEDS-1; i >= 0; i--) {
+		// Set the i'th led to red
+		leds[i] = CRGB::Red;
+		// Show the leds
+		FastLED.show();
+		// now that we've shown the leds, reset the i'th led to black
+		leds[i] = CRGB::Black;
+		// Wait a little bit before we loop around and do it again
+		delay(30);
+	}
+}
\ No newline at end of file
diff --git a/lib/FastLED/examples/Fast2Dev/Fast2Dev.ino b/lib/FastLED/examples/Fast2Dev/Fast2Dev.ino
new file mode 100644
index 0000000..16aa254
--- /dev/null
+++ b/lib/FastLED/examples/Fast2Dev/Fast2Dev.ino
@@ -0,0 +1,99 @@
+// Uncomment this line if you have any interrupts that are changing pins - this causes the library to be a little bit more cautious
+// #define FAST_SPI_INTERRUPTS_WRITE_PINS 1
+
+// Uncomment this line to force always using software, instead of hardware, SPI (why?)
+// #define FORCE_SOFTWARE_SPI 1
+
+// Uncomment this line if you want to talk to DMX controllers
+// #define FASTSPI_USE_DMX_SIMPLE 1
+
+#include "FastLED.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// test code
+//
+//////////////////////////////////////////////////
+
+#define NUM_LEDS 150
+
+CRGB leds[NUM_LEDS];
+
+void setup() {
+	// sanity check delay - allows reprogramming if accidently blowing power w/leds
+   	delay(2000);
+
+   	// For safety (to prevent too high of a power draw), the test case defaults to
+   	// setting brightness to 25% brightness
+   	LEDS.setBrightness(64);
+
+   	// LEDS.addLeds<WS2811, 13>(leds, NUM_LEDS);
+   	// LEDS.addLeds<TM1809, 13>(leds, NUM_LEDS);
+   	// LEDS.addLeds<UCS1903, 13>(leds, NUM_LEDS);
+   	// LEDS.addLeds<TM1803, 13>(leds, NUM_LEDS);
+
+   	// LEDS.addLeds<P9813>(leds, NUM_LEDS);
+   	CLEDController *pLed = new ClocklessController_Trinket<4, NS(400), NS(400), NS(450), GRB>();
+   	LEDS.addLeds(pLed, leds, NUM_LEDS);
+   	// LEDS.addLeds<LPD8806>(leds, NUM_LEDS);
+	// LEDS.addLeds<WS2801>(leds, NUM_LEDS);
+   	// LEDS.addLeds<SM16716>(leds, NUM_LEDS);
+
+   	// LEDS.addLeds<WS2811, 11>(leds, NUM_LEDS);
+
+	// Put ws2801 strip on the hardware SPI pins with a BGR ordering of rgb and limited to a 1Mhz data rate
+	// LEDS.addLeds<WS2801, 11, 13, BGR, DATA_RATE_MHZ(1)>(leds, NUM_LEDS);
+
+   	// LEDS.addLeds<LPD8806, 10, 11>(leds, NUM_LEDS);
+   	// LEDS.addLeds<WS2811, 13, BRG>(leds, NUM_LEDS);
+   	// LEDS.addLeds<LPD8806, BGR>(leds, NUM_LEDS);
+}
+
+void loop() {
+	for(int i = 0; i < 3; i++) {
+		for(int iLed = 0; iLed < NUM_LEDS; iLed++) {
+			memset(leds, 0,  NUM_LEDS * sizeof(struct CRGB));
+
+			switch(i) {
+				// You can access the rgb values by field r, g, b
+			 	case 0: leds[iLed].r = 128; break;
+
+			 	// or by indexing into the led (r==0, g==1, b==2)
+			 	case 1: leds[iLed][i] = 128; break;
+
+			 	// or by setting the rgb values for the pixel all at once
+			 	case 2: leds[iLed] = CRGB(0, 0, 128); break;
+			}
+
+			// and now, show your led array!
+			LEDS.show();
+			delay(10);
+		}
+
+		// fade up
+		for(int x = 0; x < 128; x++) {
+			// The showColor method sets all the leds in the strip to the same color
+			LEDS.showColor(CRGB(x, 0, 0));
+			delay(10);
+		}
+
+		// fade down
+		for(int x = 128; x >= 0; x--) {
+			LEDS.showColor(CRGB(x, 0, 0));
+			delay(10);
+		}
+
+		// let's fade up by scaling the brightness
+		for(int scale = 0; scale < 128; scale++) {
+			LEDS.showColor(CRGB(0, 128, 0), scale);
+			delay(10);
+		}
+
+		// let's fade down by scaling the brightness
+		for(int scale = 128; scale > 0; scale--) {
+			LEDS.showColor(CRGB(0, 128, 0), scale);
+			delay(10);
+		}
+	}
+}
diff --git a/lib/FastLED/examples/FirstLight/FirstLight.ino b/lib/FastLED/examples/FirstLight/FirstLight.ino
new file mode 100644
index 0000000..be7f33c
--- /dev/null
+++ b/lib/FastLED/examples/FirstLight/FirstLight.ino
@@ -0,0 +1,65 @@
+// #define FORCE_SOFTWARE_SPI
+// #define FORCE_SOFTWARE_PINS
+#include "FastLED.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+// Move a white dot along the strip of leds.  This program simply shows how to configure the leds,
+// and then how to turn a single pixel white and then off, moving down the line of pixels.
+//
+
+// How many leds are in the strip?
+#define NUM_LEDS 60
+
+// Data pin that led data will be written out over
+#define DATA_PIN 6
+
+// Clock pin only needed for SPI based chipsets when not using hardware SPI
+//#define CLOCK_PIN 8
+
+// This is an array of leds.  One item for each led in your strip.
+CRGB leds[NUM_LEDS];
+
+// This function sets up the ledsand tells the controller about them
+void setup() {
+	// sanity check delay - allows reprogramming if accidently blowing power w/leds
+   	delay(2000);
+
+      // Uncomment one of the following lines for your leds arrangement.
+      // FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
+      FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<NEOPIXEL, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2811_400, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
+
+      // FastLED.addLeds<WS2801, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<SM16716, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<LPD8806, RGB>(leds, NUM_LEDS);
+
+      // FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+}
+
+// This function runs over and over, and is where you do the magic to light
+// your leds.
+void loop() {
+   // Move a single white led
+   for(int whiteLed = 0; whiteLed < NUM_LEDS; whiteLed = whiteLed + 1) {
+      // Turn our current led on to white, then show the leds
+      leds[whiteLed] = CRGB::White;
+
+      // Show the leds (only one of which is set to white, from above)
+      FastLED.show();
+
+      // Wait a little bit
+      delay(100);
+
+      // Turn our current led back to black for the next loop around
+      leds[whiteLed] = CRGB::Black;
+   }
+}
diff --git a/lib/FastLED/examples/RGBCalibrate/RGBCalibrate.ino b/lib/FastLED/examples/RGBCalibrate/RGBCalibrate.ino
new file mode 100644
index 0000000..5566105
--- /dev/null
+++ b/lib/FastLED/examples/RGBCalibrate/RGBCalibrate.ino
@@ -0,0 +1,66 @@
+#include "FastLED.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// RGB Calibration code
+//
+// Use this sketch to determine what the RGB ordering for your chipset should be.  Steps for setting up to use:
+
+// * Uncomment the line in setup that corresponds to the LED chipset that you are using.  (Note that they
+//   all explicitly specify the RGB order as RGB)
+// * Define DATA_PIN to the pin that data is connected to.
+// * (Optional) if using software SPI for chipsets that are SPI based, define CLOCK_PIN to the clock pin
+// * Compile/upload/run the sketch
+
+// You should see six leds on.  If the RGB ordering is correct, you should see 1 red led, 2 green
+// leds, and 3 blue leds.  If you see different colors, the count of each color tells you what the
+// position for that color in the rgb orering should be.  So, for example, if you see 1 Blue, and 2
+// Red, and 3 Green leds then the rgb ordering should be BRG (Blue, Red, Green).
+
+// You can then test this ordering by setting the RGB ordering in the addLeds line below to the new ordering
+// and it should come out correctly, 1 red, 2 green, and 3 blue.
+//
+//////////////////////////////////////////////////
+
+#define NUM_LEDS 6
+
+// Data pin that led data will be written out over
+#define DATA_PIN 7
+// Clock pin only needed for SPI based chipsets when not using hardware SPI
+//#define CLOCK_PIN 8
+
+CRGB leds[NUM_LEDS];
+
+void setup() {
+	// sanity check delay - allows reprogramming if accidently blowing power w/leds
+   	delay(2000);
+
+      // Uncomment one of the following lines for your leds arrangement.
+      // FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
+
+      // FastLED.addLeds<WS2801, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<SM16716, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<LPD8806, RGB>(leds, NUM_LEDS);
+
+      // FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+      // FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+}
+
+void loop() {
+   leds[0] = CRGB::Red;
+   leds[1] = CRGB::Green;
+   leds[2] = CRGB::Green;
+   leds[3] = CRGB::Blue;
+   leds[4] = CRGB::Blue;
+   leds[5] = CRGB::Blue;
+   FastLED.show();
+   delay(1000);
+}
diff --git a/lib/FastLED/fastpin.h b/lib/FastLED/fastpin.h
new file mode 100644
index 0000000..10e8f2f
--- /dev/null
+++ b/lib/FastLED/fastpin.h
@@ -0,0 +1,424 @@
+#ifndef __INC_FASTPIN_H
+#define __INC_FASTPIN_H
+
+#include<avr/io.h>
+
+// Arduino.h needed for convinience functions digitalPinToPort/BitMask/portOutputRegister and the pinMode methods.
+#include<Arduino.h>
+
+#define NO_PIN 255
+
+// Class to ensure that a minimum amount of time has kicked since the last time run - and delay if not enough time has passed yet
+// this should make sure that chipsets that have
+template<int WAIT> class CMinWait {
+	long mLastMicros;
+public:
+	CMinWait() { mLastMicros = 0; }
+
+	void wait() {
+		long diff = micros() - mLastMicros;
+		while(diff < WAIT) {
+			diff = micros() - mLastMicros;
+		}
+	}
+
+	void mark() { mLastMicros = micros(); }
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Pin access class - needs to tune for various platforms (naive fallback solution?)
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+#define _CYCLES(_PIN) (((_PIN >= 62 ) || (_PIN>=42 && _PIN<=49) || (_PIN>=14 && _PIN <=17) || (_PIN>=6 && _PIN <=9)) ? 2 : 1)
+#else
+#define _CYCLES(_PIN) ((_PIN >= 24) ? 2 : 1)
+#endif
+
+class Selectable {
+public:
+	virtual void select() = 0;
+	virtual void release() = 0;
+	virtual bool isSelected() = 0;
+};
+
+class Pin : public Selectable {
+	uint8_t mPinMask;
+	uint8_t mPin;
+	volatile uint8_t *mPort;
+
+	void _init() {
+		mPinMask = digitalPinToBitMask(mPin);
+		mPort = portOutputRegister(digitalPinToPort(mPin));
+	}
+public:
+	Pin(int pin) : mPin(pin) { _init(); }
+
+	typedef volatile uint8_t * port_ptr_t;
+	typedef uint8_t port_t;
+
+	inline void setOutput() { pinMode(mPin, OUTPUT); }
+	inline void setInput() { pinMode(mPin, INPUT); }
+
+	inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
+	inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
+
+	inline void strobe() __attribute__ ((always_inline)) { hi(); lo(); }
+
+	inline void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
+	inline void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
+	inline void set(register port_t val) __attribute__ ((always_inline)) { *mPort = val; }
+
+	inline void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port  = val; }
+
+	port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask;  }
+	port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; }
+	port_ptr_t  port() __attribute__ ((always_inline)) { return mPort; }
+	port_t mask() __attribute__ ((always_inline)) { return mPinMask; }
+
+	virtual void select() { hi(); }
+	virtual void release() { lo(); }
+	virtual bool isSelected() { return (*mPort & mPinMask) == mPinMask; }
+};
+
+class OutputPin : public Pin {
+public:
+	OutputPin(int pin) : Pin(pin) { setOutput(); }
+};
+
+class InputPin : public Pin {
+public:
+	InputPin(int pin) : Pin(pin) { setInput(); }
+};
+
+/// The simplest level of Pin class.  This relies on runtime functions durinig initialization to get the port/pin mask for the pin.  Most
+/// of the accesses involve references to these static globals that get set up.  This won't be the fastest set of pin operations, but it
+/// will provide pin level access on pretty much all arduino environments.  In addition, it includes some methods to help optimize access in
+/// various ways.  Namely, the versions of hi, lo, and fastset that take the port register as a passed in register variable (saving a global
+/// dereference), since these functions are aggressively inlined, that can help collapse out a lot of extraneous memory loads/dereferences.
+///
+/// In addition, if, while writing a bunch of data to a pin, you know no other pins will be getting written to, you can get/cache a value of
+/// the pin's port register and use that to do a full set to the register.  This results in one being able to simply do a store to the register,
+/// vs. the load, and/or, and store that would be done normally.
+///
+/// There are platform specific instantiations of this class that provide direct i/o register access to pins for much higher speed pin twiddling.
+///
+/// Note that these classes are all static functions.  So the proper usage is Pin<13>::hi(); or such.  Instantiating objects is not recommended,
+/// as passing Pin objects around will likely -not- have the effect you're expecting.
+template<uint8_t PIN> class FastPin {
+	static uint8_t sPinMask;
+	static volatile uint8_t *sPort;
+	static void _init() {
+		sPinMask = digitalPinToBitMask(PIN);
+		sPort = portOutputRegister(digitalPinToPort(PIN));
+	}
+public:
+	typedef volatile uint8_t * port_ptr_t;
+	typedef uint8_t port_t;
+
+	inline static void setOutput() { _init(); pinMode(PIN, OUTPUT); }
+	inline static void setInput() { _init(); pinMode(PIN, INPUT); }
+
+	inline static void hi() __attribute__ ((always_inline)) { *sPort |= sPinMask; }
+	inline static void lo() __attribute__ ((always_inline)) { *sPort &= ~sPinMask; }
+
+	inline static void strobe() __attribute__ ((always_inline)) { hi(); lo(); }
+
+	inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= sPinMask; }
+	inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~sPinMask; }
+	inline static void set(register port_t val) __attribute__ ((always_inline)) { *sPort = val; }
+
+	inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port  = val; }
+
+	static port_t hival() __attribute__ ((always_inline)) { return *sPort | sPinMask;  }
+	static port_t loval() __attribute__ ((always_inline)) { return *sPort & ~sPinMask; }
+	static port_ptr_t  port() __attribute__ ((always_inline)) { return sPort; }
+	static port_t mask() __attribute__ ((always_inline)) { return sPinMask; }
+};
+
+template<uint8_t PIN> uint8_t FastPin<PIN>::sPinMask;
+template<uint8_t PIN> volatile uint8_t *FastPin<PIN>::sPort;
+
+/// Class definition for a Pin where we know the port registers at compile time for said pin.  This allows us to make
+/// a lot of optimizations, as the inlined hi/lo methods will devolve to a single io register write/bitset.
+template<uint8_t PIN, uint8_t _MASK, typename _PORT, typename _DDR, typename _PIN> class _AVRPIN {
+public:
+	typedef volatile uint8_t * port_ptr_t;
+	typedef uint8_t port_t;
+
+	inline static void setOutput() { _DDR::r() |= _MASK; }
+	inline static void setInput() { _DDR::r() &= ~_MASK; }
+
+	inline static void hi() __attribute__ ((always_inline)) { _PORT::r() |= _MASK; }
+	inline static void lo() __attribute__ ((always_inline)) { _PORT::r() &= ~_MASK; }
+	inline static void set(register uint8_t val) __attribute__ ((always_inline)) { _PORT::r() = val; }
+
+	inline static void strobe() __attribute__ ((always_inline)) { hi(); lo(); }
+
+	inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
+	inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+	inline static void fastset(register port_ptr_t port, register uint8_t val) __attribute__ ((always_inline)) { set(val); }
+
+	inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; }
+	inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; }
+	inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PORT::r(); }
+	inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+};
+
+/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers.  Note that this
+/// uses the full port GPIO registers.  In theory, in some way, bit-band register access -should- be faster, however I have found
+/// that something about the way gcc does register allocation results in the bit-band code being slower.  It will need more fine tuning.
+template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
+public:
+	typedef volatile uint32_t * port_ptr_t;
+	typedef uint32_t port_t;
+
+	inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
+	inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
+
+	inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
+	inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
+	inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
+
+	inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+
+	inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
+
+	inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
+	inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+	inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+	inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
+	inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
+	inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
+	inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+};
+
+/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers.  GCC
+/// does a poor job of optimizing around these accesses so they are not being used just yet.
+template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND {
+public:
+	typedef volatile uint32_t * port_ptr_t;
+	typedef uint32_t port_t;
+
+	inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
+	inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
+
+	inline static void hi() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 1; }
+	inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
+	inline static void set(register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
+
+	inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+
+	inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; }
+
+	inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port = 1;  }
+	inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port = 0; }
+	inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+	inline static port_t hival() __attribute__ ((always_inline)) { return 1; }
+	inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
+	inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
+	inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
+};
+
+/// AVR definitions for pins.  Getting around  the fact that I can't pass GPIO register addresses in as template arguments by instead creating
+/// a custom type for each GPIO register with a single, static, aggressively inlined function that returns that specific GPIO register.  A similar
+/// trick is used a bit further below for the ARM GPIO registers (of which there are far more than on AVR!)
+typedef volatile uint8_t & reg8_t;
+#define _R(T) struct __gen_struct_ ## T
+#define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }};
+#define _IO(L) _RD8(DDR ## L); _RD8(PORT ## L); _RD8(PIN ## L);
+#define _DEFPIN_AVR(PIN, MASK, L) template<> class FastPin<PIN> : public _AVRPIN<PIN, MASK, _R(PORT ## L), _R(DDR ## L), _R(PIN ## L)> {};
+
+// ARM definitions
+#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
+#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
+
+typedef volatile uint32_t & reg32_t;
+typedef volatile uint32_t * ptr_reg32_t;
+
+#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
+	template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
+#define _IO32(L) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR);
+
+#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
+																			_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
+
+// Don't use bit band'd pins for now, the compiler generates far less efficient code around them
+// #define _DEFPIN_ARM(PIN, BIT, L) template<> class Pin<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR),
+// 																			_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Pin definitions for AVR and ARM.  If there are pin definitions supplied below for the platform being
+// built on, then much higher speed access will be possible, namely with direct GPIO register accesses.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+#if defined(FORCE_SOFTWARE_PINS)
+#warning "Softwrae pin support forced pin access will be slightly slower.  See fastpin.h for info."
+#define NO_HARDWARE_PIN_SUPPORT
+
+#elif defined(__AVR_ATtiny85__)
+_IO(B);
+
+_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B); _DEFPIN_AVR(3, 0x08, B);
+_DEFPIN_AVR(4, 0x10, B); _DEFPIN_AVR(5, 0x20, B);
+
+#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
+// Accelerated port definitions for arduino avrs
+_IO(D); _IO(B); _IO(C);
+_DEFPIN_AVR( 0, 0x01, D); _DEFPIN_AVR( 1, 0x02, D); _DEFPIN_AVR( 2, 0x04, D); _DEFPIN_AVR( 3, 0x08, D);
+_DEFPIN_AVR( 4, 0x10, D); _DEFPIN_AVR( 5, 0x20, D); _DEFPIN_AVR( 6, 0x40, D); _DEFPIN_AVR( 7, 0x80, D);
+_DEFPIN_AVR( 8, 0x01, B); _DEFPIN_AVR( 9, 0x02, B); _DEFPIN_AVR(10, 0x04, B); _DEFPIN_AVR(11, 0x08, B);
+_DEFPIN_AVR(12, 0x10, B); _DEFPIN_AVR(13, 0x20, B); _DEFPIN_AVR(14, 0x01, C); _DEFPIN_AVR(15, 0x02, C);
+_DEFPIN_AVR(16, 0x04, C); _DEFPIN_AVR(17, 0x08, C); _DEFPIN_AVR(18, 0x10, C); _DEFPIN_AVR(19, 0x20, C);
+
+#define SPI_DATA 11
+#define SPI_CLOCK 13
+#define SPI_SELECT 10
+#define AVR_HARDWARE_SPI
+
+#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+// megas
+
+_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); _IO(G); _IO(H); _IO(J); _IO(K); _IO(L);
+
+_DEFPIN_AVR(0, 1, E); _DEFPIN_AVR(1, 2, E); _DEFPIN_AVR(2, 16, E); _DEFPIN_AVR(3, 32, E);
+_DEFPIN_AVR(4, 32, G); _DEFPIN_AVR(5, 8, E); _DEFPIN_AVR(6, 8, H); _DEFPIN_AVR(7, 16, H);
+_DEFPIN_AVR(8, 32, H); _DEFPIN_AVR(9, 64, H); _DEFPIN_AVR(10, 16, B); _DEFPIN_AVR(11, 32, B);
+_DEFPIN_AVR(12, 64, B); _DEFPIN_AVR(13, 128, B); _DEFPIN_AVR(14, 2, J); _DEFPIN_AVR(15, 1, J);
+_DEFPIN_AVR(16, 2, H); _DEFPIN_AVR(17, 1, H); _DEFPIN_AVR(18, 8, D); _DEFPIN_AVR(19, 4, D);
+_DEFPIN_AVR(20, 2, D); _DEFPIN_AVR(21, 1, D); _DEFPIN_AVR(22, 1, A); _DEFPIN_AVR(23, 2, A);
+_DEFPIN_AVR(24, 4, A); _DEFPIN_AVR(25, 8, A); _DEFPIN_AVR(26, 16, A); _DEFPIN_AVR(27, 32, A);
+_DEFPIN_AVR(28, 64, A); _DEFPIN_AVR(29, 128, A); _DEFPIN_AVR(30, 128, C); _DEFPIN_AVR(31, 64, C);
+_DEFPIN_AVR(32, 32, C); _DEFPIN_AVR(33, 16, C); _DEFPIN_AVR(34, 8, C); _DEFPIN_AVR(35, 4, C);
+_DEFPIN_AVR(36, 2, C); _DEFPIN_AVR(37, 1, C); _DEFPIN_AVR(38, 128, D); _DEFPIN_AVR(39, 4, G);
+_DEFPIN_AVR(40, 2, G); _DEFPIN_AVR(41, 1, G); _DEFPIN_AVR(42, 128, L); _DEFPIN_AVR(43, 64, L);
+_DEFPIN_AVR(44, 32, L); _DEFPIN_AVR(45, 16, L); _DEFPIN_AVR(46, 8, L); _DEFPIN_AVR(47, 4, L);
+_DEFPIN_AVR(48, 2, L); _DEFPIN_AVR(49, 1, L); _DEFPIN_AVR(50, 8, B); _DEFPIN_AVR(51, 4, B);
+_DEFPIN_AVR(52, 2, B); _DEFPIN_AVR(53, 1, B); _DEFPIN_AVR(54, 1, F); _DEFPIN_AVR(55, 2, F);
+_DEFPIN_AVR(56, 4, F); _DEFPIN_AVR(57, 8, F); _DEFPIN_AVR(58, 16, F); _DEFPIN_AVR(59, 32, F);
+_DEFPIN_AVR(60, 64, F); _DEFPIN_AVR(61, 128, F); _DEFPIN_AVR(62, 1, K); _DEFPIN_AVR(63, 2, K);
+_DEFPIN_AVR(64, 4, K); _DEFPIN_AVR(65, 8, K); _DEFPIN_AVR(66, 16, K); _DEFPIN_AVR(67, 32, K);
+_DEFPIN_AVR(68, 64, K); _DEFPIN_AVR(69, 128, K);
+
+#define SPI_DATA 51
+#define SPI_CLOCK 52
+#define SPI_SELECT 53
+#define AVR_HARDWARE_SPI
+
+// Leonardo, teensy, blinkm
+#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
+
+// teensy defs
+_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
+
+_DEFPIN_AVR(0, 1, B); _DEFPIN_AVR(1, 2, B); _DEFPIN_AVR(2, 4, B); _DEFPIN_AVR(3, 8, B);
+_DEFPIN_AVR(4, 128, B); _DEFPIN_AVR(5, 1, D); _DEFPIN_AVR(6, 2, D); _DEFPIN_AVR(7, 4, D);
+_DEFPIN_AVR(8, 8, D); _DEFPIN_AVR(9, 64, C); _DEFPIN_AVR(10, 128, C); _DEFPIN_AVR(11, 64, D);
+_DEFPIN_AVR(12, 128, D); _DEFPIN_AVR(13, 16, B); _DEFPIN_AVR(14, 32, B); _DEFPIN_AVR(15, 64, B);
+_DEFPIN_AVR(16, 128, F); _DEFPIN_AVR(17, 64, F); _DEFPIN_AVR(18, 32, F); _DEFPIN_AVR(19, 16, F);
+_DEFPIN_AVR(20, 2, F); _DEFPIN_AVR(21, 1, F); _DEFPIN_AVR(22, 16, D); _DEFPIN_AVR(23, 32, D);
+
+#define SPI_DATA 2
+#define SPI_CLOCK 1
+#define SPI_SELECT 3
+#define AVR_HARDWARE_SPI
+
+#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
+// teensy++ 2 defs
+
+_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
+
+_DEFPIN_AVR(0, 1, D); _DEFPIN_AVR(1, 2, D); _DEFPIN_AVR(2, 4, D); _DEFPIN_AVR(3, 8, D);
+_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 32, D); _DEFPIN_AVR(6, 64, D); _DEFPIN_AVR(7, 128, D);
+_DEFPIN_AVR(8, 1, E); _DEFPIN_AVR(9, 2, E); _DEFPIN_AVR(10, 1, C); _DEFPIN_AVR(11, 2, C);
+_DEFPIN_AVR(12, 4, C); _DEFPIN_AVR(13, 8, C); _DEFPIN_AVR(14, 16, C); _DEFPIN_AVR(15, 32, C);
+_DEFPIN_AVR(16, 64, C); _DEFPIN_AVR(17, 128, C); _DEFPIN_AVR(18, 64, E); _DEFPIN_AVR(19, 128, E);
+_DEFPIN_AVR(20, 1, B); _DEFPIN_AVR(21, 2, B); _DEFPIN_AVR(22, 4, B); _DEFPIN_AVR(23, 8, B);
+_DEFPIN_AVR(24, 16, B); _DEFPIN_AVR(25, 32, B); _DEFPIN_AVR(26, 64, B); _DEFPIN_AVR(27, 128, B);
+_DEFPIN_AVR(28, 1, A); _DEFPIN_AVR(29, 2, A); _DEFPIN_AVR(30, 4, A); _DEFPIN_AVR(31, 8, A);
+_DEFPIN_AVR(32, 16, A); _DEFPIN_AVR(33, 32, A); _DEFPIN_AVR(34, 64, A); _DEFPIN_AVR(35, 128, A);
+_DEFPIN_AVR(36, 16, E); _DEFPIN_AVR(37, 32, E); _DEFPIN_AVR(38, 1, F); _DEFPIN_AVR(39, 2, F);
+_DEFPIN_AVR(40, 4, F); _DEFPIN_AVR(41, 8, F); _DEFPIN_AVR(42, 16, F); _DEFPIN_AVR(43, 32, F);
+_DEFPIN_AVR(44, 64, F); _DEFPIN_AVR(45, 128, F);
+
+#define SPI_DATA 22
+#define SPI_CLOCK 21
+#define SPI_SELECT 20
+#define AVR_HARDWARE_SPI
+
+#elif defined(__AVR_ATmega32U4__)
+
+// leonard defs
+_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
+
+_DEFPIN_AVR(0, 4, D); _DEFPIN_AVR(1, 8, D); _DEFPIN_AVR(2, 2, D); _DEFPIN_AVR(3, 1, D);
+_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 64, C); _DEFPIN_AVR(6, 128, D); _DEFPIN_AVR(7, 64, E);
+_DEFPIN_AVR(8, 16, B); _DEFPIN_AVR(9, 32, B); _DEFPIN_AVR(10, 64, B); _DEFPIN_AVR(11, 128, B);
+_DEFPIN_AVR(12, 64, D); _DEFPIN_AVR(13, 128, C); _DEFPIN_AVR(14, 8, B); _DEFPIN_AVR(15, 2, B);
+_DEFPIN_AVR(16, 4, B); _DEFPIN_AVR(17, 1, B); _DEFPIN_AVR(18, 128, F); _DEFPIN_AVR(19, 64, F);
+_DEFPIN_AVR(20, 32, F); _DEFPIN_AVR(21, 16, F); _DEFPIN_AVR(22, 2, F); _DEFPIN_AVR(23, 0, F);
+
+#define SPI_DATA 16
+#define SPI_CLOCK 15
+#define AVR_HARDWARE_SPI
+
+#elif defined(__MK20DX128__) && defined(CORE_TEENSY)
+
+_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E);
+
+_DEFPIN_ARM(0, 16, B); _DEFPIN_ARM(1, 17, B); _DEFPIN_ARM(2, 0, D); _DEFPIN_ARM(3, 12, A);
+_DEFPIN_ARM(4, 13, A); _DEFPIN_ARM(5, 7, D); _DEFPIN_ARM(6, 4, D); _DEFPIN_ARM(7, 2, D);
+_DEFPIN_ARM(8, 3, D); _DEFPIN_ARM(9, 3, C); _DEFPIN_ARM(10, 4, C); _DEFPIN_ARM(11, 6, C);
+_DEFPIN_ARM(12, 7, C); _DEFPIN_ARM(13, 5, C); _DEFPIN_ARM(14, 1, D); _DEFPIN_ARM(15, 0, C);
+_DEFPIN_ARM(16, 0, B); _DEFPIN_ARM(17, 1, B); _DEFPIN_ARM(18, 3, B); _DEFPIN_ARM(19, 2, B);
+_DEFPIN_ARM(20, 5, D); _DEFPIN_ARM(21, 6, D); _DEFPIN_ARM(22, 1, C); _DEFPIN_ARM(23, 2, C);
+_DEFPIN_ARM(24, 5, A); _DEFPIN_ARM(25, 19, B); _DEFPIN_ARM(26, 1, E); _DEFPIN_ARM(27, 9, C);
+_DEFPIN_ARM(28, 8, C); _DEFPIN_ARM(29, 10, C); _DEFPIN_ARM(30, 11, C); _DEFPIN_ARM(31, 0, E);
+_DEFPIN_ARM(32, 18, B); _DEFPIN_ARM(33, 4, A);
+
+#define SPI_DATA 11
+#define SPI_CLOCK 13
+#define ARM_HARDWARE_SPI
+
+#elif defined(__SAM3X8E__)
+
+DUE_IO32(A);
+DUE_IO32(B);
+DUE_IO32(C);
+DUE_IO32(D);
+
+_DEFPIN_DUE(0, 8, A); _DEFPIN_DUE(1, 9, A); _DEFPIN_DUE(2, 25, B); _DEFPIN_DUE(3, 28, C);
+_DEFPIN_DUE(4, 26, C); _DEFPIN_DUE(5, 25, C); _DEFPIN_DUE(6, 24, C); _DEFPIN_DUE(7, 23, C);
+_DEFPIN_DUE(8, 22, C); _DEFPIN_DUE(9, 21, C); _DEFPIN_DUE(10, 29, C); _DEFPIN_DUE(11, 7, D);
+_DEFPIN_DUE(12, 8, D); _DEFPIN_DUE(13, 27, B); _DEFPIN_DUE(14, 4, D); _DEFPIN_DUE(15, 5, D);
+_DEFPIN_DUE(16, 13, A); _DEFPIN_DUE(17, 12, A); _DEFPIN_DUE(18, 11, A); _DEFPIN_DUE(19, 10, A);
+_DEFPIN_DUE(20, 12, B); _DEFPIN_DUE(21, 13, B); _DEFPIN_DUE(22, 26, B); _DEFPIN_DUE(23, 14, A);
+_DEFPIN_DUE(24, 15, A); _DEFPIN_DUE(25, 0, D); _DEFPIN_DUE(26, 1, D); _DEFPIN_DUE(27, 2, D);
+_DEFPIN_DUE(28, 3, D); _DEFPIN_DUE(29, 6, D); _DEFPIN_DUE(30, 9, D); _DEFPIN_DUE(31, 7, A);
+_DEFPIN_DUE(32, 10, D); _DEFPIN_DUE(33, 1, C); _DEFPIN_DUE(34, 2, C); _DEFPIN_DUE(35, 3, C);
+_DEFPIN_DUE(36, 4, C); _DEFPIN_DUE(37, 5, C); _DEFPIN_DUE(38, 6, C); _DEFPIN_DUE(39, 7, C);
+_DEFPIN_DUE(40, 8, C); _DEFPIN_DUE(41, 9, C); _DEFPIN_DUE(42, 19, A); _DEFPIN_DUE(43, 20, A);
+_DEFPIN_DUE(44, 19, C); _DEFPIN_DUE(45, 18, C); _DEFPIN_DUE(46, 17, C); _DEFPIN_DUE(47, 16, C);
+_DEFPIN_DUE(48, 15, C); _DEFPIN_DUE(49, 14, C); _DEFPIN_DUE(50, 13, C); _DEFPIN_DUE(51, 12, C);
+_DEFPIN_DUE(52, 21, B); _DEFPIN_DUE(53, 14, B); _DEFPIN_DUE(54, 16, A); _DEFPIN_DUE(55, 24, A);
+_DEFPIN_DUE(56, 23, A); _DEFPIN_DUE(57, 22, A); _DEFPIN_DUE(58, 6, A); _DEFPIN_DUE(59, 4, A);
+_DEFPIN_DUE(60, 3, A); _DEFPIN_DUE(61, 2, A); _DEFPIN_DUE(62, 17, B); _DEFPIN_DUE(63, 18, B);
+_DEFPIN_DUE(64, 19, B); _DEFPIN_DUE(65, 20, B); _DEFPIN_DUE(66, 15, B); _DEFPIN_DUE(67, 16, B);
+_DEFPIN_DUE(68, 1, A); _DEFPIN_DUE(69, 0, A); _DEFPIN_DUE(70, 17, A); _DEFPIN_DUE(71, 18, A);
+_DEFPIN_DUE(72, 30, C); _DEFPIN_DUE(73, 21, A); _DEFPIN_DUE(74, 25, A); _DEFPIN_DUE(75, 26, A);
+_DEFPIN_DUE(76, 27, A); _DEFPIN_DUE(77, 28, A); _DEFPIN_DUE(78, 23, B);
+
+#else
+
+#warning "No pin/port mappings found, pin access will be slightly slower.  See fastpin.h for info."
+#define NO_HARDWARE_PIN_SUPPORT
+
+#endif
+
+#endif
diff --git a/lib/FastLED/fastspi.h b/lib/FastLED/fastspi.h
new file mode 100644
index 0000000..0074713
--- /dev/null
+++ b/lib/FastLED/fastspi.h
@@ -0,0 +1,91 @@
+#ifndef __INC_FASTSPI_H
+#define __INC_FASTSPI_H
+
+#include "controller.h"
+#include "lib8tion.h"
+#include "delay.h"
+
+// Some helper macros for getting at mis-ordered byte values
+#define SPI_B0 (RGB_BYTE0(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
+#define SPI_B1 (RGB_BYTE1(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
+#define SPI_B2 (RGB_BYTE2(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
+#define SPI_ADVANCE (3 + (MASK_SKIP_BITS & SKIP))
+
+/// Some of the SPI controllers will need to perform a transform on each byte before doing
+/// anyting with it.  Creating a class of this form and passing it in as a template parameter to
+/// writeBytes/writeBytes3 below will ensure that the body of this method will get called on every
+/// byte worked on.  Recommendation, make the adjust method aggressively inlined.
+///
+/// TODO: Convinience macro for building these
+class DATA_NOP {
+public:
+	static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data) { return data; }
+	static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data, register uint8_t scale) { return scale8(data, scale); }
+	static __attribute__((always_inline)) inline void postBlock(int len) {}
+};
+
+#define FLAG_START_BIT 0x80
+#define MASK_SKIP_BITS 0x3F
+
+// Clock speed dividers
+#define SPEED_DIV_2 2
+#define SPEED_DIV_4 4
+#define SPEED_DIV_8 8
+#define SPEED_DIV_16 16
+#define SPEED_DIV_32 32
+#define SPEED_DIV_64 64
+#define SPEED_DIV_128 128
+
+#define MAX_DATA_RATE 0
+#define DATA_RATE_MHZ(X) ((F_CPU / 1000000L) / X)
+#define DATA_RATE_KHZ(X) ((F_CPU / 1000L) / X)
+
+// Include the various specific SPI implementations
+#include "fastspi_bitbang.h"
+#include "fastspi_arm.h"
+#include "fastspi_avr.h"
+#include "fastspi_dma.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// External SPI template definition with partial instantiation(s) to map to hardware SPI ports on platforms/builds where the pin
+// mappings are known at compile time.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
+
+template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class SoftwareSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
+
+#ifndef FORCE_SOFTWARE_SPI
+#if defined(SPI_DATA) && defined(SPI_CLOCK)
+
+#if defined(__MK20DX128__) && defined(CORE_TEENSY)
+
+template<uint8_t SPI_SPEED>
+class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
+
+#else
+
+template<uint8_t SPI_SPEED>
+class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
+
+#endif
+
+#else
+#warning "No hardware SPI pins defined.  All SPI access will default to bitbanged output"
+
+#endif
+
+// #if defined(USART_DATA) && defined(USART_CLOCK)
+// template<uint8_t SPI_SPEED>
+// class AVRSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> : public AVRUSARTSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> {};
+// #endif
+
+#else
+#warning "Forcing software SPI - no hardware SPI for you!"
+#endif
+
+#endif
diff --git a/lib/FastLED/fastspi_arm.h b/lib/FastLED/fastspi_arm.h
new file mode 100644
index 0000000..e9c3834
--- /dev/null
+++ b/lib/FastLED/fastspi_arm.h
@@ -0,0 +1,386 @@
+#ifndef __INC_FASTSPI_ARM_H
+#define __INC_FASTSPI_ARM_H
+
+
+#if defined(__MK20DX128__) && defined(CORE_TEENSY)
+
+#ifndef SPI_PUSHR_CONT
+#define SPI_PUSHR_CONT SPI0_PUSHR_CONT
+#define SPI_PUSHR_CTAS(X) SPI0_PUSHR_CTAS(X)
+#define SPI_PUSHR_EOQ SPI0_PUSHR_EOQ
+#define SPI_PUSHR_CTCNT SPI0_PUSHR_CTCNT
+#define SPI_PUSHR_PCS(X) SPI0_PUSHR_PCS(X)
+#endif
+
+// Template function that, on compilation, expands to a constant representing the highest bit set in a byte.  Right now,
+// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit
+// set (the zero-th bit).  Unclear if I  will want this to change at some point.
+template<int VAL, int BIT> class BitWork {
+	public:
+		static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork<VAL, BIT-1>::highestBit(); }
+};
+template<int VAL> class BitWork<VAL, 0> {
+	public:
+		static int highestBit() __attribute__((always_inline)) { return 0; }
+};
+
+#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
+
+#define USE_CONT 0
+
+// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
+template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
+    switch(VAL) {
+    		// Handle the dbl clock cases
+    		case 0: case 1:
+            case 2: preScalar = 0; scalar = 0; dbl = 1; break;
+            case 3: preScalar = 1; scalar = 0; dbl = 1; break;
+            case 5: preScalar = 2; scalar = 0; dbl = 1; break;
+            case 7: preScalar = 3; scalar = 0; dbl = 1; break;
+
+            // Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
+            // below)
+            case 9: preScalar = 1; scalar = 2; dbl = 1; break;
+            case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
+
+            case 15: preScalar = 2; scalar = 2; dbl = 1; break;
+            case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
+
+            case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
+            case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
+            default: {
+                int p2 = BitWork<VAL/2, 15>::highestBit();
+                int p3 = BitWork<VAL/3, 15>::highestBit();
+                int p5 = BitWork<VAL/5, 15>::highestBit();
+                int p7 = BitWork<VAL/7, 15>::highestBit();
+
+                int w2 = 2 * (1 << p2);
+                int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
+                int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
+                int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
+
+                int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
+
+                if(w2 == maxval) { preScalar = 0; scalar = p2; }
+                else if(w3 == maxval) { preScalar = 1; scalar = p3; }
+                else if(w5 == maxval) { preScalar = 2; scalar = p5; }
+                else if(w7 == maxval) { preScalar = 3; scalar = p7; }
+
+                dbl = 0;
+                if(scalar == 0) { dbl = 1; }
+                else if(scalar < 3) { scalar--; }
+            }
+    }
+	return;
+}
+
+
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class ARMHardwareSPIOutput {
+	Selectable *m_pSelect;
+
+	// Borrowed from the teensy3 SPSR emulation code
+	static inline void enable_pins(void) __attribute__((always_inline)) {
+		//serial_print("enable_pins\n");
+		CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
+		CORE_PIN12_CONFIG = PORT_PCR_MUX(2);
+		CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
+	}
+
+	// Borrowed from the teensy3 SPSR emulation code
+	static inline void disable_pins(void) __attribute__((always_inline)) {
+		//serial_print("disable_pins\n");
+		CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+		CORE_PIN12_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+		CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+	}
+
+public:
+	ARMHardwareSPIOutput() { m_pSelect = NULL; }
+	ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+	void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
+
+    static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
+            if (SPI0_CTAR0 == ctar) return;
+            uint32_t mcr = SPI0_MCR;
+            if (mcr & SPI_MCR_MDIS) {
+                    SPI0_CTAR0 = ctar;
+            } else {
+                    SPI0_MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
+                    SPI0_CTAR0 = ctar;
+
+                    SPI0_MCR = mcr;
+            }
+    }
+
+    static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
+            if (SPI0_CTAR1 == ctar) return;
+            uint32_t mcr = SPI0_MCR;
+            if (mcr & SPI_MCR_MDIS) {
+                    SPI0_CTAR1 = ctar;
+            } else {
+                    SPI0_MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
+                    SPI0_CTAR1 = ctar;
+                    SPI0_MCR = mcr;
+
+            }
+    }
+
+    static inline void set_ctar1_bits(int bits) {
+	    // Set ctar1 to 16 bits
+	    int ctar = SPI0_CTAR1;
+
+	    // clear the FMSZ bits
+	    ctar &= SPI_CTAR_FMSZ(0x0F);
+	    ctar |= SPI_CTAR_FMSZ((bits-1) & 0x0F);
+
+	    update_ctar1(ctar);
+    }
+
+    static inline void set_ctar0_bits(int bits) {
+	    // Set ctar1 to 16 bits
+	    int ctar = SPI0_CTAR1;
+
+	    // clear the FMSZ bits
+	    ctar &= SPI_CTAR_FMSZ(0x0F);
+	    ctar |= SPI_CTAR_FMSZ((bits-1) & 0x0F);
+
+	    update_ctar1(ctar);
+    }
+
+
+    void setSPIRate() {
+		// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
+	 	uint32_t _PBR = 0;
+	 	uint32_t _BR = 0;
+	 	uint32_t _CSSCK = 0;
+	 	uint32_t _DBR = 0;
+
+	 	// if(_SPI_CLOCK_DIVIDER >= 256) 		{ _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
+	 	// else if(_SPI_CLOCK_DIVIDER >= 128) 	{ _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
+	 	// else if(_SPI_CLOCK_DIVIDER >= 64) 	{ _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
+	 	// else if(_SPI_CLOCK_DIVIDER >= 32) 	{ _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
+	 	// else if(_SPI_CLOCK_DIVIDER >= 16) 	{ _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
+	 	// else if(_SPI_CLOCK_DIVIDER >= 8) 	{ _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
+	 	// else if(_SPI_CLOCK_DIVIDER >= 7) 	{ _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
+	 	// else if(_SPI_CLOCK_DIVIDER >= 5) 	{ _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
+	 	// else if(_SPI_CLOCK_DIVIDER >= 4) 	{ _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
+	 	// else if(_SPI_CLOCK_DIVIDER >= 3) 	{ _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
+	 	// else                                { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
+
+	 	getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
+	 	_CSSCK = _BR;
+
+	 	uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
+	 	uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
+
+#if USE_CONT == 1
+	 	ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
+	 	ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
+#endif
+
+	 	if(_DBR) {
+	 		ctar0 |= SPI_CTAR_DBR;
+	 		ctar1 |= SPI_CTAR_DBR;
+	 	}
+
+	    update_ctar0(ctar0);
+	    update_ctar1(ctar1);
+
+    }
+
+	void init() {
+		// set the pins to output
+		FastPin<_DATA_PIN>::setOutput();
+		FastPin<_CLOCK_PIN>::setOutput();
+		release();
+
+		// Enable SPI0 clock
+		uint32_t sim6 = SIM_SCGC6;
+		if (!(sim6 & SIM_SCGC6_SPI0)) {
+			//serial_print("init1\n");
+			SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
+			SPI0_CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
+		}
+
+		setSPIRate();
+
+		// Configure SPI as the master and enable
+		SPI0_MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
+		SPI0_MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
+
+		enable_pins();
+	}
+
+	static void waitFully() __attribute__((always_inline)) {
+		while( (SPI0_SR & 0xF000) > 0);
+		while (!(SPI0_SR & SPI_SR_TCF));
+		SPI0_SR |= (SPI_SR_TCF | SPI_SR_EOQF);
+	}
+
+	static bool needwait() __attribute__((always_inline)) { return (SPI0_SR & 0x4000); }
+	static void wait() __attribute__((always_inline)) { while( (SPI0_SR & 0x4000) );  }
+	static void wait1() __attribute__((always_inline)) { while( (SPI0_SR & 0xF000) >= 0x2000);  }
+
+	enum ECont { CONT, NOCONT };
+	enum EWait { PRE, POST, NONE };
+	enum ELast { NOTLAST, LAST };
+
+#if USE_CONT == 1
+	#define CM CONT
+#else
+	#define CM NOCONT
+#endif
+	#define WM PRE
+
+	template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
+	public:
+		static void writeWord(uint16_t w) __attribute__((always_inline)) {
+			if(WAIT_STATE == PRE) { wait(); }
+			SPI0_PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
+			             ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
+			             SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
+			if(WAIT_STATE == POST) { wait(); }
+		}
+
+		static void writeByte(uint8_t b) __attribute__((always_inline)) {
+			if(WAIT_STATE == PRE) { wait(); }
+			SPI0_PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
+			             ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
+			             SPI_PUSHR_CTAS(0) | (b & 0xFF);
+			if(WAIT_STATE == POST) { wait(); }
+		}
+	};
+
+	static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+	static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+
+	static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+	static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); wait(); }
+	static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+
+	static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+	static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+
+	static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+	static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); wait(); }
+	static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+
+	// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
+	template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+		uint32_t ctar1_save = SPI0_CTAR1;
+
+		// Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
+		uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0);
+		update_ctar1(ctar1);
+
+		writeWord( (b & (1 << BIT)) != 0);
+
+		update_ctar1(ctar1_save);
+	}
+
+	void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
+	void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
+
+	static void writeBytesValueRaw(uint8_t value, int len) {
+		while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
+	}
+
+	void writeBytesValue(uint8_t value, int len) {
+		setSPIRate();
+		select();
+		while(len--) {
+			writeByte(value);
+		}
+		waitFully();
+		release();
+	}
+
+	// Write a block of n uint8_ts out
+	template <class D> void writeBytes(register uint8_t *data, int len) {
+		setSPIRate();
+		uint8_t *end = data + len;
+		select();
+		while(data != end) {
+			writeByte(D::adjust(*data++));
+		}
+		D::postBlock(len);
+		waitFully();
+		release();
+	}
+
+	void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
+
+	// write a block of uint8_ts out in groups of three.  len is the total number of uint8_ts to write out.  The template
+	// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
+	template <uint8_t SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		// setSPIRate();
+		uint8_t *end = data + len;
+		select();
+		if((SKIP & FLAG_START_BIT) == 0) {
+			//If no start bit stupiditiy, write out as many 16-bit blocks as we can
+			uint8_t *first_end = end - (len % (SPI_ADVANCE * 2));
+
+			while(data != first_end) {
+				if(WM == NONE) { wait1(); }
+				Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_B0], scale) << 8 | D::adjust(data[SPI_B1], scale));
+				Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_B2], scale) << 8 | D::adjust(data[SPI_ADVANCE + SPI_B0], scale));
+				Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_ADVANCE + SPI_B1], scale) << 8 | D::adjust(data[SPI_ADVANCE + SPI_B2], scale));
+				data += (SPI_ADVANCE + SPI_ADVANCE);
+			}
+
+			if(data != end) {
+				if(WM == NONE) { wait1(); }
+				// write out the rest as alternating 16/8-bit blocks (likely to be just one)
+				Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_B0], scale) << 8 | D::adjust(data[SPI_B1], scale));
+				Write<CM, WM, NOTLAST>::writeByte(D::adjust(data[SPI_B2], scale));
+			}
+
+			D::postBlock(len);
+			waitFully();
+		} else if(SKIP & FLAG_START_BIT) {
+			uint32_t ctar1_save = SPI0_CTAR1;
+
+			// Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
+			uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8);
+			update_ctar1(ctar1);
+
+			while(data != end) {
+				writeWord( 0x100 | D::adjust(data[SPI_B0], scale));
+				writeByte(D::adjust(data[SPI_B1], scale));
+				writeByte(D::adjust(data[SPI_B2], scale));
+				data += SPI_ADVANCE;
+			}
+			D::postBlock(len);
+			waitFully();
+
+			// restore ctar1
+			update_ctar1(ctar1_save);
+		// } else {
+		// 	while(data != end) {
+		// 		writeByte(D::adjust(data[SPI_B0], scale);
+		// 		writeWord(D::adjust(data[SPI_B1], scale) << 8 | D::adjust(data[SPI_B2], scale));
+		// 		data += SPI_ADVANCE;
+		// 	}
+		// 	waitFully();
+		}
+		release();
+	}
+
+
+	template <uint8_t SKIP, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<SKIP, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	template <class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, D, RGB_ORDER>(data, len, scale);
+	}
+	template <EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB>(data, len, scale);
+	}
+};
+#endif
+
+#endif
diff --git a/lib/FastLED/fastspi_avr.h b/lib/FastLED/fastspi_avr.h
new file mode 100644
index 0000000..af116ca
--- /dev/null
+++ b/lib/FastLED/fastspi_avr.h
@@ -0,0 +1,314 @@
+#ifndef __INC_FASTSPI_AVR_H
+#define __INC_FASTSPI_AVR_H
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Hardware SPI support using USART registers and friends
+//
+// TODO: Complete/test implementation - right now this doesn't work
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// uno/mini/duemilanove
+#if defined(AVR_HARDWARE_SPI)
+#if defined(UBRR0)
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class AVRUSARTSPIOutput {
+	Selectable *m_pSelect;
+
+public:
+	AVRUSARTSPIOutput() { m_pSelect = NULL; }
+	AVRUSARTSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+	void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
+
+	void init() {
+		UBRR0 = 0;
+		UCSR0A = 1<<TXC0;
+
+		FastPin<_CLOCK_PIN>::setOutput();
+		FastPin<_DATA_PIN>::setOutput();
+
+		UCSR0C = _BV (UMSEL00) | _BV (UMSEL01);  // Master SPI mode
+		UCSR0B = _BV (TXEN0) | _BV (RXEN0);  // transmit enable and receive enable
+
+		// must be done last, see page 206
+		UBRR0 = 3;  // 2 Mhz clock rate
+	}
+
+	static void stop() {
+		// TODO: stop the uart spi output
+	}
+
+	static void wait() __attribute__((always_inline)) { while(!(UCSR0A & (1<<UDRE0))); }
+	static void waitFully() __attribute__((always_inline)) { wait(); }
+
+	static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR0 = b;}
+	static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR0 = b; wait(); }
+	static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR0 = b; }
+
+	static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
+
+	template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+		if(b && (1 << BIT)) {
+			FastPin<_DATA_PIN>::hi();
+		} else {
+			FastPin<_DATA_PIN>::lo();
+		}
+
+		FastPin<_CLOCK_PIN>::hi();
+		FastPin<_CLOCK_PIN>::lo();
+	}
+
+	void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<_SELECT_PIN>::hi(); }
+	void release() {
+		// wait for all transmissions to finish
+  		while ((UCSR0A & (1 <<TXC0)) == 0) {}
+    	if(m_pSelect != NULL) { m_pSelect->release(); } // FastPin<_SELECT_PIN>::hi();
+	}
+
+	static void writeBytesValueRaw(uint8_t value, int len) {
+		while(len--) { writeByte(value); }
+	}
+
+	void writeBytesValue(uint8_t value, int len) {
+		select();
+		while(len--) {
+			writeByte(value);
+		}
+		release();
+	}
+
+	// Write a block of n uint8_ts out
+	template <class D> void writeBytes(register uint8_t *data, int len) {
+		uint8_t *end = data + len;
+		select();
+		while(data != end) {
+#if defined(__MK20DX128__)
+			writeByte(D::adjust(*data++));
+#else
+			// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
+			writeByte(D::adjust(*data++)); delaycycles<3>();
+#endif
+		}
+		D::postBlock(len);
+		release();
+	}
+
+	void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
+
+	// write a block of uint8_ts out in groups of three.  len is the total number of uint8_ts to write out.  The template
+	// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
+	template <uint8_t SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		uint8_t *end = data + len;
+		select();
+		while(data != end) {
+			writeByte(D::adjust(data[SPI_B0], scale));
+			writeByte(D::adjust(data[SPI_B1], scale));
+			writeByte(D::adjust(data[SPI_B2], scale));
+			data += SPI_ADVANCE;
+		}
+		D::postBlock(len);
+		release();
+	}
+
+	template <uint8_t SKIP, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<SKIP, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	template <class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, D, RGB_ORDER>(data, len, scale);
+	}
+	template <EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB>(data, len, scale);
+	}
+
+};
+
+#endif
+
+#if defined(SPSR)
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Hardware SPI support using SPDR registers and friends
+//
+// Technically speaking, this uses the AVR SPI registers.  This will work on the Teensy 3.0 because Paul made a set of compatability
+// classes that map the AVR SPI registers to ARM's, however this caps the performance of output.
+//
+// TODO: implement ARMHardwareSPIOutput
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class AVRHardwareSPIOutput {
+	Selectable *m_pSelect;
+	bool mWait;
+public:
+	AVRHardwareSPIOutput() { m_pSelect = NULL; mWait = false;}
+	AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+	void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
+
+	void setSPIRate() {
+		SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); 	// clear out the prescalar bits
+
+	    bool b2x = false;
+
+	    if(_SPI_CLOCK_DIVIDER >= 128) { SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); }
+	    else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR |= (1<<SPR1);}
+	    else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR |= (1<<SPR1); b2x = true;  }
+	    else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
+	    else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR |= (1<<SPR0); b2x = true; }
+	    else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
+	    else { b2x = true; }
+
+	    if(b2x) { SPSR |= (1<<SPI2X); }
+	    else { SPSR &= ~ (1<<SPI2X); }
+	}
+
+	void init() {
+		volatile uint8_t clr;
+
+		// set the pins to output
+		FastPin<_DATA_PIN>::setOutput();
+		FastPin<_CLOCK_PIN>::setOutput();
+#ifdef SPI_SELECT
+		// Make sure the slave select line is set to output, or arduino will block us
+		FastPin<SPI_SELECT>::setOutput();
+		FastPin<SPI_SELECT>::lo();
+#endif
+		release();
+
+		SPCR |= ((1<<SPE) | (1<<MSTR) ); 		// enable SPI as master
+		SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); 	// clear out the prescalar bits
+
+		clr = SPSR; // clear SPI status register
+		clr = SPDR; // clear SPI data register
+		clr;
+
+	    bool b2x = false;
+
+	    if(_SPI_CLOCK_DIVIDER >= 128) { SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); }
+	    else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR |= (1<<SPR1);}
+	    else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR |= (1<<SPR1); b2x = true;  }
+	    else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
+	    else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR |= (1<<SPR0); b2x = true; }
+	    else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
+	    else { b2x = true; }
+
+	    if(b2x) { SPSR |= (1<<SPI2X); }
+	    else { SPSR &= ~ (1<<SPI2X); }
+
+	    SPDR=0;
+	    shouldWait(false);
+	}
+
+	static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
+		static bool sWait=false;
+		if(sWait) { sWait = wait; return true; } else { sWait = wait; return false; }
+		// return true;
+	}
+	static void wait() __attribute__((always_inline)) { if(shouldWait()) { while(!(SPSR & (1<<SPIF))); } }
+	static void waitFully() __attribute__((always_inline)) { wait(); }
+
+	static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPDR=b;  shouldWait(true); }
+	static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); wait(); }
+	static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); }
+
+	template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+		SPCR &= ~(1 << SPE);
+		if(b & (1 << BIT)) {
+			FastPin<_DATA_PIN>::hi();
+		} else {
+			FastPin<_DATA_PIN>::lo();
+		}
+
+		FastPin<_CLOCK_PIN>::hi();
+		FastPin<_CLOCK_PIN>::lo();
+		SPCR |= 1 << SPE;
+		shouldWait(false);
+	}
+
+	void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<_SELECT_PIN>::hi(); }
+	void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<_SELECT_PIN>::lo(); }
+
+	static void writeBytesValueRaw(uint8_t value, int len) {
+		while(len--) { writeByte(value); }
+	}
+
+	void writeBytesValue(uint8_t value, int len) {
+		//setSPIRate();
+		select();
+		while(len--) {
+			writeByte(value);
+		}
+		release();
+	}
+
+	// Write a block of n uint8_ts out
+	template <class D> void writeBytes(register uint8_t *data, int len) {
+		//setSPIRate();
+		uint8_t *end = data + len;
+		select();
+		while(data != end) {
+			// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
+			writeByte(D::adjust(*data++)); delaycycles<3>();
+		}
+		release();
+	}
+
+	void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
+
+	// write a block of uint8_ts out in groups of three.  len is the total number of uint8_ts to write out.  The template
+	// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
+	template <uint8_t SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		//setSPIRate();
+		uint8_t *end = data + len;
+		select();
+		while(data != end) {
+			if(SKIP & FLAG_START_BIT) {
+				writeBit<0>(1);
+			}
+			// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
+			if(false && _SPI_CLOCK_DIVIDER == 0) {
+				writeByteNoWait(D::adjust(data[SPI_B0], scale)); delaycycles<13>();
+				writeByteNoWait(D::adjust(data[SPI_B1], scale)); delaycycles<13>();
+				writeByteNoWait(D::adjust(data[SPI_B2], scale)); delaycycles<9>();
+			} else if(SKIP & FLAG_START_BIT) {
+				writeBytePostWait(D::adjust(data[SPI_B0], scale));
+				writeBytePostWait(D::adjust(data[SPI_B1], scale));
+				writeBytePostWait(D::adjust(data[SPI_B2], scale));
+			} else {
+				writeByte(D::adjust(data[SPI_B0], scale));
+				writeByte(D::adjust(data[SPI_B1], scale));
+				writeByte(D::adjust(data[SPI_B2], scale));
+			}
+
+			data += SPI_ADVANCE;
+		}
+		D::postBlock(len);
+		release();
+	}
+
+	template <uint8_t SKIP, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<SKIP, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	template <class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, D, RGB_ORDER>(data, len, scale);
+	}
+	template <EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB>(data, len, scale);
+	}
+
+};
+#endif
+
+#else
+// #define FORCE_SOFTWARE_SPI
+#endif
+
+#endif
\ No newline at end of file
diff --git a/lib/FastLED/fastspi_bitbang.h b/lib/FastLED/fastspi_bitbang.h
new file mode 100644
index 0000000..f9c1a21
--- /dev/null
+++ b/lib/FastLED/fastspi_bitbang.h
@@ -0,0 +1,368 @@
+#ifndef __INC_FASTSPI_BITBANG_H
+#define __INC_FASTSPI_BITBANG_H
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Software SPI (aka bit-banging) support - with aggressive optimizations for when the clock and data pin are on the same port
+//
+// TODO: Replace the select pin definition with a set of pins, to allow using mux hardware for routing in the future
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t SPI_SPEED>
+class AVRSoftwareSPIOutput {
+	// The data types for pointers to the pin port - typedef'd here from the Pin definition because on avr these
+	// are pointers to 8 bit values, while on arm they are 32 bit
+	typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
+	typedef typename FastPin<CLOCK_PIN>::port_ptr_t clock_ptr_t;
+
+	// The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports
+	// are 8 bits wide while on arm they are 32.
+	typedef typename FastPin<DATA_PIN>::port_t data_t;
+	typedef typename FastPin<CLOCK_PIN>::port_t clock_t;
+	Selectable 	*m_pSelect;
+
+public:
+	AVRSoftwareSPIOutput() { m_pSelect = NULL; }
+	AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+	void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
+
+	void init() {
+		// set the pins to output and make sure the select is released (which apparently means hi?  This is a bit
+		// confusing to me)
+		FastPin<DATA_PIN>::setOutput();
+		FastPin<CLOCK_PIN>::setOutput();
+		release();
+	}
+
+	// stop the SPI output.  Pretty much a NOP with software, as there's no registers to kick
+	static void stop() { }
+
+	// wait until the SPI subsystem is ready for more data to write.  A NOP when bitbanging
+	static void wait() __attribute__((always_inline)) { }
+	static void waitFully() __attribute__((always_inline)) { wait(); }
+
+	static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
+	static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
+
+	static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
+
+	// naive writeByte implelentation, simply calls writeBit on the 8 bits in the byte.
+	static void writeByte(uint8_t b) __attribute__((always_inline)) {
+		writeBit<7>(b);
+		writeBit<6>(b);
+		writeBit<5>(b);
+		writeBit<4>(b);
+		writeBit<3>(b);
+		writeBit<2>(b);
+		writeBit<1>(b);
+		writeBit<0>(b);
+	}
+
+private:
+	// writeByte implementation with data/clock registers passed in.
+	static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) __attribute__((always_inline)) {
+		writeBit<7>(b, clockpin, datapin);
+		writeBit<6>(b, clockpin, datapin);
+		writeBit<5>(b, clockpin, datapin);
+		writeBit<4>(b, clockpin, datapin);
+		writeBit<3>(b, clockpin, datapin);
+		writeBit<2>(b, clockpin, datapin);
+		writeBit<1>(b, clockpin, datapin);
+		writeBit<0>(b, clockpin, datapin);
+	}
+
+	// writeByte implementation with the data register passed in and prebaked values for data hi w/clock hi and
+	// low and data lo w/clock hi and lo.  This is to be used when clock and data are on the same GPIO register,
+	// can get close to getting a bit out the door in 2 clock cycles!
+	static void writeByte(uint8_t b, data_ptr_t datapin,
+						  data_t hival, data_t loval,
+						  clock_t hiclock, clock_t loclock) __attribute__((always_inline, hot)) {
+		writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
+		writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
+		writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
+		writeBit<4>(b, datapin, hival, loval, hiclock, loclock);
+		writeBit<3>(b, datapin, hival, loval, hiclock, loclock);
+		writeBit<2>(b, datapin, hival, loval, hiclock, loclock);
+		writeBit<1>(b, datapin, hival, loval, hiclock, loclock);
+		writeBit<0>(b, datapin, hival, loval, hiclock, loclock);
+	}
+
+	// writeByte implementation with not just registers passed in, but pre-baked values for said registers for
+	// data hi/lo and clock hi/lo values.  Note: weird things will happen if this method is called in cases where
+	// the data and clock pins are on the same port!  Don't do that!
+	static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
+						  data_t hival, data_t loval,
+						  clock_t hiclock, clock_t loclock) __attribute__((always_inline)) {
+		writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+		writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+		writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+		writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+		writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+		writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+		writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+		writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock);
+	}
+
+public:
+	#define SPI_DELAY delaycycles< (SPI_SPEED-2) / 2>();
+
+	// write the BIT'th bit out via spi, setting the data pin then strobing the clcok
+	template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
+		if(b & (1 << BIT)) {
+			FastPin<DATA_PIN>::hi();
+			if(SPI_SPEED < 3) {
+				FastPin<CLOCK_PIN>::strobe();
+			} else {
+				FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
+				FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
+			}
+		} else {
+			FastPin<DATA_PIN>::lo();
+			if(SPI_SPEED < 3) {
+				FastPin<CLOCK_PIN>::strobe();
+			} else {
+				FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
+				FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
+			}
+		}
+	}
+
+private:
+	// write the BIT'th bit out via spi, setting the data pin then strobing the clock, using the passed in pin registers to accelerate access if needed
+	template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
+		if(b & (1 << BIT)) {
+			FastPin<DATA_PIN>::hi(datapin);
+			FastPin<CLOCK_PIN>::hi(clockpin); SPI_DELAY;
+			FastPin<CLOCK_PIN>::lo(clockpin); SPI_DELAY;
+		} else {
+			FastPin<DATA_PIN>::lo(datapin);
+			FastPin<CLOCK_PIN>::hi(clockpin); SPI_DELAY;
+			FastPin<CLOCK_PIN>::lo(clockpin); SPI_DELAY;
+		}
+
+	}
+
+	// the version of write to use when clock and data are on separate pins with precomputed values for setting
+	// the clock and data pins
+	template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
+													data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
+		// // only need to explicitly set clock hi if clock and data are on different ports
+		if(b & (1 << BIT)) {
+			FastPin<DATA_PIN>::fastset(datapin, hival);
+			FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); SPI_DELAY;
+			FastPin<CLOCK_PIN>::fastset(clockpin, loclock); SPI_DELAY;
+		} else {
+			// NOP;
+			FastPin<DATA_PIN>::fastset(datapin, loval);
+			FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); SPI_DELAY;
+			FastPin<CLOCK_PIN>::fastset(clockpin, loclock); SPI_DELAY;
+		}
+	}
+
+	// the version of write to use when clock and data are on the same port with precomputed values for the various
+	// combinations
+	template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, data_ptr_t clockdatapin,
+													data_t datahiclockhi, data_t dataloclockhi,
+													data_t datahiclocklo, data_t dataloclocklo) {
+#if 0
+		writeBit<BIT>(b);
+#else
+		if(b & (1 << BIT)) {
+			FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY;
+			FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); SPI_DELAY;
+			FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY;
+		} else {
+			// NOP;
+			FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY;
+			FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); SPI_DELAY;
+			FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY;
+		}
+#endif
+	}
+public:
+
+	// select the SPI output (TODO: research whether this really means hi or lo.  Alt TODO: move select responsibility out of the SPI classes
+	// entirely, make it up to the caller to remember to lock/select the line?)
+	void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<SELECT_PIN>::hi(); }
+
+	// release the SPI line
+	void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::lo(); }
+
+	// Write out len bytes of the given value out over SPI.  Useful for quickly flushing, say, a line of 0's down the line.
+	void writeBytesValue(uint8_t value, int len) {
+		select();
+		writeBytesValueRaw(value, len);
+		release();
+	}
+
+	static void writeBytesValueRaw(uint8_t value, int len) {
+#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
+		// TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled.  Need
+		// to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall
+		// back to the degenerative code below
+		while(len--) {
+			writeByte(value);
+		}
+#else
+		register data_ptr_t datapin = FastPin<DATA_PIN>::port();
+
+		if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
+			// If data and clock are on different ports, then writing a bit will consist of writing the value foor
+			// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
+			register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
+			register data_t datahi = FastPin<DATA_PIN>::hival();
+			register data_t datalo = FastPin<DATA_PIN>::loval();
+			register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
+			register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
+			while(len--) {
+				writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo);
+			}
+
+		} else {
+			// If data and clock are on the same port then we can combine setting the data and clock pins
+			register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
+			register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
+			register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
+			register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
+
+			while(len--) {
+				writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+			}
+		}
+#endif
+	}
+
+	// write a block of len uint8_ts out.  Need to type this better so that explicit casts into the call aren't required.
+	// note that this template version takes a class parameter for a per-byte modifier to the data.
+	template <class D> void writeBytes(register uint8_t *data, int len) {
+		select();
+#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
+		uint8_t *end = data + len;
+		while(data != end) {
+			writeByte(D::adjust(*data++));
+		}
+#else
+		register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
+		register data_ptr_t datapin = FastPin<DATA_PIN>::port();
+
+		if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
+			// If data and clock are on different ports, then writing a bit will consist of writing the value foor
+			// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
+			register data_t datahi = FastPin<DATA_PIN>::hival();
+			register data_t datalo = FastPin<DATA_PIN>::loval();
+			register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
+			register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
+			uint8_t *end = data + len;
+
+			while(data != end) {
+				writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
+			}
+
+		} else {
+			// FastPin<CLOCK_PIN>::hi();
+			// If data and clock are on the same port then we can combine setting the data and clock pins
+			register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
+			register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
+			register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
+			register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
+
+			uint8_t *end = data + len;
+
+			while(data != end) {
+				writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+			}
+			// FastPin<CLOCK_PIN>::lo();
+		}
+#endif
+		D::postBlock(len);
+		release();
+	}
+
+	// default version of writing a block of data out to the SPI port, with no data modifications being made
+	void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
+
+
+	// write a block of uint8_ts out in groups of three.  len is the total number of uint8_ts to write out.  The template
+	// parameters indicate how many uint8_ts to skip at the beginning of each grouping, as well as a class specifying a per
+	// byte of data modification to be made.  (See DATA_NOP above)
+	template <uint8_t SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		select();
+
+#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
+		// If interrupts or other things may be generating output while we're working on things, then we need
+		// to use this block
+		uint8_t *end = data + len;
+		while(data != end) {
+			if(SKIP & FLAG_START_BIT) {
+				writeBit<0>(1);
+			}
+			writeByte(D::adjust(data[SPI_B0], scale));
+			writeByte(D::adjust(data[SPI_B1], scale));
+			writeByte(D::adjust(data[SPI_B2], scale));
+			data += SPI_ADVANCE;
+		}
+#else
+		// If we can guaruntee that no one else will be writing data while we are running (namely, changing the values of the PORT/PDOR pins)
+		// then we can use a bunch of optimizations in here
+		register data_ptr_t datapin = FastPin<DATA_PIN>::port();
+
+		if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
+			register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
+			// If data and clock are on different ports, then writing a bit will consist of writing the value foor
+			// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
+			register data_t datahi = FastPin<DATA_PIN>::hival();
+			register data_t datalo = FastPin<DATA_PIN>::loval();
+			register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
+			register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
+			uint8_t *end = data + len;
+
+			while(data != end) {
+				if(SKIP & FLAG_START_BIT) {
+					writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
+				}
+				writeByte(D::adjust(data[SPI_B0], scale), clockpin, datapin, datahi, datalo, clockhi, clocklo);
+				writeByte(D::adjust(data[SPI_B1], scale), clockpin, datapin, datahi, datalo, clockhi, clocklo);
+				writeByte(D::adjust(data[SPI_B2], scale), clockpin, datapin, datahi, datalo, clockhi, clocklo);
+				data += SPI_ADVANCE;
+			}
+
+		} else {
+			// If data and clock are on the same port then we can combine setting the data and clock pins
+			register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
+			register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
+			register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
+			register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
+
+			uint8_t *end = data + len;
+
+			while(data != end) {
+				if(SKIP & FLAG_START_BIT) {
+					writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+				}
+				writeByte(D::adjust(data[SPI_B0], scale), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+				writeByte(D::adjust(data[SPI_B1], scale), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+				writeByte(D::adjust(data[SPI_B2], scale), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+				data += SPI_ADVANCE;
+			}
+		}
+#endif
+		D::postBlock(len);
+		release();
+	}
+
+	template <uint8_t SKIP, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<SKIP, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	template <class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, D, RGB_ORDER>(data, len, scale);
+	}
+	template <EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB_ORDER>(data, len, scale);
+	}
+	void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+		writeBytes3<0, DATA_NOP, RGB>(data, len, scale);
+	}
+};
+
+#endif
diff --git a/lib/FastLED/fastspi_dma.h b/lib/FastLED/fastspi_dma.h
new file mode 100644
index 0000000..e69de29
diff --git a/lib/FastLED/hsv2rgb.cpp b/lib/FastLED/hsv2rgb.cpp
new file mode 100644
index 0000000..b0abdab
--- /dev/null
+++ b/lib/FastLED/hsv2rgb.cpp
@@ -0,0 +1,495 @@
+#include <stdint.h>
+
+#include "lib8tion.h"
+#include "hsv2rgb.h"
+
+// Functions to convert HSV colors to RGB colors.
+//
+//  The basically fall into two groups: spectra, and rainbows.
+//  Spectra and rainbows are not the same thing.  Wikipedia has a good
+//  illustration here
+//   http://upload.wikimedia.org/wikipedia/commons/f/f6/Prism_compare_rainbow_01.png
+//  from this article
+//   http://en.wikipedia.org/wiki/Rainbow#Number_of_colours_in_spectrum_or_rainbow
+//  that shows a 'spectrum' and a 'rainbow' side by side.  Among other
+//  differences, you'll see that a 'rainbow' has much more yellow than
+//  a plain spectrum.  "Classic" LED color washes are spectrum based, and
+//  usually show very little yellow.
+//
+//  Wikipedia's page on HSV color space, with pseudocode for conversion
+//  to RGB color space
+//   http://en.wikipedia.org/wiki/HSL_and_HSV
+//  Note that their conversion algorithm, which is (naturally) very popular
+//  is in the "maximum brightness at any given hue" style, vs the "uniform
+//  brightness for all hues" style.
+//
+//  You can't have both; either purple is the same brightness as red, e.g
+//    red = #FF0000 and purple = #800080 -> same "total light" output
+//  OR purple is 'as bright as it can be', e.g.
+//    red = #FF0000 and purple = #FF00FF -> purple is much brighter than red.
+//  The colorspace conversions here try to keep the apparent brightness
+//  constant even as the hue varies.
+//
+//  Adafruit's "Wheel" function, discussed here
+//   http://forums.adafruit.com/viewtopic.php?f=47&t=22483
+//  is also of the "constant apparent brightness" variety.
+//
+//  TODO: provide the 'maximum brightness no matter what' variation.
+//
+//  See also some good, clear Arduino C code from Kasper Kamperman
+//   http://www.kasperkamperman.com/blog/arduino/arduino-programming-hsb-to-rgb/
+//  which in turn was was based on Windows C code from "nico80"
+//   http://www.codeproject.com/Articles/9207/An-HSB-RGBA-colour-picker
+
+
+
+
+
+void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb);
+void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb);
+
+#if defined(__AVR__) && !defined( LIB8_ATTINY )
+void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb)
+{
+    hsv2rgb_raw_avr( hsv, rgb);
+}
+#else
+void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb)
+{
+    hsv2rgb_raw_C( hsv, rgb);
+}
+#endif
+
+
+
+#define APPLY_DIMMING(X) (X)
+#define HSV_SECTION_6 (0x20)
+#define HSV_SECTION_3 (0x40)
+
+void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb)
+{
+    // Convert hue, saturation and brightness ( HSV/HSB ) to RGB
+    // "Dimming" is used on saturation and brightness to make
+    // the output more visually linear.
+
+    // Apply dimming curves
+    uint8_t value = APPLY_DIMMING( hsv.val);
+    uint8_t saturation = hsv.sat;
+
+    // The brightness floor is minimum number that all of
+    // R, G, and B will be set to.
+    uint8_t invsat = APPLY_DIMMING( 255 - saturation);
+    uint8_t brightness_floor = (value * invsat) / 256;
+
+    // The color amplitude is the maximum amount of R, G, and B
+    // that will be added on top of the brightness_floor to
+    // create the specific hue desired.
+    uint8_t color_amplitude = value - brightness_floor;
+
+    // Figure out which section of the hue wheel we're in,
+    // and how far offset we are withing that section
+    uint8_t section = hsv.hue / HSV_SECTION_3; // 0..2
+    uint8_t offset = hsv.hue % HSV_SECTION_3;  // 0..63
+
+    uint8_t rampup = offset; // 0..63
+    uint8_t rampdown = (HSV_SECTION_3 - 1) - offset; // 63..0
+
+    // We now scale rampup and rampdown to a 0-255 range -- at least
+    // in theory, but here's where architecture-specific decsions
+    // come in to play:
+    // To scale them up to 0-255, we'd want to multiply by 4.
+    // But in the very next step, we multiply the ramps by other
+    // values and then divide the resulting product by 256.
+    // So which is faster?
+    //   ((ramp * 4) * othervalue) / 256
+    // or
+    //   ((ramp    ) * othervalue) /  64
+    // It depends on your processor architecture.
+    // On 8-bit AVR, the "/ 256" is just a one-cycle register move,
+    // but the "/ 64" might be a multicycle shift process. So on AVR
+    // it's faster do multiply the ramp values by four, and then
+    // divide by 256.
+    // On ARM, the "/ 256" and "/ 64" are one cycle each, so it's
+    // faster to NOT multiply the ramp values by four, and just to
+    // divide the resulting product by 64 (instead of 256).
+    // Moral of the story: trust your profiler, not your insticts.
+
+    // Since there's an AVR assembly version elsewhere, we'll
+    // assume what we're on an architecture where any number of
+    // bit shifts has roughly the same cost, and we'll remove the
+    // redundant math at the source level:
+
+    //  // scale up to 255 range
+    //  //rampup *= 4; // 0..252
+    //  //rampdown *= 4; // 0..252
+
+    // compute color-amplitude-scaled-down versions of rampup and rampdown
+    uint8_t rampup_amp_adj   = (rampup   * color_amplitude) / (256 / 4);
+    uint8_t rampdown_amp_adj = (rampdown * color_amplitude) / (256 / 4);
+
+    // add brightness_floor offset to everything
+    uint8_t rampup_adj_with_floor   = rampup_amp_adj   + brightness_floor;
+    uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
+
+
+    if( section ) {
+        if( section == 1) {
+            // section 1: 0x40..0x7F
+            rgb.r = brightness_floor;
+            rgb.g = rampdown_adj_with_floor;
+            rgb.b = rampup_adj_with_floor;
+        } else {
+            // section 2; 0x80..0xBF
+            rgb.r = rampup_adj_with_floor;
+            rgb.g = brightness_floor;
+            rgb.b = rampdown_adj_with_floor;
+        }
+    } else {
+        // section 0: 0x00..0x3F
+        rgb.r = rampdown_adj_with_floor;
+        rgb.g = rampup_adj_with_floor;
+        rgb.b = brightness_floor;
+    }
+}
+
+
+
+#if defined(__AVR__) && !defined( LIB8_ATTINY )
+void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
+{
+    uint8_t hue, saturation, value;
+
+    hue =        hsv.hue;
+    saturation = hsv.sat;
+    value =      hsv.val;
+
+    // Saturation more useful the other way around
+    saturation = 255 - saturation;
+    uint8_t invsat = APPLY_DIMMING( saturation );
+
+    // Apply dimming curves
+    value = APPLY_DIMMING( value );
+
+    // The brightness floor is minimum number that all of
+    // R, G, and B will be set to, which is value * invsat
+    uint8_t brightness_floor;
+
+    asm volatile(
+                 "mul %[value], %[invsat]            \n"
+                 "mov %[brightness_floor], r1        \n"
+                 : [brightness_floor] "=r" (brightness_floor)
+                 : [value] "r" (value),
+                 [invsat] "r" (invsat)
+                 : "r0", "r1"
+                 );
+
+    // The color amplitude is the maximum amount of R, G, and B
+    // that will be added on top of the brightness_floor to
+    // create the specific hue desired.
+    uint8_t color_amplitude = value - brightness_floor;
+
+    // Figure how far we are offset into the section of the
+    // color wheel that we're in
+    uint8_t offset = hsv.hue & (HSV_SECTION_3 - 1);  // 0..63
+    uint8_t rampup = offset * 4; // 0..252
+
+
+    // compute color-amplitude-scaled-down versions of rampup and rampdown
+    uint8_t rampup_amp_adj;
+    uint8_t rampdown_amp_adj;
+
+    asm volatile(
+                 "mul %[rampup], %[color_amplitude]       \n"
+                 "mov %[rampup_amp_adj], r1               \n"
+                 "com %[rampup]                           \n"
+                 "mul %[rampup], %[color_amplitude]       \n"
+                 "mov %[rampdown_amp_adj], r1             \n"
+                 : [rampup_amp_adj] "=&r" (rampup_amp_adj),
+                 [rampdown_amp_adj] "=&r" (rampdown_amp_adj),
+                 [rampup] "+r" (rampup)
+                 : [color_amplitude] "r" (color_amplitude)
+                 : "r0", "r1"
+                 );
+
+
+    // add brightness_floor offset to everything
+    uint8_t rampup_adj_with_floor   = rampup_amp_adj   + brightness_floor;
+    uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
+
+
+    // keep gcc from using "X" as the index register for storing
+    // results back in the return structure.  AVR's X register can't
+    // do "std X+q, rnn", but the Y and Z registers can.
+    // if the pointer to 'rgb' is in X, gcc will add all kinds of crazy
+    // extra instructions.  Simply killing X here seems to help it
+    // try Y or Z first.
+    asm volatile(  ""  :  :  : "r26", "r27" );
+
+
+    if( hue & 0x80 ) {
+        // section 2: 0x80..0xBF
+        rgb.r = rampup_adj_with_floor;
+        rgb.g = brightness_floor;
+        rgb.b = rampdown_adj_with_floor;
+    } else {
+        if( hue & 0x40) {
+            // section 1: 0x40..0x7F
+            rgb.r = brightness_floor;
+            rgb.g = rampdown_adj_with_floor;
+            rgb.b = rampup_adj_with_floor;
+        } else {
+            // section 0: 0x00..0x3F
+            rgb.r = rampdown_adj_with_floor;
+            rgb.g = rampup_adj_with_floor;
+            rgb.b = brightness_floor;
+        }
+    }
+
+    cleanup_R1();
+}
+// End of AVR asm implementation
+
+#endif
+
+void hsv2rgb_spectrum( const CHSV& hsv, CRGB& rgb)
+{
+    CHSV hsv2(hsv);
+    hsv2.hue = scale8( hsv2.hue, 192);
+    hsv2rgb_raw(hsv2, rgb);
+}
+
+
+// Sometimes the compiler will do clever things to reduce
+// code size that result in a net slowdown, if it thinks that
+// a variable is not used in a certain location.
+// This macro does its best to convince the compiler that
+// the variable is used in this location, to help control
+// code motion and de-duplication that would result in a slowdown.
+#define FORCE_REFERENCE(var)  asm volatile( "" : : "r" (var) )
+
+
+#define K255 255
+#define K171 171
+#define K85  85
+
+void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
+{
+    // Yellow has a higher inherent brightness than
+    // any other color; 'pure' yellow is perceived to
+    // be 93% as bright as white.  In order to make
+    // yellow appear the correct relative brightness,
+    // it has to be rendered brighter than all other
+    // colors.
+    // Level Y1 is a moderate boost, the default.
+    // Level Y2 is a strong boost.
+    const uint8_t Y1 = 1;
+    const uint8_t Y2 = 0;
+
+    // G2: Whether to divide all greens by two.
+    // Depends GREATLY on your particular LEDs
+    const uint8_t G2 = 0;
+
+    // Gscale: what to scale green down by.
+    // Depends GREATLY on your particular LEDs
+    const uint8_t Gscale = 0;
+
+
+    uint8_t hue = hsv.hue;
+    uint8_t sat = hsv.sat;
+    uint8_t val = hsv.val;
+
+    uint8_t offset = hue & 0x1F; // 0..31
+
+    // offset8 = offset * 8
+    uint8_t offset8 = offset;
+    {
+        offset8 <<= 1;
+        asm volatile("");
+        offset8 <<= 1;
+        asm volatile("");
+        offset8 <<= 1;
+    }
+
+    uint8_t third = scale8( offset8, (256 / 3));
+
+    uint8_t r, g, b;
+
+    if( ! (hue & 0x80) ) {
+        // 0XX
+        if( ! (hue & 0x40) ) {
+            // 00X
+            //section 0-1
+            if( ! (hue & 0x20) ) {
+                // 000
+                //case 0: // R -> O
+                r = K255 - third;
+                g = third;
+                b = 0;
+                FORCE_REFERENCE(b);
+            } else {
+                // 001
+                //case 1: // O -> Y
+                if( Y1 ) {
+                    r = K171;
+                    g = K85 + third ;
+                    b = 0;
+                    FORCE_REFERENCE(b);
+                }
+                if( Y2 ) {
+                    r = K171 + third;
+                    uint8_t twothirds = (third << 1);
+                    g = K85 + twothirds;
+                    b = 0;
+                    FORCE_REFERENCE(b);
+                }
+            }
+        } else {
+            //01X
+            // section 2-3
+            if( !  (hue & 0x20) ) {
+                // 010
+                //case 2: // Y -> G
+                if( Y1 ) {
+                    uint8_t twothirds = (third << 1);
+                    r = K171 - twothirds;
+                    g = K171 + third;
+                    b = 0;
+                    FORCE_REFERENCE(b);
+                }
+                if( Y2 ) {
+                    r = K255 - offset8;
+                    g = K255;
+                    b = 0;
+                    FORCE_REFERENCE(b);
+                }
+            } else {
+                // 011
+                // case 3: // G -> A
+                r = 0;
+                FORCE_REFERENCE(r);
+                g = K255 - third;
+                b = third;
+            }
+        }
+    } else {
+        // section 4-7
+        // 1XX
+        if( ! (hue & 0x40) ) {
+            // 10X
+            if( ! ( hue & 0x20) ) {
+                // 100
+                //case 4: // A -> B
+                r = 0;
+                FORCE_REFERENCE(r);
+                uint8_t twothirds = (third << 1);
+                g = K171 - twothirds;
+                b = K85  + twothirds;
+
+            } else {
+                // 101
+                //case 5: // B -> P
+                r = third;
+                g = 0;
+                FORCE_REFERENCE(g);
+                b = K255 - third;
+
+            }
+        } else {
+            if( !  (hue & 0x20)  ) {
+                // 110
+                //case 6: // P -- K
+                r = K85 + third;
+                g = 0;
+                FORCE_REFERENCE(g);
+                b = K171 - third;
+
+            } else {
+                // 111
+                //case 7: // K -> R
+                r = K171 + third;
+                g = 0;
+                FORCE_REFERENCE(g);
+                b = K85 - third;
+
+            }
+        }
+    }
+
+    // This is one of the good places to scale the green down,
+    // although the client can scale green down as well.
+    if( G2 ) g = g >> 1;
+    if( Gscale ) g = scale8_video_LEAVING_R1_DIRTY( g, Gscale);
+
+    // Scale down colors if we're desaturated at all
+    // and add the brightness_floor to r, g, and b.
+    if( sat != 255 ) {
+
+        nscale8x3_video( r, g, b, sat);
+
+        uint8_t desat = 255 - sat;
+        desat = scale8( desat, desat);
+
+        uint8_t brightness_floor = desat;
+        r += brightness_floor;
+        g += brightness_floor;
+        b += brightness_floor;
+    }
+
+    // Now scale everything down if we're at value < 255.
+    if( val != 255 ) {
+
+        val = scale8_video_LEAVING_R1_DIRTY( val, val);
+        nscale8x3_video( r, g, b, val);
+    }
+
+    // Here we have the old AVR "missing std X+n" problem again
+    // It turns out that fixing it winds up costing more than
+    // not fixing it.
+    // To paraphrase Dr Bronner, profile! profile! profile!
+    //asm volatile(  ""  :  :  : "r26", "r27" );
+    //asm volatile (" movw r30, r26 \n" : : : "r30", "r31");
+    rgb.r = r;
+    rgb.g = g;
+    rgb.b = b;
+}
+
+
+void hsv2rgb_raw(const struct CHSV * phsv, struct CRGB * prgb, int numLeds) {
+    for(int i = 0; i < numLeds; i++) {
+        hsv2rgb_raw(phsv[i], prgb[i]);
+    }
+}
+
+void hsv2rgb_rainbow( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) {
+    for(int i = 0; i < numLeds; i++) {
+        hsv2rgb_rainbow(phsv[i], prgb[i]);
+    }
+}
+
+void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) {
+    for(int i = 0; i < numLeds; i++) {
+        hsv2rgb_spectrum(phsv[i], prgb[i]);
+    }
+}
+
+void fill_solid( struct CRGB * pFirstLED, int numToFill,
+                const struct CRGB& color)
+{
+    for( int i = 0; i < numToFill; i++) {
+        pFirstLED[i] = color;
+    }
+}
+
+void fill_rainbow( struct CRGB * pFirstLED, int numToFill,
+                  uint8_t initialhue,
+                  uint8_t deltahue )
+{
+    CHSV hsv;
+    hsv.hue = initialhue;
+    hsv.val = 255;
+    hsv.sat = 255;
+    for( int i = 0; i < numToFill; i++) {
+        hsv2rgb_rainbow( hsv, pFirstLED[i]);
+        hsv.hue += deltahue;
+    }
+}
diff --git a/lib/FastLED/hsv2rgb.h b/lib/FastLED/hsv2rgb.h
new file mode 100644
index 0000000..d30e9ae
--- /dev/null
+++ b/lib/FastLED/hsv2rgb.h
@@ -0,0 +1,59 @@
+#ifndef __INC_HSV2RGB_H
+#define __INC_HSV2RGB_H
+
+#include "pixeltypes.h"
+
+
+// hsv2rgb_rainbow - convert a hue, saturation, and value to RGB
+//                   using a visually balanced rainbow (vs a straight
+//                   mathematical spectrum).
+//                   This 'rainbow' yields better yellow and orange
+//                   than a straight 'spectrum'.
+//
+//                   NOTE: here hue is 0-255, not just 0-191
+
+void hsv2rgb_rainbow( const struct CHSV& hsv, struct CRGB& rgb);
+void hsv2rgb_rainbow( const struct CHSV* phsv, struct CRGB * prgb, int numLeds);
+#define HUE_MAX_RAINBOW 255
+
+
+// hsv2rgb_spectrum - convert a hue, saturation, and value to RGB
+//                    using a mathematically straight spectrum (vs
+//                    a visually balanced rainbow).
+//                    This 'spectrum' will have more green & blue
+//                    than a 'rainbow', and less yellow and orange.
+//
+//                    NOTE: here hue is 0-255, not just 0-191
+
+void hsv2rgb_spectrum( const struct CHSV& hsv, struct CRGB& rgb);
+void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds);
+#define HUE_MAX_SPECTRUM 255
+
+
+// hsv2rgb_raw - convert hue, saturation, and value to RGB.
+//               This 'spectrum' conversion will be more green & blue
+//               than a real 'rainbow', and the hue is specified just
+//               in the range 0-191.  Together, these result in a
+//               slightly faster conversion speed, at the expense of
+//               color balance.
+//
+//               NOTE: Hue is 0-191 only!
+//               Saturation & value are 0-255 each.
+//
+
+void hsv2rgb_raw(const struct CHSV& hsv, struct CRGB & rgb);
+void hsv2rgb_raw(const struct CHSV* phsv, struct CRGB * prgb, int numLeds);
+#define HUE_MAX 191
+
+
+// fill_solid -   fill a range of LEDs with a solid color
+void fill_solid( struct CRGB * pFirstLED, int numToFill,
+                 const struct CRGB& color);
+
+// fill_rainbow - fill a range of LEDs with a rainbow of colors, at
+//                full saturation and full value (brightness)
+void fill_rainbow( struct CRGB * pFirstLED, int numToFill,
+                   uint8_t initialhue,
+                   uint8_t deltahue = 5);
+
+#endif
diff --git a/lib/FastLED/keywords.txt b/lib/FastLED/keywords.txt
new file mode 100644
index 0000000..40ba2bd
--- /dev/null
+++ b/lib/FastLED/keywords.txt
@@ -0,0 +1,254 @@
+#######################################
+# Syntax Coloring Map For FastLED
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+CFastLED	KEYWORD1
+CHSV	KEYWORD1
+CRGB	KEYWORD1
+LEDS	KEYWORD1
+FastLED	KEYWORD1
+FastPin	KEYWORD1
+FastSPI	KEYWORD1
+FastSPI_LED2	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+# FastLED methods
+addLeds	KEYWORD2
+setBrightness 	KEYWORD2
+getBrightness 	KEYWORD2
+show 	KEYWORD2
+clear 	KEYWORD2
+showColor 	KEYWORD2
+
+# Lib8tion methods
+qadd8	KEYWORD2
+qadd7	KEYWORD2
+qsub8	KEYWORD2
+add8	KEYWORD2
+sub8	KEYWORD2
+scale8	KEYWORD2
+scale8_video	KEYWORD2
+scale8_LEAVING_R1_DIRTY	KEYWORD2
+nscale8_LEAVING_R1_DIRTY	KEYWORD2
+scale8_video_LEAVING_R1_DIRTY	KEYWORD2
+cleanup_R1	KEYWORD2
+nscale8x3	KEYWORD2
+nscale8x3_video	KEYWORD2
+nscale8x2	KEYWORD2
+nscale8x2_video	KEYWORD2
+scale16by8	KEYWORD2
+scale16by8	KEYWORD2
+scale16	KEYWORD2
+mul8	KEYWORD2
+qmul8	KEYWORD2
+abs8	KEYWORD2
+dim8_raw	KEYWORD2
+dim8_video	KEYWORD2
+brighten8_raw	KEYWORD2
+brighten8_video	KEYWORD2
+random8	KEYWORD2
+random16	KEYWORD2
+random8	KEYWORD2
+random8	KEYWORD2
+random16	KEYWORD2
+random16	KEYWORD2
+random16_set_seed	KEYWORD2
+random16_get_seed	KEYWORD2
+random16_add_entropy	KEYWORD2
+sin16_avr	KEYWORD2
+sin16_C	KEYWORD2
+cos16	KEYWORD2
+lerp8by8	KEYWORD2
+lerp16by16	KEYWORD2
+lerp16by8	KEYWORD2
+lerp15by8	KEYWORD2
+ease8InOutCubic	KEYWORD2
+ease8InOutApprox	KEYWORD2
+ease8InOutApprox	KEYWORD2
+
+# HSV methods
+hsv2grb_rainbow	KEYWORD2
+hsv2rgb_spectrum	KEYWORD2
+hsv2rgb_raw	KEYWORD2
+fill_solid	KEYWORD2
+fill_rainbow	KEYWORD2
+
+# Colors
+CRGB::AliceBlue	KEYWORD2
+CRGB::Amethyst	KEYWORD2
+CRGB::AntiqueWhite	KEYWORD2
+CRGB::Aqua	KEYWORD2
+CRGB::Aquamarine	KEYWORD2
+CRGB::Azure	KEYWORD2
+CRGB::Beige	KEYWORD2
+CRGB::Bisque	KEYWORD2
+CRGB::Black	KEYWORD2
+CRGB::BlanchedAlmond	KEYWORD2
+CRGB::Blue	KEYWORD2
+CRGB::BlueViolet	KEYWORD2
+CRGB::Brown	KEYWORD2
+CRGB::BurlyWood	KEYWORD2
+CRGB::CadetBlue	KEYWORD2
+CRGB::Chartreuse	KEYWORD2
+CRGB::Chocolate	KEYWORD2
+CRGB::Coral	KEYWORD2
+CRGB::CornflowerBlue	KEYWORD2
+CRGB::Cornsilk	KEYWORD2
+CRGB::Crimson	KEYWORD2
+CRGB::Cyan	KEYWORD2
+CRGB::DarkBlue	KEYWORD2
+CRGB::DarkCyan	KEYWORD2
+CRGB::DarkGoldenrod	KEYWORD2
+CRGB::DarkGray	KEYWORD2
+CRGB::DarkGreen	KEYWORD2
+CRGB::DarkKhaki	KEYWORD2
+CRGB::DarkMagenta	KEYWORD2
+CRGB::DarkOliveGreen	KEYWORD2
+CRGB::DarkOrange	KEYWORD2
+CRGB::DarkOrchid	KEYWORD2
+CRGB::DarkRed	KEYWORD2
+CRGB::DarkSalmon	KEYWORD2
+CRGB::DarkSeaGreen	KEYWORD2
+CRGB::DarkSlateBlue	KEYWORD2
+CRGB::DarkSlateGray	KEYWORD2
+CRGB::DarkTurquoise	KEYWORD2
+CRGB::DarkViolet	KEYWORD2
+CRGB::DeepPink	KEYWORD2
+CRGB::DeepSkyBlue	KEYWORD2
+CRGB::DimGray	KEYWORD2
+CRGB::DodgerBlue	KEYWORD2
+CRGB::FireBrick	KEYWORD2
+CRGB::FloralWhite	KEYWORD2
+CRGB::ForestGreen	KEYWORD2
+CRGB::Fuchsia	KEYWORD2
+CRGB::Gainsboro	KEYWORD2
+CRGB::GhostWhite	KEYWORD2
+CRGB::Gold	KEYWORD2
+CRGB::Goldenrod	KEYWORD2
+CRGB::Gray	KEYWORD2
+CRGB::Green	KEYWORD2
+CRGB::GreenYellow	KEYWORD2
+CRGB::Honeydew	KEYWORD2
+CRGB::HotPink	KEYWORD2
+CRGB::IndianRed	KEYWORD2
+CRGB::Indigo	KEYWORD2
+CRGB::Ivory	KEYWORD2
+CRGB::Khaki	KEYWORD2
+CRGB::Lavender	KEYWORD2
+CRGB::LavenderBlush	KEYWORD2
+CRGB::LawnGreen	KEYWORD2
+CRGB::LemonChiffon	KEYWORD2
+CRGB::LightBlue	KEYWORD2
+CRGB::LightCoral	KEYWORD2
+CRGB::LightCyan	KEYWORD2
+CRGB::LightGoldenrodYellow	KEYWORD2
+CRGB::LightGreen	KEYWORD2
+CRGB::LightGrey	KEYWORD2
+CRGB::LightPink	KEYWORD2
+CRGB::LightSalmon	KEYWORD2
+CRGB::LightSeaGreen	KEYWORD2
+CRGB::LightSkyBlue	KEYWORD2
+CRGB::LightSlateGray	KEYWORD2
+CRGB::LightSteelBlue	KEYWORD2
+CRGB::LightYellow	KEYWORD2
+CRGB::Lime	KEYWORD2
+CRGB::LimeGreen	KEYWORD2
+CRGB::Linen	KEYWORD2
+CRGB::Magenta	KEYWORD2
+CRGB::Maroon	KEYWORD2
+CRGB::MediumAquamarine	KEYWORD2
+CRGB::MediumBlue	KEYWORD2
+CRGB::MediumOrchid	KEYWORD2
+CRGB::MediumPurple	KEYWORD2
+CRGB::MediumSeaGreen	KEYWORD2
+CRGB::MediumSlateBlue	KEYWORD2
+CRGB::MediumSpringGreen	KEYWORD2
+CRGB::MediumTurquoise	KEYWORD2
+CRGB::MediumVioletRed	KEYWORD2
+CRGB::MidnightBlue	KEYWORD2
+CRGB::MintCream	KEYWORD2
+CRGB::MistyRose	KEYWORD2
+CRGB::Moccasin	KEYWORD2
+CRGB::NavajoWhite	KEYWORD2
+CRGB::Navy	KEYWORD2
+CRGB::OldLace	KEYWORD2
+CRGB::Olive	KEYWORD2
+CRGB::OliveDrab	KEYWORD2
+CRGB::Orange	KEYWORD2
+CRGB::OrangeRed	KEYWORD2
+CRGB::Orchid	KEYWORD2
+CRGB::PaleGoldenrod	KEYWORD2
+CRGB::PaleGreen	KEYWORD2
+CRGB::PaleTurquoise	KEYWORD2
+CRGB::PaleVioletRed	KEYWORD2
+CRGB::PapayaWhip	KEYWORD2
+CRGB::PeachPuff	KEYWORD2
+CRGB::Peru	KEYWORD2
+CRGB::Pink	KEYWORD2
+CRGB::Plaid	KEYWORD2
+CRGB::Plum	KEYWORD2
+CRGB::PowderBlue	KEYWORD2
+CRGB::Purple	KEYWORD2
+CRGB::Red	KEYWORD2
+CRGB::RosyBrown	KEYWORD2
+CRGB::RoyalBlue	KEYWORD2
+CRGB::SaddleBrown	KEYWORD2
+CRGB::Salmon	KEYWORD2
+CRGB::SandyBrown	KEYWORD2
+CRGB::SeaGreen	KEYWORD2
+CRGB::Seashell	KEYWORD2
+CRGB::Sienna	KEYWORD2
+CRGB::Silver	KEYWORD2
+CRGB::SkyBlue	KEYWORD2
+CRGB::SlateBlue	KEYWORD2
+CRGB::SlateGray	KEYWORD2
+CRGB::Snow	KEYWORD2
+CRGB::SpringGreen	KEYWORD2
+CRGB::SteelBlue	KEYWORD2
+CRGB::Tan	KEYWORD2
+CRGB::Teal	KEYWORD2
+CRGB::Thistle	KEYWORD2
+CRGB::Tomato	KEYWORD2
+CRGB::Turquoise	KEYWORD2
+CRGB::Violet	KEYWORD2
+CRGB::Wheat	KEYWORD2
+CRGB::White	KEYWORD2
+CRGB::WhiteSmoke	KEYWORD2
+CRGB::Yellow	KEYWORD2
+CRGB::YellowGreen	KEYWORD2
+
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+# Chipsets
+LPD8806	LITERAL1
+WS2801 	LITERAL1
+SM16716 	LITERAL1
+DMX 	LITERAL1
+TM1809 	LITERAL1
+TM1804 	LITERAL1
+TM1803	LITERAL1
+WS2811 	LITERAL1
+WS2812	LITERAL1
+WS2812B	LITERAL1
+WS2811_400	LITERAL1
+NEOPIXEL	LITERAL1
+UCS1903	LITERAL1
+
+# RGB orderings
+RGB	LITERAL1
+RBG	LITERAL1
+GRB	LITERAL1
+GBR	LITERAL1
+BRG	LITERAL1
+BGR	LITERAL1
+
diff --git a/lib/FastLED/lib8tion.cpp b/lib/FastLED/lib8tion.cpp
new file mode 100644
index 0000000..d7d6b13
--- /dev/null
+++ b/lib/FastLED/lib8tion.cpp
@@ -0,0 +1,242 @@
+#include <stdint.h>
+
+#define RAND16_SEED  1337
+uint16_t rand16seed = RAND16_SEED;
+
+
+// memset8, memcpy8, memmove8:
+//  optimized avr replacements for the standard "C" library
+//  routines memset, memcpy, and memmove.
+//
+//  There are two techniques that make these routines
+//  faster than the standard avr-libc routines.
+//  First, the loops are unrolled 2X, meaning that
+//  the average loop overhead is cut in half.
+//  And second, the compare-and-branch at the bottom
+//  of each loop decrements the low byte of the
+//  counter, and if the carry is clear, it branches
+//  back up immediately.  Only if the low byte math
+//  causes carry do we bother to decrement the high
+//  byte and check that result for carry as well.
+//  Results for a 100-byte buffer are 20-40% faster
+//  than standard avr-libc, at a cost of a few extra
+//  bytes of code.
+
+#if defined(__AVR__)
+extern "C" {
+//__attribute__ ((noinline))
+void * memset8 ( void * ptr, uint8_t val, uint16_t num )
+{
+    asm volatile(
+         "  movw r26, %[ptr]        \n\t"
+         "  sbrs %A[num], 0         \n\t"
+         "  rjmp Lseteven_%=        \n\t"
+         "  rjmp Lsetodd_%=         \n\t"
+         "Lsetloop_%=:              \n\t"
+         "  st X+, %[val]           \n\t"
+         "Lsetodd_%=:               \n\t"
+         "  st X+, %[val]           \n\t"
+         "Lseteven_%=:              \n\t"
+         "  subi %A[num], 2         \n\t"
+         "  brcc Lsetloop_%=        \n\t"
+         "  sbci %B[num], 0         \n\t"
+         "  brcc Lsetloop_%=        \n\t"
+         : [num] "+r" (num)
+         : [ptr]  "r" (ptr),
+           [val]  "r" (val)
+         : "memory"
+         );
+    return ptr;
+}
+
+
+
+//__attribute__ ((noinline))
+void * memcpy8 ( void * dst, void* src, uint16_t num )
+{
+    asm volatile(
+         "  movw r30, %[src]        \n\t"
+         "  movw r26, %[dst]        \n\t"
+         "  sbrs %A[num], 0         \n\t"
+         "  rjmp Lcpyeven_%=        \n\t"
+         "  rjmp Lcpyodd_%=         \n\t"
+         "Lcpyloop_%=:              \n\t"
+         "  ld __tmp_reg__, Z+      \n\t"
+         "  st X+, __tmp_reg__      \n\t"
+         "Lcpyodd_%=:               \n\t"
+         "  ld __tmp_reg__, Z+      \n\t"
+         "  st X+, __tmp_reg__      \n\t"
+         "Lcpyeven_%=:              \n\t"
+         "  subi %A[num], 2         \n\t"
+         "  brcc Lcpyloop_%=        \n\t"
+         "  sbci %B[num], 0         \n\t"
+         "  brcc Lcpyloop_%=        \n\t"
+         : [num] "+r" (num)
+         : [src] "r" (src),
+           [dst] "r" (dst)
+         : "memory"
+         );
+    return dst;
+}
+
+//__attribute__ ((noinline))
+void * memmove8 ( void * dst, void* src, uint16_t num )
+{
+    if( src > dst) {
+        // if src > dst then we can use the forward-stepping memcpy8
+        return memcpy8( dst, src, num);
+    } else {
+        // if src < dst then we have to step backward:
+        dst = (char*)dst + num;
+        src = (char*)src + num;
+        asm volatile(
+             "  movw r30, %[src]        \n\t"
+             "  movw r26, %[dst]        \n\t"
+             "  sbrs %A[num], 0         \n\t"
+             "  rjmp Lmoveven_%=        \n\t"
+             "  rjmp Lmovodd_%=         \n\t"
+             "Lmovloop_%=:              \n\t"
+             "  ld __tmp_reg__, -Z      \n\t"
+             "  st -X, __tmp_reg__      \n\t"
+             "Lmovodd_%=:               \n\t"
+             "  ld __tmp_reg__, -Z      \n\t"
+             "  st -X, __tmp_reg__      \n\t"
+             "Lmoveven_%=:              \n\t"
+             "  subi %A[num], 2         \n\t"
+             "  brcc Lmovloop_%=        \n\t"
+             "  sbci %B[num], 0         \n\t"
+             "  brcc Lmovloop_%=        \n\t"
+             : [num] "+r" (num)
+             : [src] "r" (src),
+               [dst] "r" (dst)
+             : "memory"
+             );
+        return dst;
+    }
+}
+
+
+} /* end extern "C" */
+
+#endif /* AVR */
+
+#if 0
+// TEST / VERIFICATION CODE ONLY BELOW THIS POINT
+#include <Arduino.h>
+#include "lib8tion.h"
+
+void test1abs( int8_t i)
+{
+    Serial.print("abs("); Serial.print(i); Serial.print(") = ");
+    int8_t j = abs8(i);
+    Serial.print(j); Serial.println(" ");
+}
+
+void testabs()
+{
+    delay(5000);
+    for( int8_t q = -128; q != 127; q++) {
+        test1abs(q);
+    }
+    for(;;){};
+}
+
+
+void testmul8()
+{
+    delay(5000);
+    byte r, c;
+
+    Serial.println("mul8:");
+    for( r = 0; r <= 20; r += 1) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 20; c += 1) {
+            byte t;
+            t = mul8( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+    Serial.println("done.");
+    for(;;){};
+}
+
+
+void testscale8()
+{
+    delay(5000);
+    byte r, c;
+
+    Serial.println("scale8:");
+    for( r = 0; r <= 240; r += 10) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 240; c += 10) {
+            byte t;
+            t = scale8( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+
+    Serial.println(' ');
+    Serial.println("scale8_video:");
+
+    for( r = 0; r <= 100; r += 4) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 100; c += 4) {
+            byte t;
+            t = scale8_video( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+
+    Serial.println("done.");
+    for(;;){};
+}
+
+
+
+void testqadd8()
+{
+    delay(5000);
+    byte r, c;
+    for( r = 0; r <= 240; r += 10) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 240; c += 10) {
+            byte t;
+            t = qadd8( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+    Serial.println("done.");
+    for(;;){};
+}
+
+void testnscale8x3()
+{
+    delay(5000);
+    byte r, g, b, sc;
+    for( byte z = 0; z < 10; z++) {
+        r = random8(); g = random8(); b = random8(); sc = random8();
+
+        Serial.print("nscale8x3_video( ");
+        Serial.print(r); Serial.print(", ");
+        Serial.print(g); Serial.print(", ");
+        Serial.print(b); Serial.print(", ");
+        Serial.print(sc); Serial.print(") = [ ");
+
+        nscale8x3_video( r, g, b, sc);
+
+        Serial.print(r); Serial.print(", ");
+        Serial.print(g); Serial.print(", ");
+        Serial.print(b); Serial.print("]");
+
+        Serial.println(' ');
+    }
+    Serial.println("done.");
+    for(;;){};
+}
+
+#endif
diff --git a/lib/FastLED/lib8tion.h b/lib/FastLED/lib8tion.h
new file mode 100644
index 0000000..5fb812c
--- /dev/null
+++ b/lib/FastLED/lib8tion.h
@@ -0,0 +1,1272 @@
+#ifndef __INC_LIB8TION_H
+#define __INC_LIB8TION_H
+
+/*
+
+ Fast, efficient 8-bit math functions specifically
+ designed for high-performance LED programming.
+
+ Because of the AVR(Arduino) and ARM assembly language
+ implementations provided, using these functions often
+ results in smaller and faster code than the equivalent
+ program using plain "C" arithmetic and logic.
+
+
+ Included are:
+
+
+ - Saturating unsigned 8-bit add and subtract.
+   Instead of wrapping around if an overflow occurs,
+   these routines just 'clamp' the output at a maxumum
+   of 255, or a minimum of 0.  Useful for adding pixel
+   values.  E.g., qadd8( 200, 100) = 255.
+
+     qadd8( i, j) == MIN( (i + j), 0xFF )
+     qsub8( i, j) == MAX( (i - j), 0 )
+
+ - Saturating signed 8-bit ("7-bit") add.
+     qadd7( i, j) == MIN( (i + j), 0x7F)
+
+
+ - Scaling (down) of unsigned 8- and 16- bit values.
+   Scaledown value is specified in 1/256ths.
+     scale8( i, sc) == (i * sc) / 256
+     scale16by8( i, sc) == (i * sc) / 256
+
+   Example: scaling a 0-255 value down into a
+   range from 0-99:
+     downscaled = scale8( originalnumber, 100);
+
+   A special version of scale8 is provided for scaling
+   LED brightness values, to make sure that they don't
+   accidentally scale down to total black at low
+   dimming levels, since that would look wrong:
+     scale8_video( i, sc) = ((i * sc) / 256) +? 1
+
+   Example: reducing an LED brightness by a
+   dimming factor:
+     new_bright = scale8_video( orig_bright, dimming);
+
+
+ - Fast 8- and 16- bit unsigned random numbers.
+   Significantly faster than Arduino random(), but
+   also somewhat less random.  You can add entropy.
+     random8()       == random from 0..255
+     random8( n)     == random from 0..(N-1)
+     random8( n, m)  == random from N..(M-1)
+
+     random16()      == random from 0..65535
+     random16( n)    == random from 0..(N-1)
+     random16( n, m) == random from N..(M-1)
+
+     random16_set_seed( k)    ==  seed = k
+     random16_add_entropy( k) ==  seed += k
+
+
+ - Absolute value of a signed 8-bit value.
+     abs8( i)     == abs( i)
+
+
+ - 8-bit math operations which return 8-bit values.
+   These are provided mostly for completeness,
+   not particularly for performance.
+     mul8( i, j)  == (i * j) & 0xFF
+     add8( i, j)  == (i + j) & 0xFF
+     sub8( i, j)  == (i - j) & 0xFF
+
+
+ - Fast 16-bit approximations of sin and cos.
+   Input angle is a uint16_t from 0-65535.
+   Output is a signed int16_t from -32767 to 32767.
+      sin16( x)  == sin( (x/32768.0) * pi) * 32767
+      cos16( x)  == cos( (x/32768.0) * pi) * 32767
+   Accurate to more than 99% in all cases.
+
+
+ - Dimming and brightening functions for 8-bit
+   light values.
+      dim8_video( x)  == scale8_video( x, x)
+      dim8_raw( x)    == scale8( x, x)
+      brighten8_video( x) == 255 - dim8_video( 255 - x)
+      brighten8_raw( x) == 255 - dim8_raw( 255 - x)
+   The dimming functions in particular are suitable
+   for making LED light output appear more 'linear'.
+
+
+ - Fast 8-bit "easing in/out" function.
+     ease8InOutCubic(x) == 3(x^i) - 2(x^3)
+     ease8InOutApprox(x) ==
+       faster, rougher, approximation of cubic easing
+
+
+ - Linear interpolation between two values, with the
+   fraction between them expressed as an 8- or 16-bit
+   fixed point fraction (fract8 or fract16).
+     lerp8by8(   fromU8, toU8, fract8 )
+     lerp16by8(  fromU16, toU16, fract8 )
+     lerp15by8(  fromS16, toS16, fract8 )
+       == from + (( to - from ) * fract8) / 256)
+     lerp16by16( fromU16, toU16, fract16 )
+       == from + (( to - from ) * fract16) / 65536)
+
+ - Optimized memmove, memcpy, and memset, that are
+   faster than standard avr-libc 1.8.
+      memmove8( dest, src,  bytecount)
+      memcpy8(  dest, src,  bytecount)
+      memset8(  buf, value, bytecount)
+
+
+Lib8tion is pronounced like 'libation': lie-BAY-shun
+
+*/
+
+
+
+#include <stdint.h>
+
+#define LIB8STATIC __attribute__ ((unused)) static
+
+
+#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
+#define LIB8_ATTINY 1
+#endif
+
+
+#if defined(__arm__)
+
+#if defined(__MK20DX128__)
+// Can use Cortex M4 DSP instructions
+#define QADD8_C 0
+#define QADD7_C 0
+#define QADD8_ARM_DSP_ASM 1
+#define QADD7_ARM_DSP_ASM 1
+#else
+// Generic ARM
+#define QADD8_C 1
+#define QADD7_C 1
+#endif
+
+#define QSUB8_C 1
+#define SCALE8_C 1
+#define SCALE16BY8_C 1
+#define SCALE16_C 1
+#define ABS8_C 1
+#define MUL8_C 1
+#define QMUL8_C 1
+#define ADD8_C 1
+#define SUB8_C 1
+#define EASE8_C 1
+
+
+#elif defined(__AVR__)
+
+// AVR ATmega and friends Arduino
+
+#define QADD8_C 0
+#define QADD7_C 0
+#define QSUB8_C 0
+#define ABS8_C 0
+#define ADD8_C 0
+#define SUB8_C 0
+
+#define QADD8_AVRASM 1
+#define QADD7_AVRASM 1
+#define QSUB8_AVRASM 1
+#define ABS8_AVRASM 1
+#define ADD8_AVRASM 1
+#define SUB8_AVRASM 1
+
+// Note: these require hardware MUL instruction
+//       -- sorry, ATtiny!
+#if !defined(LIB8_ATTINY)
+#define SCALE8_C 0
+#define SCALE16BY8_C 0
+#define SCALE16_C 0
+#define MUL8_C 0
+#define QMUL8_C 0
+#define EASE8_C 0
+#define SCALE8_AVRASM 1
+#define SCALE16BY8_AVRASM 1
+#define SCALE16_AVRASM 1
+#define MUL8_AVRASM 1
+#define QMUL8_AVRASM 1
+#define EASE8_AVRASM 1
+#define CLEANUP_R1_AVRASM 1
+#else
+// On ATtiny, we just use C implementations
+#define SCALE8_C 1
+#define SCALE16BY8_C 1
+#define SCALE16_C 1
+#define MUL8_C 1
+#define QMUL8_C 1
+#define EASE8_C 1
+#define SCALE8_AVRASM 0
+#define SCALE16BY8_AVRASM 0
+#define SCALE16_AVRASM 0
+#define MUL8_AVRASM 0
+#define QMUL8_AVRASM 0
+#define EASE8_AVRASM 0
+#endif
+
+#else
+
+// unspecified architecture, so
+// no ASM, everything in C
+#define QADD8_C 1
+#define QADD7_C 1
+#define QSUB8_C 1
+#define SCALE8_C 1
+#define SCALE16BY8_C 1
+#define SCALE16_C 1
+#define ABS8_C 1
+#define MUL8_C 1
+#define ADD8_C 1
+#define SUB8_C 1
+#define EASE8_C 1
+
+#endif
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// typdefs for fixed-point fractional types.
+//
+// sfract7 should be interpreted as signed 128ths.
+// fract8 should be interpreted as unsigned 256ths.
+// sfract15 should be interpreted as signed 32768ths.
+// fract16 should be interpreted as unsigned 65536ths.
+//
+// Example: if a fract8 has the value "64", that should be interpreted
+//          as 64/256ths, or one-quarter.
+//
+//
+//  fract8   range is 0 to 0.99609375
+//                 in steps of 0.00390625
+//
+//  sfract7  range is -0.9921875 to 0.9921875
+//                 in steps of 0.0078125
+//
+//  fract16  range is 0 to 0.99998474121
+//                 in steps of 0.00001525878
+//
+//  sfract15 range is -0.99996948242 to 0.99996948242
+//                 in steps of 0.00003051757
+//
+
+typedef uint8_t   fract8;   // ANSI: unsigned short _Fract
+typedef int8_t    sfract7;  // ANSI: signed   short _Fract
+typedef uint16_t  fract16;  // ANSI: unsigned       _Fract
+typedef int16_t   sfract15; // ANSI: signed         _Fract
+
+
+// accumXY types should be interpreted as X bits of integer,
+//         and Y bits of fraction.
+//         E.g., accum88 has 8 bits of int, 8 bits of fraction
+
+typedef uint16_t  accum88;  // ANSI: unsigned short _Accum
+typedef int16_t   saccum78; // ANSI: signed   short _Accum
+typedef uint32_t  accum1616;// ANSI: signed         _Accum
+typedef int32_t   saccum1516;//ANSI: signed         _Accum
+typedef uint16_t  accum124; // no direct ANSI counterpart
+typedef int32_t   saccum114;// no direct ANSI counterpart
+
+
+// typedef for IEEE754 "binary32" float type internals
+
+typedef union {
+    uint32_t i;
+    float    f;
+    struct {
+        uint32_t mantissa: 23;
+        uint32_t exponent:  8;
+        uint32_t signbit:   1;
+    };
+    struct {
+        uint32_t mant7 :  7;
+        uint32_t mant16: 16;
+        uint32_t exp_  :  8;
+        uint32_t sb_   :  1;
+    };
+    struct {
+        uint32_t mant_lo8 : 8;
+        uint32_t mant_hi16_exp_lo1 : 16;
+        uint32_t sb_exphi7 : 8;
+    };
+} IEEE754binary32_t;
+
+
+
+///////////////////////////////////////////////////////////////////////
+
+// qadd8: add one byte to another, saturating at 0xFF
+LIB8STATIC uint8_t qadd8( uint8_t i, uint8_t j)
+{
+#if QADD8_C == 1
+    int t = i + j;
+    if( t > 255) t = 255;
+    return t;
+#elif QADD8_AVRASM == 1
+    asm volatile(
+         /* First, add j to i, conditioning the C flag */
+         "add %0, %1    \n\t"
+
+         /* Now test the C flag.
+           If C is clear, we branch around a load of 0xFF into i.
+           If C is set, we go ahead and load 0xFF into i.
+         */
+         "brcc L_%=     \n\t"
+         "ldi %0, 0xFF  \n\t"
+         "L_%=: "
+         : "+a" (i)
+         : "a"  (j) );
+    return i;
+#elif QADD8_ARM_DSP_ASM == 1
+    asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j));
+    return i;
+#else
+#error "No implementation for qadd8 available."
+#endif
+}
+
+
+// qadd7: add one signed byte to another,
+//        saturating at 0x7F.
+LIB8STATIC int8_t qadd7( int8_t i, int8_t j)
+{
+#if QADD7_C == 1
+    int16_t t = i + j;
+    if( t > 127) t = 127;
+    return t;
+#elif QADD7_AVRASM == 1
+    asm volatile(
+         /* First, add j to i, conditioning the V flag */
+         "add %0, %1    \n\t"
+
+         /* Now test the V flag.
+          If V is clear, we branch around a load of 0x7F into i.
+          If V is set, we go ahead and load 0x7F into i.
+          */
+         "brvc L_%=     \n\t"
+         "ldi %0, 0x7F  \n\t"
+         "L_%=: "
+         : "+a" (i)
+         : "a"  (j) );
+
+    return i;
+#elif QADD7_ARM_DSP_ASM == 1
+    asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j));
+    return i;
+#else
+#error "No implementation for qadd7 available."
+#endif
+}
+
+// qsub8: subtract one byte from another, saturating at 0x00
+LIB8STATIC uint8_t qsub8( uint8_t i, uint8_t j)
+{
+#if QSUB8_C == 1
+    int t = i - j;
+    if( t < 0) t = 0;
+    return t;
+#elif QSUB8_AVRASM == 1
+
+    asm volatile(
+         /* First, subtract j from i, conditioning the C flag */
+         "sub %0, %1    \n\t"
+
+         /* Now test the C flag.
+          If C is clear, we branch around a load of 0x00 into i.
+          If C is set, we go ahead and load 0x00 into i.
+          */
+         "brcc L_%=     \n\t"
+         "ldi %0, 0x00  \n\t"
+         "L_%=: "
+         : "+a" (i)
+         : "a"  (j) );
+
+    return i;
+#else
+#error "No implementation for qsub8 available."
+#endif
+}
+
+// add8: add one byte to another, with one byte result
+LIB8STATIC uint8_t add8( uint8_t i, uint8_t j)
+{
+#if ADD8_C == 1
+    int t = i + j;
+    return t;
+#elif ADD8_AVRASM == 1
+    // Add j to i, period.
+    asm volatile( "add %0, %1" : "+a" (i) : "a" (j));
+    return i;
+#else
+#error "No implementation for add8 available."
+#endif
+}
+
+
+// sub8: subtract one byte from another, 8-bit result
+LIB8STATIC uint8_t sub8( uint8_t i, uint8_t j)
+{
+#if SUB8_C == 1
+    int t = i - j;
+    return t;
+#elif SUB8_AVRASM == 1
+    // Subtract j from i, period.
+    asm volatile( "sub %0, %1" : "+a" (i) : "a" (j));
+    return i;
+#else
+#error "No implementation for sub8 available."
+#endif
+}
+
+
+// scale8: scale one byte by a second one, which is treated as
+//         the numerator of a fraction whose denominator is 256
+//         In other words, it computes i * (scale / 256)
+//         4 clocks AVR, 2 clocks ARM
+LIB8STATIC uint8_t scale8( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+    return ((int)i * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+    asm volatile(
+         /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+         "mul %0, %1          \n\t"
+         /* Move the high 8-bits of the product (r1) back to i */
+         "mov %0, r1          \n\t"
+         /* Restore r1 to "0"; it's expected to always be that */
+         "clr __zero_reg__    \n\t"
+
+         : "+a" (i)      /* writes to i */
+         : "a"  (scale)  /* uses scale */
+         : "r0", "r1"    /* clobbers r0, r1 */ );
+
+    /* Return the result */
+    return i;
+#else
+#error "No implementation for scale8 available."
+#endif
+}
+
+
+//  The "video" version of scale8 guarantees that the output will
+//  be only be zero if one or both of the inputs are zero.  If both
+//  inputs are non-zero, the output is guaranteed to be non-zero.
+//  This makes for better 'video'/LED dimming, at the cost of
+//  several additional cycles.
+LIB8STATIC uint8_t scale8_video( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+    uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+    return j;
+#elif SCALE8_AVRASM == 1
+
+    uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    asm volatile(
+         "      tst %0           \n"
+         "      breq L_%=        \n"
+         "      mul %0, %1       \n"
+         "      mov %0, r1       \n"
+         "      add %0, %2       \n"
+         "      clr __zero_reg__ \n"
+         "L_%=:                  \n"
+
+         : "+a" (i)
+         : "a" (scale), "a" (nonzeroscale)
+         : "r0", "r1");
+
+    // Return the result
+    return i;
+#else
+#error "No implementation for scale8_video available."
+#endif
+}
+
+
+// This version of scale8 does not clean up the R1 register on AVR
+// If you are doing several 'scale8's in a row, use this, and
+// then explicitly call cleanup_R1.
+LIB8STATIC uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+    return ((int)i * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+    asm volatile(
+         /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+         "mul %0, %1    \n\t"
+         /* Move the high 8-bits of the product (r1) back to i */
+         "mov %0, r1    \n\t"
+         /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF  */
+         /* "clr __zero_reg__    \n\t" */
+
+         : "+a" (i)      /* writes to i */
+         : "a"  (scale)  /* uses scale */
+         : "r0", "r1"    /* clobbers r0, r1 */ );
+
+    // Return the result
+    return i;
+#else
+#error "No implementation for scale8_LEAVING_R1_DIRTY available."
+#endif
+}
+
+//   THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENT DIRECTLY IN PLACE
+
+LIB8STATIC void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale)
+{
+#if SCALE8_C == 1
+    i = ((int)i * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+    asm volatile(
+         /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+         "mul %0, %1    \n\t"
+         /* Move the high 8-bits of the product (r1) back to i */
+         "mov %0, r1    \n\t"
+         /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
+         /* "clr __zero_reg__    \n\t" */
+
+         : "+a" (i)      /* writes to i */
+         : "a"  (scale)  /* uses scale */
+         : "r0", "r1"    /* clobbers r0, r1 */ );
+#else
+#error "No implementation for nscale8_LEAVING_R1_DIRTY available."
+#endif
+}
+
+
+
+LIB8STATIC uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+    uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+    return j;
+#elif SCALE8_AVRASM == 1
+
+    uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    asm volatile(
+         "      tst %0          \n"
+         "      breq L_%=       \n"
+         "      mul %0, %1      \n"
+         "      mov %0, r1      \n"
+         "      add %0, %2      \n"
+         /* R1 IS LEFT DIRTY, YOU MUST ZERO IT OUT YOURSELF */
+         "L_%=:                 \n"
+
+         : "+a" (i)
+         : "a" (scale), "a" (nonzeroscale)
+         : "r0", "r1");
+
+    // Return the result
+    return i;
+#else
+#error "No implementation for scale8_video available."
+#endif
+}
+
+
+
+LIB8STATIC void cleanup_R1()
+{
+#if CLEANUP_R1_AVRASM == 1
+    // Restore r1 to "0"; it's expected to always be that
+    asm volatile( "clr __zero_reg__  \n\t" : : : "r1" );
+#endif
+}
+
+
+// nscale8x3: scale three one byte values by a fourth one, which is treated as
+//         the numerator of a fraction whose demominator is 256
+//         In other words, it computes r,g,b * (scale / 256)
+//
+//         THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
+
+LIB8STATIC void nscale8x3( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
+{
+#if SCALE8_C == 1
+    r = ((int)r * (int)(scale) ) >> 8;
+    g = ((int)g * (int)(scale) ) >> 8;
+    b = ((int)b * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+    r = scale8_LEAVING_R1_DIRTY(r, scale);
+    g = scale8_LEAVING_R1_DIRTY(g, scale);
+    b = scale8_LEAVING_R1_DIRTY(b, scale);
+    cleanup_R1();
+#else
+#error "No implementation for nscale8x3 available."
+#endif
+}
+
+
+LIB8STATIC void nscale8x3_video( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
+{
+#if SCALE8_C == 1
+    uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    r = (r == 0) ? 0 : (((int)r * (int)(scale) ) >> 8) + nonzeroscale;
+    g = (g == 0) ? 0 : (((int)g * (int)(scale) ) >> 8) + nonzeroscale;
+    b = (b == 0) ? 0 : (((int)b * (int)(scale) ) >> 8) + nonzeroscale;
+#elif SCALE8_AVRASM == 1
+    r = scale8_video_LEAVING_R1_DIRTY( r, scale);
+    g = scale8_video_LEAVING_R1_DIRTY( g, scale);
+    b = scale8_video_LEAVING_R1_DIRTY( b, scale);
+    cleanup_R1();
+#else
+#error "No implementation for nscale8x3 available."
+#endif
+}
+
+// nscale8x2: scale two one byte values by a third one, which is treated as
+//         the numerator of a fraction whose demominator is 256
+//         In other words, it computes i,j * (scale / 256)
+//
+//         THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
+
+LIB8STATIC void nscale8x2( uint8_t& i, uint8_t& j, fract8 scale)
+{
+#if SCALE8_C == 1
+    i = ((int)i * (int)(scale) ) >> 8;
+    j = ((int)j * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+    i = scale8_LEAVING_R1_DIRTY(i, scale);
+    j = scale8_LEAVING_R1_DIRTY(j, scale);
+    cleanup_R1();
+#else
+#error "No implementation for nscale8x2 available."
+#endif
+}
+
+
+LIB8STATIC void nscale8x2_video( uint8_t& i, uint8_t& j, fract8 scale)
+{
+#if SCALE8_C == 1
+    uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    i = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+    j = (j == 0) ? 0 : (((int)j * (int)(scale) ) >> 8) + nonzeroscale;
+#elif SCALE8_AVRASM == 1
+    i = scale8_video_LEAVING_R1_DIRTY( i, scale);
+    j = scale8_video_LEAVING_R1_DIRTY( j, scale);
+    cleanup_R1();
+#else
+#error "No implementation for nscale8x2 available."
+#endif
+}
+
+
+// scale16by8: scale a 16-bit unsigned value by an 8-bit value,
+//         considered as numerator of a fraction whose denominator
+//         is 256. In other words, it computes i * (scale / 256)
+
+#if SCALE16BY8_C == 1
+LIB8STATIC uint16_t scale16by8( uint16_t i, fract8 scale )
+{
+    uint16_t result;
+    result = (i * scale) / 256;
+    return result;
+}
+#elif SCALE16BY8_AVRASM == 1
+LIB8STATIC uint16_t scale16by8( uint16_t i, fract8 scale )
+{
+    uint16_t result;
+    asm volatile(
+         // result.A = HighByte(i.A x j )
+         "  mul %A[i], %[scale]                 \n\t"
+         "  mov %A[result], r1                  \n\t"
+         "  clr %B[result]                      \n\t"
+
+         // result.A-B += i.B x j
+         "  mul %B[i], %[scale]                 \n\t"
+         "  add %A[result], r0                  \n\t"
+         "  adc %B[result], r1                  \n\t"
+
+         // cleanup r1
+         "  clr __zero_reg__                    \n\t"
+
+         : [result] "=r" (result)
+         : [i] "r" (i), [scale] "r" (scale)
+         : "r0", "r1"
+         );
+    return result;
+}
+#else
+#error "No implementation for scale16by8 available."
+#endif
+
+// scale16: scale a 16-bit unsigned value by a 16-bit value,
+//         considered as numerator of a fraction whose denominator
+//         is 65536. In other words, it computes i * (scale / 65536)
+
+#if SCALE16_C == 1
+LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
+{
+    uint16_t result;
+    result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
+    return result;
+}
+#elif SCALE16_AVRASM == 1
+LIB8STATIC
+uint16_t scale16( uint16_t i, fract16 scale )
+{
+    uint32_t result = 0;
+    const uint8_t  zero = 0;
+    asm volatile(
+                 // result.A-B  = i.A x scale.A
+                 "  mul %A[i], %A[scale]                 \n\t"
+                 //  save results...
+                 // basic idea:
+                 //"  mov %A[result], r0                 \n\t"
+                 //"  mov %B[result], r1                 \n\t"
+                 // which can be written as...
+                 "  movw %A[result], r0                   \n\t"
+                 // We actually need to do anything with r0,
+                 // as result.A is never used again here, so we
+                 // could just move the high byte, but movw is
+                 // one clock cycle, just like mov, so might as
+                 // well, in case we want to use this code for
+                 // a generic 16x16 multiply somewhere.
+
+                 // result.C-D  = i.B x scale.B
+                 "  mul %B[i], %B[scale]                 \n\t"
+                 //"  mov %C[result], r0                 \n\t"
+                 //"  mov %D[result], r1                 \n\t"
+                 "  movw %C[result], r0                   \n\t"
+
+                 // result.B-D += i.B x scale.A
+                 "  mul %B[i], %A[scale]                 \n\t"
+
+                 "  add %B[result], r0                   \n\t"
+                 "  adc %C[result], r1                   \n\t"
+                 "  adc %D[result], %[zero]              \n\t"
+
+                 // result.B-D += i.A x scale.B
+                 "  mul %A[i], %B[scale]                 \n\t"
+
+                 "  add %B[result], r0                   \n\t"
+                 "  adc %C[result], r1                   \n\t"
+                 "  adc %D[result], %[zero]              \n\t"
+
+                 // cleanup r1
+                 "  clr r1                               \n\t"
+
+                 : [result] "+r" (result)
+                 : [i] "r" (i),
+                   [scale] "r" (scale),
+                   [zero] "r" (zero)
+                 : "r0", "r1"
+                 );
+    result = result >> 16;
+    return result;
+}
+#else
+#error "No implementation for scale16 available."
+#endif
+
+
+
+// mul8: 8x8 bit multiplication, with 8 bit result
+LIB8STATIC uint8_t mul8( uint8_t i, uint8_t j)
+{
+#if MUL8_C == 1
+    return ((int)i * (int)(j) ) & 0xFF;
+#elif MUL8_AVRASM == 1
+    asm volatile(
+         /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
+         "mul %0, %1          \n\t"
+         /* Extract the LOW 8-bits (r0) */
+         "mov %0, r0          \n\t"
+         /* Restore r1 to "0"; it's expected to always be that */
+         "clr __zero_reg__    \n\t"
+         : "+a" (i)
+         : "a"  (j)
+         : "r0", "r1");
+
+    return i;
+#else
+#error "No implementation for mul8 available."
+#endif
+}
+
+
+// mul8: saturating 8x8 bit multiplication, with 8 bit result
+LIB8STATIC uint8_t qmul8( uint8_t i, uint8_t j)
+{
+#if QMUL8_C == 1
+    int p = ((int)i * (int)(j) );
+    if( p > 255) p = 255;
+    return p;
+#elif QMUL8_AVRASM == 1
+    asm volatile(
+                 /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
+                 "  mul %0, %1          \n\t"
+                 /* If high byte of result is zero, all is well. */
+                 "  tst r1              \n\t"
+                 "  breq Lnospill_%=    \n\t"
+                 /* If high byte of result > 0, saturate low byte to 0xFF */
+                 "  ldi %0,0xFF         \n\t"
+                 "  rjmp Ldone_%=       \n\t"
+                 "Lnospill_%=:          \n\t"
+                 /* Extract the LOW 8-bits (r0) */
+                 "  mov %0, r0          \n\t"
+                 "Ldone_%=:             \n\t"
+                 /* Restore r1 to "0"; it's expected to always be that */
+                 "  clr __zero_reg__    \n\t"
+                 : "+a" (i)
+                 : "a"  (j)
+                 : "r0", "r1");
+
+    return i;
+#else
+#error "No implementation for qmul8 available."
+#endif
+}
+
+
+// abs8: take abs() of a signed 8-bit uint8_t
+LIB8STATIC int8_t abs8( int8_t i)
+{
+#if ABS8_C == 1
+    if( i < 0) i = -i;
+    return i;
+#elif ABS8_AVRASM == 1
+
+
+    asm volatile(
+         /* First, check the high bit, and prepare to skip if it's clear */
+         "sbrc %0, 7 \n"
+
+         /* Negate the value */
+         "neg %0     \n"
+
+         : "+r" (i) : "r" (i) );
+    return i;
+#else
+#error "No implementation for abs8 available."
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// float-to-fixed and fixed-to-float conversions
+//
+// Note that anything involving a 'float' on AVR will be slower.
+
+// floatToSfract15: conversion from IEEE754 float in the range (-1,1)
+//                  to 16-bit fixed point.  Note that the extremes of
+//                  one and negative one are NOT representable.  The
+//                  representable range is basically
+//
+// sfract15ToFloat: conversion from sfract15 fixed point to
+//                  IEEE754 32-bit float.
+
+LIB8STATIC
+float sfract15ToFloat( sfract15 y)
+{
+    return y / 32768.0;
+}
+
+LIB8STATIC
+sfract15 floatToSfract15( float f)
+{
+    return f * 32768.0;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////
+
+// Dimming and brightening functions
+//
+// The eye does not respond in a linear way to light.
+// High speed PWM'd LEDs at 50% duty cycle appear far
+// brighter then the 'half as bright' you might expect.
+//
+// If you want your midpoint brightness leve (128) to
+// appear half as bright as 'full' brightness (255), you
+// have to apply a 'dimming function'.
+//
+//
+
+LIB8STATIC uint8_t dim8_raw( uint8_t x)
+{
+    return scale8( x, x);
+}
+
+LIB8STATIC uint8_t dim8_video( uint8_t x)
+{
+    return scale8_video( x, x);
+}
+
+LIB8STATIC uint8_t brighten8_raw( uint8_t x)
+{
+    uint8_t ix = 255 - x;
+    return 255 - scale8( ix, ix);
+}
+
+LIB8STATIC uint8_t brighten8_video( uint8_t x)
+{
+    uint8_t ix = 255 - x;
+    return 255 - scale8_video( ix, ix);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+// A 16-bit PNRG good enough for LED animations
+
+// X(n+1) = (2053 * X(n)) + 13849)
+#define RAND16_2053  2053
+#define RAND16_13849 13849
+
+extern uint16_t rand16seed;// = RAND16_SEED;
+
+
+LIB8STATIC uint8_t random8()
+{
+    rand16seed = (rand16seed * RAND16_2053) + RAND16_13849;
+    return rand16seed;
+}
+
+LIB8STATIC uint16_t random16()
+{
+    rand16seed = (rand16seed * RAND16_2053) + RAND16_13849;
+    return rand16seed;
+}
+
+
+LIB8STATIC uint8_t random8(uint8_t lim)
+{
+    uint8_t r = random8();
+    r = scale8( r, lim);
+    return r;
+}
+
+LIB8STATIC uint8_t random8(uint8_t min, uint8_t lim)
+{
+    uint8_t delta = lim - min;
+    uint8_t r = random8(delta) + min;
+    return r;
+}
+
+LIB8STATIC uint16_t random16( uint16_t lim)
+{
+    uint16_t r = random16();
+    uint32_t p = (uint32_t)lim * (uint32_t)r;
+    r = p >> 16;
+    return r;
+}
+
+LIB8STATIC uint16_t random16( uint16_t min, uint16_t lim)
+{
+    uint16_t delta = lim - min;
+    uint16_t r = random16( delta) + min;
+    return r;
+}
+
+LIB8STATIC void random16_set_seed( uint16_t seed)
+{
+    rand16seed = seed;
+}
+
+LIB8STATIC uint16_t random16_get_seed()
+{
+    return rand16seed;
+}
+
+LIB8STATIC void random16_add_entropy( uint16_t entropy)
+{
+    rand16seed += entropy;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+
+// sin16 & cos16:
+//        Fast 16-bit approximations of sin(x) & cos(x).
+//        Input angle is an unsigned int from 0-65535.
+//        Output is signed int from -32767 to 32767.
+//
+//        This approximation never varies more than 0.69%
+//        from the floating point value you'd get by doing
+//          float s = sin( x ) * 32767.0;
+//
+//        Don't use this approximation for calculating the
+//        trajectory of a rocket to Mars, but it's great
+//        for art projects and LED displays.
+//
+//        On Arduino/AVR, this approximation is more than
+//        10X faster than floating point sin(x) and cos(x)
+
+#if defined(__AVR__)
+#define sin16 sin16_avr
+#else
+#define sin16 sin16_C
+#endif
+
+LIB8STATIC int16_t sin16_avr( uint16_t theta )
+{
+    static const uint8_t data[] =
+    { 0,         0,         49, 0, 6393%256,   6393/256, 48, 0,
+      12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,
+      23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,
+      30273%256, 30273/256, 14, 0, 32137%256, 32137/256,  4 /*,0*/ };
+
+    uint16_t offset = (theta & 0x3FFF);
+
+    // AVR doesn't have a multi-bit shift instruction,
+    // so if we say "offset >>= 3", gcc makes a tiny loop.
+    // Inserting empty volatile statements between each
+    // bit shift forces gcc to unroll the loop.
+    offset >>= 1; // 0..8191
+    asm volatile("");
+    offset >>= 1; // 0..4095
+    asm volatile("");
+    offset >>= 1; // 0..2047
+
+    if( theta & 0x4000 ) offset = 2047 - offset;
+
+    uint8_t sectionX4;
+    sectionX4 = offset / 256;
+    sectionX4 *= 4;
+
+    uint8_t m;
+
+    union {
+        uint16_t b;
+        struct {
+            uint8_t blo;
+            uint8_t bhi;
+        };
+    } u;
+
+    //in effect u.b = blo + (256 * bhi);
+    u.blo = data[ sectionX4 ];
+    u.bhi = data[ sectionX4 + 1];
+    m     = data[ sectionX4 + 2];
+
+    uint8_t secoffset8 = (uint8_t)(offset) / 2;
+
+    uint16_t mx = m * secoffset8;
+
+    int16_t  y  = mx + u.b;
+    if( theta & 0x8000 ) y = -y;
+
+    return y;
+}
+
+LIB8STATIC int16_t sin16_C( uint16_t theta )
+{
+    static const uint16_t base[] =
+    { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
+    static const uint8_t slope[] =
+    { 49, 48, 44, 38, 31, 23, 14, 4 };
+
+    uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
+    if( theta & 0x4000 ) offset = 2047 - offset;
+
+    uint8_t section = offset / 256; // 0..7
+    uint16_t b   = base[section];
+    uint8_t  m   = slope[section];
+
+    uint8_t secoffset8 = (uint8_t)(offset) / 2;
+
+    uint16_t mx = m * secoffset8;
+    int16_t  y  = mx + b;
+
+    if( theta & 0x8000 ) y = -y;
+
+    return y;
+}
+
+LIB8STATIC int16_t cos16( uint16_t theta)
+{
+    return sin16( theta + 16384);
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// memmove8, memcpy8, and memset8:
+//   alternatives to memmove, memcpy, and memset that are
+//   faster on AVR than standard avr-libc 1.8
+
+#if defined(__AVR__)
+extern "C" {
+void * memmove8( void * dst, const void * src, uint16_t num );
+void * memcpy8 ( void * dst, const void * src, uint16_t num )  __attribute__ ((noinline));
+void * memset8 ( void * ptr, uint8_t value, uint16_t num ) __attribute__ ((noinline)) ;
+}
+#else
+// on non-AVR platforms, these names just call standard libc.
+#define memmove8 memmove
+#define memcpy8 memcpy
+#define memset8 memset
+#endif
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// linear interpolation, such as could be used for Perlin noise, etc.
+//
+
+// linear interpolation between two unsigned 8-bit values,
+// with 8-bit fraction
+LIB8STATIC uint8_t lerp8by8( uint8_t a, uint8_t b, fract8 frac)
+{
+    uint8_t delta = b - a;
+    uint8_t scaled = scale8( delta, frac);
+    uint8_t result = a + scaled;
+    return result;
+}
+
+// linear interpolation between two unsigned 16-bit values,
+// with 16-bit fraction
+LIB8STATIC uint16_t lerp16by16( uint16_t a, uint16_t b, fract16 frac)
+{
+    uint16_t delta = b - a;
+    uint32_t prod = (uint32_t)delta * (uint32_t)frac;
+    uint16_t scaled = prod >> 16;
+    uint16_t result = a + scaled;
+    return result;
+}
+
+
+// A note on the structure of lerp16by8 (and lerp15by8) :
+// The cases for b>a and b<=a are handled separately for
+// speed: without knowing the relative order of a and b,
+// the value (a-b) might be a signed 17-bit value, which
+// would have to be stored in a 32-bit signed int and
+// processed as such.  To avoid that, we separate the
+// two cases, and are able to do all the math with 16-bit
+// unsigned values, which is much faster and smaller on AVR.
+
+// linear interpolation between two unsigned 16-bit values,
+// with 8-bit fraction
+LIB8STATIC uint16_t lerp16by8( uint16_t a, uint16_t b, fract8 frac)
+{
+    uint16_t result;
+    if( b > a) {
+        uint16_t delta = b - a;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a + scaled;
+    } else {
+        uint16_t delta = a - b;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a - scaled;
+    }
+    return result;
+}
+
+// linear interpolation between two signed 15-bit values,
+// with 8-bit fraction
+LIB8STATIC int16_t lerp15by8( int16_t a, int16_t b, fract8 frac)
+{
+    int16_t result;
+    if( b > a) {
+        uint16_t delta = b - a;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a + scaled;
+    } else {
+        uint16_t delta = a - b;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a - scaled;
+    }
+    return result;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// easing functions; see http://easings.net
+//
+
+// ease8InOuCubic: 8-bit cubic ease-in / ease-out function
+//                 Takes around 18 cycles on AVR
+LIB8STATIC fract8 ease8InOutCubic( fract8 i)
+{
+    uint8_t ii  = scale8_LEAVING_R1_DIRTY(  i, i);
+    uint8_t iii = scale8_LEAVING_R1_DIRTY( ii, i);
+
+    uint16_t r1 = (3 * (uint16_t)(ii)) - ( 2 * (uint16_t)(iii));
+
+    /* the code generated for the above *'s automatically
+       cleans up R1, so there's no need to explicitily call
+       cleanup_R1(); */
+
+    uint8_t result = r1;
+
+    // if we got "256", return 255:
+    if( r1 & 0x100 ) {
+        result = 255;
+    }
+    return result;
+}
+
+// ease8InOutApprox: fast, rough 8-bit ease-in/ease-out function
+//                   shaped approximately like 'ease8InOutCubic',
+//                   it's never off by more than a couple of percent
+//                   from the actual cubic S-curve, and it executes
+//                   more than twice as fast.  Use when the cycles
+//                   are more important than visual smoothness.
+//                   Asm version takes around 7 cycles on AVR.
+
+#if EASE8_C == 1
+LIB8STATIC fract8 ease8InOutApprox( fract8 i)
+{
+    if( i < 64) {
+        // start with slope 0.5
+        i /= 2;
+    } else if( i > (255 - 64)) {
+        // end with slope 0.5
+        i = 255 - i;
+        i /= 2;
+        i = 255 - i;
+    } else {
+        // in the middle, use slope 192/128 = 1.5
+        i -= 64;
+        i += (i / 2);
+        i += 32;
+    }
+
+    return i;
+}
+
+#elif EASE8_AVRASM == 1
+LIB8STATIC uint8_t ease8InOutApprox( fract8 i)
+{
+    // takes around 7 cycles on AVR
+    asm volatile (
+        "  subi %[i], 64         \n\t"
+        "  cpi  %[i], 128        \n\t"
+        "  brcc Lshift_%=        \n\t"
+
+        // middle case
+        "  mov __tmp_reg__, %[i] \n\t"
+        "  lsr __tmp_reg__       \n\t"
+        "  add %[i], __tmp_reg__ \n\t"
+        "  subi %[i], 224        \n\t"
+        "  rjmp Ldone_%=         \n\t"
+
+        // start or end case
+        "Lshift_%=:              \n\t"
+        "  lsr %[i]              \n\t"
+        "  subi %[i], 96         \n\t"
+
+        "Ldone_%=:               \n\t"
+
+        : [i] "+a" (i)
+        :
+        : "r0", "r1"
+        );
+    return i;
+}
+#else
+#error "No implementation for ease8 available."
+#endif
+
+
+
+
+
+#endif
diff --git a/lib/FastLED/pixeltypes.h b/lib/FastLED/pixeltypes.h
new file mode 100644
index 0000000..6d3f67f
--- /dev/null
+++ b/lib/FastLED/pixeltypes.h
@@ -0,0 +1,659 @@
+#ifndef __INC_PIXELS_H
+#define __INC_PIXELS_H
+
+#include <stdint.h>
+#include "lib8tion.h"
+
+struct CRGB;
+struct CHSV;
+
+// Forward declaration of hsv2rgb_rainbow here,
+// to avoid circular dependencies.
+extern void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb);
+
+
+struct CHSV {
+    union {
+		struct {
+		    union {
+		        uint8_t hue;
+		        uint8_t h; };
+		    union {
+		        uint8_t saturation;
+		        uint8_t sat;
+		        uint8_t s; };
+		    union {
+		        uint8_t value;
+		        uint8_t val;
+		        uint8_t v; };
+		};
+		uint8_t raw[3];
+	};
+
+    // default values are UNITIALIZED
+    inline CHSV() __attribute__((always_inline))
+    {
+    }
+
+    // allow construction from H, S, V
+    inline CHSV( uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
+        : h(ih), s(is), v(iv)
+    {
+    }
+
+    // allow copy construction
+    inline CHSV(const CHSV& rhs) __attribute__((always_inline))
+    {
+        h = rhs.h;
+        s = rhs.s;
+        v = rhs.v;
+    }
+
+    inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline))
+    {
+        h = rhs.h;
+        s = rhs.s;
+        v = rhs.v;
+        return *this;
+    }
+
+    inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
+    {
+        h = ih;
+        s = is;
+        v = iv;
+        return *this;
+    }
+};
+
+
+struct CRGB {
+	union {
+		struct {
+            union {
+                uint8_t r;
+                uint8_t red;
+            };
+            union {
+                uint8_t g;
+                uint8_t green;
+            };
+            union {
+                uint8_t b;
+                uint8_t blue;
+            };
+        };
+		uint8_t raw[3];
+	};
+
+	inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline))
+    {
+        return raw[x];
+    }
+
+    inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline))
+    {
+        return raw[x];
+    }
+
+    // default values are UNINITIALIZED
+	inline CRGB() __attribute__((always_inline))
+    {
+    }
+
+    // allow construction from R, G, B
+    inline CRGB( uint8_t ir, uint8_t ig, uint8_t ib)  __attribute__((always_inline))
+        : r(ir), g(ig), b(ib)
+    {
+    }
+
+    // allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code
+    inline CRGB( uint32_t colorcode)  __attribute__((always_inline))
+    : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
+    {
+    }
+
+    // allow copy construction
+	inline CRGB(const CRGB& rhs) __attribute__((always_inline))
+    {
+        r = rhs.r;
+        g = rhs.g;
+        b = rhs.b;
+    }
+
+    // allow construction from HSV color
+	inline CRGB(const CHSV& rhs) __attribute__((always_inline))
+    {
+        hsv2rgb_rainbow( rhs, *this);
+    }
+
+    // allow assignment from one RGB struct to another
+	inline CRGB& operator= (const CRGB& rhs) __attribute__((always_inline))
+    {
+        r = rhs.r;
+        g = rhs.g;
+        b = rhs.b;
+        return *this;
+    }
+
+    // allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code
+	inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline))
+    {
+        r = (colorcode >> 16) & 0xFF;
+        g = (colorcode >>  8) & 0xFF;
+        b = (colorcode >>  0) & 0xFF;
+        return *this;
+    }
+
+    // allow assignment from R, G, and B
+	inline CRGB& setRGB (uint8_t nr, uint8_t ng, uint8_t nb) __attribute__((always_inline))
+    {
+        r = nr;
+        g = ng;
+        b = nb;
+        return *this;
+    }
+
+    // allow assignment from H, S, and V
+	inline CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val) __attribute__((always_inline))
+    {
+        hsv2rgb_rainbow( CHSV(hue, sat, val), *this);
+        return *this;
+    }
+
+    // allow assignment from just a Hue, saturation and value automatically at max.
+	inline CRGB& setHue (uint8_t hue) __attribute__((always_inline))
+    {
+        hsv2rgb_rainbow( CHSV(hue, 255, 255), *this);
+        return *this;
+    }
+
+    // allow assignment from HSV color
+	inline CRGB& operator= (const CHSV& rhs) __attribute__((always_inline))
+    {
+        hsv2rgb_rainbow( rhs, *this);
+        return *this;
+    }
+
+    // allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code
+	inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline))
+    {
+        r = (colorcode >> 16) & 0xFF;
+        g = (colorcode >>  8) & 0xFF;
+        b = (colorcode >>  0) & 0xFF;
+        return *this;
+    }
+
+
+    // add one RGB to another, saturating at 0xFF for each channel
+    inline CRGB& operator+= (const CRGB& rhs )
+    {
+        r = qadd8( r, rhs.r);
+        g = qadd8( g, rhs.g);
+        b = qadd8( b, rhs.b);
+        return *this;
+    }
+
+    // add a contstant to each channel, saturating at 0xFF
+    // this is NOT an operator+= overload because the compiler
+    // can't usefully decide when it's being passed a 32-bit
+    // constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue)
+    inline CRGB& addToRGB (uint8_t d )
+    {
+        r = qadd8( r, d);
+        g = qadd8( g, d);
+        b = qadd8( b, d);
+        return *this;
+    }
+
+    // subtract one RGB from another, saturating at 0x00 for each channel
+    inline CRGB& operator-= (const CRGB& rhs )
+    {
+        r = qsub8( r, rhs.r);
+        g = qsub8( g, rhs.g);
+        b = qsub8( b, rhs.b);
+        return *this;
+    }
+
+    // subtract a constant from each channel, saturating at 0x00
+    // this is NOT an operator+= overload because the compiler
+    // can't usefully decide when it's being passed a 32-bit
+    // constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue)
+    inline CRGB& subtractFromRGB(uint8_t d )
+    {
+        r = qsub8( r, d);
+        g = qsub8( g, d);
+        b = qsub8( b, d);
+        return *this;
+    }
+
+    // subtract a constant of '1' from each channel, saturating at 0x00
+    inline CRGB& operator-- ()  __attribute__((always_inline))
+    {
+        subtractFromRGB(1);
+        return *this;
+    }
+
+    // subtract a constant of '1' from each channel, saturating at 0x00
+    inline CRGB operator-- (int DUMMY_ARG)  __attribute__((always_inline))
+    {
+        CRGB retval(*this);
+        --(*this);
+        return retval;
+    }
+
+    // add a constant of '1' from each channel, saturating at 0xFF
+    inline CRGB& operator++ ()  __attribute__((always_inline))
+    {
+        addToRGB(1);
+        return *this;
+    }
+
+    // add a constant of '1' from each channel, saturating at 0xFF
+    inline CRGB operator++ (int DUMMY_ARG)  __attribute__((always_inline))
+    {
+        CRGB retval(*this);
+        ++(*this);
+        return retval;
+    }
+
+    // divide each of the channels by a constant
+    inline CRGB& operator/= (uint8_t d )
+    {
+        r /= d;
+        g /= d;
+        b /= d;
+        return *this;
+    }
+
+    // multiply each of the channels by a constant,
+    // saturating each channel at 0xFF
+    inline CRGB& operator*= (uint8_t d )
+    {
+        r = qmul8( r, d);
+        g = qmul8( g, d);
+        b = qmul8( b, d);
+        return *this;
+    }
+
+    // scale down a RGB to N 256ths of it's current brightness, using
+    // 'video' dimming rules, which means that unless the scale factor is ZERO
+    // each channel is guaranteed NOT to dim down to zero.  If it's already
+    // nonzero, it'll stay nonzero, even if that means the hue shifts a little
+    // at low brightness levels.
+    inline CRGB& nscale8_video (uint8_t scaledown )
+    {
+        nscale8x3_video( r, g, b, scaledown);
+        return *this;
+    }
+
+    // %= is a synonym for nscale8_video.  Think of it is scaling down
+    // by "a percentage"
+    inline CRGB& operator%= (uint8_t scaledown )
+    {
+        nscale8x3_video( r, g, b, scaledown);
+        return *this;
+    }
+
+    // fadeLightBy is a synonym for nscale8_video( ..., 255-fadefactor)
+    inline CRGB& fadeLightBy (uint8_t fadefactor )
+    {
+        nscale8x3_video( r, g, b, 255 - fadefactor);
+        return *this;
+    }
+
+    // scale down a RGB to N 256ths of it's current brightness, using
+    // 'plain math' dimming rules, which means that if the low light levels
+    // may dim all the way to 100% black.
+    inline CRGB& nscale8 (uint8_t scaledown )
+    {
+        nscale8x3( r, g, b, scaledown);
+        return *this;
+    }
+
+    // fadeToBlackBy is a synonym for nscale8( ..., 255-fadefactor)
+    inline CRGB& fadeToBlackBy (uint8_t fadefactor )
+    {
+        nscale8x3( r, g, b, 255 - fadefactor);
+        return *this;
+    }
+
+    // "or" operator brings each channel up to the higher of the two values
+    inline CRGB& operator|= (const CRGB& rhs )
+    {
+        if( rhs.r > r) r = rhs.r;
+        if( rhs.g > g) g = rhs.g;
+        if( rhs.b > b) b = rhs.b;
+        return *this;
+    }
+    inline CRGB& operator|= (uint8_t d )
+    {
+        if( d > r) r = d;
+        if( d > g) g = d;
+        if( d > b) b = d;
+        return *this;
+    }
+
+    // "and" operator brings each channel down to the lower of the two values
+    inline CRGB& operator&= (const CRGB& rhs )
+    {
+        if( rhs.r < r) r = rhs.r;
+        if( rhs.g < g) g = rhs.g;
+        if( rhs.b < b) b = rhs.b;
+        return *this;
+    }
+    inline CRGB& operator&= (uint8_t d )
+    {
+        if( d < r) r = d;
+        if( d < g) g = d;
+        if( d < b) b = d;
+        return *this;
+    }
+
+    // this allows testing a CRGB for zero-ness
+    inline operator bool() const __attribute__((always_inline))
+    {
+        return r || g || b;
+    }
+
+    // invert each channel
+    inline CRGB operator- ()
+    {
+        CRGB retval;
+        retval.r = 255 - r;
+        retval.g = 255 - g;
+        retval.b = 255 - b;
+        return retval;
+    }
+
+
+    inline uint8_t getLuma ( )  {
+        //Y' = 0.2126 R' + 0.7152 G' + 0.0722 B'
+        //     54            183       18 (!)
+
+        uint8_t luma = scale8_LEAVING_R1_DIRTY( r, 54) + \
+        scale8_LEAVING_R1_DIRTY( g, 183) + \
+        scale8_LEAVING_R1_DIRTY( b, 18);
+        cleanup_R1();
+        return luma;
+    }
+
+    inline uint8_t getAverageLight( )  {
+        const uint8_t eightysix = 86;
+        uint8_t avg = scale8_LEAVING_R1_DIRTY( r, eightysix) + \
+        scale8_LEAVING_R1_DIRTY( g, eightysix) + \
+        scale8_LEAVING_R1_DIRTY( b, eightysix);
+        cleanup_R1();
+        return avg;
+    }
+
+    inline void maximizeBrightness( uint8_t limit = 255 )  {
+        uint8_t max = red;
+        if( green > max) max = green;
+        if( blue > max) max = blue;
+        uint16_t factor = ((uint16_t)(limit) * 256) / max;
+        red =   (red   * factor) / 256;
+        green = (green * factor) / 256;
+        blue =  (blue  * factor) / 256;
+    }
+
+    typedef enum {
+        AliceBlue=0xF0F8FF,
+        Amethyst=0x9966CC,
+        AntiqueWhite=0xFAEBD7,
+        Aqua=0x00FFFF,
+        Aquamarine=0x7FFFD4,
+        Azure=0xF0FFFF,
+        Beige=0xF5F5DC,
+        Bisque=0xFFE4C4,
+        Black=0x000000,
+        BlanchedAlmond=0xFFEBCD,
+        Blue=0x0000FF,
+        BlueViolet=0x8A2BE2,
+        Brown=0xA52A2A,
+        BurlyWood=0xDEB887,
+        CadetBlue=0x5F9EA0,
+        Chartreuse=0x7FFF00,
+        Chocolate=0xD2691E,
+        Coral=0xFF7F50,
+        CornflowerBlue=0x6495ED,
+        Cornsilk=0xFFF8DC,
+        Crimson=0xDC143C,
+        Cyan=0x00FFFF,
+        DarkBlue=0x00008B,
+        DarkCyan=0x008B8B,
+        DarkGoldenrod=0xB8860B,
+        DarkGray=0xA9A9A9,
+        DarkGreen=0x006400,
+        DarkKhaki=0xBDB76B,
+        DarkMagenta=0x8B008B,
+        DarkOliveGreen=0x556B2F,
+        DarkOrange=0xFF8C00,
+        DarkOrchid=0x9932CC,
+        DarkRed=0x8B0000,
+        DarkSalmon=0xE9967A,
+        DarkSeaGreen=0x8FBC8F,
+        DarkSlateBlue=0x483D8B,
+        DarkSlateGray=0x2F4F4F,
+        DarkTurquoise=0x00CED1,
+        DarkViolet=0x9400D3,
+        DeepPink=0xFF1493,
+        DeepSkyBlue=0x00BFFF,
+        DimGray=0x696969,
+        DodgerBlue=0x1E90FF,
+        FireBrick=0xB22222,
+        FloralWhite=0xFFFAF0,
+        ForestGreen=0x228B22,
+        Fuchsia=0xFF00FF,
+        Gainsboro=0xDCDCDC,
+        GhostWhite=0xF8F8FF,
+        Gold=0xFFD700,
+        Goldenrod=0xDAA520,
+        Gray=0x808080,
+        Green=0x008000,
+        GreenYellow=0xADFF2F,
+        Honeydew=0xF0FFF0,
+        HotPink=0xFF69B4,
+        IndianRed=0xCD5C5C,
+        Indigo=0x4B0082,
+        Ivory=0xFFFFF0,
+        Khaki=0xF0E68C,
+        Lavender=0xE6E6FA,
+        LavenderBlush=0xFFF0F5,
+        LawnGreen=0x7CFC00,
+        LemonChiffon=0xFFFACD,
+        LightBlue=0xADD8E6,
+        LightCoral=0xF08080,
+        LightCyan=0xE0FFFF,
+        LightGoldenrodYellow=0xFAFAD2,
+        LightGreen=0x90EE90,
+        LightGrey=0xD3D3D3,
+        LightPink=0xFFB6C1,
+        LightSalmon=0xFFA07A,
+        LightSeaGreen=0x20B2AA,
+        LightSkyBlue=0x87CEFA,
+        LightSlateGray=0x778899,
+        LightSteelBlue=0xB0C4DE,
+        LightYellow=0xFFFFE0,
+        Lime=0x00FF00,
+        LimeGreen=0x32CD32,
+        Linen=0xFAF0E6,
+        Magenta=0xFF00FF,
+        Maroon=0x800000,
+        MediumAquamarine=0x66CDAA,
+        MediumBlue=0x0000CD,
+        MediumOrchid=0xBA55D3,
+        MediumPurple=0x9370DB,
+        MediumSeaGreen=0x3CB371,
+        MediumSlateBlue=0x7B68EE,
+        MediumSpringGreen=0x00FA9A,
+        MediumTurquoise=0x48D1CC,
+        MediumVioletRed=0xC71585,
+        MidnightBlue=0x191970,
+        MintCream=0xF5FFFA,
+        MistyRose=0xFFE4E1,
+        Moccasin=0xFFE4B5,
+        NavajoWhite=0xFFDEAD,
+        Navy=0x000080,
+        OldLace=0xFDF5E6,
+        Olive=0x808000,
+        OliveDrab=0x6B8E23,
+        Orange=0xFFA500,
+        OrangeRed=0xFF4500,
+        Orchid=0xDA70D6,
+        PaleGoldenrod=0xEEE8AA,
+        PaleGreen=0x98FB98,
+        PaleTurquoise=0xAFEEEE,
+        PaleVioletRed=0xDB7093,
+        PapayaWhip=0xFFEFD5,
+        PeachPuff=0xFFDAB9,
+        Peru=0xCD853F,
+        Pink=0xFFC0CB,
+        Plaid=0xCC5533,
+        Plum=0xDDA0DD,
+        PowderBlue=0xB0E0E6,
+        Purple=0x800080,
+        Red=0xFF0000,
+        RosyBrown=0xBC8F8F,
+        RoyalBlue=0x4169E1,
+        SaddleBrown=0x8B4513,
+        Salmon=0xFA8072,
+        SandyBrown=0xF4A460,
+        SeaGreen=0x2E8B57,
+        Seashell=0xFFF5EE,
+        Sienna=0xA0522D,
+        Silver=0xC0C0C0,
+        SkyBlue=0x87CEEB,
+        SlateBlue=0x6A5ACD,
+        SlateGray=0x708090,
+        Snow=0xFFFAFA,
+        SpringGreen=0x00FF7F,
+        SteelBlue=0x4682B4,
+        Tan=0xD2B48C,
+        Teal=0x008080,
+        Thistle=0xD8BFD8,
+        Tomato=0xFF6347,
+        Turquoise=0x40E0D0,
+        Violet=0xEE82EE,
+        Wheat=0xF5DEB3,
+        White=0xFFFFFF,
+        WhiteSmoke=0xF5F5F5,
+        Yellow=0xFFFF00,
+        YellowGreen=0x9ACD32
+    } HTMLColorCode;
+    static uint32_t Squant;
+};
+
+
+inline __attribute__((always_inline)) bool operator== (const CRGB& lhs, const CRGB& rhs)
+{
+    return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b);
+}
+
+inline __attribute__((always_inline)) bool operator!= (const CRGB& lhs, const CRGB& rhs)
+{
+    return !(lhs == rhs);
+}
+
+inline __attribute__((always_inline)) bool operator< (const CRGB& lhs, const CRGB& rhs)
+{
+    uint16_t sl, sr;
+    sl = lhs.r + lhs.g + lhs.b;
+    sr = rhs.r + rhs.g + rhs.b;
+    return sl < sr;
+}
+
+inline __attribute__((always_inline)) bool operator> (const CRGB& lhs, const CRGB& rhs)
+{
+    uint16_t sl, sr;
+    sl = lhs.r + lhs.g + lhs.b;
+    sr = rhs.r + rhs.g + rhs.b;
+    return sl > sr;
+}
+
+inline __attribute__((always_inline)) bool operator>= (const CRGB& lhs, const CRGB& rhs)
+{
+    uint16_t sl, sr;
+    sl = lhs.r + lhs.g + lhs.b;
+    sr = rhs.r + rhs.g + rhs.b;
+    return sl >= sr;
+}
+
+inline __attribute__((always_inline)) bool operator<= (const CRGB& lhs, const CRGB& rhs)
+{
+    uint16_t sl, sr;
+    sl = lhs.r + lhs.g + lhs.b;
+    sr = rhs.r + rhs.g + rhs.b;
+    return sl <= sr;
+}
+
+
+__attribute__((always_inline))
+inline CRGB operator+( const CRGB& p1, const CRGB& p2)
+{
+    return CRGB( qadd8( p1.r, p2.r),
+                 qadd8( p1.g, p2.g),
+                 qadd8( p1.b, p2.b));
+}
+
+__attribute__((always_inline))
+inline CRGB operator-( const CRGB& p1, const CRGB& p2)
+{
+    return CRGB( qsub8( p1.r, p2.r),
+                 qsub8( p1.g, p2.g),
+                 qsub8( p1.b, p2.b));
+}
+
+__attribute__((always_inline))
+inline CRGB operator*( const CRGB& p1, uint8_t d)
+{
+    return CRGB( qmul8( p1.r, d),
+                 qmul8( p1.g, d),
+                 qmul8( p1.b, d));
+}
+
+__attribute__((always_inline))
+inline CRGB operator/( const CRGB& p1, uint8_t d)
+{
+    return CRGB( p1.r/d, p1.g/d, p1.b/d);
+}
+
+
+__attribute__((always_inline))
+inline CRGB operator&( const CRGB& p1, const CRGB& p2)
+{
+    return CRGB( p1.r < p2.r ? p1.r : p2.r,
+                 p1.g < p2.g ? p1.g : p2.g,
+                 p1.b < p2.b ? p1.b : p2.b);
+}
+
+__attribute__((always_inline))
+inline CRGB operator|( const CRGB& p1, const CRGB& p2)
+{
+    return CRGB( p1.r > p2.r ? p1.r : p2.r,
+                 p1.g > p2.g ? p1.g : p2.g,
+                 p1.b > p2.b ? p1.b : p2.b);
+}
+
+__attribute__((always_inline))
+inline CRGB operator%( const CRGB& p1, uint8_t d)
+{
+    CRGB retval( p1);
+    retval.nscale8_video( d);
+    return retval;
+}
+
+
+
+// Define RGB orderings
+enum EOrder {
+	RGB=0012,
+	RBG=0021,
+	GRB=0102,
+	GBR=0120,
+	BRG=0201,
+	BGR=0210
+};
+
+
+#endif
diff --git a/lib/FastLED/preview_changes.txt b/lib/FastLED/preview_changes.txt
new file mode 100644
index 0000000..904a5a7
--- /dev/null
+++ b/lib/FastLED/preview_changes.txt
@@ -0,0 +1,61 @@
+* Release Candidate 6
+* Rename library, offically, to FastLED, move to github
+* Update keywords with all the new stuffs
+
+Release Candidate 5
+* Gemma and Trinket: supported except for global "setBrightness"
+
+Release Candidate 4
+* Added NEOPIXEL as a synonym for WS2811
+* Fix WS2811/WS2812B timings, bring it in line to exactly 1.25ns/bit.
+* Fix handling of constant color definitions (damn you, gcc!)
+
+Release Candidate 3
+* Fixed bug when Clock and Data were on the same port
+* Added ability to set pixel color directly from HSV
+* Added ability to retrieve current random16 seed
+
+Release Candidate 2
+* mostly bug fixes
+* Fix SPI macro definitions for latest teensy3 software update
+* Teensy 2 compilation fix
+* hsv2rgb_rainbow performance fix
+
+Release Candidate 1
+* New unified/simplified API for adding/using controllers
+* fleshout clockless chip support
+* add hsv (spectrum and rainbow style colors)
+* high speed memory management operations
+* library for interpolation/easing functions
+* various api changes, addition of clear and showColor functions
+* scale value applied to all show methods
+* bug fixes for SM16716
+* performance improvements, lpd8806 exceeds 22Mbit now
+* hardware def fixes
+* allow alternate rgb color orderings
+* high speed math methods
+* rich CRGB structure
+
+Preview 3
+* True hardware SPI support for teensy (up to 20Mbit output!)
+* Minor bug fixes/tweaks
+
+Preview 2
+* Rename pin class to FastPin
+* Replace latch with select, more accurate description of what it does
+* Enforce intra-frame timing for ws2801s
+* SM16716 support
+* Add #define FAST_SPI_INTERRUPTS_WRITE_PINS to make sure world is ok w/interrupts and SPI
+* Add #define FORCE_SOFTWARE_SPI for those times when you absolutely don't want to use hardware SPI, ev
+en if you're using the hardware SPI pins
+* Add pin definitions for the arduino megas - should fix ws2811 support
+* Add pin definitions for the leonardo - should fix spi support and pin mappings
+* Add warnings when pin definitions are missing
+* Added google+ community for fastspi users - https://plus.google.com/communities/109127054924227823508
+# Add pin definitions for Teensy++ 2.0
+
+
+Preview 1
+* Initial release
+
+
diff --git a/lib/UTFT/DefaultFonts.c b/lib/UTFT/DefaultFonts.c
new file mode 100644
index 0000000..bbc822a
--- /dev/null
+++ b/lib/UTFT/DefaultFonts.c
@@ -0,0 +1,239 @@
+#if defined(__AVR__)
+	#include <avr/pgmspace.h>
+	#define fontdatatype uint8_t
+#else
+	#define PROGMEM
+	#define fontdatatype const unsigned char
+#endif
+
+// SmallFont.c
+// Font Size	: 8x12
+// Memory usage	: 1144 bytes
+// # characters	: 95
+
+fontdatatype SmallFont[1144] PROGMEM={
+0x08,0x0C,0x20,0x5F,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // <Space>
+0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, // !
+0x00,0x28,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // "
+0x00,0x00,0x28,0x28,0xFC,0x28,0x50,0xFC,0x50,0x50,0x00,0x00, // #
+0x00,0x20,0x78,0xA8,0xA0,0x60,0x30,0x28,0xA8,0xF0,0x20,0x00, // $
+0x00,0x00,0x48,0xA8,0xB0,0x50,0x28,0x34,0x54,0x48,0x00,0x00, // %
+0x00,0x00,0x20,0x50,0x50,0x78,0xA8,0xA8,0x90,0x6C,0x00,0x00, // &
+0x00,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // '
+0x00,0x04,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x08,0x04,0x00, // (
+0x00,0x40,0x20,0x10,0x10,0x10,0x10,0x10,0x10,0x20,0x40,0x00, // )
+0x00,0x00,0x00,0x20,0xA8,0x70,0x70,0xA8,0x20,0x00,0x00,0x00, // *
+0x00,0x00,0x20,0x20,0x20,0xF8,0x20,0x20,0x20,0x00,0x00,0x00, // +
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x80, // ,
+0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, // -
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, // .
+0x00,0x08,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x80,0x00, // /
+0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // 0
+0x00,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // 1
+0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x40,0x80,0xF8,0x00,0x00, // 2
+0x00,0x00,0x70,0x88,0x08,0x30,0x08,0x08,0x88,0x70,0x00,0x00, // 3
+0x00,0x00,0x10,0x30,0x50,0x50,0x90,0x78,0x10,0x18,0x00,0x00, // 4
+0x00,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0x88,0x70,0x00,0x00, // 5
+0x00,0x00,0x70,0x90,0x80,0xF0,0x88,0x88,0x88,0x70,0x00,0x00, // 6
+0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x20,0x20,0x20,0x00,0x00, // 7
+0x00,0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x88,0x70,0x00,0x00, // 8
+0x00,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x48,0x70,0x00,0x00, // 9
+0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x20,0x00,0x00, // :
+0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x20,0x00, // ;
+0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00, // <
+0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, // =
+0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00, // >
+0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x20,0x00,0x20,0x00,0x00, // ?
+0x00,0x00,0x70,0x88,0x98,0xA8,0xA8,0xB8,0x80,0x78,0x00,0x00, // @
+0x00,0x00,0x20,0x20,0x30,0x50,0x50,0x78,0x48,0xCC,0x00,0x00, // A
+0x00,0x00,0xF0,0x48,0x48,0x70,0x48,0x48,0x48,0xF0,0x00,0x00, // B
+0x00,0x00,0x78,0x88,0x80,0x80,0x80,0x80,0x88,0x70,0x00,0x00, // C
+0x00,0x00,0xF0,0x48,0x48,0x48,0x48,0x48,0x48,0xF0,0x00,0x00, // D
+0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x48,0xF8,0x00,0x00, // E
+0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x40,0xE0,0x00,0x00, // F
+0x00,0x00,0x38,0x48,0x80,0x80,0x9C,0x88,0x48,0x30,0x00,0x00, // G
+0x00,0x00,0xCC,0x48,0x48,0x78,0x48,0x48,0x48,0xCC,0x00,0x00, // H
+0x00,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // I
+0x00,0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x90,0xE0,0x00, // J
+0x00,0x00,0xEC,0x48,0x50,0x60,0x50,0x50,0x48,0xEC,0x00,0x00, // K
+0x00,0x00,0xE0,0x40,0x40,0x40,0x40,0x40,0x44,0xFC,0x00,0x00, // L
+0x00,0x00,0xD8,0xD8,0xD8,0xD8,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // M
+0x00,0x00,0xDC,0x48,0x68,0x68,0x58,0x58,0x48,0xE8,0x00,0x00, // N
+0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // O
+0x00,0x00,0xF0,0x48,0x48,0x70,0x40,0x40,0x40,0xE0,0x00,0x00, // P
+0x00,0x00,0x70,0x88,0x88,0x88,0x88,0xE8,0x98,0x70,0x18,0x00, // Q
+0x00,0x00,0xF0,0x48,0x48,0x70,0x50,0x48,0x48,0xEC,0x00,0x00, // R
+0x00,0x00,0x78,0x88,0x80,0x60,0x10,0x08,0x88,0xF0,0x00,0x00, // S
+0x00,0x00,0xF8,0xA8,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // T
+0x00,0x00,0xCC,0x48,0x48,0x48,0x48,0x48,0x48,0x30,0x00,0x00, // U
+0x00,0x00,0xCC,0x48,0x48,0x50,0x50,0x30,0x20,0x20,0x00,0x00, // V
+0x00,0x00,0xA8,0xA8,0xA8,0x70,0x50,0x50,0x50,0x50,0x00,0x00, // W
+0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x50,0x50,0xD8,0x00,0x00, // X
+0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // Y
+0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x40,0x48,0xF8,0x00,0x00, // Z
+0x00,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, // [
+0x00,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x10,0x08,0x00,0x00, // <Backslash>
+0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00, // ]
+0x00,0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC, // _
+0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // '
+0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x38,0x48,0x3C,0x00,0x00, // a
+0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0x70,0x00,0x00, // b
+0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x40,0x40,0x38,0x00,0x00, // c
+0x00,0x00,0x18,0x08,0x08,0x38,0x48,0x48,0x48,0x3C,0x00,0x00, // d
+0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x78,0x40,0x38,0x00,0x00, // e
+0x00,0x00,0x1C,0x20,0x20,0x78,0x20,0x20,0x20,0x78,0x00,0x00, // f
+0x00,0x00,0x00,0x00,0x00,0x3C,0x48,0x30,0x40,0x78,0x44,0x38, // g
+0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0xEC,0x00,0x00, // h
+0x00,0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x00, // i
+0x00,0x00,0x10,0x00,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0xE0, // j
+0x00,0x00,0xC0,0x40,0x40,0x5C,0x50,0x70,0x48,0xEC,0x00,0x00, // k
+0x00,0x00,0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // l
+0x00,0x00,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // m
+0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0xEC,0x00,0x00, // n
+0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x48,0x48,0x30,0x00,0x00, // o
+0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0x70,0x40,0xE0, // p
+0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x48,0x48,0x38,0x08,0x1C, // q
+0x00,0x00,0x00,0x00,0x00,0xD8,0x60,0x40,0x40,0xE0,0x00,0x00, // r
+0x00,0x00,0x00,0x00,0x00,0x78,0x40,0x30,0x08,0x78,0x00,0x00, // s
+0x00,0x00,0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00, // t
+0x00,0x00,0x00,0x00,0x00,0xD8,0x48,0x48,0x48,0x3C,0x00,0x00, // u
+0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x00,0x00, // v
+0x00,0x00,0x00,0x00,0x00,0xA8,0xA8,0x70,0x50,0x50,0x00,0x00, // w
+0x00,0x00,0x00,0x00,0x00,0xD8,0x50,0x20,0x50,0xD8,0x00,0x00, // x
+0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x20,0xC0, // y
+0x00,0x00,0x00,0x00,0x00,0x78,0x10,0x20,0x20,0x78,0x00,0x00, // z
+0x00,0x18,0x10,0x10,0x10,0x20,0x10,0x10,0x10,0x10,0x18,0x00, // {
+0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // |
+0x00,0x60,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0x60,0x00, // }
+0x40,0xA4,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ~
+};
+
+// BigFont.c (C)2010 by Henning Karlsen
+// Font Size	: 16x16
+// Memory usage	: 3044 bytes
+// # characters	: 95
+
+fontdatatype BigFont[3044] PROGMEM={
+0x10,0x10,0x20,0x5F,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //  <Space>
+0x00,0x00,0x00,0x00,0x07,0x00,0x0F,0x80,0x0F,0x80,0x0F,0x80,0x0F,0x80,0x0F,0x80,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00, // !
+0x00,0x00,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x06,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // "
+0x00,0x00,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x7F,0xFE,0x7F,0xFE,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x7F,0xFE,0x7F,0xFE,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x00,0x00, // #
+0x00,0x00,0x02,0x40,0x02,0x40,0x0F,0xF8,0x1F,0xF8,0x1A,0x40,0x1A,0x40,0x1F,0xF0,0x0F,0xF8,0x02,0x58,0x02,0x58,0x1F,0xF8,0x1F,0xF0,0x02,0x40,0x02,0x40,0x00,0x00, // $
+0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x10,0x0E,0x30,0x0E,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x07,0x00,0x0E,0x70,0x0C,0x70,0x08,0x70,0x00,0x00,0x00,0x00,0x00,0x00, // %
+0x00,0x00,0x00,0x00,0x0F,0x00,0x19,0x80,0x19,0x80,0x19,0x80,0x0F,0x00,0x0F,0x08,0x0F,0x98,0x19,0xF8,0x18,0xF0,0x18,0xE0,0x19,0xF0,0x0F,0x98,0x00,0x00,0x00,0x00, // &
+0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // '
+0x00,0x00,0x00,0x00,0x00,0xF0,0x01,0xC0,0x03,0x80,0x07,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x07,0x00,0x03,0x80,0x01,0xC0,0x00,0xF0,0x00,0x00,0x00,0x00, // (
+0x00,0x00,0x00,0x00,0x0F,0x00,0x03,0x80,0x01,0xC0,0x00,0xE0,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x0F,0x00,0x00,0x00,0x00,0x00, // )
+0x00,0x00,0x00,0x00,0x01,0x80,0x11,0x88,0x09,0x90,0x07,0xE0,0x07,0xE0,0x3F,0xFC,0x3F,0xFC,0x07,0xE0,0x07,0xE0,0x09,0x90,0x11,0x88,0x01,0x80,0x00,0x00,0x00,0x00, // *
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x0F,0xF0,0x0F,0xF0,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // +
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x0E,0x00,0x00,0x00, // ,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // -
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00, // ,
+0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x06,0x00,0x0E,0x00,0x1C,0x00,0x38,0x00,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x07,0x00,0x0E,0x00,0x1C,0x00,0x00,0x00,0x00,0x00, // /
+
+0x00,0x00,0x00,0x00,0x0F,0xF0,0x1C,0x38,0x1C,0x78,0x1C,0xF8,0x1C,0xF8,0x1D,0xB8,0x1D,0xB8,0x1F,0x38,0x1F,0x38,0x1E,0x38,0x1C,0x38,0x0F,0xF0,0x00,0x00,0x00,0x00, // 0
+0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x03,0x80,0x1F,0x80,0x1F,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x1F,0xF0,0x00,0x00,0x00,0x00, // 1
+0x00,0x00,0x00,0x00,0x0F,0xE0,0x1C,0x70,0x1C,0x38,0x00,0x38,0x00,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x07,0x00,0x0E,0x38,0x1C,0x38,0x1F,0xF8,0x00,0x00,0x00,0x00, // 2
+0x00,0x00,0x00,0x00,0x0F,0xE0,0x1C,0x70,0x1C,0x38,0x00,0x38,0x00,0x70,0x03,0xC0,0x03,0xC0,0x00,0x70,0x00,0x38,0x1C,0x38,0x1C,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // 3
+0x00,0x00,0x00,0x00,0x00,0xE0,0x01,0xE0,0x03,0xE0,0x06,0xE0,0x0C,0xE0,0x18,0xE0,0x1F,0xF8,0x1F,0xF8,0x00,0xE0,0x00,0xE0,0x00,0xE0,0x03,0xF8,0x00,0x00,0x00,0x00, // 4
+0x00,0x00,0x00,0x00,0x1F,0xF8,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1F,0xE0,0x1F,0xF0,0x00,0x78,0x00,0x38,0x1C,0x38,0x1C,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // 5
+0x00,0x00,0x00,0x00,0x03,0xE0,0x07,0x00,0x0E,0x00,0x1C,0x00,0x1C,0x00,0x1F,0xF0,0x1F,0xF8,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x0F,0xF0,0x00,0x00,0x00,0x00, // 6
+0x00,0x00,0x00,0x00,0x1F,0xFC,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x00,0x1C,0x00,0x38,0x00,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00, // 7
+0x00,0x00,0x00,0x00,0x0F,0xF0,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1F,0x38,0x07,0xE0,0x07,0xE0,0x1C,0xF8,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x0F,0xF0,0x00,0x00,0x00,0x00, // 8
+0x00,0x00,0x00,0x00,0x0F,0xF0,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1F,0xF8,0x0F,0xF8,0x00,0x38,0x00,0x38,0x00,0x70,0x00,0xE0,0x07,0xC0,0x00,0x00,0x00,0x00, // 9
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // :
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x03,0x80,0x03,0x80,0x03,0x80,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ;
+0x00,0x00,0x00,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x07,0x00,0x0E,0x00,0x1C,0x00,0x1C,0x00,0x0E,0x00,0x07,0x00,0x03,0x80,0x01,0xC0,0x00,0xE0,0x00,0x70,0x00,0x00, // <
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x3F,0xFC,0x00,0x00,0x00,0x00,0x3F,0xFC,0x3F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // =
+0x00,0x00,0x1C,0x00,0x0E,0x00,0x07,0x00,0x03,0x80,0x01,0xC0,0x00,0xE0,0x00,0x70,0x00,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x07,0x00,0x0E,0x00,0x1C,0x00,0x00,0x00, // >
+0x00,0x00,0x03,0xC0,0x0F,0xF0,0x1E,0x78,0x18,0x38,0x00,0x38,0x00,0x70,0x00,0xE0,0x01,0xC0,0x01,0xC0,0x00,0x00,0x00,0x00,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x00,0x00, // ?
+
+0x00,0x00,0x0F,0xF8,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xFC,0x1C,0xFC,0x1C,0xFC,0x1C,0xFC,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1F,0xF0,0x07,0xF8,0x00,0x00, // @
+0x00,0x00,0x00,0x00,0x03,0xC0,0x07,0xE0,0x0E,0x70,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1F,0xF8,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x00,0x00,0x00,0x00, // A
+0x00,0x00,0x00,0x00,0x1F,0xF0,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0F,0xF0,0x0F,0xF0,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x1F,0xF0,0x00,0x00,0x00,0x00, // B
+0x00,0x00,0x00,0x00,0x07,0xF0,0x0E,0x38,0x1C,0x38,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x38,0x0E,0x38,0x07,0xF0,0x00,0x00,0x00,0x00, // C
+0x00,0x00,0x00,0x00,0x1F,0xE0,0x0E,0x70,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x70,0x1F,0xE0,0x00,0x00,0x00,0x00, // D
+0x00,0x00,0x00,0x00,0x1F,0xF8,0x0E,0x18,0x0E,0x08,0x0E,0x00,0x0E,0x30,0x0F,0xF0,0x0F,0xF0,0x0E,0x30,0x0E,0x00,0x0E,0x08,0x0E,0x18,0x1F,0xF8,0x00,0x00,0x00,0x00, // E
+0x00,0x00,0x00,0x00,0x1F,0xF8,0x0E,0x18,0x0E,0x08,0x0E,0x00,0x0E,0x30,0x0F,0xF0,0x0F,0xF0,0x0E,0x30,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x1F,0x00,0x00,0x00,0x00,0x00, // F
+0x00,0x00,0x00,0x00,0x07,0xF0,0x0E,0x38,0x1C,0x38,0x1C,0x38,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1C,0xF8,0x1C,0x38,0x1C,0x38,0x0E,0x38,0x07,0xF8,0x00,0x00,0x00,0x00, // G
+0x00,0x00,0x00,0x00,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1F,0xF0,0x1F,0xF0,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x00,0x00,0x00,0x00, // H
+0x00,0x00,0x00,0x00,0x0F,0xE0,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x0F,0xE0,0x00,0x00,0x00,0x00, // I
+0x00,0x00,0x00,0x00,0x01,0xFC,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x38,0x70,0x38,0x70,0x38,0x70,0x38,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // J
+0x00,0x00,0x00,0x00,0x1E,0x38,0x0E,0x38,0x0E,0x70,0x0E,0xE0,0x0F,0xC0,0x0F,0x80,0x0F,0x80,0x0F,0xC0,0x0E,0xE0,0x0E,0x70,0x0E,0x38,0x1E,0x38,0x00,0x00,0x00,0x00, // K
+0x00,0x00,0x00,0x00,0x1F,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x08,0x0E,0x18,0x0E,0x38,0x1F,0xF8,0x00,0x00,0x00,0x00, // L
+0x00,0x00,0x00,0x00,0x1C,0x1C,0x1E,0x3C,0x1F,0x7C,0x1F,0xFC,0x1F,0xFC,0x1D,0xDC,0x1C,0x9C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x00,0x00,0x00,0x00, // M
+0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x1C,0x1E,0x1C,0x1F,0x1C,0x1F,0x9C,0x1D,0xDC,0x1C,0xFC,0x1C,0x7C,0x1C,0x3C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x00,0x00,0x00,0x00, // N
+0x00,0x00,0x00,0x00,0x03,0xE0,0x07,0xF0,0x0E,0x38,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x0E,0x38,0x07,0xF0,0x03,0xE0,0x00,0x00,0x00,0x00, // O
+
+0x00,0x00,0x00,0x00,0x1F,0xF0,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0F,0xF0,0x0F,0xF0,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x1F,0x00,0x00,0x00,0x00,0x00, // P
+0x00,0x00,0x00,0x00,0x03,0xE0,0x0F,0x78,0x0E,0x38,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x7C,0x1C,0xFC,0x0F,0xF8,0x0F,0xF8,0x00,0x38,0x00,0xFC,0x00,0x00, // Q
+0x00,0x00,0x00,0x00,0x1F,0xF0,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0F,0xF0,0x0F,0xF0,0x0E,0x70,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x1E,0x38,0x00,0x00,0x00,0x00, // R
+0x00,0x00,0x00,0x00,0x0F,0xF0,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x1C,0x00,0x0F,0xE0,0x07,0xF0,0x00,0x38,0x1C,0x38,0x1C,0x38,0x1C,0x38,0x0F,0xF0,0x00,0x00,0x00,0x00, // S
+0x00,0x00,0x00,0x00,0x1F,0xFC,0x19,0xCC,0x11,0xC4,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x07,0xF0,0x00,0x00,0x00,0x00, // T
+0x00,0x00,0x00,0x00,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // U
+0x00,0x00,0x00,0x00,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x03,0x80,0x00,0x00,0x00,0x00, // V
+0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x9C,0x1C,0x9C,0x1C,0x9C,0x0F,0xF8,0x0F,0xF8,0x07,0x70,0x07,0x70,0x00,0x00,0x00,0x00, // W
+0x00,0x00,0x00,0x00,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x03,0x80,0x03,0x80,0x07,0xC0,0x0E,0xE0,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x00,0x00,0x00,0x00, // X
+0x00,0x00,0x00,0x00,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x0F,0xE0,0x00,0x00,0x00,0x00, // Y
+0x00,0x00,0x00,0x00,0x1F,0xF8,0x1C,0x38,0x18,0x38,0x10,0x70,0x00,0xE0,0x01,0xC0,0x03,0x80,0x07,0x00,0x0E,0x08,0x1C,0x18,0x1C,0x38,0x1F,0xF8,0x00,0x00,0x00,0x00, // Z
+0x00,0x00,0x00,0x00,0x07,0xF0,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0xF0,0x00,0x00,0x00,0x00, // [
+0x00,0x00,0x00,0x00,0x10,0x00,0x18,0x00,0x1C,0x00,0x0E,0x00,0x07,0x00,0x03,0x80,0x01,0xC0,0x00,0xE0,0x00,0x70,0x00,0x38,0x00,0x1C,0x00,0x07,0x00,0x00,0x00,0x00, // <Backslash>
+0x00,0x00,0x00,0x00,0x07,0xF0,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x07,0xF0,0x00,0x00,0x00,0x00, // ]
+0x00,0x00,0x01,0x80,0x03,0xC0,0x07,0xE0,0x0E,0x70,0x1C,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0x7F,0xFF, // _
+
+0x00,0x00,0x00,0x00,0x1C,0x00,0x1C,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // '
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xE0,0x00,0x70,0x00,0x70,0x0F,0xF0,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0F,0xD8,0x00,0x00,0x00,0x00, // a
+0x00,0x00,0x00,0x00,0x1E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0F,0xF0,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x1B,0xF0,0x00,0x00,0x00,0x00, // b
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xE0,0x1C,0x70,0x1C,0x70,0x1C,0x00,0x1C,0x00,0x1C,0x70,0x1C,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // c
+0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x70,0x00,0x70,0x00,0x70,0x0F,0xF0,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0F,0xD8,0x00,0x00,0x00,0x00, // d
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xE0,0x1C,0x70,0x1C,0x70,0x1F,0xF0,0x1C,0x00,0x1C,0x70,0x1C,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // e
+0x00,0x00,0x00,0x00,0x03,0xE0,0x07,0x70,0x07,0x70,0x07,0x00,0x07,0x00,0x1F,0xE0,0x1F,0xE0,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x1F,0xC0,0x00,0x00,0x00,0x00, // f
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xD8,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0F,0xF0,0x07,0xF0,0x00,0x70,0x1C,0x70,0x0F,0xE0, // g
+0x00,0x00,0x00,0x00,0x1E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0xF0,0x0F,0x38,0x0F,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x1E,0x38,0x00,0x00,0x00,0x00, // h
+0x00,0x00,0x00,0x00,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x00,0x00,0x0F,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x0F,0xF8,0x00,0x00,0x00,0x00, // i
+0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x00,0x03,0xF0,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x1C,0x70,0x0C,0xF0,0x07,0xE0, // j
+0x00,0x00,0x00,0x00,0x1E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x38,0x0E,0x70,0x0E,0xE0,0x0F,0xC0,0x0E,0xE0,0x0E,0x70,0x0E,0x38,0x1E,0x38,0x00,0x00,0x00,0x00, // k
+0x00,0x00,0x00,0x00,0x0F,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x0F,0xF8,0x00,0x00,0x00,0x00, // l
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x1C,0x9C,0x1C,0x9C,0x1C,0x9C,0x1C,0x9C,0x1C,0x9C,0x1C,0x9C,0x1C,0x9C,0x00,0x00,0x00,0x00, // m
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xE0,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x00,0x00,0x00,0x00, // n
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xE0,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // o
+
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0xF0,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0F,0xF0,0x0E,0x00,0x0E,0x00,0x1F,0x00, // p
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xB0,0x38,0xE0,0x38,0xE0,0x38,0xE0,0x38,0xE0,0x38,0xE0,0x1F,0xE0,0x00,0xE0,0x00,0xE0,0x01,0xF0, // q
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0xF0,0x0F,0xF8,0x0F,0x38,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x1F,0x00,0x00,0x00,0x00,0x00, // r
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xE0,0x1C,0x30,0x1C,0x30,0x0F,0x80,0x03,0xE0,0x18,0x70,0x18,0x70,0x0F,0xE0,0x00,0x00,0x00,0x00, // s
+0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x07,0x00,0x1F,0xF0,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x70,0x07,0x70,0x03,0xE0,0x00,0x00,0x00,0x00, // t
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0F,0xD8,0x00,0x00,0x00,0x00, // u
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x03,0x80,0x00,0x00,0x00,0x00, // v
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x9C,0x1C,0x9C,0x0F,0xF8,0x07,0x70,0x07,0x70,0x00,0x00,0x00,0x00, // w
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0xE0,0x1C,0xE0,0x0F,0xC0,0x07,0x80,0x07,0x80,0x0F,0xC0,0x1C,0xE0,0x1C,0xE0,0x00,0x00,0x00,0x00, // x
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x07,0xF0,0x03,0xE0,0x00,0xE0,0x01,0xC0,0x1F,0x80, // y
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xE0,0x18,0xE0,0x11,0xC0,0x03,0x80,0x07,0x00,0x0E,0x20,0x1C,0x60,0x1F,0xE0,0x00,0x00,0x00,0x00, // z
+0x00,0x00,0x00,0x00,0x01,0xF8,0x03,0x80,0x03,0x80,0x03,0x80,0x07,0x00,0x1C,0x00,0x1C,0x00,0x07,0x00,0x03,0x80,0x03,0x80,0x03,0x80,0x01,0xF8,0x00,0x00,0x00,0x00, // {
+0x00,0x00,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x00,0x00, // |
+0x00,0x00,0x00,0x00,0x1F,0x80,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x00,0xE0,0x00,0x38,0x00,0x38,0x00,0xE0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x1F,0x80,0x00,0x00,0x00,0x00, // }
+0x00,0x00,0x00,0x00,0x1F,0x1C,0x3B,0x9C,0x39,0xDC,0x38,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00  // ~
+};
+
+// SevenSegNumFont.c
+// Font Size	: 32x50
+// Memory usage	: 2004 bytes
+// # characters	: 10
+
+fontdatatype SevenSegNumFont[2004] PROGMEM={
+0x20,0x32,0x30,0x0A,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x60,0x0C,0xFF,0xFE,0xF0,0x1E,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3E,0x00,0x00,0x78,0x38,0x00,0x00,0x18,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x38,0x00,0x00,0x18,0x3E,0x00,0x00,0x78,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x1E,0x00,0x00,0xF0,0x0C,0xFF,0xFE,0x60,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 0
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xF0,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x78,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 1
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x60,0x00,0xFF,0xFE,0xF0,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0x78,0x01,0xFF,0xFE,0x18,0x03,0xFF,0xFF,0x88,0x0F,0xFF,0xFF,0xE0,0x27,0xFF,0xFF,0xC0,0x39,0xFF,0xFF,0x00,0x3E,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0C,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 2
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x60,0x00,0xFF,0xFE,0xF0,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0x78,0x01,0xFF,0xFE,0x18,0x03,0xFF,0xFF,0x88,0x0F,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xC0,0x01,0xFF,0xFF,0x18,0x00,0x00,0x00,0x78,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0xF0,0x00,0xFF,0xFE,0x60,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 3
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x0C,0x00,0x00,0xF0,0x1E,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3E,0x00,0x00,0x78,0x39,0xFF,0xFE,0x18,0x23,0xFF,0xFF,0x88,0x0F,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xC0,0x01,0xFF,0xFF,0x18,0x00,0x00,0x00,0x78,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 4
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x0C,0xFF,0xFE,0x00,0x1E,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x39,0xFF,0xFE,0x00,0x23,0xFF,0xFF,0x80,0x0F,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xC0,0x01,0xFF,0xFF,0x18,0x00,0x00,0x00,0x78,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0xF0,0x00,0xFF,0xFE,0x60,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 5
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x0C,0xFF,0xFE,0x00,0x1E,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x39,0xFF,0xFE,0x00,0x23,0xFF,0xFF,0x80,0x0F,0xFF,0xFF,0xE0,0x27,0xFF,0xFF,0xC0,0x39,0xFF,0xFF,0x18,0x3E,0x00,0x00,0x78,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x1E,0x00,0x00,0xF0,0x0C,0xFF,0xFE,0x60,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 6
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x60,0x00,0xFF,0xFE,0xF0,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x78,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 7
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x60,0x0C,0xFF,0xFE,0xF0,0x1E,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3E,0x00,0x00,0x78,0x39,0xFF,0xFE,0x18,0x23,0xFF,0xFF,0x88,0x0F,0xFF,0xFF,0xE0,0x27,0xFF,0xFF,0xC0,0x39,0xFF,0xFF,0x18,0x3E,0x00,0x00,0x78,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x1E,0x00,0x00,0xF0,0x0C,0xFF,0xFE,0x60,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 8
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x60,0x0C,0xFF,0xFE,0xF0,0x1E,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3F,0x00,0x01,0xF8,0x3E,0x00,0x00,0x78,0x39,0xFF,0xFE,0x18,0x23,0xFF,0xFF,0x88,0x0F,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xC0,0x01,0xFF,0xFF,0x18,0x00,0x00,0x00,0x78,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0xF0,0x00,0xFF,0xFE,0x60,0x01,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0x80,0x01,0xFF,0xFF,0x00,0x00,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 9
+};
diff --git a/lib/UTFT/HW_AVR.h b/lib/UTFT/HW_AVR.h
new file mode 100644
index 0000000..e9d448f
--- /dev/null
+++ b/lib/UTFT/HW_AVR.h
@@ -0,0 +1,102 @@
+// *** Hardwarespecific functions ***
+void UTFT::LCD_Writ_Bus(char VH,char VL, byte mode)
+{
+	switch (mode)
+	{
+	case 1:
+		if (display_serial_mode==SERIAL_4PIN)
+		{
+		if (VH==1)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		}
+		else
+		{
+		if (VH==1)
+			sbi(P_RS, B_RS);
+		else
+			cbi(P_RS, B_RS);
+		}
+
+		if (VL & 0x80)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x40)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x20)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x10)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x08)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x04)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x02)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x01)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		break;
+	case 8:
+
+	//	PORTA = VH;
+		//pulse_low(P_WR, B_WR);
+		PORTA = VL;
+		pulse_low(P_WR, B_WR);
+
+		break;
+	case 16:
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+		PORTA = VH;
+		PORTC = VL;
+#else
+		PORTD = VH;
+		cport(PORTC, 0xFC);
+		sport(PORTC, (VL>>6) & 0x03);
+		PORTB =  VL & 0x3F;
+#endif
+		pulse_low(P_WR, B_WR);
+		break;
+	}
+}
+
+void UTFT::_set_direction_registers(byte mode)
+{
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+	DDRA = 0xFF;
+	if (mode==16)
+		DDRC = 0xFF;
+#else
+	DDRD = 0xFF;
+	if (mode==16)
+	{
+		DDRB |= 0x3F;
+		DDRC |= 0x03;
+	}
+#endif
+
+}
diff --git a/lib/UTFT/HW_AVR.h.bak b/lib/UTFT/HW_AVR.h.bak
new file mode 100644
index 0000000..b99c13c
--- /dev/null
+++ b/lib/UTFT/HW_AVR.h.bak
@@ -0,0 +1,107 @@
+// *** Hardwarespecific functions ***
+void UTFT::LCD_Writ_Bus(char VH,char VL, byte mode)
+{
+	switch (mode)
+	{
+	case 1:
+		if (display_serial_mode==SERIAL_4PIN)
+		{
+		if (VH==1)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		}
+		else
+		{
+		if (VH==1)
+			sbi(P_RS, B_RS);
+		else
+			cbi(P_RS, B_RS);
+		}
+
+		if (VL & 0x80)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x40)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x20)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x10)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x08)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x04)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x02)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x01)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		break;
+	case 8:
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+		PORTA = VH;
+		pulse_low(P_WR, B_WR);
+		PORTA = VL;
+		pulse_low(P_WR, B_WR);
+#else
+		PORTD = VH;
+		pulse_low(P_WR, B_WR);
+		PORTD = VL;
+		pulse_low(P_WR, B_WR);
+#endif
+		break;
+	case 16:
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+		PORTA = VH;
+		PORTC = VL;
+#else
+		PORTD = VH;
+		cport(PORTC, 0xFC);
+		sport(PORTC, (VL>>6) & 0x03);
+		PORTB =  VL & 0x3F;
+#endif
+		pulse_low(P_WR, B_WR);
+		break;
+	}
+}
+
+void UTFT::_set_direction_registers(byte mode)
+{
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+	DDRA = 0xFF;
+	if (mode==16)
+		DDRC = 0xFF;
+#else
+	DDRD = 0xFF;
+	if (mode==16)
+	{
+		DDRB |= 0x3F;
+		DDRC |= 0x03;
+	}
+#endif
+
+}
diff --git a/lib/UTFT/HW_AVR_defines.h b/lib/UTFT/HW_AVR_defines.h
new file mode 100644
index 0000000..f580fb3
--- /dev/null
+++ b/lib/UTFT/HW_AVR_defines.h
@@ -0,0 +1,16 @@
+// *** Hardwarespecific defines ***
+#define cbi(reg, bitmask) *reg &= ~bitmask
+#define sbi(reg, bitmask) *reg |= bitmask
+#define pulse_high(reg, bitmask) sbi(reg, bitmask); cbi(reg, bitmask);
+#define pulse_low(reg, bitmask) cbi(reg, bitmask); sbi(reg, bitmask);
+
+#define cport(port, data) port &= data
+#define sport(port, data) port |= data
+
+#define swap(type, i, j) {type t = i; i = j; j = t;}
+
+#define fontbyte(x) pgm_read_byte(&cfont.font[x])
+
+#define regtype volatile uint8_t
+#define regsize uint8_t
+#define bitmapdatatype unsigned int*
diff --git a/lib/UTFT/HW_PIC32.h b/lib/UTFT/HW_PIC32.h
new file mode 100644
index 0000000..3ad0cda
--- /dev/null
+++ b/lib/UTFT/HW_PIC32.h
@@ -0,0 +1,91 @@
+// *** Hardwarespecific functions ***
+
+void UTFT::LCD_Writ_Bus(char VH,char VL, byte mode)
+{
+	switch (mode)
+	{
+	case 1:
+		if (display_serial_mode==SERIAL_4PIN)
+		{
+		if (VH==1)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		}
+		else
+		{
+		if (VH==1)
+			sbi(P_RS, B_RS);
+		else
+			cbi(P_RS, B_RS);
+		}
+
+		if (VL & 0x80)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x40)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x20)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x10)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x08)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x04)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x02)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		if (VL & 0x01)
+			sbi(P_SDA, B_SDA);
+		else
+			cbi(P_SDA, B_SDA);
+		pulse_low(P_SCL, B_SCL);
+		break;
+	case 8:
+		LATECLR = 0xFF;
+		LATESET = VH;
+		*P_WR &= ~B_WR;
+		*P_WR |= B_WR;
+		LATECLR = 0xFF;
+		LATESET = VL;
+		*P_WR &= ~B_WR;
+		*P_WR |= B_WR;
+		break;
+	case 16:
+		LATDCLR = 0xFF;
+		LATDSET = VL & 0xFF;
+		LATECLR = 0xFF;
+		LATESET = VH & 0xFF;
+		*P_WR &= ~B_WR;
+		*P_WR |= B_WR;
+		break;
+	}
+}
+
+void UTFT::_set_direction_registers(byte mode)
+{
+	TRISE=0;
+	if (mode==16)
+		TRISD=0;
+}
diff --git a/lib/UTFT/HW_PIC32_defines.h b/lib/UTFT/HW_PIC32_defines.h
new file mode 100644
index 0000000..b988270
--- /dev/null
+++ b/lib/UTFT/HW_PIC32_defines.h
@@ -0,0 +1,18 @@
+// *** Hardwarespecific defines ***
+#define cbi(reg, bitmask) (*(reg + 1)) = bitmask
+#define sbi(reg, bitmask) (*(reg + 2)) = bitmask
+#define pulse_high(reg, bitmask) sbi(reg, bitmask); cbi(reg, bitmask);
+#define pulse_low(reg, bitmask) cbi(reg, bitmask); sbi(reg, bitmask);
+
+#define cport(port, data) port &= data
+#define sport(port, data) port |= data
+
+#define swap(type, i, j) {type t = i; i = j; j = t;}
+
+#define fontbyte(x) cfont.font[x]
+
+#define PROGMEM
+#define regtype volatile uint32_t
+#define regsize uint16_t
+#define bitmapdatatype unsigned short*
+
diff --git a/lib/UTFT/LICENSE.txt b/lib/UTFT/LICENSE.txt
new file mode 100644
index 0000000..77cec6d
--- /dev/null
+++ b/lib/UTFT/LICENSE.txt
@@ -0,0 +1,458 @@
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
diff --git a/lib/UTFT/UTFT.cpp b/lib/UTFT/UTFT.cpp
new file mode 100644
index 0000000..7dd6d3c
--- /dev/null
+++ b/lib/UTFT/UTFT.cpp
@@ -0,0 +1,1145 @@
+/*
+  UTFT.cpp - Arduino/chipKit library support for Color TFT LCD Boards
+  Copyright (C)2010-2012 Henning Karlsen. All right reserved
+
+  This library is the continuation of my ITDB02_Graph, ITDB02_Graph16
+  and RGB_GLCD libraries for Arduino and chipKit. As the number of
+  supported display modules and controllers started to increase I felt
+  it was time to make a single, universal library as it will be much
+  easier to maintain in the future.
+
+  Basic functionality of this library was origianlly based on the
+  demo-code provided by ITead studio (for the ITDB02 modules) and
+  NKC Electronics (for the RGB GLCD module/shield).
+
+  This library supports a number of 8bit, 16bit and serial graphic
+  displays, and will work with both Arduino and chipKit boards. For a
+  full list of tested display modules and controllers, see the
+  document UTFT_Supported_display_modules_&_controllers.pdf.
+
+  When using 8bit and 16bit display modules there are some
+  requirements you must adhere to. These requirements can be found
+  in the document UTFT_Requirements.pdf.
+  There are no special requirements when using serial displays.
+
+  You can always find the latest version of the library at
+  http://electronics.henningkarlsen.com/
+
+  If you make any modifications or improvements to the code, I would
+  appreciate that you share the code with me so that I might include
+  it in the next release. I can be contacted through
+  http://electronics.henningkarlsen.com/contact.php.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "UTFT.h"
+#include <pins_arduino.h>
+#if defined(__AVR__)
+	#include <avr/pgmspace.h>
+	#include "HW_AVR.h"
+#else
+	#include "HW_PIC32.h"
+#endif
+#include "memorysaver.h"
+
+UTFT::UTFT()
+{
+}
+
+UTFT::UTFT(byte model, int RS, int WR,int CS, int RST, int SER)
+{
+	switch (model)
+	{
+		case HX8347A:
+			disp_x_size=239;
+			disp_y_size=319;
+			display_transfer_mode=16;
+			break;
+		case ILI9327:
+			disp_x_size=239;
+			disp_y_size=399;
+			display_transfer_mode=8;
+			break;
+		case SSD1289:
+			disp_x_size=239;
+			disp_y_size=319;
+			display_transfer_mode=16;
+			break;
+
+	}
+	display_model=model;
+
+	if (display_transfer_mode!=1)
+	{
+		_set_direction_registers(display_transfer_mode);
+		P_RS	= portOutputRegister(digitalPinToPort(RS));
+		B_RS	= digitalPinToBitMask(RS);
+		P_WR	= portOutputRegister(digitalPinToPort(WR));
+		B_WR	= digitalPinToBitMask(WR);
+		P_CS	= portOutputRegister(digitalPinToPort(CS));
+		B_CS	= digitalPinToBitMask(CS);
+		P_RST	= portOutputRegister(digitalPinToPort(RST));
+		B_RST	= digitalPinToBitMask(RST);
+		pinMode(RS,OUTPUT);
+		pinMode(WR,OUTPUT);
+		pinMode(CS,OUTPUT);
+		pinMode(RST,OUTPUT);
+	}
+	else
+	{
+		P_SDA	= portOutputRegister(digitalPinToPort(RS));
+		B_SDA	= digitalPinToBitMask(RS);
+		P_SCL	= portOutputRegister(digitalPinToPort(WR));
+		B_SCL	= digitalPinToBitMask(WR);
+		P_CS	= portOutputRegister(digitalPinToPort(CS));
+		B_CS	= digitalPinToBitMask(CS);
+		P_RST	= portOutputRegister(digitalPinToPort(RST));
+		B_RST	= digitalPinToBitMask(RST);
+		if (display_serial_mode!=SERIAL_4PIN)
+		{
+			P_RS	= portOutputRegister(digitalPinToPort(SER));
+			B_RS	= digitalPinToBitMask(SER);
+			pinMode(SER,OUTPUT);
+		}
+		pinMode(RS,OUTPUT);
+		pinMode(WR,OUTPUT);
+		pinMode(CS,OUTPUT);
+		pinMode(RST,OUTPUT);
+	}
+}
+
+void UTFT::LCD_Write_COM(char VL)
+{
+	if (display_transfer_mode!=1)
+	{
+		cbi(P_RS, B_RS);
+		LCD_Writ_Bus(0x00,VL,display_transfer_mode);
+	}
+	else
+		LCD_Writ_Bus(0x00,VL,display_transfer_mode);
+}
+
+
+
+void UTFT::LCD_Write_DATA(char VL)
+{
+	if (display_transfer_mode!=1)
+	{
+		sbi(P_RS, B_RS);
+		LCD_Writ_Bus(0x00,VL,display_transfer_mode);
+	}
+	else
+		LCD_Writ_Bus(0x01,VL,display_transfer_mode);
+}
+
+void UTFT::LCD_Write_COM_DATA(char com1,int dat1)
+{
+     LCD_Write_COM(com1);
+     LCD_Write_DATA(dat1);
+}
+
+void UTFT::InitLCD(byte orientation)
+{
+	orient=orientation;
+
+	sbi(P_RST, B_RST);
+	delay(1);
+	cbi(P_RST, B_RST);
+	delay(10);
+	sbi(P_RST, B_RST);
+	delay(50);
+
+	cbi(P_CS, B_CS);
+
+	switch(display_model)
+	{
+
+#ifndef DISABLE_ILI9327
+	case ILI9327:
+		LCD_Write_COM(0xE9);
+		LCD_Write_DATA(0x20);
+		LCD_Write_COM(0x11); //Exit Sleep
+		delay(100);
+	    LCD_Write_COM(0xD1);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x71);
+		LCD_Write_DATA(0x19);
+		LCD_Write_COM(0xD0);
+		LCD_Write_DATA(0x07);
+		LCD_Write_DATA(0x01);
+		LCD_Write_DATA(0x8);
+		LCD_Write_COM(0x36);
+		LCD_Write_DATA(0x48);
+	    LCD_Write_COM(0x3A);
+		LCD_Write_DATA(0x05);
+	    LCD_Write_COM(0xC1);
+		LCD_Write_DATA(0x10);
+		LCD_Write_DATA(0x10);
+		LCD_Write_DATA(0x02);
+		LCD_Write_DATA(0x02);
+		LCD_Write_COM(0xC0); //Set Default Gamma
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x35);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x01);
+		LCD_Write_DATA(0x02);
+		LCD_Write_COM(0xC5); //Set frame rate
+		LCD_Write_DATA(0x04);
+		LCD_Write_COM(0xD2); //power setting
+		LCD_Write_DATA(0x01);
+		LCD_Write_COM(0x44);
+		LCD_Write_COM(0xC8); //Set Gamma
+		LCD_Write_DATA(0x04);
+		LCD_Write_DATA(0x67);
+		LCD_Write_DATA(0x35);
+		LCD_Write_DATA(0x04);
+		LCD_Write_DATA(0x08);
+		LCD_Write_DATA(0x06);
+		LCD_Write_DATA(0x24);
+		LCD_Write_DATA(0x1);
+		LCD_Write_DATA(0x37);
+		LCD_Write_DATA(0x40);
+		LCD_Write_DATA(0x03);
+		LCD_Write_DATA(0x10);
+		LCD_Write_DATA(0x8);
+		LCD_Write_DATA(0x80);
+		LCD_Write_DATA(0x0);
+		LCD_Write_COM(0x2A);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0xeF);
+		LCD_Write_COM(0x2B);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x00);
+		LCD_Write_DATA(0x01);
+		LCD_Write_DATA(0x3F);
+		LCD_Write_COM(0x29); //display on
+		LCD_Write_COM(0x2C); //display on
+
+		break;
+#endif
+
+	}
+
+	sbi (P_CS, B_CS);
+
+	setColor(255, 255, 255);
+	setBackColor(0, 0, 0);
+	cfont.font=0;
+}
+
+void UTFT::setXY(word x1, word y1, word x2, word y2)
+{
+	int tmp;
+
+	if (orient==LANDSCAPE)
+	{
+		swap(word, x1, y1);
+		swap(word, x2, y2)
+		y1=disp_y_size-y1;
+		y2=disp_y_size-y2;
+		swap(word, y1, y2)
+	}
+
+
+		LCD_Write_COM(0x2a);
+  		LCD_Write_DATA(x1>>8);
+  		LCD_Write_DATA(x1);
+  		LCD_Write_DATA(x2>>8);
+  		LCD_Write_DATA(x2);
+  		LCD_Write_COM(0x2b);
+  		LCD_Write_DATA(y1>>8);
+  		LCD_Write_DATA(y1);
+  		LCD_Write_DATA(y2>>8);
+  		LCD_Write_DATA(y2);
+  		LCD_Write_COM(0x2c);
+
+
+
+}
+
+void UTFT::clrXY()
+{
+	if (orient==PORTRAIT)
+		setXY(0,0,disp_x_size,disp_y_size);
+	else
+		setXY(0,0,disp_y_size,disp_x_size);
+}
+
+void UTFT::drawRect(int x1, int y1, int x2, int y2)
+{
+	int tmp;
+
+	if (x1>x2)
+	{
+		swap(int, x1, x2);
+	}
+	if (y1>y2)
+	{
+		swap(int, y1, y2);
+	}
+
+	drawHLine(x1, y1, x2-x1);
+	drawHLine(x1, y2, x2-x1);
+	drawVLine(x1, y1, y2-y1);
+	drawVLine(x2, y1, y2-y1);
+}
+
+void UTFT::drawRoundRect(int x1, int y1, int x2, int y2)
+{
+	int tmp;
+
+	if (x1>x2)
+	{
+		swap(int, x1, x2);
+	}
+	if (y1>y2)
+	{
+		swap(int, y1, y2);
+	}
+	if ((x2-x1)>4 && (y2-y1)>4)
+	{
+		drawPixel(x1+1,y1+1);
+		drawPixel(x2-1,y1+1);
+		drawPixel(x1+1,y2-1);
+		drawPixel(x2-1,y2-1);
+		drawHLine(x1+2, y1, x2-x1-4);
+		drawHLine(x1+2, y2, x2-x1-4);
+		drawVLine(x1, y1+2, y2-y1-4);
+		drawVLine(x2, y1+2, y2-y1-4);
+	}
+}
+
+void UTFT::fillRect(int x1, int y1, int x2, int y2)
+{
+	int tmp;
+
+	if (x1>x2)
+	{
+		swap(int, x1, x2);
+	}
+	if (y1>y2)
+	{
+		swap(int, y1, y2);
+	}
+
+	if (orient==PORTRAIT)
+	{
+		for (int i=0; i<((y2-y1)/2)+1; i++)
+		{
+			drawHLine(x1, y1+i, x2-x1);
+			drawHLine(x1, y2-i, x2-x1);
+		}
+	}
+	else
+	{
+		for (int i=0; i<((x2-x1)/2)+1; i++)
+		{
+			drawVLine(x1+i, y1, y2-y1);
+			drawVLine(x2-i, y1, y2-y1);
+		}
+	}
+}
+
+void UTFT::fillRoundRect(int x1, int y1, int x2, int y2)
+{
+	int tmp;
+
+	if (x1>x2)
+	{
+		swap(int, x1, x2);
+	}
+	if (y1>y2)
+	{
+		swap(int, y1, y2);
+	}
+
+	if ((x2-x1)>4 && (y2-y1)>4)
+	{
+		for (int i=0; i<((y2-y1)/2)+1; i++)
+		{
+			switch(i)
+			{
+			case 0:
+				drawHLine(x1+2, y1+i, x2-x1-4);
+				drawHLine(x1+2, y2-i, x2-x1-4);
+				break;
+			case 1:
+				drawHLine(x1+1, y1+i, x2-x1-2);
+				drawHLine(x1+1, y2-i, x2-x1-2);
+				break;
+			default:
+				drawHLine(x1, y1+i, x2-x1);
+				drawHLine(x1, y2-i, x2-x1);
+			}
+		}
+	}
+}
+
+void UTFT::drawCircle(int x, int y, int radius)
+{
+	int f = 1 - radius;
+	int ddF_x = 1;
+	int ddF_y = -2 * radius;
+	int x1 = 0;
+	int y1 = radius;
+	char ch, cl;
+
+	ch=((fcolorr&248)|fcolorg>>5);
+	cl=((fcolorg&28)<<3|fcolorb>>3);
+
+	cbi(P_CS, B_CS);
+	setXY(x, y + radius, x, y + radius);
+	LCD_Write_DATA(cl);
+	setXY(x, y - radius, x, y - radius);
+	LCD_Write_DATA(cl);
+	setXY(x + radius, y, x + radius, y);
+	LCD_Write_DATA(cl);
+	setXY(x - radius, y, x - radius, y);
+	LCD_Write_DATA(cl);
+
+	while(x1 < y1)
+	{
+		if(f >= 0)
+		{
+			y1--;
+			ddF_y += 2;
+			f += ddF_y;
+		}
+		x1++;
+		ddF_x += 2;
+		f += ddF_x;
+		setXY(x + x1, y + y1, x + x1, y + y1);
+		LCD_Write_DATA(cl);
+		setXY(x - x1, y + y1, x - x1, y + y1);
+		LCD_Write_DATA(cl);
+		setXY(x + x1, y - y1, x + x1, y - y1);
+		LCD_Write_DATA(cl);
+		setXY(x - x1, y - y1, x - x1, y - y1);
+		LCD_Write_DATA(cl);
+		setXY(x + y1, y + x1, x + y1, y + x1);
+		LCD_Write_DATA(cl);
+		setXY(x - y1, y + x1, x - y1, y + x1);
+		LCD_Write_DATA(cl);
+		setXY(x + y1, y - x1, x + y1, y - x1);
+		LCD_Write_DATA(cl);
+		setXY(x - y1, y - x1, x - y1, y - x1);
+		LCD_Write_DATA(cl);
+	}
+	sbi(P_CS, B_CS);
+	clrXY();
+}
+
+void UTFT::fillCircle(int x, int y, int radius)
+{
+	cbi(P_CS, B_CS);
+	for(int y1=-radius; y1<=radius; y1++)
+		for(int x1=-radius; x1<=radius; x1++)
+			if(x1*x1+y1*y1 <= radius*radius)
+			{
+				setXY(x+x1, y+y1, x+x1, y+y1);
+				LCD_Write_DATA((fcolorr&248)|fcolorg>>5);
+				LCD_Write_DATA((fcolorg&28)<<3|fcolorb>>3);
+			}
+	sbi(P_CS, B_CS);
+	clrXY();
+}
+void UTFT::show_color_bar()
+{
+	unsigned long i,j;
+	cbi(P_CS, B_CS);
+	clrXY();
+
+
+
+
+		for (i=0; i<(disp_y_size+1); i++)
+			{
+				for (j=0; j<(disp_x_size+1); j++)
+				{
+	 				  if(i>350){LCD_Write_DATA(0xFF);LCD_Write_DATA(0xFF);}
+ 	 				  else if(i>300){LCD_Write_DATA(0x00);LCD_Write_DATA(0x1F);}
+ 	 				  else if(i>250){LCD_Write_DATA(0x07);LCD_Write_DATA(0xE0);}
+ 	 				  else if(i>200){LCD_Write_DATA(0x7F);LCD_Write_DATA(0xFF);}
+ 	 				  else if(i>150){LCD_Write_DATA(0xF8);LCD_Write_DATA(0x00);}
+ 	 				  else if(i>100){LCD_Write_DATA(0xF8);LCD_Write_DATA(0x1F);}
+ 	 				  else if(i>50){LCD_Write_DATA(0xFF);LCD_Write_DATA(0xE0);}
+					else {LCD_Write_DATA(213);LCD_Write_DATA(156);}
+				}
+			}
+sbi(P_CS, B_CS);
+}
+void UTFT::clrScr()
+{
+	long i;
+
+	cbi(P_CS, B_CS);
+	clrXY();
+
+	if (display_transfer_mode!=1)
+		sbi(P_RS, B_RS);
+	for (i=0; i<((disp_x_size+1)*(disp_y_size+1)); i++)
+	{
+		//if (display_transfer_mode!=1)
+			//LCD_Writ_Bus(0,0,display_transfer_mode);
+		//else
+		//{
+			LCD_Writ_Bus(0,0,display_transfer_mode);
+			LCD_Writ_Bus(1,0,display_transfer_mode);
+		//}
+	}
+	sbi(P_CS, B_CS);
+}
+
+void UTFT::fillScr(byte r, byte g, byte b)
+{
+	long i;
+	char ch, cl;
+
+	ch=((r&248)|g>>5);
+	cl=((g&28)<<3|b>>3);
+
+	cbi(P_CS, B_CS);
+	clrXY();
+	if (display_transfer_mode!=1)
+		sbi(P_RS, B_RS);
+	for (i=0; i<((disp_x_size+1)*(disp_y_size+1)); i++)
+	{
+		if (display_transfer_mode!=1)
+			LCD_Writ_Bus(ch,cl,display_transfer_mode);
+		else
+		{
+			LCD_Writ_Bus(1,ch,display_transfer_mode);
+			LCD_Writ_Bus(1,cl,display_transfer_mode);
+		}
+	}
+	sbi(P_CS, B_CS);
+}
+
+void UTFT::setColor(byte r, byte g, byte b)
+{
+	fcolorr=r;
+	fcolorg=g;
+	fcolorb=b;
+}
+
+void UTFT::setBackColor(byte r, byte g, byte b)
+{
+	bcolorr=r;
+	bcolorg=g;
+	bcolorb=b;
+}
+
+void UTFT::setPixel(byte r,byte g,byte b)
+{
+	LCD_Write_DATA(((g&28)<<3|b>>3));	// rrrrrggggggbbbbb
+    LCD_Write_DATA((r&248)|g>>5);
+}
+void UTFT::drawPixel(int x, int y)
+{
+	cbi(P_CS, B_CS);
+	setXY(x, y, x, y);
+	setPixel(fcolorr, fcolorg, fcolorb);
+	sbi(P_CS, B_CS);
+	clrXY();
+}
+
+void UTFT::drawLine(int x1, int y1, int x2, int y2)
+{
+	int tmp;
+	double delta, tx, ty;
+	double m, b, dx, dy;
+	char ch, cl;
+
+	ch=((fcolorr&248)|fcolorg>>5);
+	cl=((fcolorg&28)<<3|fcolorb>>3);
+
+	if (((x2-x1)<0))
+	{
+		swap(int, x1, x2);
+		swap(int, y1, y2);
+	}
+    if (((y2-y1)<0))
+	{
+		swap(int, x1, x2);
+		swap(int, y1, y2);
+	}
+
+	if (y1==y2)
+	{
+		if (x1>x2)
+		{
+			swap(int, x1, x2);
+		}
+		drawHLine(x1, y1, x2-x1);
+	}
+	else if (x1==x2)
+	{
+		if (y1>y2)
+		{
+			swap(int, y1, y2);
+		}
+		drawVLine(x1, y1, y2-y1);
+	}
+	else if (abs(x2-x1)>abs(y2-y1))
+	{
+		cbi(P_CS, B_CS);
+		delta=(double(y2-y1)/double(x2-x1));
+		ty=double(y1);
+		if (x1>x2)
+		{
+			for (int i=x1; i>=x2; i--)
+			{
+				setXY(i, int(ty+0.5), i, int(ty+0.5));
+				LCD_Write_DATA(cl);
+        		ty=ty-delta;
+			}
+		}
+		else
+		{
+			for (int i=x1; i<=x2; i++)
+			{
+				setXY(i, int(ty+0.5), i, int(ty+0.5));
+				LCD_Write_DATA(cl);
+        		ty=ty+delta;
+			}
+		}
+		sbi(P_CS, B_CS);
+	}
+	else
+	{
+		cbi(P_CS, B_CS);
+		delta=(float(x2-x1)/float(y2-y1));
+		tx=float(x1);
+        if (y1>y2)
+        {
+			for (int i=y2+1; i>y1; i--)
+			{
+		 		setXY(int(tx+0.5), i, int(tx+0.5), i);
+				LCD_Write_DATA( cl);
+        		tx=tx+delta;
+			}
+        }
+        else
+        {
+			for (int i=y1; i<y2+1; i++)
+			{
+		 		setXY(int(tx+0.5), i, int(tx+0.5), i);
+				LCD_Write_DATA( cl);
+        		tx=tx+delta;
+			}
+        }
+		sbi(P_CS, B_CS);
+	}
+
+	clrXY();
+}
+
+void UTFT::drawHLine(int x, int y, int l)
+{
+	char ch, cl;
+
+	ch=((fcolorr&248)|fcolorg>>5);
+	cl=((fcolorg&28)<<3|fcolorb>>3);
+
+	cbi(P_CS, B_CS);
+	setXY(x, y, x+l, y);
+	for (int i=0; i<l+1; i++)
+	{
+		LCD_Write_DATA( cl);
+	}
+	sbi(P_CS, B_CS);
+	clrXY();
+}
+
+void UTFT::drawVLine(int x, int y, int l)
+{
+	char ch, cl;
+
+	ch=((fcolorr&248)|fcolorg>>5);
+	cl=((fcolorg&28)<<3|fcolorb>>3);
+
+	cbi(P_CS, B_CS);
+	setXY(x, y, x, y+l);
+	for (int i=0; i<l; i++)
+	{
+		LCD_Write_DATA( cl);
+	}
+	sbi(P_CS, B_CS);
+	clrXY();
+}
+
+void UTFT::printChar(byte c, int x, int y)
+{
+	byte i,ch;
+	word j;
+	word temp;
+
+	cbi(P_CS, B_CS);
+
+	if (orient==PORTRAIT)
+	{
+		setXY(x,y,x+cfont.x_size-1,y+cfont.y_size-1);
+
+		temp=((c-cfont.offset)*((cfont.x_size/8)*cfont.y_size))+4;
+		for(j=0;j<((cfont.x_size/8)*cfont.y_size);j++)
+		{
+			ch=pgm_read_byte(&cfont.font[temp]);
+			for(i=0;i<8;i++)
+			{
+				if((ch&(1<<(7-i)))!=0)
+				{
+					setPixel(fcolorr, fcolorg, fcolorb);
+				}
+				else
+				{
+					setPixel(bcolorr, bcolorg, bcolorb);
+				}
+			}
+			temp++;
+		}
+	}
+	else
+	{
+		temp=((c-cfont.offset)*((cfont.x_size/8)*cfont.y_size))+4;
+
+		for(j=0;j<((cfont.x_size/8)*cfont.y_size);j+=(cfont.x_size/8))
+		{
+			setXY(x,y+(j/(cfont.x_size/8)),x+cfont.x_size-1,y+(j/(cfont.x_size/8)));
+			for (int zz=(cfont.x_size/8)-1; zz>=0; zz--)
+			{
+				ch=pgm_read_byte(&cfont.font[temp+zz]);
+				for(i=0;i<8;i++)
+				{
+					if((ch&(1<<i))!=0)
+					{
+						setPixel(fcolorr, fcolorg, fcolorb);
+					}
+					else
+					{
+						setPixel(bcolorr, bcolorg, bcolorb);
+					}
+				}
+			}
+			temp+=(cfont.x_size/8);
+		}
+	}
+	sbi(P_CS, B_CS);
+	clrXY();
+}
+
+void UTFT::rotateChar(byte c, int x, int y, int pos, int deg)
+{
+	byte i,j,ch;
+	word temp;
+	int newx,newy;
+	double radian;
+	radian=deg*0.0175;
+
+	cbi(P_CS, B_CS);
+
+	temp=((c-cfont.offset)*((cfont.x_size/8)*cfont.y_size))+4;
+	for(j=0;j<cfont.y_size;j++)
+	{
+		for (int zz=0; zz<(cfont.x_size/8); zz++)
+		{
+			ch=pgm_read_byte(&cfont.font[temp+zz]);
+			for(i=0;i<8;i++)
+			{
+				newx=x+(((i+(zz*8)+(pos*cfont.x_size))*cos(radian))-((j)*sin(radian)));
+				newy=y+(((j)*cos(radian))+((i+(zz*8)+(pos*cfont.x_size))*sin(radian)));
+
+				setXY(newx,newy,newx+1,newy+1);
+
+				if((ch&(1<<(7-i)))!=0)
+				{
+					setPixel(fcolorr, fcolorg, fcolorb);
+				}
+				else
+				{
+					setPixel(bcolorr, bcolorg, bcolorb);
+				}
+			}
+		}
+		temp+=(cfont.x_size/8);
+	}
+	sbi(P_CS, B_CS);
+	clrXY();
+}
+
+void UTFT::print(char *st, int x, int y, int deg)
+{
+	int stl, i;
+
+	stl = strlen(st);
+
+	if (orient==PORTRAIT)
+	{
+	if (x==RIGHT)
+		x=(disp_x_size+1)-(stl*cfont.x_size);
+	if (x==CENTER)
+		x=((disp_x_size+1)-(stl*cfont.x_size))/2;
+	}
+	else
+	{
+	if (x==RIGHT)
+		x=(disp_y_size+1)-(stl*cfont.x_size);
+	if (x==CENTER)
+		x=((disp_y_size+1)-(stl*cfont.x_size))/2;
+	}
+
+	for (i=0; i<stl; i++)
+		if (deg==0)
+			printChar(*st++, x + (i*(cfont.x_size)), y);
+		else
+			rotateChar(*st++, x, y, i, deg);
+}
+
+void UTFT::print(String st, int x, int y, int deg)
+{
+	char buf[st.length()+1];
+
+	st.toCharArray(buf, st.length()+1);
+	print(buf, x, y, deg);
+}
+
+void UTFT::printNumI(long num, int x, int y, int length, char filler)
+{
+	char buf[25];
+	char st[27];
+	boolean neg=false;
+	int c=0, f=0;
+
+	if (num==0)
+	{
+		if (length!=0)
+		{
+			for (c=0; c<(length-1); c++)
+				st[c]=filler;
+			st[c]=48;
+			st[c+1]=0;
+		}
+		else
+		{
+			st[0]=48;
+			st[1]=0;
+		}
+	}
+	else
+	{
+		if (num<0)
+		{
+			neg=true;
+			num=-num;
+		}
+
+		while (num>0)
+		{
+			buf[c]=48+(num % 10);
+			c++;
+			num=(num-(num % 10))/10;
+		}
+		buf[c]=0;
+
+		if (neg)
+		{
+			st[0]=45;
+		}
+
+		if (length>(c+neg))
+		{
+			for (int i=0; i<(length-c-neg); i++)
+			{
+				st[i+neg]=filler;
+				f++;
+			}
+		}
+
+		for (int i=0; i<c; i++)
+		{
+			st[i+neg+f]=buf[c-i-1];
+		}
+		st[c+neg+f]=0;
+
+	}
+
+	print(st,x,y);
+}
+
+void UTFT::printNumF(double num, byte dec, int x, int y, char divider, int length, char filler)
+{
+	char buf[25];
+	char st[27];
+	boolean neg=false;
+	int c=0, f=0;
+	int c2, mult;
+	unsigned long inum;
+
+	if (dec<1)
+	dec=1;
+	if (dec>5)
+	dec=5;
+
+	if (num==0)
+	{
+		if (length!=0)
+		{
+			for (c=0; c<(length-2-dec); c++)
+				st[c]=filler;
+			st[c]=48;
+			st[c+1]=divider;
+			for (int i=0; i<dec; i++)
+				st[c+2+i]=48;
+			st[c+2+dec]=0;
+		}
+		else
+		{
+			st[0]=48;
+			st[1]=divider;
+			for (int i=0; i<dec; i++)
+				st[2+i]=48;
+			st[2+dec]=0;
+		}
+	}
+	else
+	{
+		if (num<0)
+		{
+			neg=true;
+			num=-num;
+		}
+
+		mult = 1;
+		for (int j = 0; j < dec; j++)
+			mult = mult * 10;
+		inum=long(num*mult + 0.5);
+
+		while (inum>0)
+		{
+			buf[c]=48+(inum % 10);
+			c++;
+			inum=(inum-(inum % 10))/10;
+		}
+		if ((num<1) and (num>0))
+		{
+			buf[c]=48;
+			c++;
+		}
+		while (c<(dec+1))
+		{
+			buf[c]=48;
+			c++;
+		}
+		buf[c]=0;
+
+		if (neg)
+		{
+			st[0]=45;
+		}
+
+		if (length>(c+neg-1))
+		{
+			for (int i=0; i<(length-c-neg-1); i++)
+			{
+				st[i+neg]=filler;
+				f++;
+			}
+		}
+
+		c2=neg;
+		for (int i=0; i<c; i++)
+		{
+			st[c2+f]=buf[c-i-1];
+			c2++;
+			if ((c-(c2-neg))==dec)
+			{
+				st[c2+f]=divider;
+				c2++;
+			}
+		}
+		st[c2+f]=0;
+	}
+
+	print(st,x,y);
+}
+
+void UTFT::setFont(uint8_t* font)
+{
+	cfont.font=font;
+	cfont.x_size=fontbyte(0);
+	cfont.y_size=fontbyte(1);
+	cfont.offset=fontbyte(2);
+	cfont.numchars=fontbyte(3);
+}
+
+void UTFT::drawBitmap(int x, int y, int sx, int sy, bitmapdatatype data, int scale)
+{
+	unsigned int col;
+	int tx, ty, tc, tsx, tsy;
+	byte r, g, b;
+
+	if (scale==1)
+	{
+		if (orient==PORTRAIT)
+		{
+			cbi(P_CS, B_CS);
+			setXY(x, y, x+sx-1, y+sy-1);
+			for (tc=0; tc<(sx*sy); tc++)
+			{
+				col=pgm_read_word(&data[tc]);
+				LCD_Write_DATA(col>>8,col & 0xff);
+			}
+			sbi(P_CS, B_CS);
+		}
+		else
+		{
+			cbi(P_CS, B_CS);
+			for (ty=0; ty<sy; ty++)
+			{
+				setXY(x, y+ty, x+sx-1, y+ty);
+				for (tx=sx; tx>=0; tx--)
+				{
+					col=pgm_read_word(&data[(ty*sx)+tx]);
+					LCD_Write_DATA(col>>8,col & 0xff);
+				}
+			}
+			sbi(P_CS, B_CS);
+		}
+	}
+	else
+	{
+		if (orient==PORTRAIT)
+		{
+			cbi(P_CS, B_CS);
+			for (ty=0; ty<sy; ty++)
+			{
+				setXY(x, y+(ty*scale), x+((sx*scale)-1), y+(ty*scale)+scale);
+				for (tsy=0; tsy<scale; tsy++)
+					for (tx=0; tx<sx; tx++)
+					{
+						col=pgm_read_word(&data[(ty*sx)+tx]);
+						for (tsx=0; tsx<scale; tsx++)
+							LCD_Write_DATA(col>>8,col & 0xff);
+					}
+			}
+			sbi(P_CS, B_CS);
+		}
+		else
+		{
+			cbi(P_CS, B_CS);
+			for (ty=0; ty<sy; ty++)
+			{
+				for (tsy=0; tsy<scale; tsy++)
+				{
+					setXY(x, y+(ty*scale)+tsy, x+((sx*scale)-1), y+(ty*scale)+tsy);
+					for (tx=sx; tx>=0; tx--)
+					{
+						col=pgm_read_word(&data[(ty*sx)+tx]);
+						for (tsx=0; tsx<scale; tsx++)
+							LCD_Write_DATA(col>>8,col & 0xff);
+					}
+				}
+			}
+			sbi(P_CS, B_CS);
+		}
+	}
+	clrXY();
+}
+
+void UTFT::drawBitmap(int x, int y, int sx, int sy, bitmapdatatype data, int deg, int rox, int roy)
+{
+	unsigned int col;
+	int tx, ty, newx, newy;
+	byte r, g, b;
+	double radian;
+	radian=deg*0.0175;
+
+	if (deg==0)
+		drawBitmap(x, y, sx, sy, data);
+	else
+	{
+		cbi(P_CS, B_CS);
+		for (ty=0; ty<sy; ty++)
+			for (tx=0; tx<sx; tx++)
+			{
+				col=pgm_read_word(&data[(ty*sx)+tx]);
+
+				newx=x+rox+(((tx-rox)*cos(radian))-((ty-roy)*sin(radian)));
+				newy=y+roy+(((ty-roy)*cos(radian))+((tx-rox)*sin(radian)));
+
+				setXY(newx, newy, newx, newy);
+				LCD_Write_DATA(col>>8,col & 0xff);
+			}
+		sbi(P_CS, B_CS);
+	}
+	clrXY();
+}
+
+void UTFT::lcdOff()
+{
+	cbi(P_CS, B_CS);
+	switch (display_model)
+	{
+	case PCF8833:
+		LCD_Write_COM(0x28);
+		break;
+	}
+	sbi(P_CS, B_CS);
+}
+
+void UTFT::lcdOn()
+{
+	cbi(P_CS, B_CS);
+	switch (display_model)
+	{
+	case PCF8833:
+		LCD_Write_COM(0x29);
+		break;
+	}
+	sbi(P_CS, B_CS);
+}
+
+void UTFT::setContrast(char c)
+{
+	cbi(P_CS, B_CS);
+	switch (display_model)
+	{
+	case PCF8833:
+		if (c>64) c=64;
+		LCD_Write_COM(0x25);
+		LCD_Write_DATA(c);
+		break;
+	}
+	sbi(P_CS, B_CS);
+}
+
+int UTFT::getDisplayXSize()
+{
+	if (orient==PORTRAIT)
+		return disp_x_size+1;
+	else
+		return disp_y_size+1;
+}
+
+int UTFT::getDisplayYSize()
+{
+	if (orient==PORTRAIT)
+		return disp_y_size+1;
+	else
+		return disp_x_size+1;
+}
diff --git a/lib/UTFT/UTFT.h b/lib/UTFT/UTFT.h
new file mode 100644
index 0000000..7b3de7a
--- /dev/null
+++ b/lib/UTFT/UTFT.h
@@ -0,0 +1,134 @@
+
+
+#ifndef UTFT_h
+#define UTFT_h
+
+#define LEFT 0
+#define RIGHT 9999
+#define CENTER 9998
+
+#define PORTRAIT 0
+#define LANDSCAPE 1
+
+#define HX8347A			0
+#define ILI9327			1
+#define SSD1289			2
+#define ILI9325C		3
+#define ILI9325D_8		4
+#define ILI9325D_16		5
+#define HX8340B_8		6
+#define HX8340B_S		7
+#define HX8352A			8
+#define ST7735			9
+#define PCF8833			10
+#define S1D19122		11
+#define SSD1963_480		12
+#define SSD1963_800		13
+#define S6D1121_8		14
+#define S6D1121_16		15
+
+#define ITDB32			0	// HX8347-A (16bit)
+#define ITDB32WC		1	// ILI9327  (16bit)
+#define TFT01_32W		1	// ILI9327	(16bit)
+#define ITDB32S			2	// SSD1289  (16bit)
+#define TFT01_32		2	// SSD1289  (16bit)
+#define ITDB24			3	// ILI9325C (8bit)
+#define ITDB24D			4	// ILI9325D (8bit)
+#define ITDB24DWOT		4	// ILI9325D (8bit)
+#define ITDB28			4	// ILI9325D (8bit)
+#define TFT01_24_8		4	// ILI9325D (8bit)
+#define TFT01_24_16		5	// ILI9325D (16bit)
+#define ITDB22			6	// HX8340-B (8bit)
+#define ITDB22SP		7	// HX8340-B (Serial)
+#define ITDB32WD		8	// HX8352-A (16bit)
+#define TFT01_32WD		8	// HX8352A	(16bit)
+#define ITDB18SP		9	// ST7735   (Serial)
+#define LPH9135			10	// PCF8833	(Serial)
+#define ITDB25H			11	// S1D19122	(16bit)
+#define ITDB43			12	// SSD1963	(16bit) 480x272
+#define ITDB50			13	// SSD1963	(16bit) 800x480
+#define ITDB24E_8		14	// S6D1121	(8bit)
+#define ITDB24E_16		15	// S6D1121	(16bit)
+
+#define SERIAL_4PIN		4
+#define SERIAL_5PIN		5
+
+#if defined(__AVR__)
+	#if defined(ARDUINO) && ARDUINO >= 100
+		#include "Arduino.h"
+	#else
+		#include "WProgram.h"
+	#endif
+	#include "HW_AVR_defines.h"
+#else
+	#include "WProgram.h"
+	#include "HW_PIC32_defines.h"
+#endif
+
+struct _current_font
+{
+	uint8_t* font;
+	uint8_t x_size;
+	uint8_t y_size;
+	uint8_t offset;
+	uint8_t numchars;
+};
+
+class UTFT
+{
+	public:
+		UTFT();
+		UTFT(byte model, int RS, int WR,int CS, int RST, int SER=0);
+		void show_color_bar();
+		void InitLCD(byte orientation=LANDSCAPE);
+		void clrScr();
+		void drawPixel(int x, int y);
+		void drawLine(int x1, int y1, int x2, int y2);
+		void fillScr(byte r, byte g, byte b);
+		void drawRect(int x1, int y1, int x2, int y2);
+		void drawRoundRect(int x1, int y1, int x2, int y2);
+		void fillRect(int x1, int y1, int x2, int y2);
+		void fillRoundRect(int x1, int y1, int x2, int y2);
+		void drawCircle(int x, int y, int radius);
+		void fillCircle(int x, int y, int radius);
+		void setColor(byte r, byte g, byte b);
+		void setBackColor(byte r, byte g, byte b);
+		void print(char *st, int x, int y, int deg=0);
+		void print(String st, int x, int y, int deg=0);
+		void printNumI(long num, int x, int y, int length=0, char filler=' ');
+		void printNumF(double num, byte dec, int x, int y, char divider='.', int length=0, char filler=' ');
+		void setFont(uint8_t* font);
+		void drawBitmap(int x, int y, int sx, int sy, bitmapdatatype data, int scale=1);
+		void drawBitmap(int x, int y, int sx, int sy, bitmapdatatype data, int deg, int rox, int roy);
+		void lcdOff();
+		void lcdOn();
+		void setContrast(char c);
+		int  getDisplayXSize();
+		int	 getDisplayYSize();
+
+	protected:
+		byte fcolorr,fcolorg,fcolorb;
+		byte bcolorr,bcolorg,bcolorb;
+		byte orient;
+		long disp_x_size, disp_y_size;
+		byte display_model, display_transfer_mode, display_serial_mode;
+		regtype *P_RS, *P_WR, *P_CS, *P_RST, *P_SDA, *P_SCL;
+		regsize B_RS, B_WR, B_CS, B_RST, B_SDA, B_SCL;
+		_current_font	cfont;
+
+		void LCD_Writ_Bus(char VH,char VL, byte mode);
+		void LCD_Write_COM(char VL);
+		void LCD_Write_DATA(char VH,char VL);
+		void LCD_Write_DATA(char VL);
+		void LCD_Write_COM_DATA(char com1,int dat1);
+		void setPixel(byte r,byte g,byte b);
+		void drawHLine(int x, int y, int l);
+		void drawVLine(int x, int y, int l);
+		void printChar(byte c, int x, int y);
+		void setXY(word x1, word y1, word x2, word y2);
+		void clrXY();
+		void rotateChar(byte c, int x, int y, int pos, int deg);
+		void _set_direction_registers(byte mode);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/UTFT/UTFT.pdf b/lib/UTFT/UTFT.pdf
new file mode 100644
index 0000000..3bb2781
Binary files /dev/null and b/lib/UTFT/UTFT.pdf differ
diff --git a/lib/UTFT/UTFT_Requirements.pdf b/lib/UTFT/UTFT_Requirements.pdf
new file mode 100644
index 0000000..3b247c0
Binary files /dev/null and b/lib/UTFT/UTFT_Requirements.pdf differ
diff --git a/lib/UTFT/UTFT_Supported_display_modules_&_controllers.pdf b/lib/UTFT/UTFT_Supported_display_modules_&_controllers.pdf
new file mode 100644
index 0000000..28ae8c2
Binary files /dev/null and b/lib/UTFT/UTFT_Supported_display_modules_&_controllers.pdf differ
diff --git a/lib/UTFT/examples/UTFT_Demo_400x240/UTFT_Demo_400x240.pde b/lib/UTFT/examples/UTFT_Demo_400x240/UTFT_Demo_400x240.pde
new file mode 100644
index 0000000..cdf3392
--- /dev/null
+++ b/lib/UTFT/examples/UTFT_Demo_400x240/UTFT_Demo_400x240.pde
@@ -0,0 +1,336 @@
+// UTFT_Demo_400x240 (C)2012 Henning Karlsen
+// web: http://www.henningkarlsen.com/electronics
+//
+// This program is a demo of how to use most of the functions
+// of the library with a supported display modules.
+//
+// This demo was made for modules with a screen resolution
+// of 400x240 pixels.
+//
+// This program requires the UTFT library.
+//
+
+#include <UTFT.h>
+
+// Declare which fonts we will be using
+extern uint8_t SmallFont[];
+
+// Uncomment the next line for Arduino 2009/Uno
+//UTFT myGLCD(ITDB32WC,19,18,17,16);   // Remember to change the model parameter to suit your display module!
+
+// Uncomment the next line for Arduino Mega
+UTFT myGLCD(ITDB32WC,38,39,40,41);   // Remember to change the model parameter to suit your display module!
+
+void setup()
+{
+  randomSeed(analogRead(0));
+
+// Setup the LCD
+  myGLCD.InitLCD();
+  myGLCD.setFont(SmallFont);
+}
+
+void loop()
+{
+  int buf[398];
+  int x, x2;
+  int y, y2;
+  int r;
+  myGLCD.show_color_bar();
+  delay(2000);
+
+// Clear the screen and draw the frame
+  myGLCD.clrScr();
+
+  myGLCD.setColor(255, 0, 0);
+  myGLCD.fillRect(0, 0, 399, 13);
+  myGLCD.setColor(64, 64, 64);
+  myGLCD.fillRect(0, 226, 399, 239);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.print("*** Universal Color TFT Display Library ***", CENTER, 1);
+  myGLCD.setBackColor(64, 64, 64);
+  myGLCD.setColor(255,255,0);
+  myGLCD.print("< http://electronics.henningkarlsen.com >", CENTER, 227);
+
+  myGLCD.setColor(0, 0, 255);
+  myGLCD.drawRect(0, 14, 399, 225);
+
+// Draw crosshairs
+  myGLCD.setColor(0, 0, 255);
+  myGLCD.setBackColor(0, 0, 0);
+  myGLCD.drawLine(199, 15, 199, 224);
+  myGLCD.drawLine(1, 119, 398, 119);
+  for (int i=9; i<390; i+=10)
+    myGLCD.drawLine(i, 117, i, 121);
+  for (int i=19; i<220; i+=10)
+    myGLCD.drawLine(197, i, 201, i);
+
+// Draw sin-, cos- and tan-lines
+  myGLCD.setColor(0,255,255);
+  myGLCD.print("Sin", 5, 15);
+  for (int i=1; i<398; i++)
+  {
+    myGLCD.drawPixel(i,119+(sin(((i*0.9)*3.14)/180)*95));
+  }
+
+  myGLCD.setColor(255,0,0);
+  myGLCD.print("Cos", 5, 27);
+  for (int i=1; i<398; i++)
+  {
+    myGLCD.drawPixel(i,119+(cos(((i*0.9)*3.14)/180)*95));
+  }
+
+  myGLCD.setColor(255,255,0);
+  myGLCD.print("Tan", 5, 39);
+  for (int i=1; i<398; i++)
+  {
+    y=119+(tan(((i*0.9)*3.14)/180));
+    if ((y>15) && (y<224))
+    myGLCD.drawPixel(i,y);
+  }
+
+  delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+  myGLCD.setColor(0, 0, 255);
+  myGLCD.setBackColor(0, 0, 0);
+  myGLCD.drawLine(199, 15, 199, 224);
+  myGLCD.drawLine(1, 119, 398, 119);
+
+// Draw a moving sinewave
+myGLCD.clrScr();
+  x=1;
+  for (int i=1; i<(398*20); i++)
+  {
+    x++;
+    if (x==399)
+      x=1;
+    if (i>399)
+    {
+      if ((x==199)||(buf[x-1]==119))
+        myGLCD.setColor(0,0,255);
+      else
+        myGLCD.setColor(0,0,0);
+      myGLCD.drawPixel(x,buf[x-1]);
+    }
+    myGLCD.setColor(0,255,255);
+    y=119+(sin(((i)*3.14)/180)*(90-(i / 100)));
+    myGLCD.drawPixel(x,y);
+    buf[x-1]=y;
+  }
+
+//  delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+// Draw some filled rectangles
+myGLCD.clrScr();
+  for (int i=1; i<6; i++)
+  {
+    switch (i)
+    {
+      case 1:
+        myGLCD.setColor(255,0,255);
+        break;
+      case 2:
+        myGLCD.setColor(255,0,0);
+        break;
+      case 3:
+        myGLCD.setColor(0,255,0);
+        break;
+      case 4:
+        myGLCD.setColor(0,0,255);
+        break;
+      case 5:
+        myGLCD.setColor(255,255,0);
+        break;
+    }
+    myGLCD.fillRect(110+(i*20), 30+(i*20), 170+(i*20), 90+(i*20));
+  }
+
+  //delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+// Draw some filled, rounded rectangles
+myGLCD.clrScr();
+  for (int i=1; i<6; i++)
+  {
+    switch (i)
+    {
+      case 1:
+        myGLCD.setColor(255,0,255);
+        break;
+      case 2:
+        myGLCD.setColor(255,0,0);
+        break;
+      case 3:
+        myGLCD.setColor(0,255,0);
+        break;
+      case 4:
+        myGLCD.setColor(0,0,255);
+        break;
+      case 5:
+        myGLCD.setColor(255,255,0);
+        break;
+    }
+    myGLCD.fillRoundRect(230-(i*20), 30+(i*20), 290-(i*20), 90+(i*20));
+  }
+
+//  delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+// Draw some filled circles
+myGLCD.clrScr();
+  for (int i=1; i<6; i++)
+  {
+    switch (i)
+    {
+      case 1:
+        myGLCD.setColor(255,0,255);
+        break;
+      case 2:
+        myGLCD.setColor(255,0,0);
+        break;
+      case 3:
+        myGLCD.setColor(0,255,0);
+        break;
+      case 4:
+        myGLCD.setColor(0,0,255);
+        break;
+      case 5:
+        myGLCD.setColor(255,255,0);
+        break;
+    }
+    myGLCD.fillCircle(110+(i*30),60+(i*20), 30);
+  }
+
+  //delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+// Draw some lines in a pattern
+myGLCD.clrScr();
+  myGLCD.setColor (255,0,0);
+  for (int i=15; i<224; i+=5)
+  {
+    myGLCD.drawLine(1, i, (i*1.77)-10, 224);
+  }
+  myGLCD.setColor (255,0,0);
+  for (int i=224; i>15; i-=5)
+  {
+    myGLCD.drawLine(398, i, (i*1.77)-11, 15);
+  }
+  myGLCD.setColor (0,255,255);
+  for (int i=224; i>15; i-=5)
+  {
+    myGLCD.drawLine(1, i, 411-(i*1.77), 15);
+  }
+  myGLCD.setColor (0,255,255);
+  for (int i=15; i<224; i+=5)
+  {
+    myGLCD.drawLine(398, i, 410-(i*1.77), 224);
+  }
+
+//  delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,225);
+
+// Draw some random circles
+myGLCD.clrScr();
+  for (int i=0; i<100; i++)
+  {
+    myGLCD.setColor(random(255), random(255), random(255));
+    x=32+random(336);
+    y=45+random(146);
+    r=random(30);
+    myGLCD.drawCircle(x, y, r);
+  }
+
+ // delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+// Draw some random rectangles
+myGLCD.clrScr();
+  for (int i=0; i<100; i++)
+  {
+    myGLCD.setColor(random(255), random(255), random(255));
+    x=2+random(396);
+    y=16+random(207);
+    x2=2+random(396);
+    y2=16+random(207);
+    myGLCD.drawRect(x, y, x2, y2);
+  }
+
+//  delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+// Draw some random rounded rectangles
+myGLCD.clrScr();
+  for (int i=0; i<100; i++)
+  {
+    myGLCD.setColor(random(255), random(255), random(255));
+    x=2+random(396);
+    y=16+random(207);
+    x2=2+random(396);
+    y2=16+random(207);
+    myGLCD.drawRoundRect(x, y, x2, y2);
+  }
+  myGLCD.clrScr();
+  //delay(2000);
+
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+  for (int i=0; i<100; i++)
+  {
+    myGLCD.setColor(random(255), random(255), random(255));
+    x=2+random(396);
+    y=16+random(209);
+    x2=2+random(396);
+    y2=16+random(209);
+    myGLCD.drawLine(x, y, x2, y2);
+  }
+
+//  delay(2000);
+ myGLCD.clrScr();
+  myGLCD.setColor(0,0,0);
+  myGLCD.fillRect(1,15,398,224);
+
+  for (int i=0; i<10000; i++)
+  {
+    myGLCD.setColor(random(255), random(255), random(255));
+    myGLCD.drawPixel(2+random(396), 16+random(209));
+  }
+
+//  delay(2000);
+myGLCD.clrScr();
+ // myGLCD.fillScr(0, 0, 255);
+//  myGLCD.setColor(255, 0, 0);
+//  myGLCD.fillRoundRect(120, 70, 279, 169);
+
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.print("That's it!", CENTER, 93);
+  myGLCD.print("Restarting in a", CENTER, 119);
+  myGLCD.print("few seconds...", CENTER, 132);
+
+  myGLCD.setColor(0, 255, 0);
+  myGLCD.setBackColor(0, 0, 255);
+  myGLCD.print("Runtime: (msecs)", CENTER, 210);
+  myGLCD.printNumI(millis(), CENTER, 225);
+
+  delay (10000);
+}
diff --git a/lib/UTFT/keywords.txt b/lib/UTFT/keywords.txt
new file mode 100644
index 0000000..ac4ffb3
--- /dev/null
+++ b/lib/UTFT/keywords.txt
@@ -0,0 +1,72 @@
+UTFT	KEYWORD1
+
+InitLCD	KEYWORD2
+clrScr	KEYWORD2
+fillScr	KEYWORD2
+setColor	KEYWORD2
+setBackColor	KEYWORD2
+drawPixel	KEYWORD2
+drawLine	KEYWORD2
+drawRect	KEYWORD2
+drawRoundRect	KEYWORD2
+fillRect	KEYWORD2
+fillRoundRect	KEYWORD2
+drawCircle	KEYWORD2
+fillCircle	KEYWORD2
+print	KEYWORD2
+printNumI	KEYWORD2
+printNumF	KEYWORD2
+setFont	KEYWORD2
+drawBitmap	KEYWORD2
+lcdOff	KEYWORD2
+lcdOn	KEYWORD2
+setContrast	KEYWORD2
+getDisplayXSize	KEYWORD2
+getDisplayYSize	KEYWORD2
+
+LEFT	LITERAL1
+RIGHT	LITERAL1
+CENTER	LITERAL1
+PORTRAIT	LITERAL1
+LANDSCAPE	LITERAL1
+
+HX8347A	LITERAL1
+ILI9327	LITERAL1
+SSD1289	LITERAL1
+ILI9325C	LITERAL1
+ILI9325D_8	LITERAL1
+ILI9325D_16	LITERAL1
+HX8340B_8	LITERAL1
+HX8340B_S	LITERAL1
+HX8352A	LITERAL1
+ST7735	LITERAL1
+PCF8833	LITERAL1
+S1D19122	LITERAL1
+HX8352A	LITERAL1
+SSD1963_480	LITERAL1
+SSD1963_800	LITERAL1
+S6D1121_8	LITERAL1
+S6D1121_16	LITERAL1
+
+ITDB32	LITERAL1
+ITDB32WC	LITERAL1
+TFT01_32W	LITERAL1
+ITDB32S	LITERAL1
+TFT01_32	LITERAL1
+ITDB24	LITERAL1
+ITDB24D	LITERAL1
+ITDB24DWOT	LITERAL1
+ITDB28	LITERAL1
+TFT01_24_8	LITERAL1
+TFT01_24_16	LITERAL1
+ITDB22	LITERAL1
+ITDB22SP	LITERAL1
+ITDB32WD	LITERAL1
+ITDB18SP	LITERAL1
+LPH9135	LITERAL1
+ITDB25H	LITERAL1
+TFT01_32WD	LITERAL1
+ITDB43	LITERAL1
+ITDB50	LITERAL1
+ITDB24E_8	LITERAL1
+ITDB24E_16	LITERAL1
diff --git a/lib/UTFT/memorysaver.h b/lib/UTFT/memorysaver.h
new file mode 100644
index 0000000..5f92b8c
--- /dev/null
+++ b/lib/UTFT/memorysaver.h
@@ -0,0 +1,28 @@
+// UTFT Memory Saver
+// -----------------
+//
+// Since most people have only one or possibly two different display modules a lot
+// of memory has been wasted to keep support for many unneeded controller chips.
+// You now have the option to remove this unneeded code from the library with
+// this file.
+// By disabling the controllers you don't need you can reduce the memory footprint
+// of the library by several Kb.
+//
+// Uncomment the lines for the displaycontrollers that you don't use to save
+// some flash memory by not including the init code for that particular
+// controller.
+
+//#define DISABLE_HX8347A			1
+//#define DISABLE_ILI9327			1
+//#define DISABLE_SSD1289			1
+//#define DISABLE_ILI9325C		1
+//#define DISABLE_ILI9325D		1		// This single define will disable both 8bit and 16bit mode for this controller
+//#define DISABLE_HX8340B_8		1
+//#define DISABLE_HX8340B_S		1
+//#define DISABLE_HX8352A			1
+//#define DISABLE_ST7735			1
+//#define DISABLE_PCF8833			1
+//#define DISABLE_S1D19122		1
+//#define DISABLE_SSD1963_480		1
+//#define DISABLE_SSD1963_800		1
+//#define DISABLE_S6D1121		1
diff --git a/lib/UTFT/version.txt b/lib/UTFT/version.txt
new file mode 100644
index 0000000..f96f5a0
--- /dev/null
+++ b/lib/UTFT/version.txt
@@ -0,0 +1,12 @@
+Version:
+	1.0	12 Feb 2012  -  initial release
+	1.1	09 Apr 2012  -	added support for more display modules
+				added support for String objects to print()
+				fixed a bug in printNumF()
+				added optional minimum length and filler character to pintNumI() and printNumF()
+				added option to disable unneeded controller chip code to minimize memory use
+	1.2	14 Apr 2012  -	added support for more display modules
+				added getDisplayXSize() and getDisplayYSize()
+	1.3	03 Jun 2012  -	added support for more display modules
+				fixed a bug in the ITDB02-25H init
+
\ No newline at end of file
diff --git a/lib/UTouch/LICENSE.txt b/lib/UTouch/LICENSE.txt
new file mode 100644
index 0000000..77cec6d
--- /dev/null
+++ b/lib/UTouch/LICENSE.txt
@@ -0,0 +1,458 @@
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
diff --git a/lib/UTouch/UTouch.cpp b/lib/UTouch/UTouch.cpp
new file mode 100644
index 0000000..3b42269
--- /dev/null
+++ b/lib/UTouch/UTouch.cpp
@@ -0,0 +1,233 @@
+/*
+  UTouch.cpp - Arduino/chipKit library support for Color TFT LCD Touch screens
+  Copyright (C)2010-2012 Henning Karlsen. All right reserved
+
+  Basic functionality of this library are based on the demo-code provided by
+  ITead studio. You can find the latest version of the library at
+  http://www.henningkarlsen.com/electronics
+
+  If you make any modifications or improvements to the code, I would appreciate
+  that you share the code with me so that I might include it in the next release.
+  I can be contacted through http://www.henningkarlsen.com/electronics/contact.php
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "UTouch.h"
+#include "UTouchCD.h"
+
+UTouch::UTouch(byte tclk, byte tcs, byte din, byte dout, byte irq)
+{
+    T_CLK        = tclk;
+    T_CS         = tcs;
+    T_DIN        = din;
+    T_DOUT       = dout;
+    T_IRQ        = irq;
+}
+
+void UTouch::InitTouch(byte orientation)
+{
+	orient					= orientation;
+	_default_orientation	= CAL_S>>31;
+	touch_x_left			= (CAL_X>>14) & 0x3FFF;
+	touch_x_right			= CAL_X & 0x3FFF;
+	touch_y_top				= (CAL_Y>>14) & 0x3FFF;
+	touch_y_bottom			= CAL_Y & 0x3FFF;
+	disp_x_size				= (CAL_S>>12) & 0x0FFF;
+	disp_y_size				= CAL_S & 0x0FFF;
+	prec					= 10;
+
+	pinMode(T_CLK,  OUTPUT);
+    pinMode(T_CS,   OUTPUT);
+    pinMode(T_DIN,  OUTPUT);
+    pinMode(T_DOUT, INPUT);
+    pinMode(T_IRQ,  OUTPUT);
+
+	digitalWrite(T_CS,  HIGH);
+	digitalWrite(T_CLK, HIGH);
+	digitalWrite(T_DIN, HIGH);
+	digitalWrite(T_CLK, HIGH);
+}
+
+void UTouch::touch_WriteData(byte data)
+{
+	byte temp;
+	byte nop;
+
+	temp=data;
+	digitalWrite(T_CLK,LOW);
+
+	for(byte count=0; count<8; count++)
+	{
+		if(temp & 0x80)
+			digitalWrite(T_DIN, HIGH);
+		else
+			digitalWrite(T_DIN, LOW);
+		temp = temp << 1;
+		digitalWrite(T_CLK, LOW);
+		nop++;
+		digitalWrite(T_CLK, HIGH);
+		nop++;
+	}
+}
+
+word UTouch::touch_ReadData()
+{
+	byte nop;
+	word data = 0;
+	for(byte count=0; count<12; count++)
+	{
+		data <<= 1;
+		digitalWrite(T_CLK, HIGH);
+		nop++;
+		digitalWrite(T_CLK, LOW);
+		nop++;
+		if (digitalRead(T_DOUT))
+			data++;
+	}
+	return(data);
+}
+
+void UTouch::read()
+{
+	unsigned long tx=0, temp_x=0;
+	unsigned long ty=0, temp_y=0;
+	int datacount=0;
+
+	digitalWrite(T_CS,LOW);
+
+	for (int i=0; i<prec; i++)
+	{
+		touch_WriteData(0x90);
+		digitalWrite(T_CLK,HIGH);
+		digitalWrite(T_CLK,LOW);
+		temp_x=touch_ReadData();
+
+		touch_WriteData(0xD0);
+		digitalWrite(T_CLK,HIGH);
+		digitalWrite(T_CLK,LOW);
+		temp_y=touch_ReadData();
+
+		if (!((temp_x>max(touch_x_left, touch_x_right)) or (temp_x==0) or (temp_y>max(touch_y_top, touch_y_bottom)) or (temp_y==0)))
+		{
+			ty+=temp_x;
+			tx+=temp_y;
+			datacount++;
+		}
+	}
+
+	digitalWrite(T_CS,HIGH);
+	if (datacount>0)
+	{
+		if (orient == _default_orientation)
+		{
+			TP_X=tx/datacount;
+			TP_Y=ty/datacount;
+		}
+		else
+		{
+			TP_X=ty/datacount;
+			TP_Y=tx/datacount;
+		}
+	}
+	else
+	{
+		TP_X=-1;
+		TP_Y=-1;
+	}
+}
+
+bool UTouch::dataAvailable()
+{
+	bool avail;
+	pinMode(T_IRQ,  INPUT);
+	avail = !digitalRead(T_IRQ);
+	pinMode(T_IRQ,  OUTPUT);
+	return avail;
+}
+
+int UTouch::getX()
+{
+	long c;
+
+	if (orient == _default_orientation)
+	{
+		c = long(long(TP_X - touch_x_left) * (disp_x_size)) / long(touch_x_right - touch_x_left);
+		if (c<0)
+			c = 0;
+		if (c>disp_x_size)
+			c = disp_x_size;
+	}
+	else
+	{
+		if (_default_orientation == PORTRAIT)
+			c = long(long(TP_X - touch_y_top) * (-disp_y_size)) / long(touch_y_bottom - touch_y_top) + long(disp_y_size);
+		else
+			c = long(long(TP_X - touch_y_top) * (disp_y_size)) / long(touch_y_bottom - touch_y_top);
+		if (c<0)
+			c = 0;
+		if (c>disp_y_size)
+			c = disp_y_size;
+	}
+	return c;
+}
+
+int UTouch::getY()
+{
+	int c;
+
+	if (orient == _default_orientation)
+	{
+		c = long(long(TP_Y - touch_y_top) * (disp_y_size)) / long(touch_y_bottom - touch_y_top);
+		if (c<0)
+			c = 0;
+		if (c>disp_y_size)
+			c = disp_y_size;
+	}
+	else
+	{
+		if (_default_orientation == PORTRAIT)
+			c = long(long(TP_Y - touch_x_left) * (disp_x_size)) / long(touch_x_right - touch_x_left);
+		else
+			c = long(long(TP_Y - touch_x_left) * (-disp_x_size)) / long(touch_x_right - touch_x_left) + long(disp_x_size);
+		if (c<0)
+			c = 0;
+		if (c>disp_x_size)
+			c = disp_x_size;
+	}
+	return c;
+}
+
+void UTouch::setPrecision(byte precision)
+{
+	switch (precision)
+	{
+		case PREC_LOW:
+			prec=1;
+			break;
+		case PREC_MEDIUM:
+			prec=10;
+			break;
+		case PREC_HI:
+			prec=25;
+			break;
+		case PREC_EXTREME:
+			prec=100;
+			break;
+		default:
+			prec=10;
+			break;
+	}
+}
\ No newline at end of file
diff --git a/lib/UTouch/UTouch.h b/lib/UTouch/UTouch.h
new file mode 100644
index 0000000..bb230e8
--- /dev/null
+++ b/lib/UTouch/UTouch.h
@@ -0,0 +1,72 @@
+/*
+  UTouch.h - Arduino/chipKit library support for Color TFT LCD Touch screens
+  Copyright (C)2010-2012 Henning Karlsen. All right reserved
+
+  Basic functionality of this library are based on the demo-code provided by
+  ITead studio. You can find the latest version of the library at
+  http://www.henningkarlsen.com/electronics
+
+  If you make any modifications or improvements to the code, I would appreciate
+  that you share the code with me so that I might include it in the next release.
+  I can be contacted through http://www.henningkarlsen.com/electronics/contact.php
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef UTouch_h
+#define UTouch_h
+
+#if defined(ARDUINO) && ARDUINO >= 100
+	#include "Arduino.h"
+#else
+	#include "WProgram.h"
+#endif
+
+#define PORTRAIT			0
+#define LANDSCAPE			1
+
+#define PREC_LOW			1
+#define PREC_MEDIUM			2
+#define PREC_HI				3
+#define PREC_EXTREME		4
+
+class UTouch
+{
+	public:
+		word	TP_X ,TP_Y;
+
+				UTouch(byte tclk, byte tcs, byte tdin, byte dout, byte irq);
+
+		void	InitTouch(byte orientation = LANDSCAPE);
+		void	read();
+		bool	dataAvailable();
+		int		getX();
+		int		getY();
+		void	setPrecision(byte precision);
+
+    private:
+		byte	T_CLK, T_CS, T_DIN, T_DOUT, T_IRQ;
+		long	_default_orientation;
+		byte	orient;
+		byte	prec;
+		byte	display_model;
+		long	disp_x_size, disp_y_size, default_orientation;
+		long	touch_x_left, touch_x_right, touch_y_top, touch_y_bottom;
+
+		void	touch_WriteData(byte data);
+		word	touch_ReadData();
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/UTouch/UTouch.pdf b/lib/UTouch/UTouch.pdf
new file mode 100644
index 0000000..a0d0b6f
Binary files /dev/null and b/lib/UTouch/UTouch.pdf differ
diff --git a/lib/UTouch/UTouchCD.h b/lib/UTouch/UTouchCD.h
new file mode 100644
index 0000000..78f30a7
--- /dev/null
+++ b/lib/UTouch/UTouchCD.h
@@ -0,0 +1,23 @@
+// UTouchCD.h
+// ----------
+//
+// Since there are slight deviations in all touch screens you should run a
+// calibration on your display module. Run the UTouch_Calibration sketch
+// that came with this library and follow the on-screen instructions to
+// update this file.
+//
+// Remember that is you have multiple display modules they will probably
+// require different calibration data so you should run the calibration
+// every time you switch to another module.
+// You can, of course, store calibration data for all your modules here
+// and comment out the ones you dont need at the moment.
+//
+
+// These calibration settings works with my ITDB02-3.2S.
+// They MIGHT work on your display module, but you should run the
+// calibration sketch anyway.
+#define CAL_X 0x00244F6CUL
+#define CAL_Y 0x03B7C147UL
+#define CAL_S 0x8018F0EFUL
+
+
diff --git a/lib/UTouch/UTouch_Supported_display_modules.pdf b/lib/UTouch/UTouch_Supported_display_modules.pdf
new file mode 100644
index 0000000..b1d21c5
Binary files /dev/null and b/lib/UTouch/UTouch_Supported_display_modules.pdf differ
diff --git a/lib/UTouch/examples/Arduino/UTouch_ButtonTest/UTouch_ButtonTest.pde b/lib/UTouch/examples/Arduino/UTouch_ButtonTest/UTouch_ButtonTest.pde
new file mode 100644
index 0000000..9d88e7f
--- /dev/null
+++ b/lib/UTouch/examples/Arduino/UTouch_ButtonTest/UTouch_ButtonTest.pde
@@ -0,0 +1,236 @@
+// UTouch_ButtonTest (C)2010-2012 Henning Karlsen
+// web: http://www.henningkarlsen.com/electronics
+//
+// This program is a quick demo of how create and use buttons.
+//
+// This program requires the UTFT library.
+//
+// It is assumed that the display module is connected to an
+// appropriate shield or that you know how to change the pin
+// numbers in the setup.
+//
+
+#include <UTFT.h>
+#include <UTouch.h>
+
+// Declare which fonts we will be using
+extern uint8_t BigFont[];
+
+// Uncomment the next two lines for the Arduino 2009/UNO
+//UTFT        myGLCD(ITDB24D,19,18,17,16);   // Remember to change the model parameter to suit your display module!
+//UTouch      myTouch(15,10,14,9,8);
+
+// Uncomment the next two lines for the Arduino Mega
+UTFT        myGLCD(ITDB32WC, 38,39,40,41);   // Remember to change the model parameter to suit your display module!
+UTouch      myTouch(6,5,4,3,2);
+
+int x, y;
+char stCurrent[20]="";
+int stCurrentLen=0;
+char stLast[20]="";
+
+/*************************
+**   Custom functions   **
+*************************/
+
+void drawButtons()
+{
+// Draw the upper row of buttons
+  for (x=0; x<5; x++)
+  {
+    myGLCD.setColor(0, 0, 255);
+    myGLCD.fillRoundRect (10+(x*60), 10, 60+(x*60), 60);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.drawRoundRect (10+(x*60), 10, 60+(x*60), 60);
+    myGLCD.printNumI(x+1, 27+(x*60), 27);
+  }
+// Draw the center row of buttons
+  for (x=0; x<5; x++)
+  {
+    myGLCD.setColor(0, 0, 255);
+    myGLCD.fillRoundRect (10+(x*60), 70, 60+(x*60), 120);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.drawRoundRect (10+(x*60), 70, 60+(x*60), 120);
+    if (x<4)
+      myGLCD.printNumI(x+6, 27+(x*60), 87);
+  }
+  myGLCD.print("0", 267, 87);
+// Draw the lower row of buttons
+  myGLCD.setColor(0, 0, 255);
+  myGLCD.fillRoundRect (10, 130, 150, 180);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.drawRoundRect (10, 130, 150, 180);
+  myGLCD.print("Clear", 40, 147);
+  myGLCD.setColor(0, 0, 255);
+  myGLCD.fillRoundRect (160, 130, 300, 180);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.drawRoundRect (160, 130, 300, 180);
+  myGLCD.print("Enter", 190, 147);
+  myGLCD.setBackColor (0, 0, 0);
+}
+
+void updateStr(int val)
+{
+  if (stCurrentLen<20)
+  {
+    stCurrent[stCurrentLen]=val;
+    stCurrent[stCurrentLen+1]='\0';
+    stCurrentLen++;
+    myGLCD.setColor(0, 255, 0);
+    myGLCD.print(stCurrent, LEFT, 224);
+  }
+  else
+  {
+    myGLCD.setColor(255, 0, 0);
+    myGLCD.print("BUFFER FULL!", CENTER, 192);
+    delay(500);
+    myGLCD.print("            ", CENTER, 192);
+    delay(500);
+    myGLCD.print("BUFFER FULL!", CENTER, 192);
+    delay(500);
+    myGLCD.print("            ", CENTER, 192);
+    myGLCD.setColor(0, 255, 0);
+  }
+}
+
+// Draw a red frame while a button is touched
+void waitForIt(int x1, int y1, int x2, int y2)
+{
+  myGLCD.setColor(255, 0, 0);
+  myGLCD.drawRoundRect (x1, y1, x2, y2);
+  while (myTouch.dataAvailable())
+    myTouch.read();
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.drawRoundRect (x1, y1, x2, y2);
+}
+
+/*************************
+**  Required functions  **
+*************************/
+
+void setup()
+{
+// Initial setup
+  myGLCD.InitLCD();
+  myGLCD.clrScr();
+
+  myTouch.InitTouch();
+  myTouch.setPrecision(PREC_MEDIUM);
+
+  myGLCD.setFont(BigFont);
+  myGLCD.setBackColor(0, 0, 255);
+  drawButtons();
+}
+
+void loop()
+{
+  while (true)
+  {
+    if (myTouch.dataAvailable())
+    {
+      myTouch.read();
+      x=myTouch.getX();
+      y=myTouch.getY();
+
+      if ((y>=10) && (y<=60))  // Upper row
+      {
+        if ((x>=10) && (x<=60))  // Button: 1
+        {
+          waitForIt(10, 10, 60, 60);
+          updateStr('1');
+        }
+        if ((x>=70) && (x<=120))  // Button: 2
+        {
+          waitForIt(70, 10, 120, 60);
+          updateStr('2');
+        }
+        if ((x>=130) && (x<=180))  // Button: 3
+        {
+          waitForIt(130, 10, 180, 60);
+          updateStr('3');
+        }
+        if ((x>=190) && (x<=240))  // Button: 4
+        {
+          waitForIt(190, 10, 240, 60);
+          updateStr('4');
+        }
+        if ((x>=250) && (x<=300))  // Button: 5
+        {
+          waitForIt(250, 10, 300, 60);
+          updateStr('5');
+        }
+      }
+
+      if ((y>=70) && (y<=120))  // Center row
+      {
+        if ((x>=10) && (x<=60))  // Button: 6
+        {
+          waitForIt(10, 70, 60, 120);
+          updateStr('6');
+        }
+        if ((x>=70) && (x<=120))  // Button: 7
+        {
+          waitForIt(70, 70, 120, 120);
+          updateStr('7');
+        }
+        if ((x>=130) && (x<=180))  // Button: 8
+        {
+          waitForIt(130, 70, 180, 120);
+          updateStr('8');
+        }
+        if ((x>=190) && (x<=240))  // Button: 9
+        {
+          waitForIt(190, 70, 240, 120);
+          updateStr('9');
+        }
+        if ((x>=250) && (x<=300))  // Button: 0
+        {
+          waitForIt(250, 70, 300, 120);
+          updateStr('0');
+        }
+      }
+
+      if ((y>=130) && (y<=180))  // Upper row
+      {
+        if ((x>=10) && (x<=150))  // Button: Clear
+        {
+          waitForIt(10, 130, 150, 180);
+          stCurrent[0]='\0';
+          stCurrentLen=0;
+          myGLCD.setColor(0, 0, 0);
+          myGLCD.fillRect(0, 224, 319, 239);
+        }
+        if ((x>=160) && (x<=300))  // Button: Enter
+        {
+          waitForIt(160, 130, 300, 180);
+          if (stCurrentLen>0)
+          {
+            for (x=0; x<stCurrentLen+1; x++)
+            {
+              stLast[x]=stCurrent[x];
+            }
+            stCurrent[0]='\0';
+            stCurrentLen=0;
+            myGLCD.setColor(0, 0, 0);
+            myGLCD.fillRect(0, 208, 319, 239);
+            myGLCD.setColor(0, 255, 0);
+            myGLCD.print(stLast, LEFT, 208);
+          }
+          else
+          {
+            myGLCD.setColor(255, 0, 0);
+            myGLCD.print("BUFFER EMPTY", CENTER, 192);
+            delay(500);
+            myGLCD.print("            ", CENTER, 192);
+            delay(500);
+            myGLCD.print("BUFFER EMPTY", CENTER, 192);
+            delay(500);
+            myGLCD.print("            ", CENTER, 192);
+            myGLCD.setColor(0, 255, 0);
+          }
+        }
+      }
+    }
+  }
+}
+
diff --git a/lib/UTouch/examples/Arduino/UTouch_Calibration/UTouch_Calibration.pde b/lib/UTouch/examples/Arduino/UTouch_Calibration/UTouch_Calibration.pde
new file mode 100644
index 0000000..420a3f0
--- /dev/null
+++ b/lib/UTouch/examples/Arduino/UTouch_Calibration/UTouch_Calibration.pde
@@ -0,0 +1,347 @@
+// UTouch_Calibration (C)2012 Henning Karlsen
+// web: http://www.henningkarlsen.com/electronics
+//
+// This program can be used to calibrate the touchscreen
+// of the display modules.
+// This program requires the UTFT library and a touch
+// screen module that is compatible with UTFT.
+//
+// It is assumed that the display module is connected to an
+// appropriate shield or that you know how to change the pin
+// numbers in the setup.
+//
+// Instructions will be given on the display.
+//
+
+#include <UTFT.h>
+#include <UTouch.h>
+
+// Define the orientation of the touch screen. Further
+// information can be found in the instructions.
+#define TOUCH_ORIENTATION  PORTRAIT
+
+// Declare which fonts we will be using
+extern uint8_t SmallFont[];
+
+// Uncomment the next two lines for the Arduino 2009/UNO
+//UTFT        myGLCD(ITDB24D,19,18,17,16);   // Remember to change the model parameter to suit your display module!
+//UTouch      myTouch(15,10,14,9,8);
+
+// Uncomment the next two lines for the Arduino Mega
+UTFT        myGLCD(ITDB32WC,38,39,40,41);   // Remember to change the model parameter to suit your display module!
+UTouch      myTouch(6,5,4,3,2);
+
+// ************************************
+// DO NOT EDIT ANYTHING BELOW THIS LINE
+// ************************************
+uint32_t cx, cy;
+uint32_t rx[10], ry[10];
+uint32_t clx, crx, cty, cby;
+float px, py;
+int dispx, dispy, text_y_center;
+uint32_t calx, caly, cals;
+char buf[13];
+
+void setup()
+{
+  myGLCD.InitLCD();
+  myGLCD.clrScr();
+  myGLCD.setFont(SmallFont);
+
+  myTouch.InitTouch(TOUCH_ORIENTATION);
+  myTouch.setPrecision(PREC_LOW);
+  dispx=myGLCD.getDisplayXSize();
+  dispy=myGLCD.getDisplayYSize();
+  text_y_center=(dispy/2)-6;
+}
+
+void drawCrossHair(int x, int y)
+{
+  myGLCD.drawRect(x-10, y-10, x+10, y+10);
+  myGLCD.drawLine(x-5, y, x+5, y);
+  myGLCD.drawLine(x, y-5, x, y+5);
+}
+
+void readCoordinates()
+{
+  int iter = 2000;
+  int cnt = 0;
+  uint32_t tx=0;
+  uint32_t ty=0;
+  boolean OK = false;
+
+  while (OK == false)
+  {
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.print("*  PRESS  *", CENTER, text_y_center);
+    while (myTouch.dataAvailable() == false) {}
+    myGLCD.print("*  HOLD!  *", CENTER, text_y_center);
+    while ((myTouch.dataAvailable() == true) && (cnt<iter))
+    {
+      myTouch.read();
+      tx += myTouch.TP_X;
+      ty += myTouch.TP_Y;
+      cnt++;
+    }
+    if (cnt>=iter)
+    {
+      OK = true;
+    }
+    else
+    {
+      tx = 0;
+      ty = 0;
+      cnt = 0;
+    }
+  }
+
+  cx = tx / iter;
+  cy = ty / iter;
+
+}
+
+void calibrate(int x, int y, int i)
+{
+  myGLCD.setColor(255, 255, 255);
+  drawCrossHair(x,y);
+  myGLCD.setBackColor(255, 0, 0);
+  readCoordinates();
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.print("* RELEASE *", CENTER, text_y_center);
+  myGLCD.setColor(80, 80, 80);
+  drawCrossHair(x,y);
+  rx[i]=cx;
+  ry[i]=cy;
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+  }
+}
+
+void waitForTouch()
+{
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+  }
+  while (myTouch.dataAvailable() == false) {}
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+  }
+}
+
+void toHex(uint32_t num)
+{
+  buf[0] = '0';
+  buf[1] = 'x';
+  buf[10] = 'U';
+  buf[11] = 'L';
+  buf[12] = 0;
+  for (int zz=9; zz>1; zz--)
+  {
+    if ((num & 0xF) > 9)
+      buf[zz] = (num & 0xF) + 55;
+    else
+      buf[zz] = (num & 0xF) + 48;
+    num=num>>4;
+  }
+}
+
+void startup()
+{
+  myGLCD.setColor(255, 0, 0);
+  myGLCD.fillRect(0, 0, dispx-1, 13);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.drawLine(0, 14, dispx-1, 14);
+  myGLCD.print("UTouch Calibration", CENTER, 1);
+  myGLCD.setBackColor(0, 0, 0);
+
+  if (dispx==220)
+  {
+    myGLCD.print("Use a stylus or something", LEFT, 30);
+    myGLCD.print("similar to touch as close", LEFT, 42);
+    myGLCD.print("to the center of the", LEFT, 54);
+    myGLCD.print("highlighted crosshair as", LEFT, 66);
+    myGLCD.print("possible. Keep as still as", LEFT, 78);
+    myGLCD.print("possible and keep holding", LEFT, 90);
+    myGLCD.print("until the highlight is", LEFT, 102);
+    myGLCD.print("removed. Repeat for all", LEFT, 114);
+    myGLCD.print("crosshairs in sequence.", LEFT, 126);
+    myGLCD.print("Touch screen to continue", CENTER, 162);
+  }
+  else
+  {
+    myGLCD.print("INSTRUCTIONS", CENTER, 30);
+    myGLCD.print("Use a stylus or something similar to", LEFT, 50);
+    myGLCD.print("touch as close to the center of the", LEFT, 62);
+    myGLCD.print("highlighted crosshair as possible. Keep", LEFT, 74);
+    myGLCD.print("as still as possible and keep holding", LEFT, 86);
+    myGLCD.print("until the highlight is removed. Repeat", LEFT, 98);
+    myGLCD.print("for all crosshairs in sequence.", LEFT, 110);
+
+    myGLCD.print("Further instructions will be displayed", LEFT, 134);
+    myGLCD.print("when the calibration is complete.", LEFT, 146);
+
+    myGLCD.print("Do NOT use your finger as a calibration", LEFT, 170);
+    myGLCD.print("stylus or the result WILL BE imprecise.", LEFT, 182);
+
+    myGLCD.print("Touch screen to continue", CENTER, 226);
+  }
+
+  waitForTouch();
+  myGLCD.clrScr();
+}
+
+void done()
+{
+  myGLCD.clrScr();
+  myGLCD.setColor(255, 0, 0);
+  myGLCD.fillRect(0, 0, dispx-1, 13);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.drawLine(0, 14, dispx-1, 14);
+  myGLCD.print("UTouch Calibration", CENTER, 1);
+  myGLCD.setBackColor(0, 0, 0);
+
+  if (dispx==220)
+  {
+    myGLCD.print("To use the new calibration", LEFT, 30);
+    myGLCD.print("settings you must edit the", LEFT, 42);
+    myGLCD.setColor(160, 160, 255);
+    myGLCD.print("UTouchCD.h", LEFT, 54);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.print("file and change", 88, 54);
+    myGLCD.print("the following values. The", LEFT, 66);
+    myGLCD.print("values are located right", LEFT, 78);
+    myGLCD.print("below the opening comment.", LEFT, 90);
+    myGLCD.print("CAL_X", LEFT, 110);
+    myGLCD.print("CAL_Y", LEFT, 122);
+    myGLCD.print("CAL_S", LEFT, 134);
+    toHex(calx);
+    myGLCD.print(buf, 75, 110);
+    toHex(caly);
+    myGLCD.print(buf, 75, 122);
+    toHex(cals);
+    myGLCD.print(buf, 75, 134);
+  }
+  else
+  {
+    myGLCD.print("CALIBRATION COMPLETE", CENTER, 30);
+    myGLCD.print("To use the new calibration", LEFT, 50);
+    myGLCD.print("settings you must edit the", LEFT, 62);
+    myGLCD.setColor(160, 160, 255);
+    myGLCD.print("UTouchCD.h", LEFT, 74);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.print("file and change", 88, 74);
+    myGLCD.print("the following values.", LEFT, 86);
+    myGLCD.print("The values are located right", LEFT, 98);
+    myGLCD.print("below the opening comment in", LEFT, 110);
+    myGLCD.print("the file.", LEFT, 122);
+    myGLCD.print("CAL_X", LEFT, 150);
+    myGLCD.print("CAL_Y", LEFT, 162);
+    myGLCD.print("CAL_S", LEFT, 174);
+
+    toHex(calx);
+    myGLCD.print(buf, 75, 150);
+    toHex(caly);
+    myGLCD.print(buf, 75, 162);
+    toHex(cals);
+    myGLCD.print(buf, 75, 174);
+  }
+
+}
+
+void loop()
+{
+  startup();
+
+  myGLCD.setColor(80, 80, 80);
+  drawCrossHair(dispx-11, 10);
+  drawCrossHair(dispx/2, 10);
+  drawCrossHair(10, 10);
+  drawCrossHair(dispx-11, dispy/2);
+  drawCrossHair(10, dispy/2);
+  drawCrossHair(dispx-11, dispy-11);
+  drawCrossHair(dispx/2, dispy-11);
+  drawCrossHair(10, dispy-11);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.print("***********", CENTER, text_y_center-12);
+  myGLCD.print("***********", CENTER, text_y_center+12);
+
+  calibrate(10, 10, 0);
+  calibrate(10, dispy/2, 1);
+  calibrate(10, dispy-11, 2);
+  calibrate(dispx/2, 10, 3);
+  calibrate(dispx/2, dispy-11, 4);
+  calibrate(dispx-11, 10, 5);
+  calibrate(dispx-11, dispy/2, 6);
+  calibrate(dispx-11, dispy-11, 7);
+
+  if (TOUCH_ORIENTATION == LANDSCAPE)
+    cals=(long(dispx-1)<<12)+(dispy-1);
+  else
+    cals=(long(dispy-1)<<12)+(dispx-1);
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+    px = abs(((float(rx[2]+rx[4]+rx[7])/3)-(float(rx[0]+rx[3]+rx[5])/3))/(dispy-20));  // PORTRAIT
+  else
+    px = abs(((float(rx[5]+rx[6]+rx[7])/3)-(float(rx[0]+rx[1]+rx[2])/3))/(dispy-20));  // LANDSCAPE
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+  {
+    clx = (((rx[0]+rx[3]+rx[5])/3));  // PORTRAIT
+    crx = (((rx[2]+rx[4]+rx[7])/3));  // PORTRAIT
+  }
+  else
+  {
+    clx = (((rx[0]+rx[1]+rx[2])/3));  // LANDSCAPE
+    crx = (((rx[5]+rx[6]+rx[7])/3));  // LANDSCAPE
+  }
+  if (clx<crx)
+  {
+    clx = clx - (px*10);
+    crx = crx + (px*10);
+  }
+  else
+  {
+    clx = clx + (px*10);
+    crx = crx - (px*10);
+  }
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+    py = abs(((float(ry[5]+ry[6]+ry[7])/3)-(float(ry[0]+ry[1]+ry[2])/3))/(dispx-20));  // PORTRAIT
+  else
+    py = abs(((float(ry[0]+ry[3]+ry[5])/3)-(float(ry[2]+ry[4]+ry[7])/3))/(dispx-20));  // LANDSCAPE
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+  {
+    cty = (((ry[5]+ry[6]+ry[7])/3));  // PORTRAIT
+    cby = (((ry[0]+ry[1]+ry[2])/3));  // PORTRAIT
+  }
+  else
+  {
+    cty = (((ry[0]+ry[3]+ry[5])/3));  // LANDSCAPE
+    cby = (((ry[2]+ry[4]+ry[7])/3));  // LANDSCAPE
+  }
+  if (cty<cby)
+  {
+    cty = cty - (py*10);
+    cby = cby + (py*10);
+  }
+  else
+  {
+    cty = cty + (py*10);
+    cby = cby - (py*10);
+  }
+
+  calx = (long(clx)<<14) + long(crx);
+  caly = (long(cty)<<14) + long(cby);
+  if (TOUCH_ORIENTATION == LANDSCAPE)
+    cals = cals + (1L<<31);
+
+  done();
+  while(true) {}
+}
diff --git a/lib/UTouch/examples/Arduino/UTouch_QuickDraw/UTouch_QuickDraw.pde b/lib/UTouch/examples/Arduino/UTouch_QuickDraw/UTouch_QuickDraw.pde
new file mode 100644
index 0000000..de15484
--- /dev/null
+++ b/lib/UTouch/examples/Arduino/UTouch_QuickDraw/UTouch_QuickDraw.pde
@@ -0,0 +1,48 @@
+// UTouch_QuickDraw (C)2010-2012 Henning Karlsen
+// web: http://www.henningkarlsen.com/electronics
+//
+// This program is a quick demo of how to use the library.
+//
+// This program requires the UTFT library.
+//
+// It is assumed that the display module is connected to an
+// appropriate shield or that you know how to change the pin
+// numbers in the setup.
+//
+
+#include <UTFT.h>
+#include <UTouch.h>
+
+// Uncomment the next two lines for the Arduino 2009/UNO
+//UTFT        myGLCD(ITDB24D,19,18,17,16);   // Remember to change the model parameter to suit your display module!
+//UTouch      myTouch(15,10,14,9,8);
+
+// Uncomment the next two lines for the Arduino Mega
+UTFT        myGLCD(ITDB32WC, 38,39,40,41);   // Remember to change the model parameter to suit your display module!
+UTouch      myTouch(6,5,4,3,2);
+
+void setup()
+{
+  myGLCD.InitLCD();
+  myGLCD.clrScr();
+
+  myTouch.InitTouch();
+  myTouch.setPrecision(PREC_MEDIUM);
+}
+
+void loop()
+{
+  long x, y;
+
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+    x = myTouch.getX();
+    y = myTouch.getY();
+    if ((x!=-1) and (y!=-1))
+    {
+      myGLCD.drawPixel (x, y);
+    }
+  }
+}
+
diff --git a/lib/UTouch/examples/chipKit/UTouch_ButtonTest/UTouch_ButtonTest.pde b/lib/UTouch/examples/chipKit/UTouch_ButtonTest/UTouch_ButtonTest.pde
new file mode 100644
index 0000000..83552f3
--- /dev/null
+++ b/lib/UTouch/examples/chipKit/UTouch_ButtonTest/UTouch_ButtonTest.pde
@@ -0,0 +1,236 @@
+// UTouch_ButtonTest (C)2010-2012 Henning Karlsen
+// web: http://www.henningkarlsen.com/electronics
+//
+// This program is a quick demo of how create and use buttons.
+//
+// This program requires the UTFT library.
+//
+// It is assumed that the display module is connected to an
+// appropriate shield or that you know how to change the pin
+// numbers in the setup.
+//
+
+#include <UTFT.h>
+#include <UTouch.h>
+
+// Declare which fonts we will be using
+extern uint8_t BigFont[];
+
+// Uncomment the next line for chipKit Uno32
+UTFT        myGLCD(ITDB24D,34,35,36,37);   // Remember to change the model parameter to suit your display module!
+UTouch      myTouch(20,21,22,23,24);
+
+// Uncomment the next line for chipKit Max32
+//UTFT        myGLCD(ITDB32S,82,83,84,85);   // Remember to change the model parameter to suit your display module!
+//UTouch      myTouch(62,63,64,65,66);
+
+int x, y;
+char stCurrent[20]="";
+int stCurrentLen=0;
+char stLast[20]="";
+
+/*************************
+**   Custom functions   **
+*************************/
+
+void drawButtons()
+{
+// Draw the upper row of buttons
+  for (x=0; x<5; x++)
+  {
+    myGLCD.setColor(0, 0, 255);
+    myGLCD.fillRoundRect (10+(x*60), 10, 60+(x*60), 60);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.drawRoundRect (10+(x*60), 10, 60+(x*60), 60);
+    myGLCD.printNumI(x+1, 27+(x*60), 27);
+  }
+// Draw the center row of buttons
+  for (x=0; x<5; x++)
+  {
+    myGLCD.setColor(0, 0, 255);
+    myGLCD.fillRoundRect (10+(x*60), 70, 60+(x*60), 120);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.drawRoundRect (10+(x*60), 70, 60+(x*60), 120);
+    if (x<4)
+      myGLCD.printNumI(x+6, 27+(x*60), 87);
+  }
+  myGLCD.print("0", 267, 87);
+// Draw the lower row of buttons
+  myGLCD.setColor(0, 0, 255);
+  myGLCD.fillRoundRect (10, 130, 150, 180);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.drawRoundRect (10, 130, 150, 180);
+  myGLCD.print("Clear", 40, 147);
+  myGLCD.setColor(0, 0, 255);
+  myGLCD.fillRoundRect (160, 130, 300, 180);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.drawRoundRect (160, 130, 300, 180);
+  myGLCD.print("Enter", 190, 147);
+  myGLCD.setBackColor (0, 0, 0);
+}
+
+void updateStr(int val)
+{
+  if (stCurrentLen<20)
+  {
+    stCurrent[stCurrentLen]=val;
+    stCurrent[stCurrentLen+1]='\0';
+    stCurrentLen++;
+    myGLCD.setColor(0, 255, 0);
+    myGLCD.print(stCurrent, LEFT, 224);
+  }
+  else
+  {
+    myGLCD.setColor(255, 0, 0);
+    myGLCD.print("BUFFER FULL!", CENTER, 192);
+    delay(500);
+    myGLCD.print("            ", CENTER, 192);
+    delay(500);
+    myGLCD.print("BUFFER FULL!", CENTER, 192);
+    delay(500);
+    myGLCD.print("            ", CENTER, 192);
+    myGLCD.setColor(0, 255, 0);
+  }
+}
+
+// Draw a red frame while a button is touched
+void waitForIt(int x1, int y1, int x2, int y2)
+{
+  myGLCD.setColor(255, 0, 0);
+  myGLCD.drawRoundRect (x1, y1, x2, y2);
+  while (myTouch.dataAvailable())
+    myTouch.read();
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.drawRoundRect (x1, y1, x2, y2);
+}
+
+/*************************
+**  Required functions  **
+*************************/
+
+void setup()
+{
+// Initial setup
+  myGLCD.InitLCD();
+  myGLCD.clrScr();
+
+  myTouch.InitTouch();
+  myTouch.setPrecision(PREC_MEDIUM);
+
+  myGLCD.setFont(BigFont);
+  myGLCD.setBackColor(0, 0, 255);
+  drawButtons();
+}
+
+void loop()
+{
+  while (true)
+  {
+    if (myTouch.dataAvailable())
+    {
+      myTouch.read();
+      x=myTouch.getX();
+      y=myTouch.getY();
+
+      if ((y>=10) && (y<=60))  // Upper row
+      {
+        if ((x>=10) && (x<=60))  // Button: 1
+        {
+          waitForIt(10, 10, 60, 60);
+          updateStr('1');
+        }
+        if ((x>=70) && (x<=120))  // Button: 2
+        {
+          waitForIt(70, 10, 120, 60);
+          updateStr('2');
+        }
+        if ((x>=130) && (x<=180))  // Button: 3
+        {
+          waitForIt(130, 10, 180, 60);
+          updateStr('3');
+        }
+        if ((x>=190) && (x<=240))  // Button: 4
+        {
+          waitForIt(190, 10, 240, 60);
+          updateStr('4');
+        }
+        if ((x>=250) && (x<=300))  // Button: 5
+        {
+          waitForIt(250, 10, 300, 60);
+          updateStr('5');
+        }
+      }
+
+      if ((y>=70) && (y<=120))  // Center row
+      {
+        if ((x>=10) && (x<=60))  // Button: 6
+        {
+          waitForIt(10, 70, 60, 120);
+          updateStr('6');
+        }
+        if ((x>=70) && (x<=120))  // Button: 7
+        {
+          waitForIt(70, 70, 120, 120);
+          updateStr('7');
+        }
+        if ((x>=130) && (x<=180))  // Button: 8
+        {
+          waitForIt(130, 70, 180, 120);
+          updateStr('8');
+        }
+        if ((x>=190) && (x<=240))  // Button: 9
+        {
+          waitForIt(190, 70, 240, 120);
+          updateStr('9');
+        }
+        if ((x>=250) && (x<=300))  // Button: 0
+        {
+          waitForIt(250, 70, 300, 120);
+          updateStr('0');
+        }
+      }
+
+      if ((y>=130) && (y<=180))  // Upper row
+      {
+        if ((x>=10) && (x<=150))  // Button: Clear
+        {
+          waitForIt(10, 130, 150, 180);
+          stCurrent[0]='\0';
+          stCurrentLen=0;
+          myGLCD.setColor(0, 0, 0);
+          myGLCD.fillRect(0, 224, 319, 239);
+        }
+        if ((x>=160) && (x<=300))  // Button: Enter
+        {
+          waitForIt(160, 130, 300, 180);
+          if (stCurrentLen>0)
+          {
+            for (x=0; x<stCurrentLen+1; x++)
+            {
+              stLast[x]=stCurrent[x];
+            }
+            stCurrent[0]='\0';
+            stCurrentLen=0;
+            myGLCD.setColor(0, 0, 0);
+            myGLCD.fillRect(0, 208, 319, 239);
+            myGLCD.setColor(0, 255, 0);
+            myGLCD.print(stLast, LEFT, 208);
+          }
+          else
+          {
+            myGLCD.setColor(255, 0, 0);
+            myGLCD.print("BUFFER EMPTY", CENTER, 192);
+            delay(500);
+            myGLCD.print("            ", CENTER, 192);
+            delay(500);
+            myGLCD.print("BUFFER EMPTY", CENTER, 192);
+            delay(500);
+            myGLCD.print("            ", CENTER, 192);
+            myGLCD.setColor(0, 255, 0);
+          }
+        }
+      }
+    }
+  }
+}
+
diff --git a/lib/UTouch/examples/chipKit/UTouch_Calibration/UTouch_Calibration.pde b/lib/UTouch/examples/chipKit/UTouch_Calibration/UTouch_Calibration.pde
new file mode 100644
index 0000000..81e6a61
--- /dev/null
+++ b/lib/UTouch/examples/chipKit/UTouch_Calibration/UTouch_Calibration.pde
@@ -0,0 +1,347 @@
+// UTouch_Calibration (C)2012 Henning Karlsen
+// web: http://www.henningkarlsen.com/electronics
+//
+// This program can be used to calibrate the touchscreen
+// of the display modules.
+// This program requires the UTFT library and a touch
+// screen module that is compatible with UTFT.
+//
+// It is assumed that the display module is connected to an
+// appropriate shield or that you know how to change the pin
+// numbers in the setup.
+//
+// Instructions will be given on the display.
+//
+
+#include <UTFT.h>
+#include <UTouch.h>
+
+// Define the orientation of the touch screen. Further
+// information can be found in the instructions.
+#define TOUCH_ORIENTATION  PORTRAIT
+
+// Declare which fonts we will be using
+extern uint8_t SmallFont[];
+
+// Uncomment the next line for chipKit Uno32
+UTFT        myGLCD(ITDB24D,34,35,36,37);   // Remember to change the model parameter to suit your display module!
+UTouch      myTouch(20,21,22,23,24);
+
+// Uncomment the next line for chipKit Max32
+//UTFT        myGLCD(ITDB32S,82,83,84,85);   // Remember to change the model parameter to suit your display module!
+//UTouch      myTouch(62,63,64,65,66);
+
+// ************************************
+// DO NOT EDIT ANYTHING BELOW THIS LINE
+// ************************************
+uint32_t cx, cy;
+uint32_t rx[10], ry[10];
+uint32_t clx, crx, cty, cby;
+float px, py;
+int dispx, dispy, text_y_center;
+uint32_t calx, caly, cals;
+char buf[13];
+
+void setup()
+{
+  myGLCD.InitLCD();
+  myGLCD.clrScr();
+  myGLCD.setFont(SmallFont);
+
+  myTouch.InitTouch(TOUCH_ORIENTATION);
+  myTouch.setPrecision(PREC_LOW);
+  dispx=myGLCD.getDisplayXSize();
+  dispy=myGLCD.getDisplayYSize();
+  text_y_center=(dispy/2)-6;
+}
+
+void drawCrossHair(int x, int y)
+{
+  myGLCD.drawRect(x-10, y-10, x+10, y+10);
+  myGLCD.drawLine(x-5, y, x+5, y);
+  myGLCD.drawLine(x, y-5, x, y+5);
+}
+
+void readCoordinates()
+{
+  int iter = 2000;
+  int cnt = 0;
+  uint32_t tx=0;
+  uint32_t ty=0;
+  boolean OK = false;
+
+  while (OK == false)
+  {
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.print("*  PRESS  *", CENTER, text_y_center);
+    while (myTouch.dataAvailable() == false) {}
+    myGLCD.print("*  HOLD!  *", CENTER, text_y_center);
+    while ((myTouch.dataAvailable() == true) && (cnt<iter))
+    {
+      myTouch.read();
+      tx += myTouch.TP_X;
+      ty += myTouch.TP_Y;
+      cnt++;
+    }
+    if (cnt>=iter)
+    {
+      OK = true;
+    }
+    else
+    {
+      tx = 0;
+      ty = 0;
+      cnt = 0;
+    }
+  }
+
+  cx = tx / iter;
+  cy = ty / iter;
+
+}
+
+void calibrate(int x, int y, int i)
+{
+  myGLCD.setColor(255, 255, 255);
+  drawCrossHair(x,y);
+  myGLCD.setBackColor(255, 0, 0);
+  readCoordinates();
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.print("* RELEASE *", CENTER, text_y_center);
+  myGLCD.setColor(80, 80, 80);
+  drawCrossHair(x,y);
+  rx[i]=cx;
+  ry[i]=cy;
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+  }
+}
+
+void waitForTouch()
+{
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+  }
+  while (myTouch.dataAvailable() == false) {}
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+  }
+}
+
+void toHex(uint32_t num)
+{
+  buf[0] = '0';
+  buf[1] = 'x';
+  buf[10] = 'U';
+  buf[11] = 'L';
+  buf[12] = 0;
+  for (int zz=9; zz>1; zz--)
+  {
+    if ((num & 0xF) > 9)
+      buf[zz] = (num & 0xF) + 55;
+    else
+      buf[zz] = (num & 0xF) + 48;
+    num=num>>4;
+  }
+}
+
+void startup()
+{
+  myGLCD.setColor(255, 0, 0);
+  myGLCD.fillRect(0, 0, dispx-1, 13);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.drawLine(0, 14, dispx-1, 14);
+  myGLCD.print("UTouch Calibration", CENTER, 1);
+  myGLCD.setBackColor(0, 0, 0);
+
+  if (dispx==220)
+  {
+    myGLCD.print("Use a stylus or something", LEFT, 30);
+    myGLCD.print("similar to touch as close", LEFT, 42);
+    myGLCD.print("to the center of the", LEFT, 54);
+    myGLCD.print("highlighted crosshair as", LEFT, 66);
+    myGLCD.print("possible. Keep as still as", LEFT, 78);
+    myGLCD.print("possible and keep holding", LEFT, 90);
+    myGLCD.print("until the highlight is", LEFT, 102);
+    myGLCD.print("removed. Repeat for all", LEFT, 114);
+    myGLCD.print("crosshairs in sequence.", LEFT, 126);
+    myGLCD.print("Touch screen to continue", CENTER, 162);
+  }
+  else
+  {
+    myGLCD.print("INSTRUCTIONS", CENTER, 30);
+    myGLCD.print("Use a stylus or something similar to", LEFT, 50);
+    myGLCD.print("touch as close to the center of the", LEFT, 62);
+    myGLCD.print("highlighted crosshair as possible. Keep", LEFT, 74);
+    myGLCD.print("as still as possible and keep holding", LEFT, 86);
+    myGLCD.print("until the highlight is removed. Repeat", LEFT, 98);
+    myGLCD.print("for all crosshairs in sequence.", LEFT, 110);
+
+    myGLCD.print("Further instructions will be displayed", LEFT, 134);
+    myGLCD.print("when the calibration is complete.", LEFT, 146);
+
+    myGLCD.print("Do NOT use your finger as a calibration", LEFT, 170);
+    myGLCD.print("stylus or the result WILL BE imprecise.", LEFT, 182);
+
+    myGLCD.print("Touch screen to continue", CENTER, 226);
+  }
+
+  waitForTouch();
+  myGLCD.clrScr();
+}
+
+void done()
+{
+  myGLCD.clrScr();
+  myGLCD.setColor(255, 0, 0);
+  myGLCD.fillRect(0, 0, dispx-1, 13);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.drawLine(0, 14, dispx-1, 14);
+  myGLCD.print("UTouch Calibration", CENTER, 1);
+  myGLCD.setBackColor(0, 0, 0);
+
+  if (dispx==220)
+  {
+    myGLCD.print("To use the new calibration", LEFT, 30);
+    myGLCD.print("settings you must edit the", LEFT, 42);
+    myGLCD.setColor(160, 160, 255);
+    myGLCD.print("UTouchCD.h", LEFT, 54);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.print("file and change", 88, 54);
+    myGLCD.print("the following values. The", LEFT, 66);
+    myGLCD.print("values are located right", LEFT, 78);
+    myGLCD.print("below the opening comment.", LEFT, 90);
+    myGLCD.print("CAL_X", LEFT, 110);
+    myGLCD.print("CAL_Y", LEFT, 122);
+    myGLCD.print("CAL_S", LEFT, 134);
+    toHex(calx);
+    myGLCD.print(buf, 75, 110);
+    toHex(caly);
+    myGLCD.print(buf, 75, 122);
+    toHex(cals);
+    myGLCD.print(buf, 75, 134);
+  }
+  else
+  {
+    myGLCD.print("CALIBRATION COMPLETE", CENTER, 30);
+    myGLCD.print("To use the new calibration", LEFT, 50);
+    myGLCD.print("settings you must edit the", LEFT, 62);
+    myGLCD.setColor(160, 160, 255);
+    myGLCD.print("UTouchCD.h", LEFT, 74);
+    myGLCD.setColor(255, 255, 255);
+    myGLCD.print("file and change", 88, 74);
+    myGLCD.print("the following values.", LEFT, 86);
+    myGLCD.print("The values are located right", LEFT, 98);
+    myGLCD.print("below the opening comment in", LEFT, 110);
+    myGLCD.print("the file.", LEFT, 122);
+    myGLCD.print("CAL_X", LEFT, 150);
+    myGLCD.print("CAL_Y", LEFT, 162);
+    myGLCD.print("CAL_S", LEFT, 174);
+
+    toHex(calx);
+    myGLCD.print(buf, 75, 150);
+    toHex(caly);
+    myGLCD.print(buf, 75, 162);
+    toHex(cals);
+    myGLCD.print(buf, 75, 174);
+  }
+
+}
+
+void loop()
+{
+  startup();
+
+  myGLCD.setColor(80, 80, 80);
+  drawCrossHair(dispx-11, 10);
+  drawCrossHair(dispx/2, 10);
+  drawCrossHair(10, 10);
+  drawCrossHair(dispx-11, dispy/2);
+  drawCrossHair(10, dispy/2);
+  drawCrossHair(dispx-11, dispy-11);
+  drawCrossHair(dispx/2, dispy-11);
+  drawCrossHair(10, dispy-11);
+  myGLCD.setColor(255, 255, 255);
+  myGLCD.setBackColor(255, 0, 0);
+  myGLCD.print("***********", CENTER, text_y_center-12);
+  myGLCD.print("***********", CENTER, text_y_center+12);
+
+  calibrate(10, 10, 0);
+  calibrate(10, dispy/2, 1);
+  calibrate(10, dispy-11, 2);
+  calibrate(dispx/2, 10, 3);
+  calibrate(dispx/2, dispy-11, 4);
+  calibrate(dispx-11, 10, 5);
+  calibrate(dispx-11, dispy/2, 6);
+  calibrate(dispx-11, dispy-11, 7);
+
+  if (TOUCH_ORIENTATION == LANDSCAPE)
+    cals=(long(dispx-1)<<12)+(dispy-1);
+  else
+    cals=(long(dispy-1)<<12)+(dispx-1);
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+    px = abs(((float(rx[2]+rx[4]+rx[7])/3)-(float(rx[0]+rx[3]+rx[5])/3))/(dispy-20));  // PORTRAIT
+  else
+    px = abs(((float(rx[5]+rx[6]+rx[7])/3)-(float(rx[0]+rx[1]+rx[2])/3))/(dispy-20));  // LANDSCAPE
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+  {
+    clx = (((rx[0]+rx[3]+rx[5])/3));  // PORTRAIT
+    crx = (((rx[2]+rx[4]+rx[7])/3));  // PORTRAIT
+  }
+  else
+  {
+    clx = (((rx[0]+rx[1]+rx[2])/3));  // LANDSCAPE
+    crx = (((rx[5]+rx[6]+rx[7])/3));  // LANDSCAPE
+  }
+  if (clx<crx)
+  {
+    clx = clx - (px*10);
+    crx = crx + (px*10);
+  }
+  else
+  {
+    clx = clx + (px*10);
+    crx = crx - (px*10);
+  }
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+    py = abs(((float(ry[5]+ry[6]+ry[7])/3)-(float(ry[0]+ry[1]+ry[2])/3))/(dispx-20));  // PORTRAIT
+  else
+    py = abs(((float(ry[0]+ry[3]+ry[5])/3)-(float(ry[2]+ry[4]+ry[7])/3))/(dispx-20));  // LANDSCAPE
+
+  if (TOUCH_ORIENTATION == PORTRAIT)
+  {
+    cty = (((ry[5]+ry[6]+ry[7])/3));  // PORTRAIT
+    cby = (((ry[0]+ry[1]+ry[2])/3));  // PORTRAIT
+  }
+  else
+  {
+    cty = (((ry[0]+ry[3]+ry[5])/3));  // LANDSCAPE
+    cby = (((ry[2]+ry[4]+ry[7])/3));  // LANDSCAPE
+  }
+  if (cty<cby)
+  {
+    cty = cty - (py*10);
+    cby = cby + (py*10);
+  }
+  else
+  {
+    cty = cty + (py*10);
+    cby = cby - (py*10);
+  }
+
+  calx = (long(clx)<<14) + long(crx);
+  caly = (long(cty)<<14) + long(cby);
+  if (TOUCH_ORIENTATION == LANDSCAPE)
+    cals = cals + (1L<<31);
+
+  done();
+  while(true) {}
+}
diff --git a/lib/UTouch/examples/chipKit/UTouch_QuickDraw/UTouch_QuickDraw.pde b/lib/UTouch/examples/chipKit/UTouch_QuickDraw/UTouch_QuickDraw.pde
new file mode 100644
index 0000000..0fc1cbf
--- /dev/null
+++ b/lib/UTouch/examples/chipKit/UTouch_QuickDraw/UTouch_QuickDraw.pde
@@ -0,0 +1,46 @@
+// UTouch_QuickDraw (C)2010-2012 Henning Karlsen
+// web: http://www.henningkarlsen.com/electronics
+//
+// This program is a quick demo of how to use the library.
+//
+// This program requires the UTFT library.
+//
+// It is assumed that the display module is connected to an
+// appropriate shield or that you know how to change the pin
+// numbers in the setup.
+//
+
+#include <UTFT.h>
+#include <UTouch.h>
+
+// Uncomment the next line for chipKit Uno32
+UTFT        myGLCD(ITDB24D,34,35,36,37);   // Remember to change the model parameter to suit your display module!
+UTouch      myTouch(20,21,22,23,24);
+
+// Uncomment the next line for chipKit Max32
+//UTFT        myGLCD(ITDB32S,82,83,84,85);   // Remember to change the model parameter to suit your display module!
+//UTouch      myTouch(62,63,64,65,66);
+
+void setup()
+{
+  myGLCD.InitLCD();
+  myGLCD.clrScr();
+
+  myTouch.InitTouch();
+  myTouch.setPrecision(PREC_MEDIUM);
+}
+
+void loop()
+{
+  long x, y;
+
+  while (myTouch.dataAvailable() == true)
+  {
+    myTouch.read();
+    x = myTouch.getX();
+    y = myTouch.getY();
+    if ((x!=-1) and (y!=-1))
+      myGLCD.drawPixel (x, y);
+  }
+}
+
diff --git a/lib/UTouch/keywords.txt b/lib/UTouch/keywords.txt
new file mode 100644
index 0000000..9c7db2d
--- /dev/null
+++ b/lib/UTouch/keywords.txt
@@ -0,0 +1,17 @@
+UTouch	KEYWORD1
+
+InitTouch	KEYWORD2
+read	KEYWORD2
+dataAvailable	KEYWORD2
+getX	KEYWORD2
+getY	KEYWORD2
+setPrecision	KEYWORD2
+
+PREC_LOW	LITERAL1
+PREC_MEDIUM	LITERAL1
+PREC_HI	LITERAL1
+PREC_EXTREME	LITERAL1
+PORTRAIT	LITERAL1
+LANDSCAPE	LITERAL1
+TP_X	LITERAL1
+TP_Y	LITERAL1
diff --git a/lib/UTouch/version.txt b/lib/UTouch/version.txt
new file mode 100644
index 0000000..5d1f2ba
--- /dev/null
+++ b/lib/UTouch/version.txt
@@ -0,0 +1,2 @@
+Version:
+	1.0	 1 Dec 2012  -  initial release