I THINK ∴ I'M DANGEROUS

CMA-AG1

The CMA-AG1 is what I'm calling my project to build a projectile weapon. The name stands for Conventional Mass Accelerator AirGun mark 1. This started with my desire to build a rail gun. Upon looking at the math and price of components I decided to try a gauss gun. After looking at the math and price of the components, I decided upon an air gun.

Basic Design

This is a battery powered, microcontroller operated device. The battery runs both the mcu and an electric motor which runs a pneumatic pump. The pump fills and pressurizes an air reservoir. A pressure sensor provides the mcu with the reservoir's pressure. The mcu uses the pressure sensor to maintain a minimum pressure and controls the pump via a relay. A solenoid valve, also operated via an mcu controlled relay, connects the air reservoir to the barrel.

The trigger is connected to a GPIO pin on the mcu so that fine-tuning can be controlled in software (e.g. duration of air-burst and fully automatic firing).

Practical Design

The mcu is one of the smaller 14-pin PDIP msp430 chips. Most likely an msp430g2231 which provides adequate timing (1 MHz clock) and GPIO pins (one for trigger, one for the pump, one for the solenoid valve, and 2 to 4 for the sensor depending on its interface.

The pump is a salvaged 12v tire pump rated for 300 PSI, but I believe can be operated some unknown percentage above this rating.

The reservoir will be constructed from PVC pipe. 2“ diameter Schedule 80 PVC pipe should be able to easily handle 500 PSI (3447 kPa).

12 volt solenoid valves rated for up to 2000 PSI (roughly 12 mPa) are being investigated.

Off the shelf zinc or copper clad steel BBs will be the firing medium. They are cheap, plentiful, and uniform enough.

Either piping or BB-gun repair part will be used for the barrel.

Battery, trigger, and housing will be from a battery-powered reciprocating saw.

Alternate Design

I've had to simplify my design. Originally the MCU was responsible for maintaining a minimum pressure while not exceeding a maximum as well as letting each shot “sip” at the reservoir.

The current design is to fill the the reservoir to pressure after every shot and every shot fully dumps the reservoir. I could greatly simply the electronics and rather than use an MCU I could use a 555 timer. The 555 would control the amount of time the solenoid valve is held open. The pswitch could be wired directly to the pump along with a manual kill switch so the pump runs whenever the the reservoir is under pressurized.

But considering I already built the board using the MCU, I'll stick with that. I just wanted to acknowledge that I'm aware a simpler and probably more robust design is possible.

Extras

Once I have a proven, functional prototype I'd like to add the following extras:

  • Laser sight
  • battery charge reader/display

Actual Implementation

Due to parts availability, 1 foot long, 1 inch diameter schedule 80 PVC was used for the reservoir. Which provides roughly 9 in3. PVC cement was used with threaded caps. The PVC cement is rated for 180 PSI. I am assuming that is for joints without threads thus joints with threads should be able to take significantly more.

Firmware

Here's the code for the firmware. This is for an msp430g2231, compiles with msp430-gcc (MSPGCC-GIT-UNIARCH) 4.6.3 20120301 (mspgcc LTS 20120406 unpatched).

cmaag1_fw.c
#include <msp430g2231.h>
 
// inputs
#define PSWITCH BIT1
#define TRIGGER BIT3
 
// outputs
#define SVALVE BIT6
#define PUMP BIT0
#define FEED BIT7
 
// globals
enum weaponstate { NOCHARGE, CHARGED, CHARGING, FIRING };
 
int wstate; 
 
void fire (void)
{
	/* weapon firing subroutine */
 
	/* set the state */
	wstate = FIRING;
 
	/* NB: The pswitch should not trigger ther P1 INT routine while fire() is running since fire()
	   is only ever called from p1int. */
 
	/* activate FEED magnet (prevents non-chambered ammo from falling into chamber during firing) */
 
	P1OUT |= FEED;
 
 
	/* enabler timera */
	TACTL = TASSEL_2 + MC_3 + ID_2; // ACLK, up/down mode, factor 4 divider
 
	/* open solenoid valve / dump air */
	P1OUT |= SVALVE;
 
	/* done. the timer will close valve and then run the pump ( CHARGING state ) */
 
}
 
void __attribute__((interrupt (PORT1_VECTOR))) p1int (void)
{
	if (P1IFG & TRIGGER) {
		switch (wstate) {
			case FIRING:
			case CHARGING:
			case NOCHARGE:
				/* for now, do nothing. Maybe later I'll add a sound effect or something */
				break;
			case CHARGED:
				/* the gun is charged and the trigger was pulled. FIRE ZE WEAPON! */
				fire();
				break;
		}
 
		P1IFG &= ~ TRIGGER;
	}
 
	if (P1IFG & PSWITCH) {
		if (wstate == CHARGING) 
		{
			/* pressure switch tripped while charging, that means we're charged! */
			wstate = CHARGED;
			P1OUT &= ~PUMP; /* turn off pump */
		}
 
		P1IFG &= ~ PSWITCH;
	}
}
 
void __attribute__((interrupt (TIMERA0_VECTOR))) timera (void)
{
	static unsigned int i = 0;
 
	if ( i++ > 1 ) 
	{
		i = 0; 
		TACTL = MC_0; // stop timer
 
		P1OUT &= ~( FEED + SVALVE); // close svalve, turn off FEED magnet
 
		wstate = CHARGING; // change state to charging
 
		P1OUT |= PUMP; // and activate pump
	}
}
 
int main()
{
	WDTCTL = WDTPW + WDTHOLD; // disable watchdog 
 
	P1DIR |= SVALVE + PUMP + FEED; // set SVALVE PUMP FEED to output
 
	P1DIR &= ~( TRIGGER + PSWITCH ); // set TRIGGER and PSWITCH to input
 
	P1OUT &= ~( SVALVE + PUMP + FEED); // make sure SVALVE and PUMP and FEED are off
 
 
	// setup IRQ for GPIO 	
	P1IE = PSWITCH + TRIGGER;
	P1IES |= PSWITCH + TRIGGER; // set high-to-low transition interrupt trigger
	P1IFG = 0; // clear all interrupt flags
 
	P1REN &= ~ ( PSWITCH + TRIGGER ); // enable internal pull up/down internal resistor 
	P1OUT |= (PSWITCH + TRIGGER);  // set to pull up resistor
 
 
	// set up timera configuration ( but do not start timer)
	CCTL0 = CCIE; // enable timera's interrupt 
	CCR0 = 0xffff; // count up to this then down to zero
 
 
	// check if the pswitch is active / figure out our state
 
	if ( (P1IN & PSWITCH) == 0)
	{
		wstate = CHARGING;
		P1OUT |= PUMP;
	} else {
		wstate = CHARGED;
	}	
 
	__eint(); // enable interrupts
 
	LPM0; // low power mode 0
}