To change the transmission frequency, you first want to go to the Vacant FM Frequency Finder and find a open channel in your area. Then, once you found one, a little bit of conversion is needed. I choose 96.7Mhz for my area.
So what you do is: (FMfreq + 0.304)/0.008192 where FMfreq is 96.7. That equals about 11792.
Then it needs to be converted to binary with Google: 11792 to binary which gets B10111000010000
Next, it must be 16 digits long, so add some zeros in front until you get 16 digits. I added two 0s: B0010111000010000.
Take the last 8 digits and that is register 3(00010000), and the first 8 digits are register 4(00101110).
In my code it looks like this:
spi_send(0x03, B00010000); //Register 3: Set broadcast freq to 96.7, lower byte
spi_send(0x04, B00101110); //Register 4: Set broadcast freq to 96.7, upper byte
Pics:
Note how i soldered the pins: easier to use on breadboard with labels facing up. |
Scheme for Arduino |
Code: The following sets the frequency and initializes the transmitter. this would be good for hooking a MP3 player to right and left inputs:
/*
12-1-2007
Cai Maver, caimaver(at)yahoo.com
ARRRduino+NS73M v0.3
This code allows an Arduino Diecimila to control an NS73M FM Transmitter Module (Arrr matey!)
This sets the NS73M (available from Sparkfun, SKU#: WRL-08482) to transmit
at 2mW with 75us pre-emphasis (the standard for North America) on 97.3 MHz.
Use this formula to determine the register values for a new transmitting frequency (f):
(f + 0.304)/0.008192 <-- use only the whole number and convert the result to
16-bit binary where the lower byte goes in register 3
and the upper byte goes in register 4
ex.: for 97.3 MHz, (97.3 + 0.304)/0.008192 = 11914.55...
11914 = B0010111010001010 so
Reg 3=B10001010
reg 4=B00101110
ex 96.7
//add "2" "0"s to the begginning... 16 long
11792 = B0010111000010000
reg 3 = B00010000 //second half goes here, 8 long
reg 4 = B00101110 //first half goes here
A future version of this code will allow for on-the-fly frequency changes
Thanks to Nathan Seidle at Sparkfun and Jim "ZAPNSPARK" G. for sharing their NS73M code!
*/
int CK = 12; //clock pin
int DA = 11; //data pin
int LA = 10; //latch pin
void setup(){
Serial.begin(9600); //begin Serial connection for debugging
pinMode(CK, OUTPUT);
pinMode(DA, OUTPUT);
pinMode(LA, OUTPUT);
digitalWrite(LA, LOW); //unlatch transmitter
delay(100); //Wait for VDD to settle
spi_send(0x0E, B00000101); //Software reset
spi_send(0x01, B10110100); //Register 1: forced subcarrier, pilot tone on
spi_send(0x02, B00000011); //Register 2: Unlock detect off, 2mW Tx Power
spi_send(0x03, B00010000); //Register 3: Set broadcast freq to 96.7, lower byte
spi_send(0x04, B00101110); //Register 4: Set broadcast freq to 96.7, upper byte
spi_send(0x08, B00011010); //Register 8: set Osc on band 2
spi_send(0x00, B10100001); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power on
spi_send(0x0E, B00000101); //Software reset
spi_send(0x06, B00011110); //Register 6: charge pumps at 320uA and 80 uA
Serial.print("Transmitting"); //for debugging
}
void loop()
{
}
void spi_send(byte reg, byte data) //routine to send Register Address and Data as LSB-first SPI
{
int x;
int n;
digitalWrite(LA, LOW);
for(x = 0 ; x < 4 ; x++) //send four-bit register address
{
digitalWrite(CK, LOW); //Toggle the SPI clock
n = (reg >> x) & 1; //n is the xth bit of the register byte
if (n == 1){
digitalWrite(DA, HIGH); //Put high bit on SPI data bus
}
else {
digitalWrite(DA, LOW); //Put low bit on SPI data bus
}
Serial.print(n); //send bit to serial connection for debugging
digitalWrite(CK, HIGH); //Toggle the SPI clock
}
for(x = 0 ; x < 8 ; x++) //send eight-bit register data
{
digitalWrite(CK, LOW); //Toggle the SPI clock
n = (data >> x) & 1;
if (n == 1){
digitalWrite(DA, HIGH); //Put high bit on SPI data bus
}
else {
digitalWrite(DA, LOW); //Put low bit on SPI data bus
}
Serial.print(n); //send bit to serial connection for debugging
digitalWrite(CK, HIGH); //Toggle the SPI clock
}
delayMicroseconds(1); //might not be needed, supposedly unstable command anyway
digitalWrite(LA, HIGH); //Latch this transfer
delayMicroseconds(4);
digitalWrite(LA, LOW);
digitalWrite(CK, LOW); //This is to keep CK pin at 0V at end of data transfer
Serial.print("\n"); // send new-line to serial for debugging
}
Code: The following sets the frequency, initializes the transmitter, AND it plays some popular melodies using the RTTL Tone library over FM 96.7:
/*
12-1-2007
Cai Maver, caimaver(at)yahoo.com
ARRRduino+NS73M v0.3
This code allows an Arduino Diecimila to control an NS73M FM Transmitter Module (Arrr matey!)
This sets the NS73M (available from Sparkfun, SKU#: WRL-08482) to transmit
at 2mW with 75us pre-emphasis (the standard for North America) on 97.3 MHz.
Use this formula to determine the register values for a new transmitting frequency (f):
(f + 0.304)/0.008192 <-- use only the whole number and convert the result to
16-bit binary where the lower byte goes in register 3
and the upper byte goes in register 4
ex.: for 97.3 MHz, (97.3 + 0.304)/0.008192 = 11914.55...
11914 = B0010111010001010 so
Reg 3=B10001010
reg 4=B00101110
ex 96.7, (96.7 + 0.304)/0.008192 = 11792.4
//convert to binary
11792 = B0010111000010000//add "2" "0"s to the begginning... 16 long
reg 3 = B00010000 //second half goes here, 8 long
reg 4 = B00101110 //first half goes here
Thanks to Nathan Seidle at Sparkfun and Jim "ZAPNSPARK" G. for sharing their NS73M code!
*/
int CK = 12; //clock pin
int DA = 11; //data pin
int LA = 10; //latch pin
#include <Tone.h>
Tone tone1;
#define OCTAVE_OFFSET 0
int notes[] = { 0,
NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,
NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,
NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6,
NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7
};
//char *song = "The Simpsons:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#.,8c6,8c6,8c6,c6";
//char *song = "Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6";
//char *song = "TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5";
//char *song = "Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6";
//char *song = "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c";
//char *song = "Xfiles:d=4,o=5,b=125:e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,g6,f#6,e6,d6,e6,2b.,1p,g6,f#6,e6,d6,f#6,2b.,1p,e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,e6,2b.";
//char *song = "Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f";
//char *song = "20thCenFox:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p,8e,8g#,8b,1c#6,8f#,8a,8c#6,1e6,8a,8c#6,8e6,1e6,8b,8g#,8a,2b";
//char *song = "Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6";
//char *song = "MASH:d=8,o=5,b=140:4a,4g,f#,g,p,f#,p,g,p,f#,p,2e.,p,f#,e,4f#,e,f#,p,e,p,4d.,p,f#,4e,d,e,p,d,p,e,p,d,p,2c#.,p,d,c#,4d,c#,d,p,e,p,4f#,p,a,p,4b,a,b,p,a,p,b,p,2a.,4p,a,b,a,4b,a,b,p,2a.,a,4f#,a,b,p,d6,p,4e.6,d6,b,p,a,p,2b";
//char *song = "StarWars:d=4,o=5,b=45:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6";
//char *song = "GoodBad:d=4,o=5,b=56:32p,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,d#,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,c#6,32a#,32d#6,32a#,32d#6,8a#.,16f#.,32f.,32d#.,c#,32a#,32d#6,32a#,32d#6,8a#.,16g#.,d#";
//char *song = "TopGun:d=4,o=4,b=31:32p,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,16f,d#,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,g#";
//char *song = "A-Team:d=8,o=5,b=125:4d#6,a#,2d#6,16p,g#,4a#,4d#.,p,16g,16a#,d#6,a#,f6,2d#6,16p,c#.6,16c6,16a#,g#.,2a#";
//char *song = "Flinstones:d=4,o=5,b=40:32p,16f6,16a#,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,d6,16f6,16a#.,16a#6,32g6,16f6,16a#.,32f6,32f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,a#,16a6,16d.6,16a#6,32a6,32a6,32g6,32f#6,32a6,8g6,16g6,16c.6,32a6,32a6,32g6,32g6,32f6,32e6,32g6,8f6,16f6,16a#.,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#6,16c7,8a#.6";
char *song = "Jeopardy:d=4,o=6,b=125:c,f,c,f5,c,f,2c,c,f,c,f,a.,8g,8f,8e,8d,8c#,c,f,c,f5,c,f,2c,f.,8d,c,a#5,a5,g5,f5,p,d#,g#,d#,g#5,d#,g#,2d#,d#,g#,d#,g#,c.7,8a#,8g#,8g,8f,8e,d#,g#,d#,g#5,d#,g#,2d#,g#.,8f,d#,c#,c,p,a#5,p,g#.5,d#,g#";
//char *song = "Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#";
//char *song = "Smurfs:d=32,o=5,b=200:4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8f#,p,8a#,p,4g#,4p,g#,p,a#,p,b,p,c6,p,4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8b,p,8f,p,4f#";
//char *song = "MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#.";
//char *song = "LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#";
//char *song = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d";
void setup(){
Serial.begin(9600); //begin Serial connection for debugging
tone1.begin(8);
pinMode(CK, OUTPUT);
pinMode(DA, OUTPUT);
pinMode(LA, OUTPUT);
digitalWrite(LA, LOW); //unlatch transmitter
delay(100); //Wait for VDD to settle
spi_send(0x0E, B00000101); //Software reset
spi_send(0x01, B10110100); //Register 1: forced subcarrier, pilot tone on
spi_send(0x02, B00000011); //Register 2: Unlock detect off, 2mW Tx Power
spi_send(0x03, B00010000); //Register 3: Set broadcast freq to 96.7, lower byte
spi_send(0x04, B00101110); //Register 4: Set broadcast freq to 96.7, upper byte
spi_send(0x08, B00011010); //Register 8: set Osc on band 2
spi_send(0x00, B10100001); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power on
spi_send(0x0E, B00000101); //Software reset
spi_send(0x06, B00011110); //Register 6: charge pumps at 320uA and 80 uA
Serial.print("Transmitting"); //for debugging
}
#define isdigit(n) (n >= '0' && n <= '9')
void play_rtttl(char *p)
{
// Absolutely no error checking in here
byte default_dur = 4;
byte default_oct = 6;
int bpm = 63;
int num;
long wholenote;
long duration;
byte note;
byte scale;
// format: d=N,o=N,b=NNN:
// find the start (skip name, etc)
while(*p != ':') p++; // ignore name
p++; // skip ':'
// get default duration
if(*p == 'd')
{
p++; p++; // skip "d="
num = 0;
while(isdigit(*p))
{
num = (num * 10) + (*p++ - '0');
}
if(num > 0) default_dur = num;
p++; // skip comma
}
Serial.print("ddur: "); Serial.println(default_dur, 10);
// get default octave
if(*p == 'o')
{
p++; p++; // skip "o="
num = *p++ - '0';
if(num >= 3 && num <=7) default_oct = num;
p++; // skip comma
}
Serial.print("doct: "); Serial.println(default_oct, 10);
// get BPM
if(*p == 'b')
{
p++; p++; // skip "b="
num = 0;
while(isdigit(*p))
{
num = (num * 10) + (*p++ - '0');
}
bpm = num;
p++; // skip colon
}
Serial.print("bpm: "); Serial.println(bpm, 10);
// BPM usually expresses the number of quarter notes per minute
wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds)
Serial.print("wn: "); Serial.println(wholenote, 10);
// now begin note loop
while(*p)
{
// first, get note duration, if available
num = 0;
while(isdigit(*p))
{
num = (num * 10) + (*p++ - '0');
}
if(num) duration = wholenote / num;
else duration = wholenote / default_dur; // we will need to check if we are a dotted note after
// now get the note
note = 0;
switch(*p)
{
case 'c':
note = 1;
break;
case 'd':
note = 3;
break;
case 'e':
note = 5;
break;
case 'f':
note = 6;
break;
case 'g':
note = 8;
break;
case 'a':
note = 10;
break;
case 'b':
note = 12;
break;
case 'p':
default:
note = 0;
}
p++;
// now, get optional '#' sharp
if(*p == '#')
{
note++;
p++;
}
// now, get optional '.' dotted note
if(*p == '.')
{
duration += duration/2;
p++;
}
// now, get scale
if(isdigit(*p))
{
scale = *p - '0';
p++;
}
else
{
scale = default_oct;
}
scale += OCTAVE_OFFSET;
if(*p == ',')
p++; // skip comma for next note (or we may be at the end)
// now play the note
if(note)
{
Serial.print("Playing: ");
Serial.print(scale, 10); Serial.print(' ');
Serial.print(note, 10); Serial.print(" (");
Serial.print(notes[(scale - 4) * 12 + note], 10);
Serial.print(") ");
Serial.println(duration, 10);
tone1.play(notes[(scale - 4) * 12 + note]);
delay(duration);
tone1.stop();
}
else
{
Serial.print("Pausing: ");
Serial.println(duration, 10);
delay(duration);
}
}
}
void loop(void)
{
play_rtttl(song);
Serial.println("Done.");
delay(1000);
}
void spi_send(byte reg, byte data) //routine to send Register Address and Data as LSB-first SPI
{
int x;
int n;
digitalWrite(LA, LOW);
for(x = 0 ; x < 4 ; x++) //send four-bit register address
{
digitalWrite(CK, LOW); //Toggle the SPI clock
n = (reg >> x) & 1; //n is the xth bit of the register byte
if (n == 1){
digitalWrite(DA, HIGH); //Put high bit on SPI data bus
}
else {
digitalWrite(DA, LOW); //Put low bit on SPI data bus
}
Serial.print(n); //send bit to serial connection for debugging
digitalWrite(CK, HIGH); //Toggle the SPI clock
}
for(x = 0 ; x < 8 ; x++) //send eight-bit register data
{
digitalWrite(CK, LOW); //Toggle the SPI clock
n = (data >> x) & 1;
if (n == 1){
digitalWrite(DA, HIGH); //Put high bit on SPI data bus
}
else {
digitalWrite(DA, LOW); //Put low bit on SPI data bus
}
Serial.print(n); //send bit to serial connection for debugging
digitalWrite(CK, HIGH); //Toggle the SPI clock
}
delayMicroseconds(1); //might not be needed, supposedly unstable command anyway
digitalWrite(LA, HIGH); //Latch this transfer
delayMicroseconds(4);
digitalWrite(LA, LOW);
digitalWrite(CK, LOW); //This is to keep CK pin at 0V at end of data transfer
Serial.print("\n"); // send new-line to serial for debugging
}
Hi, thanks for you detailed tutorial! I've made something similar with an arduino mini for my iPod, but with communication over I2C (shouldn't harm I guess).
ReplyDeleteHowever, I hear a high frequency noise on the received signal and I was wondering if you also experienced this. I tried an additional bypass cap, but this didn't help... Would you have any ideas?
Many thanks!
i2c from arduino to NS73M? i found this project very finicky and sometimes thins wouldn't work.sometimes id have to reprogram the Arduino, other times just reset it.
ReplyDeletebut, to your question, no. make sure your grounds are good. when mine was working, jeopardy played nice and clean.
hi, can this ns73M module send 2 frequency at the same time? Using the given codes. Example 2 different FM receiving the same signal at the same time.
ReplyDeleteThanks alot!
no it only transmits on one frequency at at time.
ReplyDeletehi jordan, i have problem compiling the 2nd code, theres always an error stated "Tone doesn't name a type"
ReplyDeleteThanks for pointing that out! download this zip: https://dl.dropboxusercontent.com/u/28052258/Tone.zip
Deleteand extract it to the libraries folder in your Arduino folder. for ex, my path is "C:\Program Files (x86)\Arduino\libraries". afterwards you should have something like this: "C:\Program Files (x86)\Arduino\libraries\Tone"
Although I did what you said, the "Note" have already change into blue colour , but it still stated " tone does not name a type", when i open the example "toneMelody" it stated " tone was not declared in this scope".
ReplyDeletewhat arduino are you using, and what IDE version?
DeleteI am using arduino due, v 1.5.2
ReplyDeletetry setting the ide to compile for uno (set board to uno in tools menu) and see if it will just compile.
DeleteHi - I bought an FM transmitter from SparkFun (product URL at bottom). In fact, I bought ~8 of these so far. Ive been unable to affix leads to each of the contacts. Any help is appreciated before I buy 80x more!
ReplyDeletehttps://www.sparkfun.com/products/8452
Do I even need to solder leads to all 8+7 (15) contacts!?
DeleteHi-
ReplyDeleteIs that a diode on the data line? Is that needed? What is it?
Thanks!