Update Keypad.ino

Update Keypad.ino

--- ComboKeypad/Keypad.ino
+++ ComboKeypad/Keypad.ino
@@ -108,7 +108,7 @@
   }
 }

-}/*----(Function Loop end)----*/
+}/*----(Function Loop END)----*/

 //////////////////////////////////////////////////////

@@ -136,7 +136,7 @@
          doonce = false;
           delay(50);
         }
-}/*----(Function pressButton end)----*/
+}/*----(Function pressButton END)----*/

 // Functions "Reset"
 void reset() {
@@ -149,7 +149,7 @@
       digitalWrite(wrongLight, 0);   //Turns the "Red" light off
        count = 0;  //Sets the counter to 0

-}/*----(Function Reset end)----*/
+}/*----(Function Reset END)----*/

 //Function Debug, Prints the count, and what has been pressed
 void debug(){


arduino.ino: Send true or false on light sensor in hold

--- board/arduino/arduino.ino
+++ board/arduino/arduino.ino
@@ -9,8 +9,10 @@
 /* General global value definitions */
 #define ARDUINO_ID 201
 #define C_ID 200
-#define BUFFER_DATA_LENGTH 5
+#define BUFFER_DATA_LENGTH 1
 #define BAUDRATE 9600
+#define LED 2
+#define LIGHT_SENSOR A0

 long updateTime = 0;

@@ -19,6 +21,8 @@
   /* Begin communication over Serial Port with a baud
      rate of 9600*/
   Serial.begin(BAUDRATE);
+  pinMode(LED, OUTPUT);
+  digitalWrite(LED, HIGH);
 }

 /* Main Arduino loop */
@@ -36,6 +40,7 @@
   if (!(curMillis - updateTime >= updateMilliThreshold)) {
     /* Prevent writing to serial to avoid serial port data overflow */
     writeToSerial = false;
+
   }

   /* Wait for 6 bytes of data to have been sent over serial */
@@ -63,13 +68,12 @@

   /* When it is possible to reply to serial */
   if (writeToSerial) {
+    updateTime = curMillis;
     /* Identification byte */
     Serial.write(ARDUINO_ID);

-    for (int i = 0; i < BUFFER_DATA_LENGTH; i++) {
-        Serial.write((uint8_t) 0);
-
-        // TODO: Instead of writing zero, write something else
-      }
+    //Serial.println(analogRead(LIGHT_SENSOR) > 800);
+    Serial.write(analogRead(LIGHT_SENSOR) > 800 );
+
   }
 }


Fix Date being corrupted when next day comes.

--- Caravan_Thermostat_TX/Caravan_Thermostat_TX.ino
+++ Caravan_Thermostat_TX/Caravan_Thermostat_TX.ino
@@ -37,7 +37,7 @@
 dht DHT;
 IRrecv irrecv(IRPIN);
 decode_results results;
-SoftwareSerial BT(BLERXPIN, BLETXPIN); // RX, TX
+SoftwareSerial BT(BLERXPIN, BLETXPIN); // RX, TX

 // Define Global Variables

@@ -211,6 +211,7 @@
   }
   if (PreviousDate != CurrentDate())
   {
+    TFTscreen.fillRect(TimeStartPointX + 75,  TimeStartPointY + 5, TFTscreen.width(), 23, Black);
     TFTscreen.setTextSize(1);
     TFTscreen.text(CurrentDate().c_str(), TimeStartPointX + 75, TimeStartPointY + 5);
     PreviousDate = CurrentDate();


Set TX/RX mode for transciever when sending

--- button_box/button_box.ino
+++ button_box/button_box.ino
@@ -1,11 +1,20 @@
-#define WHITE 5
-#define RED 6
+#include <SoftwareSerial.h>
+#define SSerialRX        4  //Serial Receive pin
+#define SSerialTX        3  //Serial Transmit pin
+#define SSerialTxControl 5
+SoftwareSerial RS485(SSerialRX, SSerialTX); // RX, TX
+#define RS485Transmit    HIGH
+#define RS485Receive     LOW
+
+#define WHITE 1
+#define RED 0
 #define ID0 12
 #define ID1 13
 #define BUTTON_WHITE 0
 #define BUTTON_RED 1

-uint8_t white = 0, red = 0, white_state = 0, red_state = 0;
+uint16_t white = 0, red = 0;
+uint8_t white_state = 0, red_state = 0;
 uint8_t id = 0;

@@ -15,6 +24,11 @@
 	pinMode(RED, INPUT);
 	pinMode(ID0, INPUT);
 	pinMode(ID1, INPUT);
+	pinMode(SSerialTxControl, OUTPUT);
+	digitalWrite(SSerialTxControl, RS485Receive);
+	RS485.begin(9600);   // set the data rate
+
+
 	id += digitalRead(ID0);
 	id += digitalRead(ID1);
 	Serial.println("[Button Box]");
@@ -24,22 +38,22 @@
 }

 void loop () {
-	white = digitalRead(WHITE);
-	red = digitalRead(RED);
-	if(white == LOW && white_state == 0) {
+	white = analogRead(WHITE);
+	red = analogRead(RED);
+	if(white < 500 && white_state == 0) {
 		white_state = 1;
 		txButton(BUTTON_WHITE);
 		delay(200);
 	}
-	else if(white == HIGH && white_state == 1) {
+	else if(white > 500 && white_state == 1) {
 		white_state = 0;
 	}
-	if(red == LOW && red_state == 0) {
+	if(red < 500 && red_state == 0) {
 		red_state = 1;
 		txButton(BUTTON_RED);
 		delay(200);
 	}
-	else if(red == HIGH && red_state == 1) {
+	else if(red > 500 && red_state == 1) {
 		red_state = 0;
 	}
 	delay(50);
@@ -49,4 +63,10 @@
 	Serial.print("#"); Serial.print(id); Serial.print(":");
 	Serial.print(button);
 	Serial.println(";");
+	digitalWrite(SSerialTxControl, RS485Transmit);
+	RS485.print("#"); RS485.print(id); RS485.print(":");
+	RS485.print(button);
+	RS485.println(";");
+	digitalWrite(SSerialTxControl, RS485Receive);
+	delay(50);
 }


updated docs

updated docs

--- gps_arduino/gps_arduino.ino
+++ gps_arduino/gps_arduino.ino
@@ -3,7 +3,8 @@
 // pins used
 //lcd  8, 9, 4, 5, 6, 7 A0
 //gps 3,4
-//mag a4 a5
+//mag A4 A5
+//Heading Adjust A2

 #include <LiquidCrystal.h>
 #include <SoftwareSerial.h>
@@ -111,6 +112,9 @@
 #define MODE_NAVIGATE 2
 #define NUMBER_OF_MODES 3
 int mMode = MODE_MENU;
+/* Time in millis that the naivagtion should update new data or not */
+#define REFRESH_RATE 1000
+unsigned long lastUpdate = 0;

 //Store Waypoints
 #define MAX_WAYPOINTS 20
@@ -176,6 +180,12 @@
       newData = true;
     }
     updateHeading();
+    //Force refresh after set amount of time
+    unsigned long currentMillis = millis();
+    if(currentMillis - lastUpdate > REFRESH_RATE){
+       lastUpdate = currentMillis;
+       newData = true;
+    }
     updateDisplay(newData);

     //Update heading offset


Accounted for negitive heading

Accounted for negitive heading

--- gps_arduino/gps_arduino.ino
+++ gps_arduino/gps_arduino.ino
@@ -180,7 +180,7 @@

     //Update heading offset
     mHeadingOffset = analogRead(HEADING_ADJUST_PIN);            // reads the value of the potentiometer (value between 0 and 1023)
-    mHeadingOffset = map(mHeadingOffset, 0, 1023, 90, -90);     // scale it to use it with the heading (value between 0 and 180)
+    mHeadingOffset = map(mHeadingOffset, 0, 1023, 180, -180);     // scale it to use it with the heading (value between 0 and 180)

   }

@@ -223,6 +223,8 @@
     compassHeading += mHeadingOffset;
     if(compassHeading >= 360){
       compassHeading -= 360;
+    } else if (compassHeading < 0){
+      compassHeading += 360;
     }
   }


Adjust the fade effects

Adjust the fade effects

--- ArduinoMotionStairLights.ino
+++ ArduinoMotionStairLights.ino
@@ -1,8 +1,9 @@
 /*
  * Description: Motion activated stair lights.
  * Author: Dean Montgomery
- * Version: 2.1
- * Date: Feb 8, 2016
+ * Version: 2.2
+ *
+ * Date: Feb 11, 2016
  *
  * 2 PIR sesors at the top and bottom of the stairs.
  * WS28012B Addressable RGB lights - 2 LEDs on each stair - This spread out the strip of 30 and left 2-pairs for spare bulbs.
@@ -27,7 +28,7 @@
 #define GO_UP -1                // Direction control - Arduino at top of stairs
 #define GO_DOWN 1               // Direction control - Arduino at top of stairs
 uint8_t gHue = 0;               // track color shifts.
-int8_t  gStair = 0;             // track curent stair.
+int8_t gStair = 0;             // track curent stair.
 uint8_t gBright = 0;            // track brightness
 uint8_t gUpDown[NUM_LEDS];      // directional array to walk/loop up or down stairs.
 int8_t gupDownDir = 1;
@@ -69,7 +70,8 @@
   delay (3000); // Power Up 3 second safety delay.
   //Serial.begin(57600);
   randomSeed(millis());
-  FastLED.addLeds<WS2812B, PIN_LED, GRB>(leds, NUM_LEDS);  // NOTE set LED string type here.
+  FastLED.addLeds<WS2812B, PIN_LED, GRB>(leds, NUM_LEDS);  // NOTE set LED string type here.
+  FastLED.setDither( 0 );  // Stops flikering in animations.
   pinMode(PIN_PIR_DOWN, INPUT);
   pinMode(PIN_PIR_UP, INPUT);
   pinMode(13, OUTPUT);
@@ -99,6 +101,9 @@
 void readSensors(){
   if ( digitalRead(PIN_PIR_UP) == HIGH ){  // Walk Down.
     previousOffMillis = currentMillis;
+    //if ( stage == stage_run ){
+    //  return;  //keep the animation fast.
+    //} else
     if ( stage == off ){
       chooseEffects();
       stage = stage_init;
@@ -121,6 +126,8 @@
 void chooseEffects(){
   randomSeed(millis());
   r = random8(1, 255);
+  //effect = efade6;
+  //return;
   if ( r >= 0 && r <= 100 ){
     effect = ewalk;  // My favorite transition with random effect variations
   } else if ( r > 100 && r <= 175 ){
@@ -446,32 +453,29 @@
   }
 }

-// Fade6 effect with each led: r,rb,b,bg,g,gr rgb(white)
+// Fade6 effect with each led using a hue shift
 void fade(){
   if ( stage == stage_init ){
     gBright = 0;
     gStair = 0;
-    x = 0;
-    interval = 7;
+    interval = 5;
+    h = 128;
+    s = 140;
+    v = BRIGHTNESS;
     r = 0;
-    h = 0;
-    s = 255;
-    v = BRIGHTNESS;
     g = ( random8() < 120 );
     stage = stage_grow;
   } else if ( stage == stage_grow ){
     if ( gBright<255 ){
       if ( gStair < NUM_LEDS ){
-        trans = blend(CRGB::Black,CHSV(h,s,v),gBright);
+        trans = blend(CHSV(h,s,0),CHSV(h,s,v),gBright);
         leds[gUpDown[gStair]] = trans;
         leds[gUpDown[gStair + 1]] = trans;
         gBright = qadd8(gBright, 1);
       } else {
         stage = stage_init_run;
-        interval = 50;
         gBright=0;
         gStair=0;
-        r = 0;
       }
       gBright = qadd8(gBright, 2);
     } else {
@@ -480,21 +484,17 @@
     }
   } else if ( stage == stage_init_run ) {
     v = BRIGHTNESS;
-    interval = 2;
+    interval = 70;
     stage = stage_run;
   } else if ( stage == stage_run ){
-    if ( gStair < NUM_LEDS ){
-      h+=1;
-      leds[gUpDown[gStair]] = CHSV(gStair + h, s, v);
-      //if (g) addGlitter(40);
-      gStair++;
-    } else {
-      gStair = 0;
-      h = h - NUM_LEDS + 1;
-    }
+    r = h;
+    for(gStair=0; gStair < NUM_LEDS; gStair++) {
+        h+=(3*gupDownDir); // left PIR go down
+        leds[gUpDown[gStair]] = CHSV(h, s, v);
+    }
+    h = r + (3*gupDownDir*-1);
   } else if ( stage == stage_init_dim ){
     interval = 7;
-    r = BRIGHTNESS;
     h = h - gStair;
     gStair = 0;
     stage = stage_dim;
@@ -503,7 +503,7 @@
       if ( gStair < NUM_LEDS ){
         leds[gUpDown[gStair]] = CHSV(gStair + h, s, v);
         leds[gUpDown[gStair + 1]]= CHSV(gStair + h, s, v);
-        v--;
+        v = qsub8(v, 1);
       } else {
         stage = off;
       }


updated LCD display

updated LCD display

--- mwsLCD/mwsLCD.ino
+++ mwsLCD/mwsLCD.ino
@@ -515,12 +515,12 @@
   Serial.print(gps.satellites.value());

   lcd.clear();
-  lcd.print("Longitude");
+  lcd.print("Longitude (dd.ddd)");
   lcd.setCursor(0, 2);
   lcd.print(gps.location.lng(), 6);
   delay(2000);
   lcd.clear();
-  lcd.print("Latitude");
+  lcd.print("Latitude (dd.ddd)");
   lcd.setCursor(0, 2);
   lcd.print(gps.location.lat(), 6);
   delay(2000);
@@ -555,11 +555,17 @@
   Serial.print(windspeedms, 1);

   lcd.clear();
-  lcd.print("Wind Direction");
+  lcd.print("Wind Direction (dd.ddd)");
   lcd.setCursor(0, 2);
-  lcd.print(sz);
+  lcd.print(winddir);
   delay(2000);

+  lcd.clear();
+  lcd.print("Wind Speed (m/s)");
+  lcd.setCursor(0, 2);
+  lcd.print(windspeedms);
+  delay(2000);
+


add log and IMU param

add log and IMU param

--- arduino/opencr_arduino/opencr/libraries/turtlebot3/turtlebot3_burger/turtlebot3_core/turtlebot3_core.ino
+++ arduino/opencr_arduino/opencr/libraries/turtlebot3/turtlebot3_burger/turtlebot3_core/turtlebot3_core.ino
@@ -174,6 +174,9 @@
 *******************************************************************************/
 void setup()
 {
+  nh.loginfo("Connected to OpenCR board!");
+  nh.loginfo("This core is compatible with TurtleBot3 Burger");
+
   // Initialize ROS node handle, advertise and subscribe the topics
   nh.initNode();
   nh.getHardware()->setBaud(115200);
@@ -187,8 +190,6 @@
   nh.advertise(mag_pub);
   tfbroadcaster.init(nh);

-  nh.loginfo("Connected to OpenCR board!");
-
   // Setting for Dynamixel motors
   motor_driver.init();

@@ -374,9 +375,9 @@
   tfs_msg.transform.rotation.y = imu.quat[2];
   tfs_msg.transform.rotation.z = imu.quat[3];

-  tfs_msg.transform.translation.x = -0.032;
-  tfs_msg.transform.translation.y = 0.0;
-  tfs_msg.transform.translation.z = 0.058;
+  tfs_msg.transform.translation.x = IMU_POS_X;
+  tfs_msg.transform.translation.y = IMU_POS_Y;
+  tfs_msg.transform.translation.z = IMU_POS_Z;

   tfbroadcaster.sendTransform(tfs_msg);
 }


example firmare for TI cc3200 launchpad button service

--- devices/cc3200_launchpad/example_energia_button/example_energia_button.ino
+++ devices/cc3200_launchpad/example_energia_button/example_energia_button.ino
@@ -10,6 +10,8 @@
 #include <WiFiClient.h>
 #include <aJSON.h>

+#define USE_UART
+
 #define DEFAULT_UDP_MEMBERSHIP  "225.0.0.37"
 #define DEFAULT_UDP_PORT  21928

@@ -18,10 +20,14 @@
 volatile int green_state = LOW;

 //access point should only work for b and g
-char ssid[] = "hellfire";
+char ssid[] = "nsynapse";
 char password[] = "elec6887";
+const char* uuid = "61ba1b0e47e34a98abb4b1d1bae10960";
 WiFiServer server(8000);
 WiFiUDP Udp;
+byte mac[6];
+String mac_string;
+

 int push1_state = LOW;
 int prev_push1_state = LOW;
@@ -48,7 +54,10 @@
   digitalWrite(YELLOW_LED, LOW);
   digitalWrite(RED_LED, LOW);

+#ifdef USE_UART
   Serial.begin(115200);
+#endif
+
   WiFi.begin(ssid, password);

   while(WiFi.status()!=WL_CONNECTED) {
@@ -61,22 +70,36 @@
     delay(300);
   }

+  WiFi.macAddress(mac);
+  for(int i=0;i<sizeof(mac);i++) {
+    mac_string += String(mac[sizeof(mac)-1-i],HEX);
+    if(i<(sizeof(mac)-1))
+      mac_string += ":";
+  }
+
   printWifiStatus();
+
   server.begin();
   Udp.begin(DEFAULT_UDP_PORT);

   //device profile
   if(announce) {
-    aJson.addStringToObject(announce, "devicename", "TI_cc3200_1");
-    aJson.addStringToObject(announce, "componentname", "cc3200button");
+    aJson.addStringToObject(announce, "devicename", "TI-CC3200");
+    aJson.addStringToObject(announce, "uuid", uuid);
+    aJson.addStringToObject(announce, "mac", mac_string.c_str());
+    aJson.addStringToObject(announce, "manufacturer", "Texas Instrument");
+    aJson.addStringToObject(announce, "author", "cossb");
+    aJson.addStringToObject(announce, "component", "cc3200button");
     aJson.addStringToObject(announce, "protocol", "tcp");
     aJson.addNumberToObject(announce, "port", 8000);
     aJson.addStringToObject(announce, "command","auth");
   }

   if(frame) {
-    aJson.addStringToObject(frame, "devicename", "TI_cc3200_1");
-    aJson.addStringToObject(frame, "componentname", "cc3200button");
+    aJson.addStringToObject(frame, "devicename", "TI-CC3200");
+    aJson.addStringToObject(frame, "uuid", uuid);
+    aJson.addStringToObject(frame, "mac", mac_string.c_str());
+    aJson.addStringToObject(frame, "component", "cc3200button");
     aJson.addStringToObject(frame, "command","buttonread");
     aJson.addBooleanToObject(frame, "button1", false);
     aJson.addBooleanToObject(frame, "button2", false);
@@ -118,7 +141,6 @@
     {
       WiFiClient client = server.available();
       if(client) {
-        Serial.println("new client");
         while(client.connected()) {
           digitalWrite(RED_LED, HIGH);

@@ -127,11 +149,9 @@

           if(push1_state!=prev_push1_state) {
             if(push1_state==HIGH) {
-              Serial.println("Push1 High");
               update(PUSH1, true);
             }
             else if(push1_state==LOW) {
-              Serial.println("Push1 Low");
               update(PUSH1, false);
             }
             client.print(aJson.print(frame));
@@ -141,23 +161,19 @@
           if(push2_state!=prev_push2_state)
           {
             if(push2_state==HIGH) {
-              Serial.println("Push2 High");
               update(PUSH2, true);
             }
             else if(push2_state==LOW) {
-              Serial.println("Push2 Low");
               update(PUSH2, false);
             }
             client.print(aJson.print(frame));
             delay(50);
           }
-
-

           prev_push1_state = push1_state;
           prev_push2_state = push2_state;
         }
-        Serial.println("connection closed");
+
         digitalWrite(RED_LED, LOW);
         client.stop();

@@ -173,7 +189,7 @@

 void printWifiStatus() {
-
+#ifdef USE_UART
   // print the SSID of the network you're attached to:
   Serial.print("SSID: ");
   Serial.println(WiFi.SSID());
@@ -188,7 +204,20 @@
   Serial.print("signal strength (RSSI):");
   Serial.print(rssi);
   Serial.println(" dBm");
-
+
+  Serial.print("MAC: ");
+  Serial.print(mac[5],HEX);
+  Serial.print(":");
+  Serial.print(mac[4],HEX);
+  Serial.print(":");
+  Serial.print(mac[3],HEX);
+  Serial.print(":");
+  Serial.print(mac[2],HEX);
+  Serial.print(":");
+  Serial.print(mac[1],HEX);
+  Serial.print(":");
+  Serial.println(mac[0],HEX);
+#endif
 }

 boolean endsWith(char* inString, char* compString) {


testing

testing

--- module/module_wifi.ino
+++ module/module_wifi.ino
@@ -35,6 +35,8 @@

     Serial.print(getStr);
     Serial.print(" ");
+    Serial.print(getStr.length());
+    Serial.print(" ");
     Serial.println(cmd);
     while (1) {
         while (WIFI.available()) {


add input type example

add input type example

--- examples/Super/OnDemandConfigPortal/OnDemandConfigPortal.ino
+++ examples/Super/OnDemandConfigPortal/OnDemandConfigPortal.ino
@@ -97,6 +97,7 @@
   WiFiManagerParameter custom_token("api_token", "api token", "", 16);
   WiFiManagerParameter custom_tokenb("invalid token", "invalid token", "", 0); // id is invalid, cannot contain spaces
   WiFiManagerParameter custom_ipaddress("input_ip", "input IP", "", 15,"pattern='\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}'"); // custom input attrs (ip mask)
+  WiFiManagerParameter custom_input_type("input_pwd", "input pass", "", 15,"type='password'"); // custom input attrs (ip mask)

   const char _customHtml_checkbox[] = "type=\"checkbox\"";
   WiFiManagerParameter custom_checkbox("my_checkbox", "My Checkbox", "T", 2, _customHtml_checkbox,WFM_LABEL_AFTER);
@@ -137,6 +138,7 @@
   wm.addParameter(&custom_tokenb);
   wm.addParameter(&custom_ipaddress);
   wm.addParameter(&custom_checkbox);
+  wm.addParameter(&custom_input_type);

   wm.addParameter(&custom_html_inputs);


explain thy self

explain thy self

--- arduino/garage_controller/garage_controller.ino
+++ arduino/garage_controller/garage_controller.ino
@@ -67,6 +67,7 @@
 }

 void toggleRelay(int wait) {
+  //probably silly use of recursion but meh
   if (wait != 0) {
     toggleRelay(0);
     delay(wait);


fixed IRSendDemo

fixed IRSendDemo

--- examples/IRsendDemo/IRsendDemo.ino
+++ examples/IRsendDemo/IRsendDemo.ino
@@ -31,8 +31,10 @@
     Serial.println(IR_SEND_PIN);
 }

+unsigned long tData = 0xa90;
+
 void loop() {
-    unsigned long tData = 0xa90;
+
     for (int i = 0; i < 3; i++) {
         irsend.sendSony(tData, 12);
         Serial.print(F("sendSony(0x"));


Update QuadCopter.ino

Update QuadCopter.ino

--- examples/QuadCopter/QuadCopter.ino
+++ examples/QuadCopter/QuadCopter.ino
@@ -14,7 +14,7 @@

 void loop() {
 	/**
-	* throttling up the ESC controller
+	* Throttling up all ESC's to 15%
 	**/
-	propellor.throttle(100);
-}+	propellor.throttle(15, 15, 15, 15);
+}


delay

delay

--- examples/Basics/IMU/IMU.ino
+++ examples/Basics/IMU/IMU.ino
@@ -81,5 +81,5 @@
   M5.Lcd.setCursor(0, 175);
   M5.Lcd.printf("Temperature : %.2f C", temp);

-  delay(1000);  // Delay 1000ms (1 sec)  //延迟1000ms（1秒）
+  delay(10);  // Delay 1000ms (1 sec)  //延迟1000ms（1秒）
 }


added SWR measurement and control commands

--- arduino/repeaterControl/repeaterControl.ino
+++ arduino/repeaterControl/repeaterControl.ino
@@ -3,24 +3,42 @@
 #include <TimerOne.h>

-//******************************************************************
-// I/O Mapping
+////******************************************************************
+//// I/O Mapping Arduino UNO R3
+////******************************************************************
+//
+////mapping of Output Pins
+//const int ONBOARD_LED = 13;
+//const int RELAIS_12V = 11;
+//const int RELAIS_5V = 10;
+//const int RELAIS_230V_PHASE = 9;
+//const int RELAIS_230V_NEUTRAL = 8;
+//
+////mapping of Input Pins
+//const int ONE_WIRE_BUS = 12;
+//const int ADC_12V = 0;            //ADC
+//const int ADC_5V = 1;             //ADC
+//const int ADC_SWR_FRONT = 2;      //ADC
+//const int ADC_SWR_BACK = 3;       //ADC
+
+//******************************************************************
+// I/O Mapping Arduino Ethernet
 //******************************************************************

 //mapping of Output Pins
-const int ONBOARD_LED = 13;
-const int RELAIS_12V = 11;
-const int RELAIS_5V = 10;
-const int RELAIS_230V_PHASE = 9;
-const int RELAIS_230V_NEUTRAL = 8;
-
+const int ONBOARD_LED = 0;
+const int RELAIS_12V = 10;
+const int RELAIS_5V = 0;
+const int RELAIS_230V_PHASE = 0;
+const int RELAIS_230V_NEUTRAL = 0;

 //mapping of Input Pins
 const int ONE_WIRE_BUS = 12;
 const int ADC_12V = 0;            //ADC
 const int ADC_5V = 1;             //ADC
-const int ADC_SWR_FRONT = 2;      //ADC
-const int ADC_SWR_BACK = 3;       //ADC
+const int ADC_VSWR_FORWARD = 2;      //ADC
+const int ADC_VSWR_RETURN = 3;       //ADC
+

 //******************************************************************
@@ -84,17 +102,17 @@
 const int ADC_12V_LOWER_LIMIT_LOW = 475;  // 10,4V lower hysteresis
 const int ADC_12V_LOWER_LIMIT_HIGH = 500;  // 11,0V upper hysteresis
 const int ADC_12V_UPPER_LIMIT_LOW = 683; //14,8V lower hysteresis
-const int ADC_12V_UPPER_LIMIT_HIGH = 713; //15,5V upper hysteresis
+const int ADC_12V_UPPER_LIMIT_HIGH = 710; //15,5V upper hysteresis

 int adc_5v_upper_limit = 587;  // 5.5V
 int adc_5v_lower_limit = 480;  // 4.5V

-int adc_vswr_upper_limit = 700;
-int adc_vswr_lower_limit = 400;
+const float SWR_LIMIT = 3;

 // Variables set True in case limits are exeeded
 bool error_12v = false;
 bool error_5v = false;
+bool error_230v = false;
 bool error_vswr = false;

 const int ADC_READINGS = 3;          // number of ADC samples used for averaging
@@ -103,21 +121,26 @@
 //arrays to store ADC values
 int adc_12v[ADC_READINGS];
 int adc_5v[ADC_READINGS];
-int adc_swr_front[ADC_READINGS];
-int adc_swr_back[ADC_READINGS];
+int adc_vswr_forward[ADC_READINGS];
+int adc_vswr_return[ADC_READINGS];
+
+//variables to store SWR
+float vswr; //VSWR
+float swr_correction_factor = 0.8768; //correction factor of couple between forward and returning voltage
+float swr_voltage_forward; //forward voltage
+float swr_voltage_return; //reflected voltage

 //variables needed to calculate average
 int adc_12v_total = 0;
-int adc_5v_total = 0;
-int adc_swr_front_total = 0;
-int adc_swr_back_total = 0;
+int adc_5v_total = 0;
+int adc_vswr_forward_total = 0;
+int adc_vswr_return_total = 0;

 // average values
 int adc_12v_average = 0;
-int adc_5v_average = 0;
-int adc_swr_front_average = 0;
-int adc_swr_back_average = 0;
-
+int adc_5v_average = 0;
+int adc_vswr_forward_average = 0;
+int adc_vswr_return_average = 0;

 //******************************************************************
 // Relais status tracking variables
@@ -131,6 +154,13 @@
 bool onboardLedStatus = false;

+//******************************************************************
+// Remote control status tracking variables
+//******************************************************************
+bool remote_12v_off = false;
+bool remote_5v_off = false;
+bool remote_230v_off = false;
+

 //******************************************************************
 // MISC
@@ -139,6 +169,7 @@
 // Counters
 int irq_temp_counter = 0; //pre-scaler for Timer1 -> temperature measurement routine
 int irq_led_blink_counter = 0; //pre-scaler for Timer1 -> status LED blinking
+int irq_swr_counter = 0; //pre-scaler for Timer1 -> SWR Measurement

 //******************************************************************
@@ -178,8 +209,8 @@
   for (int thisReading = 0; thisReading < ADC_READINGS; thisReading++){
     adc_12v[thisReading] = 0;
     adc_5v[thisReading] = 0;
-    adc_swr_front[thisReading] = 0;
-    adc_swr_back[thisReading] = 0;
+    adc_vswr_forward[thisReading] = 0;
+    adc_vswr_return[thisReading] = 0;
   }

   Timer1.initialize(50000); //timer Interrupt every 20ms
@@ -234,34 +265,72 @@
   //substract the last reading
   adc_12v_total -= adc_12v[adc_index];
   adc_5v_total -= adc_5v[adc_index];
-  adc_swr_front_total -= adc_swr_front[adc_index];
-  adc_swr_back_total -= adc_swr_back[adc_index];
+  adc_vswr_forward_total -= adc_vswr_forward[adc_index];
+  adc_vswr_return_total -= adc_vswr_return[adc_index];

   adc_12v[adc_index] = analogRead(ADC_12V);
   adc_5v[adc_index] = analogRead(ADC_5V);
-  adc_swr_front[adc_index] = analogRead(ADC_SWR_FRONT);
-  adc_swr_back[adc_index] = analogRead(ADC_SWR_BACK);
-
+  adc_vswr_forward[adc_index] = analogRead(ADC_VSWR_FORWARD);
+  adc_vswr_return[adc_index] = analogRead(ADC_VSWR_RETURN);
+
+
   adc_12v_total += adc_12v[adc_index];
   adc_5v_total += adc_5v[adc_index];
-  adc_swr_front_total += adc_swr_front[adc_index];
-  adc_swr_back_total += adc_swr_back[adc_index];
-
-  Serial.println(adc_12v[adc_index]);
+  adc_vswr_forward_total += adc_vswr_forward[adc_index];
+  adc_vswr_return_total += adc_vswr_return[adc_index];

   adc_index +=1;

-  if (adc_index >= ADC_READINGS){
+  if (adc_index >= ADC_READINGS){ //reset counter
     adc_index = 0;
   }

   adc_12v_average = adc_12v_total / ADC_READINGS;
   adc_5v_average = adc_5v_total / ADC_READINGS;
-  adc_swr_front_average = adc_swr_front_total / ADC_READINGS;
-  adc_swr_back_average = adc_swr_back_total / ADC_READINGS;
-
-}
-
+  adc_vswr_forward_average = adc_vswr_forward_total / ADC_READINGS;
+  adc_vswr_return_average = adc_vswr_return_total / ADC_READINGS;
+}
+
+void setRelais12v(bool state){
+  if ((state == true) && (overTemperatureTx == false) && (overTemperatureCabinet == false) && (remote_12v_off == false) && (error_vswr == false) && (error_12v == false)){
+    digitalWrite(RELAIS_12V, true);
+    relais_12v = true;
+  }
+  if (state == false){
+    digitalWrite(RELAIS_12V, false);
+    relais_12v = false;
+  }
+}
+
+void setRelais5v(bool state){
+  digitalWrite(RELAIS_5V, state);
+  relais_5v = state;
+}
+
+void setRelais230v(bool state){
+  digitalWrite(RELAIS_230V_PHASE, state);
+  digitalWrite(RELAIS_230V_NEUTRAL, state);
+  relais_230v_phase = state;
+  relais_230v_neutral = state;
+}
+
+//******************************************************************
+//Calculate VSWR
+//
+//******************************************************************
+
+
+float calcSWR(){
+  float vswr;
+  float gamma;
+  swr_voltage_forward = (float)adc_vswr_forward_average;
+  swr_voltage_return = (float)adc_vswr_return_average;
+  swr_voltage_return = swr_voltage_return * swr_correction_factor;
+  gamma = swr_voltage_return/swr_voltage_forward;
+  vswr = (1 + gamma) / (1 - gamma);
+  return(vswr);
+}
+

 //******************************************************************
@@ -287,33 +356,46 @@

   //Obtain values from ADCs
   getADCValues();
-
+
   //Handle 12V ADC
   if ((adc_12v_average > ADC_12V_UPPER_LIMIT_HIGH) || (adc_12v_average < ADC_12V_LOWER_LIMIT_LOW)){
-    digitalWrite(RELAIS_12V, LOW);
-    relais_12v = false;
     error_12v = true;
+    setRelais12v(false);
   }
   if((adc_12v_average < ADC_12V_UPPER_LIMIT_LOW) && (adc_12v_average > ADC_12V_LOWER_LIMIT_HIGH)){
-    if ((overTemperatureTx == false) && (overTemperatureCabinet == false)){
-      digitalWrite(RELAIS_12V, HIGH);
-      relais_12v = true;
-      error_12v = false;
-    }
+    error_12v = false;
+    setRelais12v(true);
   }

   //Handle 5V ADC
   if((adc_5v_average > adc_5v_upper_limit) || (adc_5v_average < adc_5v_lower_limit)){
     // do smth
   }
-
+
+
   //Handle Bad SWR
-  if((adc_swr_back_average) || (adc_swr_back_average)){
-    // do smth
-  }
-
-
-
+  if (irq_swr_counter >= 5){ //check approx every 100ms
+    float mySWR = calcSWR();
+    if (swr_voltage_forward > 50){  //only check SWR if transmitting - power applied
+      vswr = mySWR;
+      if(abs(vswr) >= 3){
+        setRelais12v(false);
+        error_vswr = true;
+      }
+      else{
+        error_vswr = false;
+        setRelais12v(true);
+      }
+    }
+    irq_swr_counter=0;
+  }
+  else{
+    irq_swr_counter+=1;
+  }
+
+
+
+

     //check Temperature Sensors
@@ -325,30 +407,22 @@

     //Handle Transmitter Temperature
     if (temperature_tx_average >= TEMP_TX_LIMIT_HIGH){
-      digitalWrite(RELAIS_12V, LOW);
+      overTemperatureTx = true;  //needed to ensure that ADC doesn't enable the 12V again
       relais_12v = false;
-      overTemperatureTx = true;  //needed to ensure that ADC doesn't enable the 12V again
     }
     if (temperature_tx_average <= TEMP_TX_LIMIT_LOW){
       overTemperatureTx = false;
-      if ((overTemperatureCabinet == false) && (error_12v == false)){
-        digitalWrite(RELAIS_12V, HIGH);
-        relais_12v = true;
-      }
+      setRelais12v(true);
     }

     //Handle Cabinet Temperature
     if (temperature_cabinet_average >= TEMP_CABINET_LIMIT_HIGH){
-      digitalWrite(RELAIS_12V, LOW);
-      relais_12v = false;
       overTemperatureCabinet = true;  //needed to ensure that ADC doesn't enable the 12V again
+      setRelais12v(false);
     }
     if (temperature_cabinet_average <= TEMP_CABINET_LIMIT_LOW){
       overTemperatureCabinet = false;
-      if ((overTemperatureTx == false) && (error_12v == false)){
-        digitalWrite(RELAIS_12V, HIGH);
-        relais_12v = true;
-      }
+      setRelais12v(true);
     }
   }
   else{
@@ -429,7 +503,7 @@
   Serial.print("},");

   Serial.print("{'vswr': ");
-  Serial.print(adc_swr_back_average); //TODO: VSWR needs to be properly calculated
+  printDouble(vswr, 100);
   Serial.print("}");

   Serial.print("]");
@@ -441,32 +515,97 @@
   Serial.print("[");

   Serial.print("{'tx_overheat': ");
-  Serial.print(overTemperatureTx);
+  if(overTemperatureTx){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }
   Serial.print("},");

   Serial.print("{'cabinet_overheat': ");
-  Serial.print(overTemperatureCabinet);
+  if(overTemperatureCabinet){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }
   Serial.print("}");

-  Serial.print("{'12v': ");
-  Serial.print(error_12v);
+  Serial.print("{'error_12v': ");
+  if(error_12v){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }
   Serial.print("},");
-
-  Serial.print("{'5v': ");
-  Serial.print(error_5v);
+
+
+  Serial.print("{'error_5v': ");
+  if(error_5v){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }  Serial.print("},");
+
+  Serial.print("{'error_vswr': ");
+  if(error_vswr){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }  Serial.print("},");
+
+
+  Serial.print("{'12v_relais': ");
+  if(relais_12v){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }
+  Serial.print("},");
+
+  Serial.print("{'relais_5v': ");
+  if(relais_5v){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }
   Serial.print("},");
-
-  Serial.print("{'vswr': ");
-  Serial.print(error_vswr);
+
+  Serial.print("{'relais_230v_phase': ");
+  if(relais_230v_phase){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }
+  Serial.print("},");
+
+  Serial.print("{'relais_230v_neutral': ");
+  if(relais_230v_neutral){
+    Serial.print("true");
+  }
+  else{
+    Serial.print("false");
+  }
   Serial.print("}");

   Serial.print("]");

 }

+
+
+
 //Check input from Serial Interface and execute commands
-void checkSerialInput(char inByte){
-  switch (inByte){
+void checkSerialInput(char inBytes[5]){
+  int counter = 0;
+  switch (inBytes[0]){
     case 't': {
       printTemperatures();
       break;
@@ -479,6 +618,63 @@
       printStatus();
       break;
     }
+
+    //turn 12v on/off remotely
+    case 'x':{
+      switch(inBytes[1]){
+        case '1': {
+          setRelais12v(true);
+          remote_12v_off = false;
+          break;
+        }
+        case '0': {
+          if ((overTemperatureCabinet == false) && (overTemperatureTx == false) && (error_5v == false)){
+            setRelais12v(false);
+            remote_12v_off = true;
+            break;
+          }
+        }
+      }
+      break;
+    }
+
+    //turn 5v on/off remotely
+    case 'y':{
+      switch(inBytes[1]){
+        case '1': {
+          setRelais5v(true);
+          remote_5v_off = false;
+          break;
+        }
+        case '0': {
+          if ((overTemperatureCabinet == false) && (overTemperatureTx == false) && (error_5v == false)){
+            setRelais5v(false);
+            remote_5v_off = true;
+            break;
+          }
+        }
+      }
+      break;
+    }
+
+    //turn 230v on/off remotely
+    case 'z':{
+      switch(inBytes[1]){
+        case '1': {
+          setRelais230v(true);
+          remote_230v_off = false;
+          break;
+        }
+        case '0': {
+          if ((overTemperatureCabinet == false) && (overTemperatureTx == false) && (error_230v == false)){
+            setRelais230v(false);
+            remote_230v_off = true;
+            break;
+          }
+        }
+      }
+      break;
+    }
   }
 }

@@ -498,15 +694,16 @@
   digitalWrite(RELAIS_230V_NEUTRAL, HIGH);

   getTemperature();
-  char inByte;
+  char inBytes[20];

   //main Loop
   while(true){
     //read commands comming through USB Serial Interface
     if (Serial.available() > 0){
-      inByte = Serial.read();
-      checkSerialInput(inByte);
-    }
-  }
-}
-
+      if (Serial.readBytesUntil(';', inBytes, 20)){
+      checkSerialInput(inBytes);
+      }
+    }
+  }
+}
+


Revert "Broke led functionality as a separate library."

This reverts commit 19ac1acf69e3f00a01875b40f7a41ad1fcf4bac9.

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

 // GPIO settings
-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);
+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;

 // 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}}'", 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);
+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);
 }

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

 /**
  * 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 };
-		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);
+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);
 }

 /**
  * 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) {
-		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);
+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);
 }

 /**
  * 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 };
-		led.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};
+    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");
 }

@@ -347,45 +632,53 @@
 /**
  * Setup
  */
-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();
-
-}
+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();
+}


Added ximu parser into arduino codes

Added ximu parser into arduino codes

--- backend/Arduino/ping_client/ping_client.ino
+++ backend/Arduino/ping_client/ping_client.ino
@@ -77,9 +77,29 @@
 }

 void loop(){
-
+    ErrorCode e = ERR_NO_ERROR;

+    if (Serial1.available() > 0){
+      e = receiver.processNewChar(Serial1.read());
+    }

+    if (e != ERR_NO_ERROR) {
+      Serial.print("ERROR: ");
+      Serial.print(e);
+      Serial.print("\n");
+    }
+
+    if (receiver.isInertialAndMagGetReady() && receiver.isQuaternionGetReady()) {
+      DrumSet drum = receiver.getDrum();
+      if (drum.drumID > 0 && drum.strength > 1) {
+        if (detectPeak(drum)) {
+            // Do whatever.
+            // Use drum.drumID to get ID and drum.strength to get strength
+        }
+
+        lastDrum = drum;
+      }
+    }

   unsigned long time = millis();
   Mirf.setTADDR((byte *)"serv1");


Fix up display stuff a bit, include a blinky period

--- lcd_temperature_display.ino
+++ lcd_temperature_display.ino
@@ -37,6 +37,8 @@
 TemperatureRecord high_temp = {0.0, 0};
 TemperatureRecord low_temp = {999.9, 0};

+byte dotToggle = FALSE;
+
 // initialize the library with the numbers of the interface pins
 LiquidCrystal lcd(LCD_RS_PIN, LCD_EN_PIN, LCD_D4_PIN, LCD_D5_PIN, LCD_D6_PIN, LCD_D7_PIN);

@@ -229,13 +231,13 @@
     }

     // Check for records
-    if (current_temp > high_temp.temperature)
+    if (current_temp - high_temp.temperature > 0.1)
     {
       high_temp.temperature = current_temp;
       high_temp.time = millis();
     }

-    if (current_temp < low_temp.temperature)
+    if (low_temp.temperature - current_temp > 0.1)
     {
       low_temp.temperature = current_temp;
       low_temp.time = millis();
@@ -247,8 +249,14 @@
     normalizeTime(&timediff, &timespec);

     lcd.setCursor(0, 0);
-    snprintf(outstr, 17, "H%3d.%1d @%3d%c %3d", (int)high_temp.temperature, (int)(high_temp.temperature * 10) % 10, timediff, timespec, (int)current_temp);
+    snprintf(outstr, 17, "H%3d.%1d@%2d%c %3d%1c%1d", (int)high_temp.temperature,
+             (int)(high_temp.temperature * 10) % 10, timediff, timespec,
+             (int)current_temp, (dotToggle ? '.' : ' '),
+             (int)(current_temp * 10) % 10
+            );
     lcd.print(outstr);
+
+    if (currentVoltageBin == 0) dotToggle = !dotToggle;

     // Then do the low
     timediff = minutesAgo(low_temp.time);
@@ -256,7 +264,9 @@
     timespec = 'm';

     lcd.setCursor(0, 1);
-    snprintf(outstr, 17, "L%3d.%1d @%3d%c %3c", (int)low_temp.temperature, (int)(low_temp.temperature * 10) % 10, timediff, timespec, 'C');
+    snprintf(outstr, 17, "L%3d.%1d@%2d%c  deg%1c", (int)low_temp.temperature,
+             (int)(low_temp.temperature * 10) % 10, timediff, timespec, 'C'
+            );
     lcd.print(outstr);
   } else {
     // Startup message and color test


using RTC and writing to indoor/outdoor temperature to separate files

--- homemonitor/homemonitor.ino
+++ homemonitor/homemonitor.ino
@@ -1,18 +1,23 @@
 /*
 Temperature Monitoring system

- The circuit:
- * SD card attached to SPI bus as follows:
- ** MOSI - pin 11
- ** MISO - pin 12
- ** CLK - pin 13
- ** CS - pin 4
-
-  filename has to be short :\ *NOTE*
-
- */
+The circuit:
+* SD card attached to SPI bus as follows:
+** MOSI - pin 11
+** MISO - pin 12
+** CLK - pin 13
+** CS - pin 4
+
+filename has to be short :\ *NOTE*
+
+*/
 #include <SD.h> // Modified SD.cpp in SD library to allow for multiple SD.begin() calls
 // Add to Line 343 of SD.cpp: 'if (root.isOpen()) {root.close();}'
+
+#include <Time.h>
+#include <TimeAlarms.h>
+#include <Wire.h>
+#include <DS1307RTC.h>

 #include <ThermistorSensor.h>

@@ -78,6 +83,45 @@
   }
 }

+// Log time to file
+void logTimeToFile(char* filename)
+{
+  // open the file. note that only one file can be open at a time,
+  // so you have to close this one before opening another.
+  myFile = SD.open(filename, FILE_WRITE);
+
+  // if the file opened okay, write to it:
+  if (myFile) {
+    // Serial info
+    Serial.print("Writing '");
+    Serial.print(month());
+    Serial.print(" ");
+    Serial.print(day());
+    Serial.print(" ");
+    Serial.print(hour());
+    Serial.print(" ");
+    Serial.print(minute());
+    Serial.print("' to ");
+    Serial.print(filename);
+    Serial.print("... ");
+
+    // Write value to file and close
+    myFile.print(month());
+    myFile.print(" ");
+    myFile.print(day());
+    myFile.print(" ");
+    myFile.print(hour());
+    myFile.print(" ");
+    myFile.print(minute());
+    myFile.close();
+
+    Serial.println("done.");
+  } else {
+    // if the file didn't open, print an error:
+    Serial.println("error opening for writing.");
+  }
+}
+
 boolean startSD()
 {
   Serial.print("Initializing SD card...");
@@ -101,7 +145,8 @@
   buttonPressed = true;
 }

-void doButtonCall()
+// Read and record data to log files
+void recordData()
 {
   delay(250);
   Serial.println("Woo");
@@ -111,9 +156,17 @@
     int tempA = thermistorA.getReading();
     int tempB = thermistorB.getReading();

-    logToFile(FILENAME, tempA);
-    logToFile(FILENAME, tempB);
-    readFileToSerial(FILENAME);
+    // Log Time & Temperature
+    logTimeToFile("time.txt");
+    logToFile("T_in.txt", tempA);
+    logToFile("T_out.txt", tempB);
+
+    Serial.println("time.txt: ");
+    readFileToSerial("time.txt");
+    Serial.println("T_in.txt: ");
+    readFileToSerial("T_in.txt");
+    Serial.println("T_out.txt: ");
+    readFileToSerial("T_out.txt");
   }
   buttonPressed = false;

@@ -121,25 +174,28 @@

 void setup()
 {
-  Serial.begin(9600);
-
-  // if (!startSD())
-  // {
-  //   return;
-  // }
-
-  // logToFile(FILENAME, counter);
-  // readFileToSerial(FILENAME);
-
+  Serial.begin(9600);
+  setSyncProvider(RTC.get); // get time from Real Time Clock
+  if(timeStatus()!= timeSet)
+    Serial.println("Unable to sync with the RTC");
+  else
+    Serial.println("RTC has set the system time");
+
+  // Set button to write to file
   attachInterrupt(0, buttonInterrupt, RISING);
+
+  // Set call to update files every minute
+  Alarm.timerRepeat(10, recordData);
 }

 void loop()
 {
   if (buttonPressed)
   {
-    doButtonCall();
-  }
-}
-
-
+    recordData();
+  }
+
+  Alarm.delay(1000); // Check every second
+}
+
+


Comment

Comment

--- moros/moros.ino
+++ moros/moros.ino
@@ -213,6 +213,8 @@

 Controller controller;

+// We need a separate function for each interrupt because we can't pass any
+// context to the interrupt handler
 void handle_interrupt_0() {
   Controller::handle_interrupt(0);
 }


returning Serial baud rate to 9600 to be compatible w/ other sketches

--- direct-servo-turns/direct-servo-turns.ino
+++ direct-servo-turns/direct-servo-turns.ino
@@ -12,7 +12,7 @@

 void setup()
 {
-  Serial.begin(115200);  // 57600 * 2
+  Serial.begin(9600);

   // initialize detector input pins
   pinMode(DETECTOR_LEFT, INPUT);


Tweaks to constants and support for ROTATE

Move constants around, add a few for bluetooth name and such,
reduce default APA_MHZ to something more sane,
and add a ROTATE constant to support LEDs in columns instead of rows.
To simplify, I'm retiring the custom fillRow/fillCol logic and just
letting everything use setAt().

--- blume.ino
+++ blume.ino
@@ -1,7 +1,11 @@
 // These settings change per project!
 #define NUM_LEDS    256
+// If true, LEDs are arranged in columns instead of rows.
+// BASE_WID is the vertical dimension in this case.
+#define ROTATE false
+// Width of the piece, unless ROTATE is true.
 #define BASE_WID  16
-// If true, adds 1 to BASE_WID for every other row
+// If true, adds 1 to BASE_WID for every other row (column if ROTATE)
 #define STAGGERED false
 // Wiring is in a zig-zag pattern.
 #define ZIGZAG   false
@@ -30,7 +34,10 @@
 // Only change these settings if you're wired to different pins, using a non-APA102 chipset,
 // using a different COLOR_ORDER for RGB, etc.
 #ifdef ESP32
+// Open Pixel Control support can be turned on or off.
 #define ESP32_OPC
+// Over-the-air updates can be turned on or off.
+//#define ESP32_OTA
 // No standard pins for ESP32 yet; I'm using these at the moment.
 #define DATA_PIN_0    12
 #define CLOCK_PIN_0   13
@@ -39,6 +46,10 @@
 // Here, use PIXO Pixel's defaults.
 #define DATA_PIN_1    19
 #define CLOCK_PIN_1   18
+// Give your project a name!
+#define BLUETOOTH_NAME "Blume"
+// This varies by ESP32 dev board.
+#define LED_PIN LED_BUILTIN
 #else
 #define DATA_PIN_0    A0
 #define CLOCK_PIN_0   5
@@ -47,7 +58,7 @@
 #endif
 #define COLOR_ORDER BGR
 #define CHIPSET     APA102
-#define APA_MHZ 40
+#define APA_MHZ 2
 /* For LPD8806:
 #define COLOR_ORDER BRG
 #define CHIPSET     LPD8806
@@ -76,14 +87,8 @@
 #include <BLEServer.h>
 // FastLED seems to need this.
 #define FASTLED_FORCE_SOFTWARE_PINS
-// Over-the-air updates can be turned on or off.
-//#define ESP32_OTA
-// Open Pixel Control support can be turned on or off.
-//#define ESP32_OPC
 // Something small and reasonable.
 #define EEPROM_SIZE 64
-// This varies by ESP32 dev board.
-#define LED_PIN LED_BUILTIN
 #endif // ESP32

 #ifdef ESP32_OTA
@@ -163,10 +168,16 @@
 #define MAX_FRAMES (BASE_WID == 1 ? (SRAM_SIZE - VAR_ALLOWANCE) / NUM_LEDS : 0)
 #endif // ESP32

-#define WIDTH (STAGGERED ? BASE_WID * 2 + 1 : BASE_WID)
 #define logical_num_leds (STAGGERED ? NUM_LEDS * 2 : NUM_LEDS)
+// These are the width/height assuming rows of LEDs.
+#define PHYSICAL_WIDTH (STAGGERED ? BASE_WID * 2 + 1 : BASE_WID)
 // Add extra row if there's a remainder.
-#define HEIGHT (logical_num_leds / WIDTH + (logical_num_leds % WIDTH ? 1 : 0))
+#define PHYSICAL_HEIGHT (logical_num_leds / PHYSICAL_WIDTH + (logical_num_leds % PHYSICAL_WIDTH ? 1 : 0))
+// If ROTATE is true, LEDs are in columns, not rows, so flip the
+// physical dimensions when generating the dimensions the rest of the code uses.
+uint16_t WIDTH = ROTATE ? PHYSICAL_HEIGHT : PHYSICAL_WIDTH;
+uint16_t HEIGHT = ROTATE ? PHYSICAL_WIDTH : PHYSICAL_HEIGHT;
+
 int eepromStart;
 long saveTarget;
 long lastFrameTime;
@@ -326,7 +337,7 @@
   FastLED.show();

 #ifdef ESP32
-  BLEDevice::init("Blume ESP");
+  BLEDevice::init(BLUETOOTH_NAME);
   BLEServer* server = BLEDevice::createServer();
   bleSerial.begin(server, BLEUUID((uint16_t)0xDFB0), BLEUUID((uint16_t)0xDFB1));
   BLEDeviceID* did = new BLEDeviceID(
@@ -532,69 +543,39 @@
     STAGGERED && (x % 2 != y % 2));
 }
 void setAt(uint16_t x, uint16_t y, CRGB color) {
+  uint16_t idx;
   if (FLIP_HORIZONTAL) {
     x = WIDTH - 1 - x;
   }
   if (FLIP_VERTICAL) {
     y = HEIGHT - 1 -y;
+  }
+  if (ROTATE) {
+    idx = x;
+    x = y;
+    y = idx;
   }
   if (STAGGERED) {
     if (isImaginary(x, y)) {
       return;
     }
-    uint16_t idx = x / 2 + (x % 2) * (BASE_WID + 1) + WIDTH * (y / 2);
-    if (idx >= NUM_LEDS) {
-      return;
-    }
+    idx = x / 2 + (x % 2) * (BASE_WID + 1) + PHYSICAL_WIDTH * (y / 2);
+  } else {
+    idx = ((ZIGZAG && y % 2) ? (PHYSICAL_WIDTH - 1 - x) : x) + y * PHYSICAL_WIDTH;
+  }
+  // Safety
+  if (idx < NUM_LEDS) {
     leds[idx] = color;
-  } else {
-    uint16_t idx = ((ZIGZAG && y % 2) ? (WIDTH - 1 - x) : x) + y * WIDTH;
-    if (idx < NUM_LEDS) {
-      leds[idx] = color;
-    }
   }
 }
 void fillRow(uint16_t row, CRGB color) {
-  if (FLIP_VERTICAL) {
-    row = HEIGHT - 1 - row;
-  }
-  if (STAGGERED) {
-    uint16_t start = row * WIDTH / 2;
-    uint16_t num = WIDTH / 2;
-    if (row % 2) {
-      start += 1;
-    } else {
-      num += 1;
-    }
-    if (start + num > NUM_LEDS) {
-      num = NUM_LEDS - start;
-    }
-    fill_solid(leds + start, num, color);
-  } else {
-    for (uint16_t j = 0; j < WIDTH; j++) {
-      if (row * WIDTH + j < NUM_LEDS) {
-        leds[row * WIDTH + j] = color;
-      }
-    }
+  for (uint16_t i = 0; i < WIDTH; i++) {
+    setAt(i, row, color);
   }
 }
 void fillCol(uint16_t col, CRGB color) {
-  if (FLIP_HORIZONTAL) {
-    col = WIDTH - 1 - col;
-  }
-  if (ZIGZAG) {
-    for (uint16_t i = 0; i < HEIGHT; i++) {
-      setAt(col, i, color);
-    }
-  } else if (STAGGERED) {
-    for (uint16_t i = col / 2 + (col % 2) * (BASE_WID + 1);
-         i < NUM_LEDS; i += WIDTH) {
-      leds[i] = color;
-    }
-  } else {
-    for (uint16_t j = col; j < NUM_LEDS; j+= WIDTH) {
-      leds[j] = color;
-    }
+  for (uint16_t i = 0; i < HEIGHT; i++) {
+    setAt(col, i, color);
   }
 }


Added daily statistics, use Time to make the regular readout independent from the reading time, some cleanup

--- Particle/THDweeter/src/THDweeter.ino
+++ Particle/THDweeter/src/THDweeter.ino
@@ -7,6 +7,7 @@
  * v1.0 - 1-05-2017: initial revision from https://www.hackster.io/shaiss
  * v2.0 - 6-05-2017: added wifi sleep + readout button, refactorization
  * v2.1 - 7-05-2017: dropped LED, minor changes
+ * v2.2 - 8-05-2017: added daily max and min reports, use Time
  *************************************************************************/

 // This #include statement was automatically added by the Particle IDE.
@@ -16,90 +17,127 @@
 #include "Adafruit_DHT/Adafruit_DHT.h"

 int dhtPin = 2;                        // DHT sensor pin
-int pushButtonPin = D5;                // Push button pin
+int pushButtonPin = D5;                // push button pin
 int readoutTimeIntervalMin = 3;        // perform a readout every given minutes
 String thingName = "glp_photon_vth";   // thing name for Dweeter

 DHT dht(dhtPin, DHT22);
 HttpClient http;
-
 // Headers currently need to be set at init, useful for API keys etc.
 http_header_t headers[] = {
   //  { "Content-Type", "application/json" },
   //  { "Accept" , "application/json" },
   { "Accept" , "*/*"},
-  { NULL, NULL } // NOTE: Always terminate headers will NULL
+  { NULL, NULL }  // NOTE: Always terminate headers will NULL
 };
 http_request_t request;
 http_response_t response;
-int t = readoutTimeIntervalMin*60*5;    // time counter: force a readout at startup
+long lastReadoutTime = 0;       // timestamp of the last readout
+float temp, tmax, tmin, hum, hmax, hmin;    // current and daily max and min values
+int day = 0;

 void setup() {
-  Time.zone(+1);
  	dht.begin();
   pinMode(pushButtonPin, INPUT_PULLUP);  // input with an internal pull-up resistor
 }
+
+
+int getAndStoreValues() {
+  // reading temperature or humidity takes about 250 milliseconds!
+  // Sensor readings may also be up to 2 seconds 'old' (its a
+  // very slow sensor). Also the lib has a typo...
+  temp = dht.getTempCelcius();
+  hum = dht.getHumidity();
+  //float hi = dht.getHeatIndex();
+  //float dp = dht.getDewPoint();
+  // Check if any reads failed and exit early
+  if (isnan(hum) || isnan(temp)) {
+    //Particle.publish("DEBUG", "Failed to read from DHT sensor!");
+    return -1;
+  }
+  // reset every day the max and min values
+  if(Time.day() != day) {
+    tmin = tmax = temp;
+    hmin = hmax = hum;
+    day = Time.day();
+  }
+  // and update them at every readout
+  if(temp < tmin) {
+    tmin = temp;
+  }
+  else if(temp > tmax) {
+    tmax = temp;
+  }
+  if(hum < hmin) {
+    hmin = hum;
+  }
+  else if(hum > hmax) {
+    hmax = hum;
+  }
+  return 0;
+}
+

 void readout(bool button) {
   // First wake up WiFi
   WiFi.on();
   WiFi.connect();
-  // Reading temperature or humidity takes about 250 milliseconds!
-  // Sensor readings may also be up to 2 seconds 'old' (its a
-  // very slow sensor)
- 	float h = dht.getHumidity();
-  // Read temperature as Celsius - the lib has a typo...
- 	float t = dht.getTempCelcius();
-  // Check if any reads failed and exit early
- 	if (isnan(h) || isnan(t)) {
-    //Particle.publish("DEBUG", "Failed to read from DHT sensor!");
-    // try again in 10 secs
-    t = readoutTimeIntervalMin*60*5 - 10*5;
+  // get and store current values
+  if(getAndStoreValues()) {
+    // failed to read out, try again right away
+    lastReadoutTime = 0;
     return;
- 	}
- 	//float hi = dht.getHeatIndex();
- 	//float dp = dht.getDewPoint();
- 	// cast floats to string
- 	String st(t, 1);
- 	String sh(h, 1);
+  }
+  // cast floats to strings
+  String st(temp, 1);
+  String sh(hum, 1);
   // make sure WiFi is ready
   while(!WiFi.ready()) {
-    delay(100);
+    delay(300);
   }
+  // log what we're doing
   if(button) {
     Particle.publish("STATE", "Button pressed, data readout");
   }
   else {
     Particle.publish("STATE", "Periodic data readout");
   }
-  // post to dweet: follow at http://dweet.io/follow/THINGNAME
+  // post to dweet: follow at http://dweet.io/follow/thingName
  	request.hostname = "dweet.io";
   request.port = 80;
   request.path = "/dweet/for/" + thingName +
-      "?temp=" + st + "&humidity=" + sh + "&time=" + Time.local();
+      "?temp=" + st + "&humidity=" + sh + "&time=" + Time.now();
+  if(button) {
+    // also publish daily max and min values
+    String stmin(tmin, 1);
+    String stmax(tmax, 1);
+    String shmin(hmin, 1);
+    String shmax(hmax, 1);
+    request.path += "&tmin=" + stmin + "&tmax=" + stmax +
+        "&hmin=" + shmin + "&hmax=" + shmax;
+  }
  	Particle.publish("DWEET", request.path);
   http.get(request, response, headers);
   //Particle.publish("DEBUG",response.status);
   Particle.publish("DWEET", response.body);
 }

+
 void loop() {
   int pushButtonState = digitalRead(pushButtonPin);
   if(pushButtonState == LOW) {
     // button was pressed, readout
     readout(true);
-    // don't reset the timer, to keep the regular sampling
+    // and keep WiFi on for other photon activities/upgrades
   }
   else {
-    t++;
     // force a readout after the given time interval
-    if(t >= readoutTimeIntervalMin*60*5) {
+    if(Time.now() - lastReadoutTime >= readoutTimeIntervalMin*60) {
+      // for regular readouts, record the time of this readout
+      lastReadoutTime = Time.now();
       readout(false);
-      t = 0;
-    }
-    if(t == 10*5) {
-      // turn off wifi to spare power, but allow a few seconds
-      // for upgrades/other photon activities
+      delay(1000);
+      // turn off WiFi to spare power
       WiFi.off();
     }
   }


Document constants used in Wire begin method. (#38)

--- Arduino/victoria_teensy_ros_node/victoria_teensy_ros_node.ino
+++ Arduino/victoria_teensy_ros_node/victoria_teensy_ros_node.ino
@@ -191,7 +191,9 @@
   // Initialize all the hardware

   // Setup basic I2C master mode pins 18/19, external pullups, 400kHz, 200ms default timeout
-  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
+  const uint8_t IGNORED(0x00);
+  const uint32_t I2C_BUS_RATE_400KHZ(400000);
+  Wire.begin(I2C_MASTER, IGNORED, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_BUS_RATE_400KHZ);
   Wire.setDefaultTimeout(200000); // 200ms

   // Setup IMU and magnetometer that use I2C


説明の追加

説明の追加

--- 2_example/example13_lcd/example13_lcd.ino
+++ 2_example/example13_lcd/example13_lcd.ino
@@ -88,8 +88,8 @@
         t++;                                // 変数iの値を1だけ増加させる
         if(t>TIMEOUT) break; else delay(1); // TIMEOUTに到達したらwhileを抜ける
     }
-    delay(1);
-    trUri2txt(lcd);
+    delay(1);                               // クライアント側の応答待ち時間
+    trUri2txt(lcd);                         // URLエンコードの変換処理
     lcdPrint(lcd);                          // 受信文字データを液晶へ表示
     if(client.connected()){                 // 当該クライアントの接続状態を確認
         html(client,lcd,WiFi.localIP());    // HTMLコンテンツを出力する