Bizzare delay() issues - related to Tone.h library?

I've got some fairly simple code (although not so simple since i've been debuging), that uses the Tone.h library to turn a IR LED on and off to a set sequence.

I've run into a huge issue: delay() is not working. Like, it simply does not have any effect at all. I can workaround using delayMicroseconds(), but it gets messy as that doesn't work above 32000 us, and I have a 160ms delay!

code is here:

Blockquote

#include <Tone.h>
#include <Arduino.h>

const int IRLED = 3; //13;
const unsigned int F = 38000 ;
const int nWalk[] = {4, -5, 1, -4, 1, -4, 1, -4, 1, -40, -40, -40, -40};
const int nWalkSize = 13;
const int nRoar[] = {4, -5, 1, -4, 1, -4, 4, -1, 1, -30, -31, -32, -33}; // *4
const int nRoarSize = 13;

//timing
long previousMillis = 0; 
long currentMillis = 0; 
Tone tone1;



void setup() {
  // put your setup code here, to run once:;
    //tone1.begin(IRLED);
    pinMode(3, OUTPUT);
    Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

    roar();
    Serial.println("Pause");
    delay(5000);

}

void IRsend(int seq[], int lengthSeq){
  Serial.print("IRSend length: ");
  Serial.println(lengthSeq);
  for (int x = 0; x < lengthSeq; x++) {
    if (seq[x] > 0) {  
      previousMillis = millis();
      tone1.play(F, seq[x]);
      Serial.println(previousMillis / 1000);
      Serial.print("on: ");
      Serial.println(seq[x]);
      int endMillis = (seq[x] * 1000);
      currentMillis = millis();
      while (currentMillis - previousMillis < endMillis){
        delayMicroseconds(20);
        currentMillis = millis();
      }
    }
    else {
      previousMillis = millis();
      Serial.println(previousMillis / 1000);
      Serial.print("off: ");
      int delayTime = abs(seq[x]);
      Serial.println(delayTime);
//      int endMillis = (delayTime * 1000);
//      while (currentMillis - previousMillis < endMillis){
//        delayMicroseconds(20);
//        currentMillis = millis();}
      delay(delayTime);
      
      }
  }
  
}



void walk() {
  IRsend(nWalk, nWalkSize);
}

void roar() {
  Serial.println("Roar");
  //repeat 4 times
  for (int y = 0; y < 4 ; y++){
   IRsend(nRoar, nRoarSize);
  }
}

out put is here:

Roar
14:02:44.270 -> IRSend length: 13
14:02:44.270 -> 0
14:02:44.318 -> on: 4
14:02:44.318 -> 4
14:02:44.318 -> off: 5
14:02:44.318 -> 4
14:02:44.364 -> on: 1
14:02:44.364 -> 5
14:02:44.364 -> off: 4
14:02:44.364 -> 5
14:02:44.364 -> on: 1
14:02:44.364 -> 6
14:02:44.364 -> off: 4
14:02:44.364 -> 6
14:02:44.364 -> on: 4
14:02:44.457 -> 10
14:02:44.457 -> off: 1
14:02:44.457 -> 10
14:02:44.457 -> on: 1
14:02:44.457 -> 11
14:02:44.457 -> off: 30
14:02:44.457 -> 11
14:02:44.457 -> off: 31
14:02:44.504 -> 11
14:02:44.504 -> off: 32
14:02:44.504 -> 11
14:02:44.504 -> off: 33
14:02:44.504 -> IRSend length: 13

if i duplicate the delayMillis() code from the 'on' part, then i see delays working properly, up until i hit 33ms delays.

The delay in the main loop isn't working either.

EDIT: I'm using a chinese nano clone, that needs 32 bit usb to serial drivers, but delay() works fine in a simple script that is not using the Tone.h library (standard one in Arduino ref, from here: GitHub - bhagman/Tone: A Wiring Library to produce square wave tones on arbitrary pins.)
So i'm assuming it is an issue with that library now.

Make these ‘type‘ unsigned long.

Which Arduino?

Make these ‘type‘ unsigned long .

that part is working, and unrelated to delay() not working

sorry, forgot, nano

Which nano? There are several.

For how long ? :wink:

lol. delayMicroseconds is only rated to 16,000 us anyway, according to the documents. signed long is good to 2,147,483,647 so I think i can safetly exclude that.

Which nano? There are several.

Some chinese clone. this code works:

void setup() {
  // put your setup code here, to run once:
 Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println(millis());
  delay(1000);
}

So i'm assuming the Tone.h library is causing the problem

ok, changed my current and previousMIllis to unsigned long, and now that part stopped working too! Thinking about it, of course it would as I do math to end up with a -ve number, so would have to recast that on the fly.

EDIT: from Arduino Nano Timers - Stack Overflow

I learnt that delay() eventually is using the Timer0, and delayMicroseconds() is using a software timer. Tone.h is supposed to use Timer2, but maybe there's an issue with nanos?

mine is a ATmega328, @jremington , sorry that I can't tell you more.

On the ATmega-based Arduinos, if the interrupts are disabled, then delay() malfunctions and exits after an unnoticeable pause.

If the tone library is disabling the interrupts, either turn them back on with the command interrupts(), or don't use the tone library.

I just changed F to 3800 and it works different.

Reading your code until my eyes hurt doesn't show why this should be.

And I multiplied the delay parameter by 1000 as seemed to make sense in this section

      Serial.print("off: ");
      int delayTime = abs(seq[x]);
      Serial.println(delayTime);
//      int endMillis = (delayTime * 1000);
//      while (currentMillis - previousMillis < endMillis){
//        delayMicroseconds(20);
//        currentMillis = millis();}
      delay(delayTime * 1000);  // wasn't...

And it operated sorta like it looks like it should.

I have no idea what this means, and now my head hurts too.

Edit: with F at 3800, I thought I've give a listen, noticed this

//tone1.begin(IRLED);

whilst looking for the pin with the tone on it.

Go to bed. After uncommenting the tone1.begin, I set F back to 38000 and

it operated sorta like it looks like it should.

a7

Thanks.

I've tried coding this in a few different ways now, including as a state machine, and i've come to conclusion that the nano simply isn't fast enough to keep up with the millisecond timings of the IR signal i'm trying to send. Sometimes just a single loop is taking longer than i've got available for my signal bits 29 - 30ms for the state machine i'm testing at the moment.

So I need to find another method. might have to look into the IR libraries.

SMH

a7

Note that the Tone library is asynchronous. When you call Tone.play(note, duration) it will start the tone and return. If you want to wait until the tone has finished you have to check Tone.isPlaying() until it goes false.

I think your problem is that the 38 kHz carrier is playing while you are in a delay() and thus your 'marks' (carrier on) are playing over your 'spaces' (carrier off).

You could try the built-in tone() function which waits for the duration to complete before returning.

Are you sure you want your pulse lengths in milliseconds? The old IRremote library used 50-microsecond intervals.

I think this should play the IR codes the way you want:

const int IRLED = 3;
const unsigned int F_CARRIER = 38000;

const int nWalk[] = {4, -5, 1, -4, 1, -4, 1, -4, 1, -40, -40, -40, -40};
const int nWalkSize = 13;

const int nRoar[] = {4, -5, 1, -4, 1, -4, 4, -1, 1, -30, -31, -32, -33}; // *4
const int nRoarSize = 13;

void setup()
{
  // put your setup code here, to run once:;
  Serial.begin(115200);
  delay(200);

  pinMode(IRLED, OUTPUT);
}

void loop()
{
  // put your main code here, to run repeatedly:
  roar();
  delay(5000);
}

void IRsend(const int seq[], int lengthSeq)
{
  Serial.print("IRSend length: ");
  Serial.println(lengthSeq);
  
  for (int x = 0; x < lengthSeq; x++)
  {
    int duration = seq[x];
    
    if (duration > 0)
    {
      tone(IRLED, F_CARRIER, duration);
    }
    else
    {
      delay(-duration);
    }
  }
}

void walk()
{
  IRsend(nWalk, nWalkSize);
}

void roar()
{
  Serial.println("Roar");
  //repeat 4 times
  for (int y = 0; y < 4 ; y++)
  {
    IRsend(nRoar, nRoarSize);
  }
}

Hi.

Thanks for your input. I did know Tone was non-blocking, and initially had code like yours. Delay(), however, doesn't work if you use the tone library. It literally does nothing. So i put in while loops testing against a millis() timer with a delayMicroseconds() within them, which did provide the required timings.

However, at the 4-5ms level, i found great fluctuations in the produced signal, with somewhere between 1 and 30ms extra delays sneaking in.

I've learnt that background interrupts might be causing the issues, and the adafruit IR example codes turns them off (and then back on again) when doing the 'on' signal part.

using their method still didn't work, so i;m now going to be hooking things up to an oscilloscope to observe what is going on.

and yes, the timings are correct.

I don't know why delay() won't work for you but it works for me.

#include <Tone.h>

const float SEMITONE = pow(2.0, 1.0 / 12.0);
const uint8_t MIDI_NOTE_CONCERT_A = 69;
const float PITCH_CONCERT_A = 440.00;

#define MIDI_FREQUENCY(NoteNum) (PITCH_CONCERT_A * pow(SEMITONE, (float)((NoteNum) - MIDI_NOTE_CONCERT_A)))
#define MIDI_FREQUENCY_INT(NoteNum) ((uint16_t)(MIDI_FREQUENCY(NoteNum)+0.5))

// Chromatic scale with a whole rest in the middle
unsigned melody[] = {60, 62, 64, 65, 0, 67, 69, 71 };

// note durations: 4 = quarter note, 8 = eighth note, etc.:
byte noteDurations[] = {4, 4, 4, 4, 1, 4, 4, 4};

Tone T;

void setup()
{
  T.begin(2);

  for (unsigned i = 0; i < sizeof melody / sizeof melody[0]; i++)
  {
    byte midi_note = melody[i];
    int duration = 1000 / noteDurations[i];

    if (midi_note != 0)
    {
      T.play(MIDI_FREQUENCY_INT(midi_note), duration);
      while (T.isPlaying()) ; // Wait for tone to finish
    }
    else
      delay(duration);
  }
}

void loop() {}

Which tone or tone library are you talking about?

I made your code work. delay() works in your code if you hadn’t commented out the call to begin for the tone object, at least late last night or so. Read post #11.

You had all your delays cranked up by three orders of magnitude for observation and debugging as far as I could tell. I watched your sequences play out in super slow motion using delay.

I thought you gave up because it was too slow when it tried to go fast, so to speak.

Because you called begin for you tone object...

a7

Tone.h the one that you get from following the links in the tone.h arduino documentation.

It says in the middle of the readme that Tone.h breaks delay(),

" * Also, although it's the last timer to be allocated, timer 0 (which is used for millis() among other things) will be affected if used."

I'm using a nano, (AT328)

what kind of device are you testing on? It looks like my issues are related to the Timers

I've been writing this code in a number of different ways, and yes, the code i posted had the Tone declaration commented out, but only because I'd just swapped the code back from the tone.h library and the tone() call before posting it, and missed that bit. In my testing delay() does nothing once I have an active Tone.begin(), on a AT328 nano

I was using the UNO.

I just tried it on a Nano and... it worked fine there.

# include <Tone.h>

Tone myTone;

void setup() {
  Serial.begin(115200);

  myTone.begin(7);
}

void loop() {

  Serial.print(millis()); Serial.println(" at start of tone");

  myTone.play(1000, 3000);	// non-blocking tone 1000 Hz for 3 seconds

  Serial.print(millis()); Serial.println(" tone is toning, now delay...");

  delay(777);

  Serial.print(millis()); Serial.println(" is this 777 milliseconds later?");

  while (myTone.isPlaying())	// block
    ;

  Serial.print(millis()); Serial.println(" tone is finished");

  while (1);
}

and the serial monitor sez

0 at start of tone
0 tone is toning, now delay...
777 is this 777 milliseconds later?
2999 tone is finished

This is too strange. Imma sleep on this.

a7

The phrase "last timer to be allocated" means that you should not have trouble with delay() unless you use more than two Tone objects. The UNO and Nano have three timers.

1 Like