Radio Si4732/34/35 dikontrol ESP32 dengan LCD 16x2 dan Rotary Encoder
Fitur : Receiver FM Stereo/Mono dan RDS, LW, MW1 dan MW2, SW, SSB, USB, BFO
#include <SI4735.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
// #include <LiquidCrystal_I2C.h>
#include "Rotary.h"
#include <patch_init.h> // SSB patch for whole SSBRX initialization string
byte blockChar[8] = {
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111
};
const uint16_t size_content = sizeof ssb_patch_content; // see patch_init.h
#define FM_BAND_TYPE 0
#define MW_BAND_TYPE 1
#define SW_BAND_TYPE 2
#define LW_BAND_TYPE 3
#define RESET_PIN 23
// Enconder PINs
#define ENCODER_PIN_A 16 //DT
#define ENCODER_PIN_B 17 //CLK
// LCD 16x02 or LCD20x4 PINs
#define LCD_D7 14
#define LCD_D6 27
#define LCD_D5 26
#define LCD_D4 25
#define LCD_RS 32
#define LCD_E 33
// Buttons controllers
#define ENCODER_PUSH_BUTTON 4 //SW
//#define MUTE_AMP 15
#define MIN_ELAPSED_TIME 300
#define MIN_ELAPSED_RSSI_TIME 3000
#define ELAPSED_COMMAND 2000 // time to turn off the last command controlled by encoder. Time to goes back to the FVO control
#define ELAPSED_CLICK 1500 // time to check the double click commands
#define DEFAULT_VOLUME 35 // change it for your favorite sound volume
#define FM 0
#define LSB 1
#define USB 2
#define AM 3
#define LW 4
#define SSB 1
#define EEPROM_SIZE 512
#define STORE_TIME 10000 // Time of inactivity to make the current receiver status writable (10s / 10000 milliseconds).
// EEPROM - Stroring control variables
const uint8_t app_id = 37; // Useful to check the EEPROM content before processing useful data
const int eeprom_address = 0;
long storeTime = millis();
bool itIsTimeToSave = false;
bool bfoOn = false;
bool ssbLoaded = false;
int8_t agcIdx = 0;
int8_t disableAgc = 0;
int8_t agcNdx = 0;
int8_t softMuteMaxAttIdx = 4;
int8_t avcIdx; // min 12 and max 90
uint8_t countClick = 0;
uint8_t seekDirection = 1;
bool cmdBand = false;
bool cmdVolume = false;
bool cmdAgc = false;
bool cmdBandwidth = false;
bool cmdStep = false;
bool cmdMode = false;
bool cmdMenu = false;
bool cmdRds = false;
bool cmdSoftMuteMaxAtt = false;
bool cmdAvc = false;
bool fmRDS = false;
int16_t currentBFO = 0;
long elapsedRSSI = millis();
long elapsedButton = millis();
long elapsedCommand = millis();
long elapsedClick = millis();
volatile int encoderCount = 0;
uint16_t currentFrequency;
uint16_t previousFrequency = 0;
const uint8_t currentBFOStep = 10;
const char * menu[] = {"Volume", "FM RDS", "Step", "Mode", "BFO", "BW", "AGC/Att", "SoftMute", "AVC", "Seek Up", "Seek Down"};
int8_t menuIdx = 0;
const int lastMenu = 10;
int8_t currentMenuCmd = -1;
typedef struct
{
uint8_t idx; // SI473X device bandwidth index
const char *desc; // bandwidth description
} Bandwidth;
int8_t bwIdxSSB = 4;
const int8_t maxSsbBw = 5;
Bandwidth bandwidthSSB[] = {
{4, "0.5"},
{5, "1.0"},
{0, "1.2"},
{1, "2.2"},
{2, "3.0"},
{3, "4.0"}
};
int8_t bwIdxAM = 4;
const int8_t maxAmBw = 6;
Bandwidth bandwidthAM[] = {
{4, "1.0"},
{5, "1.8"},
{3, "2.0"},
{6, "2.5"},
{2, "3.0"},
{1, "4.0"},
{0, "6.0"}
};
int8_t bwIdxFM = 0;
const int8_t maxFmBw = 4;
Bandwidth bandwidthFM[] = {
{0, "AUT"}, // Automatic - default
{1, "110"}, // Force wide (110 kHz) channel filter.
{2, " 84"},
{3, " 60"},
{4, " 40"}};
int tabAmStep[] = {1, // 0
5, // 1
9, // 2
10, // 3
50, // 4
100}; // 5
const int lastAmStep = (sizeof tabAmStep / sizeof(int)) - 1;
int idxAmStep = 3;
int tabFmStep[] = {5, 10, 20};
const int lastFmStep = (sizeof tabFmStep / sizeof(int)) - 1;
int idxFmStep = 1;
uint16_t currentStepIdx = 1;
const char *bandModeDesc[] = {". ", "LSB", "USB", "AM "};
uint8_t currentMode = FM;
/**
* Band data structure
*/
typedef struct
{
const char *bandName; // Band description
uint8_t bandType; // Band type (FM, MW or SW)
uint16_t minimumFreq; // Minimum frequency of the band
uint16_t maximumFreq; // maximum frequency of the band
uint16_t currentFreq; // Default frequency or current frequency
int8_t currentStepIdx; // Idex of tabStepAM: Defeult frequency step (See tabStepAM)
int8_t bandwidthIdx; // Index of the table bandwidthFM, bandwidthAM or bandwidthSSB;
uint8_t disableAgc;
int8_t agcIdx;
int8_t agcNdx;
int8_t avcIdx;
} Band;
/*
Band table
YOU CAN CONFIGURE YOUR OWN BAND PLAN. Be guided by the comments.
To add a new band, all you have to do is insert a new line in the table below. No extra code will be needed.
You can remove a band by deleting a line if you do not want a given band.
Also, you can change the parameters of the band.
ATTENTION: You have to RESET the eeprom after adding or removing a line of this table.
Turn your receiver on with the encoder push button pressed at first time to RESET the eeprom content.
*/
Band band[] = {
{"FM", FM_BAND_TYPE, 6400, 10800, 9200, 1, 0, 1, 0, 0, 0},
{"LW", MW_BAND_TYPE, 150, 279, 150, 0, 6, 0, 0, 0, 32},
{"MW1", MW_BAND_TYPE, 531, 1710, 630, 0, 6, 0, 0, 0, 32},
{"MW2", MW_BAND_TYPE, 2300, 3500, 1700, 0, 6, 1, 0, 0, 32},
{"80M", MW_BAND_TYPE, 3500, 4000, 3500, 0, 6, 1, 0, 0, 32},
{"SW1", SW_BAND_TYPE, 4000, 5500, 4000, 0, 6, 1, 0, 0, 32},
{"SW2", SW_BAND_TYPE, 5500, 6500, 5500, 0, 6, 1, 0, 0, 32},
{"40M", SW_BAND_TYPE, 6500, 7300, 6500, 0, 6, 1, 0, 0, 40},
{"SW3", SW_BAND_TYPE, 7300, 8000, 7300, 0, 6, 1, 0, 0, 40},
{"SW4", SW_BAND_TYPE, 8000, 11000, 8000, 0, 6, 1, 0, 0, 40},
{"SW5", SW_BAND_TYPE, 11000, 13000, 11000, 0, 6, 1, 0, 0, 40},
{"SW6", SW_BAND_TYPE, 13000, 14000, 13000, 0, 6, 1, 0, 0, 40},
{"20M", SW_BAND_TYPE, 14000, 15000, 14000, 0, 6, 1, 0, 0, 42},
{"SW7", SW_BAND_TYPE, 15000, 17000, 15000, 0, 6, 1, 0, 0, 42},
{"SW8", SW_BAND_TYPE, 17000, 18000, 17000, 0, 6, 1, 0, 0, 42},
{"15M", SW_BAND_TYPE, 18000, 21850, 18000, 0, 6, 1, 0, 0, 44}, // Si4734 maksimal frekuensi
//{"SW9", SW_BAND_TYPE, 21400, 22800, 21400, 1, 6, 1, 0, 0, 44},
//{"CB ", SW_BAND_TYPE, 22800, 28000, 22800, 0, 6, 1, 0, 0, 44},
//{"10M", SW_BAND_TYPE, 28000, 30000, 28400, 0, 6, 1, 0, 0, 44},
//{"ALL", SW_BAND_TYPE, 150, 30000, 15000, 0, 6, 1, 0, 0, 48} // All band. LW, MW and SW (from 150kHz to 30MHz)
};
const int lastBand = (sizeof band / sizeof(Band)) - 1;
int bandIdx = 0;
int tabStep[] = {1, 5, 10, 50, 100, 500, 1000};
const int lastStep = (sizeof tabStep / sizeof(int)) - 1;
uint8_t rssi = 0;
uint8_t volume = DEFAULT_VOLUME;
// Devices class declarations
Rotary encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B);
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
// LiquidCrystal_I2C lcd(0x27, 20, 4);
SI4735 rx;
void setup()
{
Serial.begin(115200);
while(!Serial);
delay(1000);
//digitalWrite(MUTE_AMP, LOW); //mute pin 9 dan 10 PAM8403 (active low)
// Encoder pins
pinMode(ENCODER_PUSH_BUTTON, INPUT_PULLUP);
pinMode(ENCODER_PIN_A, INPUT_PULLUP);
pinMode(ENCODER_PIN_B, INPUT_PULLUP);
lcd.begin(16, 2);
lcd.createChar(0, blockChar);
// Splash - Remove or Change the splash message
lcd.setCursor(0, 0);
lcd.print("RADIO SI4734");
lcd.setCursor(0, 1);
lcd.print("KERE HORE");
delay(1500);
// End splash
EEPROM.begin(EEPROM_SIZE);
// If you want to reset the eeprom, keep the VOLUME_UP button pressed during statup
if (digitalRead(ENCODER_PUSH_BUTTON) == LOW)
{
EEPROM.write(eeprom_address, 0);
lcd.setCursor(0,0);
lcd.print("EEPROM RESETED");
itIsTimeToSave = true; // forces save the current status as soon as possible.
storeTime = 0;
delay(3000);
lcd.clear();
}
// controlling encoder via interrupt
attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE);
rx.setMaxDelayPowerUp(500);
//rx.setI2CFastModeCustom(400000);
rx.getDeviceI2CAddress(RESET_PIN); // Looks for the I2C bus address and set it. Returns 0 if error
rx.setup(RESET_PIN, MW_BAND_TYPE, XOSCEN_CRYSTAL);
// Comment the line above and uncomment the lines below if you are using external ref clock (active crystal or signal generator)
// rx.setRefClock(32768);
// rx.setRefClockPrescaler(1); // will work with 32768
// select one audio mode below: regular audio analog mode or digital audio mode
// rx.setup(RESET_PIN, 0, FM_BAND_TYPE, SI473X_ANALOG_AUDIO, XOSCEN_RCLK); // Analog mode
// rx.setup(RESET_PIN, -1, FM_BAND_TYPE, SI473X_ANALOG_DIGITAL_AUDIO, XOSCEN_RCLK); // Analog and digital audio outputs (LOUT/ROUT and DCLK, DFS, DIO), external RCLK
delay(300);
// Checking the EEPROM content
if (EEPROM.read(eeprom_address) == app_id)
{
readAllReceiverInformation();
} else
rx.setVolume(volume);
useBand();
showStatus();
//digitalWrite(MUTE_AMP, HIGH); //unmute pin 9 dan 10 PAM8403
}
/*
writes the conrrent receiver information into the eeprom.
The EEPROM.update avoid write the same data in the same memory position. It will save unnecessary recording.
*/
void saveAllReceiverInformation()
{
int addr_offset;
EEPROM.begin(EEPROM_SIZE);
EEPROM.write(eeprom_address, app_id); // stores the app id;
EEPROM.write(eeprom_address + 1, rx.getVolume()); // stores the current Volume
EEPROM.write(eeprom_address + 2, bandIdx); // Stores the current band
EEPROM.write(eeprom_address + 3, fmRDS);
EEPROM.write(eeprom_address + 4, currentMode); // Stores the current Mode (FM / AM / SSB)
EEPROM.write(eeprom_address + 5, currentBFO >> 8);
EEPROM.write(eeprom_address + 6, currentBFO & 0XFF);
addr_offset = 7;
band[bandIdx].currentFreq = currentFrequency;
for (int i = 0; i <= lastBand; i++)
{
EEPROM.write(addr_offset++, (band[i].currentFreq >> 8)); // stores the current Frequency HIGH byte for the band
EEPROM.write(addr_offset++, (band[i].currentFreq & 0xFF)); // stores the current Frequency LOW byte for the band
EEPROM.write(addr_offset++, band[i].currentStepIdx); // Stores current step of the band
EEPROM.write(addr_offset++, band[i].bandwidthIdx); // table index (direct position) of bandwidth
EEPROM.write(addr_offset++, band[i].disableAgc );
EEPROM.write(addr_offset++, band[i].agcIdx);
EEPROM.write(addr_offset++, band[i].agcNdx);
EEPROM.write(addr_offset++, band[i].avcIdx);
}
EEPROM.end();
}
/**
* reads the last receiver status from eeprom.
*/
void readAllReceiverInformation()
{
uint8_t volume;
int addr_offset;
int bwIdx;
volume = EEPROM.read(eeprom_address + 1); // Gets the stored volume;
bandIdx = EEPROM.read(eeprom_address + 2);
fmRDS = EEPROM.read(eeprom_address + 3);
currentMode = EEPROM.read(eeprom_address + 4);
currentBFO = EEPROM.read(eeprom_address + 5) << 8;
currentBFO |= EEPROM.read(eeprom_address + 6);
addr_offset = 7;
for (int i = 0; i <= lastBand; i++)
{
band[i].currentFreq = EEPROM.read(addr_offset++) << 8;
band[i].currentFreq |= EEPROM.read(addr_offset++);
band[i].currentStepIdx = EEPROM.read(addr_offset++);
band[i].bandwidthIdx = EEPROM.read(addr_offset++);
band[i].disableAgc = EEPROM.read(addr_offset++);
band[i].agcIdx = EEPROM.read(addr_offset++);
band[i].agcNdx = EEPROM.read(addr_offset++);
band[i].avcIdx = EEPROM.read(addr_offset++);
// char str[100];
// sprintf(str,"Pos %2.2d | disableAgc %2.2d | agcIdx %2.2d | agcNdx %2.2d | avcIdx %2.2d", i, band[i].disableAgc, band[i].agcIdx, band[i].agcNdx, band[i].avcIdx );
// Serial.println(str);
}
currentFrequency = band[bandIdx].currentFreq;
if (band[bandIdx].bandType == FM_BAND_TYPE)
{
currentStepIdx = idxFmStep = band[bandIdx].currentStepIdx;
rx.setFrequencyStep(tabFmStep[currentStepIdx]);
}
else
{
currentStepIdx = idxAmStep = band[bandIdx].currentStepIdx;
rx.setFrequencyStep(tabAmStep[currentStepIdx]);
}
bwIdx = band[bandIdx].bandwidthIdx;
if (currentMode == LSB || currentMode == USB)
{
loadSSB();
bwIdxSSB = (bwIdx > 5) ? 5 : bwIdx;
rx.setSSBAudioBandwidth(bandwidthSSB[bwIdxSSB].idx);
// If audio bandwidth selected is about 2 kHz or below, it is recommended to set Sideband Cutoff Filter to 0.
if (bandwidthSSB[bwIdxSSB].idx == 0 || bandwidthSSB[bwIdxSSB].idx == 4 || bandwidthSSB[bwIdxSSB].idx == 5)
rx.setSSBSidebandCutoffFilter(0);
else
rx.setSSBSidebandCutoffFilter(1);
}
else if (currentMode == AM)
{
bwIdxAM = bwIdx;
rx.setBandwidth(bandwidthAM[bwIdxAM].idx, 1);
}
else
{
bwIdxFM = bwIdx;
rx.setFmBandwidth(bandwidthFM[bwIdxFM].idx);
}
delay(50);
rx.setVolume(volume);
}
/*
* To store any change into the EEPROM, it is needed at least STORE_TIME milliseconds of inactivity.
*/
void resetEepromDelay()
{
elapsedCommand = storeTime = millis();
itIsTimeToSave = true;
}
/**
Set all command flags to false
When all flags are disabled (false), the encoder controls the frequency
*/
void disableCommands()
{
cmdBand = false;
bfoOn = false;
cmdVolume = false;
cmdAgc = false;
cmdBandwidth = false;
cmdStep = false;
cmdMode = false;
cmdMenu = false;
cmdSoftMuteMaxAtt = false;
cmdRds = false;
cmdAvc = false;
countClick = 0;
//showCommandStatus((char *) "TUNE ");
updateRSSIBar();
}
/**
* Reads encoder via interrupt
* Use Rotary.h and Rotary.cpp implementation to process encoder via interrupt
* if you do not add ICACHE_RAM_ATTR declaration, the system will reboot during attachInterrupt call.
* With ICACHE_RAM_ATTR macro you put the function on the RAM.
*/
void rotaryEncoder()
{ // rotary encoder events
uint8_t encoderStatus = encoder.process();
if (encoderStatus)
encoderCount = (encoderStatus == DIR_CW) ? 1 : -1;
}
/**
* Shows frequency information on Display
*/
void showFrequency()
{
char tmp[15];
char bufferDisplay[15];
char *unit;
int col = 4;
sprintf(tmp, "%5.5u", currentFrequency);
bufferDisplay[0] = (tmp[0] == '0') ? ' ' : tmp[0];
bufferDisplay[1] = tmp[1];
if (rx.isCurrentTuneFM())
{
bufferDisplay[2] = tmp[2];
bufferDisplay[3] = tmp[3];
bufferDisplay[4] = tmp[4];
if ( fmRDS ) {
col = 0;
unit = (char *)" ";
} else {
unit = (char *)"MHz";
}
}
else
{
if (currentFrequency < 1000)
{
bufferDisplay[1] = ' ';
bufferDisplay[2] = tmp[2];
bufferDisplay[3] = tmp[3];
bufferDisplay[4] = tmp[4];
}
else
{
bufferDisplay[2] = tmp[2];
bufferDisplay[3] = tmp[3];
bufferDisplay[4] = tmp[4];
}
unit = (char *)"kHz";
}
bufferDisplay[5] = '\0';
strcat(bufferDisplay, unit);
lcd.setCursor(col, 1);
lcd.print(bufferDisplay);
showMode();
}
/**
* Shows the current mode
*/
void showMode()
{
char *bandMode;
if (currentFrequency < 520)
bandMode = (char *)"LW ";
else
bandMode = (char *)bandModeDesc[currentMode];
lcd.setCursor(13, 1);
lcd.print(bandMode);
if ( currentMode == FM && fmRDS ) return;
lcd.setCursor(0, 1);
lcd.print(band[bandIdx].bandName);
}
/**
* Shows some basic information on display
*/
void showStatus()
{
lcd.clear();
showFrequency();
showRSSI();
}
/**
* Shows the current Bandwidth status
*/
void showBandwidth()
{
char *bw;
char bandwidth[20];
if (currentMode == LSB || currentMode == USB)
{
bw = (char *)bandwidthSSB[bwIdxSSB].desc;
showBFO();
}
else if (currentMode == AM)
{
bw = (char *)bandwidthAM[bwIdxAM].desc;
}
else
{
bw = (char *)bandwidthFM[bwIdxFM].desc;
}
sprintf(bandwidth, "BW: %s", bw);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(bandwidth);
}
/**
* Shows the current RSSI and SNR status
*/
void showRSSI()
{
int rssiAux = 0;
char sMeter[7];
if (currentMode == FM)
{
lcd.setCursor(14, 1);
lcd.print((rx.getCurrentPilot()) ? "ST" : "MO");
lcd.setCursor(10, 0);
if ( fmRDS ) {
lcd.print("RDS");
return;
}
else
lcd.print(" ");
}
sprintf(sMeter, "R%d ", rssi);
lcd.setCursor(0, 0);
lcd.print(sMeter);
char SNR[9];
sprintf(SNR, "S%d", rx.getCurrentSNR());
lcd.setCursor(13, 0);
lcd.print(SNR);
//serial monitor
rx.getStatus();
rx.getCurrentReceivedSignalQuality();
Serial.print("You are tuned on ");
if (rx.isCurrentTuneFM())
{
Serial.print(String(currentFrequency / 100.0, 2));
Serial.print("MHz | ");
Serial.print((rx.getCurrentPilot()) ? "STEREO" : "MONO");
}
else
{
Serial.print(currentFrequency);
Serial.print("kHz | ");
}
Serial.print(" [SNR:");
Serial.print(rx.getCurrentSNR());
Serial.print("dB | ");
Serial.print(" RSSI:");
Serial.print(rx.getCurrentRSSI());
Serial.println("dBuV]");
Serial.print(" | Volume: ");
Serial.println(rx.getVolume());
Serial.flush();
}
/**
* Shows the current AGC and Attenuation status
*/
void updateRSSIBar() {
int rssi = rx.getCurrentRSSI();
rssi = constrain(rssi, 0, 126);
int numBars = map(rssi, 0, 126, 0, 9);
lcd.setCursor(4, 0);
for (int i = 0; i < 9; i++) {
if (i < numBars) {
lcd.write(byte(0));
} else {
lcd.write(' ');
}
}
}
void showAgcAtt()
{
char sAgc[15];
lcd.clear();
rx.getAutomaticGainControl();
if ( !disableAgc /*agcNdx == 0 && agcIdx == 0 */ )
strcpy(sAgc, "AGC ON");
else
sprintf(sAgc, "ATT: %2.2d", agcNdx);
lcd.setCursor(0, 0);
lcd.print(sAgc);
}
/**
* Shows the current step
*/
void showStep()
{
char sStep[15];
sprintf(sStep, "Stp:%4d", (currentMode == FM)? (tabFmStep[currentStepIdx] *10) : tabAmStep[currentStepIdx] );
lcd.setCursor(0, 0);
lcd.print(sStep);
}
/**
* Shows the current BFO value
*/
void showBFO()
{
char bfo[15];
if (currentBFO > 0)
sprintf(bfo, "BFO:+%4.4d", currentBFO);
else
sprintf(bfo, "BFO:%4.4d", currentBFO);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(bfo);
elapsedCommand = millis();
}
/*
* Shows the volume level on LCD
*/
void showVolume()
{
char volAux[12];
sprintf(volAux, "VOLUME: %2u", rx.getVolume());
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(volAux);
}
/**
* Show Soft Mute
*/
void showSoftMute()
{
char sMute[18];
sprintf(sMute, "Soft Mute: %2d", softMuteMaxAttIdx);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(sMute);
}
/**
* Show Soft Mute
*/
void showAvc()
{
char sAvc[18];
sprintf(sAvc, "AVC: %2d", avcIdx);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(sAvc);
}
/**
* Shows RDS ON or OFF
*/
void showRdsSetup()
{
char sRdsStatus[10];
sprintf(sRdsStatus, "RDS: %s", (fmRDS)? "ON ": "OFF");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(sRdsStatus);
}
/***************
* RDS
*
*/
char *stationName;
char bufferStatioName[20];
char localTime[10];
uint16_t localYear, localMonth, localDay, localHour, localMinute;
void clearRDS() {
stationName = (char *) " ";
showRDSStation();
}
void showRDSStation()
{
int col = 7;
for (int i = 0; i < 8; i++ ) {
if (stationName[i] != bufferStatioName[i] ) {
lcd.setCursor(col + i, 1);
lcd.print(stationName[i]);
bufferStatioName[i] = stationName[i];
}
}
delay(100);
}
void showRDSTime()
{
lcd.setCursor(8,1);
lcd.print(localTime);
delay(200);
}
/*
* Checks the station name is available
*/
void checkRDS()
{
rx.getRdsStatus();
if (rx.getRdsReceived())
{
if (rx.getRdsSync() && rx.getRdsSyncFound() && !rx.getRdsSyncLost() && !rx.getGroupLost() )
{
stationName = rx.getRdsText0A();
if (stationName != NULL )
{
showRDSStation();
}
if ( rx.getRdsDateTime(&localYear, &localMonth, &localDay, &localHour, &localMinute) ) {
sprintf(localTime,"%02u:%02u", localHour, localMinute);
showRDSTime();
delay(500);
clearRDS();
}
}
}
}
/**
* Sets Band up (1) or down (!1)
*/
void setBand(int8_t up_down)
{
band[bandIdx].currentFreq = currentFrequency;
band[bandIdx].currentStepIdx = currentStepIdx;
if (up_down == 1)
bandIdx = (bandIdx < lastBand) ? (bandIdx + 1) : 0;
else
bandIdx = (bandIdx > 0) ? (bandIdx - 1) : lastBand;
useBand();
delay(MIN_ELAPSED_TIME); // waits a little more for releasing the button.
}
/**
* Switch the radio to current band
*/
void useBand()
{
if (band[bandIdx].bandType == FM_BAND_TYPE)
{
currentMode = FM;
//rx.setTuneFrequencyAntennaCapacitor(0);
rx.setFM(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, tabFmStep[band[bandIdx].currentStepIdx]);
rx.setSeekFmLimits(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq);
rx.setRdsConfig(1, 2, 2, 2, 2);
rx.setFifoCount(1);
bfoOn = ssbLoaded = false;
bwIdxFM = band[bandIdx].bandwidthIdx;
rx.setFmBandwidth(bandwidthFM[bwIdxFM].idx);
}
else
{
disableAgc = band[bandIdx].disableAgc;
agcIdx = band[bandIdx].agcIdx;
agcNdx = band[bandIdx].agcNdx;
avcIdx = band[bandIdx].avcIdx;
// char str[100];
// sprintf(str,"Pos %2.2d | disableAgc %2.2d | agcIdx %2.2d | agcNdx %2.2d | avcIdx %2.2d", bandIdx, disableAgc, agcIdx, agcNdx, avcIdx );
// Serial.println(str);
// set the tuning capacitor for SW or MW/LW
rx.setTuneFrequencyAntennaCapacitor((band[bandIdx].bandType == MW_BAND_TYPE || band[bandIdx].bandType == LW_BAND_TYPE) ? 0 : 1);
if (ssbLoaded)
{
rx.setSSB(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, tabAmStep[band[bandIdx].currentStepIdx], currentMode);
rx.setSSBAutomaticVolumeControl(1);
rx.setSsbSoftMuteMaxAttenuation(softMuteMaxAttIdx); // Disable Soft Mute for SSB
bwIdxSSB = band[bandIdx].bandwidthIdx;
rx.setSSBAudioBandwidth(bandwidthSSB[bwIdxSSB].idx);
delay(500);
rx.setSsbAgcOverrite(disableAgc, agcNdx);
}
else
{
currentMode = AM;
rx.setAM(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq, band[bandIdx].currentFreq, tabAmStep[band[bandIdx].currentStepIdx]);
bfoOn = false;
bwIdxAM = band[bandIdx].bandwidthIdx;
rx.setBandwidth(bandwidthAM[bwIdxAM].idx, 1);
rx.setAmSoftMuteMaxAttenuation(softMuteMaxAttIdx); // Soft Mute for AM or SSB
rx.setAutomaticGainControl(disableAgc, agcNdx);
}
rx.setSeekAmLimits(band[bandIdx].minimumFreq, band[bandIdx].maximumFreq); // Consider the range all defined current band
rx.setSeekAmSpacing(5); // Max 10kHz for spacing
rx.setAvcAmMaxGain(avcIdx);
}
delay(100);
currentFrequency = band[bandIdx].currentFreq;
currentStepIdx = band[bandIdx].currentStepIdx;
rssi = 0;
showStatus();
showCommandStatus((char *) "Band");
}
void loadSSB() {
rx.setI2CFastModeCustom(400000); // You can try rx.setI2CFastModeCustom(700000); or greater value
rx.loadPatch(ssb_patch_content, size_content, bandwidthSSB[bwIdxSSB].idx);
rx.setI2CFastModeCustom(100000);
ssbLoaded = true;
}
/**
* Switches the Bandwidth
*/
void doBandwidth(int8_t v)
{
if (currentMode == LSB || currentMode == USB)
{
bwIdxSSB = (v == 1) ? bwIdxSSB + 1 : bwIdxSSB - 1;
if (bwIdxSSB > maxSsbBw)
bwIdxSSB = 0;
else if (bwIdxSSB < 0)
bwIdxSSB = maxSsbBw;
rx.setSSBAudioBandwidth(bandwidthSSB[bwIdxSSB].idx);
// If audio bandwidth selected is about 2 kHz or below, it is recommended to set Sideband Cutoff Filter to 0.
if (bandwidthSSB[bwIdxSSB].idx == 0 || bandwidthSSB[bwIdxSSB].idx == 4 || bandwidthSSB[bwIdxSSB].idx == 5)
rx.setSSBSidebandCutoffFilter(0);
else
rx.setSSBSidebandCutoffFilter(1);
band[bandIdx].bandwidthIdx = bwIdxSSB;
}
else if (currentMode == AM)
{
bwIdxAM = (v == 1) ? bwIdxAM + 1 : bwIdxAM - 1;
if (bwIdxAM > maxAmBw)
bwIdxAM = 0;
else if (bwIdxAM < 0)
bwIdxAM = maxAmBw;
rx.setBandwidth(bandwidthAM[bwIdxAM].idx, 1);
band[bandIdx].bandwidthIdx = bwIdxAM;
} else {
bwIdxFM = (v == 1) ? bwIdxFM + 1 : bwIdxFM - 1;
if (bwIdxFM > maxFmBw)
bwIdxFM = 0;
else if (bwIdxFM < 0)
bwIdxFM = maxFmBw;
rx.setFmBandwidth(bandwidthFM[bwIdxFM].idx);
band[bandIdx].bandwidthIdx = bwIdxFM;
}
showBandwidth();
delay(MIN_ELAPSED_TIME); // waits a little more for releasing the button.
}
/**
* Show cmd on display. It means you are setting up something.
*/
void showCommandStatus(char * currentCmd)
{
lcd.setCursor(5, 0);
lcd.print(currentCmd);
}
/**
* Show menu options
*/
void showMenu() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.setCursor(0, 1);
lcd.print(menu[menuIdx]);
showCommandStatus( (char *) "Menu");
}
/**
* AGC and attenuattion setup
*/
void doAgc(int8_t v) {
agcIdx = (v == 1) ? agcIdx + 1 : agcIdx - 1;
if (agcIdx < 0 )
agcIdx = 37;
else if ( agcIdx > 37)
agcIdx = 0;
disableAgc = (agcIdx > 0); // if true, disable AGC; esle, AGC is enable
if (agcIdx > 1)
agcNdx = agcIdx - 1;
else
agcNdx = 0;
if ( currentMode == AM )
rx.setAutomaticGainControl(disableAgc, agcNdx); // if agcNdx = 0, no attenuation
else
rx.setSsbAgcOverrite(disableAgc, agcNdx, 0B1111111);
band[bandIdx].disableAgc = disableAgc;
band[bandIdx].agcIdx = agcIdx;
band[bandIdx].agcNdx = agcNdx;
showAgcAtt();
delay(MIN_ELAPSED_TIME); // waits a little more for releasing the button.
elapsedCommand = millis();
}
/**
* Switches the current step
*/
void doStep(int8_t v)
{
if ( currentMode == FM ) {
idxFmStep = (v == 1) ? idxFmStep + 1 : idxFmStep - 1;
if (idxFmStep > lastFmStep)
idxFmStep = 0;
else if (idxFmStep < 0)
idxFmStep = lastFmStep;
currentStepIdx = idxFmStep;
rx.setFrequencyStep(tabFmStep[currentStepIdx]);
} else {
idxAmStep = (v == 1) ? idxAmStep + 1 : idxAmStep - 1;
if (idxAmStep > lastAmStep)
idxAmStep = 0;
else if (idxAmStep < 0)
idxAmStep = lastAmStep;
currentStepIdx = idxAmStep;
rx.setFrequencyStep(tabAmStep[currentStepIdx]);
rx.setSeekAmSpacing(5); // Max 10kHz for spacing
}
band[bandIdx].currentStepIdx = currentStepIdx;
showStep();
delay(MIN_ELAPSED_TIME); // waits a little more for releasing the button.
elapsedCommand = millis();
}
/**
* Switches to the AM, LSB or USB modes
*/
void doMode(int8_t v)
{
if (currentMode != FM)
{
if (v == 1) { // clockwise
if (currentMode == AM)
{
// If you were in AM mode, it is necessary to load SSB patch (every time)
loadSSB();
ssbLoaded = true;
currentMode = LSB;
}
else if (currentMode == LSB)
currentMode = USB;
else if (currentMode == USB)
{
currentMode = AM;
bfoOn = ssbLoaded = false;
}
} else { // and counterclockwise
if (currentMode == AM)
{
// If you were in AM mode, it is necessary to load SSB patch (every time)
loadSSB();
ssbLoaded = true;
currentMode = USB;
}
else if (currentMode == USB)
currentMode = LSB;
else if (currentMode == LSB)
{
currentMode = AM;
bfoOn = ssbLoaded = false;
}
}
// Nothing to do if you are in FM mode
band[bandIdx].currentFreq = currentFrequency;
band[bandIdx].currentStepIdx = currentStepIdx;
useBand();
}
delay(MIN_ELAPSED_TIME); // waits a little more for releasing the button.
elapsedCommand = millis();
}
/**
* Sets the audio volume
*/
void doVolume( int8_t v ) {
if ( v == 1)
rx.volumeUp();
else
rx.volumeDown();
showVolume();
delay(MIN_ELAPSED_TIME); // waits a little more for releasing the button.
}
/**
* This function is called by the seek function process.
*/
void showFrequencySeek(uint16_t freq)
{
currentFrequency = freq;
showFrequency();
}
/**
* Find a station. The direction is based on the last encoder move clockwise or counterclockwise
*/
void doSeek()
{
if ((currentMode == LSB || currentMode == USB)) return; // It does not work for SSB mode
lcd.clear();
rx.seekStationProgress(showFrequencySeek, seekDirection);
showStatus();
currentFrequency = rx.getFrequency();
}
/**
* Sets the Soft Mute Parameter
*/
void doSoftMute(int8_t v)
{
softMuteMaxAttIdx = (v == 1) ? softMuteMaxAttIdx + 1 : softMuteMaxAttIdx - 1;
if (softMuteMaxAttIdx > 32)
softMuteMaxAttIdx = 0;
else if (softMuteMaxAttIdx < 0)
softMuteMaxAttIdx = 32;
rx.setAmSoftMuteMaxAttenuation(softMuteMaxAttIdx);
showSoftMute();
elapsedCommand = millis();
}
/**
* Sets the Max gain for Automatic Volume Control.
*/
void doAvc(int8_t v)
{
avcIdx = (v == 1) ? avcIdx + 2 : avcIdx - 2;
if (avcIdx > 90)
avcIdx = 12;
else if (avcIdx < 12)
avcIdx = 90;
rx.setAvcAmMaxGain(avcIdx);
band[bandIdx].avcIdx = avcIdx;
showAvc();
elapsedCommand = millis();
}
/**
* Turns RDS ON or OFF
*/
void doRdsSetup(int8_t v)
{
fmRDS = (v == 1)? true:false;
showRdsSetup();
elapsedCommand = millis();
}
/**
* Menu options selection
*/
void doMenu( int8_t v) {
menuIdx = (v == 1) ? menuIdx + 1 : menuIdx - 1;
if (menuIdx > lastMenu)
menuIdx = 0;
else if (menuIdx < 0)
menuIdx = lastMenu;
showMenu();
delay(MIN_ELAPSED_TIME); // waits a little more for releasing the button.
elapsedCommand = millis();
}
/**
* Return true if the current status is Menu command
*/
bool isMenuMode() {
return (cmdMenu | cmdStep | cmdBandwidth | cmdAgc | cmdVolume | cmdSoftMuteMaxAtt | cmdMode | cmdRds | cmdAvc);
}
/**
* Starts the MENU action process
*/
void doCurrentMenuCmd() {
disableCommands();
switch (currentMenuCmd) {
case 0: // VOLUME
cmdVolume = true;
showVolume();
break;
case 1:
cmdRds = true;
showRdsSetup();
break;
case 2: // STEP
cmdStep = true;
showStep();
break;
case 3: // MODE
cmdMode = true;
lcd.clear();
showMode();
break;
case 4:
bfoOn = true;
if ((currentMode == LSB || currentMode == USB)) {
showBFO();
}
// showFrequency();
break;
case 5: // BW
cmdBandwidth = true;
showBandwidth();
break;
case 6: // AGC/ATT
cmdAgc = true;
showAgcAtt();
break;
case 7:
cmdSoftMuteMaxAtt = true;
showSoftMute();
break;
case 8:
cmdAvc = true;
showAvc();
break;
case 9:
seekDirection = 1;
doSeek();
break;
case 10:
seekDirection = 0;
doSeek();
break;
default:
showStatus();
break;
}
currentMenuCmd = -1;
elapsedCommand = millis();
}
/**
* Main loop
*/
void loop()
{
// Check if the encoder has moved.
if (encoderCount != 0)
{
if (bfoOn & (currentMode == LSB || currentMode == USB))
{
currentBFO = (encoderCount == 1) ? (currentBFO + currentBFOStep) : (currentBFO - currentBFOStep);
rx.setSSBBfo(currentBFO);
showBFO();
}
else if (cmdMenu)
doMenu(encoderCount);
else if (cmdMode)
doMode(encoderCount);
else if (cmdStep)
doStep(encoderCount);
else if (cmdAgc)
doAgc(encoderCount);
else if (cmdBandwidth)
doBandwidth(encoderCount);
else if (cmdVolume)
doVolume(encoderCount);
else if (cmdSoftMuteMaxAtt)
doSoftMute(encoderCount);
else if (cmdAvc)
doAvc(encoderCount);
else if (cmdBand)
setBand(encoderCount);
else if (cmdRds )
doRdsSetup(encoderCount);
else
{
if (encoderCount == 1)
{
rx.frequencyUp();
}
else
{
rx.frequencyDown();
}
// Show the current frequency only if it has changed
currentFrequency = rx.getFrequency();
showFrequency();
}
encoderCount = 0;
resetEepromDelay();
}
else
{
if (digitalRead(ENCODER_PUSH_BUTTON) == LOW)
{
countClick++;
if (cmdMenu)
{
currentMenuCmd = menuIdx;
doCurrentMenuCmd();
}
else if (countClick == 1)
{ // If just one click, you can select the band by rotating the encoder
if (isMenuMode())
{
disableCommands();
showStatus();
showCommandStatus((char *)"VFO ");
}
else if ( bfoOn ) {
bfoOn = false;
showStatus();
}
else
{
cmdBand = !cmdBand;
showCommandStatus((char *)"Band");
}
}
else
{ // GO to MENU if more than one click in less than 1/2 seconds.
cmdMenu = !cmdMenu;
if (cmdMenu)
showMenu();
}
delay(MIN_ELAPSED_TIME);
elapsedCommand = millis();
}
}
// Show RSSI status only if this condition has changed
if ((millis() - elapsedRSSI) > MIN_ELAPSED_RSSI_TIME)
{
rx.getCurrentReceivedSignalQuality();
int aux = rx.getCurrentRSSI();
if (rssi != aux && !isMenuMode())
{
rssi = aux;
showRSSI();
}
elapsedRSSI = millis();
}
// Disable commands control
if ((millis() - elapsedCommand) > ELAPSED_COMMAND)
{
if ((currentMode == LSB || currentMode == USB) )
{
bfoOn = false;
// showBFO();
showStatus();
} else if (isMenuMode())
showStatus();
disableCommands();
elapsedCommand = millis();
}
if ((millis() - elapsedClick) > ELAPSED_CLICK)
{
countClick = 0;
elapsedClick = millis();
}
// Show the current frequency only if it has changed
if (itIsTimeToSave)
{
if ((millis() - storeTime) > STORE_TIME)
{
saveAllReceiverInformation();
storeTime = millis();
itIsTimeToSave = false;
}
}
if (currentMode == FM && fmRDS && !isMenuMode() )
{
if (currentFrequency != previousFrequency)
{
clearRDS();
previousFrequency = currentFrequency;
}
else
{
checkRDS();
}
}
delay(5);
}
Sketch, Library dan Skema menggunakan Github Si4735 PU2CLR dengan modifikasi dan penambahan seperlunya.