Arduino Projects

This page has information about my electronics projects using the Arduino microcontroller.

Lava Shoes

A pressure sensor in the heel of the shoes controls the number of LEDs that light up. I used these shoes for my "The floor is lava" Halloween costume. This project was inspired by these firewalker-led-sneakers.

video1              video2

The lights are LED strips controlled by an Arduino Flora using the NeoPixels library. The flora probably isn't the best microcontroller for this project, but I had them on hand, and they worked great.

The project is powered by a 3.7V 2500mA Lithium battery that lasted all day without fading color. I tested a 1200mAh current battery and it also worked fine. I bought a small microUSB charger board that worked well with the battery.

The project reads the sensorValue from a velostat with a wire on each side. As pressure is applied, the velostat changes conductivity. These values from the sensor dance around, so they get smoothed out with by only adding 10% of the new value to the old. (value = value * 0.9 + newValue * 0.1;)

The lava colors are stored in an array as just red. To make yellow sparks, green LEDs randomly light up and fade out.

materials:

  • a pair of shoes
  • duct tape
  • Adafruit FLORA - Wearable electronic platform: Arduino-compatible
  • ALITOVE 16.4FT 300 Pixels WS2812B Programmable Addressable LED Strip
  • velostat: ADAFRUIT INDUSTRIES 1361 Accessory Type:Conductive Sheet
  • thin non-insulated copper wire
  • Adafruit 328 Battery, Lithium Ion Polymer, 3.7V, 2500mAh
  • Adafruit Micro Lipo w/MicroUSB Jack - USB LiIon/LiPoly charger
  • Soldering required
  • code

    Github - lavashoes.ino

    #include <Adafruit_NeoPixel.h>
    
    const int analogInPin = A11;  // A11 is called #12 on the Flora
    const int NUM_LEDS = 30;
    const int PIN  = 9;
    const int WAIT = 10;
    // adjust SENSITIVITY first, then adjust LED_OFFSET until no LEDs light up with no pressure
    const int SENSITIVITY = 160;  // smaller means less lights,  bigger means more lights
    const int LED_OFFSET = 56;  // adjust this to remove LEDs that stay lit up after changing sensitivity
    
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
    
    int sensorValue = 0;
    float stripLength = 0;
    int color[NUM_LEDS][3];
    
    void setup() {
      // Serial.begin(9600);
      digitalWrite(analogInPin, HIGH);
      strip.begin();
      strip.show();
    
      // set up colors array as red
      for (uint16_t i = 0; i < NUM_LEDS; i++) {
        color[i][0] = 255;
        color[i][1] = 0;
        color[i][2] = 0;
      }
    }
    
    void loop() {
      // read the analog in value:
      sensorValue = analogRead(analogInPin);
      // Serial.print("sensor = " );
      // Serial.println(sensorValue);
    
      // smooth the sensorValue and scale to number of LEDs
      stripLength = stripLength * 0.9 +
        (SENSITIVITY * NUM_LEDS / sensorValue - LED_OFFSET) * 0.1;
      //  stripLength = NUM_LEDS;//light up full strip for testing
    
      // green spark
      for (uint16_t i = 0; i < NUM_LEDS; i++) {
        if (random(400) == 0 ) {
          color[i][1] = 125;
        } else if (color[i][1] > 1) {
          //green fade out
          color[i][1] -= 2;
        } else {
          color[i][1] = 0;
        }
      }
      //light up LEDs up to stripLength
      for (uint16_t i = 0; i < stripLength; i++) {
        strip.setPixelColor(i, color[i][0], color[i][1], color[i][2]);
      }
      //turn off the rest of the LEDs
      for (uint16_t i = stripLength; i < NUM_LEDS; i++) {
        strip.setPixelColor(i, 0, 0, 0);
      }
      strip.show();
      delay(WAIT);
    }
           

    Tron: Lightcycles

    This project reproduces the tron lightcycles game. Each player controls a line of color that moves through the grid. If a player hits a location that is already lit up they die. The players will move 4x faster if the joysticks are pressed in.

    After building the project I mounted it on plexiglass with screws and hotglue.

    video1

    The game state is stored in a 2D array of 16 x 16. The array is checked to see if a player will collide after each move.

    The 16 x 16 LED matrix is organized in a zig zag pattern. I wrote a function that converts to the zigzag pattern. The NeoPixel library is used to send the final signal to light up each LED.

    more pictures:
    materials:

  • Arduino Uno microcontroller
  • flexible 16 x 16 NeoPixel RGB LED Matrix
  • 2 Arduino Joy Sticks
  • small breadboard
  • jumper wires (male to female and male to male)
  • code

    Github - lightcycle.ino

    #include <Adafruit_NeoPixel.h>
    
    const int WAIT = 250;         // how many cycles to look for player joystick input
    const int LEDPIN = 6;         // pin for LEDS
    const int SWITCH_PIN_1 = 2;   // pin for P1 switch
    const int SWITCH_PIN_2 = 4;   // pin for P2 switch
    const int NUM_LEDS = 256;     // 16x16 = 256
    const int BRIGHTNESS = 30;     // 0-100 LED brightness
    
    //player 1 is blue
    //player 2 is orange
    
    int p1[2] = {8, -1};  //player location
    int p2[2] = {8, 16};   //player location
    int p1Input[2] = {0, 0};
    int p2Input[2] = {0, 0};
    int p1Velocity[2] = {0, -1};
    int p2Velocity[2] = {0, 1};
    bool isP1Move = true;
    bool isP2Move = true;
    
    // locations where the players have been
    bool on[16][16] = {
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    };
    
    // Parameter 1 = number of pixels in strip
    // Parameter 2 = pin number (most are valid)
    // Parameter 3 = pixel type flags, add together as needed:
    //   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
    //   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
    //   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
    //   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LEDPIN, NEO_GRB + NEO_KHZ800);
    
    void setup() {
      Serial.begin(9600);
      //activate pull-up resistor on the push-button pin
      pinMode(SWITCH_PIN_1, INPUT_PULLUP);
      pinMode(SWITCH_PIN_2, INPUT_PULLUP);
      strip.setBrightness(BRIGHTNESS);
      strip.begin();
      for (int i = 0; i < NUM_LEDS; i += 4) {
        for (int j = 0; j < 4; j++) {
          strip.setPixelColor(i + j, 255,50, 0);
          strip.setPixelColor(NUM_LEDS - i - j, 0, 150, 200);
        }
        strip.show();
      }
      // clear all pixels
      for (int i = 0; i < NUM_LEDS; i++) {
        strip.setPixelColor(i, 0, 0, 0);
      }
      strip.show();
    }
    
    void loop() {
      isP1Move = true;
      isP2Move = true;
      for (int i = 0; i < 4; i++) {
        //player moves extra if button is down
        if (!digitalRead(SWITCH_PIN_1)) {
          isP1Move = true;
          Serial.print(1);
          Serial.println();
        }
        if (!digitalRead(SWITCH_PIN_2)) {
          isP2Move = true;
          Serial.print(2);
          Serial.println();
        }
        if (isP1Move) P1Move();
        if (isP2Move) P2Move();
        playerJoystickInput(); //extra check for player input to smooth control sensing
        playerCollisions();
        updateGameState();
        drawPlayers();
    
        //check for player input
        for (int i = 0; i < WAIT; i++) {
          playerJoystickInput(); //consider placing playerJoystickInput() in more places in the loop to get more twitch response
        }
      }
    }
    
    void updateGameState() {
      on[p1[0]][p1[1]] = true;
      on[p2[0]][p2[1]] = true;
    }
    
    void playerJoystickInput() {
      //read input from game controllers
      int p1x = map(analogRead(0), 0, 1023, -3, 4);
      int p1y = map(analogRead(1), 0, 1023, -3, 4);
      int p2x = map(analogRead(2), 0, 1023, -3, 4);
      int p2y = map(analogRead(3), 0, 1023, -3, 4);
    
      // log the input if it is not zero
      if (!(p1x == 0 && p1y == 0)) {
        p1Input[0] = p1x;
        p1Input[1] = p1y;
      }
      if (!(p2x == 0 && p2y == 0)) {
        p2Input[0] = p2x;
        p2Input[1] = p2y;
      }
    }
    
    void P1Move() {
      // change player velocity from inputs
      // don't let player go backwards and die
      if (p1Input[0] > 0 && p1Velocity[0] != -1) {
        p1Velocity[0] = 1;
        p1Velocity[1] = 0;
      } else if (p1Input[0] < 0 && p1Velocity[0] != 1) {
        p1Velocity[0] = -1;
        p1Velocity[1] = 0;
      } else if (p1Input[1] > 0 && p1Velocity[1] != -1) {
        p1Velocity[1] = 1;
        p1Velocity[0] = 0;
      } else if (p1Input[1] < 0 && p1Velocity[1] != 1) {
        p1Velocity[1] = -1;
        p1Velocity[0] = 0;
      }
    
      //move player 1
      p1[0] += p1Velocity[0];
      p1[1] += p1Velocity[1];
    
      //edge around the world
      if (p1[0] < 0) {
        p1[0] = 15;
      } else if (p1[0] > 15) {
        p1[0] = 0;
      }
      if (p1[1] < 0) {
        p1[1] = 15;
      } else if (p1[1] > 15) {
        p1[1] = 0;
      }
    }
    
    void P2Move() {
      // change player velocity from inputs
      // don't let player go backwards and die
      if (p2Input[0] > 0 && p2Velocity[0] != -1) {
        p2Velocity[0] = 1;
        p2Velocity[1] = 0;
      } else if (p2Input[0] < 0  && p2Velocity[0] != 1) {
        p2Velocity[0] = -1;
        p2Velocity[1] = 0;
      } else if (p2Input[1] > 0  && p2Velocity[1] != -1) {
        p2Velocity[1] = 1;
        p2Velocity[0] = 0;
      } else if (p2Input[1] < 0  && p2Velocity[1] != 1) {
        p2Velocity[1] = -1;
        p2Velocity[0] = 0;
      }
    
      //move player 2
      p2[0] += p2Velocity[0];
      p2[1] += p2Velocity[1];
    
      //edge around the world
      if (p2[0] < 0) {
        p2[0] = 15;
      } else if (p2[0] > 15) {
        p2[0] = 0;
      }
      if (p2[1] < 0) {
        p2[1] = 15;
      } else if (p2[1] > 15) {
        p2[1] = 0;
      }
    }
    
    void playerCollisions() {
      //check for player + player tip collision
      if (p1[0] == p2[0] && p1[1] == p2[1]) {
        tie();
      }
      //check for player1 + wall collision
      if (isP1Move && on[p1[0]][p1[1]]) {
        p2Wins();
      }
      //check for player2 + wall collision
      if (isP2Move && on[p2[0]][p2[1]]) {
        p1Wins();
      }
      //reset player moved to false
      isP1Move = false;
      isP2Move = false;
    }
    
    void p1Wins() {
      if (isP1Move && on[p1[0]][p1[1]]) {
        tie();
      }
      //  Serial.println("p1/blue wins!");
      //  GameStateDump();
      strip.setBrightness(20);
      FillEmpty(0, 15, 20);
      ZigZagPixel(p2[0], p2[1], 255, 255, 255);
      strip.show();
    
      endGame();
    }
    
    void p2Wins() {
      if (isP2Move && on[p2[0]][p2[1]]) {
        tie();
      }
      //  Serial.println("p2/orange, wins!");
      //  GameStateDump();
      strip.setBrightness(20);
      FillEmpty(25, 15, 0);
      ZigZagPixel(p1[0], p1[1], 255, 255, 255);
      strip.show();
      endGame();
    }
    
    void tie() {
      //  Serial.println("tie game");
      //  GameStateDump();
      strip.setBrightness(20);
      FillEmpty(15, 15, 15);
      ZigZagPixel(p1[0], p1[1], 255, 255,255);
      strip.show();
      endGame();
    }
    
    void drawPlayers() {
      //draw players
      ZigZagPixel(p1[0], p1[1], 0, 150, 200);
      ZigZagPixel(p2[0], p2[1], 255,50, 0);
      strip.show();
    }
    
    void ZigZagPixel(int x, int y, int r, int g, int b) {
      if (y % 2) {
        strip.setPixelColor(y * 16 + x, r, g,  b);
      } else {
        strip.setPixelColor(y * 16 + 15 - x, r, g,  b);
      }
    }
    
    void getZigZagPixel(int x, int y) {
      if (y % 2) {
        Serial.println(strip.getPixelColor(y * 16 + x) );
      } else {
        Serial.println(strip.getPixelColor(y * 16 + 15 - x));
      }
    }
    
    void GameStateDump() {
      for (int i = 0; i < 16; i++) {
        for (int j = 0; j < 16; j++)
        {
          Serial.print(on[j][i]);
          Serial.print(",");
        }
        Serial.println();
      }
      Serial.print("p1 = ");
      Serial.print(p1[0]);
      Serial.print(", ");
      Serial.print(p1[1]);
      Serial.println();
      Serial.print("p2 = ");
      Serial.print(p2[0]);
      Serial.print(", ");
      Serial.print(p2[1]);
      Serial.println();
      Serial.print("p1 Velocity = ");
      Serial.print(p1Velocity[0]);
      Serial.print(", ");
      Serial.print(p1Velocity[1]);
      Serial.println();
      Serial.print("p2 Velocity = ");
      Serial.print(p2Velocity[0]);
      Serial.print(",");
      Serial.print(p2Velocity[1]);
      Serial.println();
      Serial.println();
    }
    
    void FillEmpty(int r, int g, int b) {
      for (int i = 0; i < 16; i++) {
        for (int j = 0; j < 16; j++)
        {
          if (!on[i][j]) {
            ZigZagPixel(i, j, r, g, b);
          }
        }
      }
    }
    
    void(* resetFunc) (void) = 0; //declare reset function @ address 0
    void endGame() {
      delay(5000);
      resetFunc();  //call reset
    }