The Japanino, tone() and millis()

The tone() command lets us use an analog pin (A0 to A5) to play sounds to an attached speaker.  For the P.O.V. kit, we're told to connect the gray speaker wire to pin A0 and the black wire to Gnd, although, we could use any of the analog pins if we wanted to.  In any case, A0 is pin 14.

    tone(pin, frequency);   // or
    tone(pin, frequency, duration);

If you don't specify a duration, the tone will continue until you run tone() again, or until you use:

    noTone(pin);

You can only play one tone at a time per pin, so starting a new tone causes the old one to stop first.  Duration is given in milliseconds.

Example:

    tone(14, 1000, 100);  // 1 KHz squarewave on pin 14 (A0) for 0.1 seconds.


-------------

In the last page, regarding the while-statement, I talked about using the P.O.V. switch to turn the sound on and off.  This is actually quite simple.  First, we want to set the pin the switch is connected to (D6) as input, and then read it.  If it's a 0, then the switch is closed.

    //  Blink and Beep Sketch

    #define SWPIN  6
    #define SPKR  14
    boolean play_tone = 0;
    boolean tripped;
    unsigned long start_time;

    void setup() {
       pinMode(SWPIN, INPUT);     // Sets D6 as input
       digitalWrite(SWPIN, HIGH); // Uses the internal pull-up resistor so an open switch is 5V (a logical 1).
    }

    void loop() {
       if(play_tone) {
          tone(SPKR, 500, 100);  // 500 Hz tone on A0 for 0.1 second
       }
  
       tripped = 0;
       start_time = millis();

       while(millis() - start_time < 500) {
          if(digitalRead(SWPIN) == LOW && ! tripped) {    // If switch D6 closed, toggle play_tone.
             play_tone =! play_tone;
             tripped = 1;
          }
       }
    }

Again, the reason for using while() instead of delay() is that delay() prevents us from reading the input pins until the delay time has ended.  On the other hand, the while() statement lets us do whatever we want.  Which can cause us a different problem, and that is that we end up reading the input switch more than once, and end up turning sound on and off almost randomly.  That's why I put in the "tripped" variable.

What this program example does is:

1) Create constants for the D6 and A0 pins.
2) Creates some variables.
3) Runs set up, initializing D6 as an input pin.
4) Checks if play_tone is 1.  If so, plays the tone.
5) Sets tripped to FALSE and start_time to the current time.
6) Enters the time delay loop. After 500 milliseconds, exits the loop.
6a) If D6 is 0, the switch was closed during this 500 millisecond window. Toggle play_sound.
6b) Also, if D6 is 0, set tripped to TRUE. Now, when we keep going through the while loop, we'll ignore pin D6 until the 500ms are up.

---------------------------------
=================================
---------------------------------

Whoof. This is a lot of explanation just to get to the first really interesting program for the P.O.V. Try to figure out what it does on your own, without comments.

    // Really Interesting POV Sketch

    #define SWPIN  6
    #define SPKR  14

    int ledPin         = 7;
    int number_of_LEDs = 7;

    boolean play_tone = 0;
    boolean tripped;
    unsigned long start_time;

    void setup() {
       for(int x=ledPin; x < ledPin + number_of_LEDs; x++) {
          pinMode(x, OUTPUT);
       }
       pinMode(SWPIN, INPUT);
       digitalWrite(SWPIN, HIGH);
    }

    void loop() {
       for(int i = 0; i < number_of_LEDs; i++) {
          digitalWrite(ledPin + i, HIGH);

          if(play_tone) {
            tone(SPKR, 50 * i, 100);
          }

          tripped = 0;
          start_time = millis();

          while(millis() - start_time < 100) {
             if(digitalRead(SWPIN) == LOW && ! tripped) {
                play_tone =! play_tone;
                tripped = 1;
             }
          }

          digitalWrite(ledPin + i, LOW);
       }
    }

Return to main Index
Arrays and Functions