multi tasking using Millis() help required

Hi, I am a novice with Arduino and programming in general and am slowly moving on to writing code that is a bit more complicated (for me anyway).

Basically this is what I am trying to do…

I have a serial device that will return the number of ‘events’ it has stored in it if I send it an = serial command. It will return a {=,nnnn} where the nnnn represents a decimal number from 0 to 9999.

I used some examples from the very helpful Nick Gammon and managed to cobble something that returns the number of events successfully using:

#include <Bounce.h>


typedef enum {  NONE, GOT_COMMA, GOT_CURLY } states;

// current state-machine state
states state = NONE;
// current partial number
unsigned int currentValue;

const unsigned int HASH_BUTTON = 30;
const unsigned int DEBOUNCE_DELAY = 20;



void setup ()
{
  Serial.begin (19200);
  Serial2.begin(19200);
  state = NONE;
  
  pinMode(HASH_BUTTON, INPUT); 
}  // end of setup

Bounce hash_button(HASH_BUTTON, DEBOUNCE_DELAY);

void process_events (const unsigned int value)
{
  // do something with RPM 
  Serial.print ("EVENTS = ");
  Serial.print (value);
} // end of processRPM

void process_curly (const unsigned int value)
{
  Serial.println();
}

void handlePreviousState ()
{
  switch (state)
  {
  case GOT_COMMA:
    process_events (currentValue);
    break;
  case GOT_CURLY:
    process_curly(currentValue);
    break;
  }  // end of switch  

  currentValue = 0; 
}  // end of handlePreviousState

void processIncomingByte (const byte c)
{
  if (isdigit (c))
  {
    currentValue *= 10;
    currentValue += c - '0';
  }  // end of digit
  else 
  {

    // The end of the number signals a state change
    handlePreviousState ();

    // set the new state, if we recognize it
    switch (c)
    {
    case ',':
      state = GOT_COMMA;
      break;
    case '}':
      state = GOT_CURLY;
      break;
    default:
      state = NONE;
      break;
    }  // end of switch on incoming byte
  } // end of not digit  
  
} // end of processIncomingByte

void loop ()
{
  if (Serial.available() > 0) 
  {
  int c = Serial.read();
  if (c == '=') 
  {
  Serial2.print("{=}");
  delay(20);
  }
  }
  count_events();
  handle_hash_button();
}  // end of loop

void count_events() {

if (Serial2.available ())
    processIncomingByte (Serial2.read ());
}


void handle_hash_button() {
  if(hash_button.update()) {
    if (hash_button.read() == HIGH) {
      Serial.println("Event Trigger");
      Serial2.print("{a}");
    }
  }
}

However, I want this to carry on in the ‘background’ every 1 second or so whilst I execute other code because the trigger is from an external button which is independent. I then want to keep an eye on the number of events so that I can carry out other things when the number of events reaches a certain number.

I have tried to do this but without any luck. I am unable to get the auto count code to update when I manually trigger the marker…

#include <Bounce.h>


typedef enum {  NONE, GOT_COMMA, GOT_CURLY } states;

// current state-machine state
states state = NONE;

unsigned int currentValue;

const unsigned int HASH_BUTTON = 30;
const unsigned int DEBOUNCE_DELAY = 20;

long previousMillis = 0;
long INTERVAL = 3000;

void setup ()
{
  Serial.begin (19200);
  Serial2.begin(19200);
  state = NONE;
  
  pinMode(HASH_BUTTON, INPUT); 
}  // end of setup

Bounce hash_button(HASH_BUTTON, DEBOUNCE_DELAY);

void process_events (const unsigned int value)
{
 
  Serial.print ("EVENTS = ");
  Serial.print (value);
} 

void process_curly (const unsigned int value)
{
  Serial.println();
}

void handlePreviousState ()
{
  switch (state)
  {
  case GOT_COMMA:
    process_events (currentValue);
    break;
  case GOT_CURLY:
    process_curly(currentValue);
    break;
  }  // end of switch  

  currentValue = 0; 
}  // end of handlePreviousState

void processIncomingByte (const byte c)
{
  if (isdigit (c))
  {
    currentValue *= 10;
    currentValue += c - '0';
  }  // end of digit
  else 
  {

  
    handlePreviousState ();

    // set the new state, if we recognize it
    switch (c)
    {
    case ',':
      state = GOT_COMMA;
      break;
    case '}':
      state = GOT_CURLY;
      break;
    default:
      state = NONE;
      break;
    }  // end of switch on incoming byte
  } // end of not digit  
  
} // end of processIncomingByte

void loop ()
{
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > INTERVAL) {
    previousMillis = currentMillis;
    count_events();
  }
  handle_hash_button();
}  // end of loop

void count_events() {

    Serial2.print("{=}");
    delay(20);  
if (Serial2.available ())
    processIncomingByte (Serial2.read ());
}
void handle_hash_button() {
  if(hash_button.update()) {
    if (hash_button.read() == HIGH) {
      Serial.println("Event Triggered");
      Serial2.print("{a}");
    }
  }
}

I am sure its a simple remedy but either my way of doing is incorrect or I am not thinking about the process properly…

I am sure its a simple remedy but either my way of doing is incorrect or I am not thinking about the process properly....

You did a nice job of tap dancing there, but the post boils down to "this doesn't work", without saying what it actually does, what you want it to do, or how those two things are different.

With or without millis(), the Arduino can not do multi-tasking. It can do things at semi-regular intervals, without blocking, so that it sort of appears to be doing multiple things at once, but it isn't really.

Well, it returns the number of events ok without any issues. Its just that if I activate the trigger input to update the number of events to the serial device I know its updated the number but when the arduino comes round to do the 'count events' its doesnt seem to fetch the updated number. I basically have to reset it to update and catch the correct number of events.

I know it cant do multiple things at the same time, but just need a bit of a pointer to slot things up nicely. I know I'm close...

Well, it returns the number of events ok without any issues. Its just that if I activate the trigger input to update the number of events to the serial device I know its updated the number but when the arduino comes round to do the 'count events' its doesnt seem to fetch the updated number. I basically have to reset it to update and catch the correct number of events.

I guess you have to have a stern talk with the function, and tell it to shape up. If you want to have a group intercession, you'll need to share your serial output, annotated to show us where you (or the mysterious device) does something.

but just need a bit of a pointer to slot things up nicely.

Here's one:

int *ptr;

8)

The stern talk did no good and I feel anything physical will do more harm to me (and my wallet)…

Basically the code allows me to push a button (connected to pin 30) which sends a {a} to the logger which increments the count in the event log. When I do this ‘Event Triggered’ is sent to the serial monitor.
Also running every 3 seconds or so is the event counter which displays the number of events in the logger.

Below is an output which starts at 103 events but as I press the button it keeps returning the same count.

EVENTS = 104
EVENTS = 104
EVENTS = 104
Event Triggered
EVENTS = 104
Event Triggered
Event Triggered
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 104
EVENTS = 0

EVENTS = 0EVENTS = 0EVENTS = 0
EVENTS = 0

If I leave it it eventually plays up and does that at the end…

any better?

Every three seconds, you ask it for the count of events, by sending it (whatever it is) “{=}”. Then, you wait 20 milliseconds for a reply. Then, you read one single character of that reply. I don’t see how that is going to work.

You need to separate the sending of the request for information from the reading of the response. Every three seconds, send the request. EVERY pass through loop, read the next character, if there is one. Even better would be to replace the if in count_events() with a while, so you read all pending data.

PaulS:

I am sure its a simple remedy but either my way of doing is incorrect or I am not thinking about the process properly....

You did a nice job of tap dancing there, but the post boils down to "this doesn't work", without saying what it actually does, what you want it to do, or how those two things are different.

With or without millis(), the Arduino can not do multi-tasking. It can do things at semi-regular intervals, without blocking, so that it sort of appears to be doing multiple things at once, but it isn't really.

It can be made to do a sort of "co-operative multi-tasking" - albeit one you have to program yourself, no OS to help out behind the scenes.

Looks like I managed to get it working by putting a carefully placed Serial2.flush() in one of the functions which has helped it catch the correct number this time. Only problem is that if an event is triggered during the background routine it misses it. However it catches up on the next iteration which I guess I can live with for now.

Another question then, do you thinks it possible to utilise the millis() function a second time and run another function that checks the status of another serial logger?

Another question then, do you thinks it possible to utilise the millis() function a second time and run another function that checks the status of another serial logger?

Of course. You can use millis() as many times as you want.

Thanks for the help PaulS, I seem to have just about got it to work..

I have another bit I am stuck on regarding loops and terminating a situation only once.

Like I mentioned earlier I am using the millis() function to monitor the status of a serial logger and count the number events stored in its memory by sending a {=} every few seconds. When it reaches a certain number of events (e.g. 99) I need to send a serial command through the other serial port on the Mega 2560 to a DVR to stop it recording, wait a while until the DVR is finished writing, then start the record again. This is to capture 99 events at a time in a separate recording on the DVR. In my code I used a simple if loop to send the command when the count equals 99 which works fine to stop, wait and restart the record, but it keeps repating obviously until the event account increases. This action is not under my control and until it changes the system stays in that dodgy state. How can I get the loop to only execute once for that condition and then carry on?

I have 2 millis() loops working, one is countin events and one is checking the status of the DVR (to see if its in record or not). I set a REC FLAG to 1 when its in the REC state and I use this along with the event_count number to initiate the STOP/REC sequence.

void check_limit() {

if (REC_FLAG == 1 && no_of_events == 99) {

Serial.println("Stopping REC"); Serial1.println(STOP); delay(1000); } }

I need this loop to execute only once when the number of eents reaches 99 and then jump to the main loop again and to ignore this even though the count may remain at 99 for a while after this.

Any ideas?

I need this loop to execute only once when the number of eents reaches 99 and then jump to the main loop again and to ignore this even though the count may remain at 99 for a while after this.

The simplest approach is to set no_of_events to 0 in the body of that if statement (or set REC_FLAG to something other than 1).