Tuesday, August 2, 2011

Si4735 AM/FM/SW/LW Radio Project


[UPDATE #9] 

Summary of changes specific to Si4735 library:
  • Added support for USE flags. These flags make it quick and easy to strip down the Si4735 library features and will ultimately help you save memory space at the cost of features.
  • Added setProperty and getProperty methods. This will make it very easy for individuals to easily customize the functionality and properties of the Si4735 chip. Consult the Si4735 programmers manual for the register addresses, properties, and acceptable values.
  • Added seekThresholds method to allow the user a quick and easy way to adjust the seek SNR and RSSI thresholds.
  • Added a new field to the Station struct that indicates when the radioText has changed (or is being changed). The radio text is usually left unchanged during a song so this could be used to detect that a new song is playing and you could create an event that triggers off of this flag. This boolean field should only stay high for one readRDS call.
Summary of changes to my project and helper libraries:
  • 'visible' method added to SerLCD.
  • Added a reset baud function to the setBaud method in SerLCD. Issuing setBaud(0) while the splash screen is being displayed will set SerLCD to 9600 baud.
  • Fixed typo in serCommand method in SerLCD library
  • increased stability of sketch by disabling interrupts during callback execution. This should help minimize the frequency of system freeze-up (I don't think it eliminates it completely though).
  • SerLCD is automatically reconfigured to it's maximum supported baud rate of 38400baud. Note there is a known bug where after a cold start-up (arduino completely discharged) that the sketch will not complete the setup process (it will stop at "Loading-Up"). A simple reset will allow it to successfully load.
  • Increased the seek process' sensitivity. It will now detect more "good" quality stations by default.
Si4735 Shield with custom made Logic Level Converter PCB attached

[UPDATE #8] I have updated the library once again. Here is an incomplete list of changes:

  • Updated the code to be compatible with the Arduino IDE v1.0 
  • Added SerLCD.cpp to the repo. SerLCD.h is now an actual class library.
  • Added Rotary_one class library to the repo. This is a single interrupt rotary encoder library. It is not prefect by any means but it is a step in the right direction. Freeing up pin 2 means I can start working on utilizing the Si4735 interrupt capabilities. The original rotary encoder library is left untouched and the two are interchangeable. By default the original is still being used, but in the future, I plan on fine tuning this library so that it is as responsive as the old one, but I have no plans on when that may be.
  • Made minor adjustments to my Si4735_Advanced_Radio project to address a few minor bugs. Audio quality will be slightly cleaner on stations that don't have RDS/RBDS program services. I was constantly writing on the UART line in these circumstances, which created unnecessary noise.

[UPDATE #7] My advanced radio project file is now included under the examples section of the Si4735 library repository. I also made a small update to the library to adjust the deemphasis to the proper levels depending on the region/locale you have specified when calling the setLocale method.

[UPDATE #6] I have finally made my radio project public! For now you can download my pde file and the accompanying libraries (with exception to the Si4735 library which you can just grab the normal way) in my GitHub downloads section. I am still trying to learn how to add files to an existing fork'ed repository, but once I figure that out, I will be adding these files to the "examples" folder in the Si4735 repo. If you have any questions pertaining to this project please let me know. I tried my best to comment most of the code without being overly verbose so some stuff is bound to cause a few people to scratch their heads.

Hopefully this example will provide users with enough insight on how "one" might use the different features that exist in the library.

I still have plans to add USE_FLAGS (as described in the previous update) but I have been busy with other things at the moment.

[UPDATE #5] The Si4735 library has been updated again, here are some of the changes:

  •  Added the retrieval of the UTC month, day, and year(2-digit). Note the hour and minute that are provided in the "Today" structure are local values and not the UTC values. This may change in the future.
  • Fixed the hour and minute computation. These were being incorrectly computed due to a couple mathematical errors.
  • Compacted the Program Type lookup tables into one large LUT. This reduces redundancy and ultimately saves space.
  • Added a private function that helps filter out non-printable characters from strings that are meant to be printed.
I have been playing with the idea of adding in USE_FLAGS to enable/disable the various RDS/RBDS features. I think this might be useful for individuals that want to minimize their memory footprint and compile only the portions of the code that are actually being used. Just a thought...

    [UPDATE #4] This is a quick update to show my audio amplifier circuit at work. I decided against throwing in a preamp into the mix and just went with an LM386N-1. The audio quality is very nice considering the parts being used. There is a little bit of audio clipping occurring when I set the audio at 100%, which I imagine I can eliminate by swapping out my 9V regulated DC adapter with a 12V regulated DC adapter. The speaker that I used was one of my car's old stock speakers. I plan to make another identical circuit so that I can get stereo output, but before I get into that, I will need to start thinking of an enclosure to start housing this stuff.


    Also, one thing I forgot to show off last time was my scanFreq Labview program that communicates with the Arduino and runs through a series of tuneFrequency() and getRSQ() calls. The program takes the SNR information acquired from the chip and plots it versus frequency. This is makes for a very basic spectrum analyzer over the FM frequency band. I would not rely on the metrics too much but they certainly give you a general idea of what to expect for sound quality when tuning to a particular frequency.

    For those interested in this you can download it here: download (note: make sure to change the REFRESH/HOLD button to REFRESH when you want to reacquire the data. HOLD will simply maintain the current data)
    The code essentially expects comma separated values (see the code below for the exact form of the output). The function below will interface with the Labview code perfectly (assuming you define your Si4735 class as "radio" and that you are using my latest version of the library). 

    [UPDATE #3] I got around to updating the Si4735 library. BE WARNED, this update WILL BREAK YOUR CODE...however, in my opinion this update is a necessary step forward and future updates will be less painful for both the users and the developers. Here is an incomplete list of changes and additions:
    • Created a Metrics structure, making the getRSQ method a lot simpler to interface
    • Created a Station structure, making the getRDS method a lot simpler to interface
    • Created a getTime method and a Today structure which acquires the station's local time (eventually this will also include the date, but currently it only contains the 24-hour time). Note this data field is rarely transmitted (most stations send time/date information about 1 time a minute)
    • Removed unused return values (mainly to clean up the code)
    • Added the ability to set (and get) the "locale" (setLocale) to either NA (North American) or EU (European). This effectively sets the look-up table to use for the Program Type.
    • Added setMode and getMode methods. The setMode method calls the "end" method which effectively powers down the Si4735 and the changes then mode. The user is responsible for calling the "begin" method again.
    • Added the interpretation/conversion of the PI field to a Callsign. It seems that not many stations (atleast where I live) use this field. The callsign is the 4 (or 3) letter identification assigned/used by radio station, such as WHEB or KHNN. Note: 3 letter stations are currently not supported in this code but it is on my list of TODOs.
    This was a rather large update and if something doesn't seem quite right please let me know, as I could have forgotten to change something when I went to update the repository.

    My next update will be a small one which is to enable the "date" field in the Today structure. This date field will contain the current year, month, and day.

    Also as a side note, I will be providing access to my Arduino Sketch in the near future.  This will probably be at the same time I perform the above mentioned update to the library

    [UPDATE #2] Here is another update showing performance improvements and added features such as showing the Program Type field (PTY). I have also added a getRSQ method to the library that can be used to get the "Received Signal Quality" of the tuned station. This method provides the following metrics:
    STBLEND - Percent Stereo Blend [0 = Mono, 100 = Stereo]
    RSSI - Receive Signal Strength Indicator [0 - 127 dBuV]
    SNR - Signal to Noise Ratio [0 - 127 dB]
    MULT - Multipath [0 = No multipath, 100 = Full multipath]
    FREQOFF - Signed Frequency offset
    For those interested in accessing the library that my code is running off of (for the most part...there are some minor differences), you can head over to: github Si4735. This is a fork of Trunet's repository with some additions, tweaks, and minor bug fixes.


    [UPDATE #1] Below is a video update to my project. I have now added RDS/RBDS support to my project as well as a LabView interface for controlling the device through the USB/COM port.




    I recently acquired an Arduino board and I have found that it is at least 100 times easier to develop/design projects than an FPGA. Don't get me wrong I still prefer FPGA (I feel like I am more in control of what I do), but programming hardware from a C/C++ level is so much easier in comparison to HDL programming. This fact, coupled with the fact that the Arduino is an open source, driven community, hardware/software platfrom makes for a very user friendly environment. 

    Anyways, I decided to jump into the realm of Radio by using Sparkfun's  Si4735 Shield. The Si4735 is a pretty impressive chip that has a lot of functionality in a small form factor. Check out the video and see the basic block diagram of this project. When I get around to it, I will provide more details here.

    Things that need improving:
    1. You might have noticed in the video that the rotary encoder had an issue when I was attempting to change the frequency in "stepped" mode. This is a bug that currently only exists while in this mode. I am not sure if the underlying cause is my rotary encoder's debouncing algorithm or if it is some weird timing issue. This was fixed via modification to my rotary encoder callback procedure
    2. The rotary encoder's debouncing algorithm also needs improvement, while the performance is reasonable, it is far from perfect and needs some fine tuning. Updating my code and cleaning up my wiring helped a lot.
    3. There appears to be an issue with using a power source connected to the Arduino's external power jack (instead of using the USB's supplied power). The SPI interface becomes a little unreliable.
    Overall though this project, when powered from the USB port, is very reliable and user friendly. I have a few neat ideas for adding onto this project that I will share at a later time. The performance of the Radio shield is very good considering that the FM signal that I was receiving were coming from the line out (that is wired to my amplifier and speakers), and the audio quality was exceptional.