Homework - Paul DiPastina

  • Hey, my name is Paul DiPastina, I'm a programmer / game developer / part of a film group that makes youtube videos and I'll be updating my assignment throughout the day as I narrow down the parts I need.

    For my project, I'll be working on some of the components of a suit of power armor for a local LARP. The armor will be built mostly out of EVA foam / cross linked polyethylene , which I've been doing a lot of work in lately. It needs to be comfortable to wear and run around in, contact safe, easy to get on and off, and the electronics need to be shielded from damage so they don't break or injure me when I get hit.

    For this course, I will probably be focusing on lights / sounds, and the helmet servo, as well as parts of the foam exterior, which I will be patterning and crafting this weekend while I watch. 

    For the electronics, I've drawn a lot of inspiration from this Iron Man build:
    http://www.instructables.com/id/Animatronic-Iron-Man-Mk-III-suit/?ALLSTEPS

    I want to emulate their boot design as well as their helmet design. Eventually I would like to make the helmet system wireless, but I feel like the first prototype will not be. I will still be purchasing the parts so I can play with RFID applications.

    The chest lights for my build need to be directly controlled by the board, though. The armor in-game has an overdrive effect, and I want to have the lights pulse and change color in overdrive to make it visually distinctive and intimidating. The iron man build uses a pre-built system for the lights on the center torso, but I'd like to roll my won. I would like to replace the lights they use with a neopixel ring, so that I can control the colors / brightness from the chip. 

    I'll be eliminating one of the Arduino boards because I don't have rocket pods or shoulder rockets or anything like that to control on my suit. I may consider adding a small text display with four buttons so I can track how much fuel I have and turn the suit on and off or engage overdrive mode.

    Visually though, I don't want this to look like Iron Man's suit. I have other plans.

    The power armor I wear in the game is supposed to be built from a suit of post-apoc crafted armor, so I wanted something a little more retro looking than iron man. At least to star out with as a base, I'm going with the Blood Dragon Armor design from Mass Effect, and basing it off of this individual's build (Note: Not my build), which as far as I've seen is the best execution of this particular cosplay:

    http://geektyrant.com/news/mass-effect-blood-dragon-armor-cosplay-by-leon-chiro-cosplay

    I've got some sketches drawn out for the different parts (crude, hand drawn template guestimates) and I'll try to upload those over the next 24 hours.

    I've also done some crude mockups of the design modifications I want to make, to fit the armor design into the setting. This image may be jarring, so let me explain: My character belongs to a post-apoc gang I started called the Fundertakers. If that name doesn't speak for itself, this image should get the idea across.

    Finally, the extensive parts list, which I will narrow down further until I know exactly what I need. I also need to add some parts from the materials list on this page, like a display and possibly an alternate sound system. I don't want to bite off TOO much more than I can chew, but I have been known to do that all the time and it usually works out in the end. I may not get to everything during this class but eventually I will, and as long as I've got a neat suit of power armor to wear at the event next month it's mission: success!

    Parts List - Currently this comes out to $576, so consider this an "ideal" parts list, which I will pare down to something much more managable. I'll be working through the night to modify the parts list down, remove un-nessecary duplicates, etc.


    Foamsmithing:

    EVA Foam Tiles: 20 (most Mass Effect armor requires 16)
    L200: 1 sheet each of 1/2", 1/4", 3/8" foam
    Heat Gun
    Dremel
    Sandpaper
    Persona Blades
    Hot Glue
    Super Glue
    Barge
    Assorted Straps (Elastic and non-elastic)
    Assorted Buckles

    Many of the following parts are copied directly from the iron man build's materials list, hence text like "I usually buy them in bulk" - that's the author of the original build talking, not me. My notes are in bold.
    Unknown / miscellaneous:
    Arduino ProMini 328 5V Sparkfun part # DEV-11113 
    Arduino ProMini 328 3.3V Sparkfun part # DEV-11114 
    Arduino Pro 328 5V Sparkfun part # DEV-10915
    8x RJ45 8-pin connector Ethernet cable connector Sparkfun part # PRT-00643 
    8x Breakout Board for RJ45 Sparkfun part # BOB-00716
    4x JST RCY Connector for the helmet battery packs Sparkfun part # PRT-10501  These parts are on backorder, may need to find another solution.
    SD/MicroSD Memory Card (4 GB SDHC) Adafruit Product ID: 102
    4x Straight Break Away Headers - for making connectors/servo leads Sparkfun part # PRT-00116
    4x Female Headers for making connectors/servo leads Sparkfun part # PRT-00115
    2x Adafruit Perma-Proto Quarter-sized Breadboard for mounting connectors, etc. Adafruit Product ID: 589
    6x TIP 120 transistors for turning on LEDs and triggering sound effects. Digkey sells them.
    10 x power slide switches. These connect all of the individual power sources in the suit. Digikey sells them.
    High Strength Velcro
    Wire and Ethernet Cables.

    Head

    ID-12 RFID tag reader Sparkfun part # SEN-11827 (LA version is replacement)
    XBee Series 1 module Sparkfun part # WRL-11215
    Adafruit XBee Adapter board Adafruit Product ID: 126
    Adafruit Wave Shield Adafruit Product ID: 94 
    Pololu 5V DC/DC converter D24V5F5 -steps down the helmet 7.4V battery voltage for the ProMini
    Hitec HS-5087MH micro servo for the helmet chin section- Servocity.com
    Hitec HS-7245MH mini servo for the helmet faceplate- Servocity.com
    1-4 16mm RFID button tag (125 kHz) Sparkfun part # SEN-09417 
    6x AAA NiMH cells 1.2V for the helmet battery pack - local grocery store
    RFID reader breakout Sparkfun part # Product SEN-08423 Parts on backorder, may need to hold off on RFID project until they come in.
    1 x Adafruit tactile on/off power switch for helmet- Adafruit Product ID: 1092
    4-40 swivel links (pkg of 4)- you need two links for the helmet, two for the forearm missile and four for the hip pods- Servocity.com
    4-40 12" length threaded rod- used to make the helmet and hip pod servo linkages- Servocity.com
    SMD prototyping board- used for soldering the surface mount LEDs and resistors for the eyes. Sparkfun part # PRT-08708

    Boots

    2x Luxeon Rebel high power white LED for the lights in the boots Sparkfun part # BOB-09656 These parts are no longer available, I will need to find replacement LEDs.
    Sharp GP2D120XJ00F Infrared Proximity Sensor Sensor for the bottom of the boot Sparkfun part # SEN-08959  This part is no longer available, so I am replacing it with what appears to be its successor, part # SEN-12728 
    7027 "BuckToot" LED Driver Module to power the Luxeon LEDs Sparkfun part # COM-09642  Also no longer available, I will need to find another solution for my LED driver needs as well. May try to replace both driver and LED with these from neopixel: http://www.adafruit.com/products/2376

    Lights

    34 x PLCC-2 package SMT white LEDs- 10 for the eyes and 24 for the chest light. You could easily substitute standard LEDs. I buy these in bulk on eBay but retailers like Digikey also sell them.
    34 x 1206 package 100 Ohm SMT resistors- 10 for the eyes and 24 for the chest light.You could easily substitute standard resistors. I buy these surplus from a local shop but retailers like Digikey also sell them.
    Neopixel Ring: http://www.adafruit.com/products/1586

    Controller
    Screen

    Electrical Build Equipment:






    This image has been resized to fit in the page. Click to enlarge.


Comments

  • Alright, update so far:

    I just ordered all the parts I need. Some parts I got with rushed shipping and supposedly they'll get here today or tomorrow. Others like the infrared sensors only had standard shipping options and will arrive more slowly, but at least I'll have enough parts to post up a demo video.

    My goal for the week is to create functions to refuel the armor, power the armor / deactivate  it, set it into overdrive / turn overdrive off. I should be able to track the mode and the fuel usage on the LCD display. Each mode should make different LEDs blink, until I get the NeoPixels in the mail at which point I want to make it change the color of the LEDs, and possibly the pulse pattern. I may also add a button to activate a servo. I would like to also get it to play a few sounds.

    I did not order the wireless parts or servo parts for the helmet, because I want to focus on lights and sounds for the body and the boots first. One of the kits I ordered comes with a small servo that I will use to simulate the helmet's functionality without buying the expensive servo and wireless equipment.

    I have a lot going on this week, so I don't know how far I'm going to get. I've got a major LARP event this weekend for which we have been developing several monster costumes. Last week both of the sewing machines we were using broke and we had to go buy our own somewhat more modern machine, but the result was that we fell behind with only one machine to sew. I don't think I'll get much done with the foam armor fabrication this week, though I do plan on prototyping the torso and getting as many of the electronics working as possible.

    Stay tuned for a video update later this week!
  • edited April 2015
    One more update: Got all my parts! Wave Shield 1.1, NeoPixel ring and strip of lights, some buttons, some leds, arduino uno, bread board, holder, etc. 

    Got a program working too! At first I wrote one to cycle modes and change the LED's color based on the mode it was in. The different modes auto cycled until I hooked up the buttons, and now it THEORETICALLY does everything I want it to, except that I'm having trouble getting the buttons to work the way I want them to. For learning purposes, here's why:

    I've kept the delays in, which currently are 1 second delays. That means after it checks the mode, it waits a second before it checks again. This was to prevent it constantly jumping modes if the button was held down more than one processor cycle, but the result is that you have to hold the button down for a little while before it registers that it should switch modes, and once it does realize that it sometimes takes a while before it does the switch! So I need to figure out how to handle button presses, and print some debug statements so I can see what exactly is happening as it happens. But it's going really well!

    It turns out that the Wave Shield has a lot of parts I'm going to have to solder to it myself, and I'm a bit nervous about doing that for the first time so I'm going to save that for later and focus on getting this working, maybe setting up the LCD screen as well and THEN I'll tackle soldering this weekend.

    Edit: Tackled the button press problem by eliminating delays entirely from the script and instead tracking the time of the last button press. I only check for new button presses if 1 second has passed since the last press occured. After that second has passed, the program checks for a button press every cycle. In this way, I can make sure that all button presses happen instantaneously as soon as the button is pressed, but that I won't get the computer registering the same button as being pressed over and over and over if I hold it down for less than a second. Now the logic of the program is working exactly as intended, and I can focus on getting the lighting effects where I want them (right now it's just a single RGB LED, and I want to get the strip and the ring animating differently for each mode - right now they are not wired in).
    Post edited by Paul Dipastina on
  • edited April 2015
    I know that we're posting these to a facebook group, but I'm going to be gone all weekend so I thought I'd share here since I'm not currently invited and I may not have time to participate before I leave.

    Pretty simple code so far, going to get into more complex stuff like NeoPixels and sound this weekend and post an update when I have internet access again. Won't be participating live this time, but I'll be watching it as soon as I get home on Sunday and then I'll be on Facebook and the forums while I finish up. The video is processing right now but the link should be good within the hour or so.



    const int RED_PIN = 9;
    const int GREEN_PIN = 10;
    const int BLUE_PIN = 11;
    const int button1Pin = 2;  // pushbutton 1 pin
    const int button2Pin = 3;  // pushbutton 2 pin
    String state = "UNPOWERED";
    signed long fuelRemaining = 0.0;
    unsigned long lastFuelUpdate;
    unsigned long lastButtonPress;
    
    // So the current problem with the system is the button presses. 
    // We want there to be a 1 second delay BETWEEN button presses, 
    // but for the press itself to be instantaneous.
    // Therefore we need to track the time of the LAST button press
    // and use that value to determine whether we are ready to check for a new one.
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
      pinMode(13, OUTPUT);
      pinMode(12, OUTPUT);
      pinMode(RED_PIN, OUTPUT);
      pinMode(GREEN_PIN, OUTPUT);
      pinMode(BLUE_PIN, OUTPUT);
      pinMode(button1Pin, INPUT);
      pinMode(button2Pin, INPUT);
    
    }
    
    void loop() {
      int button1State, button2State;
      button1State = digitalRead(button1Pin);
      button2State = digitalRead(button2Pin);
      Serial.print("Fuel: ");
      Serial.println(fuelRemaining);
      
      if(state == "UNPOWERED")
      {
        unpowered(button1State, button2State);
      }
      else if (state == "POWERED")
      {
        powered(button1State, button2State);
      }
      else if(state == "OVERDRIVE")
      {
        overdrive(button1State, button2State);
      }
      else if(state == "REFUELING")
      {
        refueling(button1State, button2State);
      }
      
      
    }
    
    void powered(int button1State, int button2State)
    {
      // Check if fuel has expired
      if(fuelRemaining <= 0)
      {
        state = "UNPOWERED";
        return;
      }
      
      // Calculate fuel expenditure
      unsigned long newFuelUpdate = millis();
      unsigned long timeElapsed = newFuelUpdate - lastFuelUpdate;
      fuelRemaining -= timeElapsed;
      
      // If we've used up more fuel than we have left, set remaining fuel to zero
      fuelRemaining = max(fuelRemaining, 0);
      
      // Reset our latest fuel update to the current time
      lastFuelUpdate = newFuelUpdate;
      
      // Colorize the lights on the board to represent the mode we're in
      digitalWrite(RED_PIN, HIGH);
      digitalWrite(GREEN_PIN, LOW);
      digitalWrite(BLUE_PIN, LOW);
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > 1000)
      {
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "OVERDRIVE";
          lastButtonPress = millis();
        }
      }
    }
    
    void overdrive(int button1State, int button2State)
    {
      // Check if fuel has expired
      if(fuelRemaining <= 0)
      {
        state = "UNPOWERED";
        return;
      }
      
      // Calculate fuel expenditure
      unsigned long newFuelUpdate = millis();
      unsigned long timeElapsed = newFuelUpdate - lastFuelUpdate;
      fuelRemaining -= (timeElapsed * 2);
      
      // If we've used up more fuel than we have left, set remaining fuel to zero
      fuelRemaining = max(fuelRemaining, 0);
      
      // Reset our latest fuel update to the current time
      lastFuelUpdate = newFuelUpdate;
      
      // Colorize the lights on the board to represent the mode we're in
      digitalWrite(RED_PIN, HIGH);
      digitalWrite(GREEN_PIN, HIGH);
      digitalWrite(BLUE_PIN, HIGH);
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > 1000){
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "POWERED";
          lastButtonPress = millis();
        }
      }
    }
    
    void unpowered(int button1State, int button2State)
    {
      digitalWrite(RED_PIN, LOW);
      digitalWrite(GREEN_PIN, LOW);
      digitalWrite(BLUE_PIN, HIGH);
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > 1000){
        if(button1State == LOW)
        {
          state = "REFUELING";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "POWERED";
          lastButtonPress = millis();
        }
      }
    }
    
    void refueling(int button1State, int button2State)
    {
      digitalWrite(RED_PIN, LOW);
      digitalWrite(GREEN_PIN, HIGH);
      digitalWrite(BLUE_PIN, LOW);
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > 1000){
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          fuelRemaining += 60000;
          lastButtonPress = millis();
        }
      }
     
    }
  • Update - Note, there's one more update coming tomorrow night, I'm not sure when the class is officially over but I feel a Saturday update is also due. The code and the video will be in separate posts.
  • #include <Adafruit_NeoPixel.h>
    #include <avr/power.h>
    #define PIN 6
    const int RED_PIN = 9;
    const int GREEN_PIN = 10;
    const int BLUE_PIN = 11;
    const int button1Pin = 2;  // pushbutton 1 pin
    const int button2Pin = 3;  // pushbutton 2 pin
    String state = "UNPOWERED";
    signed long fuelRemaining = 0.0;
    unsigned long lastFuelUpdate;
    unsigned long lastButtonPress;
    const int buttonWait = 500;
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
    
    // So the current problem with the system is the button presses. 
    // We want there to be a 1 second delay BETWEEN button presses, 
    // but for the press itself to be instantaneous.
    // Therefore we need to track the time of the LAST button press
    // and use that value to determine whether we are ready to check for a new one.
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
      pinMode(13, OUTPUT);
      pinMode(12, OUTPUT);
      pinMode(RED_PIN, OUTPUT);
      pinMode(GREEN_PIN, OUTPUT);
      pinMode(BLUE_PIN, OUTPUT);
      pinMode(button1Pin, INPUT);
      pinMode(button2Pin, INPUT);
      strip.begin();
      strip.show(); // Initialize all pixels to 'off'
    
    }
    
    void loop() {
      int button1State, button2State;
      button1State = digitalRead(button1Pin);
      button2State = digitalRead(button2Pin);
      Serial.print("Fuel: ");
      Serial.println(fuelRemaining);
      
      if(state == "UNPOWERED")
      {
        unpowered(button1State, button2State);
      }
      else if (state == "POWERED")
      {
        powered(button1State, button2State);
      }
      else if(state == "OVERDRIVE")
      {
        overdrive(button1State, button2State);
      }
      else if(state == "REFUELING")
      {
        refueling(button1State, button2State);
      }
      
      
    }
    
    void powered(int button1State, int button2State)
    {
      // Check if fuel has expired
      if(fuelRemaining <= 0)
      {
        state = "UNPOWERED";
        return;
      }
      
      // Calculate fuel expenditure
      unsigned long newFuelUpdate = millis();
      unsigned long timeElapsed = newFuelUpdate - lastFuelUpdate;
      fuelRemaining -= timeElapsed;
      
      // If we've used up more fuel than we have left, set remaining fuel to zero
      fuelRemaining = max(fuelRemaining, 0);
      
      // Reset our latest fuel update to the current time
      lastFuelUpdate = newFuelUpdate;
      
      // Colorize the lights on the board to represent the mode we're in
      digitalWrite(RED_PIN, HIGH);
      digitalWrite(GREEN_PIN, LOW);
      digitalWrite(BLUE_PIN, LOW);
      
      poweredGlow(timeElapsed);
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait)
      {
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "OVERDRIVE";
          lastButtonPress = millis();
        }
      }
    }
    
    void overdrive(int button1State, int button2State)
    {
      // Check if fuel has expired
      if(fuelRemaining <= 0)
      {
        state = "UNPOWERED";
        return;
      }
      
      // Calculate fuel expenditure
      unsigned long newFuelUpdate = millis();
      unsigned long timeElapsed = newFuelUpdate - lastFuelUpdate;
      fuelRemaining -= (timeElapsed * 2);
      
      // If we've used up more fuel than we have left, set remaining fuel to zero
      fuelRemaining = max(fuelRemaining, 0);
      
      // Reset our latest fuel update to the current time
      lastFuelUpdate = newFuelUpdate;
      
      // Colorize the lights on the board to represent the mode we're in
      digitalWrite(RED_PIN, HIGH);
      digitalWrite(GREEN_PIN, HIGH);
      digitalWrite(BLUE_PIN, HIGH);
      poweredGlow(timeElapsed);
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait){
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "POWERED";
          lastButtonPress = millis();
        }
      }
    }
    
    void unpowered(int button1State, int button2State)
    {
      digitalWrite(RED_PIN, LOW);
      digitalWrite(GREEN_PIN, LOW);
      digitalWrite(BLUE_PIN, HIGH);
      noGlow();
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait){
        if(button1State == LOW)
        {
          state = "REFUELING";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "POWERED";
          lastButtonPress = millis();
        }
      }
    }
    
    void refueling(int button1State, int button2State)
    {
      digitalWrite(RED_PIN, LOW);
      digitalWrite(GREEN_PIN, HIGH);
      digitalWrite(BLUE_PIN, LOW);
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait){
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          fuelRemaining += 60000;
          lastButtonPress = millis();
        }
      }
     
    }
    
    void noGlow()
    {
      uint32_t c = strip.Color(0, 0, 0);
      for(uint16_t i=0; i<strip.numPixels(); i++) {
          strip.setPixelColor(i, c);
          
      }
      strip.show();
    }
    
    
    unsigned long totalPulseTimeElapsed = 0.0;
    int pulseIndex = 0;
    int pulseSize = 11; // Must be odd please
    int currentPulseSize = 1;
    int brightnessStep = 10;
    void poweredGlow(unsigned long elapsed)
    {
      // Send a pulse of light from the tips of the strand out to the middle, shifting the color very slightly in the pulse's wake
      // Start all the pixels at the same color
      uint32_t c = strip.Color(0, 225, 0);
      for(uint16_t i=0; i<strip.numPixels(); i++) {
          strip.setPixelColor(i, c);
          
      }
      
      
      totalPulseTimeElapsed += elapsed;
      if(totalPulseTimeElapsed >= 20)
      {
        updatePulse();
      }
      strip.show();
    }
    
    void updatePulse()
    {
      int index = pulseIndex;
      int midpoint = (strip.numPixels() /2) -1;
      int brightness = brightnessStep;
      // While index is >= 0
      while (index >=0 && brightness > 0)
      {
        // if index is greater than midpoint, then the pulses have met in the middle. Do not render a pulse at this index. 
        
        // Otherwise, render a pulse at the current index at the current brightness
        // Then, subtract the current index from the end of the string and render a pulse at that index at the current brightness.
        if(index < midpoint)
        {
          uint32_t color = strip.Color(brightness, 225, brightness);
          strip.setPixelColor(index, color);
          strip.setPixelColor(strip.numPixels() - (index+1), color);
        
        }
        // If pulseIndex - index < half of the pulse size, rounding up, subtract 1 from index and add a step to the brightness
        if(pulseIndex - index < (pulseSize/2) + 1)
        {
          index -= 1;
          brightness += brightnessStep;
        }
        // Otherwise, subtract 1 from index and subtract a brightness step.
        else
        {
          index -= 1;
          brightness -= brightnessStep;
        }
      }
      pulseIndex += 1;
      if(pulseIndex >= midpoint + pulseSize)
      {
        pulseIndex = 0;
      }
      
    }
    
    // Input a value 0 to 255 to get a color value.
    // The colours are a transition r - g - b - back to r.
    uint32_t Wheel(byte WheelPos) {
      WheelPos = 255 - WheelPos;
      if(WheelPos < 85) {
       return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
      } else if(WheelPos < 170) {
        WheelPos -= 85;
       return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
      } else {
       WheelPos -= 170;
       return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
      }
    }
    
  • Got the overdrive mode working for the LED strip. It's not perfect yet! I'm pretty sure what is happening is that the "glow" that I'm creating is getting initialized and then the "time remaining" is not getting incremented, so it just continues to linger and twinkle forever. But this also could be something to do with me miscalculating the indexes that I'm allowing a glow to appear on. I have a few ideas. I won't post up code until I have the effect working as intended.


  • Interestingly I've discovered that the print statements are adding a delay into the system that is making the pulse work the way I want it to and my logic is wrong on the elapsed time bit. Working on debugging that. If I actually make elapsed time work the way I meant it to (the timer resets after every update to the pulse) then it just constantly blinks, no matter how long I set the delay. I will figure out what is going wrong with it but for now I need rest.
  • Found the problem: I was setting the strip to be green every time I ran the update function, but I only added the glow if a certain amount of time had elapsed. The result was that each time I moved the glow, I showed it only for an instant, and then overwrote it with green. Now it's all pulsing properly without using comments to cause a delay. I've got the overdrive working too, but it still isn't really doing what I'd like it to, and it still has that damn gap in the lights where nothing is showing up.
  • Fixed the random shutdown problem, I was using too much memory by constantly creating new glow objects instead of overwriting the data for the glow objects that were already there. Here's the new code (no new video is needed, it still does the same thing as the one above, just better).

    #include <Adafruit_NeoPixel.h>
    #include <avr/power.h>
    #define PIN 6
    #define NUMBER_GLOWS 3
    const int RED_PIN = 9;
    const int GREEN_PIN = 10;
    const int BLUE_PIN = 11;
    const int button1Pin = 2;  // pushbutton 1 pin
    const int button2Pin = 3;  // pushbutton 2 pin
    String state = "UNPOWERED";
    signed long fuelRemaining = 0.0;
    unsigned long lastFuelUpdate;
    unsigned long lastButtonPress;
    const int buttonWait = 500;
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
    
    class OverdriveGlow
    {
      public:
        signed long timeRemaining = 0;
        int centerIndex;
        int glowsize;
        boolean b_available = true;
    };
    OverdriveGlow overdriveGlows[NUMBER_GLOWS];
    
    // So the current problem with the system is the button presses. 
    // We want there to be a 1 second delay BETWEEN button presses, 
    // but for the press itself to be instantaneous.
    // Therefore we need to track the time of the LAST button press
    // and use that value to determine whether we are ready to check for a new one.
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
      pinMode(13, OUTPUT);
      pinMode(12, OUTPUT);
      pinMode(RED_PIN, OUTPUT);
      pinMode(GREEN_PIN, OUTPUT);
      pinMode(BLUE_PIN, OUTPUT);
      pinMode(button1Pin, INPUT);
      pinMode(button2Pin, INPUT);
      strip.begin();
      strip.show(); // Initialize all pixels to 'off'
      overdriveGlows[0] = *new OverdriveGlow();
      overdriveGlows[0].timeRemaining = 0;
      overdriveGlows[1] = *new OverdriveGlow();
      overdriveGlows[1].timeRemaining = 0;
      overdriveGlows[2] = *new OverdriveGlow();
      overdriveGlows[2].timeRemaining = 0;
    
    }
    
    void loop() {
      int button1State, button2State;
      button1State = digitalRead(button1Pin);
      button2State = digitalRead(button2Pin);
      Serial.print("Fuel: ");
      Serial.println(fuelRemaining);
      
      if(state == "UNPOWERED")
      {
        unpowered(button1State, button2State);
      }
      else if (state == "POWERED")
      {
        powered(button1State, button2State);
      }
      else if(state == "OVERDRIVE")
      {
        overdrive(button1State, button2State);
      }
      else if(state == "REFUELING")
      {
        refueling(button1State, button2State);
      }
      
      
    }
    
    void powered(int button1State, int button2State)
    {
      // Check if fuel has expired
      if(fuelRemaining <= 0)
      {
        state = "UNPOWERED";
        return;
      }
      
      // Calculate fuel expenditure
      unsigned long newFuelUpdate = millis();
      unsigned long timeElapsed = newFuelUpdate - lastFuelUpdate;
      fuelRemaining -= timeElapsed;
      
      // If we've used up more fuel than we have left, set remaining fuel to zero
      fuelRemaining = max(fuelRemaining, 0);
      
      // Reset our latest fuel update to the current time
      lastFuelUpdate = newFuelUpdate;
      
      // Colorize the lights on the board to represent the mode we're in
      digitalWrite(RED_PIN, HIGH);
      digitalWrite(GREEN_PIN, LOW);
      digitalWrite(BLUE_PIN, LOW);
      
      poweredGlow(timeElapsed);
      strip.show();
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait)
      {
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "OVERDRIVE";
          backgroundColor();
          lastButtonPress = millis();
        }
      }
    }
    
    void overdrive(int button1State, int button2State)
    {
      Serial.println("OVERDRIVE");
      // Check if fuel has expired
      if(fuelRemaining <= 0)
      {
        state = "UNPOWERED";
        return;
      }
      
      // Calculate fuel expenditure
      unsigned long newFuelUpdate = millis();
      unsigned long timeElapsed = newFuelUpdate - lastFuelUpdate;
      fuelRemaining -= (timeElapsed * 2);
      
      // If we've used up more fuel than we have left, set remaining fuel to zero
      fuelRemaining = max(fuelRemaining, 0);
      
      // Reset our latest fuel update to the current time
      lastFuelUpdate = newFuelUpdate;
      
      // Colorize the lights on the board to represent the mode we're in
      digitalWrite(RED_PIN, HIGH);
      digitalWrite(GREEN_PIN, HIGH);
      digitalWrite(BLUE_PIN, HIGH);
      poweredGlow(timeElapsed);
      overdriveGlow(timeElapsed);
      strip.show();
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait){
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "POWERED";
          backgroundColor();
          lastButtonPress = millis();
        }
      }
    }
    
    void unpowered(int button1State, int button2State)
    {
      digitalWrite(RED_PIN, LOW);
      digitalWrite(GREEN_PIN, LOW);
      digitalWrite(BLUE_PIN, HIGH);
      noGlow();
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait){
        if(button1State == LOW)
        {
          state = "REFUELING";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          state = "POWERED";
          backgroundColor();
          lastButtonPress = millis();
        }
      }
    }
    
    void refueling(int button1State, int button2State)
    {
      digitalWrite(RED_PIN, LOW);
      digitalWrite(GREEN_PIN, HIGH);
      digitalWrite(BLUE_PIN, LOW);
      
      // Check button states to see if we should switch mode
      if(millis() - lastButtonPress > buttonWait){
        if(button1State == LOW)
        {
          state = "UNPOWERED";
          lastButtonPress = millis();
        }
        else if(button2State == LOW)
        {
          fuelRemaining += 60000;
          lastButtonPress = millis();
        }
      }
     
    }
    
    void backgroundColor()
    {
      uint32_t c = strip.Color(0, 225, 0);
      for(uint16_t i=0; i<strip.numPixels(); i++) {
          strip.setPixelColor(i, c);
          
      }
    }
    
    void noGlow()
    {
      uint32_t c = strip.Color(0, 0, 0);
      for(uint16_t i=0; i<strip.numPixels(); i++) {
          strip.setPixelColor(i, c);
          
      }
      strip.show();
    }
    
    
    unsigned long totalPulseTimeElapsed = 0.0;
    int pulseIndex = 0;
    int pulseSize = 11; // Must be odd please
    int currentPulseSize = 1;
    int brightnessStep = 10;
    void poweredGlow(unsigned long elapsed)
    {
      //Serial.println("Powered Glow");
      // Send a pulse of light from the tips of the strand out to the middle, shifting the color very slightly in the pulse's wake
      // Start all the pixels at the same color
      
      
      
      
      totalPulseTimeElapsed += elapsed;
      //Serial.println("Time Elapsed:" + elapsed);
      //Serial.println("Total Elapsed:" + totalPulseTimeElapsed);
      if(totalPulseTimeElapsed >= 20)
      {
        updatePulse();
        totalPulseTimeElapsed = 0;
      }
    }
    
    void updatePulse()
    {
      //Serial.println("Update Pulse");
      backgroundColor();
      int index = pulseIndex;
      int midpoint = (strip.numPixels() /2) -1;
      int brightness = brightnessStep;
      // While index is >= 0
      
      while (index >=0 && brightness > 0)
      {
        
        // if index is greater than midpoint, then the pulses have met in the middle. Do not render a pulse at this index. 
        
        // Otherwise, render a pulse at the current index at the current brightness
        // Then, subtract the current index from the end of the string and render a pulse at that index at the current brightness.
        if(index <= midpoint)
        {
          uint32_t color = strip.Color(brightness, 225, brightness);
          strip.setPixelColor(index, color);
          strip.setPixelColor(strip.numPixels() - (index+1), color);
        
        }
        // If pulseIndex - index < half of the pulse size, rounding up, subtract 1 from index and add a step to the brightness
        if(pulseIndex - index < (pulseSize/2) + 1)
        {
          index -= 1;
          brightness += brightnessStep;
        }
        // Otherwise, subtract 1 from index and subtract a brightness step.
        else
        {
          index -= 1;
          brightness -= brightnessStep;
        }
      }
      pulseIndex += 1;
      if(pulseIndex >= midpoint + pulseSize)
      {
        pulseIndex = 0;
      }
      
    }
    
    
    void overdriveGlow(unsigned long elapsed)
    {
      //Serial.println("Overdrive Glow");
     // First decide whether or not we should have a pulse
      // Check to see if there are already three active pulses
      int availableGlow = 0;
      boolean glowAvailable = false;
      for(int i = 0; i < NUMBER_GLOWS; i++)
      {
        // If we find a pulse that has expired, this is where we will add a new pulse
        if(overdriveGlows[i].b_available == true)
        {
          glowAvailable = true;
          availableGlow = i;
        }
      }
      //If no glows were found, return
      if(glowAvailable)
      {
        //Serial.println("Glow Is Available");
        // If we have a glow available, then generate a random number to see if a new glow will occur!
        int rand = random(1, 100);
        int percentage = 30;
        // If the random number indicates that we should have a glow, generate the pulse data
        if(rand < percentage)
        {
          //Serial.print("Creating New Glow");
          // Determine how long the pulse should last and where it should be located
          int midpoint = (strip.numPixels() /2) -1;
          int center = random(0, midpoint);
          unsigned long duration = 0 + random(200, 300);
          
          
          overdriveGlows[availableGlow].centerIndex = center;
          overdriveGlows[availableGlow].timeRemaining = duration;
          //Serial.println(duration);
          overdriveGlows[availableGlow].glowsize = random(3,9);
          overdriveGlows[availableGlow].b_available = false;
         
        }
        
      }
      updateGlow(elapsed);
      
     
    }
    
    void updateGlow(unsigned long elapsed)
    {
      //Serial.println("Updateing Glow");
      //Serial.print("Number of Glows: ");
      //Serial.println(NUMBER_GLOWS);
      int midpoint = (strip.numPixels() /2) -1;
      //Serial.print("Midpoint: ");
      //Serial.println(midpoint);
      for(int i = 0; i < NUMBER_GLOWS; i++)
      {
        //Serial.println(i);
        int brightness = random(0,225);
        //Serial.print("Brightness: ");
        //Serial.println(brightness);
        if(overdriveGlows[i].timeRemaining >= 0)
        {
          //Serial.print("Time Remaining:");
          //Serial.println(overdriveGlows[i].timeRemaining);
          overdriveGlows[i].timeRemaining -= elapsed;
          for(int j = 0; j < overdriveGlows[i].glowsize; j++)
          {
            int brightness = random(0,225);
            int center = overdriveGlows[i].centerIndex;
            if(center +j <= midpoint)
            {
              strip.setPixelColor(center + j, strip.Color(brightness, 225, brightness));
            }
            if(center - j >= 0)
            {
              strip.setPixelColor(center - j, strip.Color(brightness, 225, brightness));
            }
            
            if(midpoint < strip.numPixels() - center - j-1)
            {
              strip.setPixelColor(strip.numPixels() - center -j-1, strip.Color(brightness, 225, brightness));
            }
            if(strip.numPixels() > strip.numPixels() - center +j-1)
            {
              strip.setPixelColor(strip.numPixels() - center +j-1, strip.Color(brightness, 225, brightness));
            }
            brightness = max(0, brightness - brightnessStep);
          }
        }
        else{
          overdriveGlows[i].b_available = true;
        }
      }
    }
    
    boolean validateStripIndex(int i)
    {
      if(i < 60 && i >=0)
      {
        return true;
      }
      return false;
    }
    // Input a value 0 to 255 to get a color value.
    // The colours are a transition r - g - b - back to r.
    uint32_t Wheel(byte WheelPos) {
      WheelPos = 255 - WheelPos;
      if(WheelPos < 85) {
       return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
      } else if(WheelPos < 170) {
        WheelPos -= 85;
       return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
      } else {
       WheelPos -= 170;
       return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
      }
    }
    
  • I actually am going to post a video later tonight, once I get it crammed in the armor's spine, I realized the old video was from back when the individual "glows" were not being deleted, it looks a lot cooler now because the glow jumps around all over the place.
  • Ok, Paul. Looking forward to it.


  • There's the updated glow, the spine still isn't finished so I guess I"m not doing to well on the part where you incorporate it with your prop, but at least I have the lights going the way I wanted to. 
  • And here's the paper prototype of the spine:

  • Here is the paper prototype with the electronics removed (since it can't support their weight). This is just the torso, there are also arm, shoulder and leg sections that I haven't even touched yet since they won't have electronics in them for a while. Now that I have the shape down, I'm going to cut it all into foam. The electronics are going to live in the back plate, with plenty of foam padding on either side of them, probably nested between layers of foam, with significant ventilation so that heat buildup doesn't become much of a problem.
    1.jpg 23.3K
    2.jpg 17.3K
    3.jpg 20.4K
    4.jpg 19.8K
    5.jpg 21.3K
Sign In or Register to comment.