Advertisement
grist

Wendy's Lights Final

Oct 22nd, 2012
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.17 KB | None | 0 0
  1. /*
  2.  
  3.     Wendy's Lights Beta.
  4.    
  5.     Using TLC5940 PWM driver chips and Jez's Perlin Simplex noise generator functions modified slightly
  6.  
  7.     Now with multiple lamp code and pots for control.
  8.    
  9.     25/09/2012 Adding photoresistor and PIR code. PIR triggers an interrupt on falling edge.
  10.     02/10/2012 Added to SVN. Rev 3.
  11.         Re-added PIR and photoresistor code after overwrite.
  12.     12/10/2012 Adding Pot control code. 3 pots
  13.         1. light_duration: How long the lights stay on for after movement is detected. Tuning so 0 = 30 sec,
  14.             1 - 1000 linear scale up to 6 hours. Over 1000 = 24 hours.
  15.         2. time_inc: How fast we progress through the colour space.
  16.         3. lamp_offset: The difference between where each lamp is in the colour space.
  17.     14/10/2012 Hardware finalised. This code seems close to final. Moving to alpha.
  18.  
  19.  
  20. */
  21.  
  22. // Handles the grunt work of talking to the PWM chips. Must be edited to match
  23. // the number of TLC5940s if you have more than one. This implementation uses 2.
  24. #include "Tlc5940.h"
  25.  
  26. // Defines and constants
  27. // Update this to match LED driver resolution
  28. #define PWM_RESOLUTION 12 // bits
  29. #define LAMPS 6 // how many lamps we're controlling
  30. #define COLOURS 3 // 3 colours, red, green and blue
  31. #define FRAME_DELAY 5 // milliseconds to display each frame for
  32. // Map colour names to array postions
  33. #define RED 0
  34. #define GREEN 1
  35. #define BLUE 2
  36. // Pot pins
  37. #define AMBIENT_LIGHT_SENSOR 0 // The photoresistor
  38. // Adjustment potentiometers are on these analog pins
  39. #define ADJ_ONE              1 // light_duration
  40. #define ADJ_TWO              2 // time_inc
  41. #define ADJ_THREE            3 // lamp_offset
  42. // The ambient light level above which the lights will not be triggered. This is adjustable
  43. // in the hardware via a trim pot. The value was chosen after experimenting with the available
  44. // range from the hardware. 300 gives a nice range in both directions with the 50k pot in the
  45. // middle position. Turning the pot clockwise will
  46. #define AMBIENT_LIGHT_MIN_LEVEL 300
  47. #define PIR_PIN 2 // must be a pin we can attach hardware interrupts to (2 is int0)
  48. #define FADE_UP_TIME 500 //  milliseconds to fade the lights up
  49. #define FADE_DOWN_TIME 5000 // milliseconds to fade the lights down
  50.  
  51. // Simplex noise parameters:
  52. // obtained empirically and used in scaling returned values to match the PWM_RESOLUTION
  53. const double NOISE_LIMIT = 0.312768; // the noise generator returns values between -/+ this (on the Arduino)
  54. // Increment range, smaller is a slower walk through the noise space,
  55. // which means smaller colour differences between frames and a slower colour progression.
  56. double time_inc; // set by a pot
  57. // This is volatile as it could be changed inside the interrupt routine.
  58. volatile unsigned long end_time = 0; // used to track whether the lights should be on or not
  59. // used to store the current position in the noise space. Global so it's persistent between calls
  60. double offset = 0;
  61.  
  62. // how much of a difference between where each lamp is in the walk. This will affect how much
  63. // of a colour difference there is between each individual lamp in any given frame.
  64. double lamp_offset; // adjustable via a pot.
  65. // seconds for the light to stay on after movement is detected
  66. unsigned long light_duration; // Adjusted by a pot.
  67. // globals for the simplex generation
  68. // calulate the highest allowable value from the bit resolution. Used in scaling
  69. // Simplex noise returned values to the limits of the PWM_RESOLUTION available.
  70. int LED_MAX_LEVEL = (1 << PWM_RESOLUTION) - 1;
  71. // some working containers used by the Perlin noise generator
  72. // These are modified in multiple subs. Easier to make them global than keep passing them around
  73. int i, j, k, A[] = {0, 0, 0};
  74. double u, v, w, s;
  75. const double onethird = 0.333333333;
  76. const double onesixth = 0.166666667;
  77. // Not sure what these represent. They're used in the noise generation
  78. int T[] = {0x15, 0x38, 0x32, 0x2c, 0x0d, 0x13, 0x07, 0x2a}; // {21, 56, 50, 44, 13, 19, 7, 42)
  79. // To hold the values from the Simplex generator
  80. int colours[COLOURS];
  81.  
  82. // To hold the next frame of colours to be displayed on each lamp
  83. int frame[LAMPS][COLOURS];
  84.  
  85. // Globals for the light levels
  86. int red_max = LED_MAX_LEVEL;
  87. int green_max = LED_MAX_LEVEL;
  88. int blue_max = LED_MAX_LEVEL;
  89. int led_min_level = 0;
  90.  
  91. void setup() {
  92.     /* Call Tlc.init() to setup the tlc.
  93.     You can optionally pass an initial PWM value (0 - 4095) for all channels.*/
  94.     Tlc.init(0);
  95.     // The TLC doesn't initialise properly unless there's a delay between initialising
  96.     // and sending data to it. The rest of the code will usually take care of that,
  97.     // but leaving it here just in case.
  98.     delay(1); // OMG come on start already!
  99.    
  100.     attachInterrupt(0,isr_MovementDetected,FALLING); // Hook the interrupt for the PIR switch
  101.    
  102.     testTwo(1); // Show that we're alive
  103.     makePotAdjustments(); // Set the values to whatever the pots are set to for starters
  104. }
  105.  
  106. void loop() {
  107.   // Wait for something to happen.  
  108.   if (end_time != 0) { // movement detected
  109.     if (analogRead(AMBIENT_LIGHT_SENSOR) < AMBIENT_LIGHT_MIN_LEVEL) {
  110.       simplexNoiseWalk(); // will keep running as long as the PIR sensor keeps getting tripped
  111.       end_time = 0; // fell out of the loop, so reset this
  112.       fadeToBlank(FADE_DOWN_TIME);
  113.     }
  114.   }
  115.   delay(5); // spin wheels for a tick. Going to replace with a proper sleep mode.
  116.  
  117. }
  118.  
  119. void isr_MovementDetected() {
  120.   // Interrupt Service Routince for the PIR sensor  
  121.   // end_time is usually 0, so setting this notifies the main loop that something has happened
  122.   end_time = millis() + light_duration * 1000;  
  123. }
  124.  
  125. void showFrame() {
  126.     // Show the current frame
  127.     int tlc_pin = 0;
  128.     // set the new values to display
  129.     for (int l=0;l<LAMPS;l++) {
  130.         for (int c=0;c<COLOURS;c++) {
  131.             Tlc.set(tlc_pin,frame[l][c]);
  132.             tlc_pin++;
  133.         }
  134.     }
  135.     // send the values to the driver chip
  136.     Tlc.update();
  137. }
  138.  
  139. void fadeUpToFrame(int fade_up_time) {
  140.     // fade up from blank to the current frame taking the given number of milliseconds (give or take)
  141.     float incr_values[LAMPS][COLOURS];
  142.     // need a floating point copy to do an accurate fade up in cases where the incr value is non-integer
  143.     float ghost_frame[LAMPS][COLOURS] = {0};
  144.     // number of steps and frame delay dictates how long the fade up will take
  145.     int frame_delay = 1; // milliseconds
  146.     float steps = fade_up_time/frame_delay;
  147.     // work out the increment factors to bring each lamp up to brightness evenly
  148.     for (int l=0;l<LAMPS;l++) {
  149.         for (int c=0;c<COLOURS;c++) {
  150.             incr_values[l][c] = frame[l][c]/steps;
  151.         }
  152.     }
  153.     // fade up
  154.     for (double i=0;i<steps;i++) {
  155.         for (int l=0;l<LAMPS;l++) {
  156.             for (int c=0;c<COLOURS;c++) {
  157.                 ghost_frame[l][c] += incr_values[l][c];
  158.                 frame[l][c] = ghost_frame[l][c];
  159.             }
  160.         }
  161.         showFrame();
  162.         delay(frame_delay);
  163.     }
  164. }
  165.  
  166. void fadeToBlank(int fade_down_time) {
  167.   // Fade the frame to blank taking the given number of milliseconds (give or take)
  168.     float decr_values[LAMPS][COLOURS];
  169.     float ghost_frame[LAMPS][COLOURS] = {0}; // need a floating point copy to keep track when decr < 1
  170.     // number of steps and frame delay dictates how long the fade will take
  171.     int frame_delay = 1; // milliseconds
  172.     float steps = fade_down_time/frame_delay;
  173.     // work out the decrement factors to bring each lamp down to blank evenly and initialize the ghost array
  174.     for (int l=0;l<LAMPS;l++) {
  175.         for (int c=0;c<COLOURS;c++) {
  176.             decr_values[l][c] = frame[l][c]/steps;
  177.             ghost_frame[l][c] = frame[l][c];
  178.         }
  179.     }
  180.     for (double i=0;i<steps;i++) {
  181.         for (int l=0;l<LAMPS;l++) {
  182.             for (int c=0;c<COLOURS;c++) {
  183.                 ghost_frame[l][c] -= decr_values[l][c];
  184.                 frame[l][c] = ghost_frame[l][c];
  185.             }
  186.         }
  187.         showFrame();
  188.         // check if there's been any movement during the fade down and cancel it if there was
  189.         if (end_time != 0) {
  190.           return;  
  191.         }
  192.         delay(frame_delay);
  193.     }
  194.     // make sure everything is off before leaving
  195.     blankFrame();
  196.     showFrame();
  197.    
  198. }
  199.    
  200. void blankFrame() {
  201.     // Reset the frame to all zeroes
  202.     for (int l=0;l<LAMPS;l++) {
  203.         for (int c=0;c<COLOURS;c++) {
  204.             frame[l][c] = 0;
  205.         }
  206.     }
  207. }
  208.  
  209. void testOne(int repetitions) {
  210.   // Turn each channel on and off again in turn
  211.   int time_delay = 100; //milliseconds
  212.   for (int r=0;r<repetitions;r++) {
  213.       for (int l=0;l<LAMPS;l++) {
  214.           for (int c=0;c<COLOURS;c++) {
  215.               frame[l][c] = LED_MAX_LEVEL;
  216.               showFrame();
  217.               delay(time_delay);
  218.               frame[l][c] = 0;
  219.               showFrame();
  220.               delay(time_delay);
  221.           }
  222.       }
  223.   }
  224. }
  225.  
  226. void testTwo(int repetitions) {
  227.    
  228.   // Turn each colour on and off again in turn
  229.   int time_delay = 250; //milliseconds
  230.   for (int r=0;r<repetitions;r++) {
  231.        for (int c=0;c<COLOURS;c++) {
  232.            for (int l=0;l<LAMPS;l++) {
  233.               frame[l][c] = LED_MAX_LEVEL;
  234.           }
  235.           fadeUpToFrame(time_delay);
  236.           delay(time_delay);
  237.           for (int l=0;l<LAMPS;l++) {
  238.               frame[l][c] = 0;
  239.           }
  240.           showFrame();
  241.           delay(time_delay);
  242.       }
  243.   }
  244. }    
  245.    
  246.  
  247. void allOn(int duration){
  248.   // Turn all channels on
  249.   for (int l=0;l<LAMPS;l++) {
  250.       for (int c=0;c<COLOURS;c++) {
  251.           frame[l][c] = LED_MAX_LEVEL;
  252.       }
  253.   }
  254.   showFrame();
  255.   delay(duration * 1000);
  256.  
  257. }
  258.  
  259. void makePotAdjustments () {
  260.   // Read the pots and set values as appropriate
  261.  
  262.   // work out how long to leave the lights on for
  263.   int pot_one = analogRead(ADJ_ONE);
  264.   // Using a staggered setting here. Fully off is 30 seconds
  265.   if (pot_one == 0) {
  266.       light_duration = 30;
  267.   } else if (pot_one <= 1000) {
  268.       // scaling this range to 31 - 21596 (approx 6 hours)
  269.       light_duration = pot_one * 21.59 + 6;
  270.   } else { // pot is over 1000
  271.       light_duration = 81600; // 24 hours
  272.   }
  273.  
  274.   // how fast to move through the colour space
  275.   int pot_two = analogRead(ADJ_TWO);
  276.   // Scale the input range from barely moving to fairly fast changes
  277.   time_inc = (double)pot_two/512000; // This seemed to give a nice range
  278.  
  279.   // offset between the lamps
  280.   int pot_three = analogRead(ADJ_THREE);
  281.   lamp_offset = (double)pot_three/1024;
  282.  
  283. }
  284.  
  285. void simplexNoiseWalk() {
  286.     // Walk through a simplex noise space generating values for the implementation's number
  287.     // of LAMPS and COLOURS
  288.  
  289.     const int LIMIT = 16384; // noise pattern stops changing after this, so reset if we reach it
  290.     // generate the first frame and fade up to it
  291.     for (int l=0;l<LAMPS;l++) {
  292.     generateSimplexNoisePattern(offset - (lamp_offset * l));
  293.     for (int c=0;c<COLOURS;c++) {
  294.         frame[l][c] = colours[c];
  295.         }
  296.         offset += time_inc; // how fast we move through the space
  297.     }
  298.     fadeUpToFrame(500);
  299.    
  300.     while (millis() < end_time) {
  301.         makePotAdjustments(); // this may make changes to some globals used in the colour generation
  302.         for (int l=0;l<LAMPS;l++) {
  303.         generateSimplexNoisePattern(offset - (lamp_offset * l));
  304.         for (int c=0;c<COLOURS;c++) {
  305.             frame[l][c] = colours[c];
  306.         }
  307.         offset += time_inc; // how fast we move through the space
  308.     }
  309.        
  310.         // make brightness adjustments
  311.         for (int l=0;l<LAMPS;l++) {
  312.             if (frame[l][RED] > red_max) {frame[l][RED] = red_max;}
  313.             if (frame[l][GREEN] > green_max) {frame[l][GREEN] = green_max;}
  314.             if (frame[l][BLUE] > blue_max) {frame[l][BLUE] = blue_max;}
  315.             if (frame[l][RED] < led_min_level) {frame[l][RED] = led_min_level;}
  316.             if (frame[l][GREEN] < led_min_level) {frame[l][GREEN] = led_min_level;}
  317.             if (frame[l][BLUE] < led_min_level) {frame[l][BLUE] = led_min_level;}
  318.         }
  319.         showFrame();
  320.         delay(FRAME_DELAY);
  321.         // wrap around if we ever get to the end of the space
  322.         if (offset > LIMIT) {
  323.             offset = 0;
  324.         }
  325.         // interrupt only triggers on a falling edge, so we need to check if movement
  326.         // is still being detected.
  327.         if (digitalRead(PIR_PIN) == LOW) {
  328.             end_time = millis() + light_duration * 1000;
  329.         }
  330.     }    
  331. }
  332.  
  333. void generateSimplexNoisePattern( double yoffset) {
  334.     // Calculate simplex noise for LEDs that are nodes:
  335.     // Store raw values from simplex function
  336.     double r = SimplexNoise(yoffset,0, 0);
  337.     double g = SimplexNoise(yoffset,1, 0);
  338.     double b = SimplexNoise(yoffset,2, 0);
  339.     // Convert values from raw noise to scaled r,g,b and feed to LEDs.
  340.     // Raw noise is +/-NOISE_LIMIT.
  341.     // Result converted to int here to save a step, some detail is lost, but not enough to worry about.
  342.     // Difference is about +/- 2 with a 12 bit range (0-4095) or 0.05%
  343.     colours[RED] = (r + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
  344.     colours[GREEN] = (g + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
  345.     colours[BLUE] = (b + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
  346.  
  347. }
  348.  
  349. /*****************************************************************************/
  350. // Simplex noise code:
  351. // From an original algorithm by Ken Perlin.
  352. // Returns a value in the range of about [-0.313 .. 0.347] <-- this may not be true depending on your c/c++ implementation and target processor
  353. double SimplexNoise(double x, double y, double z) {
  354.   // Skew input space to relative coordinate in simplex cell
  355.   s = (x + y + z) * onethird;
  356.   i = fastfloor(x+s);
  357.   j = fastfloor(y+s);
  358.   k = fastfloor(z+s);
  359.    
  360.   // Unskew cell origin back to (x, y , z) space
  361.   s = (i + j + k) * onesixth;
  362.   u = x - i + s;
  363.   v = y - j + s;
  364.   w = z - k + s;;
  365.    
  366.   A[0] = A[1] = A[2] = 0;
  367.    
  368.   // For 3D case, the simplex shape is a slightly irregular tetrahedron.
  369.   // Determine which simplex we're in
  370.   int hi = u >= w ? u >= v ? 0 : 1 : v >= w ? 1 : 2;
  371.   int lo = u < w ? u < v ? 0 : 1 : v < w ? 1 : 2;
  372.    
  373.   return k_fn(hi) + k_fn(3 - hi - lo) + k_fn(lo) + k_fn(0);
  374. }
  375.  
  376.  
  377. int fastfloor(double n) {
  378.   return n > 0 ? (int) n : (int) n - 1;
  379. }
  380.  
  381.  
  382. double k_fn(int a) {
  383.   s = (A[0] + A[1] + A[2]) * onesixth;
  384.   double x = u - A[0] + s;
  385.   double y = v - A[1] + s;
  386.   double z = w - A[2] + s;
  387.   double t = 0.6f - x * x - y * y - z * z;
  388.   int h = shuffle(i + A[0], j + A[1], k + A[2]);
  389.   A[a]++;
  390.   if (t < 0) return 0;
  391.   int b5 = h >> 5 & 1;
  392.   int b4 = h >> 4 & 1;
  393.   int b3 = h >> 3 & 1;
  394.   int b2 = h >> 2 & 1;
  395.   int b = h & 3;
  396.   double p = b == 1 ? x : b == 2 ? y : z;
  397.   double q = b == 1 ? y : b == 2 ? z : x;
  398.   double r = b == 1 ? z : b == 2 ? x : y;
  399.   p = b5 == b3 ? -p : p;
  400.   q = b5 == b4 ? -q: q;
  401.   r = b5 != (b4^b3) ? -r : r;
  402.   t *= t;
  403.   return 8 * t * t * (p + (b == 0 ? q + r : b2 == 0 ? q : r));
  404. }
  405.  
  406.  
  407. int shuffle(int i, int j, int k) {
  408.   return b(i, j, k, 0) + b(j, k, i, 1) + b(k, i, j, 2) + b(i, j, k, 3) + b(j, k, i, 4) + b(k, i, j, 5) + b(i, j, k, 6) + b(j, k, i, 7);
  409. }
  410.  
  411.  
  412. int b(int i, int j, int k, int B) {
  413.   return T[b(i, B) << 2 | b(j, B) << 1 | b(k, B)];
  414. }
  415.  
  416.  
  417. int b(int N, int B) {
  418.   return N >> B & 1;
  419. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement