Pārlūkot izejas kodu

Initial commit.

Gogs 5 gadi atpakaļ
revīzija
715d903728
3 mainītis faili ar 1546 papildinājumiem un 0 dzēšanām
  1. 932 0
      LEDWall.ino
  2. 527 0
      evo_shandor.ino
  3. 87 0
      otalib.ino

+ 932 - 0
LEDWall.ino

@@ -0,0 +1,932 @@
+#include <ESP32Lib.h>
+
+#define LEDPin 2
+#define LED_X_PIXELS 25
+#define LED_Y_PIXELS 20
+#define LEADER_PIXELS 0 // unused pixels at the beginning
+SerialLED gfx;
+
+#define STATUS 23 // We've started!
+
+#define seedPin 21 // an unused pin to see the RNG
+
+unsigned char frame[LED_X_PIXELS][LED_Y_PIXELS][3];
+unsigned char difference[LED_X_PIXELS][LED_Y_PIXELS];
+
+/* Animation Functions */
+void cycle(int delaytime); // cycle all pixels through red, green, then blue
+void chasePixel(int delaytime, unsigned char r = 255, unsigned char g = 255, unsigned char b = 255); // pixel left to right, up and down the display
+void fillAndClear(int delaytime, unsigned char r = 255, unsigned char g = 255, unsigned char b = 255); // Fill and then empty
+void mrsparky(unsigned long duration, int delaytime = 0); // chaos, unless there's a
+void squares(int count, int delaytime, bool dir); // squares - dir == true means from the inside out
+
+/* Helper functions */
+void printFrame(); // display whatever's in the frame
+void oneColor(unsigned char r, unsigned char g, unsigned char b); //set all to one color
+
+void setup() {
+  Serial.begin(115200);
+  ota_setup(); // Initialize WiFi and OTA
+
+  //gfx.setGamma(2.8f, 2.8f, 2.8f); // R, G, B gamma correction?
+  gfx.init(LEDPin, LED_X_PIXELS * LED_Y_PIXELS + LEADER_PIXELS, -1, -1);
+
+  Serial.println("Testing pixelMap:");
+  for (int y = LED_Y_PIXELS - 1; y >= 0; y--) {
+    Serial.print("Row "); Serial.print(y); Serial.print(": ");
+    for (int x = 0; x < LED_X_PIXELS; x++) {
+      Serial.print(pixelMap(x, y)); Serial.print(" ");
+    }
+    Serial.println("");
+  }
+
+  oneColor(0, 0, 0);
+  printFrame();
+
+  randomSeed(analogRead(seedPin));
+
+  pinMode(STATUS, OUTPUT);
+  digitalWrite(STATUS, HIGH);
+}
+
+void loop() {
+  vTaskDelay(10); // feed the other tasks
+  switch (random(0, 100)) {
+    case 0:
+      Serial.println("Cycle...");
+      cycle(random(1,15));
+      break;
+    case 1:
+      Serial.println("Chase Pixel...");
+      chasePixel(random(1,10), darkrandColor(), darkrandColor(), darkrandColor());
+      break;
+    case 2:
+      Serial.println("Fill and Clear...");
+      fillAndClear(random(1,10), darkrandColor(), darkrandColor(), darkrandColor());
+      break;
+    case 3:
+      Serial.println("Mr. Sparky...");
+      mrsparky(5000);
+      break;
+    case 4:
+      Serial.println("Mr. Sparky with delay (aka discoteque)...");
+      mrsparky(10000, 100);
+      break;
+    case 5:
+      Serial.println("Mr. Sparky with even longer delay (aka 50s?)...");
+      mrsparky(10000, 750);
+      break;
+    case 6:
+      Serial.println("Squares, inside out...");
+      squares(30, random(0, 100), random(2, 6), true);
+      break;
+    case 7:
+      Serial.println("Squares, outside in...");
+      squares(30, random(0, 100), random(2, 6), false);
+      break;
+    case 8:
+      Serial.println("Wormin...");
+      worm(random(10,70), random(500,1000));
+      break;
+    case 9:
+      Serial.println("Crazy Worms!");
+      crazyworm(random(10,70), random(500,1000));
+      break;
+    case 10:
+      Serial.println("Fading Worm...");
+      fadeworm(random(10,70), random(500,1000), (unsigned char) random(1,10));
+      break;
+    case 11:
+      Serial.println("Starfall...");
+      starfall(random(10,50), random(500,1000), (unsigned char) random(1,10), random(5, 20));
+      break;
+    case 12:
+      Serial.println("Starfall...");
+      rainbowfall(random(10,50), random(500,1000), (unsigned char) random(1,10), random(5, 20));
+      break;
+    case 13:
+      Serial.println("Random cycle...");
+      randomcycle(random(0, 7), random(2000, 5000), random(500, 2000));
+      break;
+    case 14:
+      Serial.println("Gradient cycle...");
+      gradientcycle(random(0, 7), random(2000, 10000), random(500, 2000));
+      break;
+    case 15:
+      Serial.println("Evo Shandor...");
+      show_evo(500);
+      break;
+    default:
+      Serial.println("Oops... no selection... that's NUMBERWANG!");
+  }
+}
+
+unsigned char randColor() {
+  // weighted to prefer the lower 128
+  if (random(0, 1)) {
+    return (unsigned char) random(0, 255);
+  } else {
+    return (unsigned char) random(0, 128);
+  }
+}
+
+unsigned char darkrandColor() {
+  // Weighted even more for lower numbers
+  switch (random(1, 3)) {
+    //case 0: return (unsigned char) random(0, 31);
+    case 1: return (unsigned char) random(0, 63);
+    case 2: return (unsigned char) random(0, 127);
+    case 3: return (unsigned char) random(0, 255);
+  }
+  // shouldn't get here
+  return (unsigned char) random(0, 255);
+}
+
+void mrsparky(unsigned long duration, int delaytime) {
+  unsigned long starttime = millis();
+  while (millis() - starttime < duration) {
+    randFrame();
+    printFrame();
+    if (delaytime > 0) {
+      delay(delaytime);
+    }
+  }
+}
+
+void fillAndClear(int delaytime, unsigned char r, unsigned char g, unsigned char b) {
+  oneColor(0, 0, 0);
+  printFrame();
+
+  // fill with color
+  for (int y = 0; y < LED_Y_PIXELS; y++) {
+    for (int x = 0; x < LED_X_PIXELS; x++) {
+      frame[x][y][0] = r;
+      frame[x][y][1] = g;
+      frame[x][y][2] = b;
+      printFrame();
+      delay(delaytime);
+    }
+  }
+  // back to black
+  for (int y = LED_Y_PIXELS - 1; y >= 0; y--) {
+    for (int x = LED_X_PIXELS - 1; x >= 0; x--) {
+      frame[x][y][0] = 0;
+      frame[x][y][1] = 0;
+      frame[x][y][2] = 0;
+      printFrame();
+      delay(delaytime);
+    }
+  }
+}
+
+void chasePixel(int delaytime, unsigned char r, unsigned char g, unsigned char b) {
+  oneColor(0, 0, 0);
+  printFrame();
+
+  for (int y = 0; y < LED_Y_PIXELS; y++) {
+    if (y % 2 == 0) {
+      // even rows go left to right:
+      for (int x = 0; x < LED_X_PIXELS; x++) {
+        frame[x][y][0] = r;
+        frame[x][y][1] = g;
+        frame[x][y][2] = b;
+        printFrame();
+        delay(delaytime);
+        oneColor(0, 0, 0);
+      }
+    } else {
+      // odd rows go right to left
+      for (int x = LED_X_PIXELS - 1; x >= 0; x--) {
+        frame[x][y][0] = r;
+        frame[x][y][1] = g;
+        frame[x][y][2] = b;
+        printFrame();
+        delay(delaytime);
+        oneColor(0, 0, 0);
+      }
+    }
+  }
+  for (int y = LED_Y_PIXELS - 1; y >= 0; y--) {
+    if (y % 2 == 0) {
+      // even rows go right to left:
+      for (int x = LED_X_PIXELS - 1; x >= 0; x--) {
+        frame[x][y][0] = r;
+        frame[x][y][1] = g;
+        frame[x][y][2] = b;
+        printFrame();
+        delay(delaytime);
+        oneColor(0, 0, 0);
+      }
+    } else {
+      // odd rows go left to right
+      for (int x = 0; x < LED_X_PIXELS; x++) {
+        frame[x][y][0] = r;
+        frame[x][y][1] = g;
+        frame[x][y][2] = b;
+        printFrame();
+        delay(delaytime);
+        oneColor(0, 0, 0);
+      }
+    }
+  }
+}
+
+void cycle(int delaytime) {
+  // cycle all pixels through red, green, then blue
+  for (unsigned char r = 0; r <= 254; r++) {
+    oneColor(r, 0, 0);
+    printFrame();
+    delay(delaytime);
+  }
+  for (unsigned char r = 255; r > 0; r--) {
+    oneColor(r, 0, 0);
+    printFrame();
+    delay(delaytime);
+  }
+  for (unsigned char g = 0; g <= 254; g++) {
+    oneColor(0, g, 0);
+    printFrame();
+    delay(delaytime);
+  }
+  for (unsigned char g = 255; g > 0; g--) {
+    oneColor(0, g, 0);
+    printFrame();
+    delay(delaytime);
+  }
+  for (unsigned char b = 0; b <= 254; b++) {
+    oneColor(0, 0, b);
+    printFrame();
+    delay(delaytime);
+  }
+  for (unsigned char b = 255; b > 0; b--) {
+    oneColor(0, 0, b);
+    printFrame();
+    delay(delaytime);
+  }
+  oneColor(0, 0, 0);
+  printFrame();
+}
+
+void printFrame() {
+  for (int l = 0; l < LEADER_PIXELS; l++) {
+    // Turn any lead pixels off
+    gfx.setLED(l, 0, 0, 0);
+  }
+  for (int x = 0; x < LED_X_PIXELS; x++) {
+    for (int y = 0; y < LED_Y_PIXELS; y++) {
+      //Serial.print("Setting pixel "); Serial.print(pixelMap(x, y));
+      //Serial.print(" to R:"); Serial.print((int) frame[x][y][0]);
+      //Serial.print(" G:"); Serial.print((int) frame[x][y][1]);
+      //Serial.print(" B:"); Serial.println((int) frame[x][y][2]);
+      gfx.setLED(pixelMap(x, y), frame[x][y][0], frame[x][y][1], frame[x][y][2]);
+    }
+  }
+}
+
+int pixelMap(int x, int y) {
+  // Translates x and y into a pixel number
+  // (0, 0) is bottom left, (LED_X_PIXELS-1, LED_Y_PIXELS-1) is top right)
+  if (x < 0 || x >= LED_X_PIXELS || y < 0 || y >= LED_Y_PIXELS) {
+    Serial.print("ERROR: Cannot map ("); Serial.print(x); Serial.print(","); Serial.print(y); Serial.println(") into wall map");
+    return -1;
+  }
+  x = LED_X_PIXELS - x - 1; // x starts at bottom right, so invert. If x is at bottom left, comment this out.
+
+  int pixel = y * LED_X_PIXELS;
+  if (y % 2 == 0) {
+    // even rows are added
+    pixel += x;
+  } else {
+    // odd rows are subtracted from the next one
+    pixel += LED_X_PIXELS - x - 1;
+  }
+  //assert(pixel < LED_X_PIXELS * LED_Y_PIXELS);
+  return pixel + LEADER_PIXELS;
+}
+
+void oneColor(unsigned char r, unsigned char g, unsigned char b) {
+  // Sets the frame to an initial design
+  for (int x = 0; x < LED_X_PIXELS; x++) {
+    for (int y = 0; y < LED_Y_PIXELS; y++) {
+      frame[x][y][0] = r; // Red
+      frame[x][y][1] = g; // Green
+      frame[x][y][2] = b; // Blue
+    }
+  }
+}
+
+void randFrame() {
+  // Generates a random frame
+  for (int x = 0; x < LED_X_PIXELS; x++) {
+    for (int y = 0; y < LED_Y_PIXELS; y++) {
+      frame[x][y][0] = randColor(); // Red
+      frame[x][y][1] = randColor(); // Green
+      frame[x][y][2] = randColor(); // Blue
+    }
+  }
+}
+
+/* Does nto work at all:
+  void Flames(int delaytime) {
+  for(int y = 0; y < LED_Y_PIXELS; y++) {
+    for(int x = 0; x < LED_X_PIXELS; x++) {
+      int c = pixelMap(x,y);
+      frame[x][y][0] = c;
+      frame[x][y][1] = (c * c) / (255);
+      frame[x][y][2] = (c * c) / (255 * 8);
+    }
+  }
+  printFrame();
+  delay(delaytime);
+
+  for(int c = 0; c < 255; c++) {
+    for(int y = 0; y < LED_Y_PIXELS; y++) {
+      for(int x = 0; x < LED_X_PIXELS; x++) {
+        if(frame[x][y][0] > 127) {
+          // Big number, probably intensify
+          if(random(0, 4) > 0) {
+            // intensify!
+            int diff = 255 - frame[x][y][0];
+            frame[x][y][0] += random(0, diff);
+          } else {
+            // detensify!
+            frame[x][y][0] -= random(0, 127);
+          }
+        } else {
+          // small number, probably weaken
+          // Big number, probably intensify
+          if(random(0, 4) > 0) {
+            // detensify!
+            frame[x][y][0] -= random(0, frame[x][y][0]);
+          } else {
+            // intensify!
+            frame[x][y][0] += random(0, 127);
+          }
+        }
+        frame[x][y][1] = (frame[x][y][0] * frame[x][y][0]) / 255;
+        frame[x][y][2] = (frame[x][y][0] * frame[x][y][0]) / (255 * 8);
+      }
+    }
+    printFrame();
+    delay(delaytime);
+  }
+  }
+*/
+
+int maxSquares(void) {
+  // returns the maximum number of squares that can be drawn.
+  // Note that squares can be drawn "outside" of one dimension.
+  int maxdim = min(LED_X_PIXELS, LED_Y_PIXELS);
+  // on an odd number, inside square is 1x1
+  return floor(maxdim / 2);
+}
+
+void drawSquare(int squareno, unsigned char r, unsigned char g, unsigned char b) {
+  // draws the nth square, counting from 0 from the middle out
+  if (squareno > maxSquares()) {
+    return;
+  }
+  int center_x = floor((LED_X_PIXELS - 1) / 2);
+  int center_width = LED_X_PIXELS % 2 == 0 ? 2 : 1; // odd numbers have a 1x1 center
+  int center_y = floor((LED_Y_PIXELS - 1) / 2);
+  int center_height = LED_Y_PIXELS % 2 == 0 ? 2 : 1;
+
+  int x_left  = center_x - squareno;
+  int x_right = center_x + center_width + squareno - 1;
+  int y_bottom = center_y - squareno;
+  int y_top    = center_y + center_height + squareno - 1;
+  Serial.print("Dimensions: X_left: "); Serial.print(x_left);
+  Serial.print("; X_Right: "); Serial.print(x_right);
+  Serial.print("; Y_Bottom: "); Serial.print(y_bottom);
+  Serial.print("; Y_top: "); Serial.print(y_top);
+  Serial.print("; Center_x: "); Serial.print(center_x);
+  Serial.print("; Center_y: "); Serial.print(center_y);
+  Serial.print("; Center_Width: "); Serial.print(center_width);
+  Serial.print("; Center_Height: "); Serial.println(center_height);
+
+  // top and bottom lines
+  for (int x = x_left; x <= x_right; x++) {
+    if (x < 0 || x > LED_X_PIXELS - 1 || y_bottom < 0 || y_top > LED_Y_PIXELS - 1) {
+      Serial.print("  Skipping: ("); Serial.print(x);
+      continue;
+    }
+    frame[x][y_bottom][0] = r;
+    frame[x][y_bottom][1] = g;
+    frame[x][y_bottom][2] = b;
+    frame[x][y_top][0] = r;
+    frame[x][y_top][1] = g;
+    frame[x][y_top][2] = b;
+  }
+
+  // left and right lines
+  for (int y = y_bottom; y <= y_top; y++) {
+    if (x_left < 0 || x_right > LED_X_PIXELS - 1 || y < 0 || y > LED_Y_PIXELS - 1) {
+      continue;
+    }
+    frame[x_left][y][0] = r;
+    frame[x_left][y][1] = g;
+    frame[x_left][y][2] = b;
+    frame[x_right][y][0] = r;
+    frame[x_right][y][1] = g;
+    frame[x_right][y][2] = b;
+  }
+}
+
+void squares(int count, int delaytime, int numsquares, bool dir) {
+  numsquares += 2; // 2 black squares
+  oneColor(0, 0, 0);
+  // draws squares moving out
+  unsigned char colors[255][3];
+  colors[0][0] = 0; //black makes the motion clearer
+  colors[0][1] = 0;
+  colors[0][2] = 0;
+  colors[1][0] = 0; //black makes the motion clearer
+  colors[1][1] = 0;
+  colors[1][2] = 0;
+  for (int i = 2; i < numsquares; i++) {
+    // assign random colors
+    colors[i][0] = randColor();
+    colors[i][1] = randColor();
+    colors[i][2] = randColor();
+  }
+
+  if (dir) {
+    // inside out
+    for (int i = count; i > 0; i--) {
+      // draw each of the squares
+      for (int squareno = 0; squareno < maxSquares(); squareno++) {
+        Serial.println("--------------");
+        Serial.print("Drawing Square: "); Serial.println(squareno);
+        drawSquare(squareno, colors[(i + squareno) % numsquares][0], colors[(i + squareno) % numsquares][1], colors[(i + squareno) % numsquares][2]);
+      }
+      printFrame();
+      delay(delaytime);
+    }
+  } else {
+    for (int i = 0; i < count; i++) {
+      // draw each of the squares
+      for (int squareno = 0; squareno < maxSquares(); squareno++) {
+        Serial.println("--------------");
+        Serial.print("Drawing Square: "); Serial.println(squareno);
+        drawSquare(squareno, colors[(i + squareno) % numsquares][0], colors[(i + squareno) % numsquares][1], colors[(i + squareno) % numsquares][2]);
+      }
+      printFrame();
+      delay(delaytime);
+    }
+  }
+}
+
+void worm(int delaytime, int steps) {
+  int x = random(3, LED_X_PIXELS - 3);
+  int y = random(3, LED_Y_PIXELS - 3);
+  unsigned char color[3];
+  color[0] = randColor();
+  color[1] = randColor();
+  color[2] = randColor();
+  unsigned char trailcolor[3];
+  trailcolor[0] = randColor();
+  trailcolor[1] = randColor();
+  trailcolor[2] = randColor();
+  unsigned char bgcolor[3];
+  bgcolor[0] = darkrandColor();
+  bgcolor[1] = darkrandColor();
+  bgcolor[2] = darkrandColor();
+
+  oneColor(bgcolor[0], bgcolor[1], bgcolor[2]);
+  printFrame();
+
+  bool moved = false;
+  int lastmove = -1;
+  for (int step = 0; step < steps; step++) {
+    frame[x][y][0] = color[0];
+    frame[x][y][1] = color[1];
+    frame[x][y][2] = color[2];
+    printFrame();
+    // it's going to bet the trail color next
+    frame[x][y][0] = trailcolor[0];
+    frame[x][y][1] = trailcolor[1];
+    frame[x][y][2] = trailcolor[2];
+    delay(delaytime);
+
+    // move randomly
+    moved = false;
+    int move;
+    while (moved == false) {
+      move = random(0, 12);
+      if (move > 3) {
+        if (lastmove == 0) {
+          // the odd case of this being our first move
+          move = random(0, 3);
+        }
+        move = lastmove; //repeat the last move
+      }
+      switch (move) {
+        case 0: // move up
+          if (lastmove != 1 && y < LED_Y_PIXELS - 1) {
+            lastmove = 0;
+            y++;
+            moved = true;
+          }
+          break;
+        case 1: // move down
+          if (lastmove != 0 && y > 0) {
+            lastmove = 1;
+            y--;
+            moved = true;
+          }
+          break;
+        case 2: // move right
+          if (lastmove != 3 && x < LED_X_PIXELS - 1) {
+            lastmove = 2;
+            x++;
+            moved = true;
+          }
+          break;
+        case 3: // move left
+          if (lastmove != 2 && x > 0) {
+            lastmove = 3;
+            x--;
+            moved = true;
+          }
+          break;
+      }
+    }
+  }
+}
+
+void crazyfadebyint(int factor) {
+  for (int x = 0; x < LED_X_PIXELS; x++) {
+    for (int y = 0; y < LED_Y_PIXELS; y++) {
+      if (frame[x][y][0] > factor) {
+        frame[x][y][0] -= factor;
+      } else {
+        frame[x][y][0] = 0;
+      }
+      if (frame[x][y][1] > factor) {
+        frame[x][y][1] -= factor;
+      } else {
+        frame[x][y][1] = 0;
+      }
+      if (frame[x][y][2] > factor) {
+        frame[x][y][2] -= factor;
+      } else {
+        frame[x][y][2] = 0;
+      }
+    }
+  }
+}
+
+void crazyworm(int delaytime, int steps) {
+  int x = random(3, LED_X_PIXELS - 3);
+  int y = random(3, LED_Y_PIXELS - 3);
+  unsigned char color[3];
+  color[0] = randColor();
+  color[1] = randColor();
+  color[2] = randColor();
+
+  oneColor(0, 0, 0);
+  printFrame();
+
+  bool moved = false;
+  int lastmove = -1;
+  for (int step = 0; step < steps; step++) {
+    crazyfadebyint(-10);
+    frame[x][y][0] = color[0];
+    frame[x][y][1] = color[1];
+    frame[x][y][2] = color[2];
+    printFrame();
+    delay(delaytime);
+
+    // move randomly
+    moved = false;
+    int move;
+    while (moved == false) {
+      move = random(0, 12);
+      if (move > 3) {
+        if (lastmove == 0) {
+          // the odd case of this being our first move
+          move = random(0, 3);
+        }
+        move = lastmove; //repeat the last move
+      }
+      switch (move) {
+        case 0: // move up
+          if (lastmove != 1 && y < LED_Y_PIXELS - 1) {
+            lastmove = 0;
+            y++;
+            moved = true;
+          }
+          break;
+        case 1: // move down
+          if (lastmove != 0 && y > 0) {
+            lastmove = 1;
+            y--;
+            moved = true;
+          }
+          break;
+        case 2: // move right
+          if (lastmove != 3 && x < LED_X_PIXELS - 1) {
+            lastmove = 2;
+            x++;
+            moved = true;
+          }
+          break;
+        case 3: // move left
+          if (lastmove != 2 && x > 0) {
+            lastmove = 3;
+            x--;
+            moved = true;
+          }
+          break;
+      }
+    }
+  }
+}
+
+
+void fadebyint(unsigned char factor) {
+  for (int x = 0; x < LED_X_PIXELS; x++) {
+    for (int y = 0; y < LED_Y_PIXELS; y++) {
+      //Serial.print('Current R: ');
+      //Serial.print(frame[x][y][0]);
+      //Serial.print('Current G: ');
+      //Serial.print(frame[x][y][1]);
+      //Serial.print('Current B: ');
+      //Serial.println(frame[x][y][2]);
+      if (frame[x][y][0] > factor) {
+        frame[x][y][0] -= factor;
+      } else {
+        frame[x][y][0] = 0;
+      }
+      if (frame[x][y][1] > factor) {
+        frame[x][y][1] -= factor;
+      } else {
+        frame[x][y][1] = 0;
+      }
+      if (frame[x][y][2] > factor) {
+        frame[x][y][2] -= factor;
+      } else {
+        frame[x][y][2] = 0;
+      }
+      //Serial.print('Faded R: ');
+      //Serial.print(frame[x][y][0]);
+      //Serial.print('Faded G: ');
+      //Serial.print(frame[x][y][1]);
+      //Serial.print('Faded B: ');
+      //Serial.println(frame[x][y][2]);
+    }
+  }
+}
+
+void fadeworm(int delaytime, int steps, unsigned char fadefactor) {
+  int x = random(3, LED_X_PIXELS - 3);
+  int y = random(3, LED_Y_PIXELS - 3);
+  unsigned char color[3];
+  color[0] = randColor();
+  color[1] = randColor();
+  color[2] = randColor();
+
+  oneColor(0, 0, 0);
+  printFrame();
+
+  bool moved = false;
+  int lastmove = -1;
+  for (int step = 0; step < steps; step++) {
+    fadebyint(fadefactor);
+    frame[x][y][0] = color[0];
+    frame[x][y][1] = color[1];
+    frame[x][y][2] = color[2];
+    printFrame();
+    delay(delaytime);
+
+    // move randomly
+    moved = false;
+    int move;
+    while (moved == false) {
+      move = random(0, 12);
+      if (move > 3) {
+        if (lastmove == 0) {
+          // the odd case of this being our first move
+          move = random(0, 3);
+        }
+        move = lastmove; //repeat the last move
+      }
+      switch (move) {
+        case 0: // move up
+          if (lastmove != 1 && y < LED_Y_PIXELS - 1) {
+            lastmove = 0;
+            y++;
+            moved = true;
+          }
+          break;
+        case 1: // move down
+          if (lastmove != 0 && y > 0) {
+            lastmove = 1;
+            y--;
+            moved = true;
+          }
+          break;
+        case 2: // move right
+          if (lastmove != 3 && x < LED_X_PIXELS - 1) {
+            lastmove = 2;
+            x++;
+            moved = true;
+          }
+          break;
+        case 3: // move left
+          if (lastmove != 2 && x > 0) {
+            lastmove = 3;
+            x--;
+            moved = true;
+          }
+          break;
+      }
+    }
+  }
+}
+
+int starfall(int delaytime, int steps, unsigned char fadefactor, int chance) {
+  int pos[LED_X_PIXELS];
+  unsigned char color[3];
+  color[0] = randColor();
+  color[1] = randColor();
+  color[2] = randColor();
+
+  oneColor(0, 0, 0);
+
+  // initialize starting positions
+  for (int p = 0; p < LED_X_PIXELS; p++) {
+    if (random(0, 100) <= chance) {
+      pos[p] = random(0, LED_Y_PIXELS);
+      frame[p][pos[p]][0] = color[0];
+      frame[p][pos[p]][1] = color[1];
+      frame[p][pos[p]][2] = color[2];
+    } else {
+      pos[p] = -1;
+    }
+  }
+
+  printFrame();
+
+  for (int step = 0; step < steps; step++) {
+    fadebyint(fadefactor);
+    for (int p = 0; p < LED_X_PIXELS; p++) {
+      if (pos[p] == 0) {
+        //Serial.print("Position "); Serial.print(p); Serial.println(" hit bottom");
+        pos[p] = -1; // it hit the bottom
+      }
+      if (pos[p] > 0) {
+        //Serial.print("Position "); Serial.print(p); Serial.print(" dropping from "); Serial.println(pos[p]);
+        pos[p]--; // drop down one
+        frame[p][pos[p]][0] = color[0];
+        frame[p][pos[p]][1] = color[1];
+        frame[p][pos[p]][2] = color[2];
+      }
+      if (pos[p] == -1 && random(0, 100) <= chance) {
+        // spawn a new one
+        //Serial.print("Position "); Serial.print(p); Serial.println(" spawning.");
+        pos[p] = LED_Y_PIXELS - 1;
+        frame[p][pos[p]][0] = color[0];
+        frame[p][pos[p]][1] = color[1];
+        frame[p][pos[p]][2] = color[2];
+      }
+    }
+    printFrame();
+    delay(delaytime);
+  }
+}
+
+int rainbowfall(int delaytime, int steps, unsigned char fadefactor, int chance) {
+  int pos[LED_X_PIXELS];
+  unsigned char colors[LED_X_PIXELS][3];
+
+  oneColor(0, 0, 0);
+
+  // initialize starting positions
+  for (int p = 0; p < LED_X_PIXELS; p++) {
+    if (random(0, 100) <= chance) {
+      pos[p] = random(0, LED_Y_PIXELS);
+      frame[p][pos[p]][0] = colors[p][0] = randColor();
+      frame[p][pos[p]][1] = colors[p][1] = randColor();
+      frame[p][pos[p]][2] = colors[p][2] = randColor();
+    } else {
+      pos[p] = -1;
+    }
+  }
+
+  printFrame();
+
+  for (int step = 0; step < steps; step++) {
+    fadebyint(fadefactor);
+    for (int p = 0; p < LED_X_PIXELS; p++) {
+      if (pos[p] == 0) {
+        //Serial.print("Position "); Serial.print(p); Serial.println(" hit bottom");
+        pos[p] = -1; // it hit the bottom
+      }
+      if (pos[p] > 0) {
+        //Serial.print("Position "); Serial.print(p); Serial.print(" dropping from "); Serial.println(pos[p]);
+        pos[p]--; // drop down one
+        frame[p][pos[p]][0] = colors[p][0];
+        frame[p][pos[p]][1] = colors[p][1];
+        frame[p][pos[p]][2] = colors[p][2];
+      }
+      if (pos[p] == -1 && random(0, 100) <= chance) {
+        // spawn a new one
+        //Serial.print("Position "); Serial.print(p); Serial.println(" spawning.");
+        pos[p] = LED_Y_PIXELS - 1;
+        frame[p][pos[p]][0] = colors[p][0] = randColor();
+        frame[p][pos[p]][1] = colors[p][1] = randColor();
+        frame[p][pos[p]][2] = colors[p][2] = randColor();
+      }
+    }
+    printFrame();
+    delay(delaytime);
+  }
+}
+
+void randomcycle(int delaytime, int steps, int stepspercycle) {
+  // start with random colors
+  unsigned char colors[LED_X_PIXELS][LED_Y_PIXELS][3];
+  for (int x = 0; x < LED_X_PIXELS; x++) {
+    for (int y = 0; y < LED_Y_PIXELS; y++) {
+      colors[x][y][0] = randColor();
+      colors[x][y][1] = randColor();
+      colors[x][y][2] = randColor();
+    }
+  }
+
+  for (int step = 0; step < steps; step++) {
+    float factor = 0.5 + 0.5 * sin( (2.0 * 3.14 * step / stepspercycle));
+    //Serial.print("Factor = "); Serial.println(factor);
+    for (int x = 0; x < LED_X_PIXELS; x++) {
+      for (int y = 0; y < LED_Y_PIXELS; y++) {
+        frame[x][y][0] = (unsigned char) floor(factor * colors[x][y][0]);
+        frame[x][y][1] = (unsigned char) floor(factor * colors[x][y][1]);
+        frame[x][y][2] = (unsigned char) floor(factor * colors[x][y][2]);
+      }
+    }
+    printFrame();
+    delay(delaytime);
+  }
+}
+
+float distance(int x1, int x2, int y1, int y2) {
+  return (float) sqrt( pow((x2 - x1), 2) + pow((y2 - y1), 2) );
+}
+
+void one_gradientcycle(int delaytime, int stepspercycle) {
+  int c_x = random(0, LED_X_PIXELS);
+  int c_y = random(0, LED_Y_PIXELS);
+  unsigned char basecolor[3];
+  basecolor[0] = randColor();
+  basecolor[1] = randColor();
+  basecolor[2] = randColor();
+
+  float maxdistance = distance(c_x, 0, c_y, 0);
+
+  unsigned char colors[LED_X_PIXELS][LED_Y_PIXELS][3];
+  for (int x = 0; x < LED_X_PIXELS; x++) {
+    for (int y = 0; y < LED_Y_PIXELS; y++) {
+      float gradfactor = 0.5 + 0.5 * sin( 2.0 * 3.14 * distance(x, c_x, y, c_y) / maxdistance - 3.14/2);
+      colors[x][y][0] = (unsigned char) floor(gradfactor * basecolor[0]);
+      colors[x][y][1] = (unsigned char) floor(gradfactor * basecolor[1]);
+      colors[x][y][2] = (unsigned char) floor(gradfactor * basecolor[2]);
+      //Serial.print("Base color: "); Serial.print(colors[x][y][0]);
+      //Serial.print("; Distance: "); Serial.print(distance(x,y));
+      //Serial.print("; Factor: "); Serial.print(gradfactor);
+      //Serial.print("; Setting color: "); Serial.println(colors[x][y][0]);
+    }
+  }
+
+  for (int step = 0; step < stepspercycle; step++) {
+    float factor = 0.5 + 0.5 * sin( -3.14/2 + (2.0 * 3.14 * step / stepspercycle));
+    //Serial.print("Factor = "); Serial.println(factor);
+    for (int x = 0; x < LED_X_PIXELS; x++) {
+      for (int y = 0; y < LED_Y_PIXELS; y++) {
+        frame[x][y][0] = (unsigned char) floor(factor * colors[x][y][0]);
+        frame[x][y][1] = (unsigned char) floor(factor * colors[x][y][1]);
+        frame[x][y][2] = (unsigned char) floor(factor * colors[x][y][2]);
+      }
+    }
+    printFrame();
+    delay(delaytime);
+  }
+}
+
+void gradientcycle(int delaytime, int steps, int stepspercycle) {
+  for(int step = 0; step < steps/stepspercycle; step++) {
+    one_gradientcycle(delaytime, stepspercycle);
+  }
+}
+
+/*
+ * still not sure how to do this since we're not a square.
+void pilotsquare(int delaytime, int steps) {
+  int center_x = floor((LED_X_PIXELS - 1) / 2);
+  int center_width = LED_X_PIXELS % 2 == 0 ? 2 : 1; // odd numbers have a 1x1 center
+  int center_y = floor((LED_Y_PIXELS - 1) / 2);
+  int center_height = LED_Y_PIXELS % 2 == 0 ? 2 : 1;  
+
+  
+}
+*/

+ 527 - 0
evo_shandor.ino

@@ -0,0 +1,527 @@
+unsigned char evo_shandor[20][25][3] = {
+{
+  { 22, 26, 32 },
+  { 24, 27, 33 },
+  { 21, 26, 35 },
+  { 51, 47, 53 },
+  { 143, 116, 105 },
+  { 129, 97, 87 },
+  { 39, 38, 44 },
+  { 25, 31, 42 },
+  { 25, 28, 35 },
+  { 20, 23, 30 },
+  { 19, 22, 29 },
+  { 19, 23, 27 },
+  { 21, 24, 30 },
+  { 21, 24, 31 },
+  { 21, 24, 31 },
+  { 20, 23, 30 },
+  { 19, 22, 29 },
+  { 17, 21, 25 },
+  { 10, 15, 19 },
+  { 50, 44, 43 },
+  { 110, 83, 78 },
+  { 77, 58, 52 },
+  { 19, 20, 17 },
+  { 12, 14, 13 },
+  { 14, 15, 15 }
+},
+{
+  { 30, 32, 41 },
+  { 29, 33, 42 },
+  { 26, 33, 42 },
+  { 55, 47, 56 },
+  { 180, 114, 113 },
+  { 139, 89, 90 },
+  { 36, 36, 45 },
+  { 33, 35, 48 },
+  { 28, 32, 40 },
+  { 23, 26, 34 },
+  { 22, 25, 32 },
+  { 21, 24, 31 },
+  { 20, 23, 28 },
+  { 19, 23, 28 },
+  { 21, 24, 30 },
+  { 21, 24, 31 },
+  { 20, 23, 31 },
+  { 18, 21, 26 },
+  { 11, 16, 18 },
+  { 49, 40, 38 },
+  { 130, 87, 75 },
+  { 128, 78, 67 },
+  { 34, 26, 25 },
+  { 10, 14, 14 },
+  { 14, 15, 14 }
+},
+{
+  { 35, 39, 54 },
+  { 35, 39, 52 },
+  { 31, 38, 50 },
+  { 80, 62, 69 },
+  { 187, 111, 118 },
+  { 114, 67, 86 },
+  { 31, 36, 50 },
+  { 35, 37, 50 },
+  { 30, 36, 50 },
+  { 29, 34, 43 },
+  { 25, 27, 34 },
+  { 22, 25, 32 },
+  { 18, 22, 26 },
+  { 17, 21, 24 },
+  { 20, 24, 29 },
+  { 21, 24, 31 },
+  { 20, 23, 31 },
+  { 19, 22, 27 },
+  { 16, 19, 21 },
+  { 9, 14, 16 },
+  { 89, 49, 63 },
+  { 159, 72, 76 },
+  { 67, 40, 39 },
+  { 11, 14, 14 },
+  { 15, 15, 15 }
+},
+{
+  { 36, 43, 59 },
+  { 35, 41, 57 },
+  { 42, 44, 55 },
+  { 146, 102, 87 },
+  { 171, 124, 99 },
+  { 80, 60, 55 },
+  { 31, 37, 52 },
+  { 33, 39, 51 },
+  { 32, 37, 54 },
+  { 31, 37, 50 },
+  { 24, 29, 38 },
+  { 27, 30, 36 },
+  { 34, 34, 38 },
+  { 38, 37, 40 },
+  { 23, 24, 28 },
+  { 17, 21, 25 },
+  { 17, 21, 25 },
+  { 17, 21, 24 },
+  { 15, 19, 22 },
+  { 11, 14, 20 },
+  { 61, 49, 43 },
+  { 164, 110, 88 },
+  { 114, 77, 67 },
+  { 19, 16, 18 },
+  { 16, 17, 17 }
+},
+{
+  { 38, 44, 60 },
+  { 37, 41, 59 },
+  { 58, 50, 62 },
+  { 180, 139, 125 },
+  { 169, 124, 108 },
+  { 90, 58, 50 },
+  { 31, 36, 52 },
+  { 36, 38, 55 },
+  { 37, 39, 56 },
+  { 35, 38, 50 },
+  { 84, 77, 78 },
+  { 99, 88, 82 },
+  { 107, 93, 87 },
+  { 114, 97, 90 },
+  { 79, 66, 59 },
+  { 42, 39, 35 },
+  { 17, 20, 24 },
+  { 17, 21, 22 },
+  { 16, 18, 20 },
+  { 8, 12, 17 },
+  { 66, 50, 47 },
+  { 154, 119, 110 },
+  { 123, 82, 70 },
+  { 36, 26, 25 },
+  { 14, 15, 15 }
+},
+{
+  { 38, 42, 59 },
+  { 37, 40, 57 },
+  { 48, 43, 56 },
+  { 164, 127, 114 },
+  { 186, 138, 123 },
+  { 143, 95, 80 },
+  { 81, 61, 62 },
+  { 36, 38, 52 },
+  { 31, 36, 50 },
+  { 76, 69, 72 },
+  { 132, 110, 96 },
+  { 143, 100, 86 },
+  { 153, 108, 99 },
+  { 132, 91, 77 },
+  { 91, 68, 53 },
+  { 79, 57, 48 },
+  { 40, 34, 34 },
+  { 15, 18, 23 },
+  { 16, 18, 23 },
+  { 48, 44, 46 },
+  { 103, 77, 72 },
+  { 155, 118, 111 },
+  { 121, 77, 64 },
+  { 32, 22, 20 },
+  { 12, 15, 15 }
+},
+{
+  { 39, 40, 58 },
+  { 39, 41, 55 },
+  { 36, 38, 53 },
+  { 65, 51, 59 },
+  { 180, 140, 131 },
+  { 202, 163, 150 },
+  { 185, 143, 126 },
+  { 95, 65, 59 },
+  { 49, 46, 54 },
+  { 80, 67, 67 },
+  { 114, 80, 65 },
+  { 180, 112, 96 },
+  { 210, 137, 123 },
+  { 182, 118, 100 },
+  { 105, 68, 55 },
+  { 80, 58, 50 },
+  { 67, 50, 41 },
+  { 22, 24, 24 },
+  { 105, 87, 84 },
+  { 166, 132, 126 },
+  { 159, 125, 116 },
+  { 125, 85, 72 },
+  { 51, 30, 24 },
+  { 17, 16, 15 },
+  { 14, 16, 16 }
+},
+{
+  { 37, 38, 56 },
+  { 37, 38, 55 },
+  { 36, 37, 54 },
+  { 31, 35, 48 },
+  { 93, 73, 72 },
+  { 185, 143, 128 },
+  { 191, 153, 136 },
+  { 165, 108, 92 },
+  { 164, 119, 103 },
+  { 121, 88, 71 },
+  { 121, 82, 68 },
+  { 163, 93, 79 },
+  { 179, 105, 91 },
+  { 177, 112, 94 },
+  { 80, 51, 43 },
+  { 62, 39, 33 },
+  { 104, 68, 54 },
+  { 131, 91, 71 },
+  { 165, 132, 112 },
+  { 167, 131, 123 },
+  { 132, 94, 79 },
+  { 50, 34, 27 },
+  { 12, 15, 16 },
+  { 17, 17, 17 },
+  { 16, 16, 16 }
+},
+{
+  { 38, 39, 57 },
+  { 36, 36, 54 },
+  { 35, 37, 52 },
+  { 33, 40, 50 },
+  { 31, 38, 46 },
+  { 100, 83, 73 },
+  { 151, 87, 84 },
+  { 174, 127, 120 },
+  { 191, 147, 128 },
+  { 161, 103, 73 },
+  { 90, 55, 42 },
+  { 99, 60, 50 },
+  { 116, 72, 59 },
+  { 99, 60, 46 },
+  { 112, 66, 51 },
+  { 151, 101, 78 },
+  { 152, 110, 93 },
+  { 160, 119, 106 },
+  { 140, 96, 77 },
+  { 119, 95, 72 },
+  { 55, 37, 31 },
+  { 12, 16, 17 },
+  { 17, 18, 18 },
+  { 17, 17, 17 },
+  { 16, 16, 16 }
+},
+{
+  { 36, 39, 54 },
+  { 35, 37, 51 },
+  { 35, 37, 50 },
+  { 32, 37, 49 },
+  { 34, 44, 50 },
+  { 108, 72, 73 },
+  { 88, 36, 56 },
+  { 119, 100, 99 },
+  { 198, 158, 145 },
+  { 166, 106, 85 },
+  { 135, 69, 52 },
+  { 121, 62, 49 },
+  { 137, 71, 53 },
+  { 148, 80, 55 },
+  { 153, 88, 65 },
+  { 137, 89, 71 },
+  { 157, 123, 108 },
+  { 161, 128, 114 },
+  { 84, 57, 49 },
+  { 85, 71, 48 },
+  { 76, 51, 48 },
+  { 14, 16, 16 },
+  { 16, 18, 17 },
+  { 16, 16, 16 },
+  { 16, 16, 16 }
+},
+{
+  { 33, 40, 50 },
+  { 34, 38, 50 },
+  { 33, 38, 49 },
+  { 30, 36, 47 },
+  { 34, 43, 48 },
+  { 112, 75, 75 },
+  { 66, 38, 51 },
+  { 68, 63, 68 },
+  { 186, 145, 129 },
+  { 161, 101, 81 },
+  { 167, 110, 99 },
+  { 159, 98, 84 },
+  { 165, 106, 90 },
+  { 151, 85, 64 },
+  { 142, 85, 69 },
+  { 139, 92, 76 },
+  { 168, 128, 112 },
+  { 126, 87, 73 },
+  { 40, 28, 30 },
+  { 100, 78, 57 },
+  { 83, 63, 51 },
+  { 17, 14, 19 },
+  { 17, 17, 19 },
+  { 16, 16, 16 },
+  { 17, 17, 17 }
+},
+{
+  { 32, 39, 49 },
+  { 33, 36, 48 },
+  { 32, 34, 47 },
+  { 30, 33, 45 },
+  { 33, 41, 46 },
+  { 110, 74, 74 },
+  { 62, 36, 50 },
+  { 31, 36, 42 },
+  { 138, 95, 77 },
+  { 158, 95, 72 },
+  { 160, 101, 85 },
+  { 162, 101, 85 },
+  { 160, 103, 83 },
+  { 150, 89, 70 },
+  { 140, 88, 70 },
+  { 136, 89, 71 },
+  { 132, 92, 75 },
+  { 46, 31, 27 },
+  { 17, 20, 23 },
+  { 93, 70, 52 },
+  { 78, 65, 45 },
+  { 17, 17, 22 },
+  { 18, 17, 21 },
+  { 17, 17, 16 },
+  { 16, 16, 16 }
+},
+{
+  { 37, 39, 56 },
+  { 37, 38, 53 },
+  { 35, 36, 50 },
+  { 33, 35, 48 },
+  { 39, 45, 50 },
+  { 113, 73, 75 },
+  { 60, 36, 53 },
+  { 25, 32, 41 },
+  { 74, 52, 48 },
+  { 153, 91, 72 },
+  { 154, 108, 98 },
+  { 156, 103, 92 },
+  { 154, 97, 81 },
+  { 146, 84, 67 },
+  { 132, 80, 63 },
+  { 125, 75, 57 },
+  { 75, 45, 39 },
+  { 16, 21, 25 },
+  { 20, 21, 24 },
+  { 92, 68, 57 },
+  { 77, 69, 45 },
+  { 19, 25, 26 },
+  { 16, 17, 21 },
+  { 17, 16, 17 },
+  { 16, 16, 16 }
+},
+{
+  { 40, 40, 59 },
+  { 38, 40, 56 },
+  { 36, 39, 53 },
+  { 36, 39, 52 },
+  { 40, 41, 53 },
+  { 94, 51, 65 },
+  { 49, 39, 53 },
+  { 31, 36, 49 },
+  { 49, 43, 46 },
+  { 115, 91, 79 },
+  { 130, 118, 112 },
+  { 133, 115, 110 },
+  { 124, 92, 79 },
+  { 113, 77, 66 },
+  { 115, 79, 67 },
+  { 109, 75, 63 },
+  { 45, 34, 34 },
+  { 20, 26, 30 },
+  { 22, 21, 27 },
+  { 67, 56, 47 },
+  { 104, 84, 54 },
+  { 34, 37, 32 },
+  { 16, 18, 22 },
+  { 17, 18, 22 },
+  { 17, 17, 18 }
+},
+{
+  { 33, 38, 51 },
+  { 31, 36, 48 },
+  { 31, 35, 47 },
+  { 31, 34, 46 },
+  { 36, 37, 45 },
+  { 61, 41, 44 },
+  { 27, 33, 43 },
+  { 26, 30, 42 },
+  { 72, 65, 64 },
+  { 144, 132, 116 },
+  { 140, 132, 126 },
+  { 194, 178, 149 },
+  { 192, 182, 168 },
+  { 55, 48, 44 },
+  { 85, 74, 66 },
+  { 162, 146, 131 },
+  { 67, 60, 55 },
+  { 20, 23, 27 },
+  { 19, 23, 27 },
+  { 24, 34, 29 },
+  { 73, 59, 38 },
+  { 27, 24, 24 },
+  { 15, 18, 19 },
+  { 16, 18, 18 },
+  { 16, 16, 17 }
+},
+{
+  { 28, 36, 43 },
+  { 29, 33, 41 },
+  { 29, 32, 41 },
+  { 29, 32, 40 },
+  { 29, 31, 42 },
+  { 28, 31, 39 },
+  { 27, 31, 38 },
+  { 24, 30, 38 },
+  { 51, 54, 54 },
+  { 110, 107, 90 },
+  { 142, 127, 115 },
+  { 151, 134, 119 },
+  { 145, 142, 129 },
+  { 56, 64, 48 },
+  { 73, 83, 60 },
+  { 127, 139, 103 },
+  { 61, 78, 48 },
+  { 19, 21, 24 },
+  { 19, 20, 25 },
+  { 16, 19, 23 },
+  { 12, 16, 20 },
+  { 16, 17, 18 },
+  { 16, 16, 16 },
+  { 16, 16, 16 },
+  { 16, 16, 16 }
+},
+{
+  { 29, 35, 41 },
+  { 29, 32, 39 },
+  { 29, 32, 39 },
+  { 29, 32, 39 },
+  { 28, 31, 39 },
+  { 27, 30, 37 },
+  { 27, 30, 37 },
+  { 23, 27, 36 },
+  { 71, 69, 58 },
+  { 118, 149, 89 },
+  { 105, 136, 86 },
+  { 94, 129, 78 },
+  { 96, 140, 76 },
+  { 113, 146, 83 },
+  { 122, 134, 79 },
+  { 101, 104, 58 },
+  { 56, 66, 36 },
+  { 17, 22, 25 },
+  { 17, 21, 25 },
+  { 16, 21, 24 },
+  { 17, 17, 22 },
+  { 16, 17, 19 },
+  { 17, 17, 17 },
+  { 16, 16, 16 },
+  { 16, 16, 16 }
+},
+{
+  { 31, 34, 41 },
+  { 29, 32, 39 },
+  { 29, 32, 39 },
+  { 29, 32, 39 },
+  { 28, 31, 38 },
+  { 27, 30, 37 },
+  { 24, 30, 37 },
+  { 37, 35, 39 },
+  { 141, 98, 82 },
+  { 156, 118, 96 },
+  { 133, 138, 92 },
+  { 95, 128, 61 },
+  { 129, 114, 79 },
+  { 157, 111, 92 },
+  { 137, 97, 85 },
+  { 84, 53, 46 },
+  { 53, 40, 41 },
+  { 36, 37, 40 },
+  { 44, 39, 41 },
+  { 42, 38, 40 },
+  { 40, 36, 36 },
+  { 42, 36, 36 },
+  { 39, 35, 35 },
+  { 35, 34, 34 },
+  { 34, 34, 34 }
+},
+{
+  { 29, 32, 39 },
+  { 28, 31, 38 },
+  { 27, 30, 37 },
+  { 27, 30, 37 },
+  { 27, 30, 37 },
+  { 27, 30, 37 },
+  { 22, 28, 37 },
+  { 81, 56, 49 },
+  { 165, 119, 104 },
+  { 163, 126, 119 },
+  { 143, 93, 80 },
+  { 111, 57, 40 },
+  { 131, 73, 54 },
+  { 160, 115, 95 },
+  { 106, 95, 95 },
+  { 18, 2, 3 },
+  { 23, 6, 6 },
+  { 11, 4, 4 },
+  { 30, 7, 7 },
+  { 32, 7, 8 },
+  { 29, 7, 8 },
+  { 32, 8, 8 },
+  { 24, 6, 6 },
+  { 4, 2, 2 },
+  { 2, 1, 1 }
+}
+};
+
+void show_evo(int delaytime) {
+  for(int x=0; x<25; x++) {
+    for(int y=0; y<20; y++) {
+      for(int c=0; c<3; c++) {
+        frame[x][20-y-1][c] = evo_shandor[y][x][c];
+      }
+    }
+  }
+  printFrame();
+  delay(delaytime);
+}

+ 87 - 0
otalib.ino

@@ -0,0 +1,87 @@
+#include <WiFi.h>
+#include <ESPmDNS.h>
+#include <WiFiUdp.h>
+#include <ArduinoOTA.h>
+
+const char* ssid = "monkeybox";
+const char* password = "6165504423";
+
+TaskHandle_t OTA_Handle_Loop; // a task to assign to core 0
+
+void ota_setup() {
+  Serial.begin(115200);
+  Serial.println("Initializing WiFi");
+  WiFi.mode(WIFI_STA);
+  WiFi.begin(ssid, password);
+  if(WiFi.waitForConnectResult() != WL_CONNECTED) {
+    Serial.println("Connection Failed! No WiFi Available...");
+  } else {
+    // Port defaults to 3232
+    // ArduinoOTA.setPort(3232);
+
+    // Hostname defaults to esp3232-[MAC]
+    ArduinoOTA.setHostname("pingpongwall25x20");
+
+    // No authentication by default
+    // ArduinoOTA.setPassword("admin");
+
+    // Password can be set with it's md5 value as well
+    ArduinoOTA.setPasswordHash("51376e1ae98edbd44194c0d55f248634");
+
+    ArduinoOTA
+      .onStart([]() {
+        String type;
+        if (ArduinoOTA.getCommand() == U_FLASH)
+          type = "sketch";
+        else // U_SPIFFS
+          type = "filesystem";
+
+        // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
+        Serial.println("Start updating " + type);
+      })
+      .onEnd([]() {
+        Serial.println("\nEnd");
+      })
+      .onProgress([](unsigned int progress, unsigned int total) {
+        Serial.printf("Progress: %u%%\n", (progress / (total / 100)));
+      })
+      .onError([](ota_error_t error) {
+        Serial.printf("Error[%u]: ", error);
+        if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
+        else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
+        else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
+        else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
+        else if (error == OTA_END_ERROR) Serial.println("End Failed");
+      });
+
+    ArduinoOTA.begin();
+
+    Serial.println("WiFi/OTA Ready");
+    Serial.print("IP address: ");
+    Serial.println(WiFi.localIP());
+
+    // assign the loop to run on core 0, so we don't have to worry about the main loop.
+    xTaskCreatePinnedToCore(
+      ota_loop, /* Function to implement the task */
+      "OTAUpdateLoop", /* Name of the task */
+      10000,  /* Stack size in words */
+      NULL,  /* Task input parameter */
+      0,  /* Priority of the task; higher is lower priority */
+      &OTA_Handle_Loop,  /* Task handle. */
+      0); /* Core where the task should run */    
+  }
+  // end of ota_setup();
+}
+
+void safe_ota_handle() {
+  // idea is maybe set a variable to say that we're updating, and then not allow the main task to run while it's doing it?
+  ArduinoOTA.handle();
+}
+
+void ota_loop(void * parameter) {
+  for(;;) {
+    safe_ota_handle();
+    vTaskDelay(10); // feed the other tasks
+    delay(1000); // Just go ahead and check 1/second
+  }
+}