Interrupt vs. Delay - who wins?


#7

Thanks!

The ignition delay is between 1 to 3 seconds (depending on the pump used)

I was thinking about using timers (I’d need quite a few given the repeating)

When I’m running this function - I’m blocking all other functions and looped code from running by using some logic checks:

if (pumpStarting == 0)
{
//Do all other code
}
else
{
//Do nothing
}

Maybe I should swap delay() for delaymillis() that way I can maintain the connection.
I’m also using SYSTEM_THREAD(ENABLED) to help maintain the connection.

Am I right to say, delay only stops user code - but with SYSTEM_THREAD(ENABLED) my other connection and background tasks keep running in the other thread?


#8

You can use one, resetting and re-triggering it.

Yes, but threads can step on each other… using Timers would let you get your pump intervals in the interrupt queue.

@Cameron would only writing to a pin and resetting a timer, which he may easily make happen within the ISR.


#9

I was thinking that… but the logic I have was

  • I’m in a function outside of loop() and don’t want the loop() to continue
  • I don’t want the function to go to the next line of the function
  • I’m using SYSTEM_THREAD so the delay() wont impact the connection management (This is an assumption) - but looks like @BulldogLowell has confirmed this is the case :slight_smile:

#10

Thanks - I’ll have a look into leveraging a timer.

I’m still a little unclear what I gain from a timer vs. delay if I have system_thread(enabled) set to enabled and don’t want any loop(), interrupts or other code running in the mean time?

The only thing - I think if a SMS is received by the unit during the ‘delay’ event… I’m not sure what happens to the SMS hummm


#11

It may be better to keep loop() looping, managing the events with a state machine-like approach.

If you have the skills, you you may want to try to create a little class that inherits from Timer that includes at least two member functions. 1) starts the pump start sequence and 2) returns true if the sequence is active.

That’s one approach, but I’m spiraling out here… sorry.


#12

exactly… Asynchronous events are what makes a State Machine approach so simple and so popular.


#13

I think I just realised, I do want to block SMSs during that time…

If I receive a spam SMS during that time, I don’t want it interrupting the starting or shutdown sequence… that wouldn’t be ideal.

I think I’ll just force the user to assume a lack of SMS confirmation means their action wasn’t received. So I don’t have to ‘worry’ about that problem… fweph!.

Thanks for your help! I think we are okay with timers or delays, as long as the system_thread(enabled) mode enables the connection to be maintained :slight_smile:


#14

PS: Ideally… we’d get a damn pump that started off a single digital HIGH signal… sigh…


#15

Latching relay?


#16

Really? don’t you just want to handle them? if it is ‘spam’ you must know how to determine that.

No news is bad news? what if it isn’t working at all… no news is worse news!

Handle the SMS, return “I’m busy” if it is busy.

What you want to do isn’t that hard to do!


#17

1 second might seem short but its an age to a microcontroller switching threads every 1ms. Now if you were instead talking much smaller intervals, then you might need to be worried e.g the example shown here https://docs.particle.io/reference/firmware/photon/#single_threaded_block-
The only things you really need worry about are the synchronous calls that could cause blocking behaviour listed shortly before that example. But that’s easy enough, if the ignition sequence is in progress don’t call any Particle methods until it is done.


#18

Humm, I thought about this and wondered if that meant I needed to keep the latch state in memory - if we had power outages or used the bypass switch I’d end up with some complex logic… the alternative being I guess to use another relay to reset the latching - but that increases the pin count (already running low) and also makes it really hard for the onsite installer/electrician to wire it up.

Unless I’m missing something about the latching relay ponders


#19

Oh you mean - I should avoid Particle.publish?


#20

That is a good point!

I was thinking, disregard the SMS’s in memory as they may contradict each other.
e.g. One user turns the pump on, another user tries, someone tries to turn it off, someone tries to check the state. I’d end up with scrambled up perspectives of ‘state’ of the pumping unit.

Maybe the answer lies in your suggestion of ‘i’m busy’ … argh - that brings me back to the complex world of trying to read SMS messages. I have another thread going for that one! haha


#21

Reading the SMS is not so bad and you can even create a priority list of senders to which your device responds.


#22

Latching things do indeed open up several other cans of worms when you need to verify their current state make sure they fail safe etc. Many types of latching relays circuits and FETs exist out there you could certainly have fun investigating them.


#23

Going back to @BulldogLowell’s comments, an FSM in loop() is the best way to control your pump. Loop will run every millisecond and with non-blocking delays (using millis()), a properly written FSM will step through the pump activities regardless of interrupts. Your ISR and callbacks need to be written short and non-blocking. If you have any concerns with SMS messages arriving asynchronously, queue them in a buffer (circular or otherwise), set a flag and deal with them synchronously in loop(). :wink:


#24

@Cameron,

I was “nerd sniped” too, thinking about your pump program driving my kids to school…

here is an example of your process using Timers in a class object. You can try it by sending it a “1” via Particle function. your function will return -99 if the process is underway…

Non-blocking and should be pretty accurate as to the dwell times.

have fun with it! :slight_smile: :smile: :laughing:

//#include "Particle.h"

class PumpManager
{
  public:
    PumpManager(const int &_pumpPin, const int &_numCycles, const int &_dwellTime);  // Pump Output Pin, Number of Startup Attempts, Pulse time in milliseconds
    void begin() const;
    bool startPump(void);
    bool isStarting(void) const;
  private:
    Timer* timer;
    enum State{
      PUMP_INACTIVE,
      PUMP_ACTIVE
    };
    State pumpState = PUMP_INACTIVE;
    int numCycles;
    int remainingCycles;
    int pumpPin;
    void callback(void);
};

PumpManager::PumpManager(const int &_pumpPin, const int &_numCycles, const int &_dwellTime)
{
  pumpPin = _pumpPin;
  numCycles = _numCycles;
  timer = new Timer(_dwellTime, &PumpManager::callback, *this);
}

void PumpManager::callback(void)
{
   if(pumpState == PUMP_INACTIVE)
    return;
   if(digitalRead(pumpPin) == LOW)
   {
     digitalWrite(pumpPin, HIGH);
   }
   else
   {
     digitalWrite(pumpPin, LOW);
     remainingCycles--;
   }
   if(!remainingCycles)
   {
     pumpState = PUMP_INACTIVE;
     remainingCycles = numCycles;
     timer->stop();
   }
}

void PumpManager::begin(void) const
{
  pinMode(pumpPin, OUTPUT);
  digitalWrite(pumpPin, LOW);
}

bool PumpManager::startPump(void)
{
  remainingCycles = numCycles;
  digitalWrite(pumpPin, HIGH);
  pumpState = PUMP_ACTIVE;
  timer->reset();
}

bool PumpManager::isStarting(void) const
{
  return (pumpState == PUMP_ACTIVE);
}

/***********************************************/
/*************** Main Program ******************/
/***********************************************/

PumpManager pump1(D7, 3, 1000);  // Pump Output Pin, Number of Startup Attempts, Pulse time in milliseconds

bool isStarting;

void setup(void)
{
  Particle.function("RunPump", runPump);
  Particle.variable("PumpStarting", isStarting);
  pump1.begin();
}

void loop(void)
{
  isStarting = pump1.isStarting();
  // waiting for Sleep Dragons...
  // I'm free, to do what I want, any old time...
}
int runPump(String command)
{
  if(command.toInt() == 1)
  {
    if(pump1.isStarting())
      return -99; // rejected the command
    pump1.startPump();  // otherwise go ahead and start the pump
    return 1;
  }
  return 0;
}

you could easily add functions to allow the setting of your pump times and attempts…

Keywords: Timer class member function callback non-blocking


#25

Thank you! @BulldogLowell & @peekay123 - it is a bit late here, I’m going to try and wrap my head around this tomorrow and start testing. :smiley:

Will let you know how I go!


#26

I must admit, I will have to claim a lack of knowledge here - how does that work? Do you have a link or example that might be able to point me in the right direction? Is that similar to Bulldog’s code above?