Jump to content


Photo

[WIP] Stampede ACB, the Arduino-powered Stampede ECS

First ever blaster mod

28 replies to this topic

#26 Oryhara

Oryhara

    Member

  • Members
  • 4 posts

Posted 11 August 2013 - 04:51 PM

Ok, here is the code for my pair of relay-controlled stampedes.
I'll apologize now because it is both horribly commented and way more than you need for what you want to do.
This project had an LCD display that showed ammo for both stampedes, had configurable burst fire, semi, and fully automatic fire modes, and maintained preferences when powering on and off.
here is the bit that managed the relay and stampede fire cycle switch.

void pullTrigger(int barrel)
{//0 neither, 1 upper, 2 lower
  if(barrel == 1)
  {
    while(digitalRead(UpperSensor) == HIGH)
    {//check and see if the firing cycle has taken over
      Serial.println("firing interval");
      digitalWrite(RelayUpper, LOW);//turn the relay on 
      delay(FIRING_INTERVAL);//wait a little bit
      digitalWrite(RelayUpper, HIGH);//turn the relay off
    }
  }
  else if(barrel == 2)
  {
    while(digitalRead(LowerSensor) == HIGH)
    {//check and see if the firing cycle has taken over
      Serial.println("firing interval");
      digitalWrite(RelayLower, LOW);//turn the relay on 
      delay(FIRING_INTERVAL);//wait a little bit
      digitalWrite(RelayLower, HIGH);//turn the relay off
    }    
  }
}

Like i said, i had two stampedes, but there you can see how it handled firing via relay.

the full code is below

 /*

*/

#include <LiquidCrystal.h>
#include <DFR_Key.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <phi_big_font.h>
#include <EEPROM.h>

//storing preferences between power cycles
#define EEPCOUNTUPPER 0
#define EEPCOUNTLOWER 1
#define EEPBARREL 2
#define EEPROUNDS 3
#define EEPMODE 5


#define CAPACITY 18
#define DISPLAYCOUNT 19
char* capacityDisplay[] = {"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "XX"};
static unsigned char CountUpper;
static unsigned char CountLower;

#define BARRELS 3 

static boolean Shooting = true;
static boolean menuBarrel = true;
static int Mode; //0: Both, 3 Empty uppersecond; 4 Lower emptysecond; 5 Both empty; 6 menuRounds; 7 menuBarrel
//0 > 5 > 0
// 1 > 3 > 2 > 4 > 5
// 2 > 4 > 1 > 3 > 5

static char Rounds; //0: semi-auto, 1: Full auto, 2+ bursts 
static unsigned char Barrel; //0 both, 1 upper, 2 lower

const static int RelayUpper = 11;
static const int RelayLower = 12;
static const int Trigger = 13;
#define UpperSensor  A2
#define LowerSensor  A3
#define FIRING_INTERVAL 10

//A1 unused
#define UPPERMAGSENSOR A4
#define LOWERMAGSENSOR A5
#define UPPEROVERRIDE 2
#define LOWEROVERRIDE 3
#define BLINKRATE 500
#define DISPLAYRATE 3000
//Pin assignments for SainSmart LCD Keypad Shield
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 
//---------------------------------------------
DFR_Key keypad;

int localKey = 0;
String keyString = "";

unsigned long time;
unsigned long lastDisplay;
unsigned long lastBlink;

void setup() 
{ 
  Serial.begin(9600);
  
  digitalWrite(RelayUpper, HIGH);
  digitalWrite(RelayLower, HIGH);
  pinMode(RelayUpper,OUTPUT);
  pinMode(RelayLower,OUTPUT);
  pinMode(Trigger,INPUT_PULLUP);
  pinMode(UpperSensor,INPUT_PULLUP);
  pinMode(LowerSensor,INPUT_PULLUP);
  pinMode(UPPERMAGSENSOR, INPUT_PULLUP);
  pinMode(LOWERMAGSENSOR, INPUT_PULLUP);
  pinMode(UPPEROVERRIDE, INPUT_PULLUP);
  pinMode(LOWEROVERRIDE, INPUT_PULLUP);
  init_big_font(&lcd);
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("SPARKYBFG ONLINE");
  lcd.setCursor(0,1);
  lcd.print("INITIALIZING NOW");
  waitTrig(2000);
  lcd.clear();

  Mode = 1;

  keypad.setRate(2500);
  char check = EEPROM.read(0);
  if(check == 255)
  {//no preferences saved
    CountUpper = CAPACITY;
    CountLower = CAPACITY;
    Rounds = 1;
    Barrel = 0;
  }
  else
  {
    CountUpper = EEPROM.read(EEPCOUNTUPPER);
    CountLower = EEPROM.read(EEPCOUNTLOWER);
    Rounds = EEPROM.read(EEPROUNDS);
    Barrel = EEPROM.read(EEPBARREL);
  }
  displayCount();
}

void loop()
{ 
  waitTrig(100);
  Serial.print("Barrel: ");
  Serial.println( Barrel);
  Serial.print("Rounds: ");
  Serial.println(Rounds, DEC);
  HandleKeys();

  //reload detection
  if(digitalRead(UPPERMAGSENSOR) == HIGH)
  {
    Serial.println("Upper Reloaded");
    CountUpper = CAPACITY;
  }
  if(digitalRead(LOWERMAGSENSOR) == HIGH)
  {
    Serial.println("Lower Reloaded");
     CountLower = CAPACITY;
  }
  
//  Serial.println(analogRead(UPPEROVERRIDE));
//  Serial.println(analogRead(LOWEROVERRIDE));
  if(digitalRead(UPPEROVERRIDE) == LOW)
  {
    Serial.println("Upper Disabled");
    Barrel = 1;
    CountUpper = 0;
  }
  if(digitalRead(LOWEROVERRIDE) == LOW)
  {
    Serial.println("Lower Disabled"); 
    Barrel = 2;
    CountLower = 0;
  }
//firing control -- Blocking
  while(digitalRead(Trigger) == LOW)
  {
    Serial.println("shooting");
    Shooting = true;
    
    switch(Barrel){
      case 0://fire both barrels
      {
        FireBoth(Rounds);
      }    
      break;
      case 1://fire upper 
      {
        FireUpper(Rounds);
      }
      break;
      case 2://fire lower
      {
        FireLower(Rounds);
      }
      break;
      default://not a valid firing mode
      {
        
      }
      break;
      
    }
  }
  time = millis();
  if(Shooting == false)
  {
    Serial.println("Not Shooting.  Display menu");
    if(time > lastDisplay + DISPLAYRATE)
    {
      selectCount();
      lastDisplay = millis();
    }
    if(lastBlink + BLINKRATE < time)
    {
      if(((time/BLINKRATE)%2) == 0)
      {
        blinkMenuCursor();
      }
      else
      {
        blankMenuCursor();
      }
    }
  }
  else
  {
    if(time > lastDisplay + DISPLAYRATE)
    {
      displayCount();
      lastDisplay = millis();
    }
  }
    
  switch(Mode){
    break;
    case 3:
    {
      if(CountLower == 0)
      {
        Mode = 5;
      }
      else
      {
        Mode = 2;
        Barrel = 2;
      }
    }
    break;
    case 4:
    {
      if(CountUpper == 0)
      {
        Mode = 5;
      }
      else
      {
        Mode = 1;
        Barrel = 1;
      }
    }
    break;
    case 5:
    {
      reload();
    }
    break;
    default:
    {
    }
    //don't need to do nothin
  }
  savePrefs();
}
void FireBoth(int rounds)//0 makes no sense in this mode, but is valid.  
{
  if(rounds == 0)
  {
    FireUpper(rounds);
    Barrel = 1;
  }
  else
  {
    short ready;
    while(rounds > 0)
    {
      ready = readyToFire();
      if(ready)
      {
        pullTrigger(ready);
        rounds--;
        shotFired(ready);
      } 
    }
  }
}

void shotFired(short barrel)
{
  if(barrel == 1)
  {
    CountUpper--;
    displayCount();
  }
  else
  {
    CountLower--;
    displayCount();
  }
}

short readyToFire()//checks upper first
{
  if(digitalRead(UpperSensor) == HIGH)
  {
    return 1;
  }
  else if(digitalRead(LowerSensor) == HIGH)
  {
    return 2;
  }
  else
  {
    return 0;
  }
}

void pullTrigger(int barrel)
{//0 neither, 1 upper, 2 lower
  if(barrel == 1)
  {
    while(digitalRead(UpperSensor) == HIGH)
    {//check and see if the firing cycle has taken over
      Serial.println("firing interval");
      digitalWrite(RelayUpper, LOW);//turn the relay on 
      delay(FIRING_INTERVAL);//wait a little bit
      digitalWrite(RelayUpper, HIGH);//turn the relay off
    }
  }
  else if(barrel == 2)
  {
    while(digitalRead(LowerSensor) == HIGH)
    {//check and see if the firing cycle has taken over
      Serial.println("firing interval");
      digitalWrite(RelayLower, LOW);//turn the relay on 
      delay(FIRING_INTERVAL);//wait a little bit
      digitalWrite(RelayLower, HIGH);//turn the relay off
    }    
  }
}
  
void FireUpper(int rounds)
{ 
  if(CountUpper != 0)
  {
    if(rounds == 0)
    {
      while(digitalRead(UpperSensor) == HIGH)
      {//check and see if the firing cycle has taken over
        Serial.println("firing interval");
        digitalWrite(RelayUpper, LOW);//turn the relay on 
        delay(FIRING_INTERVAL);//wait a little bit
        digitalWrite(RelayUpper, HIGH);//turn the relay off
      }
      while(digitalRead(UpperSensor) == LOW)
      {
        //firing in progress
      }//and finished
      shotFired(Barrel);
      if(CountUpper == 0)
      {
        Mode = 3;
      }
      while(digitalRead(Trigger) == LOW)
      {
      //wait because semi-auto means 1 shot per pull
      }
    }

    for(int i = rounds; i> 0; i--)
    {
      if(CountUpper == 0)
      {
        FireLower(i);
        i = 0;
      }
      while(digitalRead(UpperSensor) == HIGH){//check and see if the firing cycle has taken over
        Serial.println("firing interval");
        digitalWrite(RelayUpper, LOW);//turn the relay on 
        delay(FIRING_INTERVAL);//wait a little bit
        digitalWrite(RelayUpper, HIGH);//turn the relay off
      }
      while(digitalRead(UpperSensor) == LOW)
      {
        //firing in progress
      }//and finished
      shotFired(Barrel);
    }
  }
}

void FireLower(int rounds)
{ 
  if(CountLower != 0)
  {
    if(rounds == 0)
    {
      while(digitalRead(LowerSensor) == HIGH)
      {//check and see if the firing cycle has taken over
        Serial.println("firing interval");
        digitalWrite(RelayLower, LOW);//turn the relay on 
        delay(FIRING_INTERVAL);//wait a little bit
        digitalWrite(RelayLower, HIGH);//turn the relay off
      }
      while(digitalRead(LowerSensor) == LOW)
      {
        //firing in progress
      }//and finished
      shotFired(Barrel);
      if(CountLower == 0)
      {
        Mode = 3;
      }
      while(digitalRead(Trigger) == LOW)
      {
      //wait because semi-auto means 1 shot per pull
      }
    }

    for(int i = rounds; i> 0; i--)
    {
      if(CountLower == 0)
      {
        FireUpper(i);
        i = 0;
        Mode = 3;
      }
      else
      {
        while(digitalRead(LowerSensor) == HIGH){//check and see if the firing cycle has taken over
          Serial.println("firing interval");
          digitalWrite(RelayLower, LOW);//turn the relay on 
          delay(FIRING_INTERVAL);//wait a little bit
          digitalWrite(RelayLower, HIGH);//turn the relay off
        }
        while(digitalRead(LowerSensor) == LOW)
        {
          //firing in progress
        }//and finished
        shotFired(Barrel);
      }
    }
  }
}
void Select_Button()
{
  Serial.println("Select Pressed");
  Shooting = !Shooting;
    Serial.print("Shooting value : ");
      Serial.println(Shooting);
  if(Shooting)
  {
    displayCount();
  }
  else
  {
    lastDisplay = 0;
  }
}  

void Left_Button()
{
  Serial.println("leftbutton pressed");
  if(Shooting)
  {
    reload();
  }
  else if(menuBarrel)
  {
    Barrel = (Barrel +(BARRELS-1))%BARRELS;
    lastDisplay = 0;
  }
  else
  {
    
    if(Rounds == 0)
    {
      Rounds = 1;
    }
    else
    {
      Rounds--;
    }
    lastDisplay = 0;
  }
}  

void Right_Button()
{
  Serial.println("Right Pressed");
  if(Shooting)
  {
    reload();
  }
  else if(menuBarrel)
  {
    Barrel = (Barrel + 1)%BARRELS;
    lastDisplay = 0;
  }
  else
  {
    Rounds++;
    lastDisplay = 0;
  }
}
void Up_Button()
{
  Serial.println("Up Pressed");
  if(Shooting)
  {
    reload();
  }
  else 
  {
    menuBarrel = !menuBarrel;
    lastDisplay = 0;
    lastBlink = 0;
  }
}

void Down_Button()
{
  Serial.println("Down Pressed");
  if(Shooting)
  {
    reload();
  }
  else 
  {
    menuBarrel = !menuBarrel;
    lastDisplay = 0;
    lastBlink = 0;
  }
}

void HandleKeys()
{
  localKey = keypad.getKey();
  
  if (localKey != -1)//TODO confirm that -1 is correct and not 0
  {
    //Serial.println(localKey);
  //Key Codes (in left-to-right order):
  //None   - 0
  //Select - 1
  //Left   - 2
  //Up     - 3
  //Down   - 4
  //Right  - 5
    switch(localKey){
      case 1:
      {
        Select_Button();
      }
      break;
  
      case 2:
      {
        Left_Button();
      }
      break;
      
      case 3:
      {
        Up_Button();
      }
      break;
      
      case 4:
      {
        Down_Button();
      }
      break;
      
      case 5:
      {
        Right_Button();
      }
      break;
      
      default: //should never happen
      break;
    }

  }
}

void blinkMenuCursor()
{
  if(menuBarrel)
  {
    lcd.setCursor(5,1);
    lcd.print("::");
  }
  else
  {
    lcd.setCursor(5,0);
    lcd.print("::");
  }
}
void blankMenuCursor()
{
  if(menuBarrel)
  {
    lcd.setCursor(5,1);
    lcd.print("  ");
  }
  else
  {
    lcd.setCursor(5,0);
    lcd.print("  ");
  }
}

void selectCount(){
// ________________
// BURST: SEMI-AUTO
// BURST: FULL-AUTO
// BURST: XX ROUNDS
// ________________
// FIRE :  UPPER
// FIRE :  LOWER
// FIRE :  DOUBLE

  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("FIRE   ");
  switch(Barrel)
  {
    case 0:
      lcd.print(" DOUBLE");
    break;
    case 1:
      lcd.print(" UPPER");
    break;
    case 2:
      lcd.print(" LOWER");
    break;
    default:
      lcd.print(" ERROR");
  }
  lcd.setCursor(0,0);
  lcd.print("BURST  ");
  switch(Rounds)
  {
    case 0:
      lcd.print("SEMI-AUTO");
    break;
    case 1:
      lcd.print("FULL-AUTO");
    break;
    default:
      lcd.print(Rounds, DEC);
      lcd.print(" ROUNDS");
  }

}


void displayCount()
{
  lcd.clear();
  render_big_msg(capacityDisplay[CountLower],0,0);
  render_big_msg(capacityDisplay[CountUpper],8,0);
  
}

void reload()
{
  CountUpper = CAPACITY;
  CountLower = CAPACITY;
  displayCount();
}

void waitTrig( int mseconds)
{
  unsigned long endTime = millis() + mseconds;
  while(endTime > time)
  {
    time = millis();
    if(digitalRead(Trigger) == LOW)
    {
      endTime = 0;
    }
  }
}
    
void savePrefs()
{
  char check;
  check = EEPROM.read(EEPCOUNTUPPER);
  if(check != CountUpper)
  {
    EEPROM.write(EEPCOUNTUPPER, CountUpper);
  }
  check = EEPROM.read(EEPCOUNTLOWER);
  if(check != CountLower)
  {
    EEPROM.write(EEPCOUNTLOWER, CountLower);
  }
  check = EEPROM.read(EEPBARREL);
  if(check != Barrel)
  {
    EEPROM.write(EEPBARREL, Barrel);
  }  
  check = EEPROM.read(EEPROUNDS);
  if(check != Rounds)
  {
    EEPROM.write(EEPROUNDS, Rounds);
  }
}






  • 0

#27 Hammy

Hammy

    Member

  • Members
  • 104 posts
  • Location:Singapore
  • Country:Singapore

Posted 11 August 2013 - 11:34 PM

If it has not been mentioned, when scanning the status of buttons with your microcontroller, you must scan 2-3 times and only return the result Opened or Closed if all 2-3 scans return the same status. There must be 10-20 ms delay between each scan. All this is to avoid ground-bounce, and obtain a reliable reading from your switch.

If your microcontroller input already contains a ground-bounce circuit. Then forget about this.

Edited by Hammy, 11 August 2013 - 11:48 PM.

  • 0

#28 Papkee

Papkee

    Member

  • Members
  • 11 posts
  • Location:Michigan City
  • State:Indiana
  • Country:United States

Posted 12 August 2013 - 09:00 AM

Ok, here is the code for my pair of relay-controlled stampedes.
I'll apologize now because it is both horribly commented and way more than you need for what you want to do.
This project had an LCD display that showed ammo for both stampedes, had configurable burst fire, semi, and fully automatic fire modes, and maintained preferences when powering on and off.
here is the bit that managed the relay and stampede fire cycle switch.

void pullTrigger(int barrel)
{//0 neither, 1 upper, 2 lower
  if(barrel == 1)
  {
    while(digitalRead(UpperSensor) == HIGH)
    {//check and see if the firing cycle has taken over
      Serial.println("firing interval");
      digitalWrite(RelayUpper, LOW);//turn the relay on 
      delay(FIRING_INTERVAL);//wait a little bit
      digitalWrite(RelayUpper, HIGH);//turn the relay off
    }
  }
  else if(barrel == 2)
  {
    while(digitalRead(LowerSensor) == HIGH)
    {//check and see if the firing cycle has taken over
      Serial.println("firing interval");
      digitalWrite(RelayLower, LOW);//turn the relay on 
      delay(FIRING_INTERVAL);//wait a little bit
      digitalWrite(RelayLower, HIGH);//turn the relay off
    }    
  }
}

Like i said, i had two stampedes, but there you can see how it handled firing via relay.


The only issue I have with that is that I would like my LED to be flashing even when firing. A delay wouldn't allow that. Now, adding a whole timing function is a lot more work, but I'd really rather not have the whole code stop for firing anyway.
  • 0

"But I can't hit with my driver!"

"Just hit with your putter; rules don't define you!" - anonymous


#29 Oryhara

Oryhara

    Member

  • Members
  • 4 posts

Posted 12 August 2013 - 03:42 PM

The only issue I have with that is that I would like my LED to be flashing even when firing. A delay wouldn't allow that. Now, adding a whole timing function is a lot more work, but I'd really rather not have the whole code stop for firing anyway.


It really wouldn't be hard to process LED blink code in there.

But its your project, I just wanted to share the lessons i learned while building remote-controlled stampedes.
  • 0


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users