SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By StaticDet5
#140181
I don't know how fast it's shooting. It's a decent AEG running a LiPo. I imagine it's more than 15 rounds per second. I'll measure it with Audacity tomorrow morning.

I'm going to re-write some Arduino code tomorrow to attempt a single-shot system with the current sensor.

One of the things that has really drawn me to this project is the advanced interpretation of basic sensors.
One of the things that happens to newbie players (and some not so new) is "Dry Firing". The gearbox is cycling, but no rounds are being fired (It can happen for a couple of reasons). That is literally why I want to track rounds fired, AND gearbox cycles. It'll be nice to see how many cycles the gearbox has, but it will be "tactically relevant" to see if the AEG is actually firing rounds when it is cycling.
By StaticDet5
#140206
The system is cycling 24 times per second, cleanly, on the test bed. This was measured using the Audacity open-source sound software, with no magazine (dry-firing).

I've hit a big stumbling block. I need to do more research today.
I've written code for a single shot. Essentially it waits until the amp sensor reads a low value (In this case, 5 amps), and then it stops the firing cycle and waits for the user to let off of the trigger.
The issue is that I'm consistently getting 3 round bursts.
I think this is because of the sample rate. This is a big issue, because right now there is no "code overhead". The only thing the processor is doing is checking the sensor state.

I'm going to play around with the low amp value, but I'm also going to research overclocking an Arduino.
By dkulinski
#140209
I have been following this fantastic thread and I must applaud your efforts. This is a brilliant project and I have enjoyed following it.

You'll have to excuse my lack of knowledge on electronics, but I do know programming and am familiar with the limits of the Arduino/ATMega series. I am using one in my lasertag project.

As for your Arduino, lets do some quick math to figure out a decent sample rate. You stated that your firing rate is 24 times per second. That is 41.7ms per round. What I have seen on the internet is that analogRead takes about 100us to complete. So you should be able to read at least 410 times per round fired if that is all the main loop did. Honestly that is overkill and you just want to pick up when the amperage is at a low threshold.

How are you currently reading the analog pin? Are you just looping through it constantly? Technically you should be able to poll all the way up to 9600Hz but that would take up all the CPU time at 16MHz. Maybe at 1/4 of that speed, so 2400Hz or 100 samples per cycle? Setting up a timer interrupt to read the analog pin would be what I consider ideal.

Looking forward to your progress and I hope I can be more than a vague help in the future.
By StaticDet5
#140217
Thank you for the compliments.
You're in very good company. I really don't have any experience with electronics or microprocessors. It's interesting that you mention you're using an Arduino for your lasertag project. That's the next project that I want to tackle (open source lasertag). Right now it's on the shelf because I'm going to need to delve a little more into optics to determine the best way to focus an IR LED to extreme ranges.

I stopped messing with the code a couple of hours ago. I couldn't get past the previous issue. I can't seem to get the sample cycle short enough.

However, I'm doing the math and it seems like I should be able to get this working.
If the sensor is capable of updating at 40kHz, then it is updating 1,666 times between every round being fired.
The Arduino itself should be able to measure the timings easily. Hell, I'm guessing that if I can just measure ten times per firing cycle, I can get enough of a read to stop the gearbox.

I'm not too worried (right now) about taking up all of the processor time. Right now, during the firing cycle, the only thing the processor needs to be doing is checking the current flow to the motor. I'm not doing any serial outputs or anything. Heck, I have the board LED (pin 13) being driven high when the trigger switch is pressed, but all of that code is outside of the firing/amp detection code.
The fact that it is reading a low current condition leads me to think that the sensor is working correctly.

However, what I'm seeing does not match our expectations. I need to figure out what's going on. Tomorrow morning I may try programming an array, to hold amperage values, to be reported at the end of the firing cycle. This should not contribute significantly to processor overhead, and give me a peek into what's going on.

dkulinski, where is the 9600Hz figure coming from?
If I set up a timer interrupt, how long do I set the timer for? How do I find that period? I'm trying to use the current sensor to detect the end of the firing cycle (when the current flow drops dramatically). I'm trying to avoid using a timer to regulate the firing cycle.
By dkulinski
#140219
It is from the wiring source code. There is a default scaler being applied to the 16MHz clock of the Arduino and with that sample rate you get top resolution of the ADC. If you change the scaler you can get more samples but less accuracy. With the large jumps in amperage this may not be a detriment.

http://www.arduino.cc/cgi-bin/yabb2/YaB ... 8715493/11

Check out reply number 2 in that thread to see what I am talking about and give you a bit of insight of the ADC on the ATMega.

Alright, timers, one of my favorite subjects. You need a timer to fire off X times every 41.7ms. Lets call it 100 for now. So every 417us we need to fire this interrupt and read the ADC. We know that at stock settings the analogRead function will take 100us so we need just need to do a simple assignment in the interrupt function itself, such as assignment and then read that function later.

I have found a library that will help with this for Arduino, TimerOne (link http://code.google.com/p/arduino-timero ... loads/list). Install that and then you can attach an interrupt function with a specified period and it will take care of it for you, as simple as (warning pseudo code follows!):
Code: Select all
int sensorRead = 0;

main()
{
attachInterrupt(function,417);
  if(sensorRead)
  {
    sensorRead(0);
    if (motorAmps < MAXAMPS)
      stopMotor();
  }
}
function ()
{
  sensorRead = 1;
  motorAmps = analogRead(pin);
}

As for lasertag, what is extreme range? People have current rigs which do 150M in daylight conditions. I am making a small system for my children and family to be played with mostly indoors.
By Mee_n_Mac
#140226
StaticDet5 wrote:I've hit a big stumbling block. I need to do more research today.
I've written code for a single shot. Essentially it waits until the amp sensor reads a low value (In this case, 5 amps), and then it stops the firing cycle and waits for the user to let off of the trigger.
The issue is that I'm consistently getting 3 round bursts.
It might be some sampling issue but I think it's more likely one of 2 other potential problems. I don't know what your test bed is ATM, whether you're using the switch from the new AEG or have the pot you want to use for the trigger. Let me cover both cases.

Case 1: You may have switch bounce problems. You wait until the cycle gets to some point and then turn off the FET. You then look to see the trigger released. Presumably if that all happens and then the trigger is pressed again, you'll fire again. So if the switch is still bouncing btw fire and not fire when you ask those questions you may well see trigger and then back on ... all due to 1 press and release. Switches bouncing for longer than 40 msec would not be unheard of. My suggestion, even if just to prove/disprove this hypothesis is that you add a delay between shutting off the FET and looking for the trigger state. Make is 80-90 msec to start and work it up from there. If bounce is the problem then at some point the frequency of your "slamfires" will decrease and then go away. If that happens we can figure out the best way to solve that problem.

Case 2: If you're reading the pot for trigger position, have you put enough hysteresis between your trigger pulled vs trigger off thresholds ? You need to have enough of a difference that noise (due to anything) won't cause the pot readings to do essentially the same thing as switch bounce. To see if that's the issue increase the difference btw the thresholds. I guess you could also send some trigger pot readings out the serial port to see what they look like as the motor runs.

Case 3: Maybe there's something goofy in your coding. Won't hurt you to post it so people can poke fun ... errr, help you out. :mrgreen:
By StaticDet5
#140229
Code! I knew I forgot something
This is the basic single shot code
Code: Select all
/*
  BasicAirsoftControlSingleShotFail2-23-2012
 

 
 The circuit:
 * LED on board at pin 13
 * Motor control on pin 3 (PWM pin) 
 * pushbutton attached to pin 7 from +5V
 * 10K resistor attached to pin 7 from ground
 * ADC on analog pin 0
 * ---------------Important-------------------
 * Hall Effect Sensor not used in this sketch!
 * Hall Effect Sensor on pin 8 (Honeywell SS451A)
   Hall Effect Vin (5v) has a 1k resistor to Hall Effect Output
 
 * Note: on most Arduinos there is already an LED on the board
 attached to pin 13.
 * 30amp Current Sensor between battery plug and MOSFET board.
 * Current sensor output wired to Analog 3
 

*/

const int buttonPin = 7;
const int ledPin = 13;
const int MotorPin = 3;
const int VoltageMain = 3;  //analog 0
const int Hall = 8;
const int CurrentSensor = 0;  //analog 3
const int MotorSpeed = 255;  //PWM value for motor, max 255
const float LowAmpTrigger = 10.0;  //Amp level signaling end of gearbox cycle
int VMain = 0;
float ADCRead =0;
int buttonState = 0;
int HallState = 0;
int IMain = 0;
float IRead = 0;
int DebounceDelay = 25;



void setup() {
  pinMode(MotorPin, OUTPUT);
  analogWrite(MotorPin, 0);
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  
}

void loop() {
  buttonState = digitalRead(buttonPin);
  
  if (buttonState == HIGH) {
    analogWrite(MotorPin, MotorSpeed);
    digitalWrite(ledPin, HIGH);
    do
    {
      
      IMain = analogRead(CurrentSensor);
      IRead = (IMain-511.5)*0.66;
      
    }
    while (IRead > LowAmpTrigger);
    
    if (IRead < LowAmpTrigger) {
      do
    {
      buttonState = digitalRead(buttonPin);
      //delay(50);
      analogWrite(MotorPin, 0);
      
    }
    while (buttonState == HIGH);
    }
    delay(DebounceDelay);
    digitalWrite(ledPin, LOW);
  }
  else {
      digitalWrite(ledPin, LOW);
      analogWrite(MotorPin, 0);
  }
}

   
   
  
    
  
The idea is to not use a timer.

The code checks to see if the button is pressed. It then triggers the motor control pin at full PWM (255) and turns on the LED.
Then enters a loop to repeatedly check the current sensor until the current sensor reads a "low condition", causing the system to turn off the motor, and causing it to exit the loop.
Then it enters a loop to check and see if the button is no longer pressed. Once the button is no longer pressed, the LED is turned off.

I don't think the initial button debounce is needed. The system won't exit the motor drive loop until the motor is in a low amp state.

I'm not using a pot on the trigger. Eventually I may incorporate that, but for now, I want a pure digital trigger.
By dkulinski
#140232
The motor will be in a low amp state in less than 41.7ms. If your button bounces past 40ms this could very well trigger a second shot. A bounce on the level of 90ms would be a total of 3 shots get fired.

Why is the idea to avoid timers?

Also, I may have missed this, but why are you driving the motor via PWM? Is the MOSFET a logic level gate? If so, use a digitalWrite. I do this on my high power LED on my lasertag board.
By StaticDet5
#140233
I want to drive the MOSFET with a PWM signal so that I can adjust the speed of the gearbox. This way, I can allow a slower rate of fire.

There are currently AEG computers out there that work using timers. I'm trying to drive the firing cycle based on the actual state of the gearbox, not the presumed state of the gearbox due to an arbitrary timer (your AEG will shoot with a higher round per second, with a fresh battery). If I use a timer to time how long to drive the motor, then it is eventually going to "skip a beat", or stop the gearbox during the compression cycle. Instead, if I use a current sensor to detect when the motor is done compressing the spring, it will ALWAYS stop the motor with the spring uncompressed (barring bizarre failure).
By dkulinski
#140234
Neat idea on controlling cycling rate via PWM, hadn't occurred to me.

I was suggesting using the timer to monitor the amperage sensor, not as a guess to the motor position. Right now you do analogRead once a loop cycle and a few analogWrite. These do pile up but not enough to make it miss a cycle.

On a fresh battery you mentioned getting a 24Hz cycle rate. This translates to 41.7ms per cycle. The timer scenario would force a read of the amp sensor 100 times per cycle on a fresh charge. A timer interrupt is a software feature, that when your timer reaches the set point (417 us in this case) it actually jumps out of the main loop, does an analogRead and then resumes the main loop where it left off. This way you are guaranteed (unless you do too much in the interrupt function) to get a reading at 2.4KHz.
By StaticDet5
#140237
But wouldn't it be better to continuously read the sensor and trigger the "break out" of the loop when it first reads a low amperage condition?
By StaticDet5
#140238
While I'm thinking about it:
Thank you all for the help. I was talking to a friend of mine today, and Just struck by the things I've learned from this project.
Thank you
By dkulinski
#140240
I understand what you are getting at. However, as you continue to grow the code base you are going to add more and more code to the main loop. Using such a tight loop will produce the effect you are looking for. However, here is something that could hurt you code. You aren't in that inner loop until the rest of your main loop finishes. If you keep adding features you may delay your loop even further.

Some tips to get a tight loop, forget about IRead when comparing the amperage to a low point. Transform LowAmpTrigger to something you can directly compare to IMain. This takes out the floating point math which is slow. Set LowAmpTrigger to 527 (10/0.66+511.5 and rounded up) and compare IMain against that.

Now you have one fast comparison vs 2 floating point calculations. If you need to convert to actual amps later on, you can always use Excel to scale it for you.
By Mee_n_Mac
#140251
StaticDet5 wrote:Code! I knew I forgot something
This is the basic single shot code.
I don't think what you have for code is guaranteed to do what I think you wanted it to do. :? I think you intended to see the current get "big" to indicate that the spring was being compressed (ie - the hammer has been cocked) and then see it get "low" to know that the piston had been released and that the sector gear was now disengaged. Look at the diagram below. Depending on your threshold, your software might be fooled by the motor turn on (inrush) transient. I have no idea how big this might be compared to the max spring compression current (so the diagram is not to scale) but it will exist if the motor is turned on after being off. There's also something gnawing at me elsewhere in your code but I've yet to pin it down.

FWIW : the switch bounce may (probably will) happen on both the press and the release of the trigger. Both cases need to be debounced in hardware or software or some combo thereof.
AEGmotorCurrent.jpg
You do not have the required permissions to view the files attached to this post.
By Mee_n_Mac
#140254
dkulinski wrote:Why is the idea to avoid timers?
I think the OP wants everything to be event driven. That said I forsee the use of some timers to do what I think he eventually intends to do. He'll need a "fast" and probably 16 bit timer to do the chronograph function. That will need to be done using ISRs as well. I think he'll want (though we've not discussed it so far) some form of "watch dog" timer(s) so if the code hangs (for any reason, perhaps a HW fault), eventually if finds it's way "home" and shuts off the firing (and perhaps displays an error code). Lastly perhaps one other timer instead of using delays for various functions, like a debounce delay. For the most part though I can see everything going from state to state and doing one tight loop per state, to make it work. This is a small enough project that I'd make up a simple flowchart to lay it all out.

BTW : Good catch on the floating point. Definitely no need to do that type of calcs for this project.
  • 1
  • 3
  • 4
  • 5
  • 6
  • 7
  • 32