arduino rgb led strip driver
authorClinton Ebadi <clinton@unknownlamer.org>
Mon, 24 Nov 2014 04:48:40 +0000 (23:48 -0500)
committerClinton Ebadi <clinton@unknownlamer.org>
Mon, 24 Nov 2014 04:48:40 +0000 (23:48 -0500)
A few iterations in, time for version control. led-controller.scm
interfaces with this.

This supports driving an arbitrary number of rgb led strips. All
currently are set to the same color. You just wire up the pins, and
send it three bytes (r, g, b) and read the response line to keep in
sync.

Originally read r g b using Serial.parseInt(). Returning to that soon
as I am porting it to Teensy 3.1 and it has fast buffered
communication making the current approach unsuitable.

It uses a basic intensity correction table from another
project (generated by gamma.py). This is most likely wrong... each
color needs its own table, I think.

README
arduino-rgb-led/arduino-rgb-led.ino [new file with mode: 0644]
arduino-rgb-led/gamma.h [new file with mode: 0644]
arduino-rgb-led/gamma.py [new file with mode: 0644]
arduino-rgb-led/pwm_rgb_led_corrected.ino [new file with mode: 0644]

diff --git a/README b/README
index 3c7d6c6..afcd2d1 100644 (file)
--- a/README
+++ b/README
@@ -4,4 +4,6 @@ Miscellaneous (often broken) bits of code that may have been worth
 recording in version control, but are not large enough to need a
 dedicated project.
 
 recording in version control, but are not large enough to need a
 dedicated project.
 
-Code is public domain unless another license is noted in the file.
\ No newline at end of file
+Code is public domain unless another license is noted in the
+file. Some files are from third parties and are noted as such. All are
+under Free licenses.
\ No newline at end of file
diff --git a/arduino-rgb-led/arduino-rgb-led.ino b/arduino-rgb-led/arduino-rgb-led.ino
new file mode 100644 (file)
index 0000000..e9b3c5a
--- /dev/null
@@ -0,0 +1,100 @@
+/* -*- c -*- */
+/* Arduino rgb led driver */
+/* Copyright (c) 2014 Clinton Ebadi <clinton@unknownlamer.org> */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 3 of the License, or */
+/* (at your option) any later version. */
+
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the */
+/* GNU General Public License for more details. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <avr/pgmspace.h>
+#include "gamma.h"
+
+const int dither_steps = pow (2, DITHER_BITS);
+
+typedef struct rgb_pins
+{
+    int red;
+    int green;
+    int blue;
+} rgb_pins;
+
+const int rgb_banks = 2;
+
+const rgb_pins pins[rgb_banks] = { {10, 9, 11},
+                                   {5, 6, 3} };
+
+static int led_colors[rgb_banks * 3];
+
+void set_color_x (int r, int g, int b, int dither_step = 0)
+{
+ int offset = dither_step * 256;
+
+ for (int bank = 0; bank < rgb_banks; bank++)
+ {
+   analogWrite (pins[bank].red, pgm_read_byte_near (gammaTable + (r >= 0 ? offset : 0) + r));
+   analogWrite (pins[bank].green, pgm_read_byte_near (gammaTable + (g >= 0 ? offset : 0) + g));
+   analogWrite (pins[bank].blue, pgm_read_byte_near (gammaTable + (b >= 0 ? offset : 0) + b));
+ }
+}
+
+void setup ()
+{
+ Serial.begin (115200);
+ Serial.setTimeout (100000);
+
+ for (int bank = 0; bank < rgb_banks; bank++)
+ {
+   pinMode (pins[bank].red, OUTPUT);
+   pinMode (pins[bank].green, OUTPUT);
+   pinMode (pins[bank].blue, OUTPUT); 
+ }
+
+  // Test sequence to confirm wiring
+  set_color_x (255, 255, 255);
+  delay(500);
+  set_color_x (255, 0, 0);
+  delay (500);
+  set_color_x (0, 255, 0);
+  delay (500);
+  set_color_x (0, 0, 255);
+  delay (500);
+}
+
+void loop ()
+{
+  static int r = 192;
+  static int g = 192;
+  static int b = 192;
+  static int step = -1;
+  
+  //step = (step + 1) % dither_steps;
+  step = 0; // dithering is bogus/i'm-not-doing-it-right
+
+  if (Serial.available () >= 3)
+    {
+      r = Serial.read ();
+      g = Serial.read ();
+      b = Serial.read ();
+
+      Serial.print ("set color ");
+      Serial.print (r);
+      Serial.print (" ");
+      Serial.print (g);
+      Serial.print (" ");
+      Serial.print (b);
+      Serial.println ();
+      
+      set_color_x (r, g, b);
+    }
+
+  set_color_x (r, g, b, step);
+
+}
diff --git a/arduino-rgb-led/gamma.h b/arduino-rgb-led/gamma.h
new file mode 100644 (file)
index 0000000..4021a21
--- /dev/null
@@ -0,0 +1,7 @@
+/* Dithered luminance correction table - autogenerated by gamma.py */
+#define DITHER_BITS 1
+const prog_uchar PROGMEM gammaTable[]={
+       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x4,0x4,0x4,0x4,0x4,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0x7,0x7,0x7,0x7,0x8,0x8,0x8,0x8,0x9,0x9,0x9,0xa,0xa,0xa,0xa,0xb,0xb,0xb,0xc,0xc,0xc,0xd,0xd,0xe,0xe,0xe,0xf,0xf,0x10,0x10,0x10,0x11,0x11,0x12,0x12,0x13,0x13,0x14,0x14,0x14,0x15,0x15,0x16,0x16,0x17,0x18,0x18,0x19,0x19,0x1a,0x1a,0x1b,0x1b,0x1c,0x1d,0x1d,0x1e,0x1f,0x1f,0x20,0x20,0x21,0x22,0x22,0x23,0x24,0x25,0x25,0x26,0x27,0x28,0x28,0x29,0x2a,0x2b,0x2b,0x2c,0x2d,0x2e,0x2f,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4e,0x4f,0x50,0x51,0x52,0x54,0x55,0x56,0x57,0x59,0x5a,0x5b,0x5d,0x5e,0x5f,0x61,0x62,0x63,0x65,0x66,0x68,0x69,0x6a,0x6c,0x6d,0x6f,0x70,0x72,0x73,0x75,0x76,0x78,0x7a,0x7b,0x7d,0x7e,0x80,0x82,0x83,0x85,0x87,0x88,0x8a,0x8c,0x8d,0x8f,0x91,0x93,0x95,0x96,0x98,0x9a,0x9c,0x9e,0xa0,0xa2,0xa3,0xa5,0xa7,0xa9,0xab,0xad,0xaf,0xb1,0xb3,0xb5,0xb7,0xb9,0xbc,0xbe,0xc0,0xc2,0xc4,0xc6,0xc9,0xcb,0xcd,0xcf,0xd1,0xd4,0xd6,0xd8,0xdb,0xdd,0xdf,0xe2,0xe4,0xe6,0xe9,0xeb,0xee,0xf0,0xf3,0xf5,0xf8,0xfa,0xfd,
+       0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x4,0x4,0x4,0x4,0x4,0x5,0x5,0x5,0x5,0x5,0x6,0x6,0x6,0x6,0x6,0x7,0x7,0x7,0x7,0x8,0x8,0x8,0x8,0x9,0x9,0x9,0x9,0xa,0xa,0xa,0xb,0xb,0xb,0xc,0xc,0xc,0xd,0xd,0xd,0xe,0xe,0xe,0xf,0xf,0x10,0x10,0x10,0x11,0x11,0x12,0x12,0x13,0x13,0x14,0x14,0x14,0x15,0x15,0x16,0x16,0x17,0x18,0x18,0x19,0x19,0x1a,0x1a,0x1b,0x1b,0x1c,0x1d,0x1d,0x1e,0x1e,0x1f,0x20,0x20,0x21,0x22,0x22,0x23,0x24,0x24,0x25,0x26,0x27,0x27,0x28,0x29,0x2a,0x2a,0x2b,0x2c,0x2d,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x51,0x52,0x53,0x54,0x55,0x57,0x58,0x59,0x5a,0x5c,0x5d,0x5e,0x60,0x61,0x62,0x64,0x65,0x67,0x68,0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74,0x75,0x77,0x78,0x7a,0x7c,0x7d,0x7f,0x80,0x82,0x84,0x85,0x87,0x89,0x8a,0x8c,0x8e,0x90,0x91,0x93,0x95,0x97,0x99,0x9b,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,0xc0,0xc2,0xc5,0xc7,0xc9,0xcb,0xcd,0xd0,0xd2,0xd4,0xd7,0xd9,0xdb,0xdd,0xe0,0xe2,0xe5,0xe7,0xe9,0xec,0xee,0xf1,0xf3,0xf6,0xf8,0xfb,0xfd
+       };
+
diff --git a/arduino-rgb-led/gamma.py b/arduino-rgb-led/gamma.py
new file mode 100644 (file)
index 0000000..405b8b4
--- /dev/null
@@ -0,0 +1,69 @@
+# https://github.com/raplin/HexaWS2811/blob/master/gamma.py
+# Lookup table generator
+#
+# Really it's correcting for the eye's luminance response, it's not actually gamma correction (it's misnamed!)
+#
+# We're generating a set of 256-entry lookup tables which you use to (separately) convert the 8-bit R, G and B components of your color
+# into something you output, typically with an intelligent RGB LED such as a WS2812B; but a simple CPU-based PWM will give the same results (and benefit from this correction)
+# 
+# We generate several sets  (2,4,8...) of 256-entry tables so you can cycle through them, using a successive 8-bit lookup table each successive output frame.
+# This is 'temporal dithering', which aims to give you better color resolution at the dark end of the scale.
+# If you update at e.g. 100hz, try using two or three 'ditherBits' (i.e. 2^^2 or 2^^3=8 tables. The faster your update the more tables you can use
+# and you get better color resolution. More than 5 ditherbits is probably excessive, even 2 will help somewhat.
+#
+# If you get objectionable flickering when displaying low-intensity pixels, you should either update your leds faster or reduce ditherBits
+# 
+# 
+#
+fout=open("gamma.h","wt")
+
+#adjust me! Each extra bit doubles the table size 
+ditherBits=1
+
+ditherMSB=1<<(ditherBits-1)
+
+res="/* Dithered luminance correction table - autogenerated by gamma.py */\n#define DITHER_BITS %d\nconst prog_uchar PROGMEM gammaTable[]={" % ditherBits
+
+useRealLuminanceCalcuation=True # CIE 1931 formula
+
+finalAdjust=0  #set this to 1 if you want your values to start at one (1..255). This is a quirky request for FastLED users only
+
+for dither in range(1<<ditherBits):
+    out=[]
+
+    #reverse the low order bits so the dithering is less flickery
+    ditherValue=0
+    dread=1<<ditherBits
+    dout=1
+    for d in range(ditherBits):
+        dread>>=1
+        if dither & dread:
+            ditherValue|=dout
+        dout<<=1;
+    
+    ditherValue=(ditherValue<<(8-ditherBits))
+
+    for n in range(256):
+        if useRealLuminanceCalcuation:
+            # CIE 1931 
+            brightness=n/2.56
+            if brightness > 8:
+                pwmValue= pow( ((brightness + 16) / 116) , 3  )
+            else:
+                pwmValue = brightness / 903.3      
+            pwmValue*=256
+        else:
+            #use simple power 
+            pwmValue=pow(255,(n/256.0))-1
+        pwmValue=int(pwmValue*256)
+        pwmValue+=ditherValue
+        pwmValue=min(255, (pwmValue>>8)+finalAdjust)
+        out.append( pwmValue )
+    if dither:
+        res+=","
+    res+="\n\t"+(",".join([ "0x%x"%n for n in out]))  
+    
+res+="\n\t};\n"
+
+print >>fout,res
+fout.close()
diff --git a/arduino-rgb-led/pwm_rgb_led_corrected.ino b/arduino-rgb-led/pwm_rgb_led_corrected.ino
new file mode 100644 (file)
index 0000000..e9b3c5a
--- /dev/null
@@ -0,0 +1,100 @@
+/* -*- c -*- */
+/* Arduino rgb led driver */
+/* Copyright (c) 2014 Clinton Ebadi <clinton@unknownlamer.org> */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 3 of the License, or */
+/* (at your option) any later version. */
+
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the */
+/* GNU General Public License for more details. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <avr/pgmspace.h>
+#include "gamma.h"
+
+const int dither_steps = pow (2, DITHER_BITS);
+
+typedef struct rgb_pins
+{
+    int red;
+    int green;
+    int blue;
+} rgb_pins;
+
+const int rgb_banks = 2;
+
+const rgb_pins pins[rgb_banks] = { {10, 9, 11},
+                                   {5, 6, 3} };
+
+static int led_colors[rgb_banks * 3];
+
+void set_color_x (int r, int g, int b, int dither_step = 0)
+{
+ int offset = dither_step * 256;
+
+ for (int bank = 0; bank < rgb_banks; bank++)
+ {
+   analogWrite (pins[bank].red, pgm_read_byte_near (gammaTable + (r >= 0 ? offset : 0) + r));
+   analogWrite (pins[bank].green, pgm_read_byte_near (gammaTable + (g >= 0 ? offset : 0) + g));
+   analogWrite (pins[bank].blue, pgm_read_byte_near (gammaTable + (b >= 0 ? offset : 0) + b));
+ }
+}
+
+void setup ()
+{
+ Serial.begin (115200);
+ Serial.setTimeout (100000);
+
+ for (int bank = 0; bank < rgb_banks; bank++)
+ {
+   pinMode (pins[bank].red, OUTPUT);
+   pinMode (pins[bank].green, OUTPUT);
+   pinMode (pins[bank].blue, OUTPUT); 
+ }
+
+  // Test sequence to confirm wiring
+  set_color_x (255, 255, 255);
+  delay(500);
+  set_color_x (255, 0, 0);
+  delay (500);
+  set_color_x (0, 255, 0);
+  delay (500);
+  set_color_x (0, 0, 255);
+  delay (500);
+}
+
+void loop ()
+{
+  static int r = 192;
+  static int g = 192;
+  static int b = 192;
+  static int step = -1;
+  
+  //step = (step + 1) % dither_steps;
+  step = 0; // dithering is bogus/i'm-not-doing-it-right
+
+  if (Serial.available () >= 3)
+    {
+      r = Serial.read ();
+      g = Serial.read ();
+      b = Serial.read ();
+
+      Serial.print ("set color ");
+      Serial.print (r);
+      Serial.print (" ");
+      Serial.print (g);
+      Serial.print (" ");
+      Serial.print (b);
+      Serial.println ();
+      
+      set_color_x (r, g, b);
+    }
+
+  set_color_x (r, g, b, step);
+
+}