2

Is it possible to delay a task inside a function that is called only once (meaning, it's not inside the loop) without using delay()?

This library I'm using, for reading sensor measurements via serial has a delay() in it. I want to get rid of it, but I don't how to do it, because the library's function is not looped.

I guess the simple solution would be to move the library functions inside the loop, so I can do a simple check with millis() and run the delayed part as soon as 250 ms have passed. I would like to improve this library though and do a simple, single call for sensor data that doesn't not hold up the entire program.

This is the function with the delay:

uint16_t COZIR::Request(char* s)
{
  Command(s);
  // empty buffer
  buffer[0] = '\0';
  // read answer; there may be a 100ms delay!
  // TODO: PROPER TIMEOUT CODE.
  delay(250);  
  int idx = 0;
  while(CZR_Serial.available())
  {
    buffer[idx++] = CZR_Serial.read();
  }
  buffer[idx] = '\0';
  uint16_t rv = 0;

  switch(buffer[1])
  {
    case 'T' :
            rv = atoi(&buffer[5]);
            if (buffer[4] == 1) rv += 1000;
            break;
    default :
            rv = atoi(&buffer[2]);
            break;
  }
  return rv;
}

I've had a look at the SoftTimer library, but it requires sketches to be written very differently (without a loop). I'm also not sure if I can even pass arguments to delayed tasks with that library.

  • 1
    The first question is: why do you want to eliminate that delay call? Does it harm your program? – jfpoilpret Feb 23 '15 at 11:56
  • Split the function into a startMeasurement and readMeasurement method. Maybe add a measurementReady method for checking if the measurement is done. – Gerben Feb 23 '15 at 13:51
  • The sensor doesn't return data immediately and I use a pulsating LED to indicate certain levels. The delay disrupts the LED's smooth transitions. – kslstn Feb 23 '15 at 21:01
4

You could split Request() into two separate functions: You call sendRequest(), then go do other stuff, then, some time later, you call getResponse(). If you get RESPONSE_NOT_READY, then you know you have to call it later:

#define REQ_WAIT_DELAY     250  // Wait for 250 ms
#define RESPONSE_NOT_READY ((uint16_t) -1)

uint32_t time_request_sent;

void COZIR::sendRequest(char* s)
{
  Command(s);
  time_request_sent = millis();
}

uint16_t COZIR::getResponse()
{
  if (millis() - time_request_sent < REQ_WAIT_DELAY)
      return RESPONSE_NOT_READY;
  buffer[0] = '\0';  // empty buffer
  int idx = 0;
  // From here, same as original COZIR::Request()
}

This assumes that RESPONSE_NOT_READY (0xffff) is not an otherwise valid response. If all 2^16 values of an uint16_t may be valid, then you need another way for the library to signal that the response is not ready. Also, make sure you never compare timestamps, because of the millis() rollover problem. Taking the difference between two timestamps and comparing this with a specified delay (as in the example above) is rollover-safe.

Update: Here is an example of how this could be used:

COZIR cozir(some_serial_connection);

void loop()
{
    static bool waiting_for_a_cozir_response = false;

    if (time_to_get_another_cozir_reading()) {
        if (waiting_for_a_cozir_response) {
            report_something_is_wrong("This COZIR is too slow!");
        }
        else {
            cozir.sendRequest("Hi Cozir! Pliz send me another reading.");
            waiting_for_a_cozir_response = true;
            /* We won't get the response on this iteration. *
             * We will ask next time.                       */
        }
    }
    else if (waiting_for_a_cozir_response) {
        uint16_t response = cozir.getResponse();
        if (response == RESPONSE_NOT_READY) {
            /* Nothing to do now, really. We will just try again *
             * at the next loop() iteration.                     */
        }
        else {
            waiting_for_a_cozir_response = false;
            do_something_with_the_cozir_response(response);
        }
    }
    do_whatever_else_has_to_be_done_in_the_loop();
}
  • Thanks, but I'm not sure if I completely understand. In the example getResponse() is not called. I could put a call in the loop, but I'd prefer not to. I tried the code as proposed and it compiles, but now I don't get sensor measurements returned any longer with czr.Celsius(); – kslstn Feb 23 '15 at 18:27
  • 1
    Yes, you have have to call getResponse() in the loop, see updated answer. Why do you want to avoid it? Here is the deal: You send a request to the Cozir, but it is slow to respond. You have three choices: 1) you just wait: that's the cozir lib approach 2) yo go do your business, but you remember you have to come later see whether the answer is ready: that's the code I gave you 3) you go do your business, and have something interrupt you and tell you it's time to get the response: that's the SoftTimer approach. Now it's time for you to make a choice: which approach do you prefer? – Edgar Bonet Feb 24 '15 at 9:28
  • Thanks so much for the clarification, this was exactly what I was wondering about, as I didn't know the options. I will probably go with your second option with the code you provided. That way I don't have to rewrite the sketch completely. For a next project I will probably use SoftTimer though. – kslstn Feb 24 '15 at 14:15
0

You can look at the BlinkWithoutDelay example sketch (under Digital). Basically you store the current millis as a variable, then have an if statement to check that it has been more than x milliseconds.

  • I would still have to put that inside the loop. – kslstn Feb 23 '15 at 21:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.