just sharing my life experiences

Welcome

Rabu, 14 Oktober 2026

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. 

Jumat, 28 Agustus 2026

Radio FM menggunakan RDA5807 dengan ESP32 dan oled 0.96 inch

 bahan :

ESP32, RDA5807FP atau RDA5807M, oled display 0.96inch, rotary encoder, kabel secukupnya, PCB dual layer (opsional)

kode cpp :

#include <Wire.h>

#include <RDA5807.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>


// Definisi Dimensi OLED

#define SCREEN_I2C_ADDR 0x3C

#define SCREEN_WIDTH 128

#define SCREEN_HEIGHT 64

#define OLED_RESET    -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


// Definisi Pin Rotary Encoder

#define PIN_CLK 14

#define PIN_DT  27

#define PIN_SW  26


//status baterai

#define PIN_BATTERY 34

int getBatteryPercentage() {

  // Ambil beberapa sampel pembacaan agar angka persentase stabil / tidak melompat-lompat

  long sum = 0;

  for (int i = 0; i < 10; i++) {

    sum += analogRead(PIN_BATTERY);

    delay(2);

  }

  float rawADC = sum / 10.0;

  

  // Konversi ADC (12-bit = 0-4095) ke Tegangan Baterai

  // Catatan: Jika persen baterai penuh terbaca kurang dari 100%, 

  // ubah angka 3.3 di bawah menjadi 3.15 atau 3.2 secara perlahan.

  float voltage = (rawADC / 4095.0) * 3.3 * 2.0; 

  

  // Rentang tegangan Li-ion 18650: 3.2V (0%) sampai 4.2V (100%)

  int percentage = map(voltage * 100, 320, 420, 0, 100);

  

  // Batasi agar tidak minus atau lebih dari 100%

  return constrain(percentage, 0, 100);

}


// animasi sinyal

#define FRAME_DELAY (42)

#define FRAME_WIDTH (64)

#define FRAME_HEIGHT (64)

#define FRAME_COUNT (sizeof(frames) / sizeof(frames[0]))

const byte PROGMEM frames[][512] = 

{

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,7,224,0,62,60,30,240,0,255,255,0,15,120,15,224,7,255,255,224,7,240,7,192,31,224,7,248,3,224,3,0,127,0,0,254,0,192,0,1,248,0,0,31,128,0,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,192,3,240,224,0,0,3,158,0,0,121,192,0,0,1,252,0,0,63,128,0,0,0,240,0,0,31,0,0,0,0,96,0,0,6,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,7,224,0,62,60,30,240,0,255,255,0,15,120,15,224,7,255,255,224,7,240,7,192,31,224,7,248,3,224,3,0,127,0,0,254,0,192,0,1,248,0,0,31,128,0,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,192,3,240,224,0,0,3,158,0,0,121,192,0,0,1,252,0,0,63,128,0,0,0,240,0,0,31,0,0,0,0,96,0,0,6,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,7,224,0,62,60,30,240,0,255,255,0,15,120,15,224,7,255,255,224,7,240,7,192,31,224,7,248,3,224,3,0,127,0,0,254,0,192,0,1,248,0,0,31,128,0,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,192,3,240,224,0,0,3,158,0,0,121,192,0,0,1,252,0,0,63,128,0,0,0,240,0,0,31,0,0,0,0,96,0,0,6,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,7,224,0,62,60,30,240,0,255,255,0,15,120,15,224,7,255,255,224,7,240,7,192,31,224,7,248,3,224,3,0,127,0,0,254,0,192,0,1,248,0,0,31,128,0,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,192,3,240,224,0,0,3,158,0,0,121,192,0,0,1,252,0,0,63,128,0,0,0,240,0,0,31,0,0,0,0,96,0,0,6,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,127,254,0,62,60,30,240,7,255,255,224,15,120,15,224,31,224,7,248,7,240,7,192,127,0,0,254,3,224,3,0,248,0,0,31,0,192,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,224,7,240,224,0,0,3,159,0,0,249,192,0,0,1,252,0,0,63,128,0,0,1,248,0,0,31,128,0,0,0,224,0,0,7,0,0,0,0,0,31,248,0,0,0,0,0,0,127,254,0,0,0,0,0,1,248,31,128,0,0,0,0,3,192,3,192,0,0,0,0,3,128,1,192,0,0,0,0,1,192,3,128,0,0,0,0,0,224,7,0,0,0,0,0,0,240,14,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,255,0,0,0,0,0,15,255,255,240,0,0,0,0,127,255,255,254,0,0,0,3,254,0,0,127,192,0,0,15,224,0,0,7,240,0,0,63,128,0,0,1,252,0,0,254,0,0,0,0,127,0,1,248,0,0,0,0,31,128,3,192,0,255,255,0,7,192,15,128,15,255,255,240,1,240,31,0,127,128,1,254,0,248,60,1,252,0,0,63,128,60,120,7,224,0,0,7,192,30,240,15,128,0,0,1,240,15,120,30,0,255,255,0,120,30,60,124,7,255,255,224,62,60,30,240,31,224,7,248,15,120,15,224,126,0,0,126,7,240,7,192,248,0,0,31,3,224,3,3,224,0,0,7,192,192,0,7,128,0,0,1,224,0,0,15,0,3,192,0,240,0,0,30,0,127,254,0,120,0,0,30,3,255,255,192,120,0,0,15,7,224,7,224,240,0,0,7,159,0,0,249,224,0,0,3,252,0,0,63,192,0,0,1,248,0,0,31,128,0,0,0,224,0,0,15,0,0,0,0,64,15,240,2,0,0,0,0,0,63,252,0,0,0,0,0,0,255,255,0,0,0,0,0,1,240,15,128,0,0,0,0,3,192,3,192,0,0,0,0,3,128,1,192,0,0,0,0,1,192,3,128,0,0,0,0,0,224,7,0,0,0,0,0,0,112,14,0,0,0,0,0,0,56,28,0,0,0,0,0,0,28,56,0,0,0,0,0,0,14,112,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,224,0,0,0,0,0,3,255,255,192,0,0,0,0,63,255,255,252,0,0,0,1,255,224,7,255,128,0,0,7,248,0,0,31,224,0,0,31,192,0,0,3,248,0,0,126,0,0,0,0,126,0,0,248,0,0,0,0,31,0,3,224,0,31,248,0,7,192,7,128,3,255,255,192,1,224,15,0,63,255,255,252,0,240,30,0,255,0,0,255,0,120,120,3,248,0,0,31,192,30,240,15,192,0,0,3,240,15,240,31,128,0,0,1,248,15,120,62,0,7,224,0,124,30,60,248,1,255,255,128,30,60,31,240,15,255,255,240,15,248,15,192,63,192,3,252,3,240,7,128,126,0,0,126,1,224,3,1,240,0,0,15,128,192,0,3,192,0,0,3,192,0,0,15,128,0,0,1,224,0,0,30,0,15,240,0,120,0,0,28,0,255,255,0,56,0,0,30,3,255,255,192,120,0,0,15,15,224,7,240,240,0,0,7,159,0,0,249,224,0,0,3,252,0,0,63,192,0,0,1,248,0,0,31,128,0,0,0,224,0,0,7,0,0,0,0,64,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,255,255,0,0,0,0,0,1,224,7,128,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,14,112,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,255,128,0,0,0,0,15,255,255,240,0,0,0,0,255,248,31,255,0,0,0,3,252,0,0,63,192,0,0,15,192,0,0,3,240,0,0,63,0,0,0,0,252,0,0,252,0,0,0,0,63,0,1,240,0,0,0,0,15,128,7,192,0,255,255,0,3,224,15,0,15,255,255,240,0,240,30,0,127,248,31,254,0,120,60,1,254,0,0,127,128,60,248,7,240,0,0,15,224,31,240,15,128,0,0,1,240,15,240,30,0,0,0,0,120,15,120,124,0,0,0,0,62,30,60,240,0,0,0,0,15,60,31,224,0,0,0,0,7,248,15,128,0,0,0,0,1,240,7,0,0,0,0,0,0,224,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,254,0,0,0,0,0,3,255,255,192,0,0,0,0,15,240,15,240,0,0,0,0,31,0,0,248,0,0,0,0,124,0,0,60,0,0,0,0,240,0,0,15,0,0,0,1,224,15,240,7,128,0,0,3,192,127,254,3,192,0,0,1,225,255,255,135,128,0,0,0,247,192,3,239,0,0,0,0,127,0,0,254,0,0,0,0,62,0,0,124,0,0,0,0,24,7,224,24,0,0,0,0,0,63,252,0,0,0,0,0,0,255,255,0,0,0,0,0,1,248,31,128,0,0,0,0,3,192,3,192,0,0,0,0,3,128,1,192,0,0,0,0,1,192,3,128,0,0,0,0,0,224,7,0,0,0,0,0,0,240,14,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,3,255,255,192,0,0,0,0,31,192,3,248,0,0,0,0,126,0,0,126,0,0,0,1,240,0,0,15,128,0,0,3,192,0,0,3,192,0,0,15,0,63,252,0,240,0,0,28,3,255,255,192,56,0,0,56,15,224,7,240,28,0,0,240,62,0,0,124,15,0,0,224,248,0,0,31,7,0,0,113,224,0,0,7,142,0,0,59,128,0,0,3,220,0,0,31,0,0,0,0,248,0,0,14,0,0,0,0,112,0,0,4,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,248,0,0,0,0,0,0,124,62,0,0,0,0,0,1,239,247,128,0,0,0,0,3,191,253,192,0,0,0,0,3,255,255,192,0,0,0,0,3,224,7,128,0,0,0,0,3,128,3,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,14,112,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,224,0,0,0,0,0,0,63,252,0,0,0,0,0,0,224,7,0,0,0,0,0,3,143,225,192,0,0,0,0,6,63,252,96,0,0,0,0,12,224,7,48,0,0,0,0,7,159,249,224,0,0,0,0,3,127,254,192,0,0,0,0,1,252,63,128,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,224,7,0,0,0,0,0,3,159,249,192,0,0,0,0,7,127,254,224,0,0,0,0,3,252,63,192,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,248,0,0,0,0,0,1,255,255,128,0,0,0,0,7,252,63,224,0,0,0,0,31,0,0,248,0,0,0,0,124,0,0,62,0,0,0,0,240,127,254,15,0,0,0,3,195,255,255,195,192,0,0,7,135,224,7,225,224,0,0,15,31,0,0,248,240,0,0,7,60,0,0,60,224,0,0,3,248,0,0,31,192,0,0,1,224,0,0,7,128,0,0,0,192,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,248,0,0,0,0,0,0,127,254,0,0,0,0,0,0,255,255,0,0,0,0,0,1,252,63,128,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,248,0,0,0,0,0,3,255,255,192,0,0,0,0,63,255,255,252,0,0,0,1,255,0,0,255,128,0,0,3,240,0,0,15,192,0,0,15,128,0,0,1,240,0,0,62,0,0,0,0,124,0,0,120,0,127,254,0,30,0,0,240,7,255,255,224,15,0,3,224,31,255,255,248,7,192,7,128,127,0,1,254,1,224,15,1,248,0,0,31,128,240,14,7,224,0,0,7,224,112,7,15,128,0,0,1,240,224,3,158,0,0,0,0,121,192,1,252,0,0,0,0,63,128,0,248,0,0,0,0,31,0,0,224,0,0,0,0,7,0,0,64,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,254,0,0,0,0,0,1,255,255,128,0,0,0,0,7,224,7,224,0,0,0,0,15,128,1,240,0,0,0,0,30,7,224,120,0,0,0,0,56,63,252,28,0,0,0,0,60,255,255,60,0,0,0,0,31,240,15,248,0,0,0,0,15,207,243,240,0,0,0,0,7,63,252,224,0,0,0,0,0,255,255,0,0,0,0,0,1,224,7,128,0,0,0,0,3,128,3,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,14,112,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,255,128,0,0,0,0,15,255,255,240,0,0,0,0,255,248,31,255,0,0,0,3,252,0,0,63,192,0,0,15,192,0,0,3,240,0,0,63,0,0,0,0,252,0,0,252,0,0,0,0,63,0,1,240,0,0,0,0,15,128,7,192,0,255,255,0,3,224,15,0,15,255,255,240,0,240,30,0,127,248,31,254,0,120,60,1,254,0,0,127,128,60,248,7,240,0,0,15,224,31,240,15,128,0,0,1,240,15,240,30,0,0,0,0,120,15,120,124,0,0,0,0,62,30,60,240,0,0,0,0,15,60,31,224,0,0,0,0,7,248,15,128,0,0,0,0,1,240,7,0,0,0,0,0,0,224,2,0,0,31,248,0,0,64,0,0,0,255,255,0,0,0,0,0,7,255,255,224,0,0,0,0,31,192,3,248,0,0,0,0,124,0,0,62,0,0,0,0,240,0,0,31,0,0,0,1,224,0,0,7,128,0,0,7,192,31,248,3,224,0,0,7,128,255,255,1,224,0,0,3,131,255,255,193,192,0,0,1,199,192,3,227,128,0,0,0,255,0,0,255,0,0,0,0,252,0,0,62,0,0,0,0,120,0,0,30,0,0,0,0,48,0,0,12,0,0,0,0,0,7,224,0,0,0,0,0,0,63,252,0,0,0,0,0,0,255,255,0,0,0,0,0,1,248,31,128,0,0,0,0,3,192,3,192,0,0,0,0,3,128,1,192,0,0,0,0,1,192,3,128,0,0,0,0,0,224,7,0,0,0,0,0,0,240,14,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,224,0,0,0,0,0,3,255,255,192,0,0,0,0,63,255,255,252,0,0,0,1,255,224,7,255,128,0,0,7,248,0,0,31,224,0,0,31,192,0,0,3,248,0,0,126,0,0,0,0,126,0,0,248,0,0,0,0,31,0,3,224,0,31,248,0,7,192,7,128,3,255,255,192,1,224,15,0,63,255,255,252,0,240,30,0,255,0,0,255,0,120,120,3,248,0,0,31,192,30,240,15,192,0,0,3,240,15,240,31,128,0,0,1,248,15,120,62,0,7,224,0,124,30,60,248,1,255,255,128,30,60,31,240,15,255,255,240,15,248,15,192,63,192,3,252,3,240,7,128,126,0,0,126,1,224,3,1,240,0,0,15,128,192,0,3,192,0,0,3,192,0,0,15,128,0,0,1,224,0,0,30,0,15,240,0,120,0,0,28,0,255,255,0,56,0,0,30,3,255,255,192,120,0,0,15,15,224,7,240,240,0,0,7,159,0,0,249,224,0,0,3,252,0,0,63,192,0,0,1,248,0,0,31,128,0,0,0,224,0,0,7,0,0,0,0,64,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,255,255,0,0,0,0,0,1,224,7,128,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,14,112,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,255,0,0,0,0,0,15,255,255,240,0,0,0,0,127,255,255,254,0,0,0,3,254,0,0,127,192,0,0,15,224,0,0,7,240,0,0,63,128,0,0,1,252,0,0,254,0,0,0,0,127,0,1,248,0,0,0,0,31,128,3,192,0,255,255,0,7,192,15,128,15,255,255,240,1,240,31,0,127,128,1,254,0,248,60,1,252,0,0,63,128,60,120,7,224,0,0,7,192,30,240,15,128,0,0,1,240,15,120,30,0,255,255,0,120,30,60,124,7,255,255,224,62,60,30,240,31,224,7,248,15,120,15,224,126,0,0,126,7,240,7,192,248,0,0,31,3,224,3,3,224,0,0,7,192,192,0,7,128,0,0,1,224,0,0,15,0,3,192,0,240,0,0,30,0,127,254,0,120,0,0,30,3,255,255,192,120,0,0,15,7,224,7,224,240,0,0,7,159,0,0,249,224,0,0,3,252,0,0,63,192,0,0,1,248,0,0,31,128,0,0,0,224,0,0,15,0,0,0,0,64,15,240,2,0,0,0,0,0,63,252,0,0,0,0,0,0,255,255,0,0,0,0,0,1,240,15,128,0,0,0,0,3,192,3,192,0,0,0,0,3,128,1,192,0,0,0,0,1,192,3,128,0,0,0,0,0,224,7,0,0,0,0,0,0,112,14,0,0,0,0,0,0,56,28,0,0,0,0,0,0,28,56,0,0,0,0,0,0,14,112,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,127,254,0,62,60,30,240,7,255,255,224,15,120,15,224,31,224,7,248,7,240,7,192,127,0,0,254,3,224,3,0,248,0,0,31,0,192,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,224,7,240,224,0,0,3,159,0,0,249,192,0,0,1,252,0,0,63,128,0,0,1,248,0,0,31,128,0,0,0,224,0,0,7,0,0,0,0,0,31,248,0,0,0,0,0,0,127,254,0,0,0,0,0,1,248,31,128,0,0,0,0,3,192,3,192,0,0,0,0,3,128,1,192,0,0,0,0,1,192,3,128,0,0,0,0,0,224,7,0,0,0,0,0,0,240,14,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,7,224,0,62,60,30,240,0,255,255,0,15,120,15,224,7,255,255,224,7,240,7,192,31,224,7,248,3,224,3,0,127,0,0,254,0,192,0,1,248,0,0,31,128,0,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,192,3,240,224,0,0,3,158,0,0,121,192,0,0,1,252,0,0,63,128,0,0,0,240,0,0,31,0,0,0,0,96,0,0,6,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,7,224,0,62,60,30,240,0,255,255,0,15,120,15,224,7,255,255,224,7,240,7,192,31,224,7,248,3,224,3,0,127,0,0,254,0,192,0,1,248,0,0,31,128,0,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,192,3,240,224,0,0,3,158,0,0,121,192,0,0,1,252,0,0,63,128,0,0,0,240,0,0,31,0,0,0,0,96,0,0,6,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,252,0,0,0,0,0,7,255,255,224,0,0,0,0,127,255,255,254,0,0,0,1,254,0,0,127,128,0,0,7,224,0,0,7,224,0,0,31,128,0,0,1,248,0,0,126,0,0,0,0,126,0,1,248,0,0,0,0,31,128,3,192,0,127,254,0,7,192,7,128,7,255,255,224,1,224,31,0,63,248,31,252,0,240,60,0,254,0,0,127,0,60,120,3,240,0,0,15,192,30,240,15,192,0,0,3,240,15,120,31,0,0,0,0,248,30,60,124,0,7,224,0,62,60,30,240,0,255,255,0,15,120,15,224,7,255,255,224,7,240,7,192,31,224,7,248,3,224,3,0,127,0,0,254,0,192,0,1,248,0,0,31,128,0,0,3,224,0,0,7,192,0,0,7,128,0,0,1,224,0,0,15,0,15,240,0,240,0,0,30,0,255,255,0,120,0,0,14,3,255,255,192,112,0,0,7,15,192,3,240,224,0,0,3,158,0,0,121,192,0,0,1,252,0,0,63,128,0,0,0,240,0,0,31,0,0,0,0,96,0,0,6,0,0,0,0,0,15,240,0,0,0,0,0,0,127,254,0,0,0,0,0,0,252,63,0,0,0,0,0,3,224,7,192,0,0,0,0,3,128,1,192,0,0,0,0,3,192,3,192,0,0,0,0,1,224,7,128,0,0,0,0,0,240,15,0,0,0,0,0,0,120,30,0,0,0,0,0,0,60,60,0,0,0,0,0,0,30,120,0,0,0,0,0,0,15,240,0,0,0,0,0,0,7,224,0,0,0,0,0,0,3,192,0,0,0,0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

};



//efek teks ketik berjalan

void printTypewriter(const char* str, uint16_t delayMs) {

  while (*str) {

    display.print(*str);

    display.display();

    delay(delayMs);

    str++;

  }

}


// deteksi IC RDA5807

uint8_t i2cAdd[5];

int i2cStatus;


// Inisialisasi Radio

RDA5807 rx;


// Variabel Kontrol Radio

uint16_t currentFrequency = 9200; // 100.00 MHz (dalam format kHz / 10)

uint8_t currentVolume = 5;        // Skala 0 - 63 pada RDA5807

bool isStereoMode = true;          // true = Stereo, false = Mono

uint16_t stepFreq = 10;            // Step 10 = 100 kHz (0.1 MHz), Step 5 = 50 kHz (0.05 MHz)


unsigned long lastRssiUpdate = 0;

const unsigned long RSSI_UPDATE_INTERVAL = 250; // Perbarui status RSSI setiap 250ms


// Modul Menu State

// 0: Tuning Frekuensi, 1: Volume, 2: Step Frekuensi (50k/100k), 3: Mono/Stereo

enum ModeMenu { TUNING, VOLUME, STEP_SELECT, AUDIO_MODE };

ModeMenu currentMenu = TUNING;


// Variabel Encoder State

int lastClkState;

unsigned long lastDebounceTime = 0;


void setup() {

  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_I2C_ADDR);

  

Serial.begin(115200);


  // Inisialisasi OLED

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {

    Serial.println(F("OLED SSD1306 gagal diinisialisasi!"));

    for (;;);

  }

  display.clearDisplay();

  display.setTextColor(SSD1306_WHITE);

  

  // Inisialisasi IC RDA5807

  i2cStatus = rx.checkI2C(i2cAdd); 

  if (i2cStatus == -1) {

    display.print(F("\nError while try to access the device.\n")); 

    while(1);

  }

  else if(i2cStatus == 0) {

    display.print(F("\nNo device was detected. Please, check your circuit!"));

    while(1);

  }

  else { 

    display.print(F("\n"));

    printTypewriter("RDA5807 found: \nStarting", 75);

    display.setCursor(0, 25);

    display.display();

    delay(1000);

display.clearDisplay();

  }

  

  // play animasi 

  unsigned long welcomeStartTime = millis();

  const unsigned long WELCOME_DURATION = 3500;

  int frame = 0;


  while (millis() - welcomeStartTime < WELCOME_DURATION) {

    display.clearDisplay();

    display.drawBitmap(32, 0, frames[frame], FRAME_WIDTH, FRAME_HEIGHT, 1);

    display.display();

    

    frame = (frame + 1) % FRAME_COUNT;

    delay(FRAME_DELAY); // Delay antar frame animasi

  }

  

  // Inisialisasi Pin Encoder

  pinMode(PIN_CLK, INPUT_PULLUP);

  pinMode(PIN_DT, INPUT_PULLUP);

  pinMode(PIN_SW, INPUT_PULLUP);

  lastClkState = digitalRead(PIN_CLK);


  // Inisialisasi Setup RDA5807

  rx.setup();

  rx.setVolume(currentVolume);

  rx.setBand(2); // 0 = Band FM World (87.5-108 MHz) atau 2 = 76-108 MHz tergantung konfigurasi

  rx.setFrequency(currentFrequency);

  //rx.setMono(false); // Mode Awal Stereo

  rx.setSoftmute(false);

  rx.setRdsFifo(true);

  rx.setBass(true);

  rx.setNewDemodulateMethod(true);

  rx.setAFC(true);

  rx.setSeekThreshold(50);

  

  delay(3000);

  updateDisplay();

}


void loop() {

  handleEncoder();

  handleButton();

  

if (millis() - lastRssiUpdate >= RSSI_UPDATE_INTERVAL) {

    lastRssiUpdate = millis();

    updateDisplay(); 

  }

}


// Handler Pembacaan Diputar Encoder

void handleEncoder() {

  int currentClkState = digitalRead(PIN_CLK);


  if (currentClkState != lastClkState && currentClkState == LOW) {

    if (digitalRead(PIN_DT) != currentClkState) {

      // Putar Kanan (Clockwise)

      processEncoderTurn(1);

    } else {

      // Putar Kiri (Counter-Clockwise)

      processEncoderTurn(-1);

    }

// updateDisplay();

  }

  lastClkState = currentClkState;

}


// Evaluasi Aksi Berdasarkan Menu Saat Ini

void processEncoderTurn(int direction) {

  switch (currentMenu) {

    case TUNING:

      if (direction > 0) {

        currentFrequency += stepFreq;

        if (currentFrequency > 10800) currentFrequency = 7600;

      } else {

        if (currentFrequency < 7600) currentFrequency = 10800;

        else currentFrequency -= stepFreq;

      }

      rx.setFrequency(currentFrequency);

  delay(30);

      break;


    case VOLUME:

      if (direction > 0) {

        if (currentVolume < 63) currentVolume++;

      } else {

        if (currentVolume > 0) currentVolume--;

      }

      rx.setVolume(currentVolume);

      break;


    case STEP_SELECT:

      // Toggle antara step 100 kHz (10) dan 50 kHz (5)

      stepFreq = (stepFreq == 10) ? 5 : 10;

      break;


    case AUDIO_MODE:

      isStereoMode = !isStereoMode;

      rx.setMono(!isStereoMode); // RDA5807 library: setMono(true) merubah ke MONO

      break;

  }

  updateDisplay();

}


// Handler Tombol Push Button Encoder

void handleButton() {

  if (digitalRead(PIN_SW) == LOW) {

    if (millis() - lastDebounceTime > 250) { // Simple Debounce

      // Pindah Mode Menu

      currentMenu = static_cast<ModeMenu>((currentMenu + 1) % 4);

      updateDisplay();

      lastDebounceTime = millis();

    }

  }

}


// Fungsi Khusus Menggambar Ikon Baterai Grafis + Persentase

void drawBatteryStatus(int x, int y) {

  int percent = getBatteryPercentage();


  // 1. Gambar Batang/Garis Luar Baterai (Panjang 12px, Tinggi 7px)

  display.drawRect(x, y, 12, 7, SSD1306_WHITE);

  // Gambar Kutub Positif kecil di sebelah kanan ikon

  display.drawFastVLine(x + 12, y + 2, 3, SSD1306_WHITE);


  // 2. Gambar Isi Baterai (Diisi berdasarkan persentase 0 - 100%)

  // Panjang isi maksimal adalah 8 piksel (di dalam garis luar)

  int fillWidth = map(percent, 0, 100, 0, 8);

  if (fillWidth > 0) {

    display.fillRect(x + 2, y + 2, fillWidth, 3, SSD1306_WHITE);

  }


  // 3. Tampilkan Angka Persentase di Sebelah Kanan Ikon Baterai

  display.setCursor(x + 16, y);

  display.print(percent);

  display.print(F("%"));

}



// Fungsi Utama Menggambar Tampilan Antarmuka

void updateDisplay() {

  display.clearDisplay();


  // --- 1. BARIS ATAS: STATUS STEREO/MONO & SIGNAL ---

  //display.setTextSize(1);

  //display.fillRect(0, 0, 24, 8, SSD1306_BLACK);

  //display.setCursor(0, 0);

display.setCursor(0, 36);

if (isStereoMode) {

  // Jika tombol diset ke Stereo, periksa apakah sinyal antena asli mendukung Stereo

  if (rx.isStereo()) {

    display.print(F("ST"));      // Sinyal bagus & suara Stereo

  } else {

    display.print(F("MO"));      // Sinyal lemah, otomatis jadi Mono

  }

} else {

  // Jika tombol diset ke Mono, suara pasti Mono karena dipaksa

  display.print(F("MONO")); 

}


  // Tampilkan Informasi Menu Aktif Saat Ini

  //display.fillRect(42, 0, 24, 8, SSD1306_BLACK);

  //display.setCursor(42, 0);

  display.setCursor(42, 36);

  switch (currentMenu) {

    case TUNING:      display.print(F("[TUNE]")); break;

    case VOLUME:      display.print(F("[VOL]")); break;

    case STEP_SELECT: display.print(F("[STEP]")); break;

    case AUDIO_MODE:  display.print(F("[MODE]")); break;

  }


  // Sinyal RSSI (0 - 63 dari RDA5807)

  /*char rssi[12];

  rx.convertToChar(rx.getRssi(), rssi, 3, 0, '.');

  strcat(rssi, "dB");

  //display.fillRect(98, 0, 18, 8, SSD1306_BLACK);

  //display.setCursor(98, 0);

  display.setCursor(98, 36);

  display.print(rssi);*/

  int nilaiRssi = rx.getRssi(); 

  display.setCursor(98, 36);

  display.print("      ");

  display.setCursor(98, 36);

  display.print(nilaiRssi);

  display.print("dB");

  

  // --- 2. BARIS TENGAH: FREKUENSI (3 DIGIT KOMA 2 DIGIT) ---

  float freqMHz = currentFrequency / 100.0;

  char freqStr[10];

  sprintf(freqStr, "%06.2f", freqMHz);

  //display.fillRect(5, 18, 72, 16, SSD1306_BLACK);

  display.setTextSize(2);

  display.setCursor(5, 18);

  display.print(freqStr);

  display.setCursor(85, 18);

  display.print(F("MHz"));


  // Sub-informasi volume

  display.setTextSize(1);

  //display.fillRect(0, 36, 32, 8, SSD1306_BLACK);

  //display.setCursor(0, 36);

  display.setCursor(0, 0);

  

  display.print(F("Vol:"));

  display.print(currentVolume);

  

  // Sub-info baterai

  drawBatteryStatus(40, 0);

  

  // Sub-informasi Step

  //display.fillRect(80, 36, 36, 8, SSD1306_BLACK);

  //display.setCursor(75, 36);

  display.setCursor(80, 0);

  

  display.print(F("Step:"));

  display.print(stepFreq == 10 ? F("100") : F("50"));


  // --- 3. BARIS BAWAH: SKALA JARUM VINTAGE ---

  drawVintageDial(currentFrequency);


// --- TAMPILAN PERSEN BATERAI ---

  display.setTextSize(1);

  display.setCursor(55, 0); // Atur koordinat X, Y sesuai area kosong di OLED

  display.print(getBatteryPercentage());

  display.print(F("%"));

  

  display.display();

}


/* Fungsi Gambar Jarum Analog Radio Vintage

void drawVintageDial(uint16_t freq) {

  int dialY = 52;

  int dialHeight = 10;

  

  // Border Skala Radio

  display.drawRect(0, dialY, 128, dialHeight, SSD1306_WHITE);


  // Menggambar garis-garis tick mark (skala)

  for (int x = 4; x < 124; x += 8) {

    display.drawLine(x, dialY, x, dialY + 3, SSD1306_WHITE);

  }


  // Kalkulasi Posisi Jarum Penunjuk (Mapping rentang 76.00-108.00 MHz ke koordinat pixel 2-125)

  int needleX = map(freq, 8700, 10800, 2, 125);

  needleX = constrain(needleX, 2, 125);


  // Gambar Jarum Penunjuk

  display.drawLine(needleX, dialY - 2, needleX, dialY + dialHeight + 1, SSD1306_WHITE);

  display.drawLine(needleX - 1, dialY - 2, needleX + 1, dialY - 2, SSD1306_WHITE); // Segitiga atas jarum

} */


void drawVintageDial(uint16_t freq) {

  int dialY = 52;

  int dialHeight = 10;

  

  // Menggambar TITIK skala

  for (int x = 4; x <= 124; x += 8) {

    display.fillRect(x, dialY + 4, 2, 2, SSD1306_WHITE);

  }


  // Kalkulasi Posisi Jarum Penunjuk

  int needleX = map(freq, 7600, 10800, 2, 125);

  needleX = constrain(needleX, 2, 125);


  // Gambar Jarum Penunjuk

  display.drawLine(needleX, dialY + 1, needleX, dialY + dialHeight + 1, SSD1306_WHITE);

  display.drawPixel(needleX, dialY - 3, SSD1306_WHITE);      

  display.drawLine(needleX - 1, dialY - 2, needleX + 1, dialY - 2, SSD1306_WHITE); 

  display.drawPixel(needleX, dialY - 1, SSD1306_WHITE);      

}


menampilkan : volume suara, baterai indikator, step frekuensi (50/100), Mono/Stereo Mode, kekuatan sinyal RSSI. jarum skala

fitur : deteksi oled, deteksi chipset RDA5807, logo startup, frekuensi 76 - 108 MHz; bisa nguping orang nge brik kalau antena dan lokasi mendukung; pilih mode stereo atau force mono atau otomatis mengikuti kekuatan sinyal.

- kontrol sepenuhnya menggunakan 1 rotary encoder. rencana penambahan sensor sentuh untuk kontrol mono/stereo dan volume, itupun klo ingat

- RDA5807FP wajib menggunakan resistor pullup 10K untuk jalur SDA dan SCL

- indikator baterai menggunakan resistor pembagi masing2 10K ke PIN_BATTERY 34 (pakai baterai 3,7v bekas laptop)

- library menggunakan pu2clr

foto penampakan menyusul klo ingat hehe

My Son