-
Notifications
You must be signed in to change notification settings - Fork 15
Description
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
-
add interrupt
reg04->refined.RDSIEN = 1; // was 0 = no interrupt drp -
add link interrupt:
reg04->refined.GPIO1 = reg04->refined.GPIO2 = reg04->refined.GPIO3 = 0;
reg04->refined.GPIO2 = 1; // + drp -
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;
}; -
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