DSP Radio Receiver Code Rev. 2.5
DSP Radio Receiver Code Rev. 2.5
***********************************************************************************
**********************************************************************
Si473x LW/MW/SW/CB AM/FM Stereo RDS Radio Receiver w/ ST7735 TFT Display and
Arduino Nano/Uno - AM 150kHz To 30MHz - FM 64 to 108MHz Stereo with RDS.
Please see the schematics for wiring connections and install all needed
libraries that are located in the "INSTALL THESE LIBRARIES" folder.
By J.CesarSound - ver 2.5 - Mar/2021.
***********************************************************************************
***********************************************************************/
//Libraries
#include <EEPROM.h> //IDE Standard
#include <SPI.h> //IDE Standard
#include "Wire.h" //IDE Standard
#include <TFT_ST7735.h> //BODMER graphics and font library for ST7735 TFT display
#include <Si4735.h> //Michael Kennedy SI4735 I2C library modified by
J.CesarSound
#include <Rotary.h> //Ben Buxton https://github.com/brianlow/Rotary
//User preferences
//---------------------------------------------------------------------------------
----------------------------------------------------------
#define SI7435_ADDRESS 0x63 //Si473x I2C address (0x11 = sen pin to GND or 0x63
= sen pin to 3v3), module PL102BA-S V2 uses 0x63
#define pb_0 A0 //AM mode and AM Band selector: short press AM / UP
band, long press DOWN band
#define pb_1 A1 //Save current frequency, band, theme to EEPROM and
Tuning Step selector: short press SAVE, long press Tstep
#define pb_2 A2 //Bandwidth IF Filter selector for AM and FM
#define pb_3 A3 //FM mode, RDS program type and color theme
selector: short press FM / RDS type, long press switch color theme
//---------------------------------------------------------------------------------
-----------------------------------------------------------
void setup() {
tft.init();
tft.setRotation(3);
Wire.begin();
start_msg();
tft.fillScreen(ca);
pinMode(pb_0, INPUT_PULLUP);
pinMode(pb_1, INPUT_PULLUP);
pinMode(pb_2, INPUT_PULLUP);
pinMode(pb_3, INPUT_PULLUP);
if (digitalRead(A1) == LOW) {
tft.fillScreen(TFT_BLUE); tft.setTextColor(TFT_WHITE, TFT_BLUE);
tft.drawString("* DEFAULT VALUES *", 12, 58, 2);
delay(500);
am_fm = 0x20;
freq = 10050;
EEPROM.write(5, 6);
eeprom_write();
}
void loop() {
if (freqold != freq) {
freqold = freq;
tune_freq();
frequency();
if (am_fm != 0x20) band_ind();
}
if (digitalRead(pb_0) == LOW) {
delay(100);
if (buttonActive_0 == false) {
buttonActive_0 = true;
buttonTimer = millis();
}
if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
longPressActive = true;
dec_preset();
}
} else {
if (buttonActive_0 == true) {
if (longPressActive == true) {
longPressActive = false;
} else {
inc_preset();
}
buttonActive_0 = false;
}
}
if (digitalRead(pb_1) == LOW) {
delay(100);
if (buttonActive_1 == false) {
buttonActive_1 = true;
buttonTimer = millis();
}
if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
longPressActive = true;
if (am_fm != 0x20) inc_step();
if (am_fm == 0x20) setstepFM();
}
} else {
if (buttonActive_1 == true) {
if (longPressActive == true) {
longPressActive = false;
} else {
eeprom_write();
}
buttonActive_1 = false;
}
}
if ((digitalRead(pb_2)) == LOW) {
delay(200);
if (am_fm != 0x20) filterAM();
if (am_fm == 0x20) filterFM();
while (digitalRead(pb_2) == LOW);
}
if (digitalRead(pb_3) == LOW) {
delay(500);
if (am_fm != 0x20) {
fm_init();
} else rds_t = !rds_t;
tft.drawString(" ", 0, 74, 2);
push_count++;
if (push_count > 2) {
sw_theme();
while (digitalRead(pb_3) == LOW);
}
} else push_count = 0;
if (am_fm == 0x20) {
if (millis() > time_now_rds + period_rds) {
time_now_rds = millis();
rds();
}
}
}
ISR(PCINT2_vect) {
char result = r.process();
if (result == DIR_CW) set_frequency(1);
else if (result == DIR_CCW) set_frequency(-1);
}
void inc_preset() {
count++;
if (count > 14) count = 1;
bandpresets();
}
void dec_preset() {
count--;
if (count < 1) count = 14;
bandpresets();
}
void bandpresets() {
tft.setTextSize(1); tft.setTextColor(cb, ca); tft.drawString(" ", 30,
57, 2);
switch (count) {
case 1:
freq = 198;
stp = 1;
setstep();
break;
case 2:
freq = 522;
stp = 4;
setstep();
break;
case 3:
freq = 520;
stp = 3;
setstep();
break;
case 4:
freq = 3700;
stp = 2;
setstep();
break;
case 5:
freq = 5000;
stp = 2;
setstep();
break;
case 6:
freq = 6100;
stp = 2;
setstep();
break;
case 7:
freq = 7200;
stp = 2;
setstep();
break;
case 8:
freq = 10000;
stp = 2;
setstep();
break;
case 9:
freq = 11940;
stp = 2;
setstep();
break;
case 10:
freq = 13790;
stp = 2;
setstep();
break;
case 11:
freq = 15400;
stp = 2;
setstep();
break;
case 12:
freq = 17780;
stp = 2;
setstep();
break;
case 13:
freq = 21525;
stp = 2;
setstep();
break;
case 14:
freq = 27015;
stp = 2;
setstep();
break;
}
if (hasRun2 == false) {
am_init(); hasRun2 = true;
}
if (count >= 1 && count <= 4) period = 4000; else period = 500; band_ind();
}
void filterAM() {
tft.setTextSize(1);
tft.setTextColor(cb, ca);
switch (fil) {
case 0:
fil = 1;
filter = 0x00; tft.drawString("6k ", 135, 92, 2);
break;
case 1:
fil = 2;
filter = 0x01; tft.drawString("4k ", 135, 92, 2);
break;
case 2:
fil = 3;
filter = 0x02; tft.drawString("3k ", 135, 92, 2);
break;
case 3:
fil = 4;
filter = 0x06; tft.drawString("2k5", 135, 92, 2);
break;
case 4:
fil = 5;
filter = 0x03; tft.drawString("2k ", 135, 92, 2);
break;
case 5:
fil = 6;
filter = 0x05; tft.drawString("1k8", 135, 92, 2);
break;
case 6:
fil = 0;
filter = 0x04; tft.drawString("1k ", 135, 92, 2);
break;
}
property_filter_AM();
}
void filterFM() {
tft.setTextSize(1);
tft.setTextColor(cb, ca);
switch (fil) {
case 0:
fil = 1;
filter = 0x00; tft.drawString("Aut", 135, 92, 2);
break;
case 1:
fil = 2;
filter = 0x01; tft.drawString("110", 134, 92, 2);
break;
case 2:
fil = 3;
filter = 0x02; tft.drawString("84k", 135, 92, 2);
break;
case 3:
fil = 4;
filter = 0x03; tft.drawString("60k", 135, 92, 2);
break;
case 4:
fil = 0;
filter = 0x04; tft.drawString("40k", 135, 92, 2);
break;
}
fm_command();
}
void inc_step() {
ctr++; if (ctr > 3) ctr = 1;
byte eu[3] = {1, 4, 1}; byte us[3] = {1, 2, 3};
if (count == 2) stp = eu[ctr];
if (count != 2) stp = us[ctr];
setstep();
}
void setstep() {
tft.setTextSize(1);
tft.setTextColor(cb, ca);
switch (stp) {
case 1:
fstep = 1;
tft.drawString(" 1k", 100, 57, 2);
break;
case 2:
fstep = 5;
tft.drawString(" 5k", 100, 57, 2);
break;
case 3:
fstep = 10;
tft.drawString("10k", 100, 57, 2);
break;
case 4:
fstep = 9;
tft.drawString(" 9k", 100, 57, 2);
break;
}
}
void setstepFM() {
tft.setTextSize(1);
tft.setTextColor(cb, ca);
switch (stp) {
case 0:
stp = 1;
fstep = 10; // 100kHz
tft.drawString(" x1 ", 100, 57, 2);
break;
case 1:
stp = 0;
fstep = 20; // 200kHz
tft.drawString(" x2 ", 100, 57, 2);
break;
}
}
void frequency() {
tft.setTextColor(cg, ca);
if (freq >= 10000) tft.drawNumber(freq, 0, 1, 7); if (freq < 10000 && freq >=
1000) tft.drawNumber(freq, 32, 1, 7);
if (freq < 1000 && freq >= 100) tft.drawNumber(freq, 64, 1, 7); if (freq < 100
&& freq >= 10) tft.drawNumber(freq, 96, 1, 7);
if (freq < 10) tft.drawNumber(freq, 128, 1, 7); if (freq < 10000) tft.fillRect
(2, 1, 28, 48, ca);
if (freq < 1000) tft.fillRect (34, 1, 28, 48, ca);
if (am_fm == 0x40 && freq >= 1000) tft.fillCircle(63, 51, 2, cg); else
tft.fillCircle(63, 51, 2, ca);
if (am_fm == 0x20) tft.fillCircle(95, 51, 2, cg); else tft.fillCircle(95, 51,
2, ca);
}
void snmeter() {
if (sn > 42) sn = 42;
byte range; if (am_fm == 0x20) range = 42; else range = 28;
byte sn_g = map(sn, 0, range, 0, 65);
tft.fillRect(31, 95, sn_g, 11, ce);
tft.fillRect(31 + sn_g, 95, 65 - sn_g, 11, cf);
}
void smeter() {
if (sm > 90) sm = 90; mx = sm;
tft.fillRect(31, 113, sm, 11, cd);
tft.fillRect(31 + sm, 113, 90 - sm, 11, cf);
tft.setTextSize(1);
tft.setTextColor(cb, ca);
if (mx < 1) tft.drawString(" S0", 130, 111, 2);
if ((mx >= 1) && (mx < 10)) tft.drawString(" S1", 130, 111, 2);
if ((mx >= 10) && (mx < 25)) tft.drawString(" S3", 130, 111, 2);
if ((mx >= 25) && (mx < 35)) tft.drawString(" S5", 130, 111, 2);
if ((mx >= 35) && (mx < 50)) tft.drawString(" S7", 130, 111, 2);
if ((mx >= 50) && (mx < 60)) tft.drawString(" S9", 130, 111, 2);
if ((mx >= 60) && (mx < 70)) tft.drawString("+10", 130, 111, 2);
if ((mx >= 70) && (mx < 75)) tft.drawString("+20", 130, 111, 2);
if ((mx >= 75) && (mx < 80)) tft.drawString("+40", 130, 111, 2);
if (mx >= 80) tft.drawString("+60", 130, 111, 2);
}
void layout() {
tft.setTextColor(TFT_WHITE, TFT_BLUE);
tft.drawFastHLine(0, 56, 160, cc);
tft.drawFastVLine(125, 59, 12, cc);
tft.drawFastHLine(0, 73, 160, cc);
tft.drawFastHLine(0, 91, 160, cc);
tft.drawFastHLine(0, 109, 160, cc);
tft.setTextSize(1);
tft.setTextColor(cb, ca);
tft.drawString("Ts", 85, 57, 2);
tft.drawString("S/N", 3, 92, 2);
tft.drawString("BWF", 105, 92, 2);
tft.drawString("SIG", 3, 111, 2);
}
void start_msg() {
tft.fillScreen(TFT_BLACK);
tft.setTextSize(1);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("JCR RADIO", 48, 20, 2);
tft.drawString("AM/FM RDS Receiver", 15, 50, 2);
tft.drawString("VER 2.5 - MAR/2021", 15, 80, 2);
delay(1600);
}
void band_ind() {
tft.setTextSize(1); tft.setTextColor(cb, ca);
if (freq >= 1800 && freq <= 2000) tft.drawString("160m ", 30, 57, 2); if (freq
>= 2300 && freq <= 2495) tft.drawString("120m", 30, 57, 2);
if (freq >= 3200 && freq <= 3400) tft.drawString("90m ", 30, 57, 2); if (freq
>= 3500 && freq <= 3800) tft.drawString("80m", 30, 57, 2);
if (freq >= 3900 && freq <= 4000) tft.drawString("75m", 30, 57, 2); if (freq
>= 4750 && freq <= 5500) tft.drawString("60m", 30, 57, 2);
if (freq >= 5700 && freq <= 6200) tft.drawString("49m", 30, 57, 2); if (freq >=
7000 && freq <= 7299) tft.drawString("40m", 30, 57, 2);
if (freq >= 7300 && freq <= 7600) tft.drawString("41m", 30, 57, 2); if (freq >=
9400 && freq <= 10000) tft.drawString("31m", 30, 57, 2);
if (freq >= 10100 && freq <= 10150) tft.drawString("30m", 30, 57, 2); if (freq
>= 11600 && freq <= 12100) tft.drawString("25m", 30, 57, 2);
if (freq >= 13570 && freq <= 13870) tft.drawString("22m", 30, 57, 2); if (freq
>= 14000 && freq <= 14350) tft.drawString("20m", 30, 57, 2);
if (freq >= 15100 && freq <= 15830) tft.drawString("19m", 30, 57, 2); if (freq
>= 18068 && freq <= 18168) tft.drawString("17m", 30, 57, 2);
if (freq >= 17480 && freq <= 17900) tft.drawString("16m", 30, 57, 2); if (freq
>= 21000 && freq <= 21450) tft.drawString("15m", 30, 57, 2);
if (freq >= 21451 && freq <= 21850) tft.drawString("13m", 30, 57, 2); if (freq
>= 24890 && freq <= 24990) tft.drawString("12m", 30, 57, 2);
if (freq >= 25670 && freq <= 27999) tft.drawString("11m", 30, 57, 2); if (freq
>= 28000 && freq <= 29700) tft.drawString("10m", 30, 57, 2);
if (freq >= 150 && freq <= 515) tft.drawString("LW ", 30, 57, 2);
if (freq >= 520 && freq <= 1710 && count == 3) tft.drawString("MW US", 30, 57,
2);
if (freq >= 522 && freq <= 1728 && count == 2) tft.drawString("MW EU", 30, 57,
2);
}
void tune_freq() {
fr_h = (freq) >> 8;
fr_l = (freq) & 0x00FF;
if ((freq) < 1800) mw_sw = 0x00; else (mw_sw = 0x01);
tune();
}
void rsq() {
RSQMetrics rsq;
radio.getRSQ(&rsq);
sm = (rsq.RSSI);
sn = (rsq.SNR) * 0.5 + sn * 0.5;
}
void pilot() {
RSQMetrics rsq;
radio.getRSQ(&rsq);
tft.setTextSize(1);
void rds() {
radio.getRDS();
if (rds_t == false) {
if (radio.rds.RDSSignal) {
texsw++; if (texsw > 60) texsw = 1;
tft.setTextSize(1); tft.setTextColor(cb, ca);
if (hasRun == false) {
tft.drawString(" ", 0, 74, 2);
hasRun = true;
}
tft.drawString("RDS ", 3, 74, 2);
if (texsw > 0 && texsw < 41) tft.drawString(radio.rds.programService, 60,
74, 2);
if (texsw == 41) tft.drawString(" ", 33, 74, 2);
if (texsw > 40 && texsw < 61) tft.drawString(radio.rds.radioText, 33, 74,
2);
if (texsw == 1) tft.drawString(" ", 33, 74, 2);
hasRun3 = false;
} else {
radio.clearStationInfo();
tft.setTextColor(cb, ca); tft.fillRect (0, 75, 3, 15, ca);
if (hasRun3 == false) {
tft.drawString("RDS No Information ", 3, 74, 2);
hasRun3 = true;
}
hasRun = false;
}
}
if (rds_t == true) {
if (radio.rds.RDSSignal) {
texsw += 8; if (texsw > 296) texsw = 0;
tft.setTextSize(1); tft.setTextColor(cb, ca);
if (hasRun == false) {
tft.drawString(" ", 0, 74, 2);
hasRun = true;
}
tft.drawString(radio.rds.radioText, 20 - texsw, 74, 2); tft.fillRect (156,
75, 4, 15, ca);
if (texsw == 296) tft.drawString(" ", 0, 74,
2);
hasRun3 = false;
} else {
radio.clearStationInfo(); texsw = 0;
tft.setTextColor(cb, ca); tft.fillRect (0, 75, 3, 15, ca);
if (hasRun3 == false) {
tft.drawString("RDS No Information ", 3, 74, 2);
hasRun3 = true;
}
hasRun = false;
}
}
}
void am_init() {
encoder = 1;
radio.begin(0, SI7435_ADDRESS);
power_down();
power_up_norm();
delay(400);
am_fm = 0x40;
ssb_m = 0x00;
eeprom_read();
freq = freq_eeprom_am;
if (count == 2) stp = 4; else stp = 2; setstep();
tune();
am_command();
filter = 0x03; layout(); frequency(); if (count >= 1 && count <= 4) period =
4000; else period = 500;
tft.setTextColor(cg, ca); tft.drawString("kHz ", 132, 57, 2);
tft.setTextColor(cb, ca); tft.drawString("AM ", 3, 57, 2);
tft.drawString(" AM/FM RDS Receiver ", 0, 74, 2); tft.drawString("2k ", 135,
92, 2);
tft.drawString(" ", 30, 57, 2);
}
void fm_init() {
encoder = 2;
radio.begin(0, SI7435_ADDRESS);
power_down();
power_up_FM();
delay(400);
am_fm = 0x20;
ssb_m = 0x00;
eeprom_read();
freq = freq_eeprom_fm;
tune(); pilot();
radio.setMode(FM);
fil = 0; filterFM();
stp = 0; setstepFM(); fm_command();
hasRun2 = false; count--; layout(); frequency(); period = 2000;
tft.setTextColor(cg, ca); tft.drawString("MHz", 132, 57, 2);
tft.setTextColor(cb, ca); tft.drawString("FM ", 3, 57, 2);
tft.setTextColor(TFT_DARKGREY, ca); tft.drawString(" Stereo ", 30, 57, 2);
}
void sw_theme() {
sts_theme = !sts_theme;
if (sts_theme == false) blue_theme(); else black_theme();
tft.fillScreen(ca); tft.setTextColor(cb, ca);
hasRun1 = !hasRun1;
fm_init();
if (am_fm != 0x20) {
fil = 4; filterAM();
}
if (am_fm == 0x20) {
fil = 0; filterFM();
}
}
void blue_theme() {
ca = TFT_BLUE; //Screen background
cb = TFT_WHITE; //Texts
cc = TFT_LIGHTGREY; //Lines
cd = TFT_GREEN; //S-Meter bargraph
ce = TFT_MAGENTA; //S/N bargraph
cf = TFT_BLUE; //Bargraph background
cg = TFT_WHITE; //Frequency and kHz MHz
ch = TFT_CYAN; //Reserved
tft.fillScreen(ca);
}
void black_theme() {
ca = TFT_BLACK; //Screen background
cb = TFT_WHITE; //Texts
cc = TFT_LIGHTGREY; //Lines
cd = TFT_GREEN; //S-Meter bargraph
ce = TFT_MAGENTA; //S/N bargraph
cf = TFT_BLACK; //Bargraph background
cg = TFT_CYAN; //Frequency and kHz MHz
ch = TFT_RED; //Reserved
tft.fillScreen(ca);
}
void am_command() {
radio.setProperty(PROP_AM_SOFT_MUTE_MAX_ATTENUATION, 1); // 0 (OFF) - 63
radio.setProperty(PROP_AM_AUTOMATIC_VOLUME_CONTROL_MAX_GAIN, 0x32C8); // 0x1000
- 0x7800
radio.setProperty(PROP_AM_AGC_ATTACK_RATE, 4); // 4–248
radio.setProperty(PROP_AM_AGC_RELEASE_RATE, 4); // 4–248
}
void fm_command() {
radio.setProperty(PROP_FM_DEEMPHASIS, 0x0001); // 02 = 75 μs; 01 = 50 μs
radio.setProperty(PROP_FM_CHANNEL_FILTER, filter); // 0 = automat; 1 = 110kHz;
4 = 40kHz
radio.setProperty(PROP_FM_SOFT_MUTE_MAX_ATTENUATION, 0); // 0 = disable; 31 =
max
radio.setProperty(PROP_FM_BLEND_STEREO_THRESHOLD, 0); // 0 = stereo; 127 = mono
radio.setProperty(PROP_FM_BLEND_MONO_THRESHOLD, 0); // 0 = stereo; 127 = mono
}
void power_down() {
Wire.beginTransmission(SI7435_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
delay(1);
}
void power_up_norm() {
Wire.beginTransmission(SI7435_ADDRESS);
Wire.write(0x01);
Wire.write(0x11);
Wire.write(0x05);
Wire.endTransmission();
delay(1);
}
void power_up_FM() {
Wire.beginTransmission(SI7435_ADDRESS);
Wire.write(0x01);
Wire.write(0x10);
Wire.write(0x05);
Wire.endTransmission();
delay(1);
}
void tune() {
Wire.beginTransmission(SI7435_ADDRESS);
Wire.write(am_fm);
Wire.write(ssb_m);
Wire.write(fr_h);
Wire.write(fr_l);
Wire.write(0x00);
Wire.write(mw_sw);
Wire.endTransmission();
delay(1);
}
void property_filter_AM() {
Wire.beginTransmission(SI7435_ADDRESS);
Wire.write(0x12);
Wire.write(0x00);
Wire.write(0x31);
Wire.write(0x02);
Wire.write(0x00);
Wire.write(filter);
Wire.endTransmission();
delay(1);
}
void eeprom_write() {
if (am_fm == 0x20) {
fr_eeprom_h = (freq) >> 8;
fr_eeprom_l = (freq) & 0x00FF;
EEPROM.write(0, fr_eeprom_l);
EEPROM.write(1, fr_eeprom_h);
EEPROM.write(2, 1);
}
if (am_fm != 0x20) {
fr_eeprom_h = (freq) >> 8;
fr_eeprom_l = (freq) & 0x00FF;
EEPROM.write(3, fr_eeprom_l);
EEPROM.write(4, fr_eeprom_h);
EEPROM.write(2, 0);
EEPROM.write(5, count);
}
EEPROM.write(6, sts_theme);
tft.setTextColor(TFT_WHITE, TFT_MAGENTA); tft.drawString("SAVE", 126, 111, 2);
delay(1500);
tft.setTextColor(cb, ca); tft.drawString(" ", 126, 111, 2);
delay(300);
}
void eeprom_read() {
if (am_fm == 0x20) {
fr_eeprom_l = EEPROM.read(0);
fr_eeprom_h = EEPROM.read(1);
freq_eeprom_fm = (fr_eeprom_h << 8U) | fr_eeprom_l;
}
if (am_fm != 0x20) {
fr_eeprom_l = EEPROM.read(3);
fr_eeprom_h = EEPROM.read(4);
freq_eeprom_am = (fr_eeprom_h << 8U) | fr_eeprom_l;
count = EEPROM.read(5);
}
sts_am_fm = EEPROM.read(2);
}