Arduino radio project

Source code for an Arduino “radio” project. Per the author, “The base code is from Spark Fun for Si4703 radio hardware. It uses a serial interface to run the radio. I use hardware to run it.” I’m about to do something similar with a Raspberry Pi, and I’m using this code as reference. I saw this code referenced in the comments on this Sparkfun page.

The code:

/*
//************************************************************
// Radio project 2                                           *
// Version 1.1                                               *
// 30 Dec 2012                                               *
// Use Serial LCD for display                                *
// See http://www.arduino.cc/playground/Learning/SparkFunSerLCD
// Michael R. Wild                                           *
//************************************************************
 
 
 The base code is from Spark Fun for Si4703 radio hardware. It uses a serial interface to run the radio. I use hardware to run it.
 
 1-6-2011
 Spark Fun Electronics 2011
 Nathan Seidle
 
 This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
 
 To use this code, connect the following 5 wires:
 Arduino : Si470x board
 3.3V : VCC
 GND : GND
 A5 : SCLK
 A4 : SDIO
 D2 : RST
 A0 : Trimpot (optional) MRW--Not used.
 
 Look for serial output at 57600bps. MRW--9600 is better for me.
 
 The Si4703 ACKs the first byte, and NACKs the 2nd byte of a read.
 
 1/18 - after much hacking, I suggest NEVER write to a register without first reading the contents of a chip.
 ie, don't updateRegisters without first readRegisters.
 
 If anyone manages to get this datasheet downloaded
 http://wenku.baidu.com/view/d6f0e6ee5ef7ba0d4a733b61.html
 Please let us know. It seem to be the latest version of the programming guide. It had a change on page 12 (write 0x8100 to 0x07)
 that allowed me to get the chip working..
 
 Also, if you happen to find "AN243: Using RDS/RBDS with the Si4701/03", please share. I love it when companies refer to
 documents that don't exist. MRW--Have one now.
 
 1/20 - Picking up FM stations from a plane flying over Portugal! Sweet! 93.9MHz sounds a little soft for my tastes,s but
 it's in Porteguese.
 
 ToDo:
 Display current status (from 0x0A) - done 1/20/11
 Add RDS decoding - works, sort of
 Volume Up/Down - done 1/20/11
 Mute toggle - done 1/20/11
 Tune Up/Down - done 1/20/11
 Read current channel (0xB0) - done 1/20/11
 Setup for Europe - done 1/20/11
 Seek up/down - done 1/25/11
 
 The Si4703 breakout does work with line out into a stereo or other amplifier. Be sure to test with different length 3.5mm
 cables. Too short of a cable may degrade reception.
 */

#include  // MRW

#include 

int STATUS_LED = 13;
int resetPin = 2;
int SDIO = A4; //SDA/A4 on Arduino
int SCLK = A5; //SCL/A5 on Arduino
char printBuffer[50];
uint16_t si4703_registers[16]; //There are 16 registers, each 16 bits large

#define FAIL  0
#define SUCCESS  1

#define SI4703 0x10 //0b._001.0000 = I2C address of Si4703 - note that the Wire function assumes non-left-shifted I2C address, not 0b.0010.000W
#define I2C_FAIL_MAX  10 //This is the number of attempts we will try to contact the device before erroring out

//#define IN_EUROPE //Use this define to setup European FM reception. I wuz there for a day during testing (TEI 2011).

#define SEEK_DOWN  0 //Direction used for seeking. Default is down
#define SEEK_UP  1

//Define the register names
#define DEVICEID 0x00
#define CHIPID  0x01
#define POWERCFG  0x02
#define CHANNEL  0x03
#define SYSCONFIG1  0x04
#define SYSCONFIG2  0x05
#define STATUSRSSI  0x0A
#define READCHAN  0x0B
#define RDSA  0x0C
#define RDSB  0x0D
#define RDSC  0x0E
#define RDSD  0x0F

//Register 0x02 - POWERCFG
#define SMUTE  15
#define DMUTE  14
#define SKMODE  10
#define SEEKUP  9
#define SEEK  8

//Register 0x03 - CHANNEL
#define TUNE  15

//Register 0x04 - SYSCONFIG1
#define RDS  12
#define DE  11

//Register 0x05 - SYSCONFIG2
#define SPACE1  5
#define SPACE0  4

//Register 0x0A - STATUSRSSI
#define RDSR  15
#define STC  14
#define SFBL  13
#define AFCRL  12
#define RDSS  11
#define STEREO  8


//#define TRACE 1 //Use this define create TRACE code

//************************************************************************
// Pins 7 for display

#define txPin 7
#define LCDMax 16

SoftwareSerial LCD = SoftwareSerial(0, txPin);
const int LCDdelay=10; 

#define RefreshRate 1024

//************************************************************************
// Physical Controls
#define seekUpButton 8
#define seekDownButton 9
#define volumeNob A3

//************************************************************************
// House keeping 

char  version_code[ ]   = "1.5";
char  project_name[ ]   = "Wild's Radio";

//********************************
//* Serial LCD                   *
//********************************

void lcdPosition(int col, int row) {
  LCD.write(0xFE);   //command flag
  LCD.write((col + row*64 + 128));    //position 
  delay(LCDdelay);
}
void clearLCD(){
  LCD.write(0xFE);   //command flag
  LCD.write(0x01);   //clear command.
  delay(LCDdelay);
}
void backlightOn() {  //turns on the backlight
  LCD.write(0x7C);   //command flag for backlight stuff
  LCD.write(157);    //light level.
  delay(LCDdelay);
}
void backlightOff(){  //turns off the backlight
  LCD.write(0x7C);   //command flag for backlight stuff
  LCD.write(128);     //light level for off.
   delay(LCDdelay);
}
void serCommand(){   //a general function to call the command flag for issuing all other commands   
  LCD.write(0xFE);
}

//************************************************************************

void setup() {                
  pinMode(13, OUTPUT);
  pinMode(A0, INPUT); //Optional trimpot for analog station control

  Serial.begin(9600); //MRW 9600 works better for me
  Serial.println();

  si4703_init(); //Init the Si4703 - we need to toggle SDIO before Wire.begin takes over.
  
  si4703_readRegisters();
  si4703_registers[POWERCFG] ^= (1<<dmute); toggle="" mute="" bit="" si4703_updateregisters();="" *************************************************************************="" set="" up="" the="" lcd's="" number="" of="" columns="" and="" rows:="" pinmode(txpin,="" output);="" lcd.begin(9600);="" clearlcd();="" backlighton();="" lcdposition(0,0);="" print="" a="" message="" to="" lcd.="" this="" lets="" us="" know="" about="" reset="" lcd.print(project_name);="" lcdposition(0,1);="" lcd.print("version="" ");="" lcd.print(version_code);="" delay(500);="" lcd.print("="" ****************************************************************************="" physical="" controls="" pinmode(seekupbutton,="" input);="" pinmode(seekdownbutton,="" pinmode(volumenob,="" volume="" control="" }="" void="" loop()="" {="" define="" values="" used="" for="" rsd="" display="" int="" pi_code="0," previouspi="-1;" pi="" code="" radio="" station="" uint16_t="" breg[lcdmax+1],="" creg[lcdmax+1],="" dreg[lcdmax+1];="" bdata,="" cdata,="" ddata;="" char="" message0_buffer[lcdmax+1]="                " ;="" 1234567890123456="" message0_lots[100],="" hashrsd;="" volumesetting,="" previousvolumesetting;="" use="" these="" refreshcount="0;" cause="" refresh="" every="" once="" in="" while="" option;="" currentchannel,="" previouschannel;="" currentchannel="971;//Default" unit="" known="" good="" local="" gotochannel(currentchannel);="" si4703_readregisters();="" si4703_registers[powercfg]="" ^="(1< RefreshRate) {
        refreshCount = 0;
        previousChannel = - 1; //Force a restart
      }
      else ++refreshCount;
      
      // Get channel and check if change
      
      currentChannel = readChannel();
      if (currentChannel != previousChannel) {//print channel
        previousChannel = currentChannel;
        clearLCD();
        backlightOn();
        sprintf(printBuffer, "FM %02d.%01dMHz", currentChannel / 10, currentChannel % 10);
        lcdPosition(0,0);
        LCD.print(printBuffer);
        PI_Code = -1; // force change
      }// currentChannel !=
      
      //  Check for RSD information      
        si4703_readRegisters();
        if(si4703_registers[STATUSRSSI] & (1<> 8;
          Al = (si4703_registers[RDSA] & 0x00FF);

          Bh = (si4703_registers[RDSB] & 0xFF00) >> 8;
          Bl = (si4703_registers[RDSB] & 0x00FF);

          Ch = (si4703_registers[RDSC] & 0xFF00) >> 8;
          Cl = (si4703_registers[RDSC] & 0x00FF);

          Dh = (si4703_registers[RDSD] & 0xFF00) >> 8;
          Dl = (si4703_registers[RDSD] & 0x00FF);

// Used for spottin duplcate messages
          BData = si4703_registers[RDSB];
          CData = si4703_registers[RDSC];
          DData = si4703_registers[RDSD];
          hashRSD = (BData * 1000) + (CData * 100) + DData;//should be unique for each message

// Break out into useful values
          PI_Code = si4703_registers[RDSA];

          byte groupTypeCode, B_code, trafficCode, PTY_code, extraCode;
          groupTypeCode = (Bh & 0xF0) >> 4;
          B_code = (Bh & 0x08) >> 3;
          trafficCode = (Bh & 0x04) >> 2;
          PTY_code = (Bh & 0x03) << 3;
          PTY_code = PTY_code + ((Bl & 0xE0) >> 5);
          extraCode = (Bl & 0x0F);  // Not sure what to call this          

#ifdef TRACE

          sprintf(printBuffer, "PI: %05d", PI);
          Serial.println(printBuffer);

          sprintf(printBuffer, "Group Type Code: %02x", groupTypeCode);
          Serial.println(printBuffer); 
          
          sprintf(printBuffer, "B_code: %01d", B_code);
          Serial.println(printBuffer); 
           
          sprintf(printBuffer, "Traffic code: %01d", trafficCode);
          Serial.println(printBuffer); 

          sprintf(printBuffer, "PTY code: %01d", PTY_code);
          Serial.println(printBuffer); 

          sprintf(printBuffer, "Extra code: %02x", extraCode);
          Serial.println(printBuffer); 
          
          sprintf(printBuffer, "RDS: %02x %02x %02x %02x %02x %02x Hex !", Bh, Bl, Ch, Cl, Dh, Dl);
          Serial.println(printBuffer);      
#endif

          
//        Detect message change for 0A and 0B only and only in Western Characters

          if (groupTypeCode == 0x00) { 
            
            if (PI_Code != previousPI) {
               previousPI = PI_Code;

               for (int i=0; i> 8;
                    byte DLow = (DReg[i] & 0x00FF);
 
                    sprintf(printBuffer, "%c%c", char(DHigh), char(DLow) );
                    Serial.print(printBuffer); 
                  }
                  Serial.println();             
                } 
                Serial.println();
#endif              
 
                }//Found == FAIL
                else {// SUCCESS

                  }           
              //here
              

              
              lcdPosition(0,1);
              LCD.print(message0_Buffer);

#ifdef TRACE              

              Serial.println(message0_Buffer);
#endif
               
          }// if group 0x00
        } // If we have and RSD
   

//    Check if we have a button pressed

       if (digitalRead(seekUpButton) != 0) {
         seek(SEEK_UP);  
       }//seekup
       
       if (digitalRead(seekDownButton) != 0) {
         seek(SEEK_DOWN);
       }//seekdown
       
       volumeSetting = (analogRead(volumeNob)/ 64) ;
       if (previousVolumeSetting != volumeSetting) {
         si4703_registers[SYSCONFIG2] &= 0xFFF0; //Clear volume bits
         si4703_registers[SYSCONFIG2] |= volumeSetting; //Set new volume
         si4703_updateRegisters(); //Update
         previousVolumeSetting = volumeSetting;
       }//volume change

      } // check for serial input
    option = Serial.read(); // read input

  }//While(1)
}
//*************************************************************************************************
//this is more SparkFUn Code

//Given a channel, tune to it
//Channel is in MHz, so 973 will tune to 97.3MHz
//Note: gotoChannel will go to illegal channels (ie, greater than 110MHz)
//It's left to the user to limit these if necessary
//Actually, during testing the Si4703 seems to be internally limiting it at 87.5. Neat.
void gotoChannel(int newChannel){
  //Freq(MHz) = 0.200(in USA) * Channel + 87.5MHz
  //97.3 = 0.2 * Chan + 87.5
  //9.8 / 0.2 = 49
  newChannel *= 10; //973 * 10 = 9730
  newChannel -= 8750; //9730 - 8750 = 980

#ifdef IN_EUROPE
    newChannel /= 10; //980 / 10 = 98
#else
  newChannel /= 20; //980 / 20 = 49
#endif

  //These steps come from AN230 page 20 rev 0.5
  si4703_readRegisters();
  si4703_registers[CHANNEL] &= 0xFE00; //Clear out the channel bits
  si4703_registers[CHANNEL] |= newChannel; //Mask in the new channel
  si4703_registers[CHANNEL] |= (1<<tune); set="" the="" tune="" bit="" to="" start="" si4703_updateregisters();="" delay(60);="" wait="" 60ms="" -="" you="" can="" use="" or="" skip="" this="" delay="" poll="" see="" if="" stc="" is="" while(1)="" {="" si4703_readregisters();="" if(="" (si4703_registers[statusrssi]="" &="" (1>="" 8;="" byte="" low_byte="si4703_registers[regSpot]" &="" 0x00ff;="" wire.write(high_byte);="" upper="" 8="" bits="" wire.write(low_byte);="" lower="" }="" end="" transmission="" ack="Wire.endTransmission();" if(ack="" !="0)" we="" have="" a="" problem!="" serial.print("write="" fail:");="" no="" ack!="" serial.println(ack,="" dec);="" i2c="" error:="" 0="success," 1="data" too="" long,="" 2="rx" nack="" on="" address,="" 3="rx" data,="" 4="other" error="" return(fail);="" return(success);="" read="" entire="" register="" control="" from="" 0x00="" 0x0f="" void="" si4703_readregisters(void){="" si4703="" begins="" reading="" of="" 0x0a="" and="" reads="" 0x0f,="" then="" loops="" 0x00.="" wire.requestfrom(si4703,="" 32);="" want="" 0x09="32" bytes.="" while(wire.available()="" <="" 32)="" ;="" for="" 16="" words="" 32="" bytes="" come="" back="" slave="" device="" may="" some="" time-out="" here="" remember,="" comes="" in="" first="" so="" shuffle="" array="" around="" for(int="" x="0x0A" x++)="" these="" if(x="=" 0x10)="" loop="" zero="" si4703_registers[x]="Wire.read()" <<="" |="Wire.read();" 0x09)="" break;="" we're="" done!="" si4703_printregisters(void)="" registers="" print="" response="" debugging="" sprintf(printbuffer,="" "reg="" 0x%02x="0x%04X&quot;," x,="" si4703_registers[x]);="" serial.println(printbuffer);=""