Tuesday, August 2, 2011

Si4735 AM/FM/SW/LW Radio Project

[KEY DOWNLOADS] 




[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.



    ORIGINAL POST



    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. 

    72 comments:

    1. 1) What amplifier are you using to amplify the low voltage Si4735 IC?

      2) Are you using some 3.3v arduino or it's a regular 5v arduino(duemilanove, uno, ...)? If using 5v, did you add a diode or are using other kind of shift level to get around GPIO going too low to arduino?

      ReplyDelete
    2. Hi trunet,
      1) The amplifier I used in this demo was part of my sound system (logitech z680). I have played around with the idea of having a stand alone radio system that you could wire up any speaker and have sufficient power being driven to the speaker. If I do go down that route, I will probably use a LM741 opamp as a preamp and then an LM386 as the output/power amplifier.

      2) I am using a 5V Arduino UNO and I used Sparkfuns Logic Level shift BOB. While the diode concept works, it is very hacky and does not amplify the signal at all; it merely offsets the logic. I prefer the more tradional methods of using a FET or Opamp to convert the logic levels. But either will work just fine since noise and attenuation should not be much of an issue at these voltage levels.

      ReplyDelete
    3. Adding the RDS/RBDS standards is awesome. Sucks that most car radios do not implement them, they are very useful. Can you get Emergency Alert System data with your setup?

      ReplyDelete
    4. Yes potentially, the RDS/RBDS information is sent in a group of 4 blocks. In each group the second block contains a group type code that tells how the group is formatted (i.e. the blocks can take on many different forms of information). So all it takes is me actually bothering to check for a group type code of 0X9A and then pulling that information into some form of memory. Its pretty cool stuff.

      The one thing that I find stupid/annoying is that for some reason we (USA's RBDS) decided to not follow RDS's program type information (this a 5-bit field that receivers feed into a lookup table to determine the type of station it is tuned to, such as rock, country, top 40, news, etc). RDS and RBDS lookup tables are not the same for this field so that if you go from the US to Europe, the stations will not display their program types correctly (i.e. you might be listening to a rock station but the device says that its a weather station). It's easy enough to fix with a manual switch to determine the locale but its annoying that that has to be done at all.

      ReplyDelete
    5. It's me again... I added the library to github with your getFrequency function and I made RDS functions.

      Will be great to maintain the code there. You can fork the library and make your updates.

      https://github.com/trunet/Si4735

      ReplyDelete
    6. Its good to hear someone is taking on the task of combining all the functions people have been adding to the library. When I get around to it I will fork the code with my updates.

      ReplyDelete
    7. For some reason, everything works except the RDS/RBDS. I'm using trunet's code libraries and I have a universal remote that should trigger a display of the freq and RDBS info (I'm in California). Here's the call statement:
      Serial.println("Display Radio Freq");
      freq = radio.getFrequency(valid);
      Serial.println(freq);
      radio.readRDS();
      char ps[9];
      char radioText[65];
      radio.getRDS(ps, radioText);
      Serial.println(ps);
      Serial.println(radioText);

      I get the freq, but blank lines for the RBDS. Could this be because trunet is in Brazil, RDS is different implementation?

      I'm using a seeedstudio that has a switch for 3.3v TTL, no level switcher, every other function works so far.

      ReplyDelete
    8. Hi thisoldgeek,
      Are you sure the station you are tuned to has RDS/RBDS support? How is your signal quality? If you use my fork of trunet's code I added a getRSQ that can provide you with numeric values pertaining to signal quality. (Note: I have changed several of the methods inputs/outputs so it would require some minor rework of your code.) Nothing stands out as being the problem syntactically, at least from the syntax you have provided me with.

      I am running trunet's readRDS/getRDS function just fine and I am in the US as well.

      If my getFrequency function works then you at least know the communication on the SPI line is good so it seems to suggest that there is a programming error somewhere.

      Another question, how frequently does your looping function iterate? If it is too slow you could be missing data since the Si4735 has limited buffers for holding the RDS info.

      Hope this is helpful.

      ReplyDelete
    9. Hi Jon-

      Yes, that was helpful. I'm seeing pieces of data coming back now, just have to clean up my code.

      Basically a misunderstanding on my part, that the data had to be sort of "polled" to fill the buffers. I was only calling the routine once, on a button press.

      Doh!

      Thanks for the help

      ReplyDelete
    10. Glad to hear you figured out the problem

      ReplyDelete
    11. hi,
      first of all great work,
      thanks to your expanations and code i managed to get the radio working and displaying onto a lcd. (fm frequency, volume, RDS)
      i am mostly engineer than software and i have a couple of questions,

      1. is 63 the top value of the volume? (is it possible to have a range 0-100), and when the radio powers on, is it on full volume?
      2. i need to change volume and frequency using 2 potentiometers (or rotary encoder? or something similar),
      but my code is way to sensitive (and with bugs) for a good control.
      any suggestions?

      ReplyDelete
    12. Hi Sparky,
      1. Yes, the Si4735 utilizes an 6-bit register for the volume. As such, the maximum value is 63 meaning if you want to display your volume as a percentage, you need to take your currently set value and divide by 63 (and multiply by 100).

      2. If by sensitive you mean that the controls (potentiometer) change the value too much, then you can always make a "window function" where you break up the full range of possible values into ranges. Each range of values gets mapped to a single value. For instance, if you have an analog signal that swings digitally (through your ADC) from 0-99 and you only want the full range of analog values to modify the digital value from 0-9 you could break up the analogRead values as 0-9, 10-19, 20-29 ... 90-99 where values that exist in these ranges are mapped to 0,1,2,...9 respectively. This can be done by setting up a series of if-else statements.

      Hope this helps.

      ReplyDelete
    13. well yes it did help,
      didnt exactly do what you said, but by mapping the analogread values to a smaller range, the readings are not so sensitive to control tuning and volume. cheers. i also managed to start the radio at a desired volume level. using a for loop in the setup, after initializing the radio, i did volumeDown.
      didnt managed to manipulate the byte variable for displaying the 0-100 volume range, but stiil happy with the result.
      thanx again for sharing your work.

      ReplyDelete
    14. Hi Sparky,
      Glad to here you are making progress. One thing to note about how you setup the initial volume, trunet and I have added methods to the Si4735 class to allow you to directly set the volume to a specific value instead of going through a for loop many times. (note, I will making a rather large update to my fork later this week, so you might want to hold off looking at it until then).

      As for displaying 0-100 for volume it is really quite simple. Say you want to print the volume to the serial connection as a percentage you would take your local variable that stores the volume variable, lets call this "vol" and then issue the command:

      Serial.print(100*vol/63);

      Also, at some point down the road (maybe in a week or two) I will be sharing my entire sketch in hopes that it can act as a fairly robust demonstration of the capabilities of the Si4735 chip with the Arduino.

      ReplyDelete
    15. really nice...thanks for posting the last update...this is my first project with arduino,coding stuff , had some problems at compiling it (had to remove the Y, M, D stuff), and the serial(usb) commands weren`t working (had to remove the rotary connection to enable it to listen to serial but i guess that`s the function of that pushbutton didnt had the time to look it up) :D

      ReplyDelete
    16. hrm, not sure why you would have trouble compiling it. Do you happen to know the error that it was reporting? I would like to make sure I didn't forget to include something for the project to work correctly.

      ReplyDelete
    17. C:\arduino\libraries\SI4735_exemplufull\Si4735.cpp: In member function 'bool Si4735::readRDS()':
      C:\arduino\libraries\SI4735_exemplufull\Si4735.cpp:386: error: 'u_int' was not declared in this scope
      C:\arduino\libraries\SI4735_exemplufull\Si4735.cpp:387: error: expected `;' before 'Y'
      C:\arduino\libraries\SI4735_exemplufull\Si4735.cpp:388: error: expected `;' before 'M'
      C:\arduino\libraries\SI4735_exemplufull\Si4735.cpp:390: error: 'M' was not declared in this scope
      C:\arduino\libraries\SI4735_exemplufull\Si4735.cpp:394: error: 'Y' was not declared in this scope
      C:\arduino\libraries\SI4735_exemplufull\Si4735.cpp:395: error: 'M' was not declared in this scope

      this is the error , your code looks like this :

      // Group 4A Clock-time and Date
      //Note the time is localized but the date is the UTC date
      //Setting offset to 0 will make the time referenced to UTC
      else if (type == 4 && version == 0){
      unsigned long MJD = (response[7]&3<<15 | u_int(response[8])<<7 | (response[9]>>1)&127)&0x01FFFF;
      u_int Y=(MJD-15078.2)/365.25;
      u_int M=(MJD-14956.1-u_int(Y*365.25))/30.6001;
      byte K;
      if(M==14||M==15)
      K=1;
      else
      K=0;
      _year=(Y+K)%100;
      _month=M-1-K*12;
      _day=MJD-14956-u_int(Y*365.25)-u_int(M*30.6001);

      char sign=(((response[11]>>5)&1)/(-1));
      if(sign==0)sign=1; //Make sign a bipolar variable
      char offset=sign*(response[11]&31);

      _hour=((response[9]&1)<<4) | ((response[10]>>4)&15);
      _minute=((response[10]&15)<<2 | (response[11]>>6)&3);
      _hour= (_hour + (offset/2) + 24)%24;
      _minute=(_minute + (offset)%2*30 + 60)%60;
      }

      i just commented all that and it worked.

      ReplyDelete
    18. I believe I fixed the problem, I forgot to update the github code to include the typedef for u_int. If you re-download the Si4735.h file and replace the current one I believe this will solve the problem (atleast the errors that pertain to u_int).

      Let me know if there are any remaining issues, so that I can make sure that the github code is atleast in working order.

      Thanks for you help.

      ReplyDelete
    19. sorry for the delay but i was caught up in work,nah money doesn`t fall from the sky :) , now its working, thankx for the update. later i`ll hook up a rotary encoder and a serial lcd (didn`t had one so i made it :D )and i`ll tell you if is all ok :)

      ReplyDelete
    20. Firstly, a big thank you for releasing your code.

      I am currently trying to get it all working right but have had a few issues. (Newbie!)

      1) When initially started up, the second LCD line appears to be updating very rapidly (I can see it on the serial monitor) and causing the LCD to flicker. The frequency sometimes starts/ jumps to 100.4 Mhz instead of 100.3Mhz as expected. The first LCD line is fine. The second line occasionally becomes stable (steady) when tuned to a station (when RDS is received maybe?) Flickering appears quickest when in Seek mode, then Vol and slowest in Freq. mode. Sometimes seems to cause a lock up / other problems. Holding the pushbutton causes a solidly displayed second line. Wondering if this may be related to the rotary encoder input?

      May I ask which rotary encoder were you using? I am using a fairly cheap 20step unit which is not as responsive with your code as I'd like. (Speed / errors?)

      2) I'd really like to use the European PTY lookup table but when using radio.setLocale(EU); it's not quite right. It seems that the EU LUT is referenced to the NA one in the Si4735.cpp? How would it be best to change this?

      3) I think that the device is using 75uS deemphasis on FM as standard- would it be possible to include the change to 50uS with the setLocale(EU) or even by itself? This seems to require the use of the property 0x1100 (pg 79 of AN332)

      Once again, huge thanks for all your previous hardwork!

      ReplyDelete
    21. Hi Flamingo7,
      1)If the station that the radio tunes to does not contain RDS information it should be constantly writing to the first line of the display. Specifically it should be writing "-=ArduinoRadio=-" with a couple control characters to instruct the SerLCD module to go to character 0 on the display. I have played witha couple ideas on eliminating this behavior but I have not found a solution I am happy with yet. If you want to change this behavior the code that is responsible for what "I think" you are seeing is in the showPS() function in the main project file (more specifically, it is the "else" case).

      For the lock-up issue, I have seen this occur on very rare occasions, mainly when running the system via the DC input on the Arduino (as opposed to powering off of the USB). I am not sure if this is just a problem with my hardware or if it has to do with the code (to some extent).

      Some of this behavior maybe caused by the rotary (as you have suggested). I have not seen this "solidly displayed second line" that you described (I am attempting to reproduce it as I am writing this). The rotary encoder I am using is one that you can get from Sparkfun (COM-09117).

      2) You are correct the code was changed to combine the redundancy of having to similar LUTs for EU an NA and combining them in a way that reduces the redundancy to help reduce the compiled code size. I can't say for certain if the EU side is completely bug free but I didn't see anything that stands out. Could you describe in more detail what isn't quite right? If you want you can go to the "history" in the github library and grab an earlier version that contained two independent LUTs for EU and NA. (The September 30th code update seems to have what I am referring to). Copying/Replacing the ptystr method to your current Si4735.cpp file should work just fine.

      If you want to keep the current method for ptystr it should be only a matter of changing the values in the LUT defined in the _locale==EU case. The values are just values that point to the indices of the main LUT that contains all the strings.

      3)Absolutely, thanks for catching this. I have updated the code with the fix being applied when setting the locale via setLocale.

      ReplyDelete
    22. Hi Jon,

      Huge thanks for your response and for adding the deemphasis.

      With regards to the PTY lookup table - My initial confusion came from the 16 character display names being slightly different to the 8 character names I am used to. (My fault!) There appear to be only some minor discrepancies using the newest code eg. PTY code 10 (EU) is Pop Music but reference is made to Top 40 (Pty code 9 NA). Otherwise it's pretty close, small things like Country Music (EU) instead of Country (NA) or Weather & Metr (EU) instead of Weather (NA) Will use the alternate LUT if feeling pedantic!

      Problems that I'm still having:

      1) It is actually the second line of LCD data that seems to be troublesome for me, the first line is fine. I have now changed from the diode to a FET level shifter on the GP01 line (Pin 12) and it's remained the same.

      The behavior is the same if I send commands via the serial interface so I'm now thinking it might not have to do with the rotary encoder input though I might change the encoder as the reading of commands / direction seems a bit 'iffy' using the current one.

      [Just to clarify what I've done - the common on the encoder to the Arduino ground and A and B straight to pins 2 and 3 - (Your code activates internal pullups?) The pushbutton has a 10k pull down resistor and momentary closure puts 5v to pin 5.]

      Can you tell me about pins 7, 8 and 9? I haven't got anything connected right now.

      Further fiddling makes me think that when the signal is poor on the selected frequency (tuned manually or using seek) the (flashing/ updating of line2) problem persists. It seems to stop frantically updating the 2nd line once it's received the PTY data or something? (i.e. when on a strong station, slight pause and display of PTY with normal 2nd line)

      Commenting out the whole of the Line Two Behaviour section causes no change in the flickering, it still flashes the 'Freq: **.*Mhz' line. Commenting out (//) the line :

      update=true;//Indicate that the LCD needs to be update

      in the Rotary callback function gets rid of the problem but then the Frequency display etc. won't work as intended.

      Hopefully I'm getting to the root of the cause…

      Some further observations:

      Sometimes on startup, the frequency is .1Mhz higher than expected. This may have occurred more with USB power but I'm not sure. (For me, the device appeared better behaved in some ways on a battery supply but I think this may be due to less USB noise and therefore better reception! It did lock up at least once if I remember correctly)

      2) I haven't managed to get the metrics to display properly as part of the line one behavior. I tried uncommenting the lines in the Line One Behavior (Display Behavior) section but got a compiling error. Must have missed something

      Please excuse the long post, will hopefully be 'recharged' tomorrow and get back to this with renewed energy.

      ReplyDelete
    23. Yeah, by combining the two PTY LUTs I had to make some compromises, but these are easy enough to correct if you want them to match the actual standard for EU. I probably should put comments on each line indicating the actual label for each locale.

      1) Try commenting out line 164 through 166 (or setting the conditional statement to be true only once such as refresh_cnt==50). It's a long shot, but this might be the reason why it is acting weird. I put this peice in to help catch a station that is sending a noisy PTY field. Since the PTY is just a set of 5 bits it is very prone to error and the output from the LUT with a single bit error will result in drastically different results.

      I have had thoughts of trying to setup a way to read the PTY field several times when switching to a new station and taking the most frequently occurring result but I haven't quite gotten around to doing it yet. Maybe that will be something I add to the library in my next update.

      Your wiring for the rotary encoder is the same as what I did.

      Pins 7,8,9 are noted in my pde file as: RADIO Slave Select, RADIO Power, RADIO Reset. I forgot to correct Slave Select to say pin 10, since that is what the Si4735 normally maps the SPI SS line to (and what the Si4735 uses by default). My actual code I run is a very "slight change" in pin mapping to allow another shield to work within the system (which is a future project I will most like share down the road). In short you should not have to touch pins 8,9,10 since these are being used internally on the shield.

      The startup being .1MHz off is pretty normal occurance for me. I don't know what the cause is at the moment, but I think I need to add a delay somewhere due to stability issues with the signalling...that's just a wild guess though.

      2) The stuff that was commented out on line 151 is old code that is incompatible with the latest version of the Si4735 library. Simply remove this and it should work (showRSQ actually gets the data and shows the data)

      ReplyDelete
    24. Just a quick update:

      Looked at this persisting problem for a short bit yesterday and today. I am now suspicious that this may actually be a hardware issue. (Noise or poor data on one or more of the lines?)

      A brief experiment after rewiring the LCD and running the Uno and the serial LCD on an external DC power supply with a slightly increased voltage seems to have fixed a lot of issues! (e.g. the rapid flashing on line 2 that I had previously was gone - there was the odd flash once the scan has settled on a frequency though, do you have this?) Wonder whether the USB power I was suppling was adequate...

      Good to know that you are also experiencing the .1Mhz abnormality. I haven't yet looked at this in detail.

      More experiments to follow I expect.

      ReplyDelete
    25. I haven't fine tuned this yet but it appears that by adding a 1 second delay right after calling setLocale solves the .1MHz anomaly. Could you confirm this on your end?

      The flicker after switching to a new station is normal behavior. My code does this deliberately; however, I don't know if it is really needed anymore. Originally my code had a strange tendency of exiting out of a while loop too soon that is suppose to keep rechecking the tuned frequency until the Si4735 indicates that the data is valid. If I recall this was mainly due to the rotary encoder bouncing (or if you twist and turn the rotary encoder back and forth) which causes the code to somehow lose track of the frequency that it is tuned to. I think if you comment out line 533 (which sets refresh_trigger=true) this should remove that flickering behavior.

      One other thing you might notice is that the code does have a very rare occurance of locking up the interrupt code. Everything else is responsive but the rotary encoder does not respond. This has been something I cannot seem to reproduce in any reliable manner. Just something to keep your eye on.

      PS I really appreciate your feedback. Keep it coming =)

      ReplyDelete
    26. Today I messed around putting caps and pull up / down resistors all over the shield to try and mitigate some of these issues - I'm sad to report that my efforts made very little to no difference. The majority of the flashing is gone after the re-wire though I'm still having some issues.

      Adding the delay of 1s didn't entirely solve the issue for me sadly - I changed the delay to 4s to see if I could determine when the change up in frequency occurs - I had a couple of .1Mhz shifts after the delay (maybe 3 in 20 USB plugs and unplugs which subjectively seems less than without the delay but I haven't actually done a proper comparison) so I think it may be somewhere else. Is your shift totally gone over +- 25 full power cycles? (not resets)

      By rotating the encoder forward and backwards rapidly in seek mode, I can get the display to lock up, sometimes it does so without loosing control of the radio seek function. (Strange!)

      Even stranger is that once the display has locked up, the seek response was actually quite good. I noticed that the L led (Pin 13 - SCLK) light is normally extremely active but once the LCD no longer updates (and rotary encoder seems stuck in a usable seek mode) it only flashes on turning the encoder to change station.

      Have you heard the zipper noise from rotary encoder movement sometimes? I'm still not entirely satisfied with its response to rotation.
      Really torturing the encoder can also get 655.3Mhz displaying or ?0.0Mhz or something.

      What speed is the SPI running at? Do you think there might still be an issue here?

      I think my next step might be to slowly remove bits of code or use very basic code to see if I can narrow things down further and see if this is hardware or software.

      It's great to bounce ideas around, I really appreciate your input.

      ReplyDelete
    27. After 10 or so restarts I did see the issue return with the .1MHz offset. Is yours also always an additive .1MHz?

      Not sure what you mean by zipper noise. But I have seen the 655.3MHz and 0.0MHz show up; I am led to believe that this is likely due to the interrupt behavior and it is most likely outside the control of the user.

      I believe the SPI is configured for 500KHz inside the Si4735.cpp the statement that configured the bus speed is:
      SPIClass::setClockDivider(SPI_CLOCK_DIV32);

      Since the Arduino has a 16MHz clock, a 32clock divider would give us 500KHz. I will try to get my logic analyzer up an running to verify this when I get a chance.

      I finally think I see what you are referring to for the flickering. I had to remove another shield that I had connected to observe this though...this seems to suggest that it is a signalling issue. Either something needs timing adjustments or there is a capacitive or resistive issue with the signals. I have a total of 3 stackable headers (not including whats on the arduino itself) connected on each header. This is becoming a tricky thing to debug.

      ReplyDelete
    28. Yep, I verified that the SPI Clock is at 500KHz using my logic analyzer

      ReplyDelete
    29. Yes, I almost always have a shift up of .1Mhz when it gives the start up problem.

      I am glad that you were able to reproduce the flickering. I don't feel so alone now!

      The zipper noise I am talking about is a kind of clicking noise heard from the audio out when turning the encoder.

      Didn't spend too much time looking at the project today but did a bit of searching on the net.

      Have you seen this document for the Si4734?

      http://home.comcast.net/~phils_radio_designs/Si4734_Software.pdf

      ReplyDelete
    30. I downloaded Ver 0.8 of AN332 (12/11) and have been fiddling with some of the startup commands.

      Have you looked at 0x80 and 0x81 as mentioned in the previous document? I have been playing with these start up commands a bit.

      Interested to hear your thoughts on the GPO2/INT line and suspending data on the SDIO line during seek / tune.

      ReplyDelete
    31. I tried adding this to the begin method after the powerup sequence was executed but it made things very unstable:

      sprintf(command, "%c%c", 0x80,0x0E);
      sendCommand(command, 2);
      delay(10);
      sprintf(command, "%c%c", 0x81,0x04);
      sendCommand(command, 2);
      delay(10);

      I tried several other combinations and I had no luck.

      The problem with suspending data on the SDIO line during seek/tune is that at the end of the day you need to check the registers to see what the new frequency is. If I had another interrupt line I would have used the CTS interrupt feature on the chip to do just that. But both of my interrupt pins are being used by the rotary encoder. What might be worth trying is to insert a delay for a period of time after executing the seek operations so that we give it some time before we actually start checking the frequency.

      If only the UNO had one more interrupt pin so that I could utilize the CTS interrupt feature.

      ReplyDelete
    32. 1) I inserted this code after the 0x01 power up command in Si4735.cpp and it seems to be okay for me:

      sprintf(command, "%c%c", 0x80, 0x06);
      sendCommand(command, 2);
      delay(200);

      sprintf(command, "%c%c", 0x81, 0x00);
      sendCommand(command, 2);
      delay(200);

      The delays are probably OTT but I haven't felt a need to change them.

      2) I think it might be worthwhile investigating the use of the Si4735 interrupt further - I'm thinking it may improve overall performance significantly...

      Excuse my ignorance here - why is it necessary to have the rotary encoder on both interrupt pins of the Uno? Is there a way to rework this to use only Digital Pin 3 for the rotary encoder interrupt and move the other rotary encoder pin to a spare pin leaving Digital pin2 (GPIO/ INT) free ?

      If not, another solution may be to use a(nother) push button only on Digital pin 3 (instead of an encoder) and work out some code to work out forward and back from the duration of the push. eg. >0.5sec = backwards and <.50sec = forwards.

      3)As a experiment, I changed the defining of EncB from pin 2 to pin 4 in the .pde and moved the encoder pin off of Pin2 - it appears to have solved the .1Mhz issue (I haven't had it return so far) which makes me think that the rotary encoder/ code doesn't work so well on the Si4735 INT pin.

      4) Read a comment on the Sparkfun product page that someone was having big hassles with the shield on a Mega - may be best to keep with the Uno.

      ReplyDelete
    33. 1. I didn't really notice much difference but I do have the other shield connected right now so it might just be masking any changes that are really occurring. I will leave it with this change though unless something else comes to mind.

      2. Simply stated yes you can use just one interrupt with a rotary encoder and still get the job done (atleast as far as this project goes). There are some drawbacks to using only one interrupt (mainly for applications where careful state tracking is important) but this is not an application that this really matters. I made this rotary encoder library with the intent of using it for applications that this maybe important; however I am working on making another library that works exactly the same as the current one 'cept it only uses one interrupt pin. Once this is updated to the level I am satisfied with I will look more into the interrupt mechanism for the Si4735.

      3. Unfortunately for me I did not see this occur, but with the update to the new rotary encoder library it will be configured to this pin configuration (actually I will have it use pin 5 and move the PB to pin 6 instead since the other shield that I have uses pin 4, but that's easily changed for your own needs)

      4. Interesting, I do have a Mega that I don't use, I might have to try this out sometime and see if I get the same thing.

      ReplyDelete
    34. I'm not sure how much difference (if any) the extra code made - it doesn't seem to have negatively affected anything that I'm aware of.

      2&3 - This sounds awesome. I'm not sure I have the ability to recode your project to use the interrupt feature of the Si4735 but I would love to see what you get up. A library that uses only one interrupt pin for the rotary encoder sounds ideal.

      I think I will delve into AN332 a little more and see how far I get.

      ReplyDelete
    35. What an awesome project! Sorry for the newb question however I am trying to duplicate your setup but I need to know how to wire in the Logic Level Converter (SparkFun BOB-08745). I have cut the trace on the Si4735 in accordance to the photo guidance on http://wiki.wsartori.com/wiki/Trunet_Radio but I just cannot make out what to connect the Arduino pin 12 to on the Logic Level Converter as per KA1KJZ's page http://www.ka1kjz.com/?p=1331#respond Thank you so much for your assistance an a great project.

      ReplyDelete
    36. Hi QHENT thanks for the feedback and interest in my project. Pin 12 on the Si4735 shield is connected to the GPO1 pin on the Si4735 chip. This is the SPI output for the chip. Since the chip natively works on 3.3V, the logic level threshold is just slightly lower than what we need for a microchip that uses 5V logic. This will cause any 5V uC to have frequent bit errors. We can use a logic level converter (aka a specially configured FET) or diode to help with the communication problems this causes. With the BOB that you are trying to use you simply need to connect the Si4735's trace that goes to pin 12 to one of the the TXI lines and connect the corresponding TXO line to the Arduino. Then just connect the HV pin on the BOB to the 5V VCC and the LV pin to the 3.3V VCC that is present on eith the Arduino or the Si4735 shield.

      ReplyDelete
    37. Nice! TXI (Transmit Input), TXO (Transmit Output), HV (High Voltage...5VDC from Arduino), and LV (Low Voltage...3.3VDC to the Si4735). I guess reading is fundamental. :) All wired up and working perfectly. Thank you so much for the rapid response/clarification and totally awesome project. Have an OUTSTANDING New Year!

      ReplyDelete
    38. An RT message is not necessarily 16 segments (64 characters) long. A shorter message is terminated by 0x0D and the following segments need not be transmitted. Looking at your code, it seems as if it's always looking for 16 segments.

      if (strlen(tuned.radioText) == 64 & refresh_trigger==false)

      What am I missing?

      ReplyDelete
    39. JRMN,
      You are correct in RadioText messages being any size <=64 characters. The part of the library that reads the RadioText is based off of trunet's readRDS function (which I have expanded to read/process more of the message types). His approach was to statically allocate memory for the radiotext to be a constant 64 characters and that the rest of the unused space is padded with spaces. While this might not be the most memory efficient option it works and at the time I did not think it was worth the effort to update it so that I could save a few extra bytes.

      The statement "strlen(tuned.radioText) == 64" works only due to the fact that the clearRDS function writes NULL to all elements in the _disp private variable. Whenever the station is changed, the clearRDS method is called. If a station changes it's RadioText from type A to B or vice versa, the _disp gets cleared with spaces. This is more or less just an aesthetics mechanism to get the radio to function how I want it to.

      At any rate the radioText portion of readRDS method probably should be cleaned up and fixed to follow the specification more closely. I would also expect that it might clean up some possible bugs that I have yet to encounter. I know the statement you pointed out, does have the potential to cause a problem if you switch to a station that is using a message smaller than 64 characters; however if you are currently on a station that currently has a 64 character radiotext and then it switches to a smaller size I don't believe there will be an issue. The root cause of this bug is really a problem with the library. To remedy this possible bug I updated the code.

      Hopefully this clears things up a little.

      ReplyDelete
    40. Hi Mr Carrier,

      I currently work on a SI4735 radio project drived by a PIC18f2550 in i2c, i use PIC18 Simulator IDE (basic compiler), i am unable to get back information's from the device. I saw somewhere that you need to add a diode to GPO pin for reading. Did you think that i need to do it also? Where can i get more information's about this?

      ReplyDelete
    41. Hi Ben,
      According to the specs for the PIC18f2550, this PIC can be powered from 2V to 5.5V. The SSOP package type (the one that Sparkfun stocks) for the Si4735 also operates in this voltage range. If you are using the same power supply for both of these then you should not need to do any sort of logic level shifting.

      Other than making sure that the supplies powering the two chips are the same, I would say that you should double check the initialization procedure and make sure it is properly configuring the Si4735 ship to operate in I2C mode.

      ReplyDelete
    42. Hi Jon,

      I'm very interested your Si4735 radio project, and have been following your progress.

      I'm running into some difficulty in getting the project to work.
      I down loaded your libraries from github. In the examples for the Advanced Radio library, there are three additional libraries to add to the Arduino library; Helper, Rotary, and SerLCD.

      For both the Helper and the SerLCD libraries, there is only the Helper.h and SerLCD.h files. There is no Helper.cpp or SerLCD.cpp. I'm wondering if this is correct.

      There is both the Rotary.h and Rotary.cpp files, however.
      I'm getting many errors when trying to run the Advanced Radio Sketch and wonder if that is why.

      I've been reading about your progress with the project and I'm very impressed with it. You've certainly put a lot of work into it.

      ReplyDelete
      Replies
      1. Hi Paul,

        Thanks for your interest. Neither Helper or SerLCD have cpp files since they are only defining subroutines. However I have updated my personal version of the code to make SerLCD a class library which has both a header file and a cpp file, but I have not committed the code yet to github. This will likely happen on my next major update.

        The main problem you are running into is likely the issue that I have been trying to slowly address. If you are using a newer version of Arduino IDE (i.e. v1.0 or newer) you are likely getting a bunch of errors do to major changes Arduino.cc have made to the underlying mechanics of the IDE. I would suggest trying to compile using version 22 (version 23 I think will work as well, but I have not personally tried this) until I have updated the library.

        Hopefully this will solve your issue. If it isn't, could you provide me with the compiler's output?

        Delete
      2. Jon,

        That was the problem, I was using v1.0.

        After trying it with version 22, it worked great!

        Delete
      3. Great to hear. I have updated the library that now addresses that issue and changes a few other things (which you can read the top of the blog post to see).

        Delete
    43. Hi again Jon,

      Checked back and decided to try your newer code but initially had a problem compiling. (v1.0)
      Looks like the Si4735.cpp code hosted at github got pasted twice? A bit of a chop and it seems to compile ok. Hope to try on the hardware sometime soon.

      Again, a big thank you for the project.

      ReplyDelete
    44. Just looked at it again briefly and noticed that the 1st and 2nd codes in the .cpp are at least a little different? e.g. The second section of code adds #include "string.h" and #include "Wprogram.h" (renamed Arduino.h in v1.0?) Maybe I'm missing something here?

      ReplyDelete
      Replies
      1. Thanks for mentioning this, I was having troubles with committing/pushing my code to github. I knew something probably got messed up but I wasn't really sure.

        The library itself didn't really get much of an update this time around. I only updated my sketch (and associated libraries) to support Arduino IDE v1.0. The change you noticed IS the only difference as far as I can remember. There was no reason for me to include string.h both in Si4735.cpp and Si4735.h.

        Also, I have started looking into creating support for the MEGA. As it stands right now, my sketch can compile when using the MEGA, but when I try to upload, it times out and basically fails to upload the sketch. I will probably create a very basic sketch from scratch and slowly add more components into the design to determine what the problem is. Hopefully this issue can get resolved because I also have plans to use a MEGA for this project at some point.

        Delete
    45. Hi Jon,
      First of all, thanks for taking the time and effort to keep this library in good shape. I spend half of last night playing with my new Si4735 breakout board (not the full Arduino shield) and an Arduino Pro Mini328 3.3V/8MHz and it was all easier thanks to your code.

      I've found a few places where the library could benefit from some refactoring, it is okay if I clone your repository, do the work and then send you a diff? I would really like to give something back to the community :-)

      Thank you for your time.

      ReplyDelete
      Replies
      1. Ulysses,
        Absolutely, help is always appreciated. I will be interested to see what changes/additions you make.

        Delete
    46. I would also be interested to see the changes you make Ulysses.
      I've been following Jon's work (kudos to him!) and playing with the Sparkfun shield myself.

      ReplyDelete
      Replies
      1. Ulysses code/changelog is available here:
        https://github.com/csdexter/Si4735/commit/399acfed5a09ec826ec15598af316e1993b0d83f

        I have been very busy this week with personal matters, but when I get some free time I will work on merging his changes into my library. It looks like he did an excellent job on cleaning up the code making it easier to read.

        Delete
    47. (sorry, my english is somewhat limited)

      Hello Jon, Your project and libs were really helpfull, but I didn't get good responses from the FM Shield, I went for the 1N4148 Diode method to level the voltage, but I didn't get the getFrequency correct.
      First thing that I look, was that the internall pullup resistor were activated in the SI4735.cpp, I think (I may be wrong) that it wasn't set in that way, so I changed, but without luck, so tinkering a little more I tried pulling up a little more externally (not technically brillant perhaps) but IT WORKS, I put a 10k resistor between 5v and D12 (the same pin as the diode) and now I can see the perfect response to the getFrequency call. I mention because somebody else could be having the same problem, so maybe this helps, OR, if someone thinks that is not at all a good idea, please, let me know. I gave a 2 hours round with many calls to getFrequency and the shield still works like charm.

      Anyway, thanks for your efforts and sharing your work, this blog and trunet were very helpfull.

      ReplyDelete
    48. (corrected my spelling)

      Looking at the retry loop in the setup routine to tune the frequency, the attempts loop counter is never incremented.

      The while loop test is also short circuited so that attempts is never compared to max_attempts.

      I think this never shows up on a board that is properly configured to work with a 5v arduino since it will tune correctly. I just happened to try it with an unmodified shield and it loops forever

      ReplyDelete
      Replies
      1. Right you are. Thanks, I will update that portion of the code.

        Delete
      2. You should only break if attempts>max_attempts not attempts<=max_attempts (which will break immediately)

        Delete
      3. Yeah, not sure why I typed it wrong when moving that statement down.

        Delete
    49. I am having difficulties getting correct responses from the Si4735 Shield. I tried both the diode approach (1N4148) and the logic level shifter-approach but no success. The radio works like a charm but I would really like to be able to see more information than the frequency on the display. Does anybody have any suggestions?

      I see that "diten" mentioned the use of a 10k resistor. Is this instead of the diode? With my limited electronics knowledge I don't see how this would work...

      ReplyDelete
      Replies
      1. Hello Jonas, no, my workaround works with both components, the diode plus the resistor. I don't know if is the best method, but my shield works that way and it displays consistent results with the getFrequency function call. Please let me know if your work too. bye.

        Delete
      2. I'm not sure I understand your configuration. Do you mean that you soldered the resistor between 5V and D12 while still having the diode placed as in: http://wiki.wsartori.com/wiki/File:IMG_8955.JPG ?
        I'm ready to try just about anything to get it working. Hopefully I will have some results tonight.

        Delete
    50. Unfortunately this didn't help. Whenever I connect the diode the sound from the radio becomes "choppy" and there is no reponse or RDS. Maybe I have damaged the board somehow. Without the diode the sound is very good and I will probably just use it that way.

      ReplyDelete
    51. I noticed choppy sound on my unmodified board when running the sketch due the initial tuning while loop that was continually re-tuning the radio so check your code and make sure the software isn't causing the choppiness.

      ReplyDelete
      Replies
      1. I don't think it is due to the code since it works flawlessly when using a non-modified board. I think I will just skip RDS. It is going to be a birthday gift for my father and he only listens to one station anyway...

        Delete
    52. From the changelog of Arduino's latest revision:

      Added INPUT_PULLUP argument to pinMode() function. The INPUT mode now explicitly disables the pullup resistors.

      I don't know if this is supposed to break anything on the library, but I did see pins being set "high" and then "input", which has always been the way to set the pullup resistors.
      What I know is that I wasn't able to get any response from the board, not even the current frequency. After changing INPUT to INPUT_PULLUP, I can get the frequency, and some RDS information (although the newRadioText flag doesn't seem to catch every new information, I'm still working on getting the correct RDS info). The "updated" relevant piece of code:



      //Now configure the I/O pins properly
      pinMode(DATAOUT, OUTPUT);
      pinMode(DATAIN, INPUT_PULLUP);
      pinMode(SPICLOCK,OUTPUT);
      pinMode(SS,OUTPUT);
      pinMode(INT_PIN, INPUT_PULLUP);
      digitalWrite(SS, HIGH);

      Would this be a valid modification/correction?

      ReplyDelete
    53. Just to update my previous comment, the fix I proposed (passing INPUT_PULLUP as a parameter to pinMode) does seem to get SPI (and, thus, RDS) working on Arduino 1.0.1. If I ignore the newRadioText flag and update every time the method readRDS() returns true, I manage to get the correct information from the station.

      ReplyDelete
      Replies
      1. Thank you for your solution! My SPI seemed to be working properly until I tried to use the getFrequency function. I was getting weird values, but using INPUT_PULLUP as parameter has fixed my problems.

        Delete
    54. hi, i'm trying to get RDS information from Si4735, but when i'm compiling the program, Arduino 1.01 print:
      "
      Si4735_RDS.cpp: In function 'void loop()':
      Si4735_RDS:127: error: no matching function for call to 'Si4735::getRDS(char [9], char [65])'
      C:\Program Files\Arduino 1.0\libraries\Si4735/Si4735.h:220: note: candidates are: void Si4735::getRDS(Station*)
      "
      this is the piece of code:
      "
      case 't':
      case 'T':
      radio.readRDS();
      char ps[9];
      char radioText[65];
      radio.getRDS(ps, radioText);
      Serial.println(ps);
      Serial.println(radioText);
      break;
      "
      I don't know what i can do, can anyone help me? this is the only one blog that has got results with this ic

      ReplyDelete
      Replies
      1. Hi Davide,
        I have been on a hiatus of sorts with this project but I think I can still help. The problem is that you are using a statement that does not match the function declaration for the getRDS method. My version of the library uses (as the error message says) "void Si4735::getRDS(Station*)". You are trying to use the method with two arguments instead of one. My guess is that you got this Si4735_RDS.cpp file from someone else's project that used a difference variant of the Si4735 library. My library has not been updated for a while and lots of people have done forks off of the work I have done on the Si4735 library. I would suggest that you find out were you got the Si4735_RDS.cpp file from and then use the same version that they used.

        As an alternative you could try to fix this issue by creating a global variable that uses the "Station" structure and then update all the code that make use of the programService, radioText, callSign, and programType data. For starters you would need to add/change the following:

        Station myStation; //Define this globally
        radio.getRDS(&myStation); //use this instead of your other getRDS function call

        All other statements that use that the data that resides in the Station structure need to be updated. The station structure in my version of the library is defined as:

        typedef struct Station {
        char callSign[5];
        char programType[17];
        char programService[9];
        char radioText[65];
        bool newRadioText;
        };


        For instance your statement "Serial.println(ps);"
        should be changed to:
        Serial.println(myStation.programService);

        Hope this sets you in the right direction.

        Delete
      2. Jon, thanks so much!!! after two months i finally read the RDS from Si4735!
        I was wrong when i declared the variable "station", because I thought that was the name of a variable and not a type of variable.
        Thanks so much again!

        Delete
    55. Hey Great job with your lib. I ran into an odd problem, when I try to include wire.h with your project to get it talking to another arduino, it just doesn't work. Any ideas why?

      ReplyDelete
    56. Hello Everyone,

      I was wondering, in the 80's AM and FM radio watches were popular and today collectibles. Do you think si4735 chip could ever be implemented into a watch form. And if it could what do you think it would take for it too work. Love the work you done Jon Carrier and want to pursue a career in electronics.

      Thank you,
      Nick

      ReplyDelete
    57. Hi Jon. I wouild like to get schematics and know how to change the code to run 16.2 lcd or 0,96 Oled as I2C. Could U help-me? Nice project Thanks

      ReplyDelete