Failsafe, a final try

Hi all :roll_eyes:

My submarine sketch is now complete other than this last issue that I can’t seem to get right at all.

Even with suggestions, nothing works.

So I have stripped down the sketch to a couple of bare elements and attached it. I have attached a video of the screen and the serial monitor, running the attached script. What it shows is the reading of ‚Äėunballastin.‚Äô It works normally and you can hear the relays clicking as it runs between 1200 and 1800. When I turn the Tx off, the input becomes completely random. It appears to be between 0 and 40000 whatever.

What I need, is to have some code that recognises the scramble, waits maybe 4 seconds (there is no hurry in a sub) to check this is an ongoing problem and not an minute glitch, and then clicks into failsafe mode. What I would like is the PISTON_SURFACE_PIN, to go LOW, or ‚Äėon.‚Äô

What is also needed is for the failsafe function to come off when the input is restored to normal.

If anyone can lend a hand I would be most grateful. I have spent days on this so far. A bottle of wine may be a possibility.

John

forgot to add sketch

substripped.ino (2.54 KB)

  PISTON_DIVE_PINtimer = millis ();

You are timing a pin?

    if (unBallastIn > 1800)
  {
    if ( (millis () - PISTON_DIVE_PINtimer) >= PISTON_DIVE_PINinterval)          
      digitalWrite(PISTON_DIVE_PIN, LOW);     
  }
  else if (unBallastIn < 1800)

If unBallastIn is 1800, do nothing. Is that reasonable? Why single out that one value to ignore?

    if(bUpdateFlags&BALLAST_FLAG)

Isyourspacekeybroken?

Why are you storing time information in ints? Time is LONG!

I can see you are right. It should be >= Will do.

Isyourspacekeybroken? Why are you storing time information in ints? Time is LONG!

No idea. This is code I have copied from somewhere. It all works though. If you think it would be better redone, pls give me an example.

John

Here’s something I just can’t fathom (excuse the pun :slight_smile: )

void loop()
{

  static uint16_t unBallastIn;
  static uint8_t bUpdateFlags;

  Serial.println(unBallastIn); 


  //PISTON PARAMETERS for realy action


    if (unBallastIn > 1800)
  {
    if ( (millis () - PISTON_DIVE_PINtimer) >= PISTON_DIVE_PINinterval)          
      digitalWrite(PISTON_DIVE_PIN, LOW);     
  }
  else if (unBallastIn < 1800)
  {
    digitalWrite(PISTON_DIVE_PIN, HIGH);
    PISTON_DIVE_PINtimer = millis ();  
  }


  if (unBallastIn < 1300)
  {
    if ( (millis () - PISTON_SURFACE_PINtimer) >= PISTON_SURFACE_PINinterval)    
      digitalWrite(PISTON_SURFACE_PIN, LOW);
  }
  else if (unBallastIn > 1300)
  {
    digitalWrite(PISTON_SURFACE_PIN, HIGH);
    PISTON_SURFACE_PINtimer = millis ();  
  }

So you’re declaring a variable ( unBallastIn ) that’s local to this itteration of the loop function. You don’t initialise it, you don’t call anything to change it’s value, and yet the rest of this code is all dependent on it’s value.

HOW can this possibly work.

I don't know but it all works very nicely. Do you have any ideas about the failsafe??

The third line of your loop function. I notice reads Serial.println(unBallastIn); So what exactly do you see in the Serial monitor from that line? If there’s any value there (which is highly unlikely), it hasn’t come from any RC reciever.

With this in mind, if everything is working fine, you can remove the stuff that is now just confusing you. (ie the whole section that I mentioned above).

To come up with a failsafe we need some rational understanding of what is comming into the system from your RC. Currently it looks like it’s all working by smoke, mirrors and snake oil.

Edit: With a bit more scrutiny, I suspect the really important part (in as much as it may actually do something) is PCintPort::attachInterrupt but we’re not privy to that function. I daresay it’s in one of the include files that you have not given us.

Go back to my opening post and see the video. it shows the serial monitor in action

By the way.. you may not be aware..

I have a 36 MHZ Tx, transmitting to a corresponding Rx, one channel of which is connected to the Arduino. The reading in the serial monitor of, for example 'unballastin,' as is the usual case I believe with RC, between 1100 and 1900. (1500 neutral) I do not know why.

When I turn the transmitter off, the reading fluctuates wildly between 0-4000 at least.

What I want is code which recognises the transmitter when it is off, which can then in turn, turn on a failsafe function.

Your loop function starts

  static uint16_t unBallastIn;
  static uint8_t bUpdateFlags;

  Serial.println(unBallastIn);

So you are printing an undefined value. There's no way we're ever going to draw any conclusion as to what that should hold, let alone how it's going to change under different conditions.

Something that may be more useful would be if you were to change that println statement to Serial.println(ulBallastStart); Watch this value and you should see it rapidly escalate once you turn off your transmitter.

Hi

ulBallastStart is an ever increasing number, that increases more dramatically with the Tx off. Not sure how this helps

Ken… From what I read unBallastIn is updated by an interrupt in other code not shown here.

John… Try this. I would prefer to use State Machine library but didn’t want to cloud the task.
Have included some DEBUG code.

// include the pinchangeint library - see the links in the related topics section above for details
#include <PinChangeInt.h>
#include <Servo.h>

#define DEBUG              // Use fake data to debug ignore interrupt updates
#define BALLAST_IN_PIN 2

int PISTON_DIVE_PIN = 6;
int PISTON_SURFACE_PIN = 7;

const unsigned long PISTON_DIVE_PINinterval = 1000;
const unsigned long PISTON_SURFACE_PINinterval = 1000;

unsigned long PISTON_DIVE_PINtimer;
unsigned long PISTON_SURFACE_PINtimer;

Servo servoBallast;

#define BALLAST_FLAG 1

volatile uint8_t bUpdateFlagsShared;
volatile uint16_t unBallastInShared;
uint32_t ulBallastStart;

bool goodTXstate = true;
const int BAD_TX_HIGH_VALUE = 2000;       // Set these two to what would be considered bad data
const int BAD_TX_LOW_VALUE  = 1000;

      int iBadTXcount = 0;
const int BAD_TX_COUNT_THRESHOLD  = 10;  // Set these to what would be considered bad data count within ulBadTXtimeTolerated

unsigned long ulBadTXtimeTolerated = 4000; 
unsigned long ulBadTXtimeDetected  = 0;

#ifdef DEBUG
const int unBallastInFakeUpper =2100;
const int unBallastInFakeLower =950;
int incrementValue = 1;
#endif

  static uint16_t unBallastIn;
  static uint8_t bUpdateFlags;
  
void setup()
{

  Serial.begin(9600);

  pinMode(PISTON_DIVE_PIN, OUTPUT);
  pinMode(PISTON_SURFACE_PIN, OUTPUT);

  PISTON_DIVE_PINtimer = millis ();
  PISTON_SURFACE_PINtimer = millis ();

  PCintPort::attachInterrupt(BALLAST_IN_PIN, calcBallast,CHANGE);
  
  #ifdef DEBUG
  unBallastIn = unBallastInFakeLower;
  #endif


}




void loop()
{
 
 #ifdef DEBUG
 
 Serial.print("Bad count: ");
 Serial.println(iBadTXcount);
 
 
 unBallastIn += incrementValue;
 if (unBallastIn > unBallastInFakeUpper)
 {
  incrementValue = -1;
 }
 
 if (unBallastIn < unBallastInFakeLower)
 {
  incrementValue = 1;
 }
 
 delay(200);
 #endif
 
  Serial.println(unBallastIn); 


  //PISTON PARAMETERS for relay action


 if(goodTXstate == true && bTXDataIsBad())  // We have detected a single bad TX data value
 {
  goodTXstate = false;
  ulBadTXtimeDetected = millis();
  iBadTXcount = 0;
 }
 else if(goodTXstate == false && bTXDataIsBad()) // We now count the bad
 {
  iBadTXcount +=1;
  iBadTXcount = constrain(iBadTXcount,0,BAD_TX_COUNT_THRESHOLD);    // Stops incrementing while bad
  if(((millis() - ulBadTXtimeDetected) > ulBadTXtimeTolerated) && (iBadTXcount >= BAD_TX_COUNT_THRESHOLD))
  {
   digitalWrite(PISTON_SURFACE_PIN, LOW);
    #ifdef DEBUG
   Serial.println("+++++++++ Fail safe ++++++");
   #endif
   
   ulBadTXtimeDetected = millis();
  }
 }
 else if(goodTXstate == false && !bTXDataIsBad()) // We decrement the good
 {
  iBadTXcount -=1;
  iBadTXcount = constrain(iBadTXcount,0,BAD_TX_COUNT_THRESHOLD); 
  
  if(((millis() - ulBadTXtimeDetected) > ulBadTXtimeTolerated) && (iBadTXcount <= 0))
  {
   goodTXstate = true;
    #ifdef DEBUG
   Serial.println("===== End Fail safe ======");
   #endif
   // Will resume normal operation until more bad TX data detected
  }
 }
 else
 {
    if (unBallastIn > 1800)
    {
      if ( (millis () - PISTON_DIVE_PINtimer) >= PISTON_DIVE_PINinterval)          
      digitalWrite(PISTON_DIVE_PIN, LOW);     
      }
      else if (unBallastIn < 1800)
           {
           digitalWrite(PISTON_DIVE_PIN, HIGH);
           PISTON_DIVE_PINtimer = millis ();  
           }


  if (unBallastIn < 1300)
  {
    if ( (millis () - PISTON_SURFACE_PINtimer) >= PISTON_SURFACE_PINinterval)    
      digitalWrite(PISTON_SURFACE_PIN, LOW);
    }
    else if (unBallastIn > 1300)
    {
    digitalWrite(PISTON_SURFACE_PIN, HIGH);
    PISTON_SURFACE_PINtimer = millis ();  
    }

 }


#ifdef DEBUG
#else
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

      // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;    

    if(bUpdateFlags&BALLAST_FLAG)
    {
      unBallastIn = unBallastInShared;
    }



    bUpdateFlagsShared = 0;

    interrupts();   
  }
#endif


  if(bUpdateFlags&BALLAST_FLAG)
  {
    if(servoBallast.readMicroseconds() != unBallastIn)
    {
      servoBallast.writeMicroseconds(unBallastIn);
    }
  }


  bUpdateFlags = 0;
}

bool bTXDataIsBad()
{
 return unBallastIn >= BAD_TX_HIGH_VALUE || unBallastIn <= BAD_TX_LOW_VALUE;
}


// simple interrupt service routine
void calcBallast()
{
  // if the pin is high, its a rising edge of the signal pulse, so lets record its value
  if(digitalRead(BALLAST_IN_PIN) == HIGH)
  {
    ulBallastStart = micros();
  }
  else
  {

    unBallastInShared = (uint16_t)(micros() - ulBallastStart);
    // use set the throttle flag to indicate that a new throttle signal has been received
    bUpdateFlagsShared |= BALLAST_FLAG;
  }
}

George

When I tried this, and I appreciate you work here, it starts by triggering both piston dive and surface together but after about 12 seconds piston dive goes off, and after a minute or so the piston surface goes off.
The relays do not work from the Tx joystick at all.
The serial shows

Bad Count: (1 going up to 10 and remaining at 10)
An ever increasing number going up by 1
On first starting the words failsafe comes up after 12 seconds or so 3 or 4 times

Turning the TX off does not seem to do anything to the serial screen, or the relays

John

KenF:
Your loop function starts

  static uint16_t unBallastIn;

static uint8_t bUpdateFlags;

Serial.println(unBallastIn);




So you are printing an undefined value.

Nonsense, it's a static, so it is initialized to zero by crt0.

John... comment out DEBUG

// #define DEBUG

Johnredearth:
By the way.. you may not be aware..

If so this is your own fault for spreading the same project over several Threads instead of keeping it all in one Thread where everyone can see all of the information.

I had to look at 2 of your other Threads before I found where I had last contributed. I think I made a useful suggestion in that Thread but there has been no feedback from you. I can't really see why I should spend any more time thinking about your problem.

...R

Johnredearth:
ulBallastStart is an ever increasing number, that increases more dramatically with the Tx off. Not sure how this helps

would this be a clue

if ( (millis() - ulBallastStart) > reallyLargeNumber)
  executeFailsafe();

Obviously I don't know what the "reallyLargeNumber" is going to be because you need to start collecting information about what it gets to.

Checking if a value is above some preset threshold is a lot easier than trying to determine if a value is "fluctuating wildly".

My attempt is attached. It’s based on the latest copy I had of your code before the experiments with Serial so you’ll probably need to cut out the relevant bits and paste them into the up to date copy.

There are two functions that do the work - SignalIsOK and ProcessBadSignal. They keep track of bad signals and, as George’s version does, establish whether a threshold has been crossed in a time window. If it has, a check at the bottom of loop enables the surface piston.

I only applied the checking logic to the unBallastIn variable - you could apply it similarly for the other channels if necessary.

There are some arbitrary #defined constants that will likely need tweaking too.

It compiles, but of course I can’t test it.

UBoat2.ino (14.3 KB)

WildBill

Thanks for this. I have uploaded the code and everything still works as normal when the Tx is on. When it goes off it does nothing but there is no 'chattering,' or random calls on the relays as they are on the sketch without your additions.
But piston surface is not called.
Is there anything I should measure or can I tell you about?

John

First thing would be to put a serial.print statement in the ProcessBadSignal function to verify that it's being called. It sounds like the IsSignalOK bit is working if the relay chatter is gone.

Frankly, I'm not surprised that there's a bug - it's a bit more code than I really like to write without some intermediate testing. Sadly, my RC gear is a continent away.