// // // ARRRduino-FM: Part 15 FM Broadcasting for the Arduino // // By: Cairn; caimaver (at) yahoo dot com // Date: 22 Mar. 2008 // Version 1.1 // Language: Arduino 0010 // MODs by: Mike Yancey, May 12 - 14, 2008 // // Description: Uses an Arduino or compatible to control an // NS73M FM Transmitter module (available from // Sparkfun) on the I2C bus. // // Experimentally derived Band Settings for VFO ... // Band 0: 83.78 - 91.72 // Band 1: 88.74 - 98.28 // Band 2: 93.10 - 104.00 // Band 3: 99.50 - 112.72 // ref: http://cba.sakura.ne.jp/sub04/jisaku36.htm (translated) #include #include #include #include // Note MODS within here to conserve pins. #define upButton 6 // up button on pin 6 #define downButton 5 // down button on pin 5 #define setButton 4 // set button on pin 4 #define topFM 107900000 // Top of the FM Dial Range in USA #define botFM 87500000 // Bottom of the FM Dial Range in USA #define incrFM 200000 // FM Channel Increment in USA // define incrFM 100000 // FM Channel Increment - certain countries. // define incrFM 50000 // FM Channel Increment - certain countries... long frequency = 97300000; // the default initial frequency in Hz long newFrequency = 0; boolean gOnAir = false; // Initially, NOT On The Air... // Define the LCD LCD4Bit lcd = LCD4Bit(2); // LCD is 2 lines internally... char chrDigits[10] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; void setup() { // Setup the Up/Down and SET buttons pinMode(upButton, INPUT); pinMode(downButton, INPUT); pinMode(setButton, INPUT); //Serial.begin(9600); //for debugging // Attempt to read the last saved frequency from EEPROM newFrequency = loadFrequency(); // Test if outside our FM Range... if ( newFrequency < botFM || newFrequency > topFM ) { // Sorry - haven't saved before - use the default. frequency = 97300000; } else { // We have a valid frequency! frequency = newFrequency; } // Startup the LCD... lcd.init(); lcd.clear(); lcd.cursorTo(1, 0); lcd.printIn( "FM Stere"); lcd.cursorTo(2, 0); lcd.printIn( "o"); delay(2000); lcd.cursorTo(1, 0); lcd.printIn( "Broadcas"); lcd.cursorTo(2, 0); lcd.printIn( "ter..."); delay(2000); displayFrequency( frequency); Wire.begin(); // join i2c bus as master transmitter_setup( frequency ); } void loop() { if (digitalRead(upButton) == LOW) { frequency += incrFM; // 200kHz steps for North American channel spacing frequency = constrain( frequency, botFM, topFM); // Keeps us in range... //Serial.println( frequency, DEC ); transmitter_standby( frequency ); } if (digitalRead(downButton) == LOW) { frequency -= incrFM; // 200kHz steps for North American channel spacing frequency = constrain( frequency, botFM, topFM); // Keeps us in range... //Serial.println( frequency, DEC ); transmitter_standby( frequency ); } if (digitalRead(setButton) == LOW) { // Create a 'toggle' - pressing set while OnAir - set's StandBy and // if we're already StandBy, set the Frequency and go OnAir... if ( gOnAir ) { transmitter_standby( frequency ); } else { set_freq( frequency ); saveFrequency( frequency ); // Save the Frequency to EEPROM Memory delay(1000); } } // Lather, Rinse, Repeat.... } void transmitter_setup( long initFrequency ) { i2c_send(0x0E, B00000101); //Software reset i2c_send(0x01, B10110100); //Register 1: forced subcarrier, pilot tone on i2c_send(0x02, B00000011); //Register 2: Unlock detect off, 2mW Tx Power set_freq( initFrequency); i2c_send(0x00, B10100001); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power on i2c_send(0x0E, B00000101); //Software reset i2c_send(0x06, B00011110); //Register 6: charge pumps at 320uA and 80 uA } void transmitter_standby( long aFrequency ) { i2c_send(0x00, B10100000); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power OFF displayFrequency( aFrequency ); delay(100); gOnAir = false; } void set_freq( long aFrequency ) { int new_frequency; // New Range Checking... Implement the (experimentally determined) VFO bands: if (aFrequency < 88500000) { // Band 3 i2c_send(0x08, B00011011); //Serial.println("Band 3"); } else if (aFrequency < 97900000) { // Band 2 i2c_send(0x08, B00011010); //Serial.println("Band 2"); } else if (aFrequency < 103000000) { // Band 1 i2c_send(0x08, B00011001); //Serial.println("Band 1"); } else { // Must be OVER 103.000.000, // Band 0 i2c_send(0x08, B00011000); //Serial.println("Band 0"); } new_frequency = (aFrequency + 304000) / 8192; byte reg3 = new_frequency & 255; //extract low byte of frequency register byte reg4 = new_frequency >> 8; //extract high byte of frequency register i2c_send(0x03, reg3); //send low byte i2c_send(0x04, reg4); //send high byte // Retain old 'band set' code for reference.... // if (new_frequency <= 93100000) { i2c_send(0x08, B00011011); } // if (new_frequency <= 96900000) { i2c_send(0x08, ); } // if (new_frequency <= 99100000) { i2c_send(0x08, B00011001); } // if (new_frequency > 99100000) { i2c_send(0x08, B00011000); } i2c_send(0x0E, B00000101); //software reset //Serial.print("Frequency changed to "); //Serial.println(aFrequency, DEC); i2c_send(0x00, B10100001); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power ON lcd.cursorTo(2, 0); lcd.printIn( " On Air "); gOnAir = true; } void i2c_send(byte reg, byte data) { Wire.beginTransmission(B1100111); // transmit to device 1100111 Wire.send(reg); // sends register address Wire.send(data); // sends register data Wire.endTransmission(); // stop transmitting delay(5); // allow register to set } void saveFrequency ( long aFrequency ) { long memFrequency = 0; // For use in Read / Write to EEProm //Serial.print( "Saving: " ); //Serial.println(aFrequency, DEC); memFrequency = aFrequency / 10000; EEPROM.write( 0, memFrequency / 256); // right-most byte EEPROM.write( 1, memFrequency - (memFrequency / 256) * 256 ); // next to right-most byte } long loadFrequency () { long memFrequency = 0; // For use in Read / Write to EEProm memFrequency = EEPROM.read(0) * 256 + EEPROM.read(1); memFrequency *= 10000; //Serial.print("Retrieving: " ); //Serial.println(memFrequency, DEC); return memFrequency; } void displayFrequency( long aFrequency) { long memFrequency = 0; int aDigit = 0; // LCD Display template: "108.1Mhz" --> 8 characters TextString lcdOutput = TextString( 9 ); lcdOutput.setArray("108.0MHZ" ); // Just a template... memFrequency = aFrequency / 100000; // Gets us down to 9999 digits (where 1011 ---> 101.1 aDigit = memFrequency % 10; memFrequency = memFrequency / 10; lcdOutput.setCharAt( 4, chrDigits[ aDigit ] ); //Index into our character list... aDigit = memFrequency % 10; memFrequency = memFrequency / 10; lcdOutput.setCharAt( 2, chrDigits[ aDigit ] ); //Index into our character list... aDigit = memFrequency % 10; memFrequency = memFrequency / 10; lcdOutput.setCharAt( 1, chrDigits[ aDigit ] ); //Index into our character list... aDigit = memFrequency; // whatever's left. if ( aDigit > 0 ) { lcdOutput.setCharAt( 0, '1' ); //...it can only be a 1 or a zero. } else { lcdOutput.setCharAt( 0, ' ' ); // Suppress any leading zero... } lcdOutput.setCharAt( 3, '.' ); lcdOutput.setCharAt( 5, 'm' ); lcdOutput.setCharAt( 6, 'h' ); lcdOutput.setCharAt( 7, 'z' ); lcdOutput.setCharAt( 8, '\0'); // Be SURE there's a 'nulchar' at the end!!! //Serial.println( lcdOutput.getArray() ); // Write to the LCD //lcd.clear(); lcd.cursorTo(1, 0); lcd.printIn( lcdOutput.getArray() ); lcd.cursorTo(2, 0); lcd.printIn( " Standby"); }