KansasCoder
Posts: 7
Joined: Tue Jan 08, 2013 7:45 pm

SI4703 FM Tuner Eval Board working with RasPi

Tue Jan 08, 2013 8:36 pm

OK, got a Pi for xmas and bought this FM radio receiver Eval Board from Sparkfun.
https://www.sparkfun.com/products/10663

Neat little device.

I had never used Linux before or used a breakout board or used Python or soldered anything so forgive the bad coding or work. I just wanted to publish some information for others who might be playing with this board down the road.

The hardware connections was just power to power, ground to ground, SCL to SCL, SDA to SDA and RST on the eval board to #23 on the Pi.

SDA is also GPIO Pin 0 with the GPIO.setmode(GPIO.BCM) setup which you need to drive the SDA low to put the SI4703 in 2-wire (i2c) mode.

Once connected plug in some headphones into the Eval Board because the headphone wire acts as the antenna.

The code below is crap I will admit. I need to make the register update more fancy and not just plug in byte values. I just wanted to get it working and will fine tune later.

The SI4703 always starts reading from register 0x0A and then reads to 0x0F and then starts back at 0X01 to 0x09. It starts writing at register 0x02.

The thing that took me longest to figure out is that the i2c.write_i2c_block_data(address, cmd, list) and the i2c.read_i2c_block_data(address, cmd, list) commands ALWAYS write the CMD value to the first byte and then the list continues to update the rest. This took me a while to figure out. And cause me issues as I always had 0 in my cmd variable and it was thus writing 0 to register 0x02 and muting the sound.

I will post updated code once I get it working better and more user friendly. But this should get you actually hearing music. The board is only $20 and it sounds great with full stereo separation and high quality sound.

Please feel free to correct anything I could have done better. My first Pi project was a blast!

Code: Select all

#Python Version 2 code 
#The SI4703 reads starting at address 0x0A and starts writing to 0x02

#MANY THANKS to Nathan Seidle and his sketch example code at Sparkfun! I would have been lost without it. 
#http://www.sparkfun.com/datasheets/BreakoutBoards/Si4703_Example.pde

#Here is the SI4703 Datasheet that explains the registers
#http://www.sparkfun.com/datasheets/BreakoutBoards/Si4702-03-C19-1.pdf

#Here is the SI4703 programming guide
#http://www.silabs.com/Support%20Documents/TechnicalDocs/AN230.pdf

#This is not finished code and needs a lot of work. I just wanted to prove it can 
#work with the smbus to control the SI4703 2-wire interface. This code is just
#a proof of concept and really junk!

#Anytime you READ the regsters it writes the command byte to the first byte of the 
#SI4703 0x02 register. And then uses the list to write the 2nd bytes of register 0x02
#and continues until all the list is written!

#So the reg = i2c.read_i2c_block_data(address, zz, 32)  command
#writes the integer zz to the first byte of register 0x02.
# so after setting the first byte of 0x02 to what you want it to be you need to 
#make sure every write or read keeps populating that byte with what you need there

import RPi.GPIO as GPIO 
import smbus 
import time 
 
i2c = smbus.SMBus(1) #use 0 for older RasPi 
 
GPIO.setmode(GPIO.BCM) #board numbering
GPIO.setup(23, GPIO.OUT) 
GPIO.setup(0, GPIO.OUT)  #SDA or SDIO 
 
#put SI4703 into 2 wire mode (I2C) 
GPIO.output(0,GPIO.LOW) 
time.sleep(.1) 
GPIO.output(23, GPIO.LOW) 
time.sleep(.1) 
GPIO.output(23, GPIO.HIGH) 
time.sleep(.1) 
 
address = 0x10 #address of SI4703 from I2CDetect utility
 
print "Initial Register Readings" 
reg = i2c.read_i2c_block_data(address, 0, 32) 
print reg 
 
#write x8100 to reg 7 to activate oscellitor
list1 = [0,0,0,0,0,0,0,0,0,129,0] 
w6 = i2c.write_i2c_block_data(address, 0, list1) 
time.sleep(1) 
 
#write x4001 to reg 2 to turn off mute and activate IC
list1 = [1] 
#print list1 
w6 = i2c.write_i2c_block_data(address, 64, list1) 
time.sleep(.1) 
 
#write volume 
print "Doing VOlume lowest setting" 
list1 = [1,0,0,0,0,0,1] 
w6 = i2c.write_i2c_block_data(address, 64, list1) 
 
#write channel 
print "Setting Channel, pick a strong one" 
 
nc = 1011 #this is 101.1 The Fox In Kansas City Classic Rock!! 
nc *= 10  #this math is for USA FM only 
nc -= 8750 
nc /= 20 
 
list1 = [1,128, nc] 
#set tune bit and set channel 
w6 = i2c.write_i2c_block_data(address, 64, list1) 
time.sleep(1) #allow tuner to tune 
# clear channel tune bit 
list1 = [1,0,nc] 
w6 = i2c.write_i2c_block_data(address, 64, list1) 
 
reg2 = i2c.read_i2c_block_data(address,64, 32) 
print reg2  #just to show final register settings
 
#You should be hearing music now! 
#Headphone Cord acts as antenna

KansasCoder
Posts: 7
Joined: Tue Jan 08, 2013 7:45 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Sat Jan 12, 2013 8:44 pm

I will post more refined code soon!

mediakill
Posts: 22
Joined: Sun Jan 06, 2013 2:39 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Sun Jan 13, 2013 12:59 pm

This looks interesting. Please post a tutorial for the noobs like myself on hooking it up and running it. Thanks

eurico
Posts: 1
Joined: Mon Jan 14, 2013 10:30 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Mon Jan 14, 2013 10:55 pm

This is exactly what I was thinking on doing but like you I have no experience with electronics and I didn't know if it would ever work. Please post more details like photos or any other relevant details.
Do you output the sound from the SI4703 board directly to speakers ?

In addition to what you've done I want also to allow Pi to stream the sound via my home network. HAve you ever consider that?

cheers
Eurico

florin
Posts: 1
Joined: Wed Jan 23, 2013 11:49 am

Re: SI4703 FM Tuner Eval Board working with RasPi

Wed Jan 23, 2013 12:08 pm

Awesome post, thanks for the insight.
There's one puzzle I have though: as both i2c.read_i2c_block_data and i2c.write_i2c_block_data write the 0x02 register _and_ that register controls the SEEK bits, how does one uses the seek option of SI470x since the SEEK bit is set once then 0x0A should be polled, a read operation that will write reg 0x02 again?

Thanks!

KansasCoder
Posts: 7
Joined: Tue Jan 08, 2013 7:45 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Wed Jan 23, 2013 11:54 pm

florin wrote:Awesome post, thanks for the insight.
There's one puzzle I have though: as both i2c.read_i2c_block_data and i2c.write_i2c_block_data write the 0x02 register _and_ that register controls the SEEK bits, how does one uses the seek option of SI470x since the SEEK bit is set once then 0x0A should be polled, a read operation that will write reg 0x02 again?

Thanks!
All you have to do is make sure to use the current 0x02 value when you do the read so it will just write back the same value.

So if you read the registers and want decimal 64 in the upper 0x02 byte then reg2 = i2c.read_i2c_block_data(address,64, 32) would write 64 to that byte. The cmd parameter of the write command is writing the cmd value to the first byte.

Hammy
Posts: 1
Joined: Mon Jan 28, 2013 6:31 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Mon Jan 28, 2013 6:38 pm

Hey, I been following this project and am attempting to do it myself, I have bought the eval board and I have two questions for you,

The first is how did you protect your GPIOs from surges so that you did not damage your RPi?

The Second is when I try to run your code I get a syntax error about i2c and I was wondering if you had a similar issue?

KansasCoder
Posts: 7
Joined: Tue Jan 08, 2013 7:45 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Mon Jan 28, 2013 10:46 pm

Are you using Python 2.x? I could not get Python 3 to work with I2C.

Have you installed i2c?

There are some instructions here

http://www.instructables.com/id/Raspber ... 2C-Python/

Can you run i2cdetect? Remember, i2cdetect will not find the board until you put it into 2 wire (I2C) mode with code. But if you can run i2cdetect then i2c should be installed OK.

I did nothing special to protect the Pi Board. No issues for me so far.

Let me know if the above helps.

Some connection pics below.
Attachments
RadioCS.JPG
RadioCS.JPG (62.37 KiB) Viewed 50348 times
RadioBS.JPG
RadioBS.JPG (61.38 KiB) Viewed 50350 times
RadioA.JPG
RadioA.JPG (41.64 KiB) Viewed 50351 times

SaidTheHatter
Posts: 5
Joined: Wed Apr 10, 2013 11:44 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Wed Apr 10, 2013 11:50 pm

Could anyone possibly assist me connecting the FM receiver to the Pi? I have i2c set up and everything is connected properly (I think, anyway) but i2cdetect says nothing is connected. Do I need to set up the GPIO ports before running i2cdetect?

KansasCoder
Posts: 7
Joined: Tue Jan 08, 2013 7:45 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Thu Apr 11, 2013 1:05 am

Yes, remember, i2cdetect will not find the board until you put it into 2 wire (I2C) mode with code.
Look at the code I posted.

SaidTheHatter
Posts: 5
Joined: Wed Apr 10, 2013 11:44 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Thu Apr 11, 2013 8:45 pm

It gets to the line where reg is set to i2c.read, and then I get "IOError: [Errno 5] Input/output error" any idea what might be the cause of this?

KansasCoder
Posts: 7
Joined: Tue Jan 08, 2013 7:45 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Thu Apr 11, 2013 10:45 pm

Which line of code? Post the whole line.

SaidTheHatter
Posts: 5
Joined: Wed Apr 10, 2013 11:44 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Sat Apr 13, 2013 4:34 am

reg = i2c.read_i2c_block_data(address, 0, 32)

SaidTheHatter
Posts: 5
Joined: Wed Apr 10, 2013 11:44 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Sat Apr 13, 2013 5:07 am

I think I've gotten past that issue now (just recopied the code over and it worked) but now when I run i2cdetect it shows there is a device at every address, and running the code shows only 0s when reg is printed at the beginning and end.

SaidTheHatter
Posts: 5
Joined: Wed Apr 10, 2013 11:44 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Wed May 01, 2013 4:43 pm

For anyone else looking at using the SI4703 FM Tuner with the raspberry pi, I switched to a rev2 board and everything is working fine now. I might try to do some testing or find another rev1 board to see if it's just my Pi that isn't working, but I thought I would at least post that I've had trouble using the older board.

legarcia
Posts: 2
Joined: Sat Jul 27, 2013 4:41 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Sat Jul 27, 2013 6:16 pm

Hello Every Body

I wanted to share my 2 cents on the pitfalls that had to overcome in order to get this project working.

Originally I came into the same problem reported [by SaidTheHatter » Sat Apr 13, 2013 6:07 am] post.

Pitfall #1 Incorrect wiring:
1) Running the Pyton script using i2c = smbus.SMBus(1) [ "1" being that this one is a 512 MB PI] had IO error messages.
2) Running as SMBus(0) no error, but NO radio and, just like SaidTheHatter, a device showing up at every address (on another terminal window running on the side).

The problem was that I mistakenly used cardinal PIN 23 instead of GPIO23 (PIN#16). (KansasCoder breadboard picture clearly shows the right position). My dumb mistake fixed.

Wiring fixed, FM radio started working immediately.
gpio-pinout.jpg
gpio-pinout.jpg (63.04 KiB) Viewed 48030 times
Also worth mentioning, one of the first lines of code, after enabling GPIO23, issues a negative pulse for .1 sec and then keeps the pin at binary 1 so the python script does a clean reset into the device for starts.
Other initialization tasks are important as well, just wanted to point this one as it was the clue to solve pitfall #1.

Code: Select all

#...
GPIO.output(23, GPIO.LOW) 
time.sleep(.1) 
GPIO.output(23, GPIO.HIGH) 
time.sleep(.1)  # Allow time for reset to complete and keep it high
#...
 

Pitfall #2 Python exits and radio goes off.
Once the wiring got done right, the script ran with no errors and got sound on the head phone for a short 1 sec or 2.
When the Python script ended, I guess the GPIO got reset so the radio operation died.

By adding the following code at the end of the python script, the execution goes into an "infinite" loop which allows you to change radio stations. A non numeric value will produce an error and exit, or ctrl-C will terminate as well.

Also, while the new program was running, a terminal window on the side running sudo i2cdetect -y 1 showed the I2C bus all in order.

Thanks KansasCoder.

If somebody wants to create a fully featured Python program (volume control, seek station), please share at this space.

Code: Select all

# This python code portion adds at the bottom of KansasCoder Script


#You should be hearing music now! 
#Headphone Cord acts as antenna

var = 1
while var == 1 :
   num = raw_input("Enter FM Frequency  [for 105.3 enter 1053] :")
   print "Tuning: ", num
   nc = int(num)
   nc *= 10  #this math is for USA FM only, Greetings from San Jose California
   nc -= 8750 
   nc /= 20  
   print "Param: ", nc
   list1 = [1,128, nc] 
   #set tune bit and set channel 
   w6 = i2c.write_i2c_block_data(address, 64, list1) 
   time.sleep(.1) #allow tuner to tune 
   # clear channel tune bit 
   list1 = [1,0,nc] 
   w6 = i2c.write_i2c_block_data(address, 64, list1) 
   reg2 = i2c.read_i2c_block_data(address,64, 32) 
   print reg2  #Your newly entered Freq Param value will show up happily sitting at the 4th position of the registers array
   
Picture shows ![RST] pin tied correctly to GPIO23:


Thanks!

LE+
Attachments
PiFM.jpg
PiFM.jpg (63.1 KiB) Viewed 48030 times

funkeywoookey
Posts: 7
Joined: Fri Jun 07, 2013 5:22 am

Re: SI4703 FM Tuner Eval Board working with RasPi

Thu Aug 01, 2013 5:43 am

Legarcia, your post was extremely helpful! Thank you very much! I've been trying to do the same. Not sure why, but I hadn't had much success until your post.

My major pitfall at the moment is trying to make sense of the initial code. I completely understand how and i2c bus works physically, but after seeing the code, I'm completely lost.

Code: Select all

#write x8100 to reg 7 to activate oscellitor
list1 = [0,0,0,0,0,0,0,0,0,129,0] 
w6 = i2c.write_i2c_block_data(address, 0, list1) 
I have no idea how 8100 or register 7 relates to these lines of code

Code: Select all

#write x4001 to reg 2 to turn off mute and activate IC
list1 = [1] 
#print list1 
w6 = i2c.write_i2c_block_data(address, 64, list1)
Same thing here: How does this specify register 2 and write "4001"

I've been trying to figure this out. Most example code I come across explicitly defines the register being written along with the data. They also only write ic2_byte_data. I'm not sure what the difference between byte data and block data is. I even asked some family members who were programmers and they had no clue. They couldn't even begin to tell me why the code worked.Surely a genius out there like KansasCoder will know.

funkeywoookey
Posts: 7
Joined: Fri Jun 07, 2013 5:22 am

Re: SI4703 FM Tuner Eval Board working with RasPi

Thu Aug 22, 2013 4:36 pm

I've finally got a working module for the si4703.

Like above, the pin connections are as follows:
VIO = 3.3v
SDA = SDA
SCL = SCL
RST = GPIO 23

The code functions as you would expect a radio to work. The following functions are available for end-user consumption:
init() ## This must be the first thing you do to power up the si4703
toggle_mute()
seek_left()
seek_right()
tune_left()
tune_right() ## These are analogous to the tuning dial
volume(integer) ## Volume must be an integer from 0-15. 0 is Mute.
tune(frequency) ##To tune to 98.5 KVOO use tune(98.5)
print_registry() ## Prints all 16 registers to the screen




I'm currently working on the following features:
Saving the current configuration to file and loading it during the init() script
Decoding RDS data to display radio station and song data

Code: Select all

class si4703:
    '''
    Each sub-class in this file represents a register within the si4703 capable of being read.
    Not all registers are capable of being written to
    Some bits are capable of being written to, but should always be set to zero. Such registers have commented placeholders.
    '''
    import smbus

    i2c = smbus.SMBus(1)

    print "test2"
    def init(self):
        '''
        This is the initial setup for the Si4703. The encompasses setting up the GPIO pins on the Pi as the protocol for putting the Si4703 is 2-wire (read: "SMBus") mode
        The protocl is covered in the programming guide for the Si4703 dubbed "AN230"
        http://www.silabs.com/Support%20Documents/TechnicalDocs/AN230.pdf
        '''
        from time import sleep
        import RPi.GPIO as GPIO
        
        GPIO.setmode(GPIO.BCM)  ## We will use board numbering instead of pin numbering. 
        GPIO.setup(23 ,GPIO.OUT) ## RST
        GPIO.setup(0 ,GPIO.OUT)  ## SDA

        GPIO.output(0 ,GPIO.LOW) ## This block of code puts the Si4703 into 2-wire mode per instructions on page 7  of AN230
        sleep(.1)                        ## Actually, I'm not sure how this block of code works. According to the aforementioned page 7, it shouldn't
        GPIO.output(23 ,GPIO.LOW) ## But I ripped this code off somewhere else, so I'm not gonna bash it. Great work to the OP
        sleep(.1)
        GPIO.output(23 ,GPIO.HIGH)
        sleep(0.5)
        
        si4703.update_registry(self)
        si4703.TEST_1.POWER_SEQUENCE = True
        si4703.write_registry(self)
        si4703.TEST_1.POWER_SEQUENCE = False
        sleep(1)
        
        si4703.POWER_CONFIG.DMUTE = 1
        si4703.POWER_CONFIG.ENABLE = 1
        si4703.write_registry(self)
        sleep(1)

        si4703.SYS_CONFIG_2.VOLUME = 15
        si4703.write_registry(self)
        sleep(0.1)
      
        si4703.CHANNEL.CHAN = 85
        si4703.write_registry(self)
        sleep(0.1)
        si4703.CHANNEL.TUNE = 1
        si4703.write_registry(self)

        while si4703.STATUS_RSSI.STC == 0:
            sleep(0.1)
            si4703.update_registry(self)
        si4703.CHANNEL.TUNE = 0
        si4703.write_registry(self)
        
    def convert_reg_readings (self, old_registry, reorder):
        '''
        (list, int) ->>> list
        When Python reads  the registry of the si4703 it places all 16 registers into a 32 item list (each item being a single byte). 
        This functioon gives each register its own item.
        The register at index 0 is 0x0A. If reorder is set to 1, then index 0 will be 0x00. 
        '''
        i = 0
        response = []
        while i <=31:
            first_byte = str(bin(old_registry[i]))
            second_byte = str(bin(old_registry[i+1]))
            
            first_byte = first_byte.replace("0b", "", 1)
            second_byte = second_byte.replace("0b", "", 1)

            while len(first_byte) < 8:
                first_byte = "0" + first_byte
            while len(second_byte) < 8:
                second_byte = "0" + second_byte
                
            full_register = first_byte + second_byte
            full_register = int(full_register, 2)
            
            response.append(full_register)
            i += 2
            
        if reorder == 1:
            response = si4703.reorder_reg_readings(self, response)

        return response

    def reorder_reg_readings (self, sixteen_item_list):
        '''
        Since the si4703 starts reading at register 0x0A and wraps back around at 0x0F, the data can be hard to understand.
        This re-orders the data such that the first itme in the list is 0x00, the second item is 0x01.....twelfth item is 0x0C
        '''
        original = sixteen_item_list
        response = []

        ##The item at index 6  is register 0x00
        response.append(original[6])    #0x00
        response.append(original[7])    #0x01
        response.append(original[8])    #0x02
        response.append(original[9])    #0x03
        response.append(original[10])   #0x04
        response.append(original[11])   #0x05
        response.append(original[12])   #0x06
        response.append(original[13])   #0x07
        response.append(original[14])   #0x08
        response.append(original[15])   #0x09
        response.append(original[0])    #0x0A
        response.append(original[1])    #0x0B
        response.append(original[2])    #0x0C
        response.append(original[3])    #0x0D
        response.append(original[4])    #0x0E
        response.append(original[5])    #0x0F

        return response
    def update_registry (self):
        '''
        This method reads all registers from the Si4703, and stores then into a 32 item array
        Then, it converts the 32 items into a 16-item array (1 item for each of the 16 registers)
        Since for some odd reason the Si4703 always starts reading at 0x0A, this method also reorders the last array such that its first item is 0x00
        Finally this method parses the data into local memory
        '''
        raw_data = []
        cmd = str(si4703.POWER_CONFIG.DSMUTE) + str(si4703.POWER_CONFIG.DMUTE) + str(si4703.POWER_CONFIG.MONO) + "0" + str(si4703.POWER_CONFIG.RDSM) + str(si4703.POWER_CONFIG.SKMODE) + str(si4703.POWER_CONFIG.SEEKUP) + str(si4703.POWER_CONFIG.SEEK)
        cmd = int(cmd, 2)
        try:
            raw_data = si4703.i2c.read_i2c_block_data(0x10,cmd,32)
        except:
            print "Exception in method 'update_registry' while trying to read from si4703"
        reordered_registry = si4703.convert_reg_readings(self, raw_data, 1)
        
        current_reg = [] 
        ## DEVICE_ID                    #0x00
        current_reg = reordered_registry[0]
        si4703.DEVICE_ID.PN = si4703.get_reg_value(self, current_reg, 0, 4)
        si4703.DEVICE_ID.MFGID = si4703.get_reg_value(self, current_reg, 4, 12)

        ## CHIP_ID                      #0x01
        current_reg = reordered_registry[1]
        si4703.CHIP_ID.REV = si4703.get_reg_value(self, current_reg, 0, 6)
        si4703.CHIP_ID.DEV = si4703.get_reg_value(self, current_reg, 6, 4)
        si4703.CHIP_ID.FIRMWARE = si4703.get_reg_value(self, current_reg, 10, 6)

        ## POWER_CONFIG                 #0x02
        current_reg = reordered_registry[2]
        si4703.POWER_CONFIG.DSMUTE = si4703.get_reg_value(self, current_reg, 0, 1)
        si4703.POWER_CONFIG.DMUTE = si4703.get_reg_value(self, current_reg, 1, 1)
        si4703.POWER_CONFIG.MONO = si4703.get_reg_value(self, current_reg, 2, 1)
        ##si4703.POWER_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 3, 1)
        si4703.POWER_CONFIG.RDSM = si4703.get_reg_value(self, current_reg, 4, 1)
        si4703.POWER_CONFIG.SKMODE = si4703.get_reg_value(self, current_reg, 5, 1)
        si4703.POWER_CONFIG.SEEKUP = si4703.get_reg_value(self, current_reg, 6, 1)
        si4703.POWER_CONFIG.SEEK = si4703.get_reg_value(self, current_reg, 7, 1)
        ##si4703.POWER_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 8, 1)
        si4703.POWER_CONFIG.DISABLE = si4703.get_reg_value(self, current_reg, 9, 1)
        ##si4703.POWER_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 10, 1)
        ##si4703.POWER_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 11, 1)
        ##si4703.POWER_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 12, 1)
        ##si4703.POWER_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 13, 1)
        ##si4703.POWER_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 14, 1)
        si4703.POWER_CONFIG.ENABLE = si4703.get_reg_value(self, current_reg, 15, 1)

        ## CHANNEL                      #0x03
        current_reg = reordered_registry[3]
        si4703.CHANNEL.TUNE = si4703.get_reg_value(self, current_reg, 0, 1)
        ##si4703.CHANNEL.UNUSED = si4703.get_reg_value(self, current_reg, 1, 1)
        ##si4703.CHANNEL.UNUSED = si4703.get_reg_value(self, current_reg, 2, 1)
        ##si4703.CHANNEL.UNUSED = si4703.get_reg_value(self, current_reg, 3, 1)
        ##si4703.CHANNEL.UNUSED = si4703.get_reg_value(self, current_reg, 4, 1)
        ##si4703.CHANNEL.UNUSED = si4703.get_reg_value(self, current_reg, 5, 1)
        si4703.CHANNEL.CHAN = si4703.get_reg_value(self, current_reg, 6, 10)

        ## SYS_CONFIG_1                 0x04
        current_reg = reordered_registry[4]
        si4703.SYS_CONFIG_1.RDSIEN = si4703.get_reg_value(self, current_reg, 0, 1)
        si4703.SYS_CONFIG_1.STCIEN = si4703.get_reg_value(self, current_reg, 1, 1)
        ##si4703.SYS_CONFIG_1.UNUSED = si4703.get_reg_value(self, current_reg, 2, 1)
        si4703.SYS_CONFIG_1.RDS = si4703.get_reg_value(self, current_reg, 3, 1)
        si4703.SYS_CONFIG_1.DE = si4703.get_reg_value(self, current_reg, 4, 1)
        si4703.SYS_CONFIG_1.AGCD = si4703.get_reg_value(self, current_reg, 5, 1)
        ##si4703.SYS_CONFIG_1.UNUSED = si4703.get_reg_value(self, current_reg, 6, 1)
        ##si4703.SYS_CONFIG_1.UNUSED = si4703.get_reg_value(self, current_reg, 7, 1)
        si4703.SYS_CONFIG_1.BLNDADJ = si4703.get_reg_value(self, current_reg, 8, 2)
        si4703.SYS_CONFIG_1.GPIO3 = si4703.get_reg_value(self, current_reg, 10, 2)
        si4703.SYS_CONFIG_1.GPIO2 = si4703.get_reg_value(self, current_reg, 12, 2)
        si4703.SYS_CONFIG_1.GPIO1 = si4703.get_reg_value(self, current_reg, 14, 2)

        ## SYS_CONFIG_2                 0x05
        current_reg = reordered_registry[5]
        si4703.SYS_CONFIG_2.SEEKTH = si4703.get_reg_value(self, current_reg, 0, 8)
        si4703.SYS_CONFIG_2.BAND = si4703.get_reg_value(self, current_reg, 8, 2)
        si4703.SYS_CONFIG_2.SPACE = si4703.get_reg_value(self, current_reg, 10, 2)
        si4703.SYS_CONFIG_2.VOLUME = si4703.get_reg_value(self, current_reg, 12, 4)

        ## SYS_CONFIG_3                 0x06
        current_reg = reordered_registry[6]
        si4703.SYS_CONFIG_3.SMUTER = si4703.get_reg_value(self, current_reg, 0, 2)
        si4703.SYS_CONFIG_3.SMUTEA = si4703.get_reg_value(self, current_reg, 2, 2)
        ##si4703.SYS_CONFIG_3.UNUSED = si4703.get_reg_value(self, current_reg, 4, 1)
        ##si4703.SYS_CONFIG_3.UNUSED = si4703.get_reg_value(self, current_reg, 5, 1)
        ##si4703.SYS_CONFIG_3.UNUSED = si4703.get_reg_value(self, current_reg, 6, 1)
        si4703.SYS_CONFIG_3.VOLEXT = si4703.get_reg_value(self, current_reg, 7, 1)
        si4703.SYS_CONFIG_3.SKSNR = si4703.get_reg_value(self, current_reg, 8, 4)
        si4703.SYS_CONFIG_3.SKCNT = si4703.get_reg_value(self, current_reg, 12, 4)

        ## TEST_1                       0x07
        current_reg = reordered_registry[7]
        si4703.TEST_1.XOSCEN = si4703.get_reg_value(self, current_reg, 0, 1)
        si4703.TEST_1.AHIZEN = si4703.get_reg_value(self, current_reg, 1, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 2, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 3, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 4, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 5, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 6, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 7, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 8, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 9, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 10, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 11, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 12, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 13, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 14, 1)
        ##si4703.TEST_1.UNUSED = si4703.get_reg_value(self, current_reg, 15, 1)

        ## TEST_2                       0x08        ALL BITS UNUSED
        current_reg = reordered_registry[8]
        ##si4703.TEST_2.UNUSED = si4703.get_reg_value(self, current_reg, 0, 16)

        ## BOOT_CONFIG                  0x09        ALL BITS UNUSED
        current_reg = reordered_registry[9]
        ##si4703.BOOT_CONFIG.UNUSED = si4703.get_reg_value(self, current_reg, 0, 16)

        ## STATUS_RSSI                  0x0A
        current_reg = reordered_registry[10]
        si4703.STATUS_RSSI.RDSR = si4703.get_reg_value(self, current_reg, 0, 1)
        si4703.STATUS_RSSI.STC = si4703.get_reg_value(self, current_reg, 1, 1)
        si4703.STATUS_RSSI.SFBL = si4703.get_reg_value(self, current_reg, 2, 1)
        si4703.STATUS_RSSI.AFCRL = si4703.get_reg_value(self, current_reg, 3, 1)
        si4703.STATUS_RSSI.RDSS = si4703.get_reg_value(self, current_reg, 4, 1)
        si4703.STATUS_RSSI.BLER_A = si4703.get_reg_value(self, current_reg, 5, 2)
        si4703.STATUS_RSSI.ST = si4703.get_reg_value(self, current_reg, 7, 1)
        si4703.STATUS_RSSI.RSS = si4703.get_reg_value(self, current_reg, 8, 8)

        ## READ_CHAN                    0x0B
        current_reg = reordered_registry[11]
        si4703.READ_CHAN.BLER_B = si4703.get_reg_value(self, current_reg, 0, 2)
        si4703.READ_CHAN.BLER_C = si4703.get_reg_value(self, current_reg, 2, 2)
        si4703.READ_CHAN.BLER_D = si4703.get_reg_value(self, current_reg, 4, 2)
        si4703.READ_CHAN.READ_CHAN = si4703.get_reg_value(self, current_reg, 6, 10)

        ## RDS_A                        0x0C
        current_reg = reordered_registry[12]
        si4703.RDS_A.RDS_A = si4703.get_reg_value(self, current_reg, 0, 16)

        ## RDS_B                        0x0D
        current_reg = reordered_registry[13]
        si4703.RDS_B.RDS_B = si4703.get_reg_value(self, current_reg, 0, 16)

        ## RDS_C                        0x0E
        current_reg = reordered_registry[14]
        si4703.RDS_C.RDS_C = si4703.get_reg_value(self, current_reg, 0, 16)

        ## RDS_D                        0x0F
        current_reg = reordered_registry[15]
        si4703.RDS_D.RDS_D = si4703.get_reg_value(self, current_reg, 0, 16)


    def get_reg_value(self, register, begin, length):
        '''
        This class continually has to copy the contents of the Si4703's registers to the Pi's internal memory. This function helps parse the data from the Si4703.
        In order to parse the data, we need the raw data, along with the location of a property and its length in bits (ie READCHAN's location would be index 6 and its length would be 9 bits)

        Internally, Python converts any hex, octal, or binary number into an integer for storage. This is why the register is represented as an integer at first. We then convert it to a string of nothing but 1s and 0s, and add extra zeros until it is 16 bits long.
        After doing this, we can use the location and length information to return the value of a property.
        '''
        int_register = register                        ##give this a friendlier name
        str_register = str(bin(int_register))          ##Convert the register to a string (ie 15 becomes "0b1111") 
        str_register = str_register.replace("0b", "")  ##Get rid of the "0b" prefix (ie 15 would now become "1111") 
        while len(str_register) < 16:                  ##We want the output to be 16 bits long 
            str_register = "0" + str_register          ##Add preceeding zeros until it IS 16 characters long
        response = str_register[begin : begin + length]##Weed out all the bits we don't need
        response = int(response, 2)                    ##Convert it back to an assignable integer
        
        return response

    def write_registry(self):
        '''
        Refreshes the registers on the device with the ones stored in local memory on the Pi.
        It will only refresh the registers 0x02-07, as all other registers cannot be written to
        '''
        main_list = []
        crazy_first_number = 0
        
        first_byte = 0
        second_byte = 0

        ## POWER_CONFIG                 #0x02
        first_byte = str(si4703.POWER_CONFIG.DSMUTE) + str(si4703.POWER_CONFIG.DMUTE) + str(si4703.POWER_CONFIG.MONO) + "0" + str(si4703.POWER_CONFIG.RDSM) + str(si4703.POWER_CONFIG.SKMODE) + str(si4703.POWER_CONFIG.SEEKUP) + str(si4703.POWER_CONFIG.SEEK)
        second_byte = "0" + str(si4703.POWER_CONFIG.DISABLE) + "00000" + str(si4703.POWER_CONFIG.ENABLE)
        first_byte = int(first_byte, 2)
        crazy_first_number = first_byte
        second_byte = int(second_byte, 2)
        main_list.append(second_byte)

        ## CHANNEL                      #0x03
        first_byte = str(si4703.CHANNEL.TUNE) + "0000000"
        second_byte =si4703.return_with_padding(self, si4703.CHANNEL.CHAN, 10)
        first_byte = int(first_byte, 2)
        second_byte = int(second_byte, 2)
        main_list.append(first_byte)
        main_list.append(second_byte)

        ## SYS_CONFIG_1                 0x04
        first_byte = str(si4703.SYS_CONFIG_1.RDSIEN) + str(si4703.SYS_CONFIG_1.STCIEN) + "0" + str(si4703.SYS_CONFIG_1.RDS) + str(si4703.SYS_CONFIG_1.DE) + str(si4703.SYS_CONFIG_1.AGCD) + "00"
        second_byte = si4703.return_with_padding(self, si4703.SYS_CONFIG_1.BLNDADJ, 2) + si4703.return_with_padding(self, si4703.SYS_CONFIG_1.GPIO3, 2) + si4703.return_with_padding(self, si4703.SYS_CONFIG_1.GPIO2, 2) + si4703.return_with_padding(self, si4703.SYS_CONFIG_1.GPIO1, 2)
        first_byte = int(first_byte, 2)
        second_byte = int(second_byte, 2)
        main_list.append(first_byte)
        main_list.append(second_byte)

        ## SYS_CONFIG_2                 0x05
        first_byte = si4703.return_with_padding(self, si4703.SYS_CONFIG_2.SEEKTH, 8)
        second_byte = si4703.return_with_padding(self, si4703.SYS_CONFIG_2.BAND, 2) + si4703.return_with_padding(self, si4703.SYS_CONFIG_2.SPACE, 2) + si4703.return_with_padding(self, si4703.SYS_CONFIG_2.VOLUME, 4)
        first_byte = int(first_byte, 2)
        second_byte = int(second_byte, 2)
        main_list.append(first_byte)
        main_list.append(second_byte)

        ## SYS_CONFIG_3                 0x06
        first_byte = si4703.return_with_padding(self, si4703.SYS_CONFIG_3.SMUTER, 2) + si4703.return_with_padding(self, si4703.SYS_CONFIG_3.SMUTEA, 2) + "000" + str(si4703.SYS_CONFIG_3.VOLEXT)
        second_byte = si4703.return_with_padding(self, si4703.SYS_CONFIG_3.SKSNR, 4) + si4703.return_with_padding(self, si4703.SYS_CONFIG_3.SKCNT, 4)
        first_byte = int(first_byte, 2)
        second_byte = int(second_byte, 2)
        main_list.append(first_byte)
        main_list.append(second_byte)
        
        ## TEST_1                       0x07
        if si4703.TEST_1.POWER_SEQUENCE == 55:   ##Since all but the first two bits in this register are unused, and we only write to this to power up/down the device, it seems unessary to write this registry every time. Especially considering that writing 0 to the remaining register while in operation can prove fatal
            first_byte = str(si4703.TEST_1.XOSCEN) + str(si4703.TEST_1.AHIZEN) + si4703.return_with_padding(self, si4703.TEST_1.RESERVED_FIRST_BYTE, 4)
            second_byte = si4703.return_with_padding(self, si4703.TEST_1.RESERVED_SECOND_BYTE, 8)
            first_byte = int(first_byte, 2)
            second_byte = int(second_byte, 2)
            main_list.append(first_byte)
            main_list.append(second_byte)
        if si4703.TEST_1.POWER_SEQUENCE == True:##debug code for TEST_1. remove after debugging
            main_list.append(129) 

        print main_list
        print crazy_first_number
        w6 = si4703.i2c.write_i2c_block_data(0x10, crazy_first_number, main_list)
        si4703.update_registry(self)

    def return_with_padding (self, item_as_integer, length):
        item_as_integer = str(bin(item_as_integer))
        item_as_integer = item_as_integer.replace("0b", "")

        while len(item_as_integer) < length:
            item_as_integer = "0" + item_as_integer

        return item_as_integer
    def tune (self, frequency):
        '''
        Frequency (Mhz) (ie 98.5 tunes to 98.5; 104.5 tunes to 104.5)
        The si4703 doesn't use verbatim tuning. Such that you cannot tune to 104.5 by setting CHAN to 1045.
        Instead, setting CHAN to 0 will tune to the lowest frequency allowable for your region. Your region is set by setting BAND.
        Setting CHAN to 1 will tune to the lowest frequency allowable + spacing
        ie
        BAND = 0        ## 87.5-108.0 (default)
        SPACE = 0       ## 200 Khz (default)
        CHAN = 1
        The tuned frequency would be 87.7
        '''
        from time import sleep, time
        
        frequency = float(frequency)
        channel = 0
        spacing = 0

        if si4703.SYS_CONFIG_2.SPACE == 0:  # Typical spacing for USA & Australia (default) - 200 Khz or 0.2 Mhz
            spacing = 20
        elif si4703.SYS_CONFIG_2.SPACE == 1:# Typical spacing for Europe & Japan            - 100 Khz or 0.1 Mhz
            spacing = 10
        elif si4703.SYS_CONFIG_2.SPACE == 3:# Minimum spacing allowed                       -  50 Khz or 0.05 Mhz
            spacing = 5
            
        if si4703.SYS_CONFIG_2.BAND == 0:   # 87.5-108.0 Mhz for USA/Europe
            channel = ((frequency*100)-8750)/spacing
        else:                               # 76.0-108.0 Mhz(Japan wide-band) or 76.0-90.0 Mhz (Japan)
            channel = ((frequency*100)-7600)/spacing # These fall into the same if statement because the begining of the bands both start at 76.0 Mhz

        channel = int(channel)              #We turned this into a float earlier for some proper division, so we better turn it back to an int before we try to write it

        si4703.CHANNEL.TUNE = 1             #Must set the TUNE bit in order to perform a tune 
        si4703.CHANNEL.CHAN = channel       #Also, we need to tell it what to tune to
        si4703.write_registry(self)         #Now write all of this to the si4703

        si4703.wait_for_tune(self)
            
        si4703.CHANNEL.TUNE = 0             #In order to do anything else, we have to clear the TUNE bit once the device successfully (or unsuccessfully for that matter) tuned
        si4703.write_registry(self)

    def wait_for_tune (self):
        from time import sleep, time
        
        begining_time = time()
        
        while si4703.STATUS_RSSI.ST == 0:   #The si4703 sets the ST bit high whenever it is finished tuning/seeking
            if time() - begining_time > 0.9:  #If we've been in this loop for more than two seconds, then get out
                break
            sleep(0.1)                      #This is only precautionary. We don't want to overload anything by reading/writing over and over and back to back
            si4703.update_registry(self)

    def seek_right (self):
        '''
        Seeks to the closest station above the currently tuned station.
        '''
        si4703.update_registry(self)
        
        si4703.POWER_CONFIG.SEEKUP = 1
        si4703.POWER_CONFIG.SEEK = 1
        si4703.write_registry(self)

        si4703.wait_for_tune(self)

        si4703.POWER_CONFIG.SEEK = 0
        si4703.write_registry(self)
    def seek_left (self):
        '''
        Seeks to the closest station below the currently tuned station.
        '''
        si4703.update_registry(self)
        
        si4703.POWER_CONFIG.SEEKUP = 0
        si4703.POWER_CONFIG.SEEK = 1
        si4703.write_registry(self)

        si4703.wait_for_tune(self)

        si4703.POWER_CONFIG.SEEK = 0
        si4703.write_registry(self)

    def get_channel (self):
        '''
        get_channel() ->> float
        Returns the frequency currenly tuned to
        Since the CHAN property is only a valid property after a tune operation and not a seek operation, we must use the READ_CHAN property to get the frequency
        Also, just like CHAN, the frequency isn't verbatim and is encoded somewhat such that if READ_CHAN = 0 then the frequency is the lowest possible frequency for the reciever
        '''
        channel = si4703.READ_CHAN.READ_CHAN
        spacing = 0
        frequency = 0.0

        if si4703.SYS_CONFIG_2.SPACE == 0:  # Typical spacing for USA & Australia (default) - 200 Khz or 0.2 Mhz
            spacing = 20
        elif si4703.SYS_CONFIG_2.SPACE == 1:# Typical spacing for Europe & Japan            - 100 Khz or 0.1 Mhz
            spacing = 10
        elif si4703.SYS_CONFIG_2.SPACE == 3:# Minimum spacing allowed                       -  50 Khz or 0.05 Mhz
            spacing = 5
            
        if si4703.SYS_CONFIG_2.BAND == 0:   # 87.5-108.0 Mhz for USA/Europe
            frequency = (((channel * spacing) + 8750) / 100.0)
        else:                               # 76.0-108.0 Mhz(Japan wide-band) or 76.0-90.0 Mhz (Japan)
            frequency = (((channel * spacing) + 8750) / 100.0)# These fall into the same if statement because the begining of the bands both start at 76.0 Mhz
        return frequency

    def toggle_mute (self):
        '''
        Toggles the mute feature. The si4703 is muted by default once the device is enabled.
        Changing the mute feature is done by chaning the Disable Mute property (DMUTE)
        '''
        if si4703.POWER_CONFIG.DMUTE == 0:  
            si4703.POWER_CONFIG.DMUTE = 1
        else:
            si4703.POWER_CONFIG.DMUTE = 0
        
        si4703.write_registry(self)
    def volume (self, volume)
        '''
        Int must be a integer no lower than 0 and no higher than 15
        '''
        if 0 <= volume <= 15:   ## Did the user provide a proper value between 0 and 15?
            pass
        elif volume < 0:        ## Well at this point they didn't give us a good volume integer, so we're going to mute it if it's too low
            volume = 0
        else:                   ## And in the last case, where it is too high, we'll set it to the highest possible setting
            volume = 15
            
        si4703.SYS_CONFIG_2.VOLUME = int ## Finally, let's save all that work
        si4703.write_registry()          ## And then write it in stone

    def tune_left (self):
        '''
        Analogous to a tuning dial, this tunes to the next available frequency on the left of the current station. 
        '''
        current_channel = si4703.get_channel(self)
        if si4703.SYS_CONFIG_2.SPACE == 0:
            current_channel -= 0.2
        elif si4703.SYS_CONFIG_2 == 1:
            current_channel -= 0.1
        else:
            current_channel -= 0.05

        si4703.tune(self, current_channel)
    def tune_right (self):
        '''
        Analogous to a tuning dial, this tunes to the next available frequency on the right of the current station. 
        '''
        current_channel = si4703.get_channel(self)
        if si4703.SYS_CONFIG_2.SPACE == 0:
            current_channel += 0.2
        elif si4703.SYS_CONFIG_2 == 1:
            current_channel += 0.1
        else:
            current_channel += 0.05

        si4703.tune(self, current_channel)
    def print_registry(self):
        print"POWER_CONFIG:"
        print"\t", "DSMUTE:", si4703.POWER_CONFIG.DSMUTE
        print"\t", "DMUTE:", si4703.POWER_CONFIG.DMUTE
        print"\t", "MONO:", si4703.POWER_CONFIG.MONO
        print"\t", "RDSM:", si4703.POWER_CONFIG.RDSM
        print"\t", "SKMODE:", si4703.POWER_CONFIG.SKMODE
        print"\t", "SEEKUP:", si4703.POWER_CONFIG.SEEKUP
        print"\t", "SEEK:", si4703.POWER_CONFIG.SEEK
        print"\t", "DISABLE:", si4703.POWER_CONFIG.DISABLE
        print"\t", "ENABLE:", si4703.POWER_CONFIG.ENABLE
        
        print"CHANNEL:"
        print"\t", "TUNE:", si4703.CHANNEL.TUNE 
        fmchan = si4703.CHANNEL.CHAN
        fmchan = (((fmchan*20)+8750)/10)
        print"\t", "CHAN:", si4703.CHANNEL.CHAN, "(", fmchan, ")"
        
        print"SYS_CONFIG_1:"
        print"\t", "RDSIEN:", si4703.SYS_CONFIG_1.RDSIEN
        print"\t", "STCIEN:", si4703.SYS_CONFIG_1.STCIEN
        print"\t", "RDS:", si4703.SYS_CONFIG_1.RDS
        print"\t", "DE:", si4703.SYS_CONFIG_1.DE
        print"\t", "AGCD:", si4703.SYS_CONFIG_1.AGCD
        print"\t", "BLNDADJ:", si4703.SYS_CONFIG_1.BLNDADJ
        print"\t", "GPIO3:", si4703.SYS_CONFIG_1.GPIO3
        print"\t", "GPIO2:", si4703.SYS_CONFIG_1.GPIO2
        print"\t", "GPIO1:", si4703.SYS_CONFIG_1.GPIO1
        
        print"SYS_CONFIG_2"
        print"\t", "SEEKTH:", si4703.SYS_CONFIG_2.SEEKTH
        print"\t", "BAND:", si4703.SYS_CONFIG_2.BAND
        print"\t", "SPACE:", si4703.SYS_CONFIG_2.SPACE
        print"\t", "VOLUME:", si4703.SYS_CONFIG_2.VOLUME
        
        print"SYS_CONFIG_3"
        print"\t", "SMUTER:", si4703.SYS_CONFIG_3.SMUTER
        print"\t", "SMUTEA:", si4703.SYS_CONFIG_3.SMUTEA
        print"\t","VOLEXT:", si4703.SYS_CONFIG_3.VOLEXT
        print"\t", "SKSNR:", si4703.SYS_CONFIG_3.SKSNR
        print"\t", "SKCNT:", si4703.SYS_CONFIG_3.SKCNT
        print"TEST_1"
        print"\t", "XOSCEN:", si4703.TEST_1.XOSCEN
        print"\t", "AHIZEN:", si4703.TEST_1.AHIZEN
        print"\t", "RESERVED_FIRST_BYTE:", si4703.TEST_1.RESERVED_FIRST_BYTE
        print"\t", "RESERVED_SECOND_BYTE:", si4703.TEST_1.RESERVED_SECOND_BYTE
      
    class DEVICE_ID:                    #0x00
        PN     = 0
        MFGID  = 0
    class CHIP_ID:                      #0x01
        REV    = 0
        DEV    = 0
        FIRMWARE=0
    class POWER_CONFIG:                 #0x02
        DSMUTE = 0
        DMUTE  = 0
        MONO   = 0
        ##     = 0
        RDSM   = 0
        SKMODE = 0
        SEEKUP = 0
        SEEK   = 0
        ##     = 0
        DISABLE= 0
        ##     = 0
        ##     = 0
        ##     = 0
        ##     = 0
        ##     = 0
        ENABLE = 0
        def FULL_REGISTER (self):  
            first_byte = str(DSMUTE) + str(DMUTE) + str(MONO) + "0" + str(RDSM) + str(SKMODE) + str(SEEKUP) + str(SEEK)
            first_byte = int(first_byte, 2)
            
            second_byte = "0" + str(DISABLE) + "00000" + str(ENABLE)
            second_byte = int(second_byte, 2)
            return [first_byte, second_byte]

    class CHANNEL:                      #0x03
        TUNE   = 0
        ##     = 0
        ##     = 0
        ##     = 0
        ##     = 0
        ##     = 0
        CHAN   = 0
    class SYS_CONFIG_1:                 #0x04
        RDSIEN = 0
        STCIEN = 0
        ##     = 0
        RDS    = 0
        DE     = 0
        AGCD   = 0
        ##     = 0
        ##     = 0
        BLNDADJ= 0
        GPIO3  = 0
        GPIO2  = 0
        GPIO1  = 0
    class SYS_CONFIG_2:                 #0x05
        SEEKTH = 0
        BAND   = 0
        SPACE  = 0
        VOLUME = 0
    class SYS_CONFIG_3:                 #0x06
        SMUTER = 0
        SMUTEA = 0
        ##     =
        ##     =
        ##     = 0
        VOLEXT = 0
        SKSNR  = 0
        SKCNT  = 0
    class TEST_1:                       #0x07
        XOSCEN = 0
        AHIZEN = 0
        RESERVED_FIRST_BYTE = 0 ## These bits are reserved, but their reset values are known, so they must be set-able 
        RESERVED_SECOND_BYTE = 0
        POWER_SEQUENCE = False
    class TEST_2:                       #0x08
        TEST_2 = 0
        ##ALL BITS IN THIS REGISTER ARE UNUSED
    class BOOT_CONFIG:                  #0x09
        BOOT_CONFIG = 0
        ##ALL BITS IN THIS REGISTER ARE UNUSED
    class STATUS_RSSI:                  #0x0A
        RDSR   = 0
        STC    = 0
        SFBL   = 0
        AFCRL  = 0
        RDSS   = 0
        BLER_A = 0
        ST     = 0
        RSSI   = 0
    class READ_CHAN:                    #0x0B
        BLER_B = 0 ##SEE THE STATUS_RSI REGISTER ABOVER FOR BLER-A
        BLER_C = 0
        BLER_D = 0
        READ_CHAN = 0
    class RDS_A:                        #0x0C
        RDS_A  = 0
    class RDS_B:                        #0x0D
        RDS_B  = 0
    class RDS_C:                        #0x0E
        RDS_C  = 0
    class RDS_D:                        #0x0F
        RDS_D  = 0
Save the class in a file named si4703.py in your Pi folder
and then run the following from IDLE:

Code: Select all

import si4703
fm = si4703.si4703()
fm.init()
Further documentation:
In the programming guide it mentions always having a "shadow" or a copy of all the registers on the si4703. The class does precisely that by representing each register as a sub-class. In this way, you can refer to a property from the Si4703 with dot notation:

Code: Select all

fm.POWER_CONFIG.DMUTE = 1  ##Turn off mute
fm.SYS_CONFIG_2.VOLUME = 15##Turn the volume up to max
fm.write_registry()  ## Since the above two lines of code only change local variables on the Pi, we need to write those variables to the Si4703.
If you wish to manually change a property it is recommended that you first use update_registry(). Doing so will refresh local variables with the most recent ones from the IC. After doing so, you may change the property. As stated in the example above, you must use write_registry() to send the data to the IC.

Register 0x07 will never be written unless fm.TEST_1.POWER_SEQUENCE = True. It is only used once in the init() method. The reason for this is because only the first two bits in 0x07 are supposed to be written...but we have to write a minimum of 8 bits. The remaining 6 bits are static during power-up, which is perfect because that's the only time we need to write them.

By default, the Si4703 assumes that it will be used in USA (BAND = 0 and SPACE = 0) such that the frequency goes from 87.5-108.0 and that only odd numbered frequencies are used (98.5, 104.5, 105.3, 106.9).
You can accomodate European radio stations by setting SPACE = 1
You can accomadate Japanese radio stations by setting BAND = 2, or set BAND = 1 for wide-band

Setting SYS_CONFIG_3.VOLEXT = 1 does the opposite of what you might think. I actually decreases the volume by 28 dB. It is set to 0 by default

Rafa12321
Posts: 1
Joined: Sat Nov 16, 2013 5:03 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Sat Nov 16, 2013 5:07 pm

Hi!

Great post!!
I would like to know if you had any progress in decoding the RDS data, it would be great if you can share it.

Thank you!
Rafa

KansasCoder
Posts: 7
Joined: Tue Jan 08, 2013 7:45 pm

Re: SI4703 FM Tuner Eval Board working with RasPi

Fri Dec 20, 2013 9:16 pm

OK, here is my code for a full program that has a menu for various capabilities like increase volume, decrease volume, change channel, etc.
I also have a simple reading and displaying of the RDS and then it displays the text it found.

Here is some RDS Documentation to help you figure it out.
https://support.silabs.com/attach/BCA/B ... S_RBDS.pdf

I also created functions to most the activities.

I am not a python programmer so I am sure the code is not perfect. Especially the RDS code. But a real programmer can use it to make it work for them.

Let me know if you have questions and if this was useful!

Code: Select all

#Python Version 2 code

# Many Thanks to Nathan Seidle at Sparkfun for his Arduino Code Example
# It saved me a lot of work

import RPi.GPIO as GPIO
import smbus
import time

GPIO.setwarnings(False)

i2c = smbus.SMBus(1)      #use 0 for older RasPi
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.OUT)
GPIO.setup(0, GPIO.OUT)

#create #create 16 registers for SI4703
reg = [0] * 16
 
#Register Descriptions
DEVICEID = 0x00
CHIPID = 0x01
POWERCFG = 0x02
CHANNEL = 0x03
SYSCONFIG1 = 0x04
SYSCONFIG2 = 0x05
SYSCONFIG3 = 0x06
OSCILLATOR = 0x07
STATUSRSSI = 0x0A
READCHAN = 0x0B
RDSA = 0x0C
RDSB = 0x0D
RDSC = 0x0E
RDSD = 0x0F

z = "000000000000000"
 
si4703_addr = 0x10 #found using i2cdetect utility
 
#create list to write registers
#only need to write registers 2-7 and since first byte is in the write
# command then only need 11 bytes to write
writereg = [0] * 11
 
#read 32 bytes
readreg = [0] * 32
 
def write_registers():
    #starts writing at register 2
    #but first byte is in the i2c write command
    global writereg
    global reg
    global readreg
    cmd, writereg[0] = divmod(reg[2], 1<<8)
    writereg[1], writereg[2] = divmod(reg[3], 1<<8)
    writereg[3], writereg[4] = divmod(reg[4], 1<<8)
    writereg[5], writereg[6] = divmod(reg[5], 1<<8)
    writereg[7], writereg[8] = divmod(reg[6], 1<<8)
    writereg[9], writereg[10] = divmod(reg[7], 1<<8)
    w6 = i2c.write_i2c_block_data(si4703_addr, cmd, writereg)
    readreg[16] = cmd #readreg
    read_registers()
    return
 
def read_registers():
    global readreg
    global reg
    readreg = i2c.read_i2c_block_data(si4703_addr, readreg[16], 32)
    reg[10] = readreg[0] * 256 + readreg[1]
    reg[11] = readreg[2] * 256 + readreg[3]
    reg[12] = readreg[4] * 256 + readreg[5]
    reg[13] = readreg[6] * 256 + readreg[7]
    reg[14] = readreg[8] * 256 + readreg[9]
    reg[15] = readreg[10] * 256 + readreg[11]
    reg[0] = readreg[12] * 256 + readreg[13]
    reg[1] = readreg[14] * 256 + readreg[15]
    reg[2] = readreg[16] * 256 + readreg[17]
    reg[3] = readreg[18] * 256 + readreg[19]
    reg[4] = readreg[20] * 256 + readreg[21]
    reg[5] = readreg[22] * 256 + readreg[23]
    reg[6] = readreg[24] * 256 + readreg[25]
    reg[7] = readreg[26] * 256 + readreg[27]
    reg[8] = readreg[28] * 256 + readreg[29]
    reg[9] = readreg[30] * 256 + readreg[31]
    return

def getchannel():
    read_registers()
    channel = reg[READCHAN] & 0x03FF
    channel *= 2
    channel += 875
    return channel
 
def changechannel(newchannel):
    if newchannel < 878 or newchannel > 1080:
       return 
    global reg
    newchannel *= 10
    newchannel -= 8750
    newchannel /= 20
    read_registers()
    reg[CHANNEL] &= 0xFE00; #Clear out the channel bits
    reg[CHANNEL] |= newchannel; #Mask in the new channel
    reg[CHANNEL] |= (1<<15); #Set the TUNE bit to start
    write_registers()
    while 1:
        read_registers()
        if ((reg[STATUSRSSI] & (1<<14)) != 0):
            break
    reg[CHANNEL] &= ~(1<<15)
    write_registers()
    return

def setvolume(newvolume):
    global reg
    if newvolume > 15:
        newvolume = 15
    if newvolume < 0:
        newvolume = 0
    read_registers()
    reg[SYSCONFIG2] &= 0xFFF0 #Clear volume bits
    reg[SYSCONFIG2] = newvolume #Set volume to lowest
    write_registers()
    return

def init():
    # init code needs to activate 2-wire (i2c) mode
    # the si4703 will not show up in i2cdetect until
    # you do these steps to put it into 2-wire (i2c) mode
    
    GPIO.output(0,GPIO.LOW)
    time.sleep(.1)
    GPIO.output(23, GPIO.LOW)
    time.sleep(.1)
    GPIO.output(23, GPIO.HIGH)
    time.sleep(.1)

    read_registers()
    # do init step, turn on oscillator
    reg[OSCILLATOR] = 0x8100
    write_registers()
    time.sleep(1)

    read_registers()
    reg[POWERCFG] = 0x4001 #Enable the Radio IC and turn off muted
    write_registers()
    time.sleep(.1)

    read_registers()
    reg[SYSCONFIG1] |= (1<<12) #Enable RDS
    reg[SYSCONFIG2] &= 0xFFF0; #Clear volume bits
    reg[SYSCONFIG2] = 0x0000; #Set volume to lowest
    reg[SYSCONFIG3] = 0x0100; #Set extended volume range (too loud for me without this)
    write_registers()
    return

def seek(direction):
    read_registers()
    reg[POWERCFG] |= (1<<10 )
    if direction == 0:
        reg[POWERCFG] &= ~(1<<1)
    else:
        reg[POWERCFG] |= (1<<9)
    reg[POWERCFG] |= (1<<8)
    write_registers()
    while 1:
        read_registers()
        if ((reg[STATUSRSSI] & (1<<14)) != 0):
            break
    print "Trying Station ", float(float(getchannel())/float(10))
    read_registers()
    valuesfbl = reg[STATUSRSSI] & (1<<13)
    reg[POWERCFG] &= ~(1<<8)
    write_registers()
    return

currvol = 7
init() #init stuff

changechannel(1011) #101.1 The Fox in Kansas City, Classic Rock!!
setvolume(currvol)

#print float(float(getchannel())/float(10))

ans = True
while ans:
    print ("f) Go to 101.1 The Fox")
    print ("+) Increase Volume")
    print ("-) Decrease Volume")
    print ("=) Channel Number (=101.1)")
    print ("w) Tune Up")
    print ("s) Tune Down")
    print ("c) Print Current Channel")
    print ("d) Display Status")
    print ("r} Write RDS")
    print ("r2} Display RDS Groups 2A")
    print ("4) Seek Down")
    print ("5) Seek Up")
    print ("")
    
    ans = raw_input(">")
    if ans == "5":
        seek (1) #1=up\
    if ans == "4":
        seek (0)
    if ans == "f":
        changechannel(1011)
    if ans == "+":
        currvol += 1
        setvolume (currvol)
    if ans == "c":
        print
        print "Curr Chan=", float(float(getchannel())/float(10))
        print 
    if ans == "-":
        currvol -= 1
        setvolume (currvol)
    if ans == "w":
        currchan = getchannel()
        wc = currchan + 2
        if wc >= 878 and wc <= 1080:
            currchan = wc
            changechannel(currchan)
    if ans == "s":
        currchan = getchannel()
        wc = currchan - 2
        if wc >= 878 and wc <= 1080:
            currchan = wc
            changechannel(currchan)
    if ans != "" and ans[0] == "=":
        wc = float(ans[1:])
        wc *= 10
        if wc < 878 or wc > 1080:
            print ("Invalid Channel")
            print ("")
        else:    
            changechannel(int(wc))
            
    if ans == "d":
        read_registers()
        print
        print ("Radio Status:")
        if reg[STATUSRSSI] & (1<<15):
            print "RDS Available -",
            blockerr = reg[STATUSRSSI] & 0x0600 >> 9
            if blockerr == 0:
                print ("No RDS Errors")
            if blockerr == 1:
                print ("1-2 RDS Errors")
            if blockerr == 2:
                print ("3-5 RDS Errors")
            if blockerr == 3:
                print ("6+ RDS Errors")
            print
            #r1 = z[:16 - len(bin(reg[RDSA])[2:])] + bin(reg[RDSA])[2:]
            r2 = z[:16 - len(bin(reg[RDSB])[2:])] + bin(reg[RDSB])[2:]
            r3 = z[:16 - len(bin(reg[RDSC])[2:])] + bin(reg[RDSC])[2:]
            r4 = z[:16 - len(bin(reg[RDSD])[2:])] + bin(reg[RDSD])[2:]
            #print r1
            print r2
            print r3
            print r4
            print
        else:
            print ("RDS Not Available")
            
        if reg[STATUSRSSI] & (1<<8):
            print ("Stereo")
        else:
            print ("Mono")
        print

    if ans == "r":
        #h1=""
        h2=""        
        h3=""
        h4=""
        wc = 0
        f= open('/home/pi/rds.csv',"w")
        while 1:
            read_registers()
            if reg[STATUSRSSI] & (1<<15):
                #r1 = z[:16 - len(bin(reg[RDSA])[2:])] + bin(reg[RDSA])[2:]
                r2 = z[:16 - len(bin(reg[RDSB])[2:])] + bin(reg[RDSB])[2:]
                r3 = z[:16 - len(bin(reg[RDSC])[2:])] + bin(reg[RDSC])[2:]
                r4 = z[:16 - len(bin(reg[RDSD])[2:])] + bin(reg[RDSD])[2:]
                if h2 != r2 or h3 != r3 or h4 != r4:
                    f.write(r2 + "," + r3 + "," + r4 + "\n")
                    print wc
                    wc += 1
                    #h1 = r1
                    h2 = r2
                    h3 = r3
                    h4 = r4
                    if wc == 5000:
                        f.close
                        break
    if ans == "r2":
        msg = ""
        mi = 0
        h2=""        
        h3=""
        h4=""
        wc = 0
        f= open('/home/pi/rds.csv',"w")
        while 1:
            read_registers()
            if reg[STATUSRSSI] & (1<<15):
                r2 = z[:16 - len(bin(reg[RDSB])[2:])] + bin(reg[RDSB])[2:]
                r3 = z[:16 - len(bin(reg[RDSC])[2:])] + bin(reg[RDSC])[2:]
                r4 = z[:16 - len(bin(reg[RDSD])[2:])] + bin(reg[RDSD])[2:]
                if h2 != r2 or h3 != r3 or h4 != r4:
                    f.write(r2 + "," + r3 + "," + r4 + "\n")
                    #print wc
                    wc += 1
                    #h1 = r1
                    h2 = r2
                    h3 = r3
                    h4 = r4
                    value = int(r2[:4],2)
                    value2 = int(r2[5:-5],2)
                    if value2 == 0:
                        type = "A"
                    else:
                        type = "B"
                    code =  str(value) + type
                    #time.sleep(.)
                    #print code
                    if code == "2B":
                        chars = chr(int(r3[:8],2)) + chr(int(r3[9:],2)) + chr(int(r4[:8],2)) + chr(int(r4[9:],2))
                        index = int(r2[12:],2)
                        #print hex(int(r3[:8],2)) + '-' + hex(int(r3[9:],2)) + '-' + hex(int(r4[:8],2)) + '-' + hex(int(r4[9:],2))
                        print str(index) + '-' +  chars
                        if index == 0 and mi != 0:
                            print
                            print "RDS MSG = " + msg
                            print
                            break
                        if index == mi:
                            msg += chars
                            mi += 1

                    if wc == 500:
                        f.close
                        break

jesterod
Posts: 1
Joined: Tue Jan 28, 2014 12:11 am

Re: SI4703 FM Tuner Eval Board working with RasPi

Tue Jan 28, 2014 12:13 am

now we need a xbmc plugin for it 8-)

bboyandru
Posts: 24
Joined: Thu Feb 14, 2013 7:34 am
Contact: Website Yahoo Messenger

Re: SI4703 FM Tuner Eval Board working with RasPi

Thu Feb 27, 2014 10:26 am

First of all big thanks to funkeywoookey who did the python class for interfacing the SI4703 module.
I have used it and developed a python server(using UDP sockets). This server runs all the time and waits for an udp command.
The user who wants to send a command to the radio just have to send an UDP packet on the localhost.
I have also written an XBMC plugin to handle this and i have integrated this in my Car PC project.
See this post: http://www.engineering-diy.blogspot.ro/ ... carpc.html

The FM reception in my car is poor, without any antenna(maybe because the car behaves like a shield for electromagnetic waves). I have stripped the audio wire comming out of the jack of the SI4703 and hooked it up to the car antenna. This resulted in better quality but not very good. The next step I did was to put an FM amplifier between car's antenna and SI4703 jack ground. This made a huge improvement and now I have good signal reception in cities and near them, but sometimes the signal gets lost a little and i don't have as good reception as the original's car radio.

Most probably all these issues are coming from the antenna which is very sensible.

You can get all my code from my Google Drive(addon + radio server). I will upload it as soon as i go home.
XBMC Frodo radioFM addon: https://drive.google.com/?tab=mo&authus ... k40V0p3Ums

Andrei
http://www.engineering-diy.blogspot.com

robocop
Posts: 3
Joined: Mon Feb 10, 2014 4:08 am

Re: SI4703 FM Tuner Eval Board working with RasPi

Sun Mar 16, 2014 9:11 pm

Hello:

I am trying to make the USB InstantFM Radio that I believe have a SI470x chip. If you follow this post, I believe nobody has been able to get this thing working. Does anyone have an Idea why?

I am new to Linux and RasPi. What I will have to do to get this thing working.

Is the Alsa driver not reading the USB Correctly? I do not think so. Will I have to go through the Alsa Driver?
What will be the place to start? I will have to write a driver? will I need pyUSB for that?

Can anyone can tell me where I can find documentation or tell me the Steps I need to get started in this proyect?

Thanks in advance,

Pablo Arthur
Dominican Republic.

User avatar
ragnarjensen
Posts: 332
Joined: Wed May 15, 2013 6:13 pm
Location: Stockholm, Sweden
Contact: Website

Re: SI4703 FM Tuner Eval Board working with RasPi

Sun Mar 16, 2014 9:49 pm

robocop wrote:I am trying to make the USB InstantFM Radio that I believe have a SI470x chip. If you follow this post, I believe nobody has been able to get this thing working. Does anyone have an Idea why?
It is an USB1 audio device with only one sample rate - 96 kHz. The drivers for the Raspberry's USB hardware are not quite there yet.
http://www.raspberrypi.org/phpBB3/viewt ... 28&t=70437
--
Ragnar

robocop
Posts: 3
Joined: Mon Feb 10, 2014 4:08 am

Re: SI4703 FM Tuner Eval Board working with RasPi

Tue Apr 08, 2014 1:03 am

ragnarjensen wrote:
robocop wrote:I am trying to make the USB InstantFM Radio that I believe have a SI470x chip. If you follow this post, I believe nobody has been able to get this thing working. Does anyone have an Idea why?
It is an USB1 audio device with only one sample rate - 96 kHz. The drivers for the Raspberry's USB hardware are not quite there yet.
http://www.raspberrypi.org/phpBB3/viewt ... 28&t=70437
--
Ragnar
Ragnar:

Good morning, and Thank you very much!!!!!. I was loosing hope!!!!!. I appreciate your link!. I will pick up my Project in couple of days and will give and update. Again thank you very much for being so kind!

Pablo.

Return to “Other projects”