Go Down

### Topic: Simple Sound just an Arduino, a transistor, a battery and a speaker. (Read 962 times)previous topic - next topic

#### tkolsenattds

##### Oct 04, 2018, 10:32 pm
I teach a high school STEM class.  This project is teaching basic sound.  I show them a speaker playing a low tone and they can see and feel the cone going out and in.  We talk about pressure waves, etc.
In the project I created for them, they can play simple songs by coding in tones, octaves, and times (beats).
Everything seems to work except octave 0.  I look at the pulses with an oscilloscope and everything looks right but when I listen to it, octave 0 is messed up.  The tones are way wrong.  I could probably attach a sound file if you would like but it's quite easy to build if someone is willing.  Circuit description is in the beginning remarks of the sketch.
I am really puzzled!

Code: [Select]
`/*     0093 Play Seven Notes        Simple Circuit:        Use an NPN transistor to control the current through the speaker.        Use a separate battery pack for powering the speaker.  2 AA (3 volts) work with most speakers.        D4 to a resistor to the base of the transistor.  Anything 300 to 10K works.        Ground from Arduino to battery pack negative to emitter of the transistor.        Collector of the transistor either directly to one lead of the speaker or go through a 10 ohm resistor to the speaker.        Positive voltage from the battery pack to the other lead of the speaker.*/int spkrPin = 4;int Tempo = 1250; // multiplier for time for 1 count. If a 1/4 note gets 2 counts then time is 2 * Tempo to get millisecondsfloat C[] = {16.4 , 32.7 , 65.4 , 130.8, 261.6, 523.3 , 1046.5 , 2093.0 , 4186.0 };          //   Frequencies of notes by octave.     C[4] is middle C   261.6 Hzfloat Cs[] = {17.3 , 34.7 , 69.3 , 138.6 , 277.2 , 554.4 , 1108.7 , 2217.5 , 4434.9 };float D [] = { 18.4 , 36.7 , 73.4 , 146.8 , 293.7 , 587.3 , 1174.7 , 2349.3 , 4698.6};float Ds [] = { 19.5 , 38.9 , 77.8 , 155.6 , 311.1 , 622.3 , 1244.5 , 2489.0 , 4978.0};float Ef [] = { 19.5 , 38.9 , 77.8 , 155.6 , 311.1 , 622.3 , 1244.5 , 2489.0 , 4978.0};float E [] = { 20.6 , 41.2 , 82.4 , 164.8 , 329.6 , 659.3 , 1318.5 , 2637.0 , 5274.0 };float F [] = { 21.8 , 43.7 , 87.3 , 174.6 , 349.2 , 698.5 , 1396.9 , 2793.8 , 5587.7 };float Fs [] = { 23.1 , 46.3 , 92.5 , 185.0 , 370.0 , 740.0 , 1480.0 , 2960.0 , 5919.9};float Gf [] = { 23.1 , 46.3 , 92.5 , 185.0 , 370.0 , 740.0 , 1480.0 , 2960.0 , 5919.9};float G [] = { 24.5 , 49.0 , 98.0 , 196.0 , 392.0 , 784.0 , 1568.0 , 3136.0 , 6271.9 };float Gs [] = {26.0 , 51.9 , 103.8 , 207.7 , 415.3 , 830.6 , 1661.2 , 3322.4 , 6644.9 };float Af [] = {26.0 , 51.9 , 103.8 , 207.7 , 415.3 , 830.6 , 1661.2 , 3322.4 , 6644.9 };float A [] = { 27.5 , 55.0 , 110.0 , 220.0 , 440.0 , 880.0 , 1760.0 , 3520.0 , 7040.0 };float As [] = { 29.1 , 58.3 , 116.5 , 233.1 , 466.2 , 932.3 , 1864.7 , 3729.3 , 7458.6 };float Bf [] = { 29.1 , 58.3 , 116.5 , 233.1 , 466.2 , 932.3 , 1864.7 , 3729.3 , 7458.6 };float B [] = { 30.9 , 61.7 , 123.5 , 246.9 , 493.9 , 987.8 , 1975.5 , 3951.1 , 7902.1 };int rest = 0;  //  rest   pauseunsigned long endTime = 0;long nTime = 0;   //  time to play this notevoid setup(){  pinMode(spkrPin, OUTPUT);  Serial.begin(9600);}void playNote(int Beats, float Note) // This is function "playNote". Two parameters are passed to this function.  (Beats and Note){  // first calculate the length of time in microseconds of each pulse at a given frequency  // middle C is 262 Hz. 1 / 262 = .003822 seconds .001911 for a half cycle which equals 1911 microseconds  long pTime = 1 / Note / 2 * 1000000;  //  This is the time in microseconds to hold one pulse high or low  Serial.print(Note);  Serial.print("  ");  Serial.println(pTime);  nTime = Beats * Tempo;         //  This is the time to keep playing one note.   Tempo allows an easy way to speed up or slow down a song.  endTime = millis() + nTime;  while (millis() < endTime)  {    digitalWrite (spkrPin, HIGH);    delayMicroseconds (pTime);    digitalWrite (spkrPin, LOW);    delayMicroseconds (pTime);  }  delay(15); // a tiny pause between notes to hear each one}void loop(){  // Octave 0  (frequencies 16.4 to 30.9) don't play well.  After that, they play fine.  playNote(3, 27.5);     //    ( time to play the note, frequency of the note )   could use  A[0]  playNote(3, 29.1);     //     could use     As[0]       A sharp        A#  playNote(3, 30.9);  playNote(3, 32.7);  playNote(3, Cs[1]);  playNote(3, D[1]);  playNote(3, Ds[1]);  delay(3000);/*  //    The following will play all the notes in the array in order from low to high  for (int octave = 0; octave <= 8; octave ++)  {    playNote(1, C[octave]);    playNote(1, Cs[octave]);  //   Cs is C sharp    playNote(1, D[octave]);    playNote(1, Ds[octave]);    playNote(1, E[octave]);    playNote(1, F[octave]);    playNote(1, Fs[octave]);    playNote(1, G[octave]);    playNote(1, Gs[octave]);    playNote(1, A[octave]);    playNote(1, As[octave]);    playNote(1, B[octave]);  }  delay(2000); // delay between runs  Tempo *= .8; // speed it up  if (Tempo <= 50)  {    Tempo = 250;  }  */}`

#### DVDdoug

#1
##### Oct 04, 2018, 10:52 pmLast Edit: Oct 04, 2018, 10:53 pm by DVDdoug
Quote
Everything seems to work except octave 0.  I look at the pulses with an oscilloscope and everything looks right but when I listen to it, octave 0 is messed up.
But, octave 0 is OK on the 'scope?

...It takes a "big" subwoofer and a "big" amplifier to reproduce low-frequency notes.   You're probably just hearing harmonics & noise.

#### tkolsenattds

#2
##### Oct 05, 2018, 05:29 am
The notes above 29.1 Hz sound fine for these purposes without an amp or woofer.  I'm not trying to get quality base tones.  I'm just trying to teach very basic sound with an Arduino.  I realize it's asking a lot for someone to build this little circuit (although it is fun).  I'll attach a file with the sound.  Maybe that will help.

#### tkolsenattds

#3
##### Oct 05, 2018, 05:37 am
It wouldn't accept a  .m4a  file.     Maybe it won't accept any sound file.

#### uxomm

#4
##### Oct 05, 2018, 11:25 amLast Edit: Oct 05, 2018, 12:29 pm by uxomm
delayMicroseconds() will overflow with higher values than 16383 (pTime in your sketch). Looks like it does not work with more than 14 bit.

See reference: https://www.arduino.cc/reference/en/language/functions/time/delaymicroseconds/:
Quote
Currently, the largest value that will produce an accurate delay is 16383.
So it will overflow with pTime 18181 and you will get about 276 Hz instead of 27.5 Hz,
with pTime 17182 you get about 618 Hz instead of 29.1 Hz.

Lower values of pTime will work fine.

You could work around the problem with something like this - quick and dirty
Code: [Select]
`...    if (pTime > 16000) {    digitalWrite (spkrPin, HIGH);    delayMicroseconds(16000);    delayMicroseconds( pTime - 16000);    digitalWrite (spkrPin, LOW);    delayMicroseconds(16000);    delayMicroseconds( pTime - 16000);  }  else {    digitalWrite (spkrPin, HIGH);    delayMicroseconds (pTime);    digitalWrite (spkrPin, LOW);    delayMicroseconds (pTime);  }...`
Always decouple electronic circuitry.

#### tkolsenattds

#5
##### Oct 05, 2018, 03:17 pm
Thank you so very much.  I was so stumped.  You nailed it.
I must not have looked at it correctly with the scope.

Thank you.    Thank you.    Thank you.

#### MarkT

#6
##### Oct 15, 2018, 03:56 pm
delayMicroseconds() will overflow with higher values than 16383 (pTime in your sketch). Looks like it does not work with more than 14 bit.

See reference: https://www.arduino.cc/reference/en/language/functions/time/delaymicroseconds/:
So it will overflow with pTime 18181 and you will get about 276 Hz instead of 27.5 Hz,
with pTime 17182 you get about 618 Hz instead of 29.1 Hz.

Lower values of pTime will work fine.

You could work around the problem with something like this - quick and dirty
Code: [Select]
`...    if (pTime > 16000) {    digitalWrite (spkrPin, HIGH);    delayMicroseconds(16000);    delayMicroseconds( pTime - 16000);    digitalWrite (spkrPin, LOW);    delayMicroseconds(16000);    delayMicroseconds( pTime - 16000);  }  else {    digitalWrite (spkrPin, HIGH);    delayMicroseconds (pTime);    digitalWrite (spkrPin, LOW);    delayMicroseconds (pTime);  }...`
A much better workaround is to write a new delay function that does delay correctly for large values:
Code: [Select]
`void delayMicros(unsigned long us){  while (us >= 16000)  {    delayMicroseconds (16000) ;    us -= 16000 ;  }  delayMicroseconds (us) ;}`
and use that function everywhere....
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up