Browse Source

adding brightness adjustments and color correction code

kyle 1 year ago
parent
commit
8b519bbb30
1 changed files with 147 additions and 27 deletions
  1. 147 27
      esp8266-house-led-rgb.ino

+ 147 - 27
esp8266-house-led-rgb.ino

@@ -77,6 +77,16 @@ uint8_t animate = 0;
 uint8_t red = 1;
 uint8_t green = 1;
 uint8_t blue = 1;
+uint8_t brightness = 50;
+
+#define DISABLE_FASTLED_CORRECTION 0
+#define ENABLE_FASTLED_CORRECTION 1
+#define DISABLE_GAMMA_CORRECTION 0
+#define USE_PHILIP_GAMMA_CORRECTION 1
+#define USE_BG100_S_CURVE_GAMMA_CORRECTION 2
+
+uint8_t gammaCorrectionType = DISABLE_GAMMA_CORRECTION;
+uint8_t useFastLedCorrection = DISABLE_FASTLED_CORRECTION;
 
 // quick fix for gamma correction from Adafruit's Phillip Burgess
 static uint8_t phillip_adafruitGamma8[] = {
@@ -129,22 +139,33 @@ static uint8_t bg100_sCurveGamma8[] = {
  *  I believe that this color correction has to do with mechanical measurements as they are linear
  *  and I know that the human eye sensitivities are anything but.  Otherwise this is very crude.
  */
-static uint8_t redCorrection    = 0xFF;
-static uint8_t greenCorrection  = 0xB0;
-static uint8_t blueCorrection   = 0xF0;
+#define FASTLED_RED_CORRECTION    = 0x00FF;
+#define FASTLED_GREEN_CORRECTION  = 0x00B0;
+#define FASTLED_BLUE_CORRECTION   = 0x00F0;
+
+uint8_t correctColor(uint8_t input, uint16_t factor) {
+    uint16_t out;
+    uint16_t in;
+    in = input;
+    out = in * factor;
+    out /= 255;
+    return ENABLE_FASTLED_CORRECTION == useFastLedCorrection
+            ? gammaCorrection((uint8_t) out)
+            : gammaCorrection(input);
+}
 
-/*  we are going to cheat the brightness by adjusting pwm resolution
- *   
- *  in effect this will change the brightness and we will not need to perform any math
- *  the way this works is that PWM resolution is 10-bit but when we set the duty-cycle
- *  we only use values [0..255].  By increasing the resoltion past 255 we reduce the
- *  brightness.  Cool.
- */
-void adjustBrightness() {
-  analogWriteRange(511);
+uint8_t gammaCorrection(uint8_t input) {
+    switch(gammaCorrectionType) {
+        case USE_PHILIP_GAMMA_CORRECTION: return phillip_adafruitGamma8[input];
+        case USE_BG100_S_CURVE_GAMMA_CORRECTION: return bg100_sCurveGamma8[input];
+        case DISABLE_GAMMA_CORRECTION:
+        default: 
+    }
+    return input;
 }
 
 
+
 ESP8266WebServer server(80);    // Create a webserver object that listens for HTTP request on port 80
 
 void handleRoot();              // function prototypes for HTTP handlers
@@ -183,6 +204,10 @@ void setup(void){
   server.on("/dec", HTTP_GET, handleGetDec);
   server.on("/echo", HTTP_GET, handleEchoHex);
   server.on("/animate", HTTP_GET, handleAnimate);
+  server.on("/cfg", HTTP_GET, handleGetCfg);
+  server.on("/cfg", HTTP_POST, handleCfg);
+  server.on("/brightness", HTTP_POST, handleGetBrightness);
+  server.on("/brightness", HTTP_POST, handleBrightness
 
   server.onNotFound(handleNotFound);           // When a client requests an unknown URI (i.e. something other than "/"), call function "handleNotFound"
 
@@ -194,9 +219,23 @@ void setup(void){
   #define PIN_6 12 /* GPIO_12 */
   #define PIN_7 13 /* GPIO_13 */
 
+  /*  slowing the PWM frequency decreases the error caused by the power mosfets
+   *  having a slow turn-off
+   *  this comes at a cost of flickering.  One might think that LED PWM at
+   *  1000Hz is not noticeable by the human eye, but one can see the flicker
+   *  appear as multiple spots when moving.  It isn't really a flicker but one
+   *  can tell.
+   */
   //analogWriteFreq(1000);
   analogWriteFreq(250);
 
+  // lower brightness to minimal value
+  brightness = 0;
+  updateBrightness ();
+  
+  // turn on the onboard led for testing brightness
+  analogWrite(/* esp12f onboard led */ 2, 255);
+
   // setup pins with a test pwm
   analogWrite(PIN_5, red);
   analogWrite(PIN_6, green);
@@ -303,15 +342,98 @@ void handleGetDec() {
   server.send(200, "text/html", hex + red + "," + green + "," + blue + '\n');
 }
 
+void handleGetBrightness() {
+  String info = "";
+  server.send(200, "text/html", info + brightness + '\n');
+  return;
+}
+
+/*  we are going to cheat the brightness by adjusting pwm resolution
+ *   
+ *  in effect this will change the brightness and we will not need to perform any math
+ *  the way this works is that PWM resolution is 10-bit but when we set the duty-cycle
+ *  we only use values [0..255].  By increasing the resoltion past 255 we reduce the
+ *  brightness.  Cool.
+ *
+ *  [?] because our gate drive voltage source is a weak reverse biased zener
+ *      regulator and we just use resistors to turn off the power mosfets we
+ *      have a hard time with power losses turning on and off the mosfet.  When
+ *      we drive all three mosfets on with 100% duty cycle the voltage source
+ *      drops well below the needed drive voltage and the mosfet Rds becomes
+ *      pretty terrible.  It works but is really bad.  If we run less than
+ *      50% duty we should be okay.  Using 1023 as the starting point means that
+ *      at 0 brightness mosfets are driven at 25% duty (255/1032).  At 100
+ *      brightness they are driven at 60% duty 255/432.
+ */
+void updateBrightness () {
+    // default range is 0..255
+    analogWriteRange(1023);
+    // brightness is inverted, technically it is darkness
+    uint8_t brightnessValue = 6 * (100 - brightness);
+    // reducing PWM range will increase brightness
+    analogWriteRange(1023 - brightnessValue);
+}
+
+void handleBrightness() {
+    char buffer[4];  
+    if( ! server.hasArg("brightness")) {
+        server.send(400, "text/plain", "400: Invalid Request");         // The request is invalid, so send HTTP status 400
+        return;
+    }
+    // else
+    server.arg("brightness").getBytes(buffer, 3);
+    brightness = (nibbler(buffer[0]) << 4) + nibbler(buffer[1]);
+    // limit brightness to 100
+    brightness = 100 < brightness ? 100 : brightness;
+    updateBrightness();
+    handleGetBrightness();
+    return;
+}
+
+void handleCfg() {
+  uint8_t valid = 0;
+  char buf[3];
+  if(server.hasArg("quickGamma")) {
+    server.arg("quickGamma").getBytes(buf, 2);
+    gammaCorrectionType = '1' == buf[0]
+        ? USE_BG100_S_CURVE_GAMMA_CORRECTION
+        : DISABLE_GAMMA_CORRECTION;
+    valid += 1;
+  }
+  if(server.hasArg("sCurveGamma")) {
+    server.arg("sCurveGamma").getBytes(buf, 2);
+    gammaCorrectionType = '1' == buf[0]
+        ? USE_PHILIP_GAMMA_CORRECTION
+        : DISABLE_GAMMA_CORRECTION;
+    valid += 1;
+  }
+  if(server.hasArg("colorCorrection")) {
+    server.arg("colorCorrection").getBytes(buf, 2);
+    gammaCorrectionType = '1' == buf[0]
+        ? ENABLE_FASTLED_CORRECTION
+        : DISABLE_FASTLED_CORRECTION;
+    valid += 1;
+  }
+  if(valid) handleGetCfg(); else server.send(400, "text/plain", "400: Invalid Request");
+  return;
+}
+
+void handleGetCfg() {
+  String info = "cfg [quickGamma:";
+  uint8_t fastled = ENABLE_FASTLED_CORRECTION == useFastLedCorrection;
+  uint8_t sCurve = USE_PHILIP_GAMMA_CORRECTION == gammaCorrectionType;
+  uint8_t quick = USE_BG100_S_CURVE_GAMMA_CORRECTION == gammaCorrectionType;
+  if(valid) server.send(200, "text/html", info + quick + " sCurveGamma:" + sCurve + " colorCorrection:" + fastled + ']' + '\n');
+  return;
+}
+
 void handleRGB() {                         // If a POST request is made to URI /login
   if( ! server.hasArg("color")) {
     server.send(400, "text/plain", "400: Invalid Request");         // The request is invalid, so send HTTP status 400
     return;
   }
-
   // turn of animate
   animate = 1;
-
   // kinda lame but parsing is not super easy here
   server.arg("color").getBytes(hexStr, 7);
   setColor(
@@ -319,18 +441,8 @@ void handleRGB() {                         // If a POST request is made to URI /
     /* green */ (nibbler(hexStr[2]) << 4) + nibbler(hexStr[3]),
     /* blue  */ (nibbler(hexStr[4]) << 4) + nibbler(hexStr[5])
   )
-
-  /*
-  server.arg("color").substring(0, 2).toCharArray(hexStr, 2);
-  r = hexStrToInt(hexStr);
-  server.arg("color").substring(2, 4).toCharArray(hexStr, 2);
-  g = hexStrToInt(hexStr);
-  server.arg("color").substring(4, 6).toCharArray(hexStr, 2);
-  b = hexStrToInt(hexStr);
-  */
   String info = "degub r:";
   server.send(200, "text/html", info + red + " g:" + green + " b:" + blue + '\n');
-  //server.send(200, "text/html", "okay");
 }
 
 void setColor(uint8_t r, uint8_t g, uint8_t b) {
@@ -338,9 +450,17 @@ void setColor(uint8_t r, uint8_t g, uint8_t b) {
   red   = r;
   green = g;
   blue  = b;
-  analogWrite(PIN_5, red);
-  analogWrite(PIN_6, green);
-  analogWrite(PIN_7, blue);
+  /*  [!] it looks like we always correct color, this is not the case
+   *  the correctColor() routine, and the chained gammaCorrection() routine
+   *  check configuration settings before setting the LED color.
+   *  
+   *  the fast-led correction tones down green significantly and blue a bit.
+   *  Both linearly, I assume this is not to adjust for human eye sensitivity
+   *  but just for the junction efficiency
+   */
+  analogWrite(PIN_5, correctColor(red,   FASTLED_RED_CORRECTION));
+  analogWrite(PIN_6, correctColor(green, FASTLED_GREEN_CORRECTION));
+  analogWrite(PIN_7, correctColor(blue,  FASTLED_BLUE_CORRECTION));
 }
 // converts ASCII utf8 chars to integer values, [0..F], otherwise zero
 uint8_t nibbler(uint8_t v) {