Broke led functionality as a separate library.

--- esp8266/main/main.ino
+++ esp8266/main/main.ino
@@ -3,628 +3,343 @@
 #include <DNSServer.h>
 #include <ESP8266WebServer.h>
 #include <WiFiManager.h>
-#include <cmath>
+#include <LedEngine.h>

 // GPIO settings
-const int redPin_    = 15; //12
-const int greenPin_  = 13; //15
-const int bluePin_   = 12; //13
-const int w1Pin_     = 14;
-const int w2Pin_     = 4;
-const int led1Pin_   = 5;
-const int led2Pin_   = 1;
-const int pwmRange_  = 1023;
+const int led1Pin = 5;
+const int led2Pin = 1;
+const int redPin = 15; //12
+const int greenPin = 13; //15
+const int bluePin = 12; //13
+const int warmPin = 14;
+const int coldPin = 4;
+const int pwmRange = 1023;
+
+// LED engine
+LedEngine led(redPin, greenPin, bluePin, warmPin, coldPin, pwmRange);
+
+// HTTP server
+ESP8266WebServer server(80);

 // Index.html
 // Minify, replace double quotes with single quotes, escape backlashes in regexes
 //const char HTML_INDEX[] PROGMEM = "";
 const char HTML_INDEX[] PROGMEM = "<!DOCTYPE html><meta name='viewport' content='width=device-width, initial-scale=1.0'>

<button id='off-button' type='button' class='flex'><svg class='lnr'><use xlink:href='#lnr-moon'></use></svg></button><button id='schedule-button' type='button' class='flex'><svg class='lnr'><use xlink:href='#lnr-calendar-full'></use></svg></button><button id='settings-button' type='button' class='flex'><svg class='lnr'><use xlink:href='#lnr-cog'></use></svg></button><button id='on-button' type='button' class='flex'><svg class='lnr'><use xlink:href='#lnr-sun'></use></svg></button>

	Red	Green	Blue

CIE 1976 UCS

u'	<input id='red-u' type='number' step='0.0001' value=''>	<input id='green-u' type='number' step='0.0001' value=''>	<input id='blue-u' type='number' step='0.0001' value=''>

v'	<input id='red-v' type='number' step='0.0001' value=''>	<input id='green-v' type='number' step='0.0001' value=''>	<input id='blue-v' type='number' step='0.0001' value=''>

Luminous flux

lm	<input id='red-lum' type='number' step='0.0001' value=''>	<input id='green-lum' type='number' step='0.0001' value=''>	<input id='blue-lum' type='number' step='0.0001' value=''>

Level by distance

p1	<input id='red-p1' type='number' step='0.0001' value=''>	<input id='green-p1' type='number' step='0.0001' value=''>	<input id='blue-p1' type='number' step='0.0001' value=''>

p2	<input id='red-p2' type='number' step='0.0001' value=''>	<input id='green-p2' type='number' step='0.0001' value=''>	<input id='blue-p2' type='number' step='0.0001' value=''>

q1	<input id='red-q1' type='number' step='0.0001' value=''>	<input id='green-q1' type='number' step='0.0001' value=''>	<input id='blue-q1' type='number' step='0.0001' value=''>

<button id='settings-close-button' type='button'><svg class='lnr'><use xlink:href='#lnr-cross'></use></svg></button><button id='settings-save-button' type='button'><svg class='lnr'><use xlink:href='#lnr-checkmark-circle'></use></svg></button>

";
-
-// HTTP server
-ESP8266WebServer server(80);
-
-// Data structure for CIE 1976 UCS color coordinates
-struct Luv {
-  float L;
-  float u;
-  float v;
-};
-
-// Data structure for RGB color
-struct RGB {
-  float R;
-  float G;
-  float B;
-};
-
-// Is the light on?
-bool onOff_ = false;
-
-// Raw PWM values
-RGB raw_;
-
-// CIE 1976 UCS color coordinates
-Luv luv_;
-
-// Correlated color temperature (in Kelvins)
-int T_;
-
-
-// Calibration coefficients with default values
-
-// Luminous fluxes
-float redLum_ = 0.5;
-float greenLum_ = 1.0;
-float blueLum_ = 0.75;
-float maxLum_ = redLum_ + greenLum_ + blueLum_;
-
-// LED u', v' coordinates
-Luv redUv_ = {100, 0.5535, 0.5170};
-Luv greenUv_ = {100, 0.0373, 0.5856};
-Luv blueUv_ = {100, 0.1679, 0.1153};
-
-// Mixing fit coefficients
-float redToGreenFit_[] = {2.9658, 0.0, 1.9658};
-float greenToBlueFit_[] = {1.3587, 0.0, 0.3587};
-float blueToRedFit_[] = {-0.2121, 0.2121, 0.2121};
-
-
-
-/**
- * Utility functions
- */
-
-/**
- * Find coefficient for a LED
- */
-float findCoefficient (const Luv PT, const Luv P0, const Luv P1, const Luv P2, const float rightHandFit[3], const float leftHandFit[3]) {
-
-  double PTu = PT.u;
-  double PTv = PT.v;
-  double P0u = P0.u;
-  double P0v = P0.v;
-  double P1u = P1.u;
-  double P1v = P1.v;
-  double P2u = P2.u;
-  double P2v = P2.v;
-  double Rp1 = rightHandFit[0];
-  double Rp2 = rightHandFit[1];
-  double Rq1 = rightHandFit[2];
-  double Lp1 = leftHandFit[0];
-  double Lp2 = leftHandFit[1];
-  double Lq1 = leftHandFit[2];
-
-  double dR;
-  if (Lp1 < 0) {
-    dR = (sqrt((P0u*P0u)*(P1v*P1v)+(P1u*P1u)*(P0v*P0v)+(P0u*P0u)*(PTv*PTv)+(P0v*P0v)*(PTu*PTu)+(P1u*P1u)*(PTv*PTv)+(P1v*P1v)*(PTu*PTu)-Lp1*(P0u*P0u)*(P1v*P1v)*2.0-Lp1*(P1u*P1u)*(P0v*P0v)*2.0-Lp2*(P0u*P0u)*(P1v*P1v)*2.0-Lp2*(P1u*P1u)*(P0v*P0v)*2.0+Lq1*(P0u*P0u)*(P1v*P1v)*2.0+Lq1*(P1u*P1u)*(P0v*P0v)*2.0-Lp1*(P0u*P0u)*(PTv*PTv)*2.0-Lp1*(P0v*P0v)*(PTu*PTu)*2.0-Lp2*(P0u*P0u)*(PTv*PTv)*4.0-Lp2*(P0v*P0v)*(PTu*PTu)*4.0+Lq1*(P0u*P0u)*(PTv*PTv)*2.0+Lq1*(P0v*P0v)*(PTu*PTu)*2.0+Lq1*(P1u*P1u)*(PTv*PTv)*2.0+Lq1*(P1v*P1v)*(PTu*PTu)*2.0+(Lp1*Lp1)*(P0u*P0u)*(P1v*P1v)+(Lp1*Lp1)*(P1u*P1u)*(P0v*P0v)+(Lp2*Lp2)*(P0u*P0u)*(P1v*P1v)+(Lp2*Lp2)*(P1u*P1u)*(P0v*P0v)+(Lp1*Lp1)*(P1u*P1u)*(P2v*P2v)+(Lp1*Lp1)*(P2u*P2u)*(P1v*P1v)+(Lp2*Lp2)*(P0u*P0u)*(P2v*P2v)+(Lp2*Lp2)*(P2u*P2u)*(P0v*P0v)+(Lp2*Lp2)*(P1u*P1u)*(P2v*P2v)+(Lp2*Lp2)*(P2u*P2u)*(P1v*P1v)+(Lq1*Lq1)*(P0u*P0u)*(P1v*P1v)+(Lq1*Lq1)*(P1u*P1u)*(P0v*P0v)+(Lp1*Lp1)*(P0u*P0u)*(PTv*PTv)+(Lp1*Lp1)*(P0v*P0v)*(PTu*PTu)+(Lp1*Lp1)*(P2u*P2u)*(PTv*PTv)+(Lp1*Lp1)*(P2v*P2v)*(PTu*PTu)+(Lq1*Lq1)*(P0u*P0u)*(PTv*PTv)+(Lq1*Lq1)*(P0v*P0v)*(PTu*PTu)+(Lq1*Lq1)*(P1u*P1u)*(PTv*PTv)+(Lq1*Lq1)*(P1v*P1v)*(PTu*PTu)-P0u*P1u*(PTv*PTv)*2.0-P0u*(P1v*P1v)*PTu*2.0-P1u*(P0v*P0v)*PTu*2.0-P0v*P1v*(PTu*PTu)*2.0-(P0u*P0u)*P1v*PTv*2.0-(P1u*P1u)*P0v*PTv*2.0+Lp1*Lp2*(P0u*P0u)*(P1v*P1v)*2.0+Lp1*Lp2*(P1u*P1u)*(P0v*P0v)*2.0+Lp1*Lp2*(P1u*P1u)*(P2v*P2v)*2.0+Lp1*Lp2*(P2u*P2u)*(P1v*P1v)*2.0-Lp1*Lq1*(P0u*P0u)*(P1v*P1v)*2.0-Lp1*Lq1*(P1u*P1u)*(P0v*P0v)*2.0-Lp2*Lq1*(P0u*P0u)*(P1v*P1v)*2.0-Lp2*Lq1*(P1u*P1u)*(P0v*P0v)*2.0+Lp1*Lq1*(P0u*P0u)*(PTv*PTv)*2.0+Lp1*Lq1*(P0v*P0v)*(PTu*PTu)*2.0-(Lp1*Lp1)*P0u*P2u*(P1v*P1v)*2.0-(Lp2*Lp2)*P0u*P1u*(P2v*P2v)*2.0-(Lp2*Lp2)*P0u*P2u*(P1v*P1v)*2.0-(Lp2*Lp2)*P1u*P2u*(P0v*P0v)*2.0-(Lp1*Lp1)*(P1u*P1u)*P0v*P2v*2.0-(Lp2*Lp2)*(P0u*P0u)*P1v*P2v*2.0-(Lp2*Lp2)*(P1u*P1u)*P0v*P2v*2.0-(Lp2*Lp2)*(P2u*P2u)*P0v*P1v*2.0-(Lp1*Lp1)*P1u*(P0v*P0v)*PTu*2.0-(Lp1*Lp1)*P0u*P2u*(PTv*PTv)*2.0-(Lp1*Lp1)*P1u*(P2v*P2v)*PTu*2.0-(Lp1*Lp1)*(P0u*P0u)*P1v*PTv*2.0-(Lp1*Lp1)*P0v*P2v*(PTu*PTu)*2.0-(Lp1*Lp1)*(P2u*P2u)*P1v*PTv*2.0-(Lq1*Lq1)*P0u*P1u*(PTv*PTv)*2.0-(Lq1*Lq1)*P0u*(P1v*P1v)*PTu*2.0-(Lq1*Lq1)*P1u*(P0v*P0v)*PTu*2.0-(Lq1*Lq1)*P0v*P1v*(PTu*PTu)*2.0-(Lq1*Lq1)*(P0u*P0u)*P1v*PTv*2.0-(Lq1*Lq1)*(P1u*P1u)*P0v*PTv*2.0-P0u*P1u*P0v*P1v*2.0+P0u*P1u*P0v*PTv*2.0+P0u*P0v*P1v*PTu*2.0+P0u*P1u*P1v*PTv*2.0+P1u*P0v*P1v*PTu*2.0-P0u*P0v*PTu*PTv*2.0+P0u*P1v*PTu*PTv*2.0+P1u*P0v*PTu*PTv*2.0-P1u*P1v*PTu*PTv*2.0+Lp1*P0u*P2u*(P1v*P1v)*2.0+Lp2*P0u*P2u*(P1v*P1v)*2.0-Lp2*P1u*P2u*(P0v*P0v)*2.0+Lp1*(P1u*P1u)*P0v*P2v*2.0-Lp2*(P0u*P0u)*P1v*P2v*2.0+Lp2*(P1u*P1u)*P0v*P2v*2.0+Lp1*P0u*P1u*(PTv*PTv)*2.0+Lp1*P0u*(P1v*P1v)*PTu*2.0+Lp1*P1u*(P0v*P0v)*PTu*4.0+Lp1*P0u*P2u*(PTv*PTv)*2.0+Lp2*P0u*P1u*(PTv*PTv)*4.0+Lp2*P0u*(P1v*P1v)*PTu*2.0+Lp2*P1u*(P0v*P0v)*PTu*6.0-Lp1*P1u*P2u*(PTv*PTv)*2.0-Lp1*P2u*(P1v*P1v)*PTu*2.0+Lp2*P0u*P2u*(PTv*PTv)*4.0+Lp2*P2u*(P0v*P0v)*PTu*2.0-Lp2*P1u*P2u*(PTv*PTv)*4.0-Lp2*P2u*(P1v*P1v)*PTu*2.0+Lp1*P0v*P1v*(PTu*PTu)*2.0+Lp1*(P0u*P0u)*P1v*PTv*4.0+Lp1*(P1u*P1u)*P0v*PTv*2.0+Lp1*P0v*P2v*(PTu*PTu)*2.0+Lp2*P0v*P1v*(PTu*PTu)*4.0+Lp2*(P0u*P0u)*P1v*PTv*6.0+Lp2*(P1u*P1u)*P0v*PTv*2.0-Lp1*P1v*P2v*(PTu*PTu)*2.0-Lp1*(P1u*P1u)*P2v*PTv*2.0+Lp2*P0v*P2v*(PTu*PTu)*4.0+Lp2*(P0u*P0u)*P2v*PTv*2.0-Lp2*P1v*P2v*(PTu*PTu)*4.0-Lp2*(P1u*P1u)*P2v*PTv*2.0-Lq1*P0u*P1u*(PTv*PTv)*4.0-Lq1*P0u*(P1v*P1v)*PTu*4.0-Lq1*P1u*(P0v*P0v)*PTu*4.0-Lq1*P0v*P1v*(PTu*PTu)*4.0-Lq1*(P0u*P0u)*P1v*PTv*4.0-Lq1*(P1u*P1u)*P0v*PTv*4.0-Lp1*Lp2*P0u*P1u*(P2v*P2v)*2.0-Lp1*Lp2*P0u*P2u*(P1v*P1v)*4.0-Lp1*Lp2*P1u*P2u*(P0v*P0v)*2.0-Lp1*Lp2*(P0u*P0u)*P1v*P2v*2.0-Lp1*Lp2*(P1u*P1u)*P0v*P2v*4.0-Lp1*Lp2*(P2u*P2u)*P0v*P1v*2.0+Lp1*Lq1*P0u*P2u*(P1v*P1v)*2.0+Lp1*Lq1*P1u*P2u*(P0v*P0v)*4.0+Lp2*Lq1*P0u*P2u*(P1v*P1v)*2.0+Lp2*Lq1*P1u*P2u*(P0v*P0v)*2.0+Lp1*Lq1*(P0u*P0u)*P1v*P2v*4.0+Lp1*Lq1*(P1u*P1u)*P0v*P2v*2.0+Lp2*Lq1*(P0u*P0u)*P1v*P2v*2.0+Lp2*Lq1*(P1u*P1u)*P0v*P2v*2.0-Lp1*Lp2*P1u*(P0v*P0v)*PTu*2.0+Lp1*Lp2*P0u*(P2v*P2v)*PTu*2.0+Lp1*Lp2*P2u*(P0v*P0v)*PTu*2.0-Lp1*Lp2*P1u*(P2v*P2v)*PTu*2.0-Lp1*Lp2*(P0u*P0u)*P1v*PTv*2.0+Lp1*Lp2*(P0u*P0u)*P2v*PTv*2.0+Lp1*Lp2*(P2u*P2u)*P0v*PTv*2.0-Lp1*Lp2*(P2u*P2u)*P1v*PTv*2.0-Lp1*Lq1*P0u*P1u*(PTv*PTv)*2.0+Lp1*Lq1*P0u*(P1v*P1v)*PTu*2.0-Lp1*Lq1*P0u*P2u*(PTv*PTv)*2.0-Lp1*Lq1*P2u*(P0v*P0v)*PTu*4.0+Lp2*Lq1*P0u*(P1v*P1v)*PTu*2.0+Lp2*Lq1*P1u*(P0v*P0v)*PTu*2.0+Lp1*Lq1*P1u*P2u*(PTv*PTv)*2.0-Lp1*Lq1*P2u*(P1v*P1v)*PTu*2.0-Lp2*Lq1*P2u*(P0v*P0v)*PTu*2.0-Lp2*Lq1*P2u*(P1v*P1v)*PTu*2.0-Lp1*Lq1*P0v*P1v*(PTu*PTu)*2.0+Lp1*Lq1*(P1u*P1u)*P0v*PTv*2.0-Lp1*Lq1*P0v*P2v*(PTu*PTu)*2.0-Lp1*Lq1*(P0u*P0u)*P2v*PTv*4.0+Lp2*Lq1*(P0u*P0u)*P1v*PTv*2.0+Lp2*Lq1*(P1u*P1u)*P0v*PTv*2.0+Lp1*Lq1*P1v*P2v*(PTu*PTu)*2.0-Lp1*Lq1*(P1u*P1u)*P2v*PTv*2.0-Lp2*Lq1*(P0u*P0u)*P2v*PTv*2.0-Lp2*Lq1*(P1u*P1u)*P2v*PTv*2.0-(Lp1*Lp1)*P0u*P1u*P0v*P1v*2.0-(Lp2*Lp2)*P0u*P1u*P0v*P1v*2.0+(Lp1*Lp1)*P0u*P1u*P1v*P2v*2.0+(Lp1*Lp1)*P1u*P2u*P0v*P1v*2.0+(Lp2*Lp2)*P0u*P1u*P0v*P2v*2.0+(Lp2*Lp2)*P0u*P2u*P0v*P1v*2.0+(Lp2*Lp2)*P0u*P1u*P1v*P2v*2.0-(Lp2*Lp2)*P0u*P2u*P0v*P2v*2.0+(Lp2*Lp2)*P1u*P2u*P0v*P1v*2.0-(Lp1*Lp1)*P1u*P2u*P1v*P2v*2.0+(Lp2*Lp2)*P0u*P2u*P1v*P2v*2.0+(Lp2*Lp2)*P1u*P2u*P0v*P2v*2.0-(Lp2*Lp2)*P1u*P2u*P1v*P2v*2.0-(Lq1*Lq1)*P0u*P1u*P0v*P1v*2.0+(Lp1*Lp1)*P0u*P1u*P0v*PTv*2.0+(Lp1*Lp1)*P0u*P0v*P1v*PTu*2.0-(Lp1*Lp1)*P0u*P1u*P2v*PTv*2.0+(Lp1*Lp1)*P0u*P2u*P1v*PTv*4.0-(Lp1*Lp1)*P0u*P1v*P2v*PTu*2.0-(Lp1*Lp1)*P1u*P2u*P0v*PTv*2.0+(Lp1*Lp1)*P1u*P0v*P2v*PTu*4.0-(Lp1*Lp1)*P2u*P0v*P1v*PTu*2.0+(Lp1*Lp1)*P1u*P2u*P2v*PTv*2.0+(Lp1*Lp1)*P2u*P1v*P2v*PTu*2.0+(Lq1*Lq1)*P0u*P1u*P0v*PTv*2.0+(Lq1*Lq1)*P0u*P0v*P1v*PTu*2.0+(Lq1*Lq1)*P0u*P1u*P1v*PTv*2.0+(Lq1*Lq1)*P1u*P0v*P1v*PTu*2.0-(Lp1*Lp1)*P0u*P0v*PTu*PTv*2.0+(Lp1*Lp1)*P0u*P2v*PTu*PTv*2.0+(Lp1*Lp1)*P2u*P0v*PTu*PTv*2.0-(Lp1*Lp1)*P2u*P2v*PTu*PTv*2.0-(Lq1*Lq1)*P0u*P0v*PTu*PTv*2.0+(Lq1*Lq1)*P0u*P1v*PTu*PTv*2.0+(Lq1*Lq1)*P1u*P0v*PTu*PTv*2.0-(Lq1*Lq1)*P1u*P1v*PTu*PTv*2.0+Lp1*P0u*P1u*P0v*P1v*4.0+Lp2*P0u*P1u*P0v*P1v*4.0-Lp1*P0u*P1u*P1v*P2v*2.0-Lp1*P1u*P2u*P0v*P1v*2.0+Lp2*P0u*P1u*P0v*P2v*2.0+Lp2*P0u*P2u*P0v*P1v*2.0-Lp2*P0u*P1u*P1v*P2v*2.0-Lp2*P1u*P2u*P0v*P1v*2.0-Lq1*P0u*P1u*P0v*P1v*4.0-Lp1*P0u*P1u*P0v*PTv*4.0-Lp1*P0u*P0v*P1v*PTu*4.0-Lp1*P0u*P1u*P1v*PTv*2.0-Lp1*P1u*P0v*P1v*PTu*2.0-Lp2*P0u*P1u*P0v*PTv*6.0-Lp2*P0u*P0v*P1v*PTu*6.0+Lp1*P0u*P1u*P2v*PTv*2.0-Lp1*P0u*P2u*P1v*PTv*4.0+Lp1*P0u*P1v*P2v*PTu*2.0+Lp1*P1u*P2u*P0v*PTv*2.0-Lp1*P1u*P0v*P2v*PTu*4.0+Lp1*P2u*P0v*P1v*PTu*2.0-Lp2*P0u*P1u*P1v*PTv*2.0-Lp2*P0u*P2u*P0v*PTv*2.0-Lp2*P0u*P0v*P2v*PTu*2.0-Lp2*P1u*P0v*P1v*PTu*2.0+Lp1*P1u*P2u*P1v*PTv*2.0+Lp1*P1u*P1v*P2v*PTu*2.0-Lp2*P0u*P2u*P1v*PTv*6.0+Lp2*P0u*P1v*P2v*PTu*6.0+Lp2*P1u*P2u*P0v*PTv*6.0-Lp2*P1u*P0v*P2v*PTu*6.0+Lp2*P1u*P2u*P1v*PTv*2.0+Lp2*P1u*P1v*P2v*PTu*2.0+Lq1*P0u*P1u*P0v*PTv*4.0+Lq1*P0u*P0v*P1v*PTu*4.0+Lq1*P0u*P1u*P1v*PTv*4.0+Lq1*P1u*P0v*P1v*PTu*4.0+Lp1*P0u*P0v*PTu*PTv*4.0-Lp1*P0u*P1v*PTu*PTv*2.0-Lp1*P1u*P0v*PTu*PTv*2.0+Lp2*P0u*P0v*PTu*PTv*8.0-Lp1*P0u*P2v*PTu*PTv*2.0-Lp1*P2u*P0v*PTu*PTv*2.0-Lp2*P0u*P1v*PTu*PTv*4.0-Lp2*P1u*P0v*PTu*PTv*4.0+Lp1*P1u*P2v*PTu*PTv*2.0+Lp1*P2u*P1v*PTu*PTv*2.0-Lp2*P0u*P2v*PTu*PTv*4.0-Lp2*P2u*P0v*PTu*PTv*4.0+Lp2*P1u*P2v*PTu*PTv*4.0+Lp2*P2u*P1v*PTu*PTv*4.0-Lq1*P0u*P0v*PTu*PTv*4.0+Lq1*P0u*P1v*PTu*PTv*4.0+Lq1*P1u*P0v*PTu*PTv*4.0-Lq1*P1u*P1v*PTu*PTv*4.0-Lp1*Lp2*P0u*P1u*P0v*P1v*4.0+Lp1*Lp2*P0u*P1u*P0v*P2v*2.0+Lp1*Lp2*P0u*P2u*P0v*P1v*2.0+Lp1*Lp2*P0u*P1u*P1v*P2v*4.0+Lp1*Lp2*P1u*P2u*P0v*P1v*4.0+Lp1*Lp2*P0u*P2u*P1v*P2v*2.0+Lp1*Lp2*P1u*P2u*P0v*P2v*2.0-Lp1*Lp2*P1u*P2u*P1v*P2v*4.0+Lp1*Lq1*P0u*P1u*P0v*P1v*4.0-Lp1*Lq1*P0u*P1u*P0v*P2v*4.0-Lp1*Lq1*P0u*P2u*P0v*P1v*4.0+Lp2*Lq1*P0u*P1u*P0v*P1v*4.0-Lp1*Lq1*P0u*P1u*P1v*P2v*2.0-Lp1*Lq1*P1u*P2u*P0v*P1v*2.0-Lp2*Lq1*P0u*P1u*P0v*P2v*2.0-Lp2*Lq1*P0u*P2u*P0v*P1v*2.0-Lp2*Lq1*P0u*P1u*P1v*P2v*2.0-Lp2*Lq1*P1u*P2u*P0v*P1v*2.0+Lp1*Lp2*P0u*P1u*P0v*PTv*2.0+Lp1*Lp2*P0u*P0v*P1v*PTu*2.0-Lp1*Lp2*P0u*P2u*P0v*PTv*2.0-Lp1*Lp2*P0u*P0v*P2v*PTu*2.0-Lp1*Lp2*P0u*P1u*P2v*PTv*2.0+Lp1*Lp2*P0u*P2u*P1v*PTv*4.0-Lp1*Lp2*P0u*P1v*P2v*PTu*2.0-Lp1*Lp2*P1u*P2u*P0v*PTv*2.0+Lp1*Lp2*P1u*P0v*P2v*PTu*4.0-Lp1*Lp2*P2u*P0v*P1v*PTu*2.0-Lp1*Lp2*P0u*P2u*P2v*PTv*2.0-Lp1*Lp2*P2u*P0v*P2v*PTu*2.0+Lp1*Lp2*P1u*P2u*P2v*PTv*2.0+Lp1*Lp2*P2u*P1v*P2v*PTu*2.0-Lp1*Lq1*P0u*P1u*P1v*PTv*2.0+Lp1*Lq1*P0u*P2u*P0v*PTv*4.0+Lp1*Lq1*P0u*P0v*P2v*PTu*4.0-Lp1*Lq1*P1u*P0v*P1v*PTu*2.0-Lp2*Lq1*P0u*P1u*P0v*PTv*2.0-Lp2*Lq1*P0u*P0v*P1v*PTu*2.0+Lp1*Lq1*P0u*P1u*P2v*PTv*6.0-Lp1*Lq1*P0u*P1v*P2v*PTu*6.0-Lp1*Lq1*P1u*P2u*P0v*PTv*6.0+Lp1*Lq1*P2u*P0v*P1v*PTu*6.0-Lp2*Lq1*P0u*P1u*P1v*PTv*2.0+Lp2*Lq1*P0u*P2u*P0v*PTv*2.0+Lp2*Lq1*P0u*P0v*P2v*PTu*2.0-Lp2*Lq1*P1u*P0v*P1v*PTu*2.0+Lp1*Lq1*P1u*P2u*P1v*PTv*2.0+Lp1*Lq1*P1u*P1v*P2v*PTu*2.0+Lp2*Lq1*P0u*P1u*P2v*PTv*4.0-Lp2*Lq1*P0u*P2u*P1v*PTv*2.0-Lp2*Lq1*P0u*P1v*P2v*PTu*2.0-Lp2*Lq1*P1u*P2u*P0v*PTv*2.0-Lp2*Lq1*P1u*P0v*P2v*PTu*2.0+Lp2*Lq1*P2u*P0v*P1v*PTu*4.0+Lp2*Lq1*P1u*P2u*P1v*PTv*2.0+Lp2*Lq1*P1u*P1v*P2v*PTu*2.0-Lp1*Lq1*P0u*P0v*PTu*PTv*4.0+Lp1*Lq1*P0u*P1v*PTu*PTv*2.0+Lp1*Lq1*P1u*P0v*PTu*PTv*2.0+Lp1*Lq1*P0u*P2v*PTu*PTv*2.0+Lp1*Lq1*P2u*P0v*PTu*PTv*2.0-Lp1*Lq1*P1u*P2v*PTu*PTv*2.0-Lp1*Lq1*P2u*P1v*PTu*PTv*2.0)*(1.0/2.0)+P0u*P1v*(1.0/2.0)-P1u*P0v*(1.0/2.0)-P0u*PTv*(1.0/2.0)+P0v*PTu*(1.0/2.0)+P1u*PTv*(1.0/2.0)-P1v*PTu*(1.0/2.0)-Lp1*P0u*P1v*(1.0/2.0)+Lp1*P1u*P0v*(1.0/2.0)+Lp1*P0u*P2v-Lp1*P2u*P0v-Lp2*P0u*P1v*(1.0/2.0)+Lp2*P1u*P0v*(1.0/2.0)-Lp1*P1u*P2v*(1.0/2.0)+Lp1*P2u*P1v*(1.0/2.0)+Lp2*P0u*P2v*(1.0/2.0)-Lp2*P2u*P0v*(1.0/2.0)-Lp2*P1u*P2v*(1.0/2.0)+Lp2*P2u*P1v*(1.0/2.0)+Lq1*P0u*P1v*(1.0/2.0)-Lq1*P1u*P0v*(1.0/2.0)-Lp1*P0u*PTv*(1.0/2.0)+Lp1*P0v*PTu*(1.0/2.0)+Lp1*P2u*PTv*(1.0/2.0)-Lp1*P2v*PTu*(1.0/2.0)-Lq1*P0u*PTv*(1.0/2.0)+Lq1*P0v*PTu*(1.0/2.0)+Lq1*P1u*PTv*(1.0/2.0)-Lq1*P1v*PTu*(1.0/2.0))/(P0u*P1v-P1u*P0v-P0u*PTv+P0v*PTu+P1u*PTv-P1v*PTu-Lp1*P0u*P1v+Lp1*P1u*P0v+Lp1*P0u*P2v-Lp1*P2u*P0v-Lp1*P1u*P2v+Lp1*P2u*P1v);
-  } else {
-    dR = 1.0 - (sqrt(Lp1*Lp1*P0u*P0u*P2v*P2v - 2*Lp1*Lp1*P0u*P0u*P2v*PTv + Lp1*Lp1*P0u*P0u*PTv*PTv - 2*Lp1*Lp1*P0u*P2u*P0v*P2v + 2*Lp1*Lp1*P0u*P2u*P0v*PTv + 2*Lp1*Lp1*P0u*P2u*P2v*PTv - 2*Lp1*Lp1*P0u*P2u*PTv*PTv + 2*Lp1*Lp1*P0u*P0v*P2v*PTu - 2*Lp1*Lp1*P0u*P0v*PTu*PTv - 2*Lp1*Lp1*P0u*P2v*P2v*PTu + 2*Lp1*Lp1*P0u*P2v*PTu*PTv + Lp1*Lp1*P2u*P2u*P0v*P0v - 2*Lp1*Lp1*P2u*P2u*P0v*PTv + Lp1*Lp1*P2u*P2u*PTv*PTv - 2*Lp1*Lp1*P2u*P0v*P0v*PTu + 2*Lp1*Lp1*P2u*P0v*P2v*PTu + 2*Lp1*Lp1*P2u*P0v*PTu*PTv - 2*Lp1*Lp1*P2u*P2v*PTu*PTv + Lp1*Lp1*P0v*P0v*PTu*PTu - 2*Lp1*Lp1*P0v*P2v*PTu*PTu + Lp1*Lp1*P2v*P2v*PTu*PTu - 2*Lp1*Lp2*P0u*P0u*P1v*P2v + 2*Lp1*Lp2*P0u*P0u*P1v*PTv + 2*Lp1*Lp2*P0u*P0u*P2v*P2v - 2*Lp1*Lp2*P0u*P0u*P2v*PTv + 2*Lp1*Lp2*P0u*P1u*P0v*P2v - 2*Lp1*Lp2*P0u*P1u*P0v*PTv - 2*Lp1*Lp2*P0u*P1u*P2v*P2v + 2*Lp1*Lp2*P0u*P1u*P2v*PTv + 2*Lp1*Lp2*P0u*P2u*P0v*P1v - 4*Lp1*Lp2*P0u*P2u*P0v*P2v + 2*Lp1*Lp2*P0u*P2u*P0v*PTv + 2*Lp1*Lp2*P0u*P2u*P1v*P2v - 4*Lp1*Lp2*P0u*P2u*P1v*PTv + 2*Lp1*Lp2*P0u*P2u*P2v*PTv - 2*Lp1*Lp2*P0u*P0v*P1v*PTu + 2*Lp1*Lp2*P0u*P0v*P2v*PTu + 2*Lp1*Lp2*P0u*P1v*P2v*PTu - 2*Lp1*Lp2*P0u*P2v*P2v*PTu - 2*Lp1*Lp2*P1u*P2u*P0v*P0v + 2*Lp1*Lp2*P1u*P2u*P0v*P2v + 2*Lp1*Lp2*P1u*P2u*P0v*PTv - 2*Lp1*Lp2*P1u*P2u*P2v*PTv + 2*Lp1*Lp2*P1u*P0v*P0v*PTu - 4*Lp1*Lp2*P1u*P0v*P2v*PTu + 2*Lp1*Lp2*P1u*P2v*P2v*PTu + 2*Lp1*Lp2*P2u*P2u*P0v*P0v - 2*Lp1*Lp2*P2u*P2u*P0v*P1v - 2*Lp1*Lp2*P2u*P2u*P0v*PTv + 2*Lp1*Lp2*P2u*P2u*P1v*PTv - 2*Lp1*Lp2*P2u*P0v*P0v*PTu + 2*Lp1*Lp2*P2u*P0v*P1v*PTu + 2*Lp1*Lp2*P2u*P0v*P2v*PTu - 2*Lp1*Lp2*P2u*P1v*P2v*PTu - 2*Lp1*Lq1*P0u*P0u*P1v*P2v + 2*Lp1*Lq1*P0u*P0u*P1v*PTv + 2*Lp1*Lq1*P0u*P0u*P2v*PTv - 2*Lp1*Lq1*P0u*P0u*PTv*PTv + 2*Lp1*Lq1*P0u*P1u*P0v*P2v - 2*Lp1*Lq1*P0u*P1u*P0v*PTv - 2*Lp1*Lq1*P0u*P1u*P2v*PTv + 2*Lp1*Lq1*P0u*P1u*PTv*PTv + 2*Lp1*Lq1*P0u*P2u*P0v*P1v - 2*Lp1*Lq1*P0u*P2u*P0v*PTv - 2*Lp1*Lq1*P0u*P2u*P1v*PTv + 2*Lp1*Lq1*P0u*P2u*PTv*PTv - 2*Lp1*Lq1*P0u*P0v*P1v*PTu - 2*Lp1*Lq1*P0u*P0v*P2v*PTu + 4*Lp1*Lq1*P0u*P0v*PTu*PTv + 4*Lp1*Lq1*P0u*P1v*P2v*PTu - 2*Lp1*Lq1*P0u*P1v*PTu*PTv - 2*Lp1*Lq1*P0u*P2v*PTu*PTv - 2*Lp1*Lq1*P1u*P2u*P0v*P0v + 4*Lp1*Lq1*P1u*P2u*P0v*PTv - 2*Lp1*Lq1*P1u*P2u*PTv*PTv + 2*Lp1*Lq1*P1u*P0v*P0v*PTu - 2*Lp1*Lq1*P1u*P0v*P2v*PTu - 2*Lp1*Lq1*P1u*P0v*PTu*PTv + 2*Lp1*Lq1*P1u*P2v*PTu*PTv + 2*Lp1*Lq1*P2u*P0v*P0v*PTu - 2*Lp1*Lq1*P2u*P0v*P1v*PTu - 2*Lp1*Lq1*P2u*P0v*PTu*PTv + 2*Lp1*Lq1*P2u*P1v*PTu*PTv - 2*Lp1*Lq1*P0v*P0v*PTu*PTu + 2*Lp1*Lq1*P0v*P1v*PTu*PTu + 2*Lp1*Lq1*P0v*P2v*PTu*PTu - 2*Lp1*Lq1*P1v*P2v*PTu*PTu + Lp2*Lp2*P0u*P0u*P1v*P1v - 2*Lp2*Lp2*P0u*P0u*P1v*P2v + Lp2*Lp2*P0u*P0u*P2v*P2v - 2*Lp2*Lp2*P0u*P1u*P0v*P1v + 2*Lp2*Lp2*P0u*P1u*P0v*P2v + 2*Lp2*Lp2*P0u*P1u*P1v*P2v - 2*Lp2*Lp2*P0u*P1u*P2v*P2v + 2*Lp2*Lp2*P0u*P2u*P0v*P1v - 2*Lp2*Lp2*P0u*P2u*P0v*P2v - 2*Lp2*Lp2*P0u*P2u*P1v*P1v + 2*Lp2*Lp2*P0u*P2u*P1v*P2v + Lp2*Lp2*P1u*P1u*P0v*P0v - 2*Lp2*Lp2*P1u*P1u*P0v*P2v + Lp2*Lp2*P1u*P1u*P2v*P2v - 2*Lp2*Lp2*P1u*P2u*P0v*P0v + 2*Lp2*Lp2*P1u*P2u*P0v*P1v + 2*Lp2*Lp2*P1u*P2u*P0v*P2v - 2*Lp2*Lp2*P1u*P2u*P1v*P2v + Lp2*Lp2*P2u*P2u*P0v*P0v - 2*Lp2*Lp2*P2u*P2u*P0v*P1v + Lp2*Lp2*P2u*P2u*P1v*P1v - 2*Lp2*Lq1*P0u*P0u*P1v*P1v + 2*Lp2*Lq1*P0u*P0u*P1v*P2v + 2*Lp2*Lq1*P0u*P0u*P1v*PTv - 2*Lp2*Lq1*P0u*P0u*P2v*PTv + 4*Lp2*Lq1*P0u*P1u*P0v*P1v - 2*Lp2*Lq1*P0u*P1u*P0v*P2v - 2*Lp2*Lq1*P0u*P1u*P0v*PTv - 2*Lp2*Lq1*P0u*P1u*P1v*P2v - 2*Lp2*Lq1*P0u*P1u*P1v*PTv + 4*Lp2*Lq1*P0u*P1u*P2v*PTv - 2*Lp2*Lq1*P0u*P2u*P0v*P1v + 2*Lp2*Lq1*P0u*P2u*P0v*PTv + 2*Lp2*Lq1*P0u*P2u*P1v*P1v - 2*Lp2*Lq1*P0u*P2u*P1v*PTv - 2*Lp2*Lq1*P0u*P0v*P1v*PTu + 2*Lp2*Lq1*P0u*P0v*P2v*PTu + 2*Lp2*Lq1*P0u*P1v*P1v*PTu - 2*Lp2*Lq1*P0u*P1v*P2v*PTu - 2*Lp2*Lq1*P1u*P1u*P0v*P0v + 2*Lp2*Lq1*P1u*P1u*P0v*P2v + 2*Lp2*Lq1*P1u*P1u*P0v*PTv - 2*Lp2*Lq1*P1u*P1u*P2v*PTv + 2*Lp2*Lq1*P1u*P2u*P0v*P0v - 2*Lp2*Lq1*P1u*P2u*P0v*P1v - 2*Lp2*Lq1*P1u*P2u*P0v*PTv + 2*Lp2*Lq1*P1u*P2u*P1v*PTv + 2*Lp2*Lq1*P1u*P0v*P0v*PTu - 2*Lp2*Lq1*P1u*P0v*P1v*PTu - 2*Lp2*Lq1*P1u*P0v*P2v*PTu + 2*Lp2*Lq1*P1u*P1v*P2v*PTu - 2*Lp2*Lq1*P2u*P0v*P0v*PTu + 4*Lp2*Lq1*P2u*P0v*P1v*PTu - 2*Lp2*Lq1*P2u*P1v*P1v*PTu + 4*Lp2*P0u*P0u*P1v*P2v - 4*Lp2*P0u*P0u*P1v*PTv - 4*Lp2*P0u*P0u*P2v*PTv + 4*Lp2*P0u*P0u*PTv*PTv - 4*Lp2*P0u*P1u*P0v*P2v + 4*Lp2*P0u*P1u*P0v*PTv + 4*Lp2*P0u*P1u*P2v*PTv - 4*Lp2*P0u*P1u*PTv*PTv - 4*Lp2*P0u*P2u*P0v*P1v + 4*Lp2*P0u*P2u*P0v*PTv + 4*Lp2*P0u*P2u*P1v*PTv - 4*Lp2*P0u*P2u*PTv*PTv + 4*Lp2*P0u*P0v*P1v*PTu + 4*Lp2*P0u*P0v*P2v*PTu - 8*Lp2*P0u*P0v*PTu*PTv - 8*Lp2*P0u*P1v*P2v*PTu + 4*Lp2*P0u*P1v*PTu*PTv + 4*Lp2*P0u*P2v*PTu*PTv + 4*Lp2*P1u*P2u*P0v*P0v - 8*Lp2*P1u*P2u*P0v*PTv + 4*Lp2*P1u*P2u*PTv*PTv - 4*Lp2*P1u*P0v*P0v*PTu + 4*Lp2*P1u*P0v*P2v*PTu + 4*Lp2*P1u*P0v*PTu*PTv - 4*Lp2*P1u*P2v*PTu*PTv - 4*Lp2*P2u*P0v*P0v*PTu + 4*Lp2*P2u*P0v*P1v*PTu + 4*Lp2*P2u*P0v*PTu*PTv - 4*Lp2*P2u*P1v*PTu*PTv + 4*Lp2*P0v*P0v*PTu*PTu - 4*Lp2*P0v*P1v*PTu*PTu - 4*Lp2*P0v*P2v*PTu*PTu + 4*Lp2*P1v*P2v*PTu*PTu + Lq1*Lq1*P0u*P0u*P1v*P1v - 2*Lq1*Lq1*P0u*P0u*P1v*PTv + Lq1*Lq1*P0u*P0u*PTv*PTv - 2*Lq1*Lq1*P0u*P1u*P0v*P1v + 2*Lq1*Lq1*P0u*P1u*P0v*PTv + 2*Lq1*Lq1*P0u*P1u*P1v*PTv - 2*Lq1*Lq1*P0u*P1u*PTv*PTv + 2*Lq1*Lq1*P0u*P0v*P1v*PTu - 2*Lq1*Lq1*P0u*P0v*PTu*PTv - 2*Lq1*Lq1*P0u*P1v*P1v*PTu + 2*Lq1*Lq1*P0u*P1v*PTu*PTv + Lq1*Lq1*P1u*P1u*P0v*P0v - 2*Lq1*Lq1*P1u*P1u*P0v*PTv + Lq1*Lq1*P1u*P1u*PTv*PTv - 2*Lq1*Lq1*P1u*P0v*P0v*PTu + 2*Lq1*Lq1*P1u*P0v*P1v*PTu + 2*Lq1*Lq1*P1u*P0v*PTu*PTv - 2*Lq1*Lq1*P1u*P1v*PTu*PTv + Lq1*Lq1*P0v*P0v*PTu*PTu - 2*Lq1*Lq1*P0v*P1v*PTu*PTu + Lq1*Lq1*P1v*P1v*PTu*PTu) + 2*P0u*P1v - 2*P1u*P0v - 2*P0u*PTv + 2*P0v*PTu + 2*P1u*PTv - 2*P1v*PTu - 2*Lp1*P0u*P1v + 2*Lp1*P1u*P0v + Lp1*P0u*P2v - Lp1*P2u*P0v - Lp2*P0u*P1v + Lp2*P1u*P0v - 2*Lp1*P1u*P2v + 2*Lp1*P2u*P1v + Lp2*P0u*P2v - Lp2*P2u*P0v - Lp2*P1u*P2v + Lp2*P2u*P1v + Lq1*P0u*P1v - Lq1*P1u*P0v + Lp1*P0u*PTv - Lp1*P0v*PTu - Lp1*P2u*PTv + Lp1*P2v*PTu - Lq1*P0u*PTv + Lq1*P0v*PTu + Lq1*P1u*PTv - Lq1*P1v*PTu)/(2*(P0u*P1v - P1u*P0v - P0u*PTv + P0v*PTu + P1u*PTv - P1v*PTu - Lp1*P0u*P1v + Lp1*P1u*P0v + Lp1*P0u*P2v - Lp1*P2u*P0v - Lp1*P1u*P2v + Lp1*P2u*P1v));
-  }
-
-  double level;
-  if (Rp1 < 0) {
-    level = (Rp1*dR + Rp2) / (dR + Rq1);
-  } else {
-    level = (Rp1*(1 - dR) + Rp2) / ((1 - dR) + Rq1);
-  }
-
-  return level;
-}
-
-
-
-/**
- * Internal getters and setters
- */
-
-/**
- * Getters and setters for onboard leds
- */
-bool getLed1 () { return !digitalRead(led1Pin_); }
-void setLed1 (bool val) { digitalWrite(led1Pin_, !val); }
-bool getLed2 () { return !digitalRead(led2Pin_); }
-void setLed2 (bool val) { digitalWrite(led2Pin_, !val); }
-
-/**
- * Get raw pwm values
- * Values are normalized in the range 0..1
- */
-RGB getRaw () {
-  return raw_;
-}
-
-/**
- * Getter for onOff
- */
-bool getOnOff () {
-  return onOff_;
-}
-
-/**
- * Get CIE 1976 UCS color coordinates
- */
-Luv getCie1976Ucs () {
-  return luv_;
-}
-
-/**
- * Getter for color temperature
- */
-int getColorTemperature () {
-  return T_;
-}
-
-/**
- * Set raw pwm values
- * Values must be normalized to the range 0..1
- */
-void setRaw (RGB raw) {
-
-  // Limit values in the range 0..1
-  if (raw.R < 0) raw.R = 0;
-  if (raw.R > 1) raw.R = 1;
-  if (raw.G < 0) raw.G = 0;
-  if (raw.G > 1) raw.G = 1;
-  if (raw.B < 0) raw.B = 0;
-  if (raw.B > 1) raw.B = 1;
-
-  // Convert input floats to integers in the pwm range
-  int pwmR = static_cast<int>(raw.R * pwmRange_ + 0.5);
-  int pwmG = static_cast<int>(raw.G * pwmRange_ + 0.5);
-  int pwmB = static_cast<int>(raw.B * pwmRange_ + 0.5);
-
-  // Write PWMs
-  if (getOnOff()) {
-    analogWrite(redPin_, pwmR);
-    analogWrite(greenPin_, pwmG);
-    analogWrite(bluePin_, pwmB);
-  }
-
-  // Save values
-  raw_.R = static_cast<float>(pwmR) / pwmRange_;
-  raw_.G = static_cast<float>(pwmG) / pwmRange_;
-  raw_.B = static_cast<float>(pwmB) / pwmRange_;
-
-  // Cannot be sure that current color is result of higher level color setter
-  // unset luv_ and T_, respective setters will save the values afterwards
-  luv_.L = -1.0;
-  luv_.u = -1.0;
-  luv_.v = -1.0;
-  T_ = -1;
-}
-
-/**
- * Sets light on or off
- */
-void setOnOff (bool onOff) {
-  onOff_ = onOff;
-  if (onOff) {
-    setRaw(getRaw());
-  } else {
-    analogWrite(redPin_, 0);
-    analogWrite(greenPin_, 0);
-    analogWrite(bluePin_, 0);
-  }
-}
-
-/**
- * Set CIE 1976 UCS color coordinates
- */
-void setCie1976Ucs (Luv target) {
-
-  // Convert to raw PWM values
-  RGB raw;
-
-  // Coefficients
-  raw.R = findCoefficient(target, redUv_, greenUv_, blueUv_, redToGreenFit_, greenToBlueFit_);
-  raw.G = findCoefficient(target, greenUv_, blueUv_, redUv_, greenToBlueFit_, blueToRedFit_);
-  raw.B = findCoefficient(target, blueUv_, redUv_, greenUv_, blueToRedFit_, redToGreenFit_);
-
-  // Luma produced by the current raw values
-  float Y = (raw.R * redLum_ + raw.G * greenLum_ + raw.B * blueLum_) / maxLum_;
-
-  // Luma level needed for requested lightness
-  float Y_target = pow(((target.L + 16) / 116), 3);
-
-  // Luma factor
-  float C = Y_target / Y;
-
-  // Scale raw values to produce target luma
-  raw.R = raw.R * C;
-  raw.G = raw.G * C;
-  raw.B = raw.B * C;
-
-  // Find max scaled raw value
-  float maxRaw = raw.R;
-  if (raw.G > maxRaw) maxRaw = raw.G;
-  if (raw.B > maxRaw) maxRaw = raw.B;
-
-  // Nothing can be more than at max power, limit coefficients to 1
-  if (maxRaw > 1) {
-    raw.R = raw.R * (1.0 / maxRaw);
-    raw.G = raw.G * (1.0 / maxRaw);
-    raw.B = raw.B * (1.0 / maxRaw);
-  }
-
-  // Write PWMs
-  setRaw(raw);
-
-  // Save values
-  luv_.L = target.L;
-  luv_.u = target.u;
-  luv_.v = target.v;
-}
-
-/**
- * Sets ligth by color temperature
- */
-void setColorTemperature (float L, int T) {
-
-  // These cryptic looking formulas are a result of least RMSE fit of
-  // CIE1976UCS coordinates vs color temperature
-
-  // Fit variable has been transformed to z-score in order to avoid floating point precision problems
-  double x = (T-5500.0)/2599.0;
-  double u = (-0.0001747*pow(x,3.0) + 0.1833*pow(x,2.0) + 0.872*x + 1.227) / (pow(x,2.0) + 4.813*x + 5.933);
-  double v = (0.000311*pow(x,4.0) + 0.0009124*pow(x,3.0) + 0.3856*pow(x,2.0) + 1.873*x + 2.619) / (pow(x,2.0) + 4.323*x + 5.485);
-
-  // Construct CIE 1976 UCS values from internal lightness and newly calculated u', v' coordinates
-  Luv luv = {luv_.L, u, v};
-
-  // Lightness is given and valid
-  if (L > 0) luv.L = L;
-
-  setCie1976Ucs(luv);
-
-  // Save color temperature
-  T_ = T;
-}
-
-/**
- * Save calibration parameters
- */
-void calibrate (const Luv redUv, const Luv greenUv, const Luv blueUv, const float redLum, const float greenLum,
-const float blueLum, const float redToGreenFit[3], const float greenToBlueFit[3], const float blueToRedFit[3]) {
-  // CIE 1976 UCS coordinates
-  redUv_.u = redUv.u; redUv_.v = redUv.v;
-  greenUv_.u = greenUv.u; greenUv_.v = greenUv.v;
-  blueUv_.u = blueUv.u; blueUv_.v = blueUv.v;
-
-  // Luminous fluxes
-  redLum_ = redLum;
-  greenLum_ = greenLum;
-  blueLum_ = blueLum;
-
-  // Fit functions
-  for (uint8_t i = 0; i < 3; ++i) {
-    redToGreenFit_[i] = redToGreenFit[i];
-    greenToBlueFit_[i] = greenToBlueFit[i];
-    blueToRedFit_[i] = blueToRedFit[i];
-  }
-}
-
-/**
- * HTTP API controllers
- */
-
 /**
  * HTTP API for index.html
  */
-void httpIndexController () {
-  String html = FPSTR(HTML_INDEX);
-  html.replace("'{{debug}}'", "false");
-  html.replace("'{{onOff}}'", onOff_ ? "true" : "false");
-
-  // L, u', v', T
-  char strL[10], strU[10], strV[10], strT[10];
-  Luv luv = getCie1976Ucs();
-  dtostrf(luv.L, 6, 4, strL);
-  dtostrf(luv.u, 6, 4, strU);
-  dtostrf(luv.v, 6, 4, strV);
-  dtostrf(getColorTemperature(), 6, 4, strT);
-  html.replace("'{{L}}'", strL);
-  html.replace("'{{u}}'", strU);
-  html.replace("'{{v}}'", strV);
-  html.replace("'{{T}}'", strT);
-
-  // Calibration u', v' coordinates
-  char strRedU[10], strRedV[10], strGreenU[10], strGreenV[10], strBlueU[10], strBlueV[10];
-  dtostrf(redUv_.u, 6, 4, strRedU);
-  dtostrf(redUv_.v, 6, 4, strRedV);
-  dtostrf(greenUv_.u, 6, 4, strGreenU);
-  dtostrf(greenUv_.v, 6, 4, strGreenV);
-  dtostrf(blueUv_.u, 6, 4, strBlueU);
-  dtostrf(blueUv_.v, 6, 4, strBlueV);
-  html.replace("'{{redU}}'", strRedU);
-  html.replace("'{{redV}}'", strRedV);
-  html.replace("'{{greenU}}'", strGreenU);
-  html.replace("'{{greenV}}'", strGreenV);
-  html.replace("'{{blueU}}'", strBlueU);
-  html.replace("'{{blueV}}'", strBlueV);
-
-  // Calibration luminous fluxes
-  char strRedLum[10], strGreenLum[10], strBlueLum[10];
-  dtostrf(redLum_, 6, 4, strRedLum);
-  dtostrf(greenLum_, 6, 4, strGreenLum);
-  dtostrf(blueLum_, 6, 4, strBlueLum);
-  html.replace("'{{redLum}}'", strRedLum);
-  html.replace("'{{greenLum}}'", strGreenLum);
-  html.replace("'{{blueLum}}'", strBlueLum);
-
-  // Calibration red to green fit
-  char strRedP1[10], strRedP2[10], strRedQ1[10];
-  dtostrf(redToGreenFit_[0], 6, 4, strRedP1);
-  dtostrf(redToGreenFit_[1], 6, 4, strRedP2);
-  dtostrf(redToGreenFit_[2], 6, 4, strRedQ1);
-  html.replace("'{{redP1}}'", strRedP1);
-  html.replace("'{{redP2}}'", strRedP2);
-  html.replace("'{{redQ1}}'", strRedQ1);
-
-  // Calibration green to blue fit
-  char strGreenP1[10], strGreenP2[10], strGreenQ1[10];
-  dtostrf(greenToBlueFit_[0], 6, 4, strGreenP1);
-  dtostrf(greenToBlueFit_[1], 6, 4, strGreenP2);
-  dtostrf(greenToBlueFit_[2], 6, 4, strGreenQ1);
-  html.replace("'{{greenP1}}'", strGreenP1);
-  html.replace("'{{greenP2}}'", strGreenP2);
-  html.replace("'{{greenQ1}}'", strGreenQ1);
-
-  // Calibration blue to red fit
-  char strBlueP1[10], strBlueP2[10], strBlueQ1[10];
-  dtostrf(blueToRedFit_[0], 6, 4, strBlueP1);
-  dtostrf(blueToRedFit_[1], 6, 4, strBlueP2);
-  dtostrf(blueToRedFit_[2], 6, 4, strBlueQ1);
-  html.replace("'{{blueP1}}'", strBlueP1);
-  html.replace("'{{blueP2}}'", strBlueP2);
-  html.replace("'{{blueQ1}}'", strBlueQ1);
-
-  server.send(200, "text/html", html);
-}
-
-/**
- * HTTP API for onboard led control
- */
-void httpOnboardLedsController () {
-  int led1 = -1;
-  int led2 = -1;
-
-  // Parse args
-  for (uint8_t i = 0; i < server.args(); ++i) {
-    if (server.argName(i) == "led1") {
-      led1 = server.arg(i).toInt();
-    } else if ( server.argName(i) == "led2") {
-      led2 = server.arg(i).toInt();
-    }
-  }
-
-  // LED 1
-  if (led1 == -1) led1 = getLed1();
-  else setLed1(led1);
-
-  // LED 2
-  if (led2 == -1) led2 = getLed2();
-  else setLed2(led2);
-
-  // JSON response
-  char response[40];
-  sprintf(response, "{\n  \"led1\": %d,\n  \"led2\":%d\n}", led1, led2);
-
-  server.send(200, "application/json", response);
+void httpIndexController() {
+	String html = FPSTR(HTML_INDEX);
+	html.replace("'{{debug}}'", "false");
+	html.replace("'{{onOff}}'", led.getOnOff() ? "true" : "false");
+
+	// L, u', v', T
+	char strL[10], strU[10], strV[10], strT[10];
+	Luv luv = led.getCie1976Ucs();
+	dtostrf(luv.L, 6, 4, strL);
+	dtostrf(luv.u, 6, 4, strU);
+	dtostrf(luv.v, 6, 4, strV);
+	dtostrf(led.getColorTemperature(), 6, 4, strT);
+	html.replace("'{{L}}'", strL);
+	html.replace("'{{u}}'", strU);
+	html.replace("'{{v}}'", strV);
+	html.replace("'{{T}}'", strT);
+
+	// Calibration u', v' coordinates
+	Luv redUv = led.getRedUv();
+	Luv greenUv = led.getGreenUv();
+	Luv blueUv = led.getBlueUv();
+	char strRedU[10], strRedV[10], strGreenU[10], strGreenV[10], strBlueU[10], strBlueV[10];
+	dtostrf(redUv.u, 6, 4, strRedU);
+	dtostrf(redUv.v, 6, 4, strRedV);
+	dtostrf(greenUv.u, 6, 4, strGreenU);
+	dtostrf(greenUv.v, 6, 4, strGreenV);
+	dtostrf(blueUv.u, 6, 4, strBlueU);
+	dtostrf(blueUv.v, 6, 4, strBlueV);
+	html.replace("'{{redU}}'", strRedU);
+	html.replace("'{{redV}}'", strRedV);
+	html.replace("'{{greenU}}'", strGreenU);
+	html.replace("'{{greenV}}'", strGreenV);
+	html.replace("'{{blueU}}'", strBlueU);
+	html.replace("'{{blueV}}'", strBlueV);
+
+	// Calibration luminous fluxes
+	char strRedLum[10], strGreenLum[10], strBlueLum[10];
+	dtostrf(led.getRedLum(), 6, 4, strRedLum);
+	dtostrf(led.getGreenLum(), 6, 4, strGreenLum);
+	dtostrf(led.getBlueLum(), 6, 4, strBlueLum);
+	html.replace("'{{redLum}}'", strRedLum);
+	html.replace("'{{greenLum}}'", strGreenLum);
+	html.replace("'{{blueLum}}'", strBlueLum);
+
+	// Calibration red to green fit
+	float * redToGreenFit = led.getRedToGreenFit();
+	char strRedP1[10], strRedP2[10], strRedQ1[10];
+	dtostrf(redToGreenFit[0], 6, 4, strRedP1);
+	dtostrf(redToGreenFit[1], 6, 4, strRedP2);
+	dtostrf(redToGreenFit[2], 6, 4, strRedQ1);
+	html.replace("'{{redP1}}'", strRedP1);
+	html.replace("'{{redP2}}'", strRedP2);
+	html.replace("'{{redQ1}}'", strRedQ1);
+
+	// Calibration green to blue fit
+	float * greenToBlueFit = led.getGreenToBlueFit();
+	char strGreenP1[10], strGreenP2[10], strGreenQ1[10];
+	dtostrf(greenToBlueFit[0], 6, 4, strGreenP1);
+	dtostrf(greenToBlueFit[1], 6, 4, strGreenP2);
+	dtostrf(greenToBlueFit[2], 6, 4, strGreenQ1);
+	html.replace("'{{greenP1}}'", strGreenP1);
+	html.replace("'{{greenP2}}'", strGreenP2);
+	html.replace("'{{greenQ1}}'", strGreenQ1);
+
+	// Calibration blue to red fit
+	float * blueToRedFit = led.getBlueToRedFit();
+	char strBlueP1[10], strBlueP2[10], strBlueQ1[10];
+	dtostrf(blueToRedFit[0], 6, 4, strBlueP1);
+	dtostrf(blueToRedFit[1], 6, 4, strBlueP2);
+	dtostrf(blueToRedFit[2], 6, 4, strBlueQ1);
+	html.replace("'{{blueP1}}'", strBlueP1);
+	html.replace("'{{blueP2}}'", strBlueP2);
+	html.replace("'{{blueQ1}}'", strBlueQ1);
+
+	server.send(200, "text/html", html);
 }

 /**
  * HTTP API for setting the light on
  */
-void httpOnController () {
-  setOnOff(true);
-
-  // JSON response
-  char response[40];
-  sprintf(response, "{\n  \"onOff\": %s}", "true");
-
-  // Send response
-  server.send(200, "application/json", response);
+void httpOnController() {
+	led.setOnOff(true);
+
+	// JSON response
+	char response[40];
+	sprintf(response, "{\n  \"onOff\": %s}", "true");
+
+	// Send response
+	server.send(200, "application/json", response);
 }

 /**
  * HTTP API for setting the light off
  */
- void httpOffController () {
-  setOnOff(false);
-
-  // JSON response
-  char response[40];
-  sprintf(response, "{\n  \"onOff\": %s\n}", "false");
-
-  // Send response
-  server.send(200, "application/json", response);
+void httpOffController() {
+	led.setOnOff(false);
+
+	// JSON response
+	char response[40];
+	sprintf(response, "{\n  \"onOff\": %s\n}", "false");
+
+	// Send response
+	server.send(200, "application/json", response);
 }

 /**
  * HTTP API for raw PWM values
  */
-void httpRawController () {
-  float R = -1, G = -1, B = -1;
-
-  // Parse args
-  for (uint8_t i = 0; i < server.args(); ++i) {
-    if (server.argName(i) == "R") {
-      R = server.arg(i).toFloat();
-    } else if (server.argName(i) == "G") {
-      G = server.arg(i).toFloat();
-    } else if (server.argName(i) == "B") {
-      B = server.arg(i).toFloat();
-    }
-  }
-
-  int httpStatus = 200;
-
-  // All parameters must be given for write
-  if (R >= 0 && G >= 0 && B >= 0) {
-    // All parameters given -> set new values
-    RGB raw = {R, G, B};
-    setRaw(raw);
-  }
-
-  // Read (updated) values
-  RGB raw = getRaw();
-
-  // JSON response
-  char response[60];
-  char strR[10]; dtostrf(raw.R, 6, 4, strR);
-  char strG[10]; dtostrf(raw.G, 6, 4, strG);
-  char strB[10]; dtostrf(raw.B, 6, 4, strB);
-  sprintf(response, "{\n  \"R\": %s,\n  \"G\": %s,\n  \"B\": %s\n}", strR, strG, strB);
-
-  // Send response
-  server.send(httpStatus, "application/json", response);
+void httpRawController() {
+	float R = -1, G = -1, B = -1;
+
+	// Parse args
+	for (uint8_t i = 0; i < server.args(); ++i) {
+		if (server.argName(i) == "R") {
+			R = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "G") {
+			G = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "B") {
+			B = server.arg(i).toFloat();
+		}
+	}
+
+	int httpStatus = 200;
+
+	// All parameters must be given for write
+	if (R >= 0 && G >= 0 && B >= 0) {
+		// All parameters given -> set new values
+		RGB raw = { R, G, B };
+		led.setRaw(raw);
+	}
+
+	// Read (updated) values
+	RGB raw = led.getRaw();
+
+	// JSON response
+	char response[60];
+	char strR[10]; dtostrf(raw.R, 6, 4, strR);
+	char strG[10]; dtostrf(raw.G, 6, 4, strG);
+	char strB[10]; dtostrf(raw.B, 6, 4, strB);
+	sprintf(response, "{\n  \"R\": %s,\n  \"G\": %s,\n  \"B\": %s\n}", strR, strG, strB);
+
+	// Send response
+	server.send(httpStatus, "application/json", response);
 }

 /**
  * HTTP API for CIE 1976 UCS color coordinates
  */
-void httpCie1976UcsController () {
-  float L = -1, u = -1, v = -1;
-  // Parse args
-  for (uint8_t i = 0; i < server.args(); ++i) {
-    if (server.argName(i) == "L") {
-      L = server.arg(i).toFloat();
-    } else if (server.argName(i) == "u") {
-      u = server.arg(i).toFloat();
-    } else if (server.argName(i) == "v") {
-      v = server.arg(i).toFloat();
-    }
-  }
-
-  // All or none of the parameters must be missing
-  if (L >= 0 && u >= 0 && v >= 0) {
-    // All parameters given -> set new color
-    Luv luv = {L, u, v};
-    setCie1976Ucs(luv);
-  }
-
-  // Read (updated) color
-  Luv luv = getCie1976Ucs();
-  L = luv.L;
-  u = luv.u;
-  v = luv.v;
-
-  // JSON response
-  char response[60];
-  char strL[10]; dtostrf(L, 6, 4, strL);
-  char strU[10]; dtostrf(u, 6, 4, strU);
-  char strV[10]; dtostrf(v, 6, 4, strV);
-  sprintf(response, "{\n  \"L\": %s,\n  \"u\": %s,\n  \"v\": %s\n}", strL, strU, strV);
-
-  // Send response
-  server.send(200, "application/json", response);
+void httpCie1976UcsController() {
+	float L = -1, u = -1, v = -1;
+	// Parse args
+	for (uint8_t i = 0; i < server.args(); ++i) {
+		if (server.argName(i) == "L") {
+			L = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "u") {
+			u = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "v") {
+			v = server.arg(i).toFloat();
+		}
+	}
+
+	// All or none of the parameters must be missing
+	if (L >= 0 && u >= 0 && v >= 0) {
+		// All parameters given -> set new color
+		Luv luv = { L, u, v };
+		led.setCie1976Ucs(luv);
+	}
+
+	// Read (updated) color
+	Luv luv = led.getCie1976Ucs();
+	L = luv.L;
+	u = luv.u;
+	v = luv.v;
+
+	// JSON response
+	char response[60];
+	char strL[10]; dtostrf(L, 6, 4, strL);
+	char strU[10]; dtostrf(u, 6, 4, strU);
+	char strV[10]; dtostrf(v, 6, 4, strV);
+	sprintf(response, "{\n  \"L\": %s,\n  \"u\": %s,\n  \"v\": %s\n}", strL, strU, strV);
+
+	// Send response
+	server.send(200, "application/json", response);
 }

 /**
  * HTTP API for color temperature
  */
-void httpColorTemperatureController () {
-  int T = -1; float L = -1;
-  // Parse args
-  for (uint8_t i = 0; i < server.args(); ++i) {
-    if (server.argName(i) == "T") {
-      T = server.arg(i).toInt();
-    } else if (server.argName(i) == "L") {
-      L = server.arg(i).toFloat();
-    }
-  }
-
-  // Set color temperature
-  if (T >= 1000 && L >= 0) {
-    setColorTemperature(L, T);
-  }
-
-  // Read (updated) color temperature
-  T = getColorTemperature();
-
-  // Read (updated) lightness
-  Luv luv = getCie1976Ucs();
-  L = luv.L;
-
-  // JSON response
-  char response[60];
-  char strL[10]; dtostrf(L, 6, 4, strL);
-  sprintf(response, "{\n  \"L\": %s,\n  \"T\": %d\n}", strL, T);
-
-  // Send response
-  server.send(200, "application/json", response);
+void httpColorTemperatureController() {
+	int T = -1; float L = -1;
+	// Parse args
+	for (uint8_t i = 0; i < server.args(); ++i) {
+		if (server.argName(i) == "T") {
+			T = server.arg(i).toInt();
+		}
+		else if (server.argName(i) == "L") {
+			L = server.arg(i).toFloat();
+		}
+	}
+
+	// Set color temperature
+	if (T >= 1000 && L >= 0) {
+		led.setColorTemperature(L, T);
+	}
+
+	// Read (updated) color temperature
+	T = led.getColorTemperature();
+
+	// Read (updated) lightness
+	Luv luv = led.getCie1976Ucs();
+	L = luv.L;
+
+	// JSON response
+	char response[60];
+	char strL[10]; dtostrf(L, 6, 4, strL);
+	sprintf(response, "{\n  \"L\": %s,\n  \"T\": %d\n}", strL, T);
+
+	// Send response
+	server.send(200, "application/json", response);
 }

 /**
  * HTTP API for setting calibration parameters
  */
-void httpCalibrationController () {
-  float redU = -1, redV = -1, greenU = -1, greenV = -1, blueU = -1, blueV = -1;
-  float redLum = -1, greenLum = -1, blueLum = -1;
-  float redP1 = -10000, redP2 = -10000, redQ1 = -10000;
-  float greenP1 = -10000, greenP2 = -10000, greenQ1 = -10000;
-  float blueP1 = -10000, blueP2 = -10000, blueQ1 = -10000;
-
-  // Parse args
-  for (uint8_t i = 0; i < server.args(); ++i) {
-    if (server.argName(i) == "redU") {
-      redU = server.arg(i).toFloat();
-    } else if (server.argName(i) == "redV") {
-      redV = server.arg(i).toFloat();
-    } else if (server.argName(i) == "greenU") {
-      greenU = server.arg(i).toFloat();
-    } else if (server.argName(i) == "greenV") {
-      greenV = server.arg(i).toFloat();
-    } else if (server.argName(i) == "blueU") {
-      blueU = server.arg(i).toFloat();
-    } else if (server.argName(i) == "blueV") {
-      blueV = server.arg(i).toFloat();
-    } else if (server.argName(i) == "redLum") {
-      redLum = server.arg(i).toFloat();
-    } else if (server.argName(i) == "greenLum") {
-      greenLum = server.arg(i).toFloat();
-    } else if (server.argName(i) == "blueLum") {
-      blueLum = server.arg(i).toFloat();
-    } else if (server.argName(i) == "redP1") {
-      redP1 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "redP2") {
-      redP2 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "redQ1") {
-      redQ1 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "greenP1") {
-      greenP1 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "greenP2") {
-      greenP2 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "greenQ1") {
-      greenQ1 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "blueP1") {
-      blueP1 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "blueP2") {
-      blueP2 = server.arg(i).toFloat();
-    } else if (server.argName(i) == "blueQ1") {
-      blueQ1 = server.arg(i).toFloat();
-    }
-  }
-
-  if (redU > 0 && redV > 0 && greenU > 0 && greenV > 0 && blueU > 0 && blueV > 0 && redLum > 0 &&
-  greenLum > 0 && blueLum > 0 && redP1 > -10000 && redP2 > -10000 && redQ1 > -10000 && greenP1 > -10000 &&
-  greenP2 > -10000 && greenQ1 > -10000 && blueP1 > -10000 && blueP2 > -10000 && blueQ1 > -10000) {
-    Luv redUv = {100, redU, redV}, greenUv = {100, greenU, greenV}, blueUv = {100, blueU, blueV};
-    float redToGreenFit[3] = {redP1, redP2, redQ1};
-    float greenToBlueFit[3] = {greenP1, greenP2, greenQ1};
-    float blueToRedFit[3] = {blueP1, blueP2, blueQ1};
-    calibrate(redUv, greenUv, blueUv, redLum, greenLum, blueLum, redToGreenFit, greenToBlueFit, blueToRedFit);
-  }
-
-  // Send response
-  server.send(200, "application/json", "");
+void httpCalibrationController() {
+	float redU = -1, redV = -1, greenU = -1, greenV = -1, blueU = -1, blueV = -1;
+	float redLum = -1, greenLum = -1, blueLum = -1;
+	float redP1 = -10000, redP2 = -10000, redQ1 = -10000;
+	float greenP1 = -10000, greenP2 = -10000, greenQ1 = -10000;
+	float blueP1 = -10000, blueP2 = -10000, blueQ1 = -10000;
+
+	// Parse args
+	for (uint8_t i = 0; i < server.args(); ++i) {
+		if (server.argName(i) == "redU") {
+			redU = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "redV") {
+			redV = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "greenU") {
+			greenU = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "greenV") {
+			greenV = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "blueU") {
+			blueU = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "blueV") {
+			blueV = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "redLum") {
+			redLum = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "greenLum") {
+			greenLum = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "blueLum") {
+			blueLum = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "redP1") {
+			redP1 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "redP2") {
+			redP2 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "redQ1") {
+			redQ1 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "greenP1") {
+			greenP1 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "greenP2") {
+			greenP2 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "greenQ1") {
+			greenQ1 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "blueP1") {
+			blueP1 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "blueP2") {
+			blueP2 = server.arg(i).toFloat();
+		}
+		else if (server.argName(i) == "blueQ1") {
+			blueQ1 = server.arg(i).toFloat();
+		}
+	}
+
+	if (redU > 0 && redV > 0 && greenU > 0 && greenV > 0 && blueU > 0 && blueV > 0 && redLum > 0 &&
+		greenLum > 0 && blueLum > 0 && redP1 > -10000 && redP2 > -10000 && redQ1 > -10000 && greenP1 > -10000 &&
+		greenP2 > -10000 && greenQ1 > -10000 && blueP1 > -10000 && blueP2 > -10000 && blueQ1 > -10000) {
+		Luv redUv = { 100, redU, redV }, greenUv = { 100, greenU, greenV }, blueUv = { 100, blueU, blueV };
+		float redToGreenFit[3] = { redP1, redP2, redQ1 };
+		float greenToBlueFit[3] = { greenP1, greenP2, greenQ1 };
+		float blueToRedFit[3] = { blueP1, blueP2, blueQ1 };
+		led.calibrate(redUv, greenUv, blueUv, redLum, greenLum, blueLum, redToGreenFit, greenToBlueFit, blueToRedFit);
+	}
+
+	// Send response
+	server.send(200, "application/json", "");
 }

 /**
  * HTTP 404 - Not foudn
  */
-void httpNotFoundController () {
-  server.send(404, "text/plain", "404");
+void httpNotFoundController() {
+	server.send(404, "text/plain", "404");
 }

@@ -632,53 +347,45 @@
 /**
  * Setup
  */
-void setup(void){
-  analogWriteRange(pwmRange_);
-
-  pinMode(led1Pin_, OUTPUT); digitalWrite(led1Pin_, HIGH);
-  pinMode(led2Pin_, OUTPUT); digitalWrite(led2Pin_, HIGH);
-  pinMode(redPin_, OUTPUT); analogWrite(redPin_, 0);
-  pinMode(greenPin_, OUTPUT); analogWrite(greenPin_, 0);
-  pinMode(bluePin_, OUTPUT); analogWrite(bluePin_, 0);
-  pinMode(w1Pin_, OUTPUT); analogWrite(w1Pin_, 0);
-  pinMode(w2Pin_, OUTPUT); analogWrite(w2Pin_, 0);
-
-  // Set onboard leds on as a sign for AP mode
-  setLed1(true);
-  setLed2(true);
-
-  // Autoconnect to latest WiFi or create cofiguration portal if cannot connect as a client
-  WiFiManager wiFiManager;
-  //wiFiManager.resetSettings(); // For testing
-  wiFiManager.autoConnect("chromasome", "chromasome");
-
-  //Serial.begin(115200);
-
-  // Set onboard leds off as a sign for STA mode
-  setLed1(false);
-  setLed2(false);
-
-  // Set default to 1900K
-  setOnOff(true);
-  setColorTemperature(75, 1900);
-
-  // Routes
-  server.on("/", HTTP_GET, httpIndexController);
-  server.on("/onboardLeds", HTTP_GET, httpOnboardLedsController);
-  server.on("/on", HTTP_GET, httpOnController);
-  server.on("/off", HTTP_GET, httpOffController);
-  server.on("/raw", HTTP_GET, httpRawController);
-  server.on("/cie1976Ucs", HTTP_GET, httpCie1976UcsController);
-  server.on("/colorTemperature", HTTP_GET, httpColorTemperatureController);
-  server.on("/calibrate", HTTP_GET, httpCalibrationController);
-
-  // Not found
-  server.onNotFound(httpNotFoundController);
-
-  // Start listening
-  server.begin();
-}
-
-void loop(void){
-  server.handleClient();
-}
+void setup(void) {
+
+	pinMode(led1Pin, OUTPUT);
+	pinMode(led2Pin, OUTPUT); ;
+
+	// Set onboard leds on as a sign for AP mode
+	digitalWrite(led1Pin, LOW);
+	digitalWrite(led2Pin, LOW);
+
+	// Autoconnect to latest WiFi or create cofiguration portal if cannot connect as a client
+	WiFiManager wiFiManager;
+	//wiFiManager.resetSettings(); // For testing
+	wiFiManager.autoConnect("chromasome", "chromasome");
+
+	//Serial.begin(115200);
+
+	// Set onboard leds off as a sign for STA mode
+	digitalWrite(led1Pin, HIGH);
+	digitalWrite(led2Pin, HIGH);
+
+	// Set default to 1900K
+	led.setOnOff(true);
+	led.setColorTemperature(75, 1900);
+
+	// Routes
+	server.on("/", HTTP_GET, httpIndexController);
+	server.on("/on", HTTP_GET, httpOnController);
+	server.on("/off", HTTP_GET, httpOffController);
+	server.on("/raw", HTTP_GET, httpRawController);
+	server.on("/cie1976Ucs", HTTP_GET, httpCie1976UcsController);
+	server.on("/colorTemperature", HTTP_GET, httpColorTemperatureController);
+	server.on("/calibrate", HTTP_GET, httpCalibrationController);
+	server.onNotFound(httpNotFoundController);
+
+	// Start listening
+	server.begin();
+}
+
+void loop(void) {
+	server.handleClient();
+
+}


Update MRLCommOculus4.ino

Update MRLCommOculus4.ino

--- home/Alessandruino/MRLCommOculus4.ino
+++ home/Alessandruino/MRLCommOculus4.ino
@@ -211,7 +211,6 @@
 #define ERROR_ALREADY_EXISTS			3
 #define ERROR_DOES_NOT_EXIST			4

-#define CUSTOM_MSG                         50

 // ------ error types ------
 #define SENSOR_ULTRASONIC				1


Update

Update


move radio.begin to setup

move radio.begin to setup

200ms to 120ms

--- _25-attiny85-nrf24-ds18b20/_25-attiny85-nrf24-ds18b20.ino
+++ _25-attiny85-nrf24-ds18b20/_25-attiny85-nrf24-ds18b20.ino
@@ -45,7 +45,6 @@

 // DS18B20
 #define ONE_WIRE_BUS 3
-// 12bit - 750ms, 11bit - 375ms, 10bit - 187ms, 9bit - 93.75ms
 #define TEMPERATURE_PRECISION 9

 OneWire oneWire(ONE_WIRE_BUS);
@@ -63,7 +62,22 @@
     sleep();
     abort();
   }
+  // ds18b20 getTem 766ms
+  // 12bit - 750ms, 11bit - 375ms, 10bit - 187ms, 9bit - 93.75ms
   //sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);
+
+  // radio begin to power down : 80 ms
+  radio.begin();
+  //radio.enableDynamicPayloads();
+  // default is on
+  //radio.setAutoAck(1);
+  radio.setRetries(15, 15);
+  radio.setPALevel(RF24_PA_LOW);
+  radio.setDataRate(RF24_250KBPS);
+  radio.setPayloadSize(11);
+  radio.openWritingPipe(pipes[0]);
+  radio.stopListening();
+
   long stopmilis = millis();
   payload.humi = ( stopmilis - startmilis ) * 10 ;

@@ -75,8 +89,6 @@
   payload._salt++;
   long startmilis = millis();

-  // ds18b20 getTem 766ms
-  // 12bit - 750ms, 11bit - 375ms, 10bit - 187ms, 9bit - 93.75ms
   sensors.requestTemperatures();
   tempCoutside = sensors.getTempC(outsideThermometer);

@@ -98,18 +110,8 @@
     abort();
   }

-  // radio begin to power down : 80 ms
-  radio.begin();
-  radio.enableDynamicPayloads();
-  radio.setAutoAck(1);
-  radio.setRetries(15, 15);
-  //radio.setPALevel(RF24_PA_HIGH);
-  radio.setPALevel(RF24_PA_LOW);
-  radio.setDataRate(RF24_250KBPS);
-  radio.setPayloadSize(11);
-  radio.openWritingPipe(pipes[0]);
-  radio.stopListening();

+  radio.powerUp();
   radio.write(&payload , sizeof(payload));
   yield();
   //delay(100);


Speed up refresh rate from 89 Hz to 1/(150e-6*8*7) Hz = 119 Hz

--- avr/avr.ino
+++ avr/avr.ino
@@ -33,7 +33,7 @@
 	Serial.begin(9600);
 	// initialize SPI
 	SPI.begin();
-	Timer1.initialize(200);
+	Timer1.initialize(150);
 	Timer1.attachInterrupt(driveDisplay);
 }


Changed 0 and 1 to RN42 and HC06 (defined)

--- ballbot.ino
+++ ballbot.ino
@@ -57,10 +57,10 @@
 #define HC06 1

 void btSetup(uint8_t module, int initBaud, int finalBaud){
-    if (module == 0){
-
-    }
-    else if (module == 1){
+    if (module == RN42){
+
+    }
+    else if (module == HC06){
         BT.begin(initBaud);
         delay(100);
         Serial.print("AT");


Integrated RFM12b into sketch with Bencode message payload.

--- freezer_node_v0_1/freezer_node_v0_1.ino
+++ freezer_node_v0_1/freezer_node_v0_1.ino
@@ -32,6 +32,8 @@
 #define APIN_LDR        1  // data connection for photocell
 #define DPIN_ONEWIRE    3  // data connection for the Dallas OneWire bus
 #define DPIN_HALL       7  // data connection for hall effect
+#define DPIN_RF_LED     9  // led indicating rf traffic
+#define LED_DELAY       500

 #define MEASURE_PERIOD  100 // how often to measure, in tenths of seconds
 #define RETRY_PERIOD    10  // how soon to retry if ACK didn't come in
@@ -40,6 +42,17 @@
 #define REPORT_EVERY    2   // report every N measurement cycles
 #define SMOOTH          3   // smoothing factor used for running averages

+#define NODEID          2       // node ID used for this unit
+#define NETWORKID       99
+#define GATEWAYID       1
+#define FREQUENCY       RF12_915MHZ
+
+#define BUFFER_SIZE     128
+
+// set the sync mode to 2 if the fuses are still the Arduino default
+// mode 3 (full powerdown) can only be used with 258 CK startup fuses
+#define RADIO_SYNC_MODE 2
+
 enum { MEASURE, REPORT, TASK_END };

 //static word schedbuf[TASK_END];
@@ -49,10 +62,10 @@
 static byte reportCount;    // count up until next report, i.e. packet send

 struct SensorData {
-  byte light;           // light sensor: 0..255
-  byte openDoor :1;     // door sensor: 0..1
-  int tempInside :10;   // temperature: -500..+500 (tenths)
-  int tempOutside :10;  // temperature: -500..+500 (tenths)
+  byte light;            // light sensor: 0..255
+  byte door :1;      // door sensor: 0..1
+  int tempIn :10;    // temperature: -500..+500 (tenths)
+  int tempOut :10;   // temperature: -500..+500 (tenths)
 };

 // dallas onewire configuration
@@ -63,6 +76,8 @@

 // container for sensor data
 SensorData sensorData;
+
+char buffer[BUFFER_SIZE];

 // has to be defined because we're using the watchdog for low-power waiting
 ISR(WDT_vect) { Sleepy::watchdogEvent(); }
@@ -81,6 +96,11 @@
   #endif

   setup_sensors();
+
+  pinMode(DPIN_RF_LED, OUTPUT);
+
+  rf12_initialize(NODEID, FREQUENCY, NETWORKID);
+  rf12_sleep(RF12_SLEEP);

   reportCount = REPORT_EVERY;     // report right away for easy debugging
   scheduler.timer(MEASURE, 0);    // start the measurement loop going
@@ -116,6 +136,22 @@
 }

 //------------------------------------------------------------------------------
+void EmBencode::PushChar (char ch) {
+  int len = strlen(buffer);
+  buffer[len + 1] = buffer[len];
+  buffer[len] = ch;
+}
+
+//------------------------------------------------------------------------------
+// blinks the give led pin
+//
+static void blink_led(byte pin, int delay_ms) {
+  digitalWrite(pin, HIGH);
+  delay(delay_ms);
+  digitalWrite(pin, LOW);
+}
+
+//------------------------------------------------------------------------------
 // flushes the serial port
 //
 static void serialFlush () {
@@ -144,15 +180,37 @@
   sensorData.light = smoothedAverage(sensorData.light, light, firstTime);

   // read door status
-  sensorData.openDoor = digitalRead(DPIN_HALL);
+  sensorData.door = digitalRead(DPIN_HALL);

   // read one-wire sensors
   oneWireSensors.requestTemperatures();
-  int tempInside = int(oneWireSensors.getTempC(thermometerInside) * 10);
-  sensorData.tempInside = smoothedAverage(sensorData.tempInside, tempInside, firstTime);
-  int tempOutside = int(oneWireSensors.getTempC(thermometerOutside) * 10);
-  sensorData.tempOutside = smoothedAverage(sensorData.tempOutside, tempOutside, firstTime);
-
+  int tempIn = int(oneWireSensors.getTempC(thermometerInside) * 10);
+  sensorData.tempIn = smoothedAverage(sensorData.tempIn, tempIn, firstTime);
+  int tempOut = int(oneWireSensors.getTempC(thermometerOutside) * 10);
+  sensorData.tempOut = smoothedAverage(sensorData.tempOut, tempOut, firstTime);
+
+}
+
+//------------------------------------------------------------------------------
+// generate Bencode response
+//
+static void createReport() {
+
+    buffer[0] = '\0';
+
+    EmBencode encoder;
+    encoder.push(NODEID);
+    encoder.startDict();
+        encoder.push("light");
+        encoder.push(sensorData.light);
+        encoder.push("door");
+        encoder.push(sensorData.door);
+        encoder.push("tempIn");
+        encoder.push(sensorData.tempIn);
+        encoder.push("tempOut");
+        encoder.push(sensorData.tempOut);
+    encoder.endDict();
+
 }

 //------------------------------------------------------------------------------
@@ -160,15 +218,28 @@
 // serial port)
 //
 static void doReport() {
+
+  createReport();
+  rf12_sleep(RF12_WAKEUP);
+  rf12_sendNow(0, buffer, strlen(buffer));
+  rf12_sendWait(RADIO_SYNC_MODE);
+  rf12_sleep(RF12_SLEEP);
+  blink_led(DPIN_RF_LED, LED_DELAY);
+
   #if SERIAL
     Serial.print("FREEZER ");
     Serial.print((int) sensorData.light);
     Serial.print(' ');
-    Serial.print((int) sensorData.openDoor);
+    Serial.print((int) sensorData.door);
     Serial.print(' ');
-    Serial.print((int) sensorData.tempInside);
+    Serial.print((int) sensorData.tempIn);
     Serial.print(' ');
-    Serial.print((int) sensorData.tempOutside);
+    Serial.print((int) sensorData.tempOut);
+    #if DEBUG
+      Serial.print(" - MSG [");
+      Serial.print(buffer);
+      Serial.print("]");
+    #endif
     Serial.println();
     serialFlush();
   #endif


Renamed hardware pins definitions for LoRa32u4

--- crossbow.ino
+++ crossbow.ino
@@ -29,9 +29,9 @@
 #include "qsp.h"

 // LoRa32u4 ports
-#define SS      8
-#define RST     4
-#define DI0     7
+#define LORA32U4_SS_PIN     8
+#define LORA32U4_RST_PIN    4
+#define LORA32U4_DI0_PIN    7

 int ppm[PPM_CHANNEL_COUNT] = {0};
@@ -178,11 +178,20 @@
     Serial.println("Start");
 #endif

-    LoRa.setPins(SS,RST,DI0);
+    /*
+     * Setup hardware
+     */
+    LoRa.setPins(
+        LORA32U4_SS_PIN,
+        LORA32U4_RST_PIN,
+        LORA32U4_DI0_PIN
+    );
+
 #ifdef DEBUG_SERIAL
     Serial.println("Pins Set");
 #endif
-    if (!LoRa.begin(868E6))
+
+if (!LoRa.begin(868E6))
     {
     #ifdef DEBUG_SERIAL
         Serial.println("LoRa init failed. Check your connections.");


Remote controlling of patterns now works.

--- firmware/node/node.ino
+++ firmware/node/node.ino
@@ -1,7 +1,6 @@
 #include <util/crc16.h>
 #include <EEPROM.h>
 #include <Adafruit_NeoPixel.h>
-
 #include "parse.h"

 const uint8_t NUM_PIXELS = 4;
@@ -21,22 +20,26 @@
 // ----- globals ------------

 // time keeping
-Timer t;
-volatile uint32_t   g_ticks = 0;
-volatile uint32_t   g_target = 0, g_last_target = 0;
-uint8_t             g_delay = 10;
+uint32_t   g_target = 0, g_pattern_start = 0;
+uint8_t    g_delay = 10;

 // random seed
 uint8_t    g_random_seed = 0;

 // The current packet
-uint8_t    g_packet[MAX_PACKET_LEN];
+uint8_t     g_packet[MAX_PACKET_LEN];
+s_source_t *g_cur_pattern = NULL;

 // The LED control object
 Adafruit_NeoPixel g_pixels = Adafruit_NeoPixel(NUM_PIXELS, OUT_PIN, NEO_GRB + NEO_KHZ800);

 // our node id
 uint8_t g_node_id = 0;
+
+
+// ---- prototypes -----
+void start_pattern(s_source_t *pattern);
+

 void show_color(uint8_t r, uint8_t g, uint8_t b)
@@ -100,15 +103,13 @@
     Serial.println("led-board hello!");

     g_node_id = EEPROM.read(id_address);
-
-    t.every(1000, tick); // once per ms
 }

 void handle_packet(uint16_t len, uint8_t *packet)
 {
     uint8_t    j, type, target;
     uint8_t   *data;
-    void      *g_pattern = NULL;
+

     target = packet[0];
     if (target != BROADCAST && target != g_node_id)
@@ -122,56 +123,59 @@
             for(int j=0; j < NUM_PIXELS; j++)
                 g_pixels.setPixelColor(j, g_pixels.Color(data[0], data[1], data[2]));
             break;
+
         case PACKET_COLOR_ARRAY:
             for(int j=0; j < NUM_PIXELS; j++, data += 3)
                 g_pixels.setPixelColor(j, g_pixels.Color(data[0], data[1], data[2]));
             break;
+
         case PACKET_PATTERN:
-            g_pattern = parse(data, len - 2);
-            if (!g_pattern)
+            g_cur_pattern = (s_source_t *)parse(data, len - 2);
+            if (!g_cur_pattern)
             {
                 Serial.println("Parse failed.");
                 error_pattern();
                 return;
             }
-
-            Serial.println("Parse ok.");
-            show_pattern((s_source_t *)g_pattern);
+            start_pattern(g_cur_pattern);
             break;
+
         case PACKET_ENTROPY:
             g_random_seed = data[0];
             break;
     }
 }

-void tick()
-{
-    g_ticks++;
-
-    if (g_target && g_ticks >= g_target)
-    {
-        g_pixels.show();
-        g_last_target = g_target;
-        g_target = 0;
-    }
-}
-
-void evaluate_pattern(s_source_t *pattern)
+void start_pattern(s_source_t *pattern)
 {
     uint8_t  i;
     color_t  color;
-
-    if (g_target || !pattern)
-        return;
-
-    if (g_last_target)
-        g_target = g_last_target + g_delay;
-    else
-        g_target = ticks + g_delay;
-
-    evaluate(pattern, g_target, &color);
+
+    g_pattern_start = millis();
+    g_target = g_pattern_start;
+
+    evaluate(pattern, g_pattern_start, &color);
     for(i = 0; i < NUM_PIXELS; i++)
         g_pixels.setPixelColor(i, color.c[0], color.c[1], color.c[2]);
+}
+
+void update_pattern(s_source_t *pattern)
+{
+    uint8_t  i;
+    color_t  color;
+
+    if (!pattern)
+        return;
+
+    if (g_target && millis() >= g_target)
+    {
+        g_target += g_delay;
+        g_pixels.show();
+
+        evaluate(pattern, g_target - g_pattern_start, &color);
+        for(i = 0; i < NUM_PIXELS; i++)
+            g_pixels.setPixelColor(i, color.c[0], color.c[1], color.c[2]);
+    }
 }

 void loop()
@@ -179,7 +183,9 @@
     uint8_t            i, ch, data[3];
     static uint8_t     found_header = 0;
     static uint16_t    crc = 0, len = 0, recd = 0;
-
+
+    update_pattern(g_cur_pattern);
+
     if (Serial.available() > 0)
     {
         ch = Serial.read();
@@ -233,5 +239,4 @@
         }
     }

-    evaluate_pattern(g_pattern);
-}
+}


Update LogoBotEsp8266.ino

Update LogoBotEsp8266.ino

added basic css to make pretty

--- software/firmware/LogoBotEsp8266/LogoBotEsp8266.ino
+++ software/firmware/LogoBotEsp8266/LogoBotEsp8266.ino
@@ -10,14 +10,45 @@
 <!DOCTYPE html>
 <meta name="viewport" content="width=device-width, initial-scale=1">

-<title>Logobot Blue</title>
-
+
+<title>Logobot Blue</title>
+
+
+
 Distance: <input type="text" id="dist" value="1000" />

-Backward :
+Backward
 Forward

 Angle: <input type="text" id="angle" value="90" />

-Left :
+Left
 Right

@@ -49,6 +80,7 @@
 </script>

+
 )~";

 void handleRoot() {


Try faster comms

Try faster comms

--- arduino/led_matrix/led_matrix.ino
+++ arduino/led_matrix/led_matrix.ino
@@ -33,7 +33,7 @@
       } else {
         yy = HEIGHT - y - 1;
       }
-      leds[yy + x * HEIGHT] = CBRB(g, r, b);
+      leds[yy + x * HEIGHT] = CGRB(g, r, b);
     }
   }
   FastLED.show();


Dialog to pick color for painting.

Dialog to pick color for painting.

--- arduino/led_matrix/led_matrix.ino
+++ arduino/led_matrix/led_matrix.ino
@@ -15,6 +15,10 @@
   FastLED.addLeds<WS2812B, PIN>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
   FastLED.setBrightness(BRIGHTNESS);
   Serial.begin(115200);
+  FastLED.show();
+  leds[0] = CRGB(0.5, 0.5, 0.5);
+  delay(250);
+  leds[0] = CRGB(0, 0, 0);
   FastLED.show();
 }


Fixed a couple of syntax errors and it's working. Now to try some other smoothing algorithms.

--- arduino/tachometer/tachometer.ino
+++ arduino/tachometer/tachometer.ino
@@ -49,7 +49,7 @@

 void send(int x, int y)
 {
-  char buffer[STR_BUF];
+  char buffer[STR_LEN];
   sprintf(buffer, "[%i,%i]", x, y);
   Serial.println(buffer);
 }
@@ -58,8 +58,8 @@
 {
   static int idx = 0;

-  ring_buffer[IRQ_0][i] = pulse_count[IRQ_0];
-  ring_buffer[IRQ_1][i] = pulse_count[IRQ_1];
+  ring_buffer[IRQ_0][idx] = pulse_count[IRQ_0];
+  ring_buffer[IRQ_1][idx] = pulse_count[IRQ_1];

   pulse_count[IRQ_0] = 0;
   pulse_count[IRQ_1] = 0;


waste half of previous automatic withdrawl

--- SBR/SBR.ino
+++ SBR/SBR.ino
@@ -396,7 +396,6 @@
      lcd.setCursor(0,1);
   lcd.print(tank);lcd.print("gal  ");
   lcd.print(timedisplay);lcd.print("min  ");
-  //digitalWrite(stir, HIGH);
     delay(1000);}
     digitalWrite(stir, LOW);
 }
@@ -416,9 +415,15 @@
   digitalWrite(en1, HIGH);
 }
 }
+void sludgeremoval(){
+  stepper(6400,1,2);//fills up tube. revolutions, 1st motor, "empty" dir
+  stepper(1250,1,2);//empties(was 2500 steps, now 1250). revolutions, 1st motor, "empty" dir
+  stepper(6400,1,1);//empty tube back into sbr. revolutions, 1st motor, "fill" dir
+}
 void weekend(){
   aeration(5, 4);
   rest(50);
+  sludgeremoval();
 }
 void normalsequence(){//10 hrs
   DateTime now = rtc.now();
@@ -519,32 +524,25 @@
   lcd.setCursor(0,1);
   lcd.print(tank);
   while (x<1){
-  halftimeair(60,19);
-  delay(2000);
-  aeration(60,19);
-  delay(2000);
-  settle(180, 19);
-  delay(2000);
-  decant(6);
-   delay(1000);
+   //sludgeremoval();
    //aeration(60,17);
-   //delay(1000);
+   //settle(50, 19);//delay(1000);
    //decant(6);//loading();
     x++;}
     //write conditions for running sequence here and add functions
   if (now.dayOfTheWeek()== 1){//"Monday"
-    if (now.hour()>5 && now.hour()<7){normalsequence();}}
+    if (now.hour()>4 && now.hour()<6){normalsequence();}}
   if (now.dayOfTheWeek()== 2){//"Tuesday"
-    if (now.hour()>5 && now.hour()<7){normalsequence();}}
+    if (now.hour()>4 && now.hour()<6){normalsequence();}}
   if (now.dayOfTheWeek()== 3){//"Wednesday"
-    if (now.hour()>5 && now.hour()<7){normalsequence();}}
+    if (now.hour()>4 && now.hour()<6){normalsequence();}}
   if (now.dayOfTheWeek()== 4){//"thursday"
-    if (now.hour()>5 && now.hour()<7){normalsequence();}}
+    if (now.hour()>4 && now.hour()<6){normalsequence();}}
   if (now.dayOfTheWeek()== 5){//"Friday"
-    if (now.hour()>5 && now.hour()<7){normalsequence();}}
+    if (now.hour()>4 && now.hour()<6){normalsequence();}}
   if (now.dayOfTheWeek()== 6){//"Saturday"
-    if (now.hour()>5 && now.hour()<7){normalsequence();}}//fillplusair(21,1);thirtysecaeration(100,10);}}
+    if (now.hour()>4 && now.hour()<6){normalsequence();}}//fillplusair(21,1);thirtysecaeration(100,10);}}
   if (now.dayOfTheWeek()== 0){//"Sunday"
-    if (now.hour()>5 && now.hour()<7){normalsequence();}}//weekend();}
+    if (now.hour()>4 && now.hour()<6){normalsequence();}}//weekend();}
   delay(20000);
   weekend();}


Use Nrf24Payload.h

Use Nrf24Payload.h

--- RF24_Receiver/RF24_Receiver.ino
+++ RF24_Receiver/RF24_Receiver.ino
@@ -1,7 +1,7 @@

 /***** NB FOR MOTOR BOARD ***/

-#define DEVICE_ID 'D';
+#define DEVICE_ID 'D'

 //#define RX_ADDRESS "AAAAA"
 //#define RX_ADDRESS "BBBBB"
@@ -17,6 +17,7 @@
 #include "nRF24L01.h"
 #include "RF24.h"
 #include "printf.h"
+#include <Nrf24Payload.h>

 #define PIN_CE  SCL
 #define PIN_CSN SDA
@@ -28,27 +29,8 @@

 RF24 radio(PIN_CE, PIN_CSN);

-/**
- * Be carefull to ensure the struct size is the same as on the Pi.
- * Just having the same size variables is not enough.
- * @see http://www.delorie.com/djgpp/v2faq/faq22_11.html
- */
-typedef struct{
-  int32_t timestamp;
-  uint16_t msg_id;
-  uint16_t vcc;
-  uint16_t a;
-  uint16_t b;
-  uint16_t c;
-  uint16_t d;
-  uint8_t type;
-  uint8_t device_id;
-  int8_t y;
-  int8_t z;
-}
-payload_t;
-payload_t payload;
-
+Nrf24Payload rx_payload = Nrf24Payload();
+uint8_t rx[Nrf24Payload_SIZE];

 uint16_t msg_id = 0;

@@ -78,7 +60,7 @@

   // Setup and configure rf radio
   radio.begin(); // Start up the radio
-  radio.setPayloadSize(sizeof(payload_t));
+  radio.setPayloadSize(Nrf24Payload_SIZE);
   radio.setAutoAck(1); // Ensure autoACK is enabled
   radio.setRetries(0,15); // Max delay between retries & number of retries
   // Allow optional ack payloads
@@ -95,9 +77,6 @@

   //radio.printDetails();

-
-
- // Robot.debugPrint(sizeof(payload));
 }

 void loop(void)
@@ -121,7 +100,7 @@
     radio.writeAckPayload(1, &ack, sizeof(ack));
     ack++;

-    radio.read( &payload, sizeof(payload));
+    radio.read( &rx, Nrf24Payload_SIZE);
     processPayload();

   }
@@ -130,83 +109,67 @@

 void processPayload()
 {
-  // Create a transfer buffer of the relevant data.
-  tx_buf[0] = payload.type;
-  tx_buf[1] = payload.device_id;
-  tx_buf[2] = (payload.msg_id >> 8);
-  tx_buf[3] = payload.msg_id;
-  tx_buf[4] = (payload.a >> 8);
-  tx_buf[5] = payload.a;
-  tx_buf[6] = (payload.b >> 8);
-  tx_buf[7] = payload.b;
-  //Serial1.write(tx_buf, 10);
+  rx_payload.unserialize(rx);

+  // Create an internal transfer buffer of the relevant data.
+  //@todo provide usful data for the top board
+  tx_buf[0] = rx_payload.getType();
+  tx_buf[1] = rx_payload.getDeviceId();
+  tx_buf[2] = (rx_payload.getId() >> 8);
+  tx_buf[3] = rx_payload.getId();
+  tx_buf[4] = (rx_payload.getA() >> 8);
+  tx_buf[5] = rx_payload.getA();
+  tx_buf[6] = (rx_payload.getB() >> 8);
+  tx_buf[7] = rx_payload.getA();

-  //uint8_t *bytePtr = (uint8_t*)&payload;
-  //Serial1.write(bytePtr, sizeof(payload));
- /*
-    printf ("Got: %c %c %ld %d %d %d %d %d %d %d %d \n",
-    payload.device_id,
-    payload.type,
-    payload.timestamp,
-    payload.msg_id,
-    payload.vcc,
-    payload.a,
-    payload.b,
-    payload.c,
-    payload.d,
-    payload.y,
-    payload.z);
-    */

   // Proof of concept
-  if (payload.a == 70) {
+  if (rx_payload.getA() == 70) {
     radio.stopListening();

     // Prepare the message.
-    payload_t payload;
-    payload.device_id = DEVICE_ID;
-    payload.type = 'q';
-    payload.timestamp = millis();
-    payload.msg_id = msg_id;
-    payload.vcc = 0; //@TODO vcc
-    payload.a = 99;
-    payload.b = 0;
-    payload.c = 0;
-    payload.d = 0;
-    payload.y = 0;
-    payload.z = 0;
+    Nrf24Payload tx_payload = Nrf24Payload();
+    tx_payload.setDeviceId(DEVICE_ID);
+    tx_payload.setType('q'); // light command
+    tx_payload.setTimestamp(millis());
+    tx_payload.setId(msg_id++);
+    tx_payload.setA(99);

+    uint8_t tx_buffer[Nrf24Payload_SIZE];
+    tx_payload.serialize(tx_buffer);
+
     //printf("sending %d, %d \n", payload.msg_id, payload.a);
-    if (!radio.write( &payload, sizeof(payload))) {
+    if (!radio.write( &tx_buffer, Nrf24Payload_SIZE)) {
       //printf(" failed.\n\r");
     }

-    msg_id++; // Let it overflow
     radio.startListening();
   }

-  if (payload.c > 0) {
+  int c = rx_payload.getC();
+  int d = rx_payload.getD();
+
+  if (c > 0) {
     // Left motor command
-    if (payload.c > 255) {
+    if (c > 255) {
       // Forward 0 -> 255
-      speedL = constrain(payload.c - 255, 0, 255);
+      speedL = constrain(c - 255, 0, 255);
     } else {
       // Backward -1 -> -255
-      speedL = 1 - constrain(payload.c , 0, 255);
+      speedL = 1 - constrain(c , 0, 255);
     }
   } else {
     speedL = 0; // stopped
   }

-  if (payload.d > 0) {
+  if (d > 0) {
     // Right motor command
-    if (payload.d > 255) {
+    if (d > 255) {
       // Forward 0 -> 255
-      speedR = constrain(payload.d - 255, 0, 255);
+      speedR = constrain(d - 255, 0, 255);
     } else {
       // Backward -1 -> -255
-      speedR = 1 - constrain(payload.d , 0, 255);
+      speedR = 1 - constrain(d , 0, 255);
     }
   } else {
     speedR = 0; // stopped


Code Tidy

Code Tidy

--- FH_Tbot.ino
+++ FH_Tbot.ino
@@ -96,27 +96,25 @@
 WiFiClient serverClients[MAX_SRV_CLIENTS];
 int currentClient = 0;
 boolean pingOn = false;
-long nextBoredBotEvent = 0;
+unsigned long nextBoredBotEvent = 0;
 int boredBotTimeout = 60000;             //in ms
 boolean leftBumperHit = false;
 boolean rightBumperHit = false;
 boolean autoMode = false; 				 // drive mode with no client connected
-long autoModeNextUpdate = 0;
-long autoModeNextEvent = 0;
+unsigned long autoModeNextUpdate = 0;
+unsigned long autoModeNextEvent = 0;
 //#define Diag                           // if not defined disables serial communication after initial feedback
-long timerPing;
-
-
-void Stop(void)
-{
+unsigned long timerPing;
+
+
+void Stop(void){
   motors.manualDrive(0,0);
   setColor(RgbColor(0,0,0));             // turn off LED's to save power
   pingOn = false;                        // turn off ping to save power
   driverAssist = false;
 }

-void CheckHeartBeat(void)
-{
+void CheckHeartBeat(void){
 	updateMotorSpeed(); 				// check battery level and adjust speed
   if (HeartBeatRcvd == true)
   {
@@ -129,8 +127,7 @@
   }
 }

-void setup()
-{
+void setup(){
   system_update_cpu_freq(80);           // set cpu to 80MHZ or 160MHZ !
   system_phy_set_max_tpw(30); 			// 0 - 82 radio TX power
   initHardware();
@@ -141,8 +138,7 @@
   nextBoredBotEvent = millis() + boredBotTimeout;
 }

-void loop()
-{
+void loop(){
   // time dependant functions here
   checkBoredBot();
   checkAutoMode();
@@ -416,8 +412,7 @@
     }
   }

-void setupWiFi()
-{
+void setupWiFi(){
   WiFi.mode(WIFI_AP);

   // Create a unique name by appending the MAC address to the AP Name
@@ -466,8 +461,7 @@
   webSocket.onEvent(webSocketEvent);
 }

-void initHardware()
-{
+void initHardware(){
   Serial.begin(2000000);
   Serial.println(F("\r\n"));
   Serial.println(F("            FHTbot Serial Connected\r\n"));
@@ -577,7 +571,7 @@
     theBuffer.close();
     return;
   }
-  int bufferLength = theBuffer.size();
+  uint bufferLength = theBuffer.size();
   if ( serverClients[currentClient].write(theBuffer,2920) <  bufferLength){
     // failed to send all file
   }


now outputting motor position and stall guard value

--- examples/TMC262MotorTester/Serial.ino
+++ examples/TMC262MotorTester/Serial.ino
@@ -46,6 +46,11 @@
     Serial.print('m');
     Serial.print(tmc262Stepper.getMicrosteps(),DEC);
     Serial.print(',');
+    Serial.print("sg");
+    Serial.print(tmc262Stepper.getCurrentStallGuardReading(),DEC);
+    Serial.print(',');
+    Serial.print('p');
+    Serial.print(tmc262Stepper.getMotorPosition(),DEC);
     Serial.println();
   }
 }


Delete Receive.ino

Delete Receive.ino

--- Receive.ino
+++ Receive.ino
@@ -1,114 +0,0 @@
-#include "EEPROM.h"
-#include "cc1101.h"
-
-// The connection to the hardware chip CC1101 the RF Chip
-CC1101 cc1101;
-
-byte b;
-byte i;
-byte syncWord = 199;
-long counter = 0;
-byte chan = 0;
-
-// a flag that a wireless packet has been received
-boolean packetAvailable = false;
-
-/* Handle interrupt from CC1101 (INT0) gdo0 on pin2 */
-void cc1101signalsInterrupt(void) {
-  // set the flag that a package is available
-  packetAvailable = true;
-}
-
-void setup()
-{
-  Serial.begin(38400);
-  Serial.println("start");
-
-  // initialize the RF Chip
-  cc1101.init();
-
-  //cc1101.setSyncWord(&syncWord, false);
-  cc1101.setCarrierFreq(CFREQ_433);
-  cc1101.disableAddressCheck(); //if not specified, will only display "packet received"
-  //cc1101.setTxPowerAmp(PA_LowPower);
-
-  Serial.print("CC1101_PARTNUM "); //cc1101=0
-  Serial.println(cc1101.readReg(CC1101_PARTNUM, CC1101_STATUS_REGISTER));
-  Serial.print("CC1101_VERSION "); //cc1101=4
-  Serial.println(cc1101.readReg(CC1101_VERSION, CC1101_STATUS_REGISTER));
-  Serial.print("CC1101_MARCSTATE ");
-  Serial.println(cc1101.readReg(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & 0x1f);
-
-  cc1101.setRxState(); //ADDED BY ME
-
-  attachInterrupt(0, cc1101signalsInterrupt, FALLING);
-
-  Serial.println("device initialized");
-}
-
-void ReadLQI()
-{
-  byte lqi = 0;
-  byte value = 0;
-  lqi = (cc1101.readReg(CC1101_LQI, CC1101_STATUS_REGISTER));
-  value = 0x3F - (lqi & 0x3F);
-  Serial.print("CC1101_LQI ");
-  Serial.println(value);
-}
-
-void ReadRSSI()
-{
-  byte rssi = 0;
-  byte value = 0;
-
-  rssi = (cc1101.readReg(CC1101_RSSI, CC1101_STATUS_REGISTER));
-
-  if (rssi >= 128)
-  {
-    value = 255 - rssi;
-    value /= 2;
-    value += 74;
-  }
-  else
-  {
-    value = rssi / 2;
-    value += 74;
-  }
-  Serial.print("CC1101_RSSI ");
-  Serial.println(value);
-}
-
-void loop()
-{
-  if (packetAvailable) {
-    Serial.println("packet received");
-    // Disable wireless reception interrupt
-    detachInterrupt(0);
-
-    ReadRSSI();
-    ReadLQI();
-    // clear the flag
-    packetAvailable = false;
-
-    CCPACKET packet;
-
-    if (cc1101.receiveData(&packet) > 0) {
-      if (!packet.crc_ok) {
-        Serial.println("crc not ok");
-      }
-
-      if (packet.length > 0) {
-        Serial.print("packet: len ");
-        Serial.print(packet.length);
-        Serial.print(" data: ");
-        for (int j = 0; j < packet.length; j++) {
-          Serial.print(packet.data[j], HEX);
-          Serial.print(" ");
-        }
-        Serial.println(".");
-      }
-    }
-    // Enable wireless reception interrupt
-    attachInterrupt(0, cc1101signalsInterrupt, FALLING);
-  }
-}


Update firmware to give up on DHT22 after 100 loops

--- firmware/firmware.ino
+++ firmware/firmware.ino
@@ -245,8 +245,12 @@

 void read_dht22() {
   DHT.acquire();
-  while (DHT.acquiring());
-
+  int cnt=0;
+  while (DHT.acquiring()) {
+    if(cnt>100) break;
+    delay(30);
+    cnt++;
+  }
   reading.humidity = DHT.getHumidity();
   reading.temperature = DHT.getCelsius();
 }


Fix motor

Fix motor

--- Car_Combined/Car_Combined.ino
+++ Car_Combined/Car_Combined.ino
@@ -2,14 +2,16 @@
 #include "RF24.h"
 #include "Motor.h"

-Motor motor(0,2,1);
-Motor motor1(3,5,4);
+Motor motor(2,4,3);
+Motor motor1(5,10,6);
 RF24 radio(7, 8);

 const int PACKETSIZE = 6;
 byte packet[PACKETSIZE];
 int radioChannel = 69;
 byte address[6] = {"12345"};
+
+int test = 0;

 void setup() {
   Serial.begin(115200);
@@ -21,6 +23,36 @@
 }

 void loop() {
+
+  if (test == 0) {
+    motor.run(255);
+    motor1.run(255);
+    Serial.println("1");
+    delay(3000);
+    motor.run(0);
+    motor1.run(255);
+   Serial.println("2");
+    delay(3000);
+    motor.run(255);
+    motor1.run(0);
+    Serial.println("3");
+    delay(3000);
+    motor.run(-255);
+    motor1.run(-255);
+    Serial.println("4");
+    delay(3000);
+    motor.run(0);
+    motor1.run(-255);
+    Serial.println("5");
+    delay(3000);
+    motor.run(-255);
+    motor1.run(0);
+    Serial.println("1");
+    delay(3000);
+    motor.run(0);
+    motor1.run(0);
+    test = 1;
+  }

   if (radio.available()) {
     int *intPacket = (int *)packet;


print alarm settings in one line

print alarm settings in one line

--- vibrate_alarm_clock.ino
+++ vibrate_alarm_clock.ino
@@ -370,38 +370,33 @@

     String alarmTimeString;
     if(currentState == ALARM && !blinkOn){
-      alarmTimeString = "      " + alarmHourString + ":" + alarmMinuteString;
+      alarmTimeString = "              ";
     } else {
-      alarmTimeString = "Alarm " + alarmHourString + ":" + alarmMinuteString;
-    }
-
-
-
-    display.setCursor(0,49);
-    display.println(alarmTimeString);
-
-
-    String alarmSetting;
-
+       String alarmSetting;
+
     if(currentState == SETTING_ALARM && settingAlarmProcess == A_TYPE && !blinkOn){
      //Now setting this, do not show
      alarmSetting = "";
     } else {
-      if(alarmVibrate && alarmSound){
-          alarmSetting = "(Sound + Vibrate)";
+     if(alarmVibrate && alarmSound){
+          alarmSetting = " Ring+Vib";
       } else if(alarmVibrate){
-          alarmSetting = "(Vibrate)";
+          alarmSetting = " Vibrate";
       } else if(alarmSound){
-          alarmSetting = "(Sound)";
+          alarmSetting = " Ring";
       } else {
-        alarmSetting = "(Off)";
+        alarmSetting = " Alarm Off";
       }
-    }
-
-
-    display.println(alarmSetting);
-
-
+     }
+
+      alarmTimeString = alarmHourString + ":" + alarmMinuteString + alarmSetting;
+    }
+
+
+
+    display.setCursor(0,49);
+    display.println(alarmTimeString);
+
 }

 void writeDateTimeToDisplayBuffer(DateTime now, boolean blinkOn){


The Sunrise feature looks to be working now.  I'd like to use logistic growth for the brightness though, and I'm working on adding that now.

--- SunriseAlarm.ino
+++ SunriseAlarm.ino
@@ -206,9 +206,9 @@
 ClockTime alarmTime(6,00);
 ClockTime startTime(5,30);

-const int alarm_address = 0;
-const int sundown_address = 2;
-const int brightness_address = 3;
+const int alarm_address = 4;
+const int sundown_address = 6;
+const int brightness_address = 7;

 Adafruit_7segment matrix = Adafruit_7segment();
 int matrixBrightness = 0;
@@ -324,13 +324,35 @@
 }

-//uint8_t targetColor[3] = {255, 147, 41}; // candle
-//uint8_t targetColor[3] = {255, 255, 255}; // sun
-//uint8_t targetColor[3] = {255, 0, 0}; // red
-uint8_t targetColor[3] = {50, 0, 0}; // testing
+const uint8_t candleLight[3] = {255, 147, 41};
+const uint8_t sunLight[3] = {255, 255, 255};
+const uint8_t testingLight[3] = {50, 0, 0};
+uint8_t targetColor[3] = {255, 0, 0};
+
 unsigned long millisecondsIn30Minutes = 1800000;
 uint8_t minRGBLevel = computeMinRGBLevel(targetColor);
 uint16_t totalDimmerSteps = minRGBLevel*NUM_LED;
+
+void setColorForSunDown()
+{
+  for (uint8_t i=0 ; i<3 ; ++i)
+  {
+    targetColor[i] = candleLight[i];
+  }
+  minRGBLevel = computeMinRGBLevel(targetColor);
+  totalDimmerSteps = minRGBLevel*NUM_LED;
+}
+
+void setColorForSunRise()
+{
+  for (uint8_t i=0 ; i<3 ; ++i)
+  {
+    targetColor[i] = sunLight[i];
+  }
+  minRGBLevel = computeMinRGBLevel(targetColor);
+  totalDimmerSteps = minRGBLevel*NUM_LED;
+}
+
 uint16_t currentDimmerStep = 0;
 uint8_t baseRGBColor[3] = {0u, 0u, 0u};
 uint8_t highRGBColor[3] = {0u, 0u, 0u};
@@ -550,44 +572,37 @@
   return( s );
 }

-
-const unsigned lightUpdateInterval = 1000;
+unsigned long alarmStartMillis = 0;
+const unsigned lightUpdateInterval = 200;
 unsigned long lastLightUpdate = millis();
 void updateLight() {
   if (!isTimeNow(lastLightUpdate,lightUpdateInterval)) {
     return;
   }
-  //Serial.println("updateLight");
   ClockTime current = current_clock_time;
   int32_t seconds = secondsBtwDates(current,startTime);
-  //Serial.print("  seconds = ");
-  //Serial.println(seconds);
   if ((seconds < 0) || (seconds >= maxTime)) {
-    //Serial.println("  outside alarm zone");
     if (alarmActive) {
-      //Serial.println("  turning off light and setting alarmActive = false");
       turnLightOff();
       alarmActive = false;
     }
-    //Serial.println("  turning on alarmEnabled");
     alarmEnabled = true;
     return;
   }
-  //Serial.println("inside alarm zone");
   if (!alarmEnabled) {
-    //Serial.println("but alarmEnabled is false");
     return;
   }
-  //Serial.println("turning on alarmActive");
-  alarmActive = true;
+  if (!alarmActive)
+  {
+    setColorForSunRise();
+    alarmStartMillis = millis();
+    alarmActive = true;
+  }
   if (seconds >= thirtyminutes) {
-    //Serial.println("past alarm time but still in alarm zone turning light on full and returning");
-    //lastAlarmLightLevel = maxLightLevel;
-    //digitalWrite(LEDpinA,HIGH);
+    linearBrightOfStep(totalDimmerSteps);
     return;
   }
-  //Serial.println("in alarm zone during ramp-up, calling linearBright");
-  linearBrightOfMilliseconds(seconds);
+  linearBrightOfMilliseconds(millis()-alarmStartMillis);
 }

@@ -670,32 +685,8 @@
   updateTimeDisplay(current_clock_time,false,false);
   writeAlarmTimeToEEPROM();
   digitalWrite(AlarmLED,LOW);
-}
-
-void forwardSunSet(int & minutes) {
-//  while (digitalRead(FwdButton)==LOW) {
-//    ++minutes;
-//    matrix.print(minutes);
-//    matrix.writeDisplay();
-//    delay(forward_delay);
-//  }
-}
-
-void rewindSunSet(int & minutes) {
-// while (digitalRead(RewButton)==LOW) {
-//    --minutes;
-//    if (minutes < 0) {
-//      minutes = 0;
-//    }
-//    matrix.print(minutes);
-//    if (minutes == 0) {
-//      matrix.writeDigitNum(4,0);
-//    }
-//    matrix.writeDisplay();
-//    delay(rewind_delay);
-//  }
-}
-
+  alarmEnabled = true;
+}

 unsigned long millisAtStartOfSunSet = 0;
 bool sunSetActive = false;
@@ -794,11 +785,12 @@
       waitForButtonDepress(OffButton, HIGH);
     }
     else {
+      setColorForSunDown();
       setSunSet();
     }
   }
   updateBrightness();
-//  updateLight();
+  updateLight();
   updateCurrentTime();
   updateClockTime();
   updateSunSet();


Joep learning how to git like a boss

Joep learning how to git like a boss

--- arduino/Led_Strip/Led_Strip.ino
+++ arduino/Led_Strip/Led_Strip.ino
@@ -2,6 +2,8 @@

 // total led's in series
 #define NUM_LEDS 4
+
+// Hallo I am Joep

 // define updates per second
 #define updatesPerSecond 200


Added comments for EsploraPong

Added comments for EsploraPong

--- libraries/Esplora/examples/Experts/EsploraPong/EsploraPong.ino
+++ libraries/Esplora/examples/Experts/EsploraPong/EsploraPong.ino
@@ -22,22 +22,23 @@
 #include <Esplora.h>

 void setup() {
-  Serial.begin(9600);
+  Serial.begin(9600);     // initialize serial communication
 }

 void loop() {
+  // read the slider and three of the buttons
   int slider = Esplora.readSlider();
-  int resetButton = Esplora.readButton(1);
-  int serveButton = Esplora.readButton(3);
-  int switchPlayerButton = Esplora.readButton(4);
+  int resetButton = Esplora.readButton(SWITCH_1);
+  int serveButton = Esplora.readButton(SWITCH_3);
+  int switchPlayerButton = Esplora.readButton(SWITCH_4);

-  Serial.print(slider);
-  Serial.print(",");
-  Serial.print(resetButton);
-  Serial.print(",");
-  Serial.print(serveButton);
-  Serial.print(",");
-  Serial.println(switchPlayerButton);
-  delay(10);
+  Serial.print(slider);                // print the slider value
+  Serial.print(",");                   // add a comma
+  Serial.print(resetButton);           // print the reset button value
+  Serial.print(",");                   // add another comma
+  Serial.print(serveButton);           // print the serve button value
+  Serial.print(",");                   // add another comma
+  Serial.println(switchPlayerButton);  // print the last button with a newline
+  delay(10);                           // delay before sending the next set
 }


Increace debounce delay

Increace debounce delay

--- Sakuya_TiLDA/Sakuya_TiLDA.ino
+++ Sakuya_TiLDA/Sakuya_TiLDA.ino
@@ -26,6 +26,9 @@

   // Set button callback
   tilda.buttons.setStateCycleCallback(&button_handler);
+
+  // The tilda buttons can be a bit weird depending on how you touch them
+  tilda.buttons.setDebounceDelay(250);
 }

 void loop()


We fixed the bug. We now can connect input pins in the front end and see the values propagate to the outputs.

--- ServerSketch/ServerSketch.ino
+++ ServerSketch/ServerSketch.ino
@@ -680,12 +680,14 @@
       }
     }
   }
-
-  if( 1 == iConnCount ) // No other connections found besides itself
-    fPinValSum = g_aPins[_iPinNum].value;
-
+
   if(fPinValSum > 1.0)
     fPinValSum = 1.0;
+
+  if( 1 == iConnCount ) // No other connections found besides itself
+    fPinValSum = g_aPins[_iPinNum].value;
+  else
+    g_aPins[_iPinNum].value = fPinValSum;

   Serial.print("Total Value: "); Serial.println(fPinValSum);

@@ -716,6 +718,8 @@

   if( 1 == iConnCount ) // No other connections found besides itself
     iRetValue = g_aPins[_iPinNum].value;
+  else
+   g_aPins[_iPinNum].value = iRetValue;

   return iRetValue;
 }


Corrección en comentario

Corrección en comentario

--- arduino/Alarma/Alarma.ino
+++ arduino/Alarma/Alarma.ino
@@ -233,7 +233,7 @@
 }

 //Este método es llamado cuando la alarma se dispara
-// y hace que las luces de la casa destellen con semiperiodo de 120ms durante 1 minuto
+// y hace que las luces de la casa destellen con semiperiodo de 250ms durante 1 minuto
 void alarm() {
   long time = 0;
   while (time < 120) {


enable guide laser while inactive, new serial message format

--- arduino/scetch/scetch.ino
+++ arduino/scetch/scetch.ino
@@ -5,8 +5,9 @@
 const byte prrPin = 31;
 const byte buttonPin = A0;
 const byte ledPin = 13;
+const byte guideLaserPin = 29;

-const byte powerLevel = 50;
+const byte powerLevel = 20;

 const byte auxToPowerMillis = 100;
 const byte powerToLatchMillis = 100;
@@ -18,6 +19,7 @@
 const char enableKey = 'e';
 const char disableKey = 'd';
 const char powerKey = 'p';
+const char powerStatusKey = 's';

 boolean laserEnabled = false;
 boolean inAfterStartState = true;
@@ -32,6 +34,8 @@
   pinMode(prrPin, OUTPUT);
   pinMode(buttonPin, INPUT);
   pinMode(ledPin, OUTPUT);
+  pinMode(guideLaserPin, OUTPUT);
+  digitalWrite(guideLaserPin, HIGH);
   Serial.begin(9600);
 }

@@ -51,13 +55,17 @@
   int len = readline(Serial.read(), buffer, 80);
   if (len > 0) {
     String message = buffer;
-    if (message.charAt(0) == enableKey && laserEnabled == false) {
-      enableLaser();
-      submitPower((byte)message.charAt(1));
-    } else if (message.charAt(0) == disableKey && laserEnabled == true) {
-      disableLaser();
-    } else if (message.charAt(0) == powerKey && laserEnabled == true) {
-      submitPower((byte)message.charAt(1));
+    if (message.charAt(0) == powerKey && laserEnabled == true) {
+      String powerLvlString = String(message);
+      powerLvlString.remove(0);
+      int lvl = powerLvlString.toInt();
+      if (lvl < 0 && lvl > 100) {
+        Serial.println("Error: Invalid power level, should be greater than 0 and smaller than 100");
+      } else {
+        submitPower((byte)message.charAt(1));
+      }
+    } else if (message.charAt(0) == powerStatusKey) {
+      Serial.println(powerLevel, DEC);
     }
   }
 }
@@ -71,10 +79,12 @@
   digitalWrite(prrPin, LOW);
   laserEnabled = false;
   digitalWrite(ledPin, LOW);
+  digitalWrite(guideLaserPin, HIGH);
   Serial.println(disableKey);
 }

 void enableLaser() {
+  digitalWrite(guideLaserPin, LOW);
   digitalWrite(prrPin, HIGH);
   submitPower(powerLevel);
   delay(periodMillis);
@@ -120,3 +130,4 @@
   // No end of line has been found, so return -1.
   return -1;
 }
+


changed balls in arduino

changed balls in arduino

--- turtleCode/arduino/final/LCD12864_test/LCD12864_test.ino
+++ turtleCode/arduino/final/LCD12864_test/LCD12864_test.ino
@@ -129,14 +129,14 @@

 void checkInput(int input, int answer){
   if (input == answer){
-    Serial.println("yippie");
+    Serial.print(input);
     closeMouth();
     delay(3000);
 //    openMouth();
     Serial.flush();
   }
   else{
-    Serial.println("aaaaaah that is wrong u newb!");
+    Serial.print("w");
   }
 }

@@ -234,13 +234,6 @@
 void loop(){

   if(Serial.available()==4){
-      digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
-  delay(1000);               // wait for a second
-  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
-  delay(1000);               // wait for a second
-  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
-
-  Serial.println("the cow has landed");
     byte b1,b2,b3,b4;
     b1=Serial.read();
     b2=Serial.read();


Do not download this. Download a release

--- TinyScreenClockAndDisplay.ino
+++ TinyScreenClockAndDisplay.ino
@@ -27,25 +27,16 @@
 int buttonReleased=0;
 int countdown = 0;

-char notificationLine1[20]="Release 2.5.1";
+char notificationLine1[20]="Sunday";

-void setup(void) { // setup
+void setup(void) {
   Wire.begin();
   display.begin();
   display.setFlip(1);
-  display.setBrightness(4);
+  display.setBrightness(5);
+  Serial.begin(9600);
   RTC.start();
-  display.fontColor(GREEN,BLACK);
-  display.setFont(liberationSans_16ptFontInfo);
-  display.setCursor(0,10);
-  display.print("Welcome");
-  display.setCursor(0,40);
-  display.print("Booting up");
-  displayState = 1;
-  display.on();
-  countdown = 90;
-  delay(2000);
-  display.clearWindow(0,0,0,0);
+  startup();
 }

 void loop() {
@@ -71,6 +62,8 @@
   unsigned char month;
   unsigned char day;
   unsigned int year;
+  unsigned int weakday;
+  char* dayOfWeekName[7];
   display.fontColor(BLACK,BLACK);
   RTC.readTime();
         second = RTC.getSeconds();
@@ -79,8 +72,14 @@
         day = RTC.getDays();
         month = RTC.getMonths();
         year = RTC.getYears();
-  display.setFont(liberationSans_10ptFontInfo);
-  // Date
+        weakday = RTC.getDayOfWeek();
+        dayOfWeekName[] = { "Sunday","Monday","Tuesday","Wednesday",
+            "Thursday","Friday","Saturday"};
+
+
+day = dayOfWeekName[weekday];
+  display.setFont(liberationSans_10ptFontInfo);
+// Date
   display.setCursor(0,10);
   display.fontColor(BLUE,BLACK);
   delay(10);
@@ -89,40 +88,23 @@
   display.print(day);
   display.print("/");
   display.print(year);
-
-  // Time
+// Time
   display.setCursor(0,30);
-  display.fontColor(YELLOW,BLACK);
-  int hours = RTC.getHours();
-  if (hours > 12)
-{
-  hours = hours - 12;
-}
-display.print(hours);
+  if (hour > 12) {
+    hour = hour - 12;
+  }
+  display.print(hour);
   display.print(":");
-
-  if (RTC.getMinutes() < 10)
-   {
-     display.print("0");
-     display.print(minute);
-   }
-   else
-   {
-     display.print(minute);
-   }
-
-
-  display.print(":");
-
-   if (RTC.getSeconds() < 10)
-   {
-     display.print("0");
-     display.print(second);
-   }
-   else
-   {
-     display.print(second);
-   }
+  if (minute < 10) {
+    display.print("0");
+  }
+  display.print(minute);
+  if (hour > 12) {
+  display.print(" AM");
+  }
+  else {
+    display.print(" PM");
+  }
 }
 void displayNotifications(){
   display.setFont(liberationSans_10ptFontInfo);
@@ -160,3 +142,17 @@
     display.drawLine(x+i,y,x+i,y+height,red,green,blue);
   }
 }
+void startup()
+{
+    display.fontColor(GREEN,BLACK);
+  display.setFont(liberationSans_16ptFontInfo);
+  display.setCursor(0,10);
+  display.print("Welcome");
+  display.setCursor(0,40);
+  display.print("Booting up");
+  displayState = 1;
+  display.on();
+  countdown = 90;
+  delay(2000);
+  display.clearWindow(0,0,0,0);
+}


using interrupts to get IMU data

using interrupts to get IMU data

--- car/imu.ino
+++ car/imu.ino
@@ -3,10 +3,11 @@

 // an MPU9250 object with its I2C address
 // of 0x68 (ADDR to GRND) and on Teensy 3.5 bus 0
-MPU9250 IMU(0x68, 0, I2C_PINS_18_19, I2C_PULLUP_EXT);
+static MPU9250 IMU(0x68, 0, I2C_PINS_18_19, I2C_PULLUP_EXT);

-float q[4] = {1.0f, 0.0f, 0.0f, 0.0f};
-float d[10];
+static float q[4] = {1.0f, 0.0f, 0.0f, 0.0f};
+static volatile float d[10];
+static volatile int count = 0;

 static int imuInterval = IMU_INTERVAL;

@@ -34,6 +35,14 @@
     };

     IMU.setBias(b);
+
+    status = IMU.setFilter(DLPF_BANDWIDTH_184HZ, 9);
+    if (status < 0)
+    {
+        setupFail("IMU INT unsuccessful");
+    }
+    pinMode(IMU_INT_PIN, INPUT);
+    attachInterrupt(IMU_INT_PIN, getIMU, RISING);
 }

 void imuEeprom(int action, const unsigned long time)
@@ -41,18 +50,28 @@
     eepromRW(EEPROM_IMU_INTERVAL, IMU_INTERVAL, imuInterval, action, time);
 }

+void getIMU()
+{
+    // get the accel (m/s/s), gyro (rad/s), and magnetometer (uT), and temperature (C) data
+    IMU.getMotion10(d);
+    count++;
+}
+
 void imuLoop(unsigned long time, int mode)
 {
     static unsigned long next;
+    float d2[10];

     // is it this time to read IMU
-    if (next < time)
+    if (next < time )
     {
         // set up next ping time
-        next = time + imuInterval;
+        //next = time + imuInterval;
+        next = time + 2;

         // get the accel (m/s/s), gyro (rad/s), and magnetometer (uT), and temperature (C) data
-        IMU.getMotion10Unbiased(d);
+        //IMU.getMotion10Unbiased(d);
+

         // Sensors x (y)-axis of the accelerometer/gyro is aligned with the y (x)-axis of the magnetometer;
         // the magnetometer z-axis (+ down) is misaligned with z-axis (+ up) of accelerometer and gyro!
@@ -63,29 +82,39 @@
         // function to get North along the accel +x-axis, East along the accel -y-axis, and Down along the accel -z-axis.
         // This orientation choice can be modified to allow any convenient (non-NED) orientation convention.
         // Pass gyro rate as rad/sx
-        MadgwickQuaternionUpdate(q, d, IMU.delta);
+        //MadgwickQuaternionUpdate(q, d, IMU.delta);

         // print the data
+        if (count > 0)
         if (mode == MODE_IMU || mode == MODE_RUN || mode == MODE_COUNTDOWN || mode == MODE_RUNOFF)
         {
-            printData(time);
+            cli();
+            int c = count;
+            for (int i = 0; i < 10; i++)
+               d2[i] = d[i];
+            count = 0;
+            sei();
+            printData(time, d2, c);
         }
     }
 }

-void printData(unsigned long time)
-{
+void printData(unsigned long time, const float* data, const int cnt)
+{   /*
     float roll;
     float pitch;
     float yaw;

-    toEulerianAngle(q, roll, pitch, yaw);
+    toEulerianAngle(q, roll, pitch, yaw);*/

-    info(time, "IMU a %f %f %f g %f %f %f m %f %f %f q %f %f %f %f roll %f pitch %f yaw %f d %f\n",
-        d[0], d[1], d[2], // accel
-        d[3], d[4], d[5], // gyro
-        d[6], d[7], d[8], // magnetometer
-        q[0], q[1], q[2], q[3], // quaternion
-        roll, pitch, yaw,
-        IMU.delta);
+//    info(time, "IMU a %f %f %f g %f %f %f m %f %f %f q %f %f %f %f roll %f pitch %f yaw %f d %f\n",
+    info(time, "IMU a %f %f %f g %f %f %f m %f %f %f c %i\n",
+        data[0], data[1], data[2], // accel
+        data[3], data[4], data[5], // gyro
+        data[6], data[7], data[8], // magnetometer
+        //q[0], q[1], q[2], q[3], // quaternion
+        //roll, pitch, yaw,
+        //IMU.delta
+        cnt
+        );
 }


format

format

--- examples/Boards_USB_Serial/Digistump_Digispark/Digistump_Digispark.ino
+++ examples/Boards_USB_Serial/Digistump_Digispark/Digistump_Digispark.ino
@@ -23,8 +23,8 @@

 // Minimize size for non-Pro version
 #if !defined(ARDUINO_AVR_DIGISPARKPRO)
-  #define BLYNK_NO_INFO
-  #define BLYNK_NO_BUILTIN
+#define BLYNK_NO_INFO
+#define BLYNK_NO_BUILTIN
 #endif

 #include <BlynkSimpleStream.h>


making save prompt 40 col friendly

making save prompt 40 col friendly

git-svn-id: d8bfcd070c2c81762cca1b3912331324182919ba@21139 0d6f1817-ed0e-0410-87c9-987e46238f29

--- zimodem/zconfigmode.ino
+++ zimodem/zconfigmode.ino
@@ -825,7 +825,7 @@
       }
       case ZCFGMENU_WICONFIRM:
       {
-        serial.printf("%sYour setting changed.  Save them (y/N)?",EOLNC);
+        serial.printf("%sYour setting changed. Save (y/N)?",EOLNC);
         break;
       }
     }


Raindanse because it's working <3

Raindanse because it's working <3

--- WeatherBuddy/WeatherBuddy.ino
+++ WeatherBuddy/WeatherBuddy.ino
@@ -1,48 +1,46 @@
-const int pinSwitch = 12;  //Pin Reed
-const int pinLed    = 10;  //Pin LED
-int REGENSENSOR = A1; // Regensensor aan analoge ingang 1
-int LED = 9; // LED aan pen 8 gaat aan als het sneeuwt of refent
+// Code Bas,

-int val = 0;
-int data = 0;
+int pinSwitch = 12;  //Pin Reed
+int regenSensor = A1; // Regensensor aan analoge ingang 1, als regensor dan gaat led 9 (rood) branden
+int LED = 9; // LED aan pen 9 gaat aan als het sneeuwt of refent
+int reedLed = 10; // LED a
+int val = 0;  // = dus de regensensorwaarde
+int StatoSwitch = 0;

-
-int StatoSwitch = 0;

 void setup()
 {
-  Serial.begin(9600);
-  pinMode(pinLed, OUTPUT);
+  Serial.begin(115200);//Serial.begin(9600);
+  pinMode(reedLed, OUTPUT);
   pinMode(pinSwitch, INPUT);
   pinMode(LED, OUTPUT);

 }
 void loop() {
-  val = analogRead(REGENSENSOR);
+  Serial.print("hoi");
+  val = analogRead(regenSensor);
   if (val > 700) {
     digitalWrite(LED, LOW);
   }
   else {
     digitalWrite(LED, HIGH);
   }
-  data = val;
   Serial.print("Neerslaghoeveelheid: " );
-  Serial.print(data);
+  Serial.print(val);
   delay(400);

-
-
+
   StatoSwitch = digitalRead(pinSwitch);
   if (StatoSwitch == HIGH)
   {
-    Serial.println("Magnet near Reed Switch detected!");
-    digitalWrite(pinLed, HIGH);
+    Serial.println(" Magnet near Reed Switch detected! ");
+    digitalWrite(reedLed, HIGH);
   }
   else
   {
-    Serial.println("No Magnet Detected");
-    digitalWrite(pinLed, LOW);
+    Serial.println(" No Magnet Detected ");
+    digitalWrite(reedLed, LOW);
   }

 }


increase improve sensitivity

increase improve sensitivity

--- button_multichannel_rf6/button_multichannel_rf6.ino
+++ button_multichannel_rf6/button_multichannel_rf6.ino
@@ -314,7 +314,7 @@
 //    }
     // digitalWrite(elEnable, LOW);
     PORTF = PORTF & B11011111;
-    for (int i = 0; i < 20; i++) {
+    for (int i = 0; i < 30; i++) {
       switchHalfOutputsA();
       spinner = 0;
       while(spinner < 34) {
@@ -450,7 +450,8 @@
   }

   // Change threshold for activating, might want to make this smaller?
-  if (inactiveMeasure[activeProbe] - senseResults_internal[activeProbe] > 100) {
+  if (inactiveMeasure[activeProbe] - senseResults_internal[activeProbe] > 50 ||
+      senseResults_internal[activeProbe] < 400) {
     isSense_active[activeProbe] = true;
   }
   else {
@@ -618,12 +619,12 @@
 int chanChangeCount;

 // Here are the main levers which control the operation of this program
-#define increaseWindow 13
+#define increaseWindow 17
 #define pureIncreaseThreshold 5
 #define increaseThresholdWithImpulse 3
-#define impulseThreshold 4
-#define bigImpulseThreshold 7
-#define glowTime_us 1000
+#define impulseThreshold 3
+#define bigImpulseThreshold 8
+#define glowTime_us 1300

 void loop() {
     senseResults = senseAllActive(1000, true);