Help with double interrupts on cannon trigger feedback

Hey all,

I have been making a code for triggering an RC tank cannon. The cannon works by running a motor that loads a spring and as that spring is unloaded and fires a BB, a switch closes for a fraction of time.

The detection is done via an interrupt pin. At the same time there are a few states in which the cannon can operate. Detection of the interrupt is now only in the FIRING state as there the motor runs. In the READY state a fire command can be given, in which the attachInterrupt is set.

The issue I have is that after a succesful firing, the motor stops after receiving a valid feedback. If I then give a second fire command (after a minute or two), the state goes to FIRING, but receives an immediate interrupt with barely any movement of the motor. The command after is succesful again.

The second interrupt does not happen with a contact being made of the feedback circuit, so I expect it is in the way I have the code set up.

I have been making use of threading, but this is with other code that I want to add in the future.
Does someone have an idea where the issue might be?

/ ********************************************************************************
   DESCRIPTION:
   This is the test code for testing the Fire control function.
   The firing of turret is controlled via a state machine.
   The feedback of firing is received via resistance to ground switch circuit.
   An interrupt on the feedback trigger is used for determining fired status.

   CONNECTIONS (Arduino nano v3 - Atmega328P, old bootloader):

   Arduino 13 - Digital output fire motor control - ULN2001AN/(4-6B) via ..Ω  
   Arduino 02 - Digital input fired feedback (on interrupt pin)
 *******************************************************************************/
// Libraries
#include <Thread.h>
#include <ThreadController.h>

// Define pins
const int fireMotorPin = 13;    // Darlington transistor controlled via D13
const int fireFeedbackPin = 2;  // Feedback pin on interrupt pin

// Threads
ThreadController master = ThreadController();   // ThreadController
Thread* mainThread = new Thread();              // Thread as a pointer (longest time)
Thread cannonThread = Thread();

// Finite State Machine (FSM)
enum FireState {
  IDLE,
  READY,
  FIRING,
  FIRED
};
FireState state = FireState::IDLE;  // Initial state FSM

// Parameters
bool enableCannon;
bool fireCommand;
volatile boolean fireFeedbackReceived = false;


// Keyboard assignment for fire control
#define ENABLE_CMD 'E'
#define FIRE_CMD 'F'
#define STOP 'S'


// Initialisation
void setup() {
  Serial.begin(9600);

    // pinout Fire control
  pinMode(fireMotorPin, OUTPUT);
  pinMode(fireFeedbackPin, INPUT_PULLUP);
  enableCannon = false;
  fireCommand = false;
  digitalWrite(fireMotorPin, LOW);
  
  // Start Threads
  mainThread->onRun(keyboard_ctrl);
  mainThread->setInterval(500);
  //
  cannonThread.onRun(fire_ctrl);
  cannonThread.setInterval(20);
  //
  master.add(mainThread);
  master.add(&cannonThread);  // & to pass the pointer to it
}

// Functions
void fireFeedbackInterrupt() {
  // Detach interrupt routine
  detachInterrupt(digitalPinToInterrupt(fireFeedbackPin));
  fireFeedbackReceived = true;
  Serial.println("interrupt");
}

// Function to control Fire motor based on FSM
void fire_ctrl() {
  // State switching
  switch (state) {
    case FireState::IDLE:
      // transition to READY if cannon enabled
      // Serial.println("IDLE");
      if (fireCommand) {
        fireCommand = false;
        }
      if (enableCannon) {
        state = FireState::READY;
      }
      break;

    case FireState::READY:
      // Listen for fire command
      // Serial.println("READY");
      if (fireCommand) {
        fireCommand = false;
        // Attach interrupt routine
        attachInterrupt(digitalPinToInterrupt(fireFeedbackPin), fireFeedbackInterrupt, FALLING);
        state = FireState::FIRING;
        }
      if (!enableCannon) {
        state = FireState::IDLE;    // Transition to IDLE when safety is not enabled
        }
      break;

    case FireState::FIRING:
      Serial.println("FIRING");
      digitalWrite(fireMotorPin, HIGH);
      if(fireFeedbackReceived){
        state = FireState::FIRED;
        };
      break;

    case FireState::FIRED:
      digitalWrite(fireMotorPin, LOW);
      Serial.println("FIRED");
      fireFeedbackReceived = false;     
      // After firing back to ready
      state = FireState::READY;
      break;    
  }
}

void keyboard_ctrl(){
  char byte = 0;
  // press z to cancel and exit
  Serial.readBytes(&byte, 1);

  // FIRE CONTROL VIA KEYBOARD CONTROL  
  // -----------------------------------
  // press S to ENABLE SAFETY cannon
  if (byte == STOP) {
    //Enable Safety for cannon
    enableCannon = false;
    Serial.print("Command: SAFETY ON \n");
  }

  // press E for DISABLE SAFETY cannon
  if (byte == ENABLE_CMD) {
    //Disable Safety for cannon
    enableCannon = true;
    Serial.print("Command: SAFETY OFF \n");
  }
  // press F for Firing command cannon
  if (byte == FIRE_CMD) {
    //Set Fire command
    Serial.print("Command: Firing \n");
    fireCommand = true; 
  }
  // press z for Exit
  if (byte == 'z'){
    Serial.print("Done \n");
    Serial.end();
  }
}
    
void loop() {
  master.run(); // run thread controller
}

attaching the interrupt is only done once inside setup()

I am no expert at this but attaching the interrupt always new might cause the problem.
minimum is attaching it always new is bad programming practice

Enganging some kind of thread framework makes everything more complicated than nescessary

You are already using a finite state machine that works non-blocking.
This means as long as all your code is written non-blocking you don't need any kind of extra threading-frame-work

And it might well be that because you are using the threading frame-work you have this effect of interrupt-flag beeing already set causing the immidiately reaction.

I don't think that you get an

You can check this by using a second microcontroller that stores

  • a first time-stamp in the moment the pyhsical switch triggers
  • a second time-stamp caused on switching an output-pin from LOW to HIGH on your canon-controller

second microcontroller
permanently reading in pyhsical switch
permanently reading in state of IO-pin that is switched in case of entering state FIRING

why?
why disable the interrupt when the ISR is invoked
why not simply check for the fireFeedbackPin being LOW in the READY state

and perhaps you should insure that the READY state is not enabled if the fireFeedbackPin is LOW

Thnx for the reply,
I'll have a go at it removing the Threads to see if this has an effect on the performance.

There is a reason that the detachInterrupt came in the code, because with attachInterrupt in the setup(), I was receiving an interrupt multiple times before getting out to the FIRED state. This looked like the ISR was resulting in a delay, which resulted in another problem, that was over turning the motor such that a fire command resulted in firing twice before the motor being stopped.

This was a bit of a bigger issue than a misfire. But I don't remember if this was also the case with or without threading.

If your code is consequently non-blocking polling your switch should be sufficient.
The other things is to use a boolean flag
once the interrupt occured set flag inside the interrupt service-routine (ISR)

The isr checks if the flag is already set. If flag IS already set do nothing

reset flag in your main-code at that place and at that time you want it to be reset

is this because of bounce?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.