Skip to content

Suggestions for modifications #2

@drp0

Description

@drp0

Your library is very useful, thanks.

The rds transfer functions are particularly useful.
There is probably work to do on checking whether the rds is
a) Error free
b) Relevant to the current channel after the channel has changed

I could not eradicate these issues.
I was able to improve reliability with a few added modifications:

In SI470x.cpp

  1. add interrupt
    reg04->refined.RDSIEN = 1; // was 0 = no interrupt drp

  2. add link interrupt:
    reg04->refined.GPIO1 = reg04->refined.GPIO2 = reg04->refined.GPIO3 = 0;
    reg04->refined.GPIO2 = 1; // + drp

  3. check for number of errors before returning true in getDdsReady():
    bool SI470X::getRdsReady()
    {
    getStatus();
    if (reg0a->refined.RDSR) { // drp re-write from: return reg0a->refined.RDSR;
    if ((reg0a->refined.BLERA) < 6) return true;
    }
    return false;
    };

  4. Add get the number of rds errors:
    int SI470X::getRdsErrors()
    { // drp added
    getStatus();
    if (reg0a->refined.RDSR) return reg0a->refined.BLERA; else return -1;
    };

In SI470x.h :
add to public
int getRdsErrors(); // drp

This is a working example for the reasonably priced 128 x 128 oled screen at
https://shop.pimoroni.com/products/1-12-oled-breakout?variant=29421050757203
It uses the interrupt from GPIO2, attached to D3, to initiate rds transfer.
There are options for Serial output and polled rds transfer.

//  si470x_RDS_oled3.ino
//  D.R.Patterson (drp)
//  15/11/2020
//  Adapted from example in U8x8lib library
//  https://github.com/pu2clr/SI470X
//  Requires drp modified version of the SI470X library

//  Will work with headphones / lineout
//  With laptop:
//    radio volume 2
//    use TECNET usb input- volume < 5
//    set audio input to listen - output spakers

//  Arduino Pro Mini and SI4703 wire up

//  | Device  Si470X |  Arduino Pin  |
//  | ---------------| ------------  |
//  | RESET          |     D2        |
//  | SDIO           |     A4        |
//  | SCLK           |     A5        |
//  | GPIO2          |     D3        |

//  ATTENTION:
//    Best to avoid using the computer connected to the mains during testing.
//    Run the computer on it's battery.
//  By Ricardo Lima Caratti, 2020.

#include <SI470X.h>
#include <U8x8lib.h>
#include <Wire.h>

bool haveOled   = true;
bool useSerial  = false;
bool useIrq     = true;
bool useRds     = true;

U8X8_SH1107_PIMORONI_128X128_HW_I2C u8x8(U8X8_PIN_NONE);

const byte resetPin = 2;          // radio reset
const byte SDIO     = A4;         // sda
const byte SCLK     = A5;         // scl
const byte pot      = A3;         // volume pot
const byte potVs    = A2;         // 3v3 supply for volume pot
const byte led      = A1;         // stereo led indicator
const byte bat      = A0;         // read battery voltage across potential divider
const float m       = 1/0.3222;   // battery voltage multiplier 1 / potential divider ratio
const byte favP     = 4;          // favourite select port
const byte up       = 5;          // frequency up select port
const byte down     = 6;          // frequency down select port
const byte rdsP     = 7;          // tggle RDS port
const byte GPIO2    = 3;          // radio irq detect pin 

unsigned int fav1 = 885;    // Radio 2
unsigned int fav2 = 892;    // Pride Radio
unsigned int fav3 = 907;    // classical R3
unsigned int fav4 = 929;    // radio 4
unsigned int fav5 = 936;    // Radio Tyneside
unsigned int fav6 = 954;    // BBC Radio Newcastle
unsigned int fav7 = 971;    // metro
unsigned int fav8 = 975;    // smooth radio ne
unsigned int fav9 = 981;    // Radio 1 (loud)
unsigned int fav10 = 1003;  // classical (loud)
unsigned int fav11 = 1018;  // Heart 
unsigned int fav12 = 1053;  // Capital NE
//unsigned int fav12 = 1075;  // Smooth Radio NE (weak)

String station[] = {
"BBC 2", "Pride", "BBC 3", 
"BBC 4", "Tyneside", "Newcastle", 
"Metro", "Smooth", "BBC 1",
"Classical", "Heart", "Capital NE",
};

String fav        = "12342567890*/";
byte volume       = 8;
bool useVolPot    = true;
byte favNow       = 1;
unsigned int Channel;

#define RESET_PIN 2

#define MAX_DELAY_RDS 80   // 40ms - polling method
#define MAX_DELAY_STATUS   2000 

unsigned long rds_elapsed;
unsigned long status_elapsed;
unsigned long vstart;
volatile bool haveRds = false;

SI470X rx;

void irqDetect(){
haveRds = true;
}

void setup(){
  if (useSerial){
  Serial.begin(115200);
    while (!Serial) delay(100);
  }
  if (useSerial) Serial.println(F("\nPU2CLR SI470X Arduino Library."));
pinMode(potVs, OUTPUT);
  if (useVolPot) digitalWrite(potVs, HIGH);
pinMode(favP, INPUT_PULLUP);
pinMode(up, INPUT_PULLUP);
pinMode(down, INPUT_PULLUP);
pinMode(rdsP, INPUT_PULLUP);
pinMode(led, OUTPUT);
pinMode(GPIO2, INPUT_PULLUP);

Wire.begin();
Wire.beginTransmission(0x3C);
  if (Wire.endTransmission() == 0) {
  u8x8.begin();
  u8x8.setFont(u8x8_font_8x13B_1x2_f);
  u8x8.clear();
  u8x8.print(F("FM Radio"));
  //u8x8.setCursor(9, 14); u8x8.print(F("dBuV"));
  haveOled = true;  
  }else{
    if (useSerial) Serial.println(F("No oled"));
  Wire.end();
  haveOled = false;
  }

rx.setup(RESET_PIN, A4);  // A4 SDA pin  for Arduino ATmega328
rx.setBand(0);		        // Europe- do not setSpace(1) as reception stops

rx.setRDS(true);          // Turns RDS on

  if (useVolPot) {
  potVolume();
  }else{
  rx.setVolume(volume);
  }

delay(500);

// Select a station with RDS service in your place
  if (useSerial) {Serial.print(F("\nTuning ")); Serial.println(fav1);}
setFav();
// Enable SDR
rx.setRds(true);
rx.setRdsMode(1);   // verbose
rx.setMono(false);

  if (useSerial) showHelp();
rds_elapsed     = millis();
status_elapsed  = rds_elapsed;
vstart          = rds_elapsed;
attachInterrupt(digitalPinToInterrupt(GPIO2), irqDetect, FALLING);
}

void loop() {
unsigned long tnow;
  if (useRds){
    if (useIrq) {
    // check flag from interrupt line
      if (haveRds) {
      checkRDS(false);
      haveRds= false;
      }   
    }else{
    // polling rds
      if ((millis() - rds_elapsed) > MAX_DELAY_RDS ) {  
        if ( rx.getRdsReady() ) checkRDS(true);
      rds_elapsed = millis();
      }
    }
  }
   
  if (useVolPot)              potVolume();
  if (digitalRead(favP) == 0) setFav();
  if (digitalRead(up) == 0)   setUp();
  if (digitalRead(down) == 0) setDown();
  if (digitalRead(rdsP) == 0) setRds();

tnow = millis();
  if ((tnow - status_elapsed) > MAX_DELAY_STATUS ) {
  status_elapsed = tnow;
  showStatus();
  }

  if(useSerial) checkSerial();

tnow = millis(); 
  if ((tnow - vstart) > 999) {
  vstart = tnow;
  batVolt();
  }
}

/*********************************************************
   RDS
 *********************************************************/
char *rdsMsg;
char *stationName;
char *rdsTime;

void showCount(int errcount){
u8x8.setCursor(13,6);
  if(errcount == 0){
  u8x8.print(F("   ")); 
  }else{
  u8x8.print("E"); u8x8.print(errcount);
    if((errcount < 10) && (errcount > -1)) u8x8.print(" ");
  }
}

void checkRDS(bool polling){
int P;
String tmp;
  if (polling){
    if (!rx.getRdsReady()) return;
  }
int errCount = rx.getRdsErrors();             // get the number of errors
  if ( (rdsMsg = rx.getRdsText2A()) != NULL) {    
    if (useSerial) Serial.println(rdsMsg);
    if (haveOled) {
    wipe(14); wipe(12); wipe(10); wipe(8);
    tmp = rdsMsg;
    P = tmp.indexOf('\0');
      if(P > -1) tmp = tmp.substring(0, P);
    tmp.trim();
      if (tmp.length() > 0) u8x8.print(tmp.substring(0, 16));
      if (tmp.length() > 15) {u8x8.setCursor(0,10); u8x8.print(tmp.substring(16, 32));}
      if (tmp.length() > 31) {u8x8.setCursor(0,12); u8x8.print(tmp.substring(32, 48));}
      if (tmp.length() > 47) {u8x8.setCursor(0,14); u8x8.print(tmp.substring(48, 64));}
    showCount(errCount);    
    }
  }
  else if ( (stationName = rx.getRdsText0A()) != NULL){
    if (useSerial) Serial.println(stationName);
    if (haveOled) {
    tmp = stationName;
    P = tmp.indexOf('\0');
      if(P> -1) tmp = tmp.substring(0, P);
    tmp.trim();
    P = tmp.length();
    u8x8.setCursor(1, 6); u8x8.print(tmp);
      for (byte I = P; I < 12; I++) u8x8.print(" ");
    showCount(errCount);
    }
  }
  else if ( (rdsTime = rx.getRdsTime()) != NULL ){
    if (useSerial) Serial.println(rdsTime);
  }
}

void setFav(){
  if (favNow == 1) Channel  = fav1;
  if (favNow == 2) Channel  = fav2;
  if (favNow == 3) Channel  = fav3;
  if (favNow == 4) Channel  = fav4;
  if (favNow == 5) Channel  = fav5;
  if (favNow == 6) Channel  = fav6;
  if (favNow == 7) Channel  = fav7;
  if (favNow == 8) Channel  = fav8;
  if (favNow == 9) Channel  = fav9;
  if (favNow == 10) Channel = fav10;
  if (favNow == 11) Channel = fav11;
  if (favNow == 12) Channel = fav12;
  if (haveOled) u8x8.clear();
rx.setFrequency(Channel * 10);
  if (useSerial){
  Serial.print(F("\nFavourite ")); Serial.println(favNow);
  }
showStatus();
  if (haveOled){
  u8x8.setCursor(0, 4);
  u8x8.print(F("Favourite ")); u8x8.print(favNow);
  showFav(favNow);
  }

favNow++;
  if (favNow == 13) favNow = 1;
  while(digitalRead(favP) == 0){
  delay(100);
  }
}

void wipe(byte thisLine){
u8x8.setCursor(0, thisLine);
u8x8.print(F("                "));
u8x8.setCursor(0, thisLine);
}

void showFav(byte fav){
wipe(14);
String tmp = station[fav - 1];
byte L = (16 - tmp.length()) / 2;
  for(byte I = 0; I < L; I++){
  tmp = " " + tmp; 
  }
u8x8.print(tmp);
}

void batVolt(){
float V = analogRead(bat);
V = m * V* 3.29 / 1024.0;
u8x8.setCursor(12, 0);
u8x8.print(V,1); u8x8.print("V");
}

void potVolume(){
static byte oldVolume = 255;
static int oldVal = -1000;
int newVal = analogRead(pot);
  if(abs(newVal - oldVal) < 5) return;  // allow for pot jitter
oldVal = newVal;
volume = map(newVal, 0, 960, 0, 15);    // map(value, fromLow, fromHigh, toLow, toHigh)
  if (volume != oldVolume){
  oldVolume = volume;
  rx.setVolume(volume);
  u8x8.setCursor(10, 2); u8x8.print(volume);
    if(volume < 9) u8x8.print(" ");
  }
}

void setRds(){
useRds = !useRds;
  if(useSerial){
  Serial.print(F("Use Rds: "));
  Serial.println(useRds);
  }
  if (!haveOled) return;
    
  if(!useRds){
  wipe(6); wipe(8); wipe(10); wipe(12); wipe(14);
  u8x8.setCursor(4, 12); u8x8.print(F("Rds off"));
  }else {
  u8x8.setCursor(4, 12); u8x8.print(F("Rds on "));
  haveRds = false;
  }
  while(digitalRead(rdsP) == LOW){
  delay(100);
  }
}

void setUp(){
  if (useSerial) Serial.println(F("Search Up"));
  if (haveOled) {
  u8x8.clear(); u8x8.setCursor(0,0);
  u8x8.print(F("Search Up"));
  }
rx.seek(SI470X_SEEK_WRAP, SI470X_SEEK_UP);
wipe(0);
showStatus();
}

void setDown(){
  if (useSerial) Serial.println(F("Search Down"));
  if (haveOled) {
  u8x8.clear(); u8x8.setCursor(0,0);
  u8x8.print(F("Search down"));
  }
rx.seek(SI470X_SEEK_WRAP, SI470X_SEEK_DOWN);
wipe(0);
showStatus();
}

void showHelp(){
Serial.println(F("Type U to increase and D to decrease the frequency"));
Serial.println(F("Type S or s to seek station Up or Down"));
Serial.println(F("Type + or - to volume Up or Down"));
Serial.println(F("Type 0 to show current status"));
Serial.println(F("Type ? to this help."));
Serial.println(F("=================================================="));
Serial.flush();
}

void showStatus(){
uint16_t F = rx.getFrequency();
int RSSI = rx.getRssi();
int V = rx.getVolume();
bool S = rx.isStereo();
  if (haveOled) {
  float freq = F;
  u8x8.setCursor(0, 0); u8x8.print(freq / 100, 1);  u8x8.print(F(" MHz "));
  
  wipe(2); u8x8.print(RSSI); u8x8.print(F(" dBuV"));
  u8x8.setCursor(10, 2); u8x8.print(V); u8x8.setCursor(15, 2);
    if (S) {
    u8x8.print("S");
    digitalWrite(led, HIGH);
    }else{
    u8x8.print("M");
    digitalWrite(led, LOW);
    }
  }

  if (useSerial) {
  char aux[80];
  sprintf(aux, "\nYou are tuned on %u MHz | RSSI: %3.3u dbUv | Vol: %2.2u | Stereo: %s\n", F, RSSI, V, (S) ? "Yes" : "No" );
  Serial.print(aux); Serial.flush();
  }
status_elapsed = millis();
}

void checkSerial(){
  if (Serial.available() > 0) {
  char key = Serial.read();
    switch (key) {
      case '+':
        rx.setVolumeUp();
        break;
      case '-':
        rx.setVolumeDown();
        break;
      case 'U':
      case 'u':
        rx.setFrequencyUp();
        break;
      case 'D':
      case 'd':
        rx.setFrequencyDown();
        break;
      case 'S':
        rx.seek(SI470X_SEEK_WRAP, SI470X_SEEK_UP);
        break;
      case 's':
        rx.seek(SI470X_SEEK_WRAP, SI470X_SEEK_DOWN);
        break;
      case '?':
        showHelp();
        break;
      default:
        break;
    }
  showStatus();
  }
}

David

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions