Use Tone on 6 pwm pins interfere millis() and micros()

Introduction
[NOTE: I use Arduino Mega2560]
Firstly I was inspired by this project

He used 5 buzzers to play firefly. And Yeah it is so cool.
So I want to DIY this project but improved further by making individual buzzers volumable. (loudness control inside arduino code) . And play 6 buzzers at the same time.

For volume control. I use MCP41010 digital potentiometer and 2N3906 transistor.
MCP41010 to make current adjustable. and 2N3906 to make buzzer louder (bc MCP41010 will take most of the voltage/watt).
The equation to make buzzer's loudness stay at certain percentage required effort of calculation (bc Iāˆ1/R and IC=hFE*IB etc.,)
I use this ways bc I also want the sound to have damping. (approach to zero amplitude overtime).
(And no toneAC only works with one buzzer so I don't use it)

Here comes the problems part : 6 buzzers

Problems
So I experiment whether I can use 6 buzzers or not by using this code.

#include <SPI.h>
#include "Tone.h"


void setup() {
  Serial.begin(9600);
  // put your setup code here, to run once:
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(8,OUTPUT);
  Tone toner[6];
  for(int i=0;i<6;++i){
    toner[i].begin(13-i);
    toner[i].play(map(i,0,6-1,NOTE_A5,NOTE_DS8));
  }
  SPI.begin();
  pinMode(22,OUTPUT);
  pinMode(24,OUTPUT);
  pinMode(26,OUTPUT);
  pinMode(28,OUTPUT);
  pinMode(30,OUTPUT);
  pinMode(32,OUTPUT);
  pinMode(2,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), test_int, FALLING);
}
void test_int(){
  Serial.println("pressed");
}

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

Suprisingly. I can.
The sounds of 6th buzzer is a bit shaky but that's duable.
The big problem is millis() and delay() inside loop() function.
Here are the weird behaviours.

0-2s since the program start
Serial.println(millis()); will display "1"
delay(1000); This line do nothing. It won't even delay
For 2 seconds the loop() run continuously.
However millis() will return 1 continuously too.

2s-inf since the program start
Serial.println(millis()); will display "1"
delay(1000); stuck there,freeze and never going anywhere
therefore loop() is not looping

I also tested with other code and found out while-loop work well. The millis() itself doesn't.

If I only use 5 buzzers (by modifying the for-loop and its content). The millis()/micros()/delay() will works just fine. :person_shrugging:

Like delay() and millis() are essential to space duration between note too.
The push button that connect to pin 2 works fine.. Meaning attachInterrupt works fine.

Questions

  1. What is the reason behind this? Why using more than 5 buzzers stop timer counter?
  2. What is the solution to make delay work again?
  3. What is the work around?
  4. Do I have to use NE555 timer combine with attachInterrupt() as a workaround? If I really want to use 6 buzzers :laughing:

You should not use the serial port operations inside an interrupt service routine.

Umm Right...? But it's for testing.

I extract a paragraph from the tutorial I passed to you:

Warning: Since interrupts are disabled inside an ISR, and since the latest version of the Arduino IDE uses interrupts for Serial reading and writing, and also for incrementing the counter used by "millis" and "delay" you should not attempt to use those functions inside an ISR. To put it another way:

  • Don't attempt to delay, eg: delay (100);
  • You can get the time from a call to millis, however it won't increment, so don't attempt to delay by waiting for it to increase.
  • Don't do serial prints (eg. Serial.println ("ISR entered"); )
  • Don't try to do serial reading.

You understand?

1 Like

I'm trying to. But let me ask more question.
Here is the code before interrupt added btw.

#include <SPI.h>
#include "Tone.h"


void setup() {
  Serial.begin(9600);
  // put your setup code here, to run once:
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(8,OUTPUT);
  Tone toner[6];
  for(int i=0;i<6;++i){
    toner[i].begin(13-i);
    toner[i].play(map(i,0,6-1,NOTE_A5,NOTE_DS8));
  }
  SPI.begin();
  pinMode(22,OUTPUT);
  pinMode(24,OUTPUT);
  pinMode(26,OUTPUT);
  pinMode(28,OUTPUT);
  pinMode(30,OUTPUT);
  pinMode(32,OUTPUT);
}

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

Weird behaviours still remain the same.
So should I remove Serial.println() inside loop() too? That doesn't make sense a lil bit. It's not even ISR.
And according to your statement. Does SPI.begin() interfere timer counting too?
But this whole thing worked with 5 buzzers beforehand.

This is from the github page for the Tone library https://github.com/bhagman/Tone

The atmega2560 is not listed, it has six timers, so the 6th tone is taking over Timer0 used for the millis() counter.

1 Like

Hmm That making sense.
But wait does it means pin 13 can use any timer?
Doesn't arduino supposed to link the pin to certain timer?
Or am I tripping?

That is only for the PWM output from the timers, the Tone library uses timer compare interrupts to drive the output pins, so can use any available pin.

1 Like

So 5 usable timers for mega therefore we can safely use 5 buzzers at max.
Noted.

Do microcontroller with timers more than 6 existsed tho? :laughing:

Try this guy's library using PWM to adjust volume and tones.

1 Like
1 Like

Thank you all, for providing such essential informations :smiley:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.