diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX.sln b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX.sln new file mode 100644 index 00000000..58467673 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34408.163 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ATS_EX", "ATS_EX\ATS_EX.vcxproj", "{FABF0E8C-D02A-47FE-A9AB-573EA614A543}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|ARM.ActiveCfg = Release|ARM + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|ARM.Build.0 = Release|ARM + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|ARM.Deploy.0 = Release|ARM + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|ARM64.ActiveCfg = Release|ARM64 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|ARM64.Build.0 = Release|ARM64 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|ARM64.Deploy.0 = Release|ARM64 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|x64.ActiveCfg = Release|x64 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|x64.Build.0 = Release|x64 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|x64.Deploy.0 = Release|x64 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|x86.ActiveCfg = Release|x86 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|x86.Build.0 = Release|x86 + {FABF0E8C-D02A-47FE-A9AB-573EA614A543}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4C9B9C5E-F203-4BE8-9311-FF780FC99FAF} + EndGlobalSection +EndGlobal diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.ino b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.ino new file mode 100755 index 00000000..8ccaa2e3 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.ino @@ -0,0 +1,1901 @@ +// This sketch is a modified version of Mr. Goshante's sketch, which in +// turn is based on a sketch I developed and made excellent interface +// improvements. The changes made in this sketch are mainly enhancements +// to the FM RDS functions. I would like to thank Mr. for the excellent +// work done. +// Ricardo / PU2CLR 09.2024 +// The original comment about this sketch can be seeing below. +// ---------------------------------------------------------------------- +// ATS_EX (Extended) Firmware for ATS-20 and ATS-20+ receivers. +// Based on PU2CLR sources. +// Inspired by closed-source swling.ru firmware. +// For more information check README file in my github repository: +// http://github.com/goshante/ats20_ats_ex +// ---------------------------------------------------------------------- +// By Goshante +// 02.2024 +// http://github.com/goshante +// ---------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "font14x24sevenSeg.h" +#include "Rotary.h" +#include "SimpleButton.h" +#include "patch_ssb_compressed.h" + +#include "defs.h" +#include "globals.h" +#include "Utils.h" + +void showStatus(bool cleanFreq = false); +void applyBandConfiguration(bool extraSSBReset = false); + +bool isSSB() +{ + return g_currentMode > AM && g_currentMode < FM; +} + +int getSteps() +{ + if (isSSB()) + { + if (g_stepIndex >= g_amTotalSteps) + return g_tabStep[g_stepIndex]; + + return g_tabStep[g_stepIndex] * 1000; + } + + if (g_stepIndex >= g_amTotalSteps) + g_stepIndex = 0; + + return g_tabStep[g_stepIndex]; +} + +int getLastStep() +{ + if (isSSB()) + return g_amTotalSteps + g_ssbTotalSteps - 1; + + return g_amTotalSteps - 1; +} + +// -------------------------- +// ------- Main logic ------- +// -------------------------- + +#define APP_VERSION 118 + +//Initialize controller +void setup() +{ + //We need to save more space with this + DDRB |= (1 << DDB5); //13 pin + DDRD &= ~(1 << ENCODER_PIN_A); + PORTD |= (1 << ENCODER_PIN_A); + DDRD &= ~(1 << ENCODER_PIN_B); + PORTD |= (1 << ENCODER_PIN_B); + g_voltagePinConnnected = analogRead(BATTERY_VOLTAGE_PIN) > 300; + + oled.begin(128, 64, sizeof(tiny4koled_init_128x64br), tiny4koled_init_128x64br); + oled.clear(); + oled.on(); + oled.setFont(DEFAULT_FONT); + + //Don't use digitalRead() + //Registers save us more space + if (!(PINC & (1 << (ENCODER_BUTTON - 14)))) + { + saveAllReceiverInformation(); + oled.print(" EEPROM RESET"); + oled.setCursor(0, 2); + for (uint8_t i = 0; i < 16; i++) + { + oled.print("-"); //Just fancy animation + delay(60); + } + } + else + { + oledPrint(" ATS-20 RECEIVER", 0, 0, DEFAULT_FONT, true); + oledPrint("ATS_EX v1.18", 16, 2); + oledPrint("Goshante 2024", 12, 4); + oledPrint("Best firmware", 12, 6); + delay(2000); + } + oled.clear(); + + //Encoder interrupts + attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE); + attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE); + + g_si4735.getDeviceI2CAddress(RESET_PIN); + g_si4735.setup(RESET_PIN, MW_BAND_TYPE); + + delay(500); + + //Load settings from EEPROM + if (EEPROM.read(EEPROM_VERSION_ADDRESS) == APP_VERSION && EEPROM.read(EEPROM_APP_ID_ADDRESS) == EEPROM_APP_ID) + readAllReceiverInformation(); + else + saveAllReceiverInformation(); + + //Clock speed configuration + noInterrupts(); //cli() + CLKPR = 0x80; //Allow edit CLKPR register + CLKPR = g_Settings[SettingsIndex::CPUSpeed].param; + interrupts(); //sei() + + //Initialize current band settings and read frequency + applyBandConfiguration(); + g_currentFrequency = g_previousFrequency = g_si4735.getFrequency(); + g_si4735.setVolume(g_volume); + + //Draw main screen + oled.clear(); + showStatus(); +} + +uint8_t volumeEvent(uint8_t event, uint8_t pin) +{ + if (g_muteVolume) + { + if (!BUTTONEVENT_ISDONE(event)) + { + if ((BUTTONEVENT_SHORTPRESS != event) || (VOLUME_BUTTON == pin)) + doVolume(1); + } + } + + if (!g_muteVolume) + { +#if (0 != VOLUME_DELAY) +#if (VOLUME_DELAY > 1) + static uint8_t count; + if (BUTTONEVENT_FIRSTLONGPRESS == event) + { + count = 0; + } +#endif + if (BUTTONEVENT_ISLONGPRESS(event)) + if (BUTTONEVENT_LONGPRESSDONE != event) + { +#if (VOLUME_DELAY > 1) + if (count++ == 0) +#endif + doVolume(VOLUME_BUTTON == pin ? 1 : -1); +#if (VOLUME_DELAY > 1) + count = count % VOLUME_DELAY; +#endif + } +#else + if (BUTTONEVENT_FIRSTLONGPRESS == event) + event = BUTTONEVENT_SHORTPRESS; +#endif + } + return event; +} + +uint8_t simpleEvent(uint8_t event, uint8_t pin) +{ + if (BUTTONEVENT_FIRSTLONGPRESS == event) + event = BUTTONEVENT_SHORTPRESS; + return event; +} + +//This looks like it's better to remove them and use only simpleEvent +//But it's a part of a hack that allows us to save more flash image size +uint8_t stepEvent(uint8_t event, uint8_t pin) +{ + return simpleEvent(event, pin); +} + +uint8_t agcEvent(uint8_t event, uint8_t pin) +{ + return simpleEvent(event, pin); +} + +uint8_t bandEvent(uint8_t event, uint8_t pin) +{ +#if (0 != BAND_DELAY) + static uint8_t count; + if (BUTTONEVENT_ISLONGPRESS(event) && !g_settingsActive) + { + if (BUTTONEVENT_LONGPRESSDONE != event) + { + if (BUTTONEVENT_FIRSTLONGPRESS == event) + { + count = 0; + } + if (count++ == 0) + { + if (BAND_BUTTON == pin) + { + if (g_bandIndex < g_lastBand) + bandSwitch(true); + } + else + { + if (g_bandIndex) + bandSwitch(false); + } + } + count = count % BAND_DELAY; + } + } +#else + if (BUTTONEVENT_FIRSTLONGPRESS == event) + event = BUTTONEVENT_SHORTPRESS; +#endif + return event; +} + +// Handle encoder direction +void rotaryEncoder() +{ + uint8_t encoderStatus = g_encoder.process(); + if (encoderStatus) + { + g_encoderCount = (encoderStatus == DIR_CW) ? 1 : -1; + g_seekStop = true; + } +} + +//Saves more flash image size +void updateSSBCutoffFilter() +{ + // Auto mode: If SSB bandwidth 2 KHz or lower - it's better to enable cutoff filter + if (g_Settings[SettingsIndex::CutoffFilter].param == 0 || g_currentMode == CW) + g_si4735.setSSBSidebandCutoffFilter((g_bandwidthSSB[g_bwIndexSSB].idx == 0 || g_bandwidthSSB[g_bwIndexSSB].idx == 4 || g_bandwidthSSB[g_bwIndexSSB].idx == 5) ? 0 : 1); + else + g_si4735.setSSBSidebandCutoffFilter(g_Settings[SettingsIndex::CutoffFilter].param - 1); +} + +//EEPROM Save +void saveAllReceiverInformation() +{ + uint8_t addr = EEPROM_DATA_START_ADDRESS; + EEPROM.update(EEPROM_VERSION_ADDRESS, APP_VERSION); + EEPROM.update(EEPROM_APP_ID_ADDRESS, EEPROM_APP_ID); + + EEPROM.update(addr++, g_muteVolume > 0 ? g_muteVolume : g_si4735.getVolume()); + EEPROM.update(addr++, g_bandIndex); + EEPROM.update(addr++, g_currentMode); + EEPROM.update(addr++, g_currentBFO >> 8); + EEPROM.update(addr++, g_currentBFO & 0XFF); + EEPROM.update(addr++, g_FMStepIndex); + EEPROM.update(addr++, g_prevMode); + EEPROM.update(addr++, g_bwIndexSSB); + + for (uint8_t i = 0; i <= g_lastBand; i++) + { + EEPROM.update(addr++, (g_bandList[i].currentFreq >> 8)); + EEPROM.update(addr++, (g_bandList[i].currentFreq & 0xFF)); + EEPROM.update(addr++, ((i != FM_BAND_TYPE && g_bandList[i].currentStepIdx >= g_amTotalSteps) ? 0 : g_bandList[i].currentStepIdx)); + EEPROM.update(addr++, g_bandList[i].bandwidthIdx); + } + + for (uint8_t i = 0; i < SettingsIndex::SETTINGS_MAX; i++) + EEPROM.update(addr++, g_Settings[i].param); +} + +//EEPROM Load +void readAllReceiverInformation() +{ + uint8_t addr = EEPROM_DATA_START_ADDRESS; + int8_t bwIdx; + g_volume = EEPROM.read(addr++); + g_bandIndex = EEPROM.read(addr++); + g_currentMode = EEPROM.read(addr++); + g_currentBFO = (EEPROM.read(addr++) << 8) | EEPROM.read(addr++); + g_FMStepIndex = EEPROM.read(addr++); + g_prevMode = EEPROM.read(addr++); + g_bwIndexSSB = EEPROM.read(addr++); + + for (uint8_t i = 0; i <= g_lastBand; i++) + { + g_bandList[i].currentFreq = (EEPROM.read(addr++) << 8) | EEPROM.read(addr++); + g_bandList[i].currentStepIdx = EEPROM.read(addr++); + g_bandList[i].bandwidthIdx = EEPROM.read(addr++); + } + + for (uint8_t i = 0; i < SettingsIndex::SETTINGS_MAX; i++) + g_Settings[i].param = EEPROM.read(addr++); + + oled.setContrast(uint8_t(g_Settings[SettingsIndex::Brightness].param) * 2); + + g_previousFrequency = g_currentFrequency = g_bandList[g_bandIndex].currentFreq; + if (g_bandIndex == FM_BAND_TYPE) + g_FMStepIndex = g_bandList[g_bandIndex].currentStepIdx; + else + g_stepIndex = g_bandList[g_bandIndex].currentStepIdx; + bwIdx = g_bandList[g_bandIndex].bandwidthIdx; + if (g_stepIndex >= g_amTotalSteps) + g_stepIndex = 0; + + if (isSSB()) + { + loadSSBPatch(); + g_si4735.setSSBAudioBandwidth(g_bandwidthSSB[g_bwIndexSSB].idx); + updateSSBCutoffFilter(); + } + else if (g_currentMode == AM) + { + g_bwIndexAM = bwIdx; + g_si4735.setBandwidth(g_bandwidthAM[g_bwIndexAM].idx, 1); + } + else + { + g_bwIndexFM = bwIdx; + g_si4735.setFmBandwidth(g_bwIndexFM); + } + + applyBandConfiguration(); +} + +//For saving features +void resetEepromDelay() +{ + g_storeTime = millis(); + g_previousFrequency = 0; +} + +//Draw frequency. +//BFO and main frequency produce actual frequency that is displayed on LCD +//Too sensitive logic, do not change +void showFrequency(bool cleanDisplay = false) +{ + if (g_settingsActive) + return; + + char unit[4]; + char freqDisplay[7]; + char ssbSuffix[4]; + static uint8_t prevLen = 0; + uint16_t khzBFO, tailBFO; + uint8_t off = (isSSB() ? -5 : 4) + 8; + + unit[0] = 'K'; + unit[1] = 'H'; + unit[2] = 'z'; + unit[3] = 0x0; + + ssbSuffix[0] = '.'; + ssbSuffix[1] = '0'; + ssbSuffix[2] = '0'; + ssbSuffix[3] = '\0'; + + if (g_bandIndex == FM_BAND_TYPE) + { + convertToChar(freqDisplay, g_currentFrequency, 5, 3, '.', '/'); + unit[0] = 'M'; + } + else + { + if (g_bandIndex == SW_BAND_TYPE) + showBandTag(); + + if (!isSSB()) + { + bool swMhz = g_Settings[SettingsIndex::SWUnits].param == 1; + convertToChar(freqDisplay, g_currentFrequency, 5, (g_bandIndex == SW_BAND_TYPE && swMhz) ? 2 : 0, '.', '/'); + if (g_bandIndex == SW_BAND_TYPE && swMhz) + unit[0] = 'M'; + } + else + { + splitFreq(khzBFO, tailBFO); + //utoa(freqDisplay, khzBFO); + convertToChar(freqDisplay, khzBFO, ilen(khzBFO)); + } + } + + uint8_t len = isSSB() ? ilen(khzBFO) : ilen(g_currentFrequency); + if (cleanDisplay) + { + oled.setCursor(0, 3); + oledPrint("/////////", 0, 3, FONT14X24SEVENSEG); // This character is an empty space in my seven seg font. + } + else if (isSSB() && len > prevLen && len == 5) + oledPrint(" ", 102, 4, DEFAULT_FONT); + + oledPrint(freqDisplay, off, 3, FONT14X24SEVENSEG); + + if (isSSB()) + { + //utoa((ilen(tailBFO) == 1) ? &ssbSuffix[2] : &ssbSuffix[1], tailBFO); + convertToChar((ilen(tailBFO) == 1) ? &ssbSuffix[2] : &ssbSuffix[1], tailBFO, ilen(tailBFO)); + ssbSuffix[3] = 0; + oledPrint(ssbSuffix); + if (len != prevLen && len < prevLen) + oledPrint("/"); + } + + if (g_Settings[SettingsIndex::UnitsSwitch].param == 1 && (!isSSB() || isSSB() && len < 5)) + oledPrint(unit, 102, 4, DEFAULT_FONT); + + prevLen = len; +} + +//This function is called by station seek logic +void showFrequencySeek(uint16_t freq) +{ + g_currentFrequency = freq; + delay(10); + if (g_currentMode == FM) + { + //Fix random 10th KHz fraction + freq = (freq / 10) * 10; + g_currentFrequency = freq; + g_si4735.setFrequency(g_currentFrequency); + } + else + g_currentFrequency = g_si4735.getFrequency(); + + g_bandList[g_bandIndex].currentFreq = g_currentFrequency; + showFrequency(); +} + +bool checkStopSeeking() +{ + return g_seekStop || !(PINC & (1 << (ENCODER_BUTTON - 14))); +} + +void doSeek() +{ + if (g_seekDirection) + g_si4735.frequencyUp(); + else + g_si4735.frequencyDown(); + +#if USE_RDS + if (g_displayRDS) + oledPrint(_literal_EmptyLine, 0, 6, DEFAULT_FONT); +#endif + g_seekStop = false; + g_si4735.seekStationProgress(showFrequencySeek, checkStopSeeking, g_seekDirection); +} + +//Update and draw main screen UI. +//basicUpdate - update minimum as possible +//cleanFreq - force clean frequency line +void showStatus(bool cleanFreq) +{ + showFrequency(cleanFreq); + showModulation(); + showStep(); + showBandwidth(); + showCharge(true); + showVolume(); +} + +void updateLowerDisplayLine() +{ + oledPrint(_literal_EmptyLine, 0, 6, DEFAULT_FONT); + showModulation(); + showStep(); + showCharge(true); +} + +//Converts settings value to UI value +void SettingParamToUI(char* buf, uint8_t idx) +{ + int8_t param = g_Settings[idx].param; + switch (g_Settings[idx].type) + { + case SettingType::ZeroAuto: + if (param == 0) + { + buf[0] = 'A'; + buf[1] = 'U'; + buf[2] = 'T'; + buf[3] = 0x0; + } + else + convertToChar(buf, param, 3); + + break; + + case SettingType::Num: + convertToChar(buf, abs(param), 3); + if (param < 0) + buf[0] = '-'; + break; + + case SettingType::SwitchAuto: + if (param == 0) + { + buf[0] = 'A'; + buf[1] = 'U'; + buf[2] = 'T'; } + else if (param == 1) + { + buf[0] = 'O'; + buf[1] = 'n'; + buf[2] = ' '; + } + else + { + buf[0] = 'O'; + buf[1] = 'f'; + buf[2] = 'f'; + } + buf[3] = 0x0; + break; + + case SettingType::Switch: + if (idx == SettingsIndex::DeEmp) + { + if (param == 0) + { + buf[0] = '5'; + buf[1] = '0'; + buf[2] = 'u'; + } + else + { + buf[0] = '7'; + buf[1] = '5'; + buf[2] = 'u'; + } + } + else if (idx == SettingsIndex::SWUnits) + { + if (param == 0) + buf[0] = 'K'; + else + buf[0] = 'M'; + buf[1] = 'H'; + buf[2] = 'z'; + } + else if (idx == SettingsIndex::SSM) + { + if (param == 0) + { + buf[0] = 'R'; + buf[1] = 'S'; + buf[2] = 'S'; + } + else + { + buf[0] = 'S'; + buf[1] = 'N'; + buf[2] = 'R'; + } + } + else if (idx == SettingsIndex::CWSwitch) + { + if (param == 0) + buf[0] = 'L'; + else + buf[0] = 'U'; + + buf[1] = 'S'; + buf[2] = 'B'; + } + else if (idx == SettingsIndex::CPUSpeed) + { + if (param == 0) + { + buf[0] = '1'; + buf[1] = '0'; + buf[2] = '0'; + } + else + { + buf[0] = '5'; + buf[1] = '0'; + buf[2] = '%'; + } + } + else + { + if (param == 0) + { + buf[0] = 'O'; + buf[1] = 'f'; + buf[2] = 'f'; + } + else + { + buf[0] = 'O'; + buf[1] = 'n'; + buf[2] = ' '; + } + } + buf[3] = 0x0; + break; + } +} + +// If full false - update only value +void DrawSetting(uint8_t idx, bool full) +{ + if (!g_settingsActive) + return; + + char buf[5]; + uint8_t place = idx - ((g_SettingsPage - 1) * 6); + uint8_t yOffset = place > 2 ? (place - 3) * 2 : place * 2; + uint8_t xOffset = place > 2 ? 60 : 0; + if (full) + oledPrint(g_Settings[idx].name, 5 + xOffset, 2 + yOffset, DEFAULT_FONT, idx == g_SettingSelected && !g_SettingEditing); + SettingParamToUI(buf, idx); + oledPrint(buf, 35 + xOffset, 2 + yOffset, DEFAULT_FONT, idx == g_SettingSelected && g_SettingEditing); +} + +//Update and draw settings UI +void showSettings() +{ + for (uint8_t i = 0; i < 6 && i + ((g_SettingsPage - 1) * 6) < SettingsIndex::SETTINGS_MAX; i++) + DrawSetting(i + ((g_SettingsPage - 1) * 6), true); +} + +void showSettingsTitle() +{ + oledPrint(" SETTINGS ", 0, 0, DEFAULT_FONT, true); + oled.invertOutput(true); + oled.print(uint8_t(g_SettingsPage)); + oled.print("/"); + oled.print(uint8_t(g_SettingsMaxPages)); + oled.invertOutput(false); +} + +void switchSettingsPage() +{ + g_SettingsPage++; + g_SettingsPage = (g_SettingsPage > g_SettingsMaxPages) ? 1 : g_SettingsPage; + g_SettingSelected = 6 * (g_SettingsPage - 1); + g_SettingEditing = false; + oled.clear(); + showSettingsTitle(); + showSettings(); +} + +//Switch between main screen and settings mode +void switchSettings() +{ + oled.clear(); + if (g_settingsActive) + { + g_SettingsPage = 1; + showSettingsTitle(); + g_SettingSelected = 0; + g_SettingEditing = false; + showSettings(); + } + else + { + saveAllReceiverInformation(); + showStatus(); + } +} + +//Draw curremt modulation +void showModulation() +{ + oledPrint(g_bandModeDesc[g_currentMode], 0, 0, DEFAULT_FONT, g_cmdBand && g_currentMode == FM); + oled.print(" "); + if (isSSB() && g_Settings[SettingsIndex::Sync].param == 1) + oledPrint("S", -1, -1, LastFont, true); + else + oled.print(" "); + + showBandTag(); +} + +//Draw current band +void showBandTag() +{ + if (g_sMeterOn || g_displayRDS) + return; + + oledPrint((g_currentFrequency >= CB_LIMIT_LOW && g_currentFrequency < CB_LIMIT_HIGH)? "CB" : bandTags[g_bandIndex], 0, 6, DEFAULT_FONT, g_cmdBand && g_currentMode != FM); +} + +//Draw volume level +void showVolume() +{ + if (g_settingsActive) + return; + + char buf[3]; + if (g_muteVolume == 0) + convertToChar(buf, g_si4735.getCurrentVolume(), 2, 0, 0); + else + { + buf[0] = ' '; + buf[1] = 'M'; + buf[2] = 0; + } + + oledPrint(buf, (128 - (8 * 2) + 2 - 6), 0, DEFAULT_FONT, g_cmdVolume); +} + +//Draw battery charge +//This feature requires hardware mod +//Voltage divider made of two 10 KOhm resistors between + and GND of Li-Ion battery +//Solder it to A2 analog pin +void showCharge(bool forceShow) +{ + if (!g_voltagePinConnnected) + return; + + // mV, Percent + //This values represent voltage values in ATMega328p analog units with reference voltage 3.30v + //Voltage pin reads voltage from voltage divider, so it have to be 1/2 of Li-Ion battery voltage + constexpr const uint8_t rows = 10; + const uint16_t dischargeTable[rows][2] = + { + { 643, 100 }, //4.15v + { 620, 95 }, //4.05v + { 604, 90 }, //3.90v + { 581, 80 }, //3.75v + { 573, 60 }, //3.70v + { 558, 40 }, //3.60v + { 542, 20 }, //3.50v + { 503, 15 }, //3.25v + { 496, 5 }, //3.20v + { 488, 0 }, //3.15v + }; + + auto getBatteryPercentage = [&](uint16_t currentSamples) -> uint8_t + { + if (currentSamples >= dischargeTable[0][0]) + return 100; + + if (currentSamples <= dischargeTable[rows - 1][0]) + return 0; + + for (uint8_t i = 0; i < rows - 1; ++i) + { + if (currentSamples >= dischargeTable[i + 1][0] && currentSamples <= dischargeTable[i][0]) + { + uint16_t voltageDiff = dischargeTable[i][0] - dischargeTable[i + 1][0]; + uint16_t percentageDiff = dischargeTable[i][1] - dischargeTable[i + 1][1]; + uint16_t voltageOffset = currentSamples - dischargeTable[i + 1][0]; + return dischargeTable[i + 1][1] + (percentageDiff * voltageOffset + voltageDiff / 2) / voltageDiff; + } + } + + return 0; + }; + + static uint32_t lastChargeShow = 0; + static int16_t averageSamples = 0; + + int sample = analogRead(BATTERY_VOLTAGE_PIN); + if (sample < 0) + sample = averageSamples; + + if ((millis() - lastChargeShow) > 10000 || forceShow) + { + char buf[4]; + buf[3] = 0; + int16_t percents = getBatteryPercentage(averageSamples); + + uint8_t il = ilen(percents) < 3 ? 2 : 3; + convertToChar(buf, percents, il); + + if (il < 3) + buf[2] = '%'; + + if (!g_settingsActive && !g_sMeterOn && !g_displayRDS) + oledPrint(buf, 102, 6, DEFAULT_FONT); + lastChargeShow = millis(); + averageSamples = sample; + } + + averageSamples = (averageSamples + sample) / 2; +} + +#if USE_RDS +void showRDS() +{ + static uint16_t lastUpdatedFreq = 0; + static uint32_t lastUpdatedTime = millis(); + static bool succeed = false; + + if (g_currentMode != FM || !g_displayRDS || g_settingsActive) + { + lastUpdatedFreq = 0; + g_rdsPrevLen = 0; + succeed = false; + g_rdsActiveInfo = 0; + return; + } + + if (millis() - lastUpdatedTime > 300) + succeed = false; + + if (lastUpdatedFreq != g_currentFrequency || g_rdsSwitchPressed) + { + if (g_rdsSwitchPressed) + { + g_rdsActiveInfo++; + if (g_rdsActiveInfo > RDSActiveInfo::ProgramInfo) + g_rdsActiveInfo = RDSActiveInfo::StationName; + } + else + { + g_rdsActiveInfo = RDSActiveInfo::StationName; + succeed = false; + } + g_rdsPrevLen = 0; + oledPrint(_literal_EmptyLine, 0, 6, DEFAULT_FONT); + } + lastUpdatedFreq = g_currentFrequency; + + if (!succeed) + g_si4735.getRdsStatus(); + + if (!succeed && g_si4735.getRdsReceived() && g_si4735.getRdsSync() && g_si4735.getNumRdsFifoUsed() > 1) + { + g_RDSCells[RDSActiveInfo::StationName] = g_si4735.getRdsStationName(); + g_RDSCells[RDSActiveInfo::StationInfo] = g_si4735.getRdsStationInformation(); + g_RDSCells[RDSActiveInfo::ProgramInfo] = g_si4735.getRdsProgramInformation(); + g_RDSCells[RDSActiveInfo::StationInfo][17] = '\0'; + g_RDSCells[RDSActiveInfo::ProgramInfo][17] = '\0'; + succeed = true; + lastUpdatedTime = millis(); + } + else if (!g_rdsSwitchPressed && succeed) + return; + + uint8_t len = strlen8(g_RDSCells[g_rdsActiveInfo]); + + if (len == 0 && !g_rdsSwitchPressed) + return; + + oledPrint(g_RDSCells[g_rdsActiveInfo], 0, 6, DEFAULT_FONT); + + uint8_t toPrint = len == 0 ? 3 : (len < g_rdsPrevLen ? min(g_rdsPrevLen - len, 16 - len) : 0); + char printChar = len == 0 ? '.' : ' '; + for (uint8_t i = 0; i < toPrint; i++) + oled.print(printChar); + + g_rdsPrevLen = len; + g_rdsSwitchPressed = false; +} +#endif + +//Draw steps (with units) +void showStep() +{ + if (g_sMeterOn || g_displayRDS) + return; + + char buf[5]; + if (g_currentMode == FM) + { + if (g_tabStepFM[g_FMStepIndex] == 100) + { + buf[0] = ' '; + buf[1] = ' '; + buf[2] = '1'; + buf[3] = 'M'; + buf[4] = 0x0; + } + else + { + convertToChar(buf, g_tabStepFM[g_FMStepIndex] * 10, 3); + buf[3] = 'k'; + buf[4] = '\0'; + } + } + else + { + if (g_tabStep[g_stepIndex] == 1000) + { + buf[0] = ' '; + buf[1] = ' '; + buf[2] = '1'; + buf[3] = 'M'; + buf[4] = 0x0; + } + else if (isSSB() && g_stepIndex >= g_amTotalSteps) + convertToChar(buf, g_tabStep[g_stepIndex], 4); + else + { + convertToChar(buf, g_tabStep[g_stepIndex], 3); + buf[3] = 'k'; + buf[4] = '\0'; + } + } + + uint8_t off = 50; + oledPrint("St:", off - 16, 6, DEFAULT_FONT, g_cmdStep); + oledPrint(buf, off + 8, 6, DEFAULT_FONT, g_cmdStep); +} + +void showSMeter() +{ + static uint32_t sMeterUpdated = 0; + if (millis() - sMeterUpdated < 100) + return; + + g_si4735.getCurrentReceivedSignalQuality(); + uint8_t rssi = g_si4735.getCurrentRSSI(); + rssi = rssi > 64 ? 64 : rssi; + + int sMeterValue = rssi / (64 / 16); + char buf[17]; + for (uint8_t i = 0; i < sizeof(buf) - 1; i++) + buf[i] = i < sMeterValue ? '|' : ' '; + buf[sizeof(buf) - 1] = 0x0; + + oledPrint(buf, 0, 6, DEFAULT_FONT); + sMeterUpdated = millis(); +} + +//Draw bandwidth (Ignored for CW mode) +void showBandwidth() +{ + char* bw; + if (isSSB()) + { + bw = (char*)g_bandwidthSSB[g_bwIndexSSB].desc; + if (g_currentMode == CW) + bw = " "; + } + else if (g_currentMode == AM) + { + bw = (char*)g_bandwidthAM[g_bwIndexAM].desc; + } + else + { + bw = (char*)g_bandwidthFM[g_bwIndexFM]; + } + + oledPrint(bw, 45, 0, DEFAULT_FONT, g_cmdBw); +} + +uint16_t getNextSWSuBband(bool up) +{ + uint16_t freq = g_currentFrequency; + if (isSSB()) + freq += g_currentBFO / 1000; + + for (uint8_t i = 0; i < g_SWSubBandCount; i++) + { + uint8_t n = g_SWSubBandCount - 1 - i; + if (!up && SWSubBands[n] < freq) + return SWSubBands[n]; + else if (up && SWSubBands[i] > freq) + return SWSubBands[i]; + } + + return 0; +} + +void bandSwitch(bool up) +{ + uint16_t nextSW = getNextSWSuBband(up); + + if (g_bandIndex == SW_BAND_TYPE && nextSW != 0) + { + g_currentFrequency = nextSW; + + g_currentBFO = 0; + if (isSSB()) + updateBFO(); + g_si4735.setFrequency(nextSW); + agcSetFunc(); //Re-apply to remove noize + showFrequency(); + showBandTag(); + } + else + { + if (g_currentMode == FM) + g_bandList[g_bandIndex].currentStepIdx = g_FMStepIndex; + else + g_bandList[g_bandIndex].currentStepIdx = g_stepIndex; + + if (up) + { + if (g_bandIndex < g_lastBand) + g_bandIndex++; + else + g_bandIndex = 0; + } + else + { + if (g_bandIndex > 0) + g_bandIndex--; + else + g_bandIndex = g_lastBand; + } + + if (g_sMeterOn) + { + g_sMeterOn = false; + oledPrint(_literal_EmptyLine, 0, 6, DEFAULT_FONT); + } + +#if USE_RDS + if (g_displayRDS && g_currentMode != FM) + { + g_displayRDS = false; + oledPrint(_literal_EmptyLine, 0, 6, DEFAULT_FONT); + } +#endif + + g_currentBFO = 0; + if (isSSB()) + updateBFO(); + applyBandConfiguration(); + } +} + +// This function is required for using SSB. Si473x controllers do not support SSB by-default. +// But we can patch internal RAM of Si473x with special patch to make it work in SSB mode. +// Patch must be applied every time we enable SSB after AM or FM. +void loadSSBPatch() +{ + // This works, but i am not sure it's safe + //g_si4735.setI2CFastModeCustom(700000); + g_si4735.setI2CFastModeCustom(500000); + g_si4735.queryLibraryId(); //Do we really need this? Research it. + g_si4735.patchPowerUp(); + delay(50); + g_si4735.downloadCompressedPatch(ssb_patch_content, sizeof(ssb_patch_content), cmd_0x15, sizeof(cmd_0x15)); + g_si4735.setSSBConfig(g_bandwidthSSB[g_bwIndexSSB].idx, 1, 0, 1, 0, 1); + g_si4735.setI2CStandardMode(); + g_ssbLoaded = true; + g_stepIndex = 0; +} + +#if USE_RDS +void setRDSConfig(uint8_t bias) +{ + g_si4735.setRdsConfig(1, bias, bias, bias, bias); +} +#endif + +//Update receiver settings after changing band and modulation +void applyBandConfiguration(bool extraSSBReset = false) +{ + g_si4735.setTuneFrequencyAntennaCapacitor(uint16_t(g_bandIndex == SW_BAND_TYPE)); + if (g_bandIndex == FM_BAND_TYPE) + { + g_currentMode = FM; + g_si4735.setFM(g_bandList[g_bandIndex].minimumFreq, + g_bandList[g_bandIndex].maximumFreq, + g_bandList[g_bandIndex].currentFreq, + g_tabStepFM[g_bandList[g_bandIndex].currentStepIdx]); + g_si4735.setSeekFmLimits(g_bandList[g_bandIndex].minimumFreq, g_bandList[g_bandIndex].maximumFreq); + g_si4735.setSeekFmSpacing(1); + g_ssbLoaded = false; +#if USE_RDS + setRDSConfig(g_Settings[SettingsIndex::RDSError].param); +#endif + g_si4735.setFifoCount(1); + g_bwIndexFM = g_bandList[g_bandIndex].bandwidthIdx; + g_si4735.setFmBandwidth(g_bwIndexFM); + g_si4735.setFMDeEmphasis(g_Settings[SettingsIndex::DeEmp].param == 0 ? 1 : 2); + } + else + { + uint16_t minFreq = g_bandList[g_bandIndex].minimumFreq; + uint16_t maxFreq = g_bandList[g_bandIndex].maximumFreq; + if (g_bandIndex == SW_BAND_TYPE) + { + minFreq = SW_LIMIT_LOW; + maxFreq = SW_LIMIT_HIGH; + } + + if (g_ssbLoaded) + { + g_currentBFO = 0; + if (extraSSBReset) + loadSSBPatch(); + + //Call this before to call crazy volume after AM when SVC is off + g_si4735.setSSBAutomaticVolumeControl(g_Settings[SettingsIndex::SVC].param); + g_si4735.setSSB(minFreq, + maxFreq, + g_bandList[g_bandIndex].currentFreq, + g_bandList[g_bandIndex].currentStepIdx >= g_amTotalSteps ? 0 : g_tabStep[g_bandList[g_bandIndex].currentStepIdx], + g_currentMode == CW ? g_Settings[SettingsIndex::CWSwitch].param + 1 : g_currentMode); + updateSSBCutoffFilter(); + g_si4735.setSSBAutomaticVolumeControl(g_Settings[SettingsIndex::SVC].param); + g_si4735.setSSBDspAfc(g_Settings[SettingsIndex::Sync].param == 1 ? 0 : 1); + g_si4735.setSSBAvcDivider(g_Settings[SettingsIndex::Sync].param == 0 ? 0 : 3); //Set Sync mode + g_si4735.setAmSoftMuteMaxAttenuation(g_Settings[SettingsIndex::SoftMute].param); + g_si4735.setSSBAudioBandwidth(g_currentMode == CW ? g_bandwidthSSB[0].idx : g_bandwidthSSB[g_bwIndexSSB].idx); + updateBFO(); + g_si4735.setSSBSoftMute(g_Settings[SettingsIndex::SSM].param); + } + else + { + g_currentMode = AM; + g_si4735.setAM(minFreq, + maxFreq, + g_bandList[g_bandIndex].currentFreq, + g_bandList[g_bandIndex].currentStepIdx >= g_amTotalSteps ? 0 : g_tabStep[g_bandList[g_bandIndex].currentStepIdx]); + g_si4735.setAmSoftMuteMaxAttenuation(g_Settings[SettingsIndex::SoftMute].param); + g_bwIndexAM = g_bandList[g_bandIndex].bandwidthIdx; + g_si4735.setBandwidth(g_bandwidthAM[g_bwIndexAM].idx, 1); + } + + agcSetFunc(); + g_si4735.setAvcAmMaxGain(g_Settings[SettingsIndex::AutoVolControl].param); + g_si4735.setSeekAmLimits(minFreq, maxFreq); + g_si4735.setSeekAmSpacing((g_bandList[g_bandIndex].currentStepIdx >= g_amTotalSteps) ? 1 : g_tabStep[g_bandList[g_bandIndex].currentStepIdx]); + } + + g_currentFrequency = g_bandList[g_bandIndex].currentFreq; + if (g_currentMode == FM) + g_FMStepIndex = g_bandList[g_bandIndex].currentStepIdx; + else + g_stepIndex = g_bandList[g_bandIndex].currentStepIdx; + + if ((g_bandIndex == LW_BAND_TYPE || g_bandIndex == MW_BAND_TYPE) + && g_stepIndex > g_amTotalStepsSSB) + g_stepIndex = g_amTotalStepsSSB; + + if (!g_settingsActive) + showStatus(true); + resetEepromDelay(); +} + +//Step value regulation +void doStep(int8_t v) +{ + if (g_currentMode == FM) + { + g_FMStepIndex = (v == 1) ? g_FMStepIndex + 1 : g_FMStepIndex - 1; + if (g_FMStepIndex > g_lastStepFM) + g_FMStepIndex = 0; + else if (g_FMStepIndex < 0) + g_FMStepIndex = g_lastStepFM; + + g_si4735.setFrequencyStep(g_tabStepFM[g_FMStepIndex]); + g_bandList[g_bandIndex].currentStepIdx = g_FMStepIndex; + g_si4735.setSeekFmSpacing(1); + showStep(); + } + else + { + g_stepIndex = (v == 1) ? g_stepIndex + 1 : g_stepIndex - 1; + if (g_stepIndex > getLastStep()) + g_stepIndex = 0; + else if (g_stepIndex < 0) + g_stepIndex = getLastStep(); + + //SSB Step limit + else if (isSSB() && g_stepIndex >= g_amTotalStepsSSB && g_stepIndex < g_amTotalSteps) + g_stepIndex = v == 1 ? g_amTotalSteps : g_amTotalStepsSSB - 1; + + //LW/MW Step limit + else if ((g_bandIndex == LW_BAND_TYPE || g_bandIndex == MW_BAND_TYPE) + && v == 1 && g_stepIndex > g_amTotalStepsSSB && g_stepIndex < g_amTotalSteps) + g_stepIndex = g_amTotalSteps; + else if ((g_bandIndex == LW_BAND_TYPE || g_bandIndex == MW_BAND_TYPE) + && v != 1 && g_stepIndex > g_amTotalStepsSSB && g_stepIndex < g_amTotalSteps) + g_stepIndex = g_amTotalStepsSSB; + + if (!isSSB() || isSSB() && g_stepIndex < g_amTotalSteps) + { + g_si4735.setFrequencyStep(g_tabStep[g_stepIndex]); + g_bandList[g_bandIndex].currentStepIdx = g_stepIndex; + } + + if (!isSSB()) + g_si4735.setSeekAmSpacing((g_bandList[g_bandIndex].currentStepIdx >= g_amTotalSteps) ? 1 : g_tabStep[g_bandList[g_bandIndex].currentStepIdx]); + showStep(); + } +} + +void updateBFO() +{ + //Actually to move frequency forward you need to move BFO backwards, so just * -1 + g_si4735.setSSBBfo((g_currentBFO + (g_Settings[SettingsIndex::BFO].param * 10)) * -1); +} + +//Volume control +void doVolume(int8_t v) +{ + if (g_muteVolume) + { + g_si4735.setVolume(g_muteVolume); + g_muteVolume = 0; + } + else + { + if (v == 1) + g_si4735.volumeUp(); + else + g_si4735.volumeDown(); + } + showVolume(); +} + +//Helps to save more flash image size +void doSwitchLogic(int8_t& param, int8_t low, int8_t high, int8_t step) +{ + param += step; + if (param < low) + param = high; + else if (param > high) + param = low; +} + +void agcSetFunc() +{ + uint8_t att = g_Settings[SettingsIndex::ATT].param; + uint8_t disableAgc = att > 0; + uint8_t agcNdx; + if (att > 1) + agcNdx = att - 1; + else + agcNdx = 0; + g_si4735.setAutomaticGainControl(disableAgc, agcNdx); +} + +//Settings: Attenuation +void doAttenuation(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::ATT].param, 0, 37, v); + agcSetFunc(); +} + +//Settings: Soft Mute +void doSoftMute(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::SoftMute].param, 0, 32, v); + + if (g_currentMode != FM) + g_si4735.setAmSoftMuteMaxAttenuation(g_Settings[SettingsIndex::SoftMute].param); +} + +//Settings: Brightness +void doBrightness(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::Brightness].param, 5, 125, v); + oled.setContrast(uint8_t(g_Settings[SettingsIndex::Brightness].param) * 2); +} + +//Settings: SSB AVC Switch +void doSSBAVC(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::SVC].param, 0, 1, v); + + if (isSSB()) + { + g_si4735.setSSBAutomaticVolumeControl(g_Settings[SettingsIndex::SVC].param); + applyBandConfiguration(true); + } +} + +//Settings: Automatic Volume Control +void doAvc(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::AutoVolControl].param, 12, 90, v); + + if (g_currentMode != FM) + g_si4735.setAvcAmMaxGain(g_Settings[SettingsIndex::AutoVolControl].param); +} + +//Settings: Sync switch +void doSync(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::Sync].param, 0, 1, v); + + if (isSSB()) + { + g_si4735.setSSBDspAfc(g_Settings[SettingsIndex::Sync].param == 1 ? 0 : 1); + g_si4735.setSSBAvcDivider(g_Settings[SettingsIndex::Sync].param == 0 ? 0 : 3); //Set Sync mode + applyBandConfiguration(true); + } +} + +//Settings: FM DeEmp switch (50 or 75) +void doDeEmp(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::DeEmp].param, 0, 1, v); + + if (g_currentMode == FM) + g_si4735.setFMDeEmphasis(g_Settings[SettingsIndex::DeEmp].param == 0 ? 1 : 2); +} + +//Settings: SW Units +void doSWUnits(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::SWUnits].param, 0, 1, v); +} + +//Settings: SW Units +void doSSBSoftMuteMode(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::SSM].param, 0, 1, v); + + if (isSSB()) + g_si4735.setSSBSoftMute(g_Settings[SettingsIndex::SSM].param); +} + +//Settings: SSB Cutoff filter +void doCutoffFilter(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::CutoffFilter].param, 0, 2, v); + + if (isSSB()) + updateSSBCutoffFilter(); +} + +//Settings: CPU Frequency divider +void doCPUSpeed(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::CPUSpeed].param, 0, 1, v); + + noInterrupts(); + CLKPR = 0x80; + CLKPR = g_Settings[SettingsIndex::CPUSpeed].param; + interrupts(); +} + +//Settings: BFO Offset calibration +void doBFOCalibration(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::BFO].param, -60, 60, v); + + if (isSSB()) + { +#if USE_RDS + setRDSConfig(g_Settings[SettingsIndex::BFO].param); +#endif + updateBFO(); + } +} + +//Settings: Tune Frequency Antenna Capacitor +void doUnitsSwitch(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::UnitsSwitch].param, 0, 1, v); +} + +//Settings: Scan button switch +void doScanSwitch(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::ScanSwitch].param, 0, 1, v); +} + +//Settings: CW mode switch +void doCWSwitch(int8_t v = 0) +{ + doSwitchLogic(g_Settings[SettingsIndex::CWSwitch].param, 0, 1, v); + + if (g_currentMode == CW) + applyBandConfiguration(true); +} + +#if USE_RDS +//Settings: RDS Error Level +void doRDSErrorLevel(int8_t v) +{ + doSwitchLogic(g_Settings[SettingsIndex::RDSError].param, 0, 3, v); + + if (g_currentMode == FM) + setRDSConfig(g_Settings[SettingsIndex::RDSError].param); +} + + +void doRDS() +{ + g_displayRDS = !g_displayRDS; + + if (g_displayRDS) + { + g_sMeterOn = false; + oledPrint(_literal_EmptyLine, 0, 6, DEFAULT_FONT); + g_si4735.getRdsStatus(); + showRDS(); + } + else + updateLowerDisplayLine(); +} +#endif + +//Prevents repeatable code for flash image size saving +void doBandwidthLogic(int8_t& bwIndex, uint8_t upperLimit, uint8_t v) +{ + doSwitchLogic(bwIndex, 0, upperLimit, v); + g_bandList[g_bandIndex].bandwidthIdx = bwIndex; +} + +//Bandwidth regulation logic +void doBandwidth(uint8_t v) +{ + if (isSSB()) + { + doSwitchLogic(g_bwIndexSSB, 0, g_bwSSBMaxIdx, v); + g_si4735.setSSBAudioBandwidth(g_bandwidthSSB[g_bwIndexSSB].idx); + updateSSBCutoffFilter(); + } + else if (g_currentMode == AM) + { + doBandwidthLogic(g_bwIndexAM, g_maxFilterAM, v); + g_bandList[g_bandIndex].bandwidthIdx = g_bwIndexAM; + g_si4735.setBandwidth(g_bandwidthAM[g_bwIndexAM].idx, 1); + } + else + { + doBandwidthLogic(g_bwIndexFM, 4, v); + g_bandList[g_bandIndex].bandwidthIdx = g_bwIndexFM; + g_si4735.setFmBandwidth(g_bwIndexFM); + } + showBandwidth(); +} + +void switchCommand(bool* b, void (*showFunction)()) +{ + static bool* prev = NULL; + static void (*prevFunc)() = NULL; + + if (!b) + { + if (prev) + { + *prev = false; + if (prevFunc) + prevFunc(); + g_lastAdjustmentTime = 0; + prev = NULL; + } + return; + } + + bool last = *b; + prev = b; + prevFunc = showFunction; + + if (*b == false) + { + g_cmdVolume = false; + g_cmdStep = false; + g_cmdBw = false; + g_cmdBand = false; + g_lastAdjustmentTime = millis(); + showVolume(); + showStep(); + showBandwidth(); + showModulation(); + } + else + g_lastAdjustmentTime = 0; + + *b = !last; + + if (showFunction) + showFunction(); +} + +bool clampSSBBand() +{ + uint16_t freq = g_currentFrequency + (g_currentBFO / 1000); + auto bfoReset = [&]() + { + g_currentBFO = 0; + updateBFO(); + showFrequency(true); + showModulation(); + }; + + bool upd = false; + if (freq > g_bandList[g_bandIndex].maximumFreq) + { + g_currentFrequency = g_bandList[g_bandIndex].minimumFreq; + upd = true; + } + else if (freq < g_bandList[g_bandIndex].minimumFreq) + { + g_currentFrequency = g_bandList[g_bandIndex].maximumFreq; + upd = true; + } + + if (upd) + { + g_bandList[g_bandIndex].currentFreq = g_currentFrequency; + g_si4735.setFrequency(g_currentFrequency); + bfoReset(); + return true; + } + + return false; +} + +void doFrequencyTune() +{ + g_seekDirection = g_encoderCount == 1 ? 1 : 0; + + //Update frequency + g_previousFrequency = g_currentFrequency; //Force EEPROM update + if (g_currentMode == FM) + { + g_currentFrequency += g_tabStepFM[g_FMStepIndex] * g_encoderCount; //g_si4735.getFrequency() is too slow +#if USE_RDS + if (g_displayRDS) + oledPrint(_literal_EmptyLine, 0, 6, DEFAULT_FONT); +#endif + } + else + g_currentFrequency += g_tabStep[g_stepIndex] * g_encoderCount; + uint16_t bMin = g_bandList[g_bandIndex].minimumFreq, bMax = g_bandList[g_bandIndex].maximumFreq; + + //Special logic for fast and responsive frequency surfing + if (g_currentFrequency > bMax) + g_currentFrequency = bMin; + else if (g_currentFrequency < bMin) + g_currentFrequency = bMax; + + g_bandList[g_bandIndex].currentFreq = g_currentFrequency; + g_processFreqChange = true; + g_lastFreqChange = millis(); + + showFrequency(); +} + +void resetLowerLine() +{ + if (g_sMeterOn || g_displayRDS) + { + g_sMeterOn = false; + g_displayRDS = false; + updateLowerDisplayLine(); + } +} + +//Special feature to make SSB feel like on expensive TECSUN receivers +//BFO is now part of main frequency in SSB mode +void doFrequencyTuneSSB() +{ + const int BFOMax = 16000; + int step = g_encoderCount == 1 ? getSteps() : getSteps() * -1; + int newBFO = g_currentBFO + step; + int redundant = 0; + + if (newBFO > BFOMax) + { + redundant = (newBFO / BFOMax) * BFOMax; + g_currentFrequency += redundant / 1000; + newBFO -= redundant; + } + else if (newBFO < -BFOMax) + { + redundant = ((abs(newBFO) / BFOMax) * BFOMax); + g_currentFrequency -= redundant / 1000; + newBFO += redundant; + } + + g_currentBFO = newBFO; + updateBFO(); + + if (redundant != 0) + { + g_si4735.setFrequency(g_currentFrequency); + agcSetFunc(); //Re-apply to remove noize + g_currentFrequency = g_si4735.getFrequency(); + g_bandList[g_bandIndex].currentFreq = g_currentFrequency; + } + + g_bandList[g_bandIndex].currentFreq = g_currentFrequency + (g_currentBFO / 1000); + g_lastFreqChange = millis(); + g_previousFrequency = 0; //Force EEPROM update + if (!clampSSBBand()) //If we move outside of current band - switch it + showFrequency(); +} + +void loop() +{ + uint8_t x; + bool skipButtonEvents = false; + bool frequencyRecentlyUpdated = millis() - g_lastFreqChange < 70; + + //Faster frequency tune + if (g_processFreqChange && !isSSB()) + { + if (!frequencyRecentlyUpdated && g_encoderCount == 0) + { + g_si4735.setFrequency(g_currentFrequency); + g_processFreqChange = false; + } + else if (frequencyRecentlyUpdated && g_encoderCount != 0) + { + doFrequencyTune(); + g_encoderCount = 0; + return; + } + } + + if (millis() - g_lastFreqChange >= 1000) + { +#if USE_RDS + showRDS(); +#endif + + if (g_sMeterOn && !g_settingsActive) + showSMeter(); + + showCharge(false); + } + + if (g_lastAdjustmentTime != 0 && millis() - g_lastAdjustmentTime > ADJUSTMENT_ACTIVE_TIMEOUT) + switchCommand(NULL, NULL); + + //Encoder rotation check + if (g_encoderCount != 0) + { + if (g_lastAdjustmentTime != 0) + g_lastAdjustmentTime = millis(); + + if (g_settingsActive) + { + if (!g_SettingEditing) + { + int8_t prev = g_SettingSelected; + int8_t next = g_SettingSelected; + + next += g_encoderCount; + + uint8_t pageIdx = g_SettingsPage - 1; + uint8_t maxOnThisPage = (pageIdx * 6) + 5; + if (maxOnThisPage >= SettingsIndex::SETTINGS_MAX) + maxOnThisPage = SettingsIndex::SETTINGS_MAX - 1; + + if (next < pageIdx * 6) + g_SettingSelected = maxOnThisPage; + else if (next > maxOnThisPage) + g_SettingSelected = pageIdx * 6; + else + g_SettingSelected = next; + + DrawSetting(prev, true); + DrawSetting(g_SettingSelected, true); + } + else + { + (*g_Settings[g_SettingSelected].manipulateCallback)(g_encoderCount); + DrawSetting(g_SettingSelected, false); + delay(MIN_ELAPSED_TIME); + } + } + else if (g_cmdVolume) + doVolume(g_encoderCount); + else if (g_cmdStep) + doStep(g_encoderCount); + else if (g_cmdBw) + doBandwidth(g_encoderCount); + else if (g_cmdBand) + { + if (g_encoderCount == 1) + bandSwitch(true); + else + bandSwitch(false); + } + else if (isSSB()) + { + doFrequencyTuneSSB(); + skipButtonEvents = true; + } + else + { + doFrequencyTune(); + skipButtonEvents = true; + } + g_encoderCount = 0; + resetEepromDelay(); + } + + if (skipButtonEvents) + goto saveAttempt; + + //Command-checkers + if (BUTTONEVENT_SHORTPRESS == btn_Bandwidth.checkEvent(simpleEvent)) + { + if (!g_settingsActive && g_currentMode != CW) + switchCommand(&g_cmdBw, showBandwidth); + } + if (BUTTONEVENT_SHORTPRESS == btn_BandUp.checkEvent(bandEvent)) + { + if (!g_settingsActive) + { + resetLowerLine(); + switchCommand(&g_cmdBand, showModulation); + } + else + { + switchSettingsPage(); + } + } + if (BUTTONEVENT_SHORTPRESS == btn_BandDn.checkEvent(bandEvent)) + { + if (!g_settingsActive) + switchCommand(NULL, NULL); + g_settingsActive = !g_settingsActive; + switchSettings(); + } + if (BUTTONEVENT_SHORTPRESS == btn_VolumeUp.checkEvent(volumeEvent)) + { + if (!g_settingsActive && g_muteVolume == 0) + switchCommand(&g_cmdVolume, showVolume); + } + if (BUTTONEVENT_SHORTPRESS == btn_VolumeDn.checkEvent(volumeEvent)) + { + if (!g_cmdVolume) + { + uint8_t vol = g_si4735.getCurrentVolume(); + if (vol > 0 && g_muteVolume == 0) + { + g_muteVolume = vol; + g_si4735.setVolume(0); + } + else if (g_muteVolume > 0) + { + g_si4735.setVolume(g_muteVolume); + g_muteVolume = 0; + } + showVolume(); + } + } + if (BUTTONEVENT_SHORTPRESS == btn_Encoder.checkEvent(simpleEvent)) + { + if (g_cmdBand) + { + g_cmdBand = false; + showModulation(); + } + else if (g_cmdStep) + { + g_cmdStep = false; + showStep(); + } + else if (g_cmdBw) + { + g_cmdBw = false; + showBandwidth(); + } + else if (g_cmdVolume) + { + g_cmdVolume = false; + showVolume(); + } + else if (g_settingsActive) + { + g_SettingEditing = !g_SettingEditing; + DrawSetting(g_SettingSelected, true); + } + else if (g_displayRDS) + g_rdsSwitchPressed = true; + else if (isSSB() || g_Settings[SettingsIndex::ScanSwitch].param == 0) + { + if (!g_settingsActive) + { + switchCommand(&g_cmdStep, showStep); + resetLowerLine(); + } + } + //Seek in SSB/CW is not allowed + else if (g_currentMode == FM || g_currentMode == AM) + doSeek(); + } + + //This is a hack, it allows SHORTPRESS and LONGPRESS events + //Be processed without complicated overhead + //It requires to save checkEvent result into a variable + //That has exact same name as event processing function for this button + uint8_t agcEvent = btn_AGC.checkEvent(agcEvent); + if (BUTTONEVENT_SHORTPRESS == agcEvent) + { + if (!g_settingsActive || g_settingsActive && !g_displayOn) + { + g_displayOn = !g_displayOn; + if (g_displayOn) + oled.on(); + else + oled.off(); + } + } + if (BUTTONEVENT_LONGPRESS == agcEvent) + { + if (!g_settingsActive) + { + if (isSSB()) + doSync(1); + } + } + uint8_t stepEvent = btn_Step.checkEvent(stepEvent); + if (BUTTONEVENT_SHORTPRESS == stepEvent) + { + if (!g_settingsActive) + { + switchCommand(&g_cmdStep, showStep); + resetLowerLine(); + } + } + if (BUTTONEVENT_LONGPRESSDONE == stepEvent) + { + if (!g_settingsActive) + { + g_sMeterOn = !g_sMeterOn; + if (g_sMeterOn) + { + g_displayRDS = false; + showSMeter(); + } + else + updateLowerDisplayLine(); + } + } + if (BUTTONEVENT_SHORTPRESS == btn_Mode.checkEvent(simpleEvent)) + { + if (!g_settingsActive) + { + //Do nothing on FM mode (unfortunately no NBFM patch), otherwise switch AM modulation + if (g_currentMode != FM) + { + g_bandList[g_bandIndex].currentFreq = g_currentFrequency; + g_prevMode = g_currentMode; + switch (g_currentMode) + { + case AM: + //Patch Si473x memory every time when enabling SSB + loadSSBPatch(); + g_processFreqChange = false; + //Allow pass through + + case LSB: + g_currentMode++; + g_bandList[g_bandIndex].currentFreq += g_currentBFO / 1000; + break; + + case USB: + g_currentMode++; + g_cmdBw = false; + g_bandList[g_bandIndex].currentFreq += g_currentBFO / 1000; + break; + + case CW: + g_currentMode = AM; + g_ssbLoaded = false; + if (g_stepIndex >= g_amTotalSteps) + g_stepIndex = 0; + + g_currentFrequency += (g_currentBFO / 1000); + break; + } + + g_bandList[g_bandIndex].currentStepIdx = g_stepIndex; + applyBandConfiguration(); + } +#if USE_RDS + else + doRDS(); +#endif + } + } + +saveAttempt: + //Save EEPROM if anough time passed and frequency changed + if (g_currentFrequency != g_previousFrequency) + { + if ((millis() - g_storeTime) > STORE_TIME) + { + saveAllReceiverInformation(); + g_storeTime = millis(); + g_previousFrequency = g_currentFrequency; + } + } +} + +//Overriding original main to save some space +int main(void) +{ + init(); + setup(); + while(1) + loop(); + return 0; +} \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.vcxproj b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.vcxproj new file mode 100755 index 00000000..46cb7ae6 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x86 + + + Release + x86 + + + Debug + x64 + + + Release + x64 + + + + {fabf0e8c-d02a-47fe-a9ab-573ea614a543} + Linux + ATS_EX + ATS_EX + 15.0 + Linux + 1.0 + Generic + {D51BCBC9-82E9-4017-911E-C93873C4EA2B} + + + + + true + + + false + + + true + + + false + + + true + + + false + + + false + + + true + + + + + + c:\visualmicro\ignore + c:\visualmicro\pi-ignore + Arduino + + + + CppCode + true + + + + + + + + + + + + + + + + + + + VisualMicroDebugger + + + $(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include-fixed;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\avr\include;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts-master\src + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + false + + + $(ProjectDir)..\ATS_EX;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts-master\src;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include-fixed;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\avr\include;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src\utility;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts-master\src + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + false + + + $(ProjectDir)..\ATS_EX;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\PU2CLR_SI4735\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts\src;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include-fixed;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\avr\include;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\PU2CLR_SI4735\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src\utility;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts\src + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + false + + + + $(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include-fixed;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\avr\include;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts-master\src;%(AdditionalIncludeDirectories) + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + gnu++11 + gnu11 + + + $(ProjectDir)..\..\..\..\..\..\program files\microsoft visual studio\2022\enterprise\common7\ide\extensions\jydagq0n.l0f\Micro Platforms\default\vm-intelli-pre.h;$(ProjectDir)..\..\..\..\AppData\Local\Temp\VMBuilds\ATS_EX\nano_atmega328old\Debug\.vmintelli\b8a561aa118abd048bd0a5e5c9f0b0f6\vm-intelli-gcc-defines.h;$(ProjectDir)..\..\..\..\..\..\program files\microsoft visual studio\2022\enterprise\common7\ide\extensions\jydagq0n.l0f\Micro Platforms\default\vm-intelli-post.h;$(ProjectDir)__vm\.ATS_EX.vsarduino.h;%(ForcedIncludeFiles) + true + true + _VMICRO_INTELLISENSE;__AVR_atmega328p__;__AVR_ATmega328P__;__AVR_ATmega328p__;_VMDEBUG=1;F_CPU=16000000L;ARDUINO=108018;ARDUINO_AVR_NANO;ARDUINO_ARCH_AVR;%(PreprocessorDefinitions) + + + + + $(ProjectDir)..\ATS_EX;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts-master\src;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include-fixed;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\avr\include;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\SI4735-master\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src\utility;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED-master\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts-master\src;%(AdditionalIncludeDirectories) + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + gnu++11 + gnu11 + + + $(ProjectDir)..\..\..\..\..\..\program files\microsoft visual studio\2022\enterprise\common7\ide\extensions\jydagq0n.l0f\Micro Platforms\default\vm-intelli-pre.h;$(ProjectDir)..\..\..\..\AppData\Local\Temp\VMBuilds\ATS_EX\nano_atmega328old\Release\.vmintelli\b8a561aa118abd048bd0a5e5c9f0b0f6\vm-intelli-gcc-defines.h;$(ProjectDir)..\..\..\..\..\..\program files\microsoft visual studio\2022\enterprise\common7\ide\extensions\jydagq0n.l0f\Micro Platforms\default\vm-intelli-post.h;$(ProjectDir)__vm\.ATS_EX.vsarduino.h;%(ForcedIncludeFiles) + true + true + _VMICRO_INTELLISENSE;__AVR_atmega328p__;__AVR_ATmega328P__;__AVR_ATmega328p__;F_CPU=16000000L;ARDUINO=108018;ARDUINO_AVR_NANO;ARDUINO_ARCH_AVR;%(PreprocessorDefinitions) + None + true + MinSize + + + OmitAllSymbolInformation + + + + + $(ProjectDir)..\ATS_EX;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\PU2CLR_SI4735\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts\src;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino;$(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\lib\gcc\avr\7.3.0\include-fixed;$(ProjectDir)..\..\..\..\..\..\program files (x86)\arduino\hardware\tools\avr\avr\include;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\PU2CLR_SI4735\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src\utility;$(ProjectDir)..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\Tiny4kOLED\src;$(ProjectDir)..\..\..\..\Documents\Arduino\libraries\TinyOLED-Fonts\src;%(AdditionalIncludeDirectories) + $(ProjectDir)..\..\..\..\..\..\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\bin\avr-g++ + gnu++11 + gnu11 + + + $(ProjectDir)..\..\..\..\..\..\program files\microsoft visual studio\2022\enterprise\common7\ide\extensions\jydagq0n.l0f\Micro Platforms\default\vm-intelli-pre.h;$(ProjectDir)..\..\..\..\AppData\Local\Temp\VMBuilds\ATS_EX\nano_atmega328old\Release\.vmintelli\dd8acb9ec10164974b5980566760958e\vm-intelli-gcc-defines.h;$(ProjectDir)..\..\..\..\..\..\program files\microsoft visual studio\2022\enterprise\common7\ide\extensions\jydagq0n.l0f\Micro Platforms\default\vm-intelli-post.h;$(ProjectDir)__vm\.ATS_EX.vsarduino.h;%(ForcedIncludeFiles) + true + true + _VMICRO_INTELLISENSE;__AVR_atmega328p__;__AVR_ATmega328P__;__AVR_ATmega328p__;F_CPU=16000000L;ARDUINO=108019;ARDUINO_AVR_NANO;ARDUINO_ARCH_AVR;%(PreprocessorDefinitions) + + + + + + + + + + \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.vcxproj.filters b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.vcxproj.filters new file mode 100644 index 00000000..3550dd7f --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/ATS_EX.vcxproj.filters @@ -0,0 +1,53 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {caa261d3-d22a-4861-a4b4-24dee61c5230} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Globals and defs + + + Header Files\Globals and defs + + + Header Files\Globals and defs + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Rotary.cpp b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Rotary.cpp new file mode 100644 index 00000000..831b4f52 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Rotary.cpp @@ -0,0 +1,80 @@ +#include "Arduino.h" +#include "Rotary.h" +#define R_START 0x0 +#ifdef HALF_STEP +// Use the half-step state table (emits a code at 00 and 11) +#define R_CCW_BEGIN 0x1 +#define R_CW_BEGIN 0x2 +#define R_START_M 0x3 +#define R_CW_BEGIN_M 0x4 +#define R_CCW_BEGIN_M 0x5 +const unsigned char ttable[6][4] = { + // R_START (00) + {R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START}, + // R_CCW_BEGIN + {R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START}, + // R_CW_BEGIN + {R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START}, + // R_START_M (11) + {R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START}, + // R_CW_BEGIN_M + {R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW}, + // R_CCW_BEGIN_M + {R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW}, +}; +#else +// Use the full-step state table (emits a code at 00 only) +#define R_CW_FINAL 0x1 +#define R_CW_BEGIN 0x2 +#define R_CW_NEXT 0x3 +#define R_CCW_BEGIN 0x4 +#define R_CCW_FINAL 0x5 +#define R_CCW_NEXT 0x6 + +const unsigned char ttable[7][4] = +{ + // R_START + {R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START}, + // R_CW_FINAL + {R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW}, + // R_CW_BEGIN + {R_CW_NEXT, R_CW_BEGIN, R_START, R_START}, + // R_CW_NEXT + {R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START}, + // R_CCW_BEGIN + {R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START}, + // R_CCW_FINAL + {R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW}, + // R_CCW_NEXT + {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START}, +}; +#endif + +// Constructor. Each arg is the pin number for each encoder contact +Rotary::Rotary(char _pin1, char _pin2) +{ + // Assign variables + pin1 = _pin1; + pin2 = _pin2; + // Set pins to input. + DDRD &= ~(1 << pin1); + DDRD &= ~(1 << pin2); + +#ifdef ENABLE_PULLUPS + PORTD |= (1 << pin1); + PORTD |= (1 << pin2); +#endif + // Initialise state + state = R_START; +} + +unsigned char Rotary::process() +{ + // Grab state of input pins + unsigned char pinstate = ((PIND & (1 << 3)) >> 2) | ((PIND & (1 << 2)) >> 2); + // Determine new state from the pins and state table + state = ttable[state & 0xf][pinstate]; + // Return emit bits, ie the generated event + return state & 0x30; +} + diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Rotary.h old mode 100755 new mode 100644 similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.h rename to examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Rotary.h diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/SimpleButton.cpp b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/SimpleButton.cpp new file mode 100644 index 00000000..f3132f15 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/SimpleButton.cpp @@ -0,0 +1,159 @@ +#include "Arduino.h" +#include "SimpleButton.h" + + +#define BUTTONSTATE_IDLE 0 // Button not pressed (initial state) + // DO NOT CHANGE!!! BUTTONSTATE_IDLE must always be defined as 0 (Zero)! +#define BUTTONSTATE_DEBOUNCE 1 // Button press detected, waiting for debounce +#define BUTTONSTATE_RELEASE 2 // Button was released again + +// BUTTONSTATE_RELEASE is the last state (in numerical order) for which the button is assumed not pressed +// All states which are (numerical) higher then BUTTONSTATE_RELEASE are representing states with pressed button +// In total 16 different states can be coded (0..15), apart from the rule with BUTTONSTATE_RELEASE the order is irrelevant + +#define BUTTONSTATE_PRESSED 3 // Button pressed. Measure time to see if it is a Longpress +#define BUTTONSTATE_LONGPRESS 4 // Press is a longpress +#define BUTTONSTATE_LONGRELEASE 5 // Button released after longpress but wait for debounce the release +#define BUTTONSTATE_SHORTRELEASE 6 // Button released after shortpress but wait for debounce the release +//#define BUTTONSTATE_2DEBOUNCE 7 + + +SimpleButton::SimpleButton(uint8_t pin) +{ + //pinMode(pin, INPUT_PULLUP); + if (pin < 8) + { + DDRD &= ~(1 << pin); + PORTD |= (1 << pin); + } + else if (pin < 14) + { + DDRB &= ~(1 << (pin - 8)); + PORTB |= (1 << (pin - 8)); + } + else + { + DDRC &= ~(1 << (pin - 14)); + PORTC |= (1 << (pin - 14)); + } + _PinDebounceState = ((uint16_t)pin << 10); +} + + +uint8_t SimpleButton::checkEvent(uint8_t (*_event)(uint8_t event, uint8_t pin)) +{ + uint8_t ret = 0; + uint16_t timeNow = millis() & 0x3f0; + uint16_t state = _PinDebounceState & 0xf; + uint16_t debounce = _PinDebounceState & 0x3f0; + uint8_t pin = _PinDebounceState >> 10; + uint8_t pinState; + uint16_t elapsed; + + if (pin < 8) + pinState = (PIND & (1 << pin)) ? HIGH : LOW; + else if (pin < 14) + pinState = (PINB & (1 << (pin - 8))) ? HIGH : LOW; + else + pinState = (PINC & (1 << (pin - 14))) ? HIGH : LOW; + + if (timeNow < debounce) + timeNow = timeNow + 0x400; + + elapsed = timeNow - debounce; + switch(state) + { + case BUTTONSTATE_IDLE: + if (!pinState) + { + state = BUTTONSTATE_DEBOUNCE; + debounce = timeNow; + } + break; + case BUTTONSTATE_DEBOUNCE: + if (pinState) + { + state = BUTTONSTATE_IDLE; + } + else if (elapsed >= BUTTONTIME_PRESSDEBOUNCE) + { + state = BUTTONSTATE_PRESSED; + } + break; + case BUTTONSTATE_PRESSED: + if (pinState) + { + debounce = timeNow; + state = BUTTONSTATE_SHORTRELEASE; + } + else if (elapsed >= BUTTONTIME_LONGPRESS1) + { + ret = BUTTONEVENT_FIRSTLONGPRESS; + state = BUTTONSTATE_LONGPRESS; + debounce = timeNow; + } + break; + case BUTTONSTATE_LONGPRESS: + if (pinState) + { + state = BUTTONSTATE_LONGRELEASE; + } + else if (elapsed >= BUTTONTIME_LONGPRESSREPEAT) + { + debounce = timeNow; + ret = BUTTONEVENT_LONGPRESS; + } + break; + case BUTTONSTATE_LONGRELEASE: + if (pinState) + { + ret = BUTTONEVENT_LONGPRESSDONE; + state = BUTTONSTATE_RELEASE; + debounce = timeNow; + } + else + { + state = BUTTONSTATE_LONGPRESS; + } + break; + case BUTTONSTATE_SHORTRELEASE: + if (pinState) + { + ret = BUTTONEVENT_SHORTPRESS; + state = BUTTONSTATE_RELEASE; + } + else + { + state = BUTTONSTATE_PRESSED; + } + break; + case BUTTONSTATE_RELEASE: + if (pinState) + { + if (elapsed >= BUTTONTIME_RELEASEDEBOUNCE)//(millis() - (_debounce > (BUTTON_DEBOUNCE))) + state = BUTTONSTATE_IDLE; + } + else + { + debounce = timeNow; + } + break; + default: + break; + } + + _PinDebounceState = (_PinDebounceState & 0xfc00) | (debounce & 0x3f0) | state; + + if (ret) + { + if (_event) + ret = _event(ret, pin); + } + else + { + if (state > BUTTONSTATE_RELEASE) + ret = BUTTON_PRESSED; + } + + return ret; +} diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/SimpleButton.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/SimpleButton.h new file mode 100644 index 00000000..bb5c2cee --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/SimpleButton.h @@ -0,0 +1,97 @@ +// Simple button library for Arduino. +#include "Arduino.h" + +#ifndef simplebutton_h +#define simplebutton_h + +/* +Defines a class SimpleButton which must be linked to a GPIO to act as a button handler for this pin. + +SimpleButton will debounce the pin and is able to generate Shortpress- and (repeated) Longpress-Events. + +To keep the code clean, there are some limitations: +- Pin is set to Input with Internal Pullup. If the controller does not have internal pullups on tha specified pin, you need to add one + in hardware. +- The switch attached to the pin is considered to drive the pin to LOW if pressed. +- Valid pin numbers range from 0..63. +- Timings (for Debounce, Longpress-Events) are hardcoded at compile time (see BUTTONTIME_-#defines below) and can not be + changed by API or set differently for different buttons +- All Timings are significant as multiples of 16 only (i. e. 0..15 === 0, 16..31 === 16 etc.) and must not exceed 1023 (or 1008) + +There are only two API-Calls: + +SimpleButton::SimpleButton(uint8_t pin); + + - Constructor to create a "Button-Handler" + - The given pin number must not exceed 63 + - The given pin is set to pinMode(INPUT_PULLUP). The allplication must not change the pinMode for the given pin! + +uint8_t SimpleButton::checkEvent( uint8_t (*eventHandler)(uint8_t eventId, uint8_t pin)=NULL ); + - Must be called (frequently, i. e. in loop()) to process the button events + - will return one the return codes described below (note that the return value can be changed by the callback) + - an optional callback-function can be passed as argument to SimpleButton::checkEvent(): + - signature of callback function is uint8_t (uint8_t eventId, uint8_t pin); + - the first parameter is the event triggering the callback, equivalent to the return code of SimpleButton::checkEvent(), but + only the events coded as BUTTONEVENT_*-defines will be reported to the callback function + - the second parameter is the pin that is attached to the button. This information can be used to use the same callback function + for different buttons (i. e. for Volume+ or Volume- where the logic is the same but only the direction of change differs) + - the callback will be called from inside checkEvent() before it returns to the main application loop + - the return value of the callback will be returned by checkEvent(). When in doubt, just return the parameter event + */ + +/* +Return codes for SimpleButton::eventCheck() Do not mess around with the numbers used in the follwing defines!!! + +Each event is reported only once. I. e. if a BUTTONEVENT_SHORTPRESS is returned, the next call to eventCheck() will +return something else (most likely BUTTON_IDLE) if called again directly (and no other press has happened). + +Note that the values are somewhat bitcoded: +- if value is 0 (Zero), button is currently not pressed and no event is due... +- b0 is the "event" flag, if set, a button event has occured (pressed, longpressed) +- if b1 is set (currently only if b0 is set as well), this is a longpress event +- b2 is the "repeat" flag (currently only for continuous longpress, so b1 and b0 are set as well) +- b3 is the "done" flag. + * if no other bit is set, this signals the button is still active (pressed) but no event condition applies + * if b1 and b0 are also set, longpress has just been finished + +*/ + +#define BUTTON_IDLE 0 // Button is currently not pressed. +#define BUTTONEVENT_SHORTPRESS 1 // Shortpress-Event detected! +#define BUTTONEVENT_2PRESS 5 +#define BUTTONEVENT_FIRSTLONGPRESS 3 // Button is longpressed (Longpress just started) +#define BUTTONEVENT_LONGPRESS 7 // Button is still longpressed (event will be generated every x ms as defined by + // BUTTONTIME_LONGPRESSREPEAT (see below), if the SimpleButton::event() is called + // often enough. + // The application self must do something if longer period is needed (i. e. like + // delaying calls to ::event() or react on only every other event to achieve 400ms) +#define BUTTONEVENT_LONGPRESSDONE 11 // Button is released after longpress. The application must not treat this event + // as if the button is still pressed but either use it for some cleanup (if needed) + // or simply ignore it. +#define BUTTON_PRESSED 8 // No event, but the button is pressed (so either a BUTTONEVENT_SHORTPRESS or + // any of the longpress-Events might follow but time for this is not yet due). + +#define BUTTONEVENT_ISLONGPRESS(x) (3 == (x & 3)) +#define BUTTONEVENT_ISDONE(x) (8 == (x & 8)) + +#define BUTTONTIME_PRESSDEBOUNCE 0*16 // How long to debounce falling slope of pin (in ms), i. e. Button going to pressed + // Zero is probably fine, if the button does not generate noise on changes (oscillates + // between HIGH/LOW before going to a stable low reading). +#define BUTTONTIME_LONGPRESS1 20*16 // Time (ms) after debounce a button needs to be pressed to be considered longpressed +#define BUTTONTIME_LONGPRESSREPEAT 3*16 // Time (ms) between consecutive longpress events +#define BUTTONTIME_RELEASEDEBOUNCE 4*16 // How long to debounce rising slope of pin (in ms), i. e. Button going to released + +class SimpleButton +{ + public: + SimpleButton(uint8_t pin); + uint8_t checkEvent(uint8_t (*_event)(uint8_t event, uint8_t pin) = NULL); + private: + uint16_t _PinDebounceState; + // The data is stored as Bitfield: + // - b15..b10 (6 bit): pin number + // - b9.. b4 (6 bit): timestamp (in units of 16ms) to remember last state change in checkEvent + // - b3.. b0 (4 bit): current state of checkEvent +}; +#endif +//https://www.electronjs.org/docs/latest/development/pull-requests diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Utils.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Utils.h new file mode 100644 index 00000000..28a6515e --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/Utils.h @@ -0,0 +1,121 @@ +#pragma once + +const DCfont* LastFont = DEFAULT_FONT; + +void oledSetFont(const DCfont* font) +{ + if (font && LastFont != font) + { + LastFont = font; + oled.setFont(font); + } +} + +void oledPrint(const char* text, int offX = -1, int offY = -1, const DCfont* font = LastFont, bool invert = false) +{ + oledSetFont(font); + if (invert) + oled.invertOutput(invert); + if (offX >= 0 && offY >= 0) + oled.setCursor(offX, offY); + oled.print(text); + if (invert) + oled.invertOutput(false); +} + +void oledPrint(uint16_t u, int offX = -1, int offY = -1, const DCfont* font = LastFont, bool invert = false) +{ + oledSetFont(font); + if (invert) + oled.invertOutput(invert); + if (offX >= 0 && offY >= 0) + oled.setCursor(offX, offY); + oled.print(u); + if (invert) + oled.invertOutput(false); +} + +//Faster alternative for convertToChar +void utoa(char* out, uint16_t num) +{ + char* p = out; + if (num == 0) + *p++ = '0'; + else + { + for (uint16_t base = 10000; base > 0; base /= 10) + { + if (num >= base) + { + *p++ = '0' + num / base; + num %= base; + } + else if (p != out) + *p++ = '0'; + } + } + + *p = '\0'; +} + +//Better than sprintf which has overwhelmingly large overhead, it helps to reduce binary size +void convertToChar(char* strValue, uint16_t value, uint8_t len, uint8_t dot = 0, uint8_t separator = 0, uint8_t space = ' ') +{ + char d; + int8_t i; + for (i = (len - 1); i >= 0; i--) + { + d = value % 10; + value = value / 10; + strValue[i] = d + 48; + } + strValue[len] = '\0'; + + if (dot > 0) + { + for (int i = len; i >= dot; i--) + { + strValue[i + 1] = strValue[i]; + } + strValue[dot] = separator; + len = dot; + } + i = 0; + len--; + + while ((i < len) && ('0' == strValue[i])) + { + strValue[i++] = space; + } +} + +//Measure integer digit length +int ilen(uint16_t n) +{ + if (n < 10) + return 1; + else if (n < 100) + return 2; + else if (n < 1000) + return 3; + else if (n < 10000) + return 4; + else + return 5; +} + +//Split KHz frequency + BFO to KHz and .00 tail +void splitFreq(uint16_t& khz, uint16_t& tail) +{ + int32_t freq = (uint32_t(g_currentFrequency) * 1000) + g_currentBFO; + khz = freq / 1000; + tail = abs(freq % 1000) / 10; +} + +uint8_t strlen8(const char* str) +{ + uint8_t n = 0; + while (str[n] != '\0') + n++; + return n; +} \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/.ATS_EX.vsarduino.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/.ATS_EX.vsarduino.h new file mode 100644 index 00000000..47d34e6e --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/.ATS_EX.vsarduino.h @@ -0,0 +1,20 @@ +/* + Editor: https://www.visualmicro.com/ + This file is for intellisense purpose only. + Visual micro (and the arduino ide) ignore this code during compilation. This code is automatically maintained by visualmicro, manual changes to this file will be overwritten + The contents of the _vm sub folder can be deleted prior to publishing a project + All non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!). + Note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again + + Hardware: ATmega328P (Old Bootloader) (Arduino Nano) (nano_atmega328old), Platform=avr, Package=arduino +*/ + +#if defined(_VMICRO_INTELLISENSE) + +#ifndef _VSARDUINO_H_ +#define _VSARDUINO_H_ +#include +#include +#include "..\ATS_EX.ino" +#endif +#endif diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Compile.vmps.xml b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Compile.vmps.xml new file mode 100755 index 00000000..2c8dc42a --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Compile.vmps.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Configuration.Release.vmps.xml b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Configuration.Release.vmps.xml new file mode 100644 index 00000000..24bd6b42 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Configuration.Release.vmps.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Upload.vmps.xml b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Upload.vmps.xml new file mode 100755 index 00000000..a99283aa --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/__vm/Upload.vmps.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/defs.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/defs.h new file mode 100755 index 00000000..42978a04 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/defs.h @@ -0,0 +1,55 @@ +#pragma once + +//If you set this def to 0 project will be compiled without RDS +//and everything related to RDS will be excluded from build +#define USE_RDS 1 + +#define EEPROM_APP_ID 235 +#define EEPROM_DATA_START_ADDRESS 1 +#define EEPROM_VERSION_ADDRESS 1000 +#define EEPROM_APP_ID_ADDRESS 0 + +//EEPROM Settings +#define STORE_TIME 10000 // Inactive time to save our settings + +// OLED Const values +#define DEFAULT_FONT FONT8X16POB +#define RST_PIN -1 +#define RESET_PIN 12 + +//Battery charge monitoring analog pin (Voltage divider 10-10 KOhm directly from battery) +#define BATTERY_VOLTAGE_PIN A2 + +// Encoder +#define ENCODER_PIN_A 2 +#define ENCODER_PIN_B 3 + +// Buttons +#define MODE_SWITCH 4 +#define BANDWIDTH_BUTTON 5 +#define VOLUME_BUTTON 6 +#define AVC_BUTTON 7 +#define BAND_BUTTON 8 +#define SOFTMUTE_BUTTON 9 +#define AGC_BUTTON 11 +#define STEP_BUTTON 10 + +#define ENCODER_BUTTON 14 + +// Default values +#define MIN_ELAPSED_TIME 100 +#define MIN_ELAPSED_RSSI_TIME 150 +#define DEFAULT_VOLUME 25 +#define ADJUSTMENT_ACTIVE_TIMEOUT 3000 + +// Band settings +#define SW_LIMIT_LOW 1710 +#define SW_LIMIT_HIGH 30000 +#define LW_LIMIT_LOW 153 +#define CB_LIMIT_LOW 26200 +#define CB_LIMIT_HIGH 28000 + +#define BAND_DELAY 2 +#define VOLUME_DELAY 1 + +#define buttonEvent NULL \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/font14x24sevenSeg.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/font14x24sevenSeg.h new file mode 100644 index 00000000..f2a0f26a --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/font14x24sevenSeg.h @@ -0,0 +1,69 @@ +// ---------------------------------------------------------------------- +// Seven Segment font for Tiny4kOLED library. +// 14x24 resolution. +// Charset: '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' +// Empty space character as a '/' +// v1.1 +// Changes: +// - Removed minus character (-) +// - Font is more aligned to top +// ---------------------------------------------------------------------- +// By Goshante +// 02.2024 +// http://github.com/goshante +// ---------------------------------------------------------------------- + + +#include + + +const uint8_t ssd1306xled_font14x24sevenSeg [] PROGMEM = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x20,0x70,0x70,0x20,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xFE,0xFD,0xFB,0x07,0x07,0x07,0x07,0x07,0x07,0xFB,0xFD,0xFE,0x00, + 0x00,0xF7,0xE3,0xC1,0x00,0x00,0x00,0x00,0x00,0x00,0xC1,0xE3,0xF7,0x00, + 0x00,0x3F,0x5F,0x6F,0x70,0x70,0x70,0x70,0x70,0x70,0x6F,0x5F,0x3F,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0xFC,0xFE,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC1,0xE3,0xF7,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x1F,0x3F,0x00, + 0x00,0x00,0x01,0x03,0x07,0x07,0x07,0x07,0x07,0x07,0xFB,0xFD,0xFE,0x00, + 0x00,0xF0,0xE8,0xDC,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1D,0x0B,0x07,0x00, + 0x00,0x3F,0x5F,0x6F,0x70,0x70,0x70,0x70,0x70,0x70,0x60,0x40,0x00,0x00, + 0x00,0x00,0x01,0x03,0x07,0x07,0x07,0x07,0x07,0x07,0xFB,0xFD,0xFE,0x00, + 0x00,0x00,0x08,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xDD,0xEB,0xF7,0x00, + 0x00,0x00,0x40,0x60,0x70,0x70,0x70,0x70,0x70,0x70,0x6F,0x5F,0x3F,0x00, + 0x00,0xFE,0xFC,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0xFC,0xFE,0x00, + 0x00,0x07,0x0B,0x1D,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xDD,0xEB,0xF7,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x1F,0x3F,0x00, + 0x00,0xFE,0xFD,0xFB,0x07,0x07,0x07,0x07,0x07,0x07,0x03,0x01,0x00,0x00, + 0x00,0x07,0x0B,0x1D,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xDC,0xE8,0xF0,0x00, + 0x00,0x00,0x40,0x60,0x70,0x70,0x70,0x70,0x70,0x70,0x6F,0x5F,0x3F,0x00, + 0x00,0xFE,0xFD,0xFB,0x07,0x07,0x07,0x07,0x07,0x07,0x03,0x01,0x00,0x00, + 0x00,0xF7,0xEB,0xDD,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xDC,0xE8,0xF0,0x00, + 0x00,0x3F,0x5F,0x6F,0x70,0x70,0x70,0x70,0x70,0x70,0x6F,0x5F,0x3F,0x00, + 0x00,0x00,0x01,0x03,0x07,0x07,0x07,0x07,0x07,0x07,0xFB,0xFD,0xFE,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC1,0xE3,0xF7,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x1F,0x3F,0x00, + 0x00,0xFE,0xFD,0xFB,0x07,0x07,0x07,0x07,0x07,0x07,0xFB,0xFD,0xFE,0x00, + 0x00,0xF7,0xEB,0xDD,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xDD,0xEB,0xF7,0x00, + 0x00,0x3F,0x5F,0x6F,0x70,0x70,0x70,0x70,0x70,0x70,0x6F,0x5F,0x3F,0x00, + 0x00,0xFE,0xFD,0xFB,0x07,0x07,0x07,0x07,0x07,0x07,0xFB,0xFD,0xFE,0x00, + 0x00,0x07,0x0B,0x1D,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xDD,0xEB,0xF7,0x00, + 0x00,0x00,0x40,0x60,0x70,0x70,0x70,0x70,0x70,0x70,0x6F,0x5F,0x3F,0x00 +}; + +const DCfont TinyOLED4kfont14x24sevenSeg = +{ + (uint8_t*)ssd1306xled_font14x24sevenSeg, + 14, //Character width in pixels + 3, //Character height in pages (8 pixels) + 46, 57 //ASCII extents +}; + +//For backwards compatibility +#define FONT14X24SEVENSEG (&TinyOLED4kfont14x24sevenSeg) \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/font14x24sevenSeg_old.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/font14x24sevenSeg_old.h new file mode 100644 index 00000000..385e7d10 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/font14x24sevenSeg_old.h @@ -0,0 +1,69 @@ +// ---------------------------------------------------------------------- +// Seven Segment font for Tiny4kOLED library. +// 14x24 resolution. +// Charset: '.', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' +// Empty space character as a '/' +// v1.0 +// ---------------------------------------------------------------------- +// By Goshante +// 02.2024 +// http://github.com/goshante +// ---------------------------------------------------------------------- + + +#include + + +const uint8_t ssd1306xled_font14x24sevenSeg [] PROGMEM = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x10,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x10,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x40,0xE0,0xE0,0x40,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xFC,0xFA,0xF6,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0xF6,0xFA,0xFC,0x00, + 0x00,0xEF,0xC7,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0xC7,0xEF,0x00, + 0x00,0x7F,0xBF,0xDF,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xDF,0xBF,0x7F,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF8,0xFC,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0xC7,0xEF,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x3F,0x7F,0x00, + 0x00,0x00,0x02,0x06,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0xF6,0xFA,0xFC,0x00, + 0x00,0xE0,0xD0,0xB8,0x38,0x38,0x38,0x38,0x38,0x38,0x3B,0x17,0x0F,0x00, + 0x00,0x7F,0xBF,0xDF,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xC0,0x80,0x00,0x00, + 0x00,0x00,0x02,0x06,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0xF6,0xFA,0xFC,0x00, + 0x00,0x00,0x10,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0xBB,0xD7,0xEF,0x00, + 0x00,0x00,0x80,0xC0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xDF,0xBF,0x7F,0x00, + 0x00,0xFC,0xF8,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF8,0xFC,0x00, + 0x00,0x0F,0x17,0x3B,0x38,0x38,0x38,0x38,0x38,0x38,0xBB,0xD7,0xEF,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x3F,0x7F,0x00, + 0x00,0xFC,0xFA,0xF6,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x06,0x02,0x00,0x00, + 0x00,0x0F,0x17,0x3B,0x38,0x38,0x38,0x38,0x38,0x38,0xB8,0xD0,0xE0,0x00, + 0x00,0x00,0x80,0xC0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xDF,0xBF,0x7F,0x00, + 0x00,0xFC,0xFA,0xF6,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x06,0x02,0x00,0x00, + 0x00,0xEF,0xD7,0xBB,0x38,0x38,0x38,0x38,0x38,0x38,0xB8,0xD0,0xE0,0x00, + 0x00,0x7F,0xBF,0xDF,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xDF,0xBF,0x7F,0x00, + 0x00,0x00,0x02,0x06,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0xF6,0xFA,0xFC,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0xC7,0xEF,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x3F,0x7F,0x00, + 0x00,0xFC,0xFA,0xF6,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0xF6,0xFA,0xFC,0x00, + 0x00,0xEF,0xD7,0xBB,0x38,0x38,0x38,0x38,0x38,0x38,0xBB,0xD7,0xEF,0x00, + 0x00,0x7F,0xBF,0xDF,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xDF,0xBF,0x7F,0x00, + 0x00,0xFC,0xFA,0xF6,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0xF6,0xFA,0xFC,0x00, + 0x00,0x0F,0x17,0x3B,0x38,0x38,0x38,0x38,0x38,0x38,0xBB,0xD7,0xEF,0x00, + 0x00,0x00,0x80,0xC0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xDF,0xBF,0x7F,0x00 +}; + +const DCfont TinyOLED4kfont14x24sevenSeg = +{ + (uint8_t*)ssd1306xled_font14x24sevenSeg, + 14, //Character width in pixels + 3, //Character height in pages (8 pixels) + 45, 57 //ASCII extents +}; + +//For backwards compatibility +#define FONT14X24SEVENSEG (&TinyOLED4kfont14x24sevenSeg) \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/globals.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/globals.h new file mode 100755 index 00000000..5968079e --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/globals.h @@ -0,0 +1,304 @@ +#pragma once + +long g_storeTime = millis(); + +bool g_voltagePinConnnected = false; +bool g_ssbLoaded = false; +bool g_fmStereo = true; + +bool g_cmdVolume = false; +bool g_cmdStep = false; +bool g_cmdBw = false; +bool g_cmdBand = false; +bool g_settingsActive = false; +bool g_sMeterOn = false; +bool g_displayOn = true; +bool g_displayRDS = false; +bool g_rdsSwitchPressed = false; +bool g_seekStop = false; +uint32_t g_lastAdjustmentTime = 0; + +uint8_t g_muteVolume = 0; +int g_currentBFO = 0; + +// Encoder buttons +SimpleButton btn_Bandwidth(BANDWIDTH_BUTTON); +SimpleButton btn_BandUp(BAND_BUTTON); +SimpleButton btn_BandDn(SOFTMUTE_BUTTON); +SimpleButton btn_VolumeUp(VOLUME_BUTTON); +SimpleButton btn_VolumeDn(AVC_BUTTON); +SimpleButton btn_Encoder(ENCODER_BUTTON); +SimpleButton btn_AGC(AGC_BUTTON); +SimpleButton btn_Step(STEP_BUTTON); +SimpleButton btn_Mode(MODE_SWITCH); + +volatile int g_encoderCount = 0; + +//Frequency tracking +uint16_t g_currentFrequency; +uint16_t g_previousFrequency; + +enum SettingType +{ + ZeroAuto, + Num, + Switch, + SwitchAuto +}; + +struct SettingsItem +{ + char name[5]; + int8_t param; + uint8_t type; + void (*manipulateCallback)(int8_t); +}; + +void doAttenuation(int8_t v); +void doSoftMute(int8_t v); +void doBrightness(int8_t v); +void doSSBAVC(int8_t v = 0); +void doAvc(int8_t v); +void doSync(int8_t v = 0); +void doDeEmp(int8_t v = 0); +void doSWUnits(int8_t v = 0); +void doSSBSoftMuteMode(int8_t v = 0); +void doCutoffFilter(int8_t v); +void doCPUSpeed(int8_t v = 0); +#if USE_RDS +void doRDSErrorLevel(int8_t v); +#endif +void doBFOCalibration(int8_t v); +void doUnitsSwitch(int8_t v = 0); +void doScanSwitch(int8_t v = 0); +void doCWSwitch(int8_t v = 0); + +SettingsItem g_Settings[] = +{ + //Page 1 + { "ATT", 0, SettingType::ZeroAuto, doAttenuation }, //Attenuation + { "SM ", 0, SettingType::Num, doSoftMute }, //Soft Mute + { "SVC", 1, SettingType::Switch, doSSBAVC }, //SSB AVC Switch + { "Syn", 0, SettingType::Switch, doSync }, //SSB Sync + { "DeE", 1, SettingType::Switch, doDeEmp }, //FM DeEmphasis (0 - 50, 1 - 75) + { "AVC", 46, SettingType::Num, doAvc }, //Automatic Volume Control + //Page 2 + { "Scr", 80, SettingType::Num, doBrightness }, //Screen Brightness + { "SW ", 0, SettingType::Switch, doSWUnits }, //SW Units + { "SSM", 1, SettingType::Switch, doSSBSoftMuteMode }, //SSB Soft Mute Mode + { "COF", 0, SettingType::SwitchAuto, doCutoffFilter }, //SSB Cutoff Filter + { "CPU", 0, SettingType::Switch, doCPUSpeed }, //CPU Frequency +#if USE_RDS + { "RDS", 1, SettingType::Num, doRDSErrorLevel }, //RDS ErrorLevel +#endif + //Page 3 + { "BFO", 0, SettingType::Num, doBFOCalibration }, //BFO Offset calibration + { "Uni", 1, SettingType::Switch, doUnitsSwitch }, //Show/Hide frequency units + { "Sca", 1, SettingType::Switch, doScanSwitch }, //AM Encoder scan switch + { "CW ", 0, SettingType::Switch, doCWSwitch }, //CW is LSB or USB +}; + +enum SettingsIndex +{ + ATT, + SoftMute, + SVC, + Sync, + DeEmp, + AutoVolControl, + Brightness, + SWUnits, + SSM, + CutoffFilter, + CPUSpeed, +#if USE_RDS + RDSError, +#endif + BFO, + UnitsSwitch, + ScanSwitch, + CWSwitch, + SETTINGS_MAX +}; + +const uint8_t g_SettingsMaxPages = 3; +int8_t g_SettingSelected = 0; +int8_t g_SettingsPage = 1; +bool g_SettingEditing = false; + +//For managing BW +struct Bandwidth +{ + uint8_t idx; //Internal SI473X index + const char* desc; +}; + +int8_t g_bwIndexSSB = 4; +Bandwidth g_bandwidthSSB[] = +{ + { 4, "0.5k" }, + { 5, "1.0k" }, + { 0, "1.2k" }, + { 1, "2.2k" }, + { 2, "3.0k" }, + { 3, "4.0k" } +}; +const uint8_t g_bwSSBMaxIdx = 5; + +int8_t g_bwIndexAM = 4; +const uint8_t g_maxFilterAM = 6; +Bandwidth g_bandwidthAM[] = +{ + { 4, "1.0k" }, // 0 + { 5, "1.8k" }, // 1 + { 3, "2.0k" }, // 2 + { 6, "2.5k" }, // 3 + { 2, "3.0k" }, // 4 - Default + { 1, "4.0k" }, // 5 + { 0, "6.0k" } // 6 +}; + +int8_t g_bwIndexFM = 0; +char* g_bandwidthFM[] = +{ + "AUTO", + "110k", + " 84k", + " 60k", + " 40k" +}; + +int g_tabStep[] = +{ + // AM steps in KHz + 1, + 5, + 9, + 10, + // Large AM steps in KHz + 50, + 100, + 1000, + // SSB steps in Hz + 10, + 25, + 50, + 100, + 500 +}; +uint8_t g_amTotalSteps = 7; +uint8_t g_amTotalStepsSSB = 4; //Prevent large AM steps appear in SSB mode +uint8_t g_ssbTotalSteps = 5; +volatile int8_t g_stepIndex = 3; + +int8_t g_tabStepFM[] = +{ + 5, // 50 KHz + 10, // 100 KHz + 100 // 1 MHz +}; +int8_t g_FMStepIndex = 1; +const int8_t g_lastStepFM = (sizeof(g_tabStepFM) / sizeof(int8_t)) - 1; + +//Band table structures +enum BandType : uint8_t +{ + LW_BAND_TYPE, + MW_BAND_TYPE, + SW_BAND_TYPE, + FM_BAND_TYPE +}; + +struct Band +{ + uint16_t minimumFreq; + uint16_t maximumFreq; + uint16_t currentFreq; + int8_t currentStepIdx; + int8_t bandwidthIdx; // Bandwidth table index (internal table in Si473x controller) +}; + +#if USE_RDS +enum RDSActiveInfo : uint8_t +{ + StationName, + StationInfo, + ProgramInfo +}; +uint8_t g_rdsActiveInfo = RDSActiveInfo::StationName; +char g_rdsPrevLen = 0; +char* g_RDSCells[3]; +#endif + +char _literal_EmptyLine[17] = " "; + +char* bandTags[] = +{ + "LW", + "MW", + "SW", + " ", //It looks better +}; + +Band g_bandList[] = +{ + /* LW */ { LW_LIMIT_LOW, 520, 300, 0, 4 }, + /* MW */ { 520, 1710, 1476, 3, 4 }, + /* SW */ { SW_LIMIT_LOW, SW_LIMIT_HIGH, SW_LIMIT_LOW, 0, 4 }, + /* FM */ { 6400, 10800, 8400, 1, 0 }, +}; + +uint16_t SWSubBands[] = +{ + SW_LIMIT_LOW, // 160 Meter + 3500, // 80 Meter + 4500, + 5600, + 6800, // 40 Meter + 7200, // 41 Meter + 8500, + 10000, // 30 Meter + 11200, + 13400, + 14000, // 20 Meter + 15000, + 17200, + 18000, // 17 Meter + 21000, // 15 Meter + 21400, // 13 Meter + 24890, // 12 Meter + CB_LIMIT_LOW, // CB Band (11 Meter) + CB_LIMIT_HIGH // 10 Meter +}; +const uint8_t g_SWSubBandCount = sizeof(SWSubBands) / sizeof(uint16_t); +const uint8_t g_lastBand = (sizeof(g_bandList) / sizeof(Band)) - 1; +int8_t g_bandIndex = 1; + +// Modulation +enum Modulations : uint8_t +{ + AM, + LSB, + USB, + CW, + FM +}; +volatile uint8_t g_currentMode = FM; +const char* g_bandModeDesc[] = +{ + "AM ", + "LSB", + "USB", + "CW ", + "FM " +}; +volatile uint8_t g_prevMode = FM; +uint8_t g_seekDirection = 1; + +//Special logic for fast and responsive frequency surfing +uint32_t g_lastFreqChange = 0; +bool g_processFreqChange = 0; +uint8_t g_volume = DEFAULT_VOLUME; + +Rotary g_encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B); +SI4735 g_si4735; \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/patch_ssb_compressed.h b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/patch_ssb_compressed.h new file mode 100644 index 00000000..91a1c816 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/patch_ssb_compressed.h @@ -0,0 +1,1118 @@ +#pragma once + +const PROGMEM uint16_t cmd_0x15[] = +{ + 0, 128, 371, 374, 376, 378, 380, 383, 385, 388, 390, 392, 394, 396, 398, 400, + 402, 404, 593, 616, 650, 661, 757, 869, 872, 915, 942, 945, 948, 985, 994, + 999, 1002, 1004, 1007, 1010, 1013, 1015, 1019, 1022, 1040, 1063, 1069, 1074, + 1076, 1078, 1081, 1084, 1088, 1091, 1093, 1097, 1099, 1102, 1104 +}; + +const PROGMEM byte ssb_patch_content[] = +{ + 0x00, 0x03, 0x74, 0x0B, 0xD4, 0x84, 0x60, + 0x6F, 0xAE, 0x6C, 0xF9, 0xBB, 0x84, 0xA2, + 0x65, 0xB1, 0x4B, 0xF6, 0x72, 0x01, 0x1A, + 0x21, 0xE8, 0x5A, 0xD8, 0xD6, 0x04, 0x73, + 0x5E, 0x2C, 0x22, 0x6E, 0xC4, 0xE9, 0xB5, + 0x8B, 0x81, 0x9A, 0x1E, 0xE4, 0x87, 0xC9, + 0x9E, 0x7B, 0x17, 0x52, 0x6D, 0xAD, 0xE3, + 0xE5, 0x8B, 0x04, 0xA7, 0x2E, 0x99, 0x6A, + 0x32, 0xAA, 0x99, 0x97, 0xCC, 0xA6, 0x5B, + 0x15, 0x79, 0x56, 0x48, 0x01, 0x63, 0xB1, + 0xDF, 0x54, 0x27, 0x06, 0x57, 0x06, 0x53, + 0x8C, 0xA6, 0x27, 0xAA, 0x66, 0x89, 0x03, + 0x18, 0x51, 0x7E, 0xFB, 0x67, 0xD3, 0xCE, + 0xDD, 0x3F, 0xFD, 0x3D, 0x1F, 0x47, 0x84, + 0x01, 0xF3, 0x85, 0x6E, 0xF8, 0x3B, 0x79, + 0x2B, 0x0C, 0x90, 0x4B, 0x67, 0x46, 0x7D, + 0x54, 0x88, 0xFD, 0xD6, 0x12, 0xE5, 0xE7, + 0x48, 0xF2, 0x0E, 0xDB, 0x99, 0x4D, 0x71, + 0xA5, 0x40, 0x68, 0xD9, 0xC9, 0x59, 0x39, + 0xE4, 0x75, 0x20, 0x1C, 0xE9, 0xB1, 0xF5, + 0x01, 0x3C, 0x19, 0xBB, 0x60, 0xF7, 0xFA, + 0x6B, 0x1E, 0x68, 0x9F, 0x19, 0xB6, 0xED, + 0x14, 0x0F, 0x56, 0xBC, 0xC1, 0x34, 0xCB, + 0x00, 0x9D, 0x43, 0xD4, 0x0F, 0xF3, 0x62, + 0x9C, 0x9D, 0xF7, 0x8B, 0x9D, 0x53, 0x3A, + 0x8D, 0x2F, 0x3D, 0xDE, 0x3D, 0x38, 0x31, + 0x03, 0x9B, 0xC7, 0xC1, 0x59, 0xE9, 0xD1, + 0x75, 0x88, 0x8D, 0x36, 0x58, 0x86, 0x70, + 0x4E, 0x5B, 0x9E, 0xD0, 0xDB, 0xCB, 0xF7, + 0xEA, 0x1F, 0xDB, 0xA5, 0x3B, 0xB3, 0xBD, + 0x6B, 0xBC, 0xF3, 0x70, 0x65, 0xD5, 0x17, + 0x0F, 0x64, 0x15, 0x24, 0x20, 0xD5, 0xB8, + 0x7B, 0x0D, 0x27, 0x29, 0x85, 0x88, 0x1F, + 0x58, 0x22, 0x57, 0x4F, 0x09, 0xC0, 0x4D, + 0xD6, 0xE4, 0xC6, 0xB5, 0x3A, 0x37, 0x33, + 0x27, 0xBB, 0x6A, 0x14, 0x69, 0x03, 0x1B, + 0xED, 0xA8, 0xC3, 0x43, 0xE8, 0xEA, 0xDE, + 0x41, 0x06, 0x25, 0xC5, 0xC8, 0x3C, 0xBF, + 0x32, 0xB2, 0xA7, 0xDD, 0x22, 0xC2, 0xE9, + 0xCA, 0x84, 0xF2, 0xC0, 0x26, 0x7C, 0x78, + 0x01, 0xF7, 0xD5, 0xC6, 0x05, 0x33, 0xE4, + 0xC2, 0x78, 0xEC, 0x1B, 0xFD, 0xD2, 0x50, + 0x7D, 0x11, 0x29, 0x57, 0x99, 0xB0, 0xF1, + 0x55, 0x66, 0x74, 0xB4, 0x58, 0x23, 0xA0, + 0x6F, 0x2E, 0xD8, 0x9B, 0x08, 0xB3, 0x3E, + 0x53, 0xCF, 0x5E, 0xCC, 0x7A, 0xDF, 0x4E, + 0xED, 0x42, 0x16, 0x6A, 0x91, 0x88, 0x43, + 0x1B, 0x1D, 0x84, 0x5C, 0x52, 0x21, 0x66, + 0x58, 0x24, 0x36, 0x71, 0xC5, 0x6A, 0x0D, + 0x2C, 0x86, 0x9A, 0x23, 0x9D, 0xE0, 0x35, + 0x43, 0xC9, 0x7F, 0x41, 0xBD, 0x82, 0x99, + 0xED, 0x7A, 0x26, 0xB2, 0xD3, 0xA6, 0xCE, + 0x4F, 0x00, 0xE6, 0x87, 0xE3, 0xD7, 0x68, + 0x28, 0x86, 0xF7, 0xCE, 0xD2, 0xB3, 0xD3, + 0x1B, 0x04, 0x02, 0xB9, 0x78, 0xB3, 0xAD, + 0xE0, 0x3C, 0x53, 0xA6, 0x8A, 0x4A, 0x22, + 0xBE, 0x1F, 0xCE, 0xB1, 0x76, 0xF2, 0x2F, + 0x81, 0xA8, 0xC1, 0x78, 0x72, 0xB0, 0x15, + 0x65, 0x19, 0xE3, 0x32, 0xA7, 0x4D, 0x0C, + 0xD3, 0x00, 0xB9, 0xC3, 0x27, 0xBE, 0x74, + 0x4D, 0xD1, 0xE1, 0x0C, 0x36, 0xDD, 0x23, + 0xFB, 0xA7, 0x22, 0xD3, 0x44, 0xCA, 0xB3, + 0x08, 0x1B, 0xB4, 0x0D, 0x4B, 0xDF, 0x1A, + 0xA6, 0x90, 0x32, 0x92, 0x5C, 0x1A, 0xA7, + 0xCD, 0x85, 0x2E, 0x35, 0x11, 0x72, 0xF8, + 0x21, 0x35, 0x7D, 0x3F, 0xDB, 0x29, 0x8D, + 0x30, 0xB3, 0x4F, 0x45, 0x14, 0x77, 0xCF, + 0x99, 0x2F, 0xA3, 0x90, 0x81, 0xB0, 0xAE, + 0xE8, 0x45, 0xE6, 0x3A, 0xA9, 0xA2, 0x62, + 0xB7, 0xDB, 0xBE, 0x25, 0x39, 0xA5, 0xFC, + 0xEA, 0xC5, 0xBC, 0x3A, 0xEC, 0x90, 0xAE, + 0xA6, 0xFB, 0x12, 0xAA, 0xC7, 0x62, 0x22, + 0xAF, 0xA9, 0x84, 0xB9, 0x64, 0xB8, 0xFB, + 0x8D, 0xC1, 0x1C, 0xC3, 0xD0, 0x87, 0x6D, + 0x5C, 0xB4, 0xFE, 0x3F, 0xA4, 0xB8, 0xD8, + 0x89, 0xF4, 0x01, 0x59, 0xBB, 0x04, 0xA5, + 0x43, 0x44, 0x1A, 0xCB, 0x41, 0x30, 0xF7, + 0x5B, 0x9A, 0xEE, 0x28, 0xF4, 0x4C, 0x71, + 0x99, 0xCA, 0x4B, 0x7F, 0x2A, 0x13, 0xF0, + 0x4B, 0x54, 0xB3, 0xFC, 0x90, 0xED, 0xBE, + 0x48, 0x3F, 0xF1, 0x5E, 0x0D, 0xA1, 0xBB, + 0x1E, 0x59, 0x16, 0x15, 0xF0, 0x4B, 0x50, + 0x4C, 0x96, 0x86, 0x1B, 0xA9, 0xCC, 0xC2, + 0x69, 0x4E, 0x10, 0x03, 0xF0, 0xE9, 0x53, + 0xE5, 0xE2, 0xBB, 0x45, 0xE2, 0x8D, 0xAE, + 0x40, 0x71, 0x08, 0x86, 0x4D, 0x08, 0xF4, + 0x96, 0x90, 0x58, 0x53, 0x40, 0xA5, 0x8B, + 0x5B, 0x3C, 0xAD, 0x47, 0xB5, 0xD7, 0xE2, + 0x6A, 0x13, 0x78, 0xD4, 0xDC, 0xC9, 0xEC, + 0x0E, 0x93, 0xF0, 0xC0, 0x1D, 0x95, 0x36, + 0x76, 0xA4, 0x53, 0x85, 0xC9, 0xAC, 0xE4, + 0x71, 0x8D, 0xAF, 0x68, 0x61, 0x20, 0x9D, + 0xFF, 0x45, 0x35, 0x6E, 0x04, 0xD8, 0x27, + 0x32, 0xC3, 0xDA, 0xEE, 0xA2, 0x66, 0x04, + 0xB7, 0x21, 0x06, 0x52, 0x7F, 0x39, 0x8F, + 0xFD, 0xEE, 0x90, 0x74, 0xB6, 0x4F, 0xB6, + 0x24, 0x0E, 0x12, 0x48, 0xF0, 0xB2, 0x30, + 0xF2, 0xA5, 0x4C, 0x91, 0x52, 0xE3, 0xC6, + 0x48, 0x7B, 0x38, 0x96, 0x23, 0x9E, 0xD7, + 0x1B, 0x95, 0x15, 0x3A, 0xC9, 0x8B, 0x6F, + 0x63, 0x7D, 0x1D, 0xC9, 0xEA, 0x96, 0xE6, + 0x01, 0xD7, 0x85, 0xC0, 0xEA, 0x3D, 0xCA, + 0xD3, 0xB7, 0xFB, 0x07, 0x54, 0x3A, 0x00, + 0x31, 0x3D, 0x74, 0xE4, 0x12, 0x5B, 0xA2, + 0x38, 0x33, 0x5B, 0xBF, 0x1D, 0xDC, 0x4E, + 0xE8, 0x85, 0x10, 0x77, 0xAB, 0xEA, 0x65, + 0x79, 0xB0, 0xAA, 0x30, 0x61, 0xC6, 0xF6, + 0x3B, 0xCC, 0x95, 0xFC, 0x83, 0x7F, 0x8C, + 0x7B, 0xB5, 0x52, 0x05, 0xEB, 0xF7, 0x21, + 0xDE, 0x89, 0xB6, 0x6D, 0xEE, 0x31, 0x77, + 0x13, 0xAA, 0x93, 0x55, 0x96, 0x08, 0x6E, + 0x1C, 0x82, 0xB5, 0x7E, 0x95, 0xB9, 0x94, + 0xDD, 0x68, 0x42, 0x5C, 0x3A, 0x00, 0xC3, + 0x7B, 0x6F, 0x60, 0x6A, 0x2C, 0x07, 0xD7, + 0x6C, 0x82, 0xFB, 0xB2, 0x81, 0x25, 0x69, + 0xD6, 0x72, 0x0A, 0xD2, 0xE8, 0x50, 0x05, + 0x88, 0x49, 0x1B, 0x63, 0x72, 0x99, 0x6C, + 0xE3, 0x7A, 0xAE, 0xB0, 0x1B, 0x9E, 0xD4, + 0x25, 0x54, 0x25, 0x8A, 0x90, 0x17, 0x97, + 0xDF, 0x39, 0xB8, 0x7C, 0xEA, 0xF0, 0x82, + 0xDD, 0x25, 0xD2, 0xAD, 0xE7, 0xF6, 0x36, + 0x23, 0x84, 0xA9, 0xC9, 0x6A, 0x53, 0xF4, + 0x82, 0x2C, 0x1D, 0xEA, 0x06, 0xC6, 0x4F, + 0x70, 0x54, 0x37, 0x80, 0x2A, 0x6B, 0x63, + 0xDB, 0xCC, 0x86, 0xE6, 0x8C, 0x7F, 0x27, + 0x8C, 0x72, 0x54, 0x73, 0x9E, 0x1B, 0xD6, + 0x4C, 0xFA, 0x05, 0x82, 0x80, 0xD7, 0xB7, + 0x11, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x98, 0x69, 0x15, 0x1C, 0xFC, + 0x86, 0x87, 0x74, 0x84, 0x5B, 0xD4, 0x0F, + 0xDB, 0xD7, 0x27, 0xC5, 0x4A, 0xB2, 0x8F, + 0x29, 0x7A, 0x4A, 0x23, 0x0A, 0xB8, 0x07, + 0xD0, 0x34, 0x70, 0x81, 0x8E, 0x53, 0xEF, + 0x43, 0x16, 0x3F, 0xA8, 0xA7, 0x9E, 0x5A, + 0x77, 0xC0, 0x91, 0xB7, 0x89, 0x15, 0x52, + 0x48, 0xC0, 0xB7, 0x62, 0xAB, 0xE1, 0x74, + 0x64, 0x10, 0xAA, 0x09, 0x6B, 0x97, 0x74, + 0x21, 0x79, 0x35, 0xA1, 0x97, 0x25, 0xFC, + 0x78, 0xC1, 0xEC, 0x6E, 0x87, 0x77, 0x40, + 0x87, 0x1A, 0x74, 0x82, 0x81, 0x56, 0xDD, + 0xEC, 0x37, 0xAD, 0xAF, 0x03, 0x31, 0xAC, + 0x47, 0x85, 0x74, 0xC0, 0x2A, 0x45, 0x9D, + 0xF4, 0x12, 0x0C, 0xF9, 0x86, 0xB0, 0x4A, + 0x16, 0xD8, 0x11, 0x6C, 0x46, 0xC4, 0x4A, + 0x67, 0x98, 0x8B, 0x1B, 0x42, 0xC2, 0xBE, + 0xBB, 0x0E, 0x48, 0x84, 0xF7, 0x08, 0xA6, + 0xC3, 0x8F, 0x8E, 0x8D, 0xAB, 0xC2, 0x9B, + 0x5D, 0x5C, 0x60, 0x28, 0x57, 0x8F, 0x0E, + 0xAD, 0x2E, 0xC1, 0x5E, 0x95, 0x29, 0x9A, + 0xF4, 0x8A, 0x27, 0xD4, 0x41, 0xBB, 0x4A, + 0xAC, 0x5D, 0xB2, 0x93, 0x26, 0xFD, 0x87, + 0x81, 0x3F, 0x27, 0x35, 0xA6, 0x42, 0x2C, + 0x15, 0x38, 0xAD, 0xF8, 0x02, 0x6F, 0x94, + 0xDA, 0x08, 0xEE, 0x19, 0xD8, 0x90, 0xFA, + 0x89, 0xC3, 0xAD, 0x89, 0xFE, 0xC6, 0x02, + 0x46, 0x81, 0x1D, 0x0C, 0x8D, 0x78, 0x2F, + 0xEE, 0xD3, 0xED, 0x6C, 0x7B, 0x95, 0x49, + 0x56, 0x87, 0xF4, 0xE2, 0x63, 0xDA, 0x39, + 0xCE, 0x68, 0x16, 0x18, 0xED, 0x98, 0xEE, + 0x86, 0xF3, 0xB3, 0xC1, 0x12, 0x1A, 0x51, + 0x9B, 0x57, 0x1D, 0x19, 0x9E, 0x98, 0x7E, + 0xD2, 0xFD, 0x3D, 0x2B, 0x59, 0x15, 0x46, + 0x74, 0x57, 0x79, 0x14, 0x81, 0x71, 0x89, + 0xD9, 0x22, 0xDD, 0x92, 0x10, 0xD9, 0x89, + 0x91, 0xDF, 0x67, 0x00, 0x6E, 0x53, 0xAF, + 0xDA, 0xB5, 0x28, 0x30, 0x1E, 0x47, 0xE6, + 0xB7, 0x05, 0x2D, 0x22, 0xCF, 0x7F, 0x6A, + 0xCD, 0x88, 0x90, 0x6B, 0x8C, 0x16, 0x36, + 0x88, 0x07, 0xA1, 0xB7, 0xAF, 0xF3, 0x31, + 0xC4, 0xDB, 0x76, 0x8B, 0x96, 0x9E, 0xF0, + 0x17, 0xA7, 0x3F, 0x84, 0x78, 0x91, 0xA8, + 0xCD, 0x3F, 0x8F, 0xA5, 0xDD, 0x52, 0x14, + 0xF9, 0x39, 0x5D, 0x0B, 0x56, 0x8E, 0x1C, + 0x5A, 0x5B, 0x89, 0xEE, 0x80, 0x9E, 0x33, + 0xBD, 0xB1, 0x45, 0x15, 0x42, 0x34, 0x59, + 0x78, 0x20, 0xFC, 0x2D, 0xDB, 0xC9, 0x2E, + 0x0F, 0x35, 0x26, 0xCF, 0x23, 0x9B, 0x3E, + 0x1D, 0x9F, 0x0F, 0x26, 0x0E, 0xB7, 0xB5, + 0x4E, 0xAC, 0xA2, 0x86, 0xD5, 0xDA, 0x36, + 0x04, 0x5A, 0x73, 0x46, 0x6A, 0x88, 0xC5, + 0x2A, 0xA0, 0x83, 0xE8, 0x8B, 0x5F, 0x3C, + 0xAF, 0x6A, 0x44, 0x4E, 0x75, 0x9B, 0x8F, + 0x93, 0x37, 0x35, 0x38, 0xBD, 0x02, 0x9A, + 0x1D, 0x84, 0x29, 0x64, 0x25, 0x97, 0xE0, + 0x7C, 0x2A, 0xB6, 0xCB, 0xC1, 0x66, 0x51, + 0x3C, 0xBC, 0x3B, 0xF3, 0x55, 0x2B, 0xB7, + 0xBD, 0x81, 0xEC, 0xF3, 0x49, 0xD5, 0xF8, + 0x03, 0xE0, 0xBD, 0xC6, 0x0C, 0xEE, 0xC0, + 0x19, 0x08, 0xFB, 0x4F, 0x8C, 0x94, 0x08, + 0x0E, 0xA5, 0x30, 0x37, 0xF0, 0xB8, 0x18, + 0x93, 0x73, 0x00, 0xBB, 0x52, 0x52, 0xB4, + 0x98, 0x0D, 0x4B, 0x45, 0xB9, 0x89, 0x0A, + 0x34, 0xE9, 0x1C, 0x24, 0x5A, 0x8F, 0xA8, + 0x9D, 0xC2, 0xD9, 0x27, 0xA9, 0xEE, 0xBF, + 0xC0, 0x80, 0x90, 0xBC, 0x2A, 0x93, 0x43, + 0xFB, 0x0A, 0xB3, 0x96, 0xD1, 0xAE, 0x13, + 0x41, 0x01, 0x68, 0xB9, 0x2C, 0x53, 0x53, + 0x7C, 0x00, 0xA5, 0x84, 0x41, 0xD6, 0x98, + 0x88, 0x98, 0xFE, 0xDE, 0xF7, 0xE2, 0x37, + 0x43, 0x62, 0x0E, 0x93, 0x83, 0xEE, 0x03, + 0x0A, 0x8D, 0xCA, 0x87, 0x59, 0x36, 0x6F, + 0x61, 0xF7, 0xF4, 0xD6, 0xAB, 0x8B, 0x0F, + 0x9C, 0x83, 0x96, 0xA4, 0x6C, 0x46, 0x43, + 0x80, 0x6B, 0xEA, 0x50, 0xDC, 0xD8, 0xCE, + 0xD4, 0xF4, 0xAE, 0x5B, 0xC6, 0x01, 0x43, + 0x85, 0x71, 0x8D, 0x0C, 0x39, 0x5E, 0xE4, + 0x3B, 0x2B, 0xC4, 0x88, 0xB2, 0xC5, 0x54, + 0x96, 0x0E, 0x87, 0x9A, 0x50, 0x93, 0xD6, + 0x9C, 0x91, 0xCA, 0x09, 0x64, 0x8B, 0x23, + 0xC4, 0x29, 0x44, 0x1A, 0x67, 0xE1, 0xDA, + 0xC7, 0x1B, 0x25, 0x17, 0x02, 0x2C, 0x8F, + 0x2A, 0x13, 0x3A, 0xCF, 0xD6, 0x65, 0x31, + 0x2B, 0x10, 0xF5, 0xB5, 0x96, 0xDF, 0x01, + 0x99, 0x7B, 0xAD, 0xD6, 0xB4, 0xC0, 0x35, + 0xA3, 0x48, 0xE3, 0xB0, 0x9E, 0xFE, 0x33, + 0x10, 0x1A, 0x09, 0xAE, 0x3C, 0x0A, 0xA1, + 0x72, 0x8F, 0x00, 0x51, 0xE1, 0x78, 0x23, + 0xF5, 0xE2, 0xBF, 0xA6, 0x46, 0x28, 0xB3, + 0x4B, 0x24, 0xB1, 0xA1, 0xDE, 0xB1, 0xC4, + 0x48, 0x6F, 0xED, 0x5A, 0xD4, 0x61, 0xF0, + 0xDB, 0xE1, 0x08, 0x5C, 0x64, 0xC0, 0xCA, + 0xE9, 0xA9, 0xDE, 0xDE, 0xEC, 0xC0, 0x68, + 0x5B, 0x6D, 0xBD, 0x43, 0x82, 0x8C, 0xCA, + 0x69, 0xB1, 0xA9, 0xC8, 0x71, 0x0D, 0x5D, + 0x6D, 0xA5, 0xAB, 0x1A, 0x67, 0x94, 0xEE, + 0x05, 0x93, 0xE3, 0x3F, 0xE8, 0x1A, 0xE6, + 0x0F, 0x7F, 0x79, 0xF2, 0xCB, 0xC5, 0x37, + 0x57, 0xDF, 0xFC, 0x4F, 0xB4, 0x60, 0xC7, + 0x97, 0x3C, 0x61, 0xB0, 0x0C, 0x46, 0x64, + 0xF4, 0x43, 0x92, 0xD2, 0xA5, 0xF0, 0xF3, + 0xB7, 0xDF, 0xAB, 0x20, 0xB1, 0xF5, 0x30, + 0x2A, 0xB3, 0xDC, 0x4B, 0x39, 0xB7, 0x85, + 0x3E, 0x1E, 0x1B, 0x94, 0x8C, 0x26, 0x28, + 0x80, 0x45, 0xD6, 0x01, 0x36, 0xD0, 0xEC, + 0x93, 0x46, 0x88, 0xD3, 0xD5, 0x89, 0x5D, + 0x7A, 0x82, 0x21, 0x3E, 0xC2, 0x40, 0xE3, + 0xB4, 0xDE, 0xF5, 0xA5, 0x69, 0xD5, 0x69, + 0x49, 0x5C, 0x89, 0xCD, 0xAE, 0x13, 0x70, + 0x70, 0xED, 0x69, 0x0C, 0xEC, 0xC6, 0xFE, + 0x75, 0xEB, 0x4E, 0xD6, 0xB3, 0xC8, 0x60, + 0xFB, 0x34, 0x1E, 0xF8, 0x64, 0x92, 0xD9, + 0x29, 0xA8, 0x13, 0x38, 0x3D, 0x79, 0xC4, + 0xBF, 0x21, 0x37, 0x9D, 0xE3, 0x4F, 0x21, + 0xE5, 0x3F, 0x2C, 0x5C, 0x5F, 0x41, 0xFF, + 0x14, 0x87, 0x91, 0xE0, 0x82, 0x04, 0x0B, + 0x1E, 0x55, 0x53, 0xDC, 0x56, 0x40, 0xA4, + 0x35, 0x78, 0xE2, 0x9B, 0x49, 0x22, 0x2E, + 0x09, 0xCD, 0x1F, 0x6F, 0x94, 0x83, 0xEE, + 0xEA, 0x8B, 0xC4, 0xC2, 0x10, 0xE0, 0x80, + 0xD9, 0xA5, 0x5F, 0x53, 0x34, 0x9C, 0x8C, + 0xEA, 0x95, 0x64, 0x87, 0x94, 0xD6, 0x32, + 0x1C, 0xEB, 0x93, 0xBF, 0xE3, 0x55, 0x83, + 0xC2, 0xF6, 0xFC, 0xC8, 0x85, 0xB2, 0x94, + 0x5C, 0x42, 0x5F, 0x6B, 0x8E, 0x3B, 0x97, + 0x79, 0x74, 0x7E, 0xE3, 0x5C, 0xC2, 0x63, + 0x1A, 0x22, 0xB1, 0x7F, 0x94, 0xCB, 0xE7, + 0xB7, 0xFF, 0x0E, 0x37, 0x95, 0xAF, 0x10, + 0x53, 0x17, 0x88, 0x12, 0x72, 0xDD, 0xCB, + 0xAB, 0x4B, 0x6B, 0x68, 0x24, 0xAC, 0xC9, + 0x5C, 0x72, 0x09, 0x4F, 0xF1, 0xFB, 0x1D, + 0xE7, 0xA2, 0x30, 0xE9, 0x20, 0x4B, 0xDA, + 0x70, 0x7C, 0x9E, 0xE2, 0x34, 0xEB, 0x6A, + 0x35, 0xC2, 0x33, 0xD7, 0x9D, 0x07, 0xB1, + 0xEF, 0xF6, 0x75, 0xF5, 0xFD, 0xAD, 0xC7, + 0x4B, 0xD0, 0xB7, 0xDD, 0xAE, 0x1E, 0x8D, + 0x1E, 0xC0, 0x39, 0x9C, 0xA7, 0x1C, 0xC5, + 0xCC, 0x39, 0x50, 0xC6, 0x6B, 0xC6, 0xDA, + 0x37, 0xC7, 0xE1, 0x20, 0xEB, 0x74, 0xA5, + 0x3D, 0xC0, 0x08, 0xFA, 0xC2, 0xDF, 0xF2, + 0x9E, 0x43, 0x2B, 0xF0, 0x77, 0x50, 0x49, + 0x8C, 0xC3, 0x2F, 0x68, 0xF3, 0x2A, 0x9B, + 0x83, 0x1A, 0xDC, 0x05, 0xD8, 0xB7, 0x7C, + 0x3C, 0x61, 0x11, 0x76, 0xEC, 0x2D, 0xFE, + 0x86, 0x08, 0x87, 0x15, 0xA3, 0xC0, 0x1C, + 0xB4, 0x6A, 0x8D, 0x9E, 0xBF, 0x9A, 0xD5, + 0xC8, 0x5B, 0x05, 0x18, 0x10, 0x15, 0xFB, + 0xC6, 0x3C, 0xEE, 0xC5, 0x52, 0x4B, 0x31, + 0x36, 0x07, 0xB9, 0x4C, 0x81, 0xEA, 0x83, + 0xD7, 0x82, 0xFE, 0xFC, 0x43, 0x59, 0x4E, + 0x20, 0xBD, 0xD3, 0x65, 0xCF, 0x1F, 0x5C, + 0x71, 0x1F, 0xBF, 0x26, 0xEC, 0x44, 0xEB, + 0x01, 0x74, 0x82, 0x3C, 0x8E, 0x60, 0xDF, + 0x47, 0x90, 0x31, 0x17, 0x7E, 0x16, 0xC0, + 0xEC, 0x1E, 0xC3, 0x3A, 0x7A, 0x59, 0x21, + 0xA2, 0x9B, 0x8E, 0x87, 0x25, 0xD3, 0xFE, + 0x85, 0xDD, 0x6D, 0xE6, 0xD8, 0xCA, 0x74, + 0xBA, 0xDB, 0x42, 0x30, 0x96, 0x3D, 0x86, + 0x22, 0x86, 0x6F, 0xDE, 0x37, 0x1A, 0x93, + 0x2F, 0x03, 0x2E, 0xE1, 0x6E, 0x16, 0x62, + 0xC1, 0xE1, 0x2D, 0xFE, 0xCB, 0xA8, 0xDF, + 0x64, 0x05, 0xEF, 0x23, 0xBC, 0x71, 0x61, + 0x7B, 0x1B, 0x60, 0x22, 0x27, 0xBD, 0xC6, + 0xA3, 0x72, 0x07, 0xAA, 0x09, 0xB0, 0x52, + 0xF2, 0x19, 0x15, 0x4D, 0xBE, 0xAF, 0x26, + 0x3B, 0x4D, 0x62, 0xA6, 0x3C, 0x41, 0x07, + 0x7A, 0x34, 0x25, 0x3C, 0xD6, 0xCD, 0xB6, + 0x55, 0x47, 0x87, 0x1E, 0xCB, 0xCF, 0xCE, + 0xD2, 0x0D, 0x95, 0xC0, 0xD8, 0x72, 0xA6, + 0xE0, 0x33, 0x24, 0x26, 0x51, 0x7E, 0x71, + 0xC7, 0x53, 0x3F, 0xD4, 0x67, 0x73, 0x52, + 0xCB, 0x78, 0x5A, 0x00, 0x11, 0x15, 0x71, + 0x5F, 0x8D, 0x3C, 0xCF, 0x40, 0xCD, 0xD9, + 0x74, 0x30, 0xD9, 0xA1, 0x3F, 0xC6, 0x58, + 0xBC, 0xBE, 0x78, 0x18, 0x31, 0x9A, 0x39, + 0x60, 0xBB, 0x8D, 0x7B, 0x34, 0x95, 0xA5, + 0xF1, 0x54, 0x3B, 0xB9, 0x85, 0x99, 0xAF, + 0xC4, 0x45, 0xEB, 0x64, 0xA7, 0x6E, 0x59, + 0x60, 0xEB, 0x1E, 0x7A, 0xFB, 0xFD, 0x92, + 0x43, 0x23, 0xF1, 0x27, 0xAC, 0x66, 0x12, + 0x66, 0x28, 0x8A, 0x5C, 0xF6, 0xC6, 0x3F, + 0x76, 0x04, 0xE8, 0x09, 0x27, 0xED, 0x05, + 0xAA, 0x26, 0xE5, 0xB4, 0xAD, 0x40, 0xBA, + 0xF7, 0xF4, 0x84, 0xCB, 0x7F, 0x07, 0x10, + 0x8D, 0xB6, 0xC7, 0xD4, 0x77, 0xEA, 0xBA, + 0x96, 0x2A, 0x98, 0xF0, 0x56, 0xD7, 0xFF, + 0x50, 0x4D, 0x59, 0x79, 0x3D, 0x41, 0xE0, + 0xFA, 0xA9, 0x9D, 0x3E, 0xA6, 0xD9, 0x79, + 0xC8, 0x0D, 0xD1, 0x56, 0xC5, 0xAD, 0xD7, + 0xFB, 0x70, 0x3C, 0xBD, 0xF3, 0xE9, 0x5D, + 0x1C, 0x53, 0x93, 0x33, 0x1D, 0xF8, 0x1B, + 0xEE, 0x9F, 0xB7, 0x50, 0xAC, 0x8F, 0x48, + 0x59, 0x61, 0xC9, 0x85, 0x9E, 0xC8, 0xB0, + 0xD2, 0x74, 0x39, 0xDF, 0x33, 0xF5, 0x28, + 0xE0, 0x63, 0x11, 0xA7, 0xC4, 0x05, 0xBD, + 0x3A, 0xD9, 0x34, 0xCA, 0xF9, 0xAA, 0xE2, + 0x00, 0xE6, 0xE6, 0x38, 0x57, 0x3E, 0xEC, + 0x19, 0x7C, 0x0D, 0x27, 0x45, 0xC5, 0x52, + 0xC4, 0xFF, 0xF4, 0x69, 0xC5, 0xF5, 0x73, + 0x73, 0x4F, 0xE3, 0x07, 0xC7, 0xCF, 0x38, + 0x23, 0x5B, 0x9C, 0x99, 0xA2, 0x6A, 0x72, + 0xB0, 0x35, 0x39, 0xC1, 0x0A, 0xC7, 0x1C, + 0x1A, 0x30, 0x60, 0x18, 0x1D, 0x2B, 0xF0, + 0xC8, 0xF1, 0x79, 0xA3, 0x94, 0xE3, 0x36, + 0x35, 0x93, 0x69, 0x05, 0x91, 0x07, 0x12, + 0x60, 0x44, 0xFD, 0xB6, 0xC6, 0xFF, 0xE0, + 0xCF, 0xA2, 0xCF, 0xC2, 0xA3, 0x58, 0xAC, + 0x32, 0xE3, 0x2E, 0xEC, 0x0D, 0x9D, 0x1B, + 0xFB, 0x05, 0x40, 0xBA, 0x67, 0x8E, 0xCB, + 0x38, 0xD7, 0x60, 0x9C, 0xFE, 0x9E, 0xA1, + 0x70, 0xCC, 0x40, 0x72, 0x8C, 0x8C, 0x50, + 0x71, 0x85, 0xDD, 0x84, 0x92, 0x5C, 0xEE, + 0x44, 0xB6, 0xE0, 0x80, 0x15, 0x32, 0xDD, + 0x1B, 0x4B, 0x8C, 0x6E, 0x8E, 0x39, 0x97, + 0xA5, 0xAF, 0xC6, 0xF4, 0x29, 0x85, 0xD3, + 0x85, 0x93, 0x85, 0xC8, 0x54, 0xE4, 0x59, + 0x1D, 0x4E, 0xA0, 0xFE, 0xC5, 0x07, 0xC1, + 0xF7, 0x32, 0x52, 0xF9, 0x4D, 0x45, 0x85, + 0xFC, 0xA0, 0xFA, 0x37, 0xB5, 0x9D, 0x47, + 0xE8, 0x52, 0x23, 0x8D, 0x92, 0x87, 0x43, + 0xB4, 0xB8, 0x4F, 0x95, 0x1B, 0x1E, 0x4F, + 0xE5, 0x45, 0xBF, 0xCE, 0xC8, 0x0C, 0xB2, + 0x7D, 0xE7, 0xD9, 0x2E, 0x88, 0xB7, 0x2A, + 0x61, 0xE1, 0x2D, 0xC4, 0x21, 0x2E, 0xB8, + 0x4D, 0xFD, 0x21, 0x87, 0x30, 0x25, 0x11, + 0xEC, 0x1F, 0x29, 0x94, 0x31, 0xD2, 0x92, + 0x85, 0x38, 0x17, 0xDA, 0x63, 0x4D, 0x8C, + 0xB4, 0x06, 0x55, 0x64, 0xB7, 0x6C, 0xFC, + 0xC2, 0x8D, 0x5D, 0x97, 0x86, 0x09, 0xD3, + 0x43, 0x69, 0xE8, 0x35, 0x77, 0xCC, 0xAF, + 0x91, 0x36, 0x3C, 0x7C, 0xA3, 0x6E, 0x4E, + 0x4A, 0xA2, 0xC9, 0x41, 0x50, 0xEA, 0xAB, + 0xDF, 0x87, 0x04, 0x70, 0x4F, 0x16, 0xF5, + 0xA0, 0x38, 0xF5, 0x60, 0x16, 0xCD, 0x0C, + 0xE5, 0xC7, 0xE3, 0xAD, 0x00, 0x12, 0x6B, + 0x12, 0x85, 0x3C, 0x5C, 0x7F, 0x76, 0xED, + 0x49, 0xF2, 0x9D, 0x8F, 0xDF, 0xAB, 0xE1, + 0xDD, 0x87, 0x01, 0xED, 0x2C, 0x1B, 0x4B, + 0x55, 0x11, 0x7F, 0xC3, 0x92, 0xD3, 0x91, + 0xC1, 0xD8, 0xA7, 0x37, 0xC7, 0xE5, 0x5B, + 0x48, 0x2B, 0xED, 0x31, 0xDE, 0xCE, 0xFB, + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xE3, 0x51, 0x84, 0x00, + 0x0B, 0x4D, 0x26, 0x73, 0x11, 0x8B, 0x1E, + 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x73, 0xE0, 0x72, + 0xFF, 0xE6, 0x59, 0xC9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x48, 0xBF, 0xAB, 0xF3, + 0x99, 0x87, 0x7C, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0xFB, 0x33, 0xB1, 0x63, + 0x61, 0x61, 0x12, 0xAD, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xAA, 0x30, 0xAC, 0x17, + 0x4D, 0x30, 0x86, 0xBF, 0x67, 0x13, 0xF1, + 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x1B, 0x48, 0x42, 0x23, + 0x84, 0xBB, 0x7F, 0xB4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xA6, 0x9F, 0x43, 0x78, + 0x5C, 0xA1, 0x4E, 0x8B, 0xA6, 0xE6, 0x7C, + 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xF7, 0x31, 0x5D, 0x27, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x0B, 0x57, 0x0B, 0x79, + 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x44, 0x33, 0xBB, 0xB4, + 0x5B, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xAB, 0xE3, 0x01, 0xC2, + 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x7A, 0x07, 0x9E, 0xAD, + 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0xF1, 0xEE, 0xF7, 0xD8, + 0x29, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x2C, 0x35, 0x54, 0x3A, + 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xC9, 0x3D, 0x66, 0x6A, + 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x24, 0x7A, 0xF7, 0x1C, 0x24, + 0x40, 0xEB, 0xA2, 0x4D, 0xC2, 0x66, 0xC8, + 0x37, 0x2C, 0x03, 0x0F, 0xF3, 0x01, 0x06, + 0x6D, 0x9A, 0x42, 0xF0, 0x94, 0x8F, 0xED, + 0xBF, 0xE3, 0x28, 0x3B, 0x72, 0x69, 0x37, + 0x75, 0xB4, 0xA0, 0x9A, 0x6E, 0x31, 0xFD, + 0x66, 0x5B, 0xCB, 0xF3, 0xBA, 0x8C, 0xA4, + 0x31, 0xFF, 0xBA, 0x7C, 0x24, 0x54, 0xD3, + 0xC6, 0x63, 0x89, 0x93, 0xE4, 0xED, 0xDA, + 0x58, 0x47, 0x4E, 0xB5, 0x8F, 0xD7, 0x93, + 0xFD, 0xAF, 0x31, 0xEA, 0xB4, 0xEC, 0xC9, + 0x05, 0x7D, 0x2B, 0x15, 0x96, 0xC1, 0x00, + 0x7E, 0x80, 0xE6, 0xF7, 0x47, 0xCD, 0x6A, + 0x77, 0xF1, 0x0E, 0x59, 0x13, 0xAA, 0x5C, + 0x83, 0x88, 0x70, 0xDC, 0xC4, 0x52, 0x52, + 0x20, 0xB6, 0x69, 0x2D, 0x6C, 0x6C, 0x8B, + 0x66, 0x2A, 0xCD, 0x5A, 0x41, 0x30, 0xD6, + 0xFB, 0x7B, 0xA0, 0xEC, 0x7A, 0x22, 0x88, + 0x7C, 0x71, 0x6E, 0xAF, 0x62, 0x06, 0xF4, + 0xA8, 0xC6, 0x88, 0x85, 0x76, 0x36, 0xC5, + 0xC0, 0xD8, 0x67, 0x96, 0x7C, 0xE8, 0x4D, + 0xD4, 0xFB, 0xA9, 0x6F, 0x0F, 0xB2, 0xA7, + 0xA3, 0x19, 0x6A, 0x30, 0xFD, 0x6F, 0xFC, + 0x2B, 0xCE, 0xA4, 0x1B, 0x8E, 0x57, 0x48, + 0x51, 0xC6, 0x98, 0x0F, 0xE5, 0x08, 0x7B, + 0x4D, 0xFC, 0x59, 0xDD, 0xCF, 0x13, 0x1B, + 0x31, 0x0C, 0x5E, 0x3B, 0x98, 0xE1, 0xF4, + 0xC3, 0x41, 0xD7, 0xA2, 0x64, 0x5E, 0x16, + 0xF9, 0xEB, 0xAE, 0x20, 0xE9, 0x5D, 0x54, + 0xBD, 0x99, 0xD3, 0xA7, 0xC1, 0xD9, 0xE5, + 0x32, 0x22, 0xD6, 0x9F, 0x4F, 0xDD, 0x6B, + 0x3C, 0x6D, 0xE9, 0x4D, 0x96, 0x52, 0x11, + 0xDA, 0x23, 0x86, 0xBE, 0x01, 0xC4, 0x51, + 0xC2, 0x9C, 0xC6, 0xF5, 0x2F, 0x1F, 0xD0, + 0xF1, 0xF5, 0x57, 0x82, 0xFE, 0xCB, 0xFB, + 0x4A, 0xDF, 0x1D, 0x00, 0x46, 0xB8, 0xA7, + 0x3A, 0x56, 0x66, 0x08, 0x71, 0x4E, 0x47, + 0xF8, 0x27, 0x26, 0x5F, 0x6C, 0xD8, 0xC3, + 0x05, 0x99, 0x0A, 0x75, 0x8F, 0x4F, 0xC1, + 0x88, 0x72, 0xA0, 0x5A, 0x13, 0x08, 0xA3, + 0x82, 0x84, 0x2E, 0xCE, 0xB5, 0xA3, 0x46, + 0x05, 0xD6, 0x6A, 0x78, 0x56, 0x44, 0x23, + 0x86, 0xF2, 0xC9, 0x63, 0x08, 0x8B, 0xBD, + 0xEE, 0x56, 0xD4, 0x36, 0x1F, 0x37, 0x53, + 0x18, 0xE7, 0xDA, 0x1C, 0xFA, 0xD0, 0x05, + 0xD5, 0x5F, 0xE7, 0xB7, 0xAD, 0x36, 0x2B, + 0xCB, 0x06, 0xC0, 0x53, 0xF4, 0x32, 0xA8, + 0x75, 0x6F, 0xE3, 0xC9, 0xA1, 0xB0, 0xA6, + 0xE6, 0xC2, 0x41, 0x0F, 0x5D, 0x2E, 0x0A, + 0x64, 0x50, 0x46, 0x92, 0x49, 0x5B, 0x2D, + 0x00, 0x2E, 0xB5, 0x21, 0x3F, 0x0B, 0x4A, + 0x71, 0x96, 0x88, 0x84, 0x4C, 0x98, 0xAA, + 0x28, 0x23, 0xEB, 0xEF, 0x45, 0x5C, 0x62, + 0x78, 0xD6, 0xE4, 0xFB, 0x6D, 0xC3, 0xD9, + 0x35, 0x6C, 0xB9, 0x2A, 0xC6, 0x74, 0x71, + 0xC4, 0x0C, 0x65, 0x7C, 0xA3, 0xF9, 0x6E, + 0x2D, 0xB9, 0x86, 0xB0, 0x04, 0x63, 0x94, + 0xCF, 0x26, 0x7D, 0x9E, 0x1E, 0xF7, 0x99, + 0xDA, 0xE0, 0x8C, 0xDD, 0x86, 0xF3, 0xC8, + 0xF1, 0xC6, 0x4E, 0x15, 0x5D, 0xED, 0x34, + 0xBB, 0x90, 0xCD, 0xD6, 0x26, 0x09, 0x1E, + 0xF6, 0x23, 0x84, 0x51, 0xC1, 0xAE, 0x42, + 0x8F, 0x84, 0x96, 0xC9, 0x57, 0xAE, 0x16, + 0x36, 0x3A, 0x29, 0x87, 0x65, 0xB2, 0xBA, + 0x56, 0x96, 0xD2, 0xF5, 0x09, 0xEF, 0x30, + 0x4A, 0x0F, 0x62, 0x36, 0xFE, 0x00, 0x30, + 0xBD, 0x09, 0x1B, 0x50, 0xC3, 0x3C, 0x48, + 0xD7, 0x25, 0xE1, 0x8E, 0xB2, 0x6F, 0x4D, + 0x75, 0x2D, 0xAD, 0xC6, 0x1E, 0xBA, 0xFA, + 0x4F, 0x41, 0xAF, 0x7A, 0x6F, 0xA0, 0xA0, + 0x4F, 0xB6, 0x88, 0x34, 0x69, 0x8A, 0x9E, + 0x83, 0x7A, 0x60, 0xF3, 0xA8, 0x7E, 0x39, + 0x2A, 0x20, 0x9C, 0x32, 0x5E, 0x7D, 0xC4, + 0xAA, 0x97, 0xE6, 0x26, 0x35, 0x42, 0xA1, + 0xA0, 0xEF, 0x39, 0xB3, 0x1C, 0x27, 0x63, + 0xD2, 0x7F, 0x75, 0xC0, 0x1E, 0xB4, 0xFA, + 0x4D, 0x7A, 0x81, 0x81, 0x39, 0xE0, 0x79, + 0x2F, 0x94, 0xA5, 0x28, 0x3A, 0x43, 0x7E, + 0x97, 0xB5, 0x94, 0xAF, 0x2A, 0x10, 0x38, + 0x0A, 0x7F, 0x49, 0x91, 0xC0, 0x6C, 0x65, + 0x9A, 0xC6, 0xC9, 0x5D, 0xD5, 0x52, 0x46, + 0x98, 0x74, 0xAC, 0x93, 0xE4, 0x21, 0xB0, + 0xEB, 0x08, 0xFA, 0xF4, 0x41, 0x2E, 0xA6, + 0xF9, 0xC5, 0xED, 0xE3, 0x37, 0x98, 0xC5, + 0xDF, 0x52, 0x82, 0x41, 0xA2, 0xF2, 0x56, + 0x1A, 0xF3, 0xA4, 0xC6, 0x40, 0x8D, 0xB9, + 0x5F, 0xAA, 0x75, 0x1E, 0x15, 0x1E, 0x68, + 0x78, 0x2B, 0x2B, 0xF4, 0x3E, 0xC7, 0xCB, + 0xF3, 0x13, 0x60, 0xE0, 0xF4, 0x85, 0x79, + 0x88, 0x0C, 0x22, 0x75, 0x87, 0xB7, 0x51, + 0xE0, 0xD6, 0xE3, 0xA0, 0x92, 0xD3, 0xD7, + 0xE3, 0x6C, 0x5B, 0x3E, 0xB7, 0x80, 0xE7, + 0x48, 0xD6, 0xD9, 0x2F, 0x49, 0xCC, 0x75, + 0xA0, 0x04, 0xC9, 0x48, 0x88, 0xE9, 0xCC, + 0x32, 0x41, 0xAF, 0x30, 0xDE, 0xC6, 0xBC, + 0x9D, 0xF7, 0xFC, 0xFF, 0x69, 0x03, 0x50, + 0x58, 0xA1, 0xD6, 0xB8, 0x7B, 0x5E, 0x60, + 0xFC, 0xCC, 0x00, 0x14, 0xAF, 0x3A, 0x30, + 0x41, 0xB3, 0x4E, 0x63, 0x47, 0x95, 0xE4, + 0xBD, 0x53, 0x74, 0xD0, 0xF0, 0x02, 0xB4, + 0xFE, 0x48, 0x19, 0x6C, 0x3F, 0x23, 0xF7, + 0x9B, 0x44, 0x11, 0xF2, 0xC2, 0x45, 0xA6, + 0x37, 0x77, 0x6C, 0x10, 0x63, 0x0E, 0x1B, + 0xA7, 0xD1, 0x96, 0xCA, 0x0D, 0x4F, 0x5B, + 0x43, 0x51, 0xD6, 0x14, 0xB7, 0x23, 0xE7, + 0x99, 0xA4, 0x9F, 0x4C, 0x7D, 0xD7, 0xDD, + 0x08, 0x11, 0x84, 0x4D, 0x5C, 0xE9, 0x60, + 0x90, 0x04, 0x8B, 0x14, 0x90, 0xF0, 0x6F, + 0x26, 0x45, 0x0F, 0x20, 0x8C, 0xE3, 0x6F, + 0x46, 0x78, 0xEB, 0x12, 0xB4, 0x5E, 0xF0, + 0xDE, 0xEB, 0x22, 0x27, 0x62, 0x78, 0x42, + 0x93, 0xBC, 0xE8, 0x48, 0xC0, 0x88, 0xBD, + 0x0F, 0xDE, 0x23, 0x2B, 0xD1, 0x88, 0x0A, + 0xE2, 0xB4, 0x33, 0x8E, 0x61, 0x36, 0x43, + 0x58, 0x42, 0xAE, 0x6B, 0xD7, 0xF2, 0xFE, + 0x0A, 0xC1, 0x26, 0x94, 0x7A, 0xCE, 0x8C, + 0x3B, 0x43, 0x5A, 0x62, 0xEF, 0x70, 0x8A, + 0xFE, 0x4D, 0xD2, 0x37, 0xDB, 0xA1, 0xB1, + 0x06, 0x1B, 0x30, 0x17, 0x4C, 0xA0, 0x24, + 0x1E, 0xBE, 0xCB, 0x64, 0xE2, 0xCE, 0x13, + 0x5C, 0xD7, 0x05, 0xA6, 0x76, 0x39, 0x38, + 0xF3, 0x85, 0x2C, 0x16, 0x0E, 0xB3, 0xE2, + 0x3D, 0xFF, 0x79, 0x88, 0x0B, 0x93, 0xEF, + 0xAD, 0x55, 0xCD, 0xB4, 0xEF, 0x8F, 0xB8, + 0x16, 0x7A, 0x05, 0xC0, 0x5E, 0x92, 0x0F, + 0x4F, 0x98, 0x58, 0xD4, 0x22, 0xAD, 0x43, + 0x25, 0x31, 0x78, 0x52, 0xC6, 0x9A, 0xCA, + 0x5E, 0xA6, 0x4E, 0x79, 0x97, 0x9A, 0xBB, + 0xDF, 0x6D, 0xF1, 0xE2, 0xD0, 0xB3, 0xCA, + 0x2A, 0x5C, 0x46, 0x82, 0x5C, 0x1E, 0xDD, + 0xD0, 0xC5, 0x19, 0x89, 0x2B, 0x2C, 0xBD, + 0xC0, 0x84, 0xD9, 0x33, 0x39, 0x67, 0xE4, + 0xE4, 0x79, 0x26, 0xB6, 0x9D, 0x1C, 0x1D, + 0xE4, 0x70, 0xA5, 0xA7, 0x61, 0x89, 0x25, + 0x4A, 0x57, 0x33, 0x7D, 0x0D, 0xCA, 0xF3, + 0xCC, 0x22, 0xAA, 0xD7, 0xD9, 0xCB, 0xE6, + 0xB2, 0x96, 0x46, 0x15, 0x95, 0x68, 0xD8, + 0x0F, 0xDF, 0x6B, 0x36, 0xDA, 0x19, 0x82, + 0x16, 0xCE, 0x38, 0x4E, 0xCF, 0xF2, 0x36, + 0xC8, 0x80, 0x07, 0x53, 0x88, 0x04, 0x2F, + 0xB2, 0x14, 0x19, 0x5D, 0xC5, 0x6C, 0xEF, + 0x95, 0x9C, 0x60, 0x8B, 0x6A, 0xE4, 0xFC, + 0x9D, 0xB2, 0x9C, 0xED, 0x1D, 0x9C, 0x1B, + 0xCA, 0xA3, 0x26, 0xDD, 0x9C, 0x63, 0x5B, + 0x3B, 0x88, 0x2B, 0xBA, 0x22, 0x35, 0x24, + 0x01, 0x48, 0xEE, 0xA0, 0x4F, 0x6E, 0x96, + 0x4E, 0x29, 0x91, 0xAA, 0xAF, 0x56, 0x47, + 0xBD, 0x7B, 0xDD, 0xA1, 0x55, 0x99, 0xD8, + 0xB5, 0xA1, 0x47, 0x93, 0x7B, 0xCD, 0x7B, + 0xCF, 0x65, 0xDE, 0x50, 0x18, 0xF2, 0x05, + 0x49, 0xF1, 0x99, 0xD8, 0x94, 0x21, 0x3F, + 0x7D, 0x52, 0xC6, 0xB1, 0xB4, 0xE1, 0x9F, + 0xF0, 0x7E, 0x60, 0x64, 0x8B, 0x30, 0xE4, + 0xAD, 0x2C, 0x97, 0xFB, 0x47, 0xF7, 0x09, + 0x01, 0x04, 0x7B, 0xF4, 0xF7, 0xA7, 0xA8, + 0x04, 0xA5, 0x7F, 0x08, 0x58, 0x79, 0x6D, + 0x26, 0x27, 0x6A, 0xD0, 0xD9, 0xAF, 0xC1, + 0xC7, 0x12, 0x23, 0xE1, 0xAE, 0x98, 0xA4, + 0x0F, 0xBC, 0x1B, 0xFA, 0x9D, 0x91, 0x0B, + 0x15, 0x15, 0xA5, 0x24, 0x53, 0x1A, 0x13, + 0xE0, 0xC3, 0x00, 0x95, 0x38, 0x9C, 0x36, + 0x4C, 0xCF, 0xF6, 0xA9, 0xBC, 0xC1, 0x53, + 0x06, 0x04, 0xF4, 0x6F, 0xBF, 0x57, 0xE0, + 0xA6, 0x14, 0xA6, 0x49, 0x70, 0x7F, 0x39, + 0x2A, 0x4A, 0x5A, 0x59, 0x36, 0x33, 0xB9, + 0x0D, 0x47, 0x25, 0x39, 0x37, 0x04, 0x77, + 0x7C, 0x45, 0xAC, 0x19, 0x93, 0x25, 0x49, + 0xE0, 0x7A, 0x7B, 0xA2, 0xC6, 0x95, 0x76, + 0x8D, 0x3C, 0x79, 0xB1, 0xA7, 0xC3, 0x83, + 0x63, 0x03, 0x23, 0x47, 0xD7, 0xAC, 0xFB, + 0x84, 0x16, 0x6F, 0x22, 0x49, 0x0B, 0x97, + 0xAA, 0xD5, 0x38, 0x57, 0x5E, 0x2B, 0xBB, + 0xA7, 0x49, 0xED, 0xD2, 0xD5, 0x7A, 0x96, + 0xBE, 0x8D, 0xF5, 0xF9, 0x46, 0x56, 0xF5, + 0x2F, 0xA7, 0xA8, 0x7A, 0xFF, 0x3F, 0xE7, + 0xE8, 0x8C, 0xF1, 0x48, 0xE8, 0x46, 0xCB, + 0x4D, 0x25, 0xF9, 0x35, 0x93, 0x2F, 0xCE, + 0x8B, 0xB8, 0xA9, 0xC5, 0xD3, 0xD2, 0x27, + 0x71, 0x86, 0x6F, 0x72, 0xD9, 0xCA, 0x4E, + 0x90, 0xD3, 0x6D, 0xB1, 0x3C, 0xD3, 0xCB, + 0xC9, 0x30, 0x3A, 0x26, 0x45, 0x9D, 0x66, + 0x44, 0x5C, 0x5F, 0xD8, 0xE3, 0x90, 0x7C, + 0x1B, 0xD8, 0x82, 0xDD, 0xD3, 0x84, 0xD4, + 0xC6, 0x1B, 0x58, 0xBF, 0x02, 0xA5, 0x3B, + 0x10, 0x0A, 0x3E, 0xE3, 0x09, 0x94, 0x0B, + 0x99, 0x88, 0x2B, 0x47, 0xA7, 0x90, 0x89, + 0xC4, 0x48, 0x25, 0x36, 0xDB, 0x72, 0x31, + 0xAC, 0xF5, 0xA3, 0x01, 0x15, 0xD0, 0xE6, + 0x21, 0x61, 0xEB, 0x59, 0x72, 0xDC, 0x94, + 0x00, 0x00, 0x98, 0xF7, 0xB0, 0xF9, 0xFA, + 0xC1, 0x0A, 0x7E, 0xDC, 0x69, 0xCE, 0x90, + 0x5E, 0x82, 0x11, 0x0B, 0x97, 0xE2, 0x99, + 0x55, 0xA2, 0x3D, 0xFA, 0xEB, 0x88, 0xBC, + 0x4D, 0xC2, 0x54, 0x8D, 0x5F, 0xDE, 0xA3, + 0xE2, 0x48, 0x13, 0x20, 0x46, 0xC5, 0xA0, + 0x81, 0x04, 0xC4, 0x2B, 0x9A, 0x84, 0x15, + 0xB8, 0x9E, 0x9B, 0x62, 0xA1, 0xEC, 0x7D, + 0x08, 0x3C, 0xAD, 0x21, 0x4B, 0x89, 0xFC, + 0xC6, 0x96, 0x7C, 0x3A, 0x41, 0x35, 0x8A, + 0x13, 0xBD, 0x1B, 0x43, 0xBF, 0x36, 0x7A, + 0x4E, 0xCE, 0x70, 0x9E, 0x2A, 0xF6, 0x39, + 0x33, 0xC7, 0x33, 0x4B, 0x34, 0x7D, 0x2A, + 0x81, 0xFA, 0x25, 0x2E, 0xC5, 0xC4, 0xA1, + 0x28, 0x0E, 0xD5, 0x4D, 0x15, 0xE0, 0x69, + 0x12, 0x71, 0x19, 0xAA, 0xFF, 0x59, 0x48, + 0x81, 0x56, 0x53, 0xC9, 0x35, 0x12, 0x4D, + 0x84, 0x69, 0x91, 0x36, 0xD8, 0xAD, 0xF7, + 0x54, 0xD5, 0x8C, 0xF9, 0xC6, 0x84, 0x12, + 0x62, 0x22, 0xF1, 0xB9, 0x2B, 0x1D, 0xA5, + 0x7C, 0x56, 0x7A, 0x4D, 0x5A, 0xC4, 0xF0, + 0x9D, 0x8F, 0x5D, 0xB5, 0x9A, 0x7F, 0xE7, + 0xB7, 0xA8, 0x5D, 0x6D, 0x37, 0x00, 0x00, + 0x00, 0x00, 0xE4, 0xF2, 0xEA, 0x7A, 0x73, + 0xF8, 0xFC, 0x67, 0x17, 0xF3, 0xD9, 0x60, + 0x09, 0x0E, 0x8B, 0x0F, 0xE2, 0xF9, 0x83, + 0x61, 0xDF, 0x53, 0x69, 0xD0, 0xEE, 0x42, + 0x51, 0xB5, 0x51, 0x48, 0x64, 0xF8, 0xDD, + 0x3E, 0xEE, 0xA7, 0x47, 0xF0, 0x7C, 0x4B, + 0xBB, 0x2B, 0x8E, 0xE2, 0x8B, 0x60, 0x57, + 0x63, 0x4B, 0x9B, 0xAA, 0x67, 0x63, 0xEA, + 0xA3, 0xD5, 0x7C, 0x43, 0x41, 0xA9, 0x54, + 0x74, 0xD2, 0x86, 0xC3, 0x11, 0x42, 0x69, + 0xC5, 0x09, 0x3E, 0x8F, 0x94, 0xAF, 0xC5, + 0x99, 0xBB, 0xBB, 0xAC, 0x61, 0x64, 0x61, + 0x01, 0xF2, 0xD3, 0x59, 0xB1, 0x41, 0x50, + 0x91, 0x16, 0x6C, 0xCC, 0x9B, 0xEC, 0x5C, + 0xFE, 0x3B, 0xB3, 0xEC, 0x6E, 0xD7, 0xF1, + 0x0E, 0x5A, 0x8A, 0x5F, 0x1D, 0xB0, 0xC5, + 0x76, 0x54, 0x26, 0x07, 0xBC, 0x66, 0xF0, + 0xED, 0x51, 0x9A, 0x1F, 0xE8, 0xF7, 0x51, + 0x58, 0x8E, 0x1E, 0x80, 0x99, 0x1F, 0xEE, + 0xD6, 0x43, 0xCC, 0xFA, 0x2F, 0xAE, 0x80, + 0xF2, 0xA8, 0xA8, 0xAE, 0x43, 0x7C, 0x80, + 0x76, 0xD4, 0x1A, 0xD4, 0x73, 0x74, 0x61, + 0xDF, 0xBF, 0x29, 0x6F, 0x56, 0x15, 0x93, + 0x07, 0x58, 0x92, 0xF1, 0x1D, 0xE8, 0x7B, + 0x68, 0xB2, 0x54, 0x20, 0x30, 0x01, 0x2D, + 0xC5, 0xD8, 0xFD, 0x61, 0x50, 0x14, 0x0F, + 0x58, 0x4C, 0x1A, 0x54, 0x00, 0x12, 0x7E, + 0x46, 0x99, 0x0C, 0x9D, 0xD1, 0xA3, 0x04, + 0xF1, 0x82, 0x17, 0xFE, 0xFC, 0x12, 0xE5, + 0x75, 0x81, 0xAF, 0xFF, 0x3D, 0x08, 0xD5, + 0x39, 0x87, 0x4A, 0x8E, 0xF0, 0x7A, 0xD4, + 0xD0, 0xD6, 0xCF, 0xA9, 0xD6, 0x59, 0x48, + 0x41, 0xC2, 0x68, 0xE3, 0xA4, 0x35, 0x45, + 0x8D, 0x14, 0x1A, 0xC4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x44, 0x89, 0x2C, 0x63, 0xC8, + 0xAF, 0x7B, 0xAC, 0x35, 0x43, 0x27, 0xBE, + 0x7E, 0xCF, 0x56, 0x40, 0x3D, 0xA5, 0xAC, + 0x0F, 0x97, 0xCC, 0xD4, 0x6E, 0xD0, 0x31, + 0xD7, 0x14, 0x60, 0x45, 0xAE, 0xDF, 0x0A, + 0xAA, 0x33, 0xB3, 0x88, 0xE0, 0x1C, 0x26, + 0xDC, 0x00, 0xB8, 0x75, 0xC8, 0x67, 0x11, + 0xA1, 0xB5, 0x87, 0x71, 0xF9, 0x4A, 0x6A, + 0xEC, 0x3F, 0x0E, 0x41, 0x21, 0xC2, 0x4F, + 0x92, 0x8F, 0x17, 0x53, 0xEE, 0xD5, 0xEA, + 0x4F, 0x91, 0x75, 0x02, 0x59, 0x00, 0x00, + 0x00, 0x02, 0x94, 0xE4, 0xDA, 0x4A, 0xB3, + 0xAA, 0xA7, 0x2E, 0x9C, 0xC7, 0x80, 0x46, + 0x5E, 0xBD, 0x06, 0x33, 0x1D, 0xD8, 0x5D, + 0x9F, 0x5F, 0x77, 0x73, 0x0A, 0x7D, 0x38, + 0x6B, 0xA7, 0x0E, 0x8F, 0x4E, 0x7D, 0x01, + 0xDA, 0x44, 0x74, 0xD2, 0xF1, 0x7A, 0x2F, + 0x7F, 0xE1, 0xEC, 0xF1, 0xA5, 0x7D, 0x03, + 0x3B, 0x2F, 0x17, 0xB6, 0xE8, 0x0D, 0x33, + 0x3C, 0x21, 0x91, 0x91, 0x5B, 0x30, 0x2C, + 0x11, 0xC6, 0x58, 0x6A, 0xE4, 0x35, 0x1B, + 0xB5, 0xA8, 0x47, 0xB3, 0xFF, 0xD0, 0x96, + 0x0E, 0x48, 0x4E, 0x97, 0x37, 0x60, 0x12, + 0x04, 0xFF, 0xCB, 0xA5, 0xF5, 0x1D, 0x40, + 0x9C, 0xDF, 0x36, 0x12, 0x3D, 0xD9, 0x2A, + 0x72, 0xDA, 0xE8, 0x8B, 0xE4, 0x5B, 0xCF, + 0xE9, 0x38, 0x52, 0xEC, 0x2C, 0xA0, 0xF7, + 0x79, 0xAD, 0x20, 0x70, 0x59, 0x20, 0x62, + 0x73, 0x9C, 0xE3, 0x46, 0x09, 0xC9, 0x12, + 0xD4, 0x77, 0x55, 0x07, 0x4B, 0x6A, 0x26, + 0x1E, 0x5D, 0x07, 0x78, 0x10, 0x4E, 0x4A, + 0xDA, 0x5E, 0xF6, 0x3B, 0x9B, 0x8D, 0xFD, + 0x48, 0xD0, 0xBB, 0xAA, 0x21, 0x2E, 0xF5, + 0xDF, 0x85, 0x4C, 0xA7, 0x1D, 0x1A, 0x26, + 0x1B, 0xA0, 0x82, 0x86, 0x7F, 0x4B, 0x02, + 0xD2, 0xF6, 0x60, 0xC9, 0x86, 0x0C, 0x1D, + 0xBF, 0x73, 0x69, 0x86, 0x18, 0x4B, 0x44, + 0xBC, 0x74, 0xE5, 0x82, 0x17, 0x93, 0xB5, + 0x0A, 0x56, 0x08, 0xB7, 0x4A, 0x73, 0xB8, + 0x04, 0xD2, 0x14, 0x21, 0x5E, 0x2C, 0xA0, + 0xE0, 0xC2, 0x01, 0x5F, 0xDE, 0xCE, 0x64, + 0x2A, 0xD8, 0x65, 0x2B, 0xF0, 0x74, 0xE3, + 0x54, 0xBF, 0xA0, 0x9B, 0x50, 0xBA, 0x02, + 0x00, 0x2C, 0x95, 0xCE, 0x2A, 0x4F, 0xCF, + 0x2B, 0x2B, 0x0E, 0x9B, 0xDD, 0x8F, 0x67, + 0x5B, 0x54, 0xA1, 0x47, 0x2E, 0x84, 0x2E, + 0x24, 0xFA, 0x34, 0xE0, 0x5A, 0x10, 0x3B, + 0xCF, 0xAF, 0xE2, 0xED, 0x28, 0xD1, 0x17, + 0x6B, 0x9A, 0x1B, 0x49, 0x6C, 0xFC, 0xCB, + 0x7A, 0x74, 0x09, 0x2A, 0x41, 0x5A, 0xFF, + 0xA1, 0xF5, 0x98, 0xC6, 0x0C, 0xD2, 0x69, + 0x83, 0xD2, 0xC3, 0x69, 0x87, 0x5E, 0x7E, + 0xBB, 0xFC, 0xC0, 0xD1, 0xC1, 0x17, 0xD7, + 0x9B, 0xE1, 0x53, 0x70, 0xB7, 0x43, 0xD0, + 0x38, 0x25, 0x26, 0x49, 0x40, 0x5B, 0x57, + 0xBA, 0x49, 0x32, 0xAA, 0x9F, 0x39, 0xAA, + 0x8E, 0x73, 0x00, 0x85, 0x13, 0x79, 0x6D, + 0xF1, 0x91, 0x2F, 0x9E, 0xA3, 0xA0, 0x65, + 0x7C, 0x7D, 0x53, 0xB4, 0x70, 0x53, 0x01, + 0x6B, 0x92, 0x5F, 0x38, 0xF1, 0xD7, 0x3D, + 0x7A, 0xD6, 0x07, 0x72, 0x7A, 0xDB, 0xA1, + 0x65, 0xA5, 0x07, 0x41, 0x00, 0x4B, 0xEB, + 0xC5, 0xD0, 0x3B, 0xEB, 0x30, 0x61, 0x42, + 0x0D, 0x4B, 0xA3, 0xD8, 0x07, 0x8B, 0x12, + 0xF5, 0x89, 0x3D, 0x7C, 0xFB, 0x2E, 0x79, + 0xA7, 0xBC, 0xD5, 0xBF, 0x72, 0x9A, 0xD8, + 0x15, 0x39, 0xAD, 0x25, 0x70, 0xD1, 0x6C, + 0xB0, 0x5D, 0xBB, 0xF4, 0x3A, 0xF6, 0x60, + 0x56, 0xFA, 0xF2, 0xA5, 0x28, 0xAE, 0x0D, + 0x76, 0xB3, 0x79, 0xF1, 0x4B, 0xC7, 0x2E, + 0x6E, 0xB6, 0x61, 0x62, 0x22, 0xD9, 0xA7, + 0x30, 0x23, 0x45, 0x21, 0xDE, 0x47, 0x8C, + 0xEB, 0xE5, 0x4C, 0x8B, 0x4E, 0x79, 0xA0, + 0x9D, 0x95, 0xB3, 0x05, 0xAA, 0xAF, 0xEC, + 0xE6, 0x74, 0x73, 0xF0, 0xDC, 0x37, 0xAD, + 0x93, 0x3B, 0x20, 0x6D, 0xA1, 0xD8, 0xCF, + 0xF9, 0x7E, 0x9E, 0xC6, 0xBB, 0x40, 0x64, + 0x56, 0xA6, 0x18, 0x75, 0x28, 0xD2, 0xBA, + 0x78, 0x64, 0x5C, 0x48, 0x45, 0x70, 0x35, + 0x8C, 0x49, 0x9A, 0x45, 0x4A, 0x6D, 0xF2, + 0x9C, 0x8D, 0x48, 0x0A, 0x61, 0x2E, 0xE3, + 0xD4, 0xEE, 0xA8, 0x29, 0x40, 0x4B, 0x53, + 0xBB, 0x56, 0x39, 0xCE, 0x81, 0x6C, 0xFD, + 0x37, 0xA0, 0x79, 0x1C, 0xC7, 0x94, 0x17, + 0x6C, 0x37, 0xEA, 0x97, 0xF4, 0xD8, 0xD4, + 0x9F, 0x9F, 0x30, 0x4F, 0xE5, 0xA4, 0x08, + 0xB8, 0x6B, 0xAE, 0xB9, 0xBC, 0x4F, 0x61, + 0x20, 0x44, 0xDF, 0x92, 0xDB, 0x1E, 0x04, + 0x83, 0xC0, 0xA1, 0x2C, 0x09, 0xD8, 0xDF, + 0x21, 0xF0, 0x20, 0x04, 0x80, 0xAF, 0x73, + 0x91, 0xBC, 0x0E, 0xBA, 0xE4, 0xF7, 0x9B, + 0x36, 0x3C, 0x30, 0xF1, 0xB4, 0xBA, 0xDC, + 0xFA, 0x65, 0xF2, 0x2B, 0x1D, 0x18, 0x4E, + 0x16, 0x62, 0xD4, 0xAC, 0x8B, 0x21, 0xA5, + 0xA2, 0xF1, 0x32, 0xC3, 0xC9, 0x65, 0x63, + 0x2D, 0x31, 0x6B, 0xB8, 0x8F, 0xE1, 0x24, + 0x93, 0x87, 0x04, 0x99, 0xE6, 0xBD, 0xBA, + 0x28, 0x30, 0x63, 0xDA, 0xAC, 0xC7, 0x99, + 0x37, 0x8A, 0x86, 0x66, 0xB3, 0x44, 0xD5, + 0x84, 0xAA, 0xA1, 0x53, 0x42, 0xA0, 0x38, + 0x1E, 0x5D, 0xF7, 0x05, 0x29, 0xE7, 0xCA, + 0x2A, 0x0B, 0x6A, 0xAB, 0xCE, 0xF0, 0x3A, + 0xF3, 0x90, 0xE6, 0xD1, 0x25, 0xA1, 0x33, + 0x1C, 0x33, 0x99, 0x7D, 0x24, 0xD1, 0x74, + 0x23, 0xFC, 0x6F, 0x7D, 0xDB, 0xA7, 0xE9, + 0xC2, 0xDE, 0x17, 0x12, 0x2D, 0x1C, 0xB3, + 0x7E, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x08, 0x75, 0x7D, 0x9A, 0x49, + 0x37, 0xAF, 0x60, 0x04, 0x7C, 0xBC, 0xD3, + 0x1C, 0x53, 0x70, 0x34, 0x97, 0xEB, 0xE1, + 0xCD, 0x30, 0x79, 0xA2, 0x86, 0x94, 0x58, + 0x5A, 0x99, 0xC2, 0x69, 0xD7, 0xB2, 0x32, + 0x62, 0x6C, 0x0A, 0x36, 0x69, 0xB1, 0x35, + 0x48, 0x41, 0xCE, 0x79, 0x96, 0x7A, 0x0D, + 0x2D, 0x33, 0xBF, 0x4D, 0x2F, 0xB8, 0xD8, + 0xB3, 0xFB, 0xE4, 0x71, 0x93, 0xB9, 0x18, + 0x0D, 0x51, 0xAB, 0xBB, 0x09, 0xD9, 0x0D, + 0xD7, 0x67, 0x1E, 0x47, 0x28, 0xC7, 0xF7, + 0xCE, 0x60, 0x58, 0x28, 0x23, 0x5E, 0x95, + 0xFC, 0x2B, 0xDB, 0xF9, 0x0D, 0x1F, 0xAB, + 0x49, 0xE6, 0xD0, 0xA7, 0x37, 0x79, 0x20, + 0xE4, 0xCF, 0x05, 0xE3, 0x64, 0x21, 0xAC, + 0xDF, 0xB1, 0xE8, 0xF9, 0xE0, 0x4C, 0x3B, + 0x5E, 0x89, 0x68, 0x2A, 0x48, 0xC5, 0x64, + 0x0E, 0xDB, 0xF1, 0x53, 0x8B, 0x1A, 0x2F, + 0xF6, 0x0C, 0x18, 0x9C, 0x52, 0x9B, 0x21, + 0x2E, 0x4A, 0xE7, 0xD6, 0xBB, 0xB7, 0x1A, + 0x0A, 0x22, 0xAB, 0x4A, 0xFD, 0xD3, 0x02, + 0x7D, 0x21, 0xFF, 0xB9, 0x0E, 0x0F, 0x29, + 0xAB, 0xAC, 0xF4, 0x44, 0x5E, 0x0F, 0xD5, + 0xF3, 0x57, 0xE5, 0x4F, 0x61, 0x72, 0x4E, + 0xBA, 0x55, 0x37, 0x8F, 0x9A, 0x1C, 0xD4, + 0xB8, 0x30, 0xAF, 0x0A, 0x95, 0x1A, 0x13, + 0x86, 0xD4, 0x0A, 0x83, 0x5C, 0x99, 0x1B, + 0x2B, 0xC0, 0x93, 0xF5, 0x13, 0xF5, 0xEA, + 0xFB, 0x6F, 0x75, 0x74, 0x6C, 0x2E, 0x77, + 0xC3, 0x44, 0xC5, 0x38, 0x62, 0xB9, 0xF5, + 0xAC, 0x58, 0xB4, 0x96, 0x6E, 0xA4, 0xA5, + 0x45, 0xB5, 0x1B, 0x7C, 0x9D, 0x21, 0x95, + 0x16, 0xEE, 0x29, 0x57, 0x20, 0x12, 0xBB, + 0xF8, 0x0A, 0x23, 0x25, 0xAC, 0x51, 0xFD, + 0x7A, 0xE0, 0x9D, 0x43, 0xB0, 0x27, 0x6F, + 0x2A, 0xDF, 0x04, 0x08, 0xD3, 0xF1, 0x1D, + 0x3C, 0x12, 0x8D, 0xAF, 0xF2, 0xD7, 0xB9, + 0x02, 0x9D, 0x46, 0xE3, 0xAB, 0xFF, 0x61, + 0xC8, 0x61, 0x2B, 0x19, 0xC8, 0xC1, 0xA1, + 0x6D, 0x0A, 0xA7, 0x44, 0xFA, 0xB5, 0xA8, + 0xD7, 0x1E, 0x53, 0x15, 0x6F, 0x7A, 0x48, + 0x41, 0xE9, 0x30, 0xB5, 0xAD, 0xE2, 0xF0, + 0x70, 0x40, 0x6A, 0xF3, 0x31, 0xDD, 0x19, + 0x50, 0xE5, 0xBC, 0xB4, 0x3D, 0x83, 0x9F, + 0xDF, 0xC8, 0x25, 0x27, 0x90, 0x40, 0x5B, + 0x32, 0x04, 0x47, 0x81, 0xFA, 0x53, 0xB4, + 0x0A, 0x40, 0x15, 0x7A, 0x74, 0x5B, 0xDB, + 0x24, 0xAB, 0x3A, 0xB7, 0x91, 0xE3, 0x44, + 0xB7, 0x47, 0x07, 0x39, 0x90, 0xD2, 0xF5, + 0xA9, 0x08, 0x46, 0x58, 0xDC, 0x32, 0x10, + 0xA3, 0x47, 0x32, 0x04, 0x45, 0x44, 0x13, + 0x71, 0xAC, 0x2E, 0xA7, 0xC9, 0x71, 0xD0, + 0xF9, 0x51, 0x7E, 0xCA, 0xE5, 0x09, 0xA8, + 0x1E, 0x77, 0x5B, 0x3A, 0x08, 0xAA, 0x43, + 0x1C, 0xD6, 0x8D, 0x60, 0x91, 0x46, 0x6D, + 0x64, 0x56, 0x9E, 0x16, 0x2E, 0xCE, 0xB3, + 0x99, 0xC8, 0xF6, 0x55, 0x27, 0x13, 0x84, + 0xBC, 0x8A, 0xB3, 0xC8, 0xF5, 0x1A, 0x72, + 0xF7, 0x6A, 0x0C, 0x46, 0x95, 0x2F, 0x4A, + 0x86, 0xB6, 0xD2, 0x98, 0x94, 0xF1, 0xC3, + 0x37, 0x0E, 0x0C, 0x87, 0x11, 0x56, 0x7E, + 0xAF, 0xDA, 0x22, 0x29, 0x6F, 0xEB, 0xFA, + 0xEC, 0x96, 0xE0, 0x27, 0x6B, 0x42, 0xA1, + 0x70, 0x09, 0x1A, 0xAD, 0x48, 0x88, 0xDA, + 0xFC, 0xE1, 0x3B, 0x62, 0x95, 0x28, 0x56, + 0x70, 0x28, 0x03, 0xB2, 0x58, 0x67, 0x8D, + 0x0A, 0xEA, 0x9A, 0x15, 0x95, 0x51, 0x5F, + 0x83, 0x07, 0x09, 0x20, 0xBF, 0x37, 0xAF, + 0xF7, 0x0A, 0x74, 0x3C, 0xA5, 0x52, 0xF1, + 0xE3, 0xF5, 0x58, 0x30, 0xE1, 0x17, 0xDE, + 0x98, 0xF1, 0x38, 0x7B, 0xF0, 0xAF, 0xBD, + 0x16, 0x59, 0x52, 0x99, 0x6C, 0x74, 0x70, + 0x48, 0x77, 0x14, 0xFF, 0x53, 0x41, 0x84, + 0xBC, 0xC5, 0x37, 0x2F, 0x71, 0x84, 0x3D, + 0xF5, 0x46, 0x7C, 0x63, 0xE0, 0x00, 0xA0, + 0x56, 0x07, 0xF7, 0x8C, 0x63, 0x57, 0xBE, + 0x54, 0x5C, 0xE1, 0xFD, 0xAC, 0x35, 0xFA, + 0xC2, 0xD5, 0xF2, 0x5A, 0xBE, 0x7C, 0x31, + 0x8E, 0xBD, 0xDB, 0xFE, 0x4D, 0x03, 0x6F, + 0x5E, 0xA6, 0x1F, 0x23, 0x2D, 0xCA, 0x68, + 0xF0, 0xC6, 0x14, 0xCC, 0xC3, 0x3E, 0xF0, + 0x21, 0xD4, 0x40, 0x2A, 0x72, 0xB6, 0x7E, + 0xE2, 0x85, 0xD6, 0x8A, 0xFB, 0x48, 0x51, + 0x27, 0x77, 0xE4, 0xCB, 0xCD, 0x2F, 0xF6, + 0xE1, 0xBC, 0xFE, 0xED, 0xA1, 0x14, 0x5F, + 0xE5, 0x32, 0xFC, 0xCF, 0xD9, 0xFB, 0x2F, + 0xE9, 0xAD, 0x7D, 0x2D, 0x68, 0xE7, 0x9A, + 0x15, 0x95, 0x6A, 0x4A, 0xE4, 0xAB, 0xE4, + 0xEB, 0xE0, 0x0D, 0x40, 0xBD, 0xEF, 0x8F, + 0xB9, 0x75, 0xB4, 0x39, 0x08, 0x6D, 0xC0, + 0x9F, 0x04, 0xFF, 0xCC, 0xDE, 0x13, 0x30, + 0x4E, 0xB6, 0xE4, 0xE0, 0xCB, 0x69, 0xE5, + 0xC6, 0x1C, 0x2F, 0x43, 0x6A, 0x63, 0x25, + 0x72, 0xD5, 0xDF, 0xBD, 0xDC, 0x0B, 0xE9, + 0x08, 0x9D, 0x3D, 0x26, 0x6F, 0xC3, 0x89, + 0xF8, 0x18, 0xAC, 0x42, 0xBF, 0x4E, 0x9D, + 0x5D, 0x28, 0xB0, 0x26, 0x41, 0x36, 0x76, + 0x8D, 0x86, 0x84, 0xFA, 0xD8, 0xD4, 0x67, + 0x58, 0x19, 0x69, 0x20, 0x2D, 0x35, 0xFE, + 0x09, 0xD9, 0x8C, 0xFD, 0xF9, 0x21, 0xA1, + 0x57, 0xDF, 0x17, 0xAE, 0x8E, 0xCB, 0xC9, + 0x7F, 0x26, 0x54, 0xDE, 0x10, 0x43, 0x72, + 0xDD, 0xBC, 0x18, 0xB9, 0x34, 0xF9, 0xAB, + 0x70, 0xF9, 0x74, 0x4D, 0x8E, 0x6D, 0x2A, + 0x33, 0x14, 0x06, 0xD0, 0x71, 0xE6, 0xAD, + 0xAA, 0xBE, 0x86, 0x2E, 0x9A, 0x8D, 0x87, + 0x88, 0xAF, 0x03, 0x9F, 0xCB, 0xF8, 0x78, + 0x9A, 0x66, 0x38, 0x09, 0xCD, 0xAE, 0x89, + 0xC2, 0xCC, 0xAF, 0x6A, 0x16, 0x14, 0x09, + 0x48, 0xAE, 0x9B, 0x55, 0xE4, 0x0F, 0xA5, + 0x86, 0xA1, 0x75, 0x7F, 0x7C, 0xEF, 0x0D, + 0xCB, 0x3A, 0xB3, 0x0D, 0x1F, 0x02, 0x00, + 0x00, 0x00, 0x0C, 0x80, 0x1F, 0xB4, 0xF4, + 0xE0, 0xA6, 0x41, 0xCC, 0x87, 0x00, 0x40, + 0x61, 0x43, 0xE7, 0xFC, 0xB4, 0x00, 0x00, + 0x00, 0x01, 0x24, 0xCA, 0x45, 0x34, 0x25, + 0x3C, 0x39, 0xFF, 0x09, 0x8C, 0x39, 0x85, + 0x5A, 0x5B, 0xCC, 0x27, 0x3C, 0xBD, 0x28, + 0x43, 0x86, 0x45, 0x17, 0x9A, 0x4E, 0x35, + 0x24, 0xA4, 0x23, 0xA2, 0xFD, 0xCA, 0x6C, + 0xD4, 0xFB, 0xBA, 0xE7, 0x41, 0xB3, 0xFB, + 0x7C, 0x55, 0x07, 0xB5, 0x6E, 0x3E, 0xB1, + 0x29, 0x72, 0xCC, 0x38, 0xEA, 0x27, 0x30, + 0x80, 0xB9, 0x8C, 0xB8, 0xEE, 0x54, 0xAB, + 0x3E, 0xDB, 0xDD, 0x06, 0xD0, 0x95, 0xEA, + 0x3E, 0x42, 0x88, 0x6B, 0xAE, 0x89, 0xC7, + 0xE8, 0x4C, 0x38, 0xB6, 0x93, 0xF6, 0xBE, + 0x4C, 0x20, 0x4A, 0x8C, 0xCE, 0x2B, 0x22, + 0xD2, 0x72, 0x49, 0x46, 0x18, 0x56, 0x80, + 0x70, 0x67, 0x5A, 0xFA, 0xC0, 0xCA, 0x6D, + 0x53, 0x21, 0xCA, 0xE3, 0x86, 0x68, 0x91, + 0xD3, 0x7C, 0xA9, 0xD5, 0xAD, 0x6A, 0x72, + 0xA1, 0xFE, 0x53, 0xE9, 0x40, 0x85, 0xEA, + 0x62, 0xA9, 0x63, 0x75, 0x3A, 0x2D, 0xEC, + 0x9B, 0xFE, 0x58, 0xD8, 0x73, 0x20, 0x52, + 0x10, 0x43, 0x5C, 0x2D, 0xB4, 0x21, 0xB9, + 0x96, 0x1F, 0x72, 0x6D, 0xBA, 0x42, 0xEA, + 0xF2, 0x85, 0xBE, 0x52, 0x12, 0xA7, 0xB1, + 0x3E, 0x2B, 0x89, 0xEF, 0x0F, 0x73, 0x05, + 0x7C, 0x99, 0xC3, 0xC5, 0x5F, 0x96, 0x53, + 0xF8, 0xAF, 0x9B, 0x7A, 0x49, 0xA0, 0x44, + 0x54, 0x60, 0x55, 0xF0, 0x10, 0x3A, 0x60, + 0x8D, 0x90, 0x50, 0x65, 0xAE, 0xF7, 0x79, + 0xDB, 0x2E, 0x08, 0xF9, 0xBB, 0xF2, 0xD2, + 0x78, 0xD0, 0x86, 0xB5, 0x94, 0xA1, 0x0E, + 0xB9, 0x81, 0x89, 0xB7, 0xF9, 0xC0, 0x43, + 0xE8, 0x8F, 0x38, 0x19, 0xDA, 0xED, 0x2F, + 0xA7, 0x5C, 0xDB, 0x01, 0xB3, 0x38, 0xCE, + 0xE6, 0xC3, 0xF2, 0xD5, 0xAA, 0x87, 0x9E, + 0x11, 0x6F, 0xD8, 0x05, 0xF0, 0x72, 0xAB, + 0xD9, 0xA1, 0x0F, 0xFF, 0x46, 0xE9, 0x4A, + 0x8C, 0x00, 0x93, 0xE9, 0x86, 0x6F, 0x6B, + 0x14, 0x70, 0xBE, 0xB1, 0xB8, 0x89, 0xC9, + 0xCC, 0x7D, 0x1A, 0x11, 0xD2, 0x79, 0xFB, + 0xD9, 0xD7, 0x24, 0x13, 0x94, 0x8F, 0x01, + 0x9C, 0x4B, 0x69, 0x33, 0x66, 0x01, 0x82, + 0x98, 0x88, 0xF8, 0x4C, 0x13, 0x31, 0x0F, + 0x58, 0xC1, 0x06, 0x11, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xB4, 0x89, 0x91, 0x34, 0x9B, + 0x89, 0xFC, 0x75, 0xCD, 0x11, 0xCA, 0x1A, + 0x6F, 0xFA, 0x71, 0xD6, 0x45, 0xE7, 0x0F, + 0xEB, 0x39, 0x09, 0xB3, 0x5A, 0x13, 0x1F, + 0xAA, 0x3A, 0x93, 0xCF, 0x8D, 0x6E, 0xCA, + 0xEC, 0x2C, 0x8E, 0xC5, 0x83, 0x1B, 0x5A, + 0xD6, 0xD1, 0x89, 0xAC, 0x02, 0x4F, 0xCB, + 0x0F, 0x25, 0x76, 0x96, 0xE0, 0x5D, 0x2C, + 0xFF, 0xA7, 0xEA, 0xEA, 0x92, 0xF1, 0x72, + 0x5B, 0xEE, 0x08, 0xD8, 0x84, 0x8A, 0xA9, + 0x59, 0xE3, 0x12, 0x77, 0xDA, 0xD6, 0xCA, + 0x4E, 0x36, 0x2D, 0x7C, 0xA4, 0x13, 0xD1, + 0xE8, 0x18, 0x31, 0x99, 0x02, 0x54, 0x0A, + 0xD0, 0x7A, 0x74, 0xA0, 0xAD, 0x2C, 0x83, + 0x4A, 0x6F, 0xF1, 0xB1, 0x50, 0x1B, 0x64, + 0x69, 0xE8, 0x84, 0x2F, 0x2C, 0xC8, 0xEB, + 0x95, 0x3D, 0xA5, 0x18, 0xC7, 0x4B, 0x82, + 0xFC, 0x88, 0xE1, 0xD3, 0x7B, 0x0E, 0xBE, + 0x10, 0xBA, 0x65, 0x02, 0x85, 0x9B, 0x46, + 0x9A, 0x37, 0x7A, 0x77, 0x4D, 0xC2, 0xC4, + 0x53, 0x30, 0x67, 0x91, 0x54, 0xF6, 0x3D, + 0x39, 0x44, 0x43, 0x42, 0x13, 0x2E, 0x09, + 0x29, 0xFD, 0x50, 0xA6, 0xDA, 0x94, 0xCE, + 0x86, 0x97, 0x10, 0x2B, 0x5C, 0x1C, 0x09, + 0xCD, 0x0F, 0xFC, 0x15, 0xDC, 0x54, 0x3B, + 0x53, 0xE1, 0x7C, 0xA7, 0xE1, 0xFD, 0x47, + 0xC2, 0xAF, 0x7E, 0xEA, 0x34, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x6F, 0xE6, 0x5C, 0x04, + 0x30, 0x6C, 0x84, 0xC7, 0x51, 0xE3, 0xB5, + 0x5F, 0x34, 0x9E, 0xDD, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x1C, 0xD4, 0xBC, 0xFB, + 0xCD, 0x3A, 0xAE, 0xBC, 0x52, 0x92, 0x54, + 0x85, 0x2C, 0xED, 0x0F, 0xFB, 0x00, 0x00, + 0x00, 0x00, 0xF8, 0x73, 0x10, 0x5D, 0xD8, + 0x1F, 0x06, 0xE0, 0xA1, 0x59, 0x26, 0xB4, + 0x39, 0x3F, 0xC1, 0x19, 0xF9, 0xD4, 0xBE, + 0x4F, 0xF4, 0x33, 0xEC, 0x3F, 0x3F, 0xFE, + 0x8F, 0x91, 0xD0, 0x44, 0xF2, 0x12, 0x78, + 0x90, 0x9E, 0x79, 0xA9, 0x27, 0xEE, 0x05, + 0xCB, 0x9F, 0x5A, 0x4B, 0x15, 0xDE, 0x67, + 0x39, 0x18, 0x1F, 0xE7, 0x3A, 0x0A, 0x41, + 0x98, 0xE7, 0xDE, 0xA7, 0x28, 0xA0, 0xD3, + 0x54, 0x66, 0x33, 0x79, 0xB5, 0x3A, 0x83, + 0x37, 0x5F, 0x25, 0x40, 0x70, 0x47, 0xB8, + 0x79, 0x6B, 0x62, 0x96, 0x3F, 0x48, 0xEA, + 0x9F, 0x6E, 0xCB, 0x35, 0xE1, 0x6F, 0x2D, + 0xC4, 0xC2, 0x4B, 0x9B, 0xA7, 0x4D, 0x05, + 0x79, 0x0E, 0x1C, 0xFE, 0x09, 0x5A, 0x9F, + 0x87, 0x22, 0x7A, 0x13, 0x1B, 0x9F, 0x99, + 0x4D, 0x05, 0xDB, 0x15, 0x73, 0xD8, 0x65, + 0xFE, 0x39, 0x5E, 0xA4, 0x43, 0x7B, 0x6C, + 0x79, 0xBD, 0x73, 0x91, 0x4C, 0x9B, 0x4C, + 0x94, 0x18, 0x87, 0xEA, 0x68, 0xA7, 0x44, + 0x9F, 0x5C, 0x36, 0x66, 0xFD, 0x41, 0x14, + 0x5E, 0xB1, 0x8E, 0xEB, 0x45, 0xDA, 0x77, + 0x0E, 0xE0, 0xEC, 0x5E, 0xF4, 0x0D, 0xBF, + 0x41, 0xC6, 0x48, 0xF0, 0x15, 0xD8, 0x60, + 0xF7, 0x40, 0x76, 0x23, 0x7B, 0x1C, 0xE5, + 0x59, 0x44, 0x50, 0x30, 0xCE, 0x06, 0x4E, + 0xC5, 0xA6, 0x70, 0xC3, 0x18, 0x17, 0xA2, + 0x37, 0xAC, 0xBF, 0x2C, 0x6F, 0xBC, 0x9B, + 0x3F, 0x58, 0x12, 0x11, 0x10, 0x39, 0x00, + 0x18, 0xDB, 0x7D, 0x0B, 0xD2, 0x2C, 0xEE, + 0x0A, 0xAF, 0x76, 0x94, 0x7A, 0xC1, 0x5C, + 0x21, 0xD2, 0x61, 0x9F, 0x1D, 0x7A, 0x96, + 0x95, 0xDF, 0xA4, 0x51, 0x81, 0xE6, 0x1B, + 0xD5, 0x7C, 0x36, 0x44, 0xF0, 0x60, 0xFF, + 0x3A, 0xF6, 0x32, 0xEC, 0x8F, 0xE0, 0x9A, + 0xC7, 0xE3, 0xBC, 0xAD, 0xEB, 0xA6, 0x94, + 0x71, 0x4B, 0x6D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x97, 0x4C, 0x36, 0xA2, + 0x75, 0xFF, 0xFB, 0x3F, 0xEB, 0x60, 0xE1, + 0x31, 0xB1, 0xBC, 0x32, 0x77, 0x6C, 0xBD, + 0x49, 0x93, 0x4B, 0xC2, 0x07, 0xBA, 0xF0, + 0x67, 0xFD, 0x68, 0xCA, 0xF5, 0xA4, 0xFF, + 0x2A, 0x5E, 0xE2, 0xBE, 0xB5, 0x69, 0xB2, + 0xA0, 0x63, 0x93, 0x1B, 0xB4, 0xBD, 0x46, + 0x12, 0xFA, 0xEE, 0x88, 0x5F, 0x46, 0xDF, + 0xB2, 0xBA, 0xE8, 0xBA, 0x54, 0x47, 0xEA, + 0x00, 0x00, 0x18, 0xFA, 0x08, 0x91, 0x77, + 0x9B, 0x1C, 0x87, 0x8B, 0x01, 0x70, 0x9E, + 0xC0, 0x05, 0x2B, 0xAA, 0xD7, 0xAF, 0x30, + 0xA8, 0x01, 0x8B, 0x28, 0xA0, 0xF5, 0x95, + 0x99, 0x32, 0x5F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xC4, 0xEF, 0x65, 0x45, + 0x3C, 0xC3, 0x6D, 0xEF, 0xB8, 0x34, 0x9D, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x66, 0x86, 0xF2, 0xBC, + 0x9D, 0x87, 0x97, 0xBE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xDD, 0x58, 0xDD, 0x53, + 0x0F, 0x7D, 0xEC, 0x93, 0x0B, 0xB9, 0x5B, + 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x76, 0x15, 0xDF, 0xC7, + 0x68, 0x2F, 0xA8, 0xF8, 0x4D, 0x33, 0x8F, + 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x84, 0xC3, 0xE8, 0xCB, + 0x9C, 0x71, 0x80, 0x09, 0xD7, 0x11, 0x63, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x0E, 0xC6, 0xF0, 0x50, + 0x9E, 0x0A, 0xA7, 0x99, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x9A, 0x5A, 0xF9, 0xAA, + 0x5F, 0x06, 0xDF, 0x5C, 0x52, 0x37, 0x4B, + 0x39, 0x0C, 0x7E, 0x18, 0xC1, 0xAB, 0xD6, + 0x36, 0xF5, 0x78, 0xBD, 0x9F, 0x79, 0x00, + 0x00, 0x00, 0x0E, 0xF2, 0x33, 0x61, 0x0E, + 0xC3, 0x0B, 0x5A, 0x24, 0x02, 0x3F, 0x42, + 0x35, 0x3D, 0xA4, 0xAD, 0xC5, 0x30, 0x50, + 0x00, 0x00, 0x76, 0xE7, 0xE3, 0x45, 0x08, + 0x66, 0x18, 0x21, 0xEB, 0xDD, 0x14, 0x76, + 0xF8, 0xF1, 0x55, 0xE8, 0x3B, 0x9D, 0x8B, + 0x93, 0x92, 0xFF, 0xA7, 0x80, 0x34, 0x38, + 0xC2, 0xAD, 0x7C, 0xFF, 0xBD, 0x23, 0x2B, + 0x0B, 0x2C, 0x5F, 0xEA, 0xC0, 0x95, 0x00, + 0xFB, 0x8C, 0x37, 0x6B, 0x8A, 0x42, 0x91, + 0x3E, 0xEF, 0xD6, 0x51, 0x43, 0x21, 0x6F, + 0x80, 0x3E, 0xBD, 0xF2, 0xB3, 0x24, 0x07, + 0xA5, 0x92, 0x4A, 0x76, 0x51, 0x31, 0x72, + 0x35, 0x47, 0x25, 0xE7, 0xB9, 0xD0, 0xF4, + 0xC2, 0xDD, 0x52, 0x35, 0x3A, 0x23, 0xC4, + 0x16, 0xFA, 0x2A, 0x8D, 0x99, 0xD0, 0xB9, + 0x53, 0xC2, 0x12, 0x5C, 0x3F, 0xCE, 0x8D, + 0xDB, 0xD1, 0xC8, 0xD1, 0x59, 0xE5, 0x88, + 0xFF, 0x49, 0x11, 0xAB, 0xAB, 0x7B, 0xD1, + 0xF2, 0xB4, 0x37, 0x49, 0xA0, 0xB1, 0xFE, + 0xFE, 0x6C, 0x08, 0x76, 0x1D, 0x0B, 0x00, + 0x00, 0x00, 0x9A, 0xF4, 0xB1, 0x6D, 0xA0, + 0xF2, 0x20, 0xE4, 0x17, 0xA7, 0x4D, 0x85, + 0xB3, 0x8E, 0x7A, 0xCB, 0x87, 0x30, 0xAF, + 0x11, 0x90, 0x20, 0xA8, 0x19, 0xEA, 0x22, + 0x07, 0xFA, 0xC1, 0x4C, 0xE8, 0xFC, 0xA2, + 0x03, 0x8F, 0x2A, 0xC5, 0x36, 0xE4, 0xAF, + 0x66, 0x9B, 0xBA, 0x0A, 0xDD, 0x65, 0x90, + 0xDA, 0xD7, 0x27, 0x62, 0xD9, 0x19, 0x3B, + 0xAB, 0xF6, 0x66, 0x89, 0x2D, 0xA5, 0x30, + 0x70, 0xE9, 0xAA, 0xE2, 0xD9, 0xC1, 0x23, + 0x47, 0xFD, 0x9A, 0x25, 0x4A, 0x5F, 0x89, + 0x62, 0x97, 0x61, 0x4B, 0x91, 0xFC, 0x34, + 0x1B, 0xFC, 0x97, 0xB2, 0x7B, 0x87, 0xF4, + 0x54, 0xFC, 0xD6, 0x36, 0x48, 0xC2, 0x87, + 0x79, 0xA3, 0xC4, 0x8F, 0x42, 0xB1, 0x31, + 0x47, 0xDD, 0xE8, 0xD0, 0x0A, 0x71, 0x47, + 0x22, 0xF3, 0xF4, 0x9F, 0x28, 0x7A, 0x70, + 0x0C, 0xBC, 0x59, 0x30, 0xE0, 0xE4, 0x13, + 0x24, 0x65, 0xBD, 0x4C, 0x0E, 0x6D, 0x9E, + 0x4D, 0x15, 0x90, 0x19, 0x72, 0xC9, 0x28, + 0xB6, 0x95, 0x0E, 0x92, 0xF9, 0x79, 0x5C, + 0x64, 0xB9, 0xB9, 0x7B, 0xD7, 0xDC, 0xB4, + 0xB9, 0xB4, 0x96, 0x7F, 0x72, 0x55, 0xAE, + 0x00, 0x00, 0x1E, 0xB0, 0x02, 0x8E, 0x45, + 0xBD, 0xF0, 0xA5, 0x90, 0xB7, 0x30, 0x12, + 0x40, 0xB6, 0x40, 0x9C, 0xF1, 0x62, 0x63, + 0x72, 0x6D, 0xF0, 0xBA, 0x1A, 0xF6, 0xF0, + 0x40, 0x6A, 0x47, 0xA2, 0xFC, 0x9B, 0xD8, + 0x70, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1A, 0xF5, 0x24, 0xF6, 0x4D, + 0x2F, 0x64, 0xFD, 0xF7, 0xC3, 0xFD, 0xA7, + 0x45, 0x5D, 0x94, 0x31, 0x8F, 0xD6, 0x9F, + 0x5D, 0x21, 0x66, 0x41, 0xD4, 0x39, 0x51, + 0xAF, 0x61, 0x8C, 0x1E, 0x9F, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x11, 0xE9, 0x7A, 0x88, + 0x31, 0xE1, 0x26, 0xB6, 0x1C, 0x74, 0x00, + 0x00, 0x00, 0x06, 0xF5, 0x98, 0x35, 0x17, + 0x1A, 0xAB, 0x19, 0x3A, 0x8C, 0xC6, 0x00, + 0x00, 0x00, 0x0E, 0x7E, 0x14, 0xD0, 0x6E, + 0x44, 0x1C, 0x66, 0x61, 0x67, 0xF1, 0x8E, + 0x28, 0x72, 0x30, 0xD2, 0xBF, 0x7D, 0x6F, + 0x00, 0x00, 0x08, 0x17, 0x99, 0x62, 0xEE, + 0x78, 0x18, 0x54, 0x3A, 0xD5, 0x7F, 0x61, + 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x83, 0x3E, 0x2C, 0x92, + 0x4F, 0x5A, 0x98, 0x59, 0x97, 0xFB, 0x82, + 0x97, 0xAD, 0xC8, 0x73, 0xA2, 0x6C, 0x0D, + 0x06, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0A, 0x69, 0xDA, 0xEB, 0x9E, + 0xF7, 0x67, 0x7F, 0xDE, 0xE8, 0x83, 0x0B, + 0x46, 0x5A, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0xB3, 0x60, 0x7F, 0xDC, + 0xA3, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x8E, 0xCF, 0xA2, 0x65, + 0x72, 0xCE, 0x3E, 0x11, 0x39, 0x2D, 0x94, + 0x94, 0x47, 0x8E, 0xC7, 0x8C, 0x70, 0x6B, + 0x7C, 0x49, 0x8E, 0x3C, 0xD7, 0x4C, 0x00, + 0x00, 0x00, 0x02, 0xC8, 0x77, 0xA5, 0xFD, + 0xBA, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0A, 0x1A, 0x1B, 0xD9, 0x4A, + 0x83, 0x03, 0xDF, 0xE2, 0x75, 0x6E, 0x28, + 0xD9, 0x63, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x18, 0x27, 0x27, 0x8F, + 0x4B, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x02 +}; diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/src/arduino folders read me.txt b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/src/arduino folders read me.txt new file mode 100644 index 00000000..21ba4e69 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/ATS_EX/src/arduino folders read me.txt @@ -0,0 +1,14 @@ +##################################################### + Arduino How To: Use sub folders for source code + http://www.visualmicro.com/ +##################################################### + +* .INO code can exist in the project folder and '\src' folder. +* .Cpp/.c/.S/.h etc. can exist in the project folder, the '\src' folder and in any folder(s) below the '\src' folder. +* .Cpp/.c/.S/.h sources in shared code projects should follow the same folder rules (because they are merged with the project sources into a temporary build folder prior to compile). + +* Use the 'Show All Files' icon above the 'Solution Explorer' to switch between 'included project files' and 'physical files/folders'. +* Source code in the project folder will always be compiled regardless of inclusion in the project. This functionality can be disabled in Global Options. + + + diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/LICENCE b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/LICENCE new file mode 100644 index 00000000..bc38b066 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Goshante + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/README.md b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/README.md new file mode 100644 index 00000000..625d47d8 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/README.md @@ -0,0 +1,164 @@ +# ATS_EX Firmware for ATS-20 DSP Receiver +### Руководство на русском языке можно прочитать здесь (Russian README is here) : +[>>> Ссылка на русский README. <<<](/rus/README.md) + +If you want to donate me, you can do it here: https://t.me/tribute/app?startapp=d7vv +## Basic description +This is advanced firmware for **ATS-20** that is working on **Arduino Nano** and **Si473x** DSP receiver chip. +ATS_EX is created by **Goshante**, based on **PU2CLR** firmware and inspired by **swling.ru** firmware with closed source. + + +**Latest version:** v1.18 (03.04.2024) + +**Download binary .hex link:** [>>> Click here <<<](https://github.com/goshante/ats20_ats_ex/releases/download/v1.18/ATS_EX_v1.18.hex) + + +

+ Icon +

+ +# Features + + - Fully **reworked interface**. No more ugly stretched fonts. Minimalistic readable interface. 7-Segment frequency font is inspired by swling firmware, but created by me. + - Fully **reworked controls**. You can read user manual below. + - **BFO is now part of main frequency** and regulated by frequency step, it is no more dedicated option that makes frequency surfing experience terrible. SSB mode has more precise steps. + - **LW** Band: From **153** KHz to **520** KHz + - **MW** Band: From **520** to **1710** KHz + - **SW** Band: From **1710** to **30000** KHz (With a lot of sub-bands) + - **FM** Band: From **64** to **108** MHz. + - **Dynamic RDS** for **FM** radio stations with an option to switch RDS information lines (Displays UP to 16 characters). + - AM/FM **Stations scan**. + - Added **CW mode**. + - Poorly designed RSSI was removed and replaced with optionaly displayed **S-meter** like scale. + - All **SW bands** now feel like one large band **from 1710 to 30000 KHz**. It's possible to switch between them, but they no longer restrict the frequency step to the sub-band limits. + - **The tuning across frequencies has become as smooth as possible** in SSB mode, thanks to the merging of the receive frequency with the BFO. The rough frequency switching now occurs every 16 KHz (**the seamless tuning in both directions covers a full 32 KHz**). In Non-SSB modes now you also can tune faster, **encoder should be more responsive**. + - **A lot of steps are available for every mode**. In AM you have 1/5/9/10/50/100k/1M steps, in SSB you have 1/5/9/10k steps and 10/25/50/100/500Hz steps for more precise tuning. In FM mode you have 10k/100k/1M steps. + - Added settings page. You can configure **Gain Control and ATT**, **Soft Mute**, **Automatic Volume Control** (AVC), On or Off **AVC for SSB mode**, **DeEmphasis** for FM mode and enable or disable **SSB Sync** mode and other useful settings. + - **BFO** offset **calibration**. + - Adjustable **screen brightness**. + - Adjustable **CPU Frequency** for power saving purposes and reducing interference. + - Added **Mute button** and **Display on/off button**. + - Added **Battery charge status** (Requires simple physical mod: Make **voltage divider** from two 10 KOhm resistors and solder it's middle output to **A2** pin) + - Added **S-Meter** + - **Atm328p controller is now running on it's full clock**. Controls have to be more responsive. (Don't know how it impacts on battery drain.) + - Code refactoring, optimizations + - Fixed some bugs + + ## About new feature requests... + Please, do not request new features. This firmware has hit the limit of maximum flash size and optimized as much as possible so it's impossible to add new features, there is not enough flash memory for new features. It's also has the bootloader and I am not going to remove it. + I only accept tickets about bugs and errors. This firmware works on the verge of ATS-20 capabilities. + + If you want to add something new you can try it yourself. All source code is available and present in this repository. + + # How to flash it on my receiver? +You can use any software capable of flashing a .hex firmware file to Arduino. You will only need a Micro USB cable and a **USB UART driver** (most likely, it will be the driver for the **CH341** controller). I recommend using **AVRDUDESS** if you are on Windows. It's a simple tool with a GUI that can dump and flash firmware files onto Atmel microcontrollers (Arduino). + +**There are two types of ATS-20 receivers**: Those on **Arduino Nano** and those on **Arduino Uno**. They are quite easy to distinguish. You need to disassemble and look. The ones on Nano have the Arduino soldered as a separate board onto the main board. If inside there isn't a soldered board and the ATMega368 chip is directly soldered with all components on one board, then you have the Uno revision. This is important to know before flashing. + +You need to select from the **Presets** section either **"Arduino Nano (ATmega328P)"** or **"Arduino Uno (ATmega328P)"**, then select your actual **COM port** and in the **"Flash"** section specify **the path to the .hex firmware file**. Choose the **Write** mode and press the **Go** button next to it. After this, the flashing will start. + +Alternatively, you can compile the firmware yourself from the sources that are in this repository. I use Visual Studio 2022 with the VSMicro extension, which uses Arduino IDE 1.8. You can simply use Arduino IDE, compile the sketch yourself, and upload it to the controller directly from the IDE. + +## My receiver fails to flash, what should I do? +This can happen if you're flashing through a USB port that's not capable of supplying sufficient current to power the controller along with the entire device. If you turn on the receiver before connecting it to the PC and connect it to a **USB 3.0 port**, your chances of success will be much higher. Yes, the receiver does not use USB 3.0, it has version 2.0, but the third version ports can supply **more current**. And if it still doesn't help, try to reflash the receiver through the Arduino IDE with a **dummy firmware** (empty programm), build an empty new project without any logic, and flash it into the receiver. If you flashing Arduino Nano version make sure you selected **"Old Bootloader"** board version. After that, try to flash the actual firmware into it again. + +# User manual +**ATTENTION:** After flashing it's strongly **recommended to reset EEPROM memory**. To do this just hold the **Encoder Button** while turning receiver on. + +### Button functionality +#### **BAND+** Button + 1. **Band selection**: Short press to enter band selection mode. Select the band using **Encoder Rotation** and confirm with the **Encoder Button** or by pressing **BAND+** again. Or hold **BAND+** to quickly scroll through bands forward. In **SW mode** switches to the nearest **SW subband**. After latest SW subband switches to the next (or prev) band. + 2. **Settings page switch**: This button switches **settings pages** while settings are open. +#### **BAND-** Button + 1. **Settings menu**: Short press to open/close **settings menu**. When **closing** settings menu all settings are **saved to EEPROM**. + 2. **Band selection**: Long press **BAND-** to quickly scroll through bands backward. +#### **VOL+** Button + 1. **Volume adjustment**: Short press to enter **volume adjustment** mode. Set the volume using **Encoder Rotation** and confirm with the **Encoder Button** or by pressing **VOL+** again. + 2. **Quick volume increase**: Hold for quick volume increase. +#### **VOL-** Button + 1. **Mute**: Short press to **mute and unmute**. + 2. **Quick volume decrease**: Hold for quick volume decrease. +#### **STEP** Button + 1. **Step adjustment**: Short press to enter **step adjustment** mode. Set the step with **Encoder Rotation** and confirm with the **Encoder Button** or by pressing **STEP** again. + 2. **Signal level scale**: Long press shows a signal level bar at the bottom, **similar to an S-meter**. Can be turned off with another long or short press, or by changing the band. +#### **AGC** Button + 1. **Display on/off**: Short press works as a display switch. + 2. **Sync mode for SSB**: Long press to switch to **Sync** mode when **SSB** modulation is active. +#### **BW** Button + 1. **Bandwidth adjustment**: Short press to enter **bandwidth adjustment** mode. Set the step using **Encoder Rotation** and confirm with the **Encoder Button** or by pressing **BW** again. Each modulation has its set of steps. +#### **MODE** Button + 1. **Modulation selection in AM/SSB mode**: Short press to **switch between modulations** in AM/SSB mode. In the **FM** band, the only available modulation is **WFM** (Wide FM), and it's not possible to switch modulation in the FM band (due to Si4735 chip limitations). On all other bands, the following modulations are available: **AM/USB/LSB/CW**. In all modulations (especially in SSB), improved frequency tuning without interruption at each step. + 2. **RDS in FM band**: Short press allows displaying a **metadata line** decoded from the RDS traffic of the current radio station below the frequency. As long as synchronization is not lost, the RDS string can **dynamically update** if the FM radio station cyclically outputs different information there. In this mode, you can switch between **3 different RDS information modes** using the **Encoder Button**: **Station Name**, **Station Information**, and **Program Information**. If any information cell is not decoded or is missing, three dots **...** will be displayed. If RDS data did not display upon activation or after you stopped on right frequency, you should turn RDS off and on again OR move the encoder to another frequency and return to allow the Si4735 chip to synchronize with the RDS traffic stream again. If synchronization is lost, it automatically restores only after changing frequencies. +#### **Encoder Rotation** +1. **Frequency Tuning**: In radio mode (normal mode), rotation adjusts the frequency by the step indicated at the bottom of the screen. +2. **Settings navigation**: In settings mode, the encoder allows you to select the necessary setting and, after selecting, change its value. +#### **Encoder Button** +1. **Frequency scan**: Works **only in FM and AM** modulations and only if option **Sca** is **On** in settings. Press to scan stations by frequency in the last direction with the given step. Rotate encoder or press encoder button to **stop scanning**. +2. **Universal button**: Confirms settings, makes selections, switches RDS modes. +3. **EEPROM reset**: Important functionality that allows **resetting settings to defaults**. To do this, turn on the receiver with the encoder button already pressed. After that, the **EEPROM RESET** message should appear. +4. **Quick step adjustment**: Only for **SSB mode** - pressing allows immediate entry into step adjustment mode. Also works in **AM/FM** modes if option **Sca** is **Off** in settings. + +### Settings +Navigate in settings with **Encoder Rotation**, confirm selection with **Encoder Button**, change value with **Encoder Rotation** and save it with **Encoder Button**. Close settings with **BAND-** button. Navigate between **settings pages** with **BAND+** button. + +

+ Icon +

+ +**ATT** : **Attenuation** value. **AUT** means **Auto Gain Congrol**. This value can be **AUT** and from **1** to **37**. **Warning:** AGC mode (**AUT**) in any SSB modes will add some constant noize (It is present in ANY firmware). it's not highly noticeble, but still keep this in mind. + +**SM** : **Soft Mute**. This is number from **0** to **32**. + +**AVC** : Automatic Volume Control. This is number from **12** to **90**. + +**SVC** : Enable or disable **AVC for SSB**. + +**DeE** : Only for **FM** mode. It's **DeEmphasis** value in microseconds. It can be only **50** or **75**. + +**Syn** : Enable or disable **Sync mode for SSB**. + +**Scr** : **Screen brightness** adjustment. This is number from **5** to **125**. + +**SW** : **Frequency units** for AM mode in SW band. It can be only **KHz** or **MHz**. In MHz mode it used dot as delimiter. + +**SSM** : **SSB Soft Mute Mode**. It can be only **RSS** (SM based on RSSI) or **SNR** (SM based on SNR). + +**COF** : **SSB Cutoff Filter**. It has 3 modes: **On** - Band pass filter to cutoff both the unwanted side band and high frequency components. **Off** - Low pass filter to cutoff the unwanted side band. And **AUT** mode: Automatically turns on and off, depending on **Bandwidth** (Off when bandwidth is higher than **2.0 KHz**). + +**CPU** : **CPU Frequency**. This is useful for **battery saving** purposes. It can be only **100** or **50%**. 100% - CPU works on x1 of own frequency, using it's full potential. 50% - CPU works on half of it's own frequency, it's slower, but allowing you to save more battery. + +**RDS** : **RDS Decoding error threshold** level. The lower this number, the better the RDS text appears, but the lower the chances for successful sync. The higher this value, the easier it is to decode the RDS stream, but it could look messed up frequently. This is number from **0** to **3**. + +**BFO** : **BFO Offset calibration**. One unit of this parameter is **1 Hz * 10** or **0.01 KHz**. This is permanent **BFO offset for SSB**. If you think SSB frequency is not precise enough you can calibrate it. Allowed offset value is from **-60 to 60 units** or from **-0.60 KHz to 0.60 KHz**. + +**Uni** : Show/Hide **frequency units**. + +**Sca** : On/Off **station scan feature** in AM or FM modes on **encoder button**. In disabled state **encoder button will always act as a Step button**. + +**CW** : **CW Side mode**. Based on **LSB** or **USB** modulation. + +### Display elements description + +

+ Icon +

+ + **1**. **Current modulation**. From **149** to **30000** KHz you have **AM/LSB/USB/CW** modulations available. When **Sync** is active in **SSB** modes, the **S** letter will appear near modulation status. In **FM** band you have only **FM** (or **WFM**) modulation. I don't have **NBFM patch** for **Si473x**, so it's not available now. + + **2**. **Bandwidth** status. Can be **AUTO** in **FM** mode. Not available in **CW** mode. + + **3**. **Current frequency**. In **FM** and **SSB** modes it has fractional part. + + **4**. **Band name**. There are such bands as **LW, MW, SW, CB** and **FM**. + +**5**. **Step value**. It is different for all modulations, **SSB has more precise steps**, but doesn't have large. AM has harger steps, but doesn't have precise. If it doesn't have a units suffix, that means that it's **Hz**. + +**6**. **Frequency units**. Units are always displayed, except when decimal part of frequency in **SSB** is **5 digits long**. + +**7**. **Volume**. This is sound volume of receiver. Could be value between **0** and **63**. When mute is enabled it displays letter **M** instead of volume. + +**8**. **Battery charge level** in percents. Requires **voltage divider** soldered to **A2** pin. Not displayed if not connected. When charge level is too low or USB connected on some revisions this indicator will display **---**. + +# How to make the battery charge level display? + +You will need a soldering iron and two resistors of **10kΩ** each. Solder one resistor to the negative (ground) and the other directly to the positive of the lithium battery. Then solder their meeting point together; this will be their midpoint. You need to solder it to pin **A2** on the controller. Thus, half of the battery voltage will be applied to it. By making such a simple modification, the **ATX_EX** firmware will automatically detect the connected battery voltage pin and start displaying the charge. diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20.png b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20.png new file mode 100644 index 00000000..7571e919 Binary files /dev/null and b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20.png differ diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20_display.png b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20_display.png new file mode 100644 index 00000000..e19da16c Binary files /dev/null and b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20_display.png differ diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20_settings.png b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20_settings.png new file mode 100644 index 00000000..35fcd2b6 Binary files /dev/null and b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/img/ats20_settings.png differ diff --git a/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/rus/README.md b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/rus/README.md new file mode 100644 index 00000000..2d9efbe3 --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/ATS20_ATS_EX/rus/README.md @@ -0,0 +1,160 @@ +# Прошивка ATS_EX для приёмника ATS-20 +## Общее описание +Это расширенная прошивка для приёмников **ATS-20**, которые работают на **Arduino Nano** и чипе DSP приёмника **Si473x**. +Прошивка ATS_EX создана мной, **Goshante**, основана на наработках прошивки **PU2CLR** и вдохновлена прошивкой от **swling.ru** с закрытым исходным кодом. + + +**Последняя версия:** v1.18 (03.04.2024) + +**Ссылка на скачивание готового .hex файла прошивки:** [>>> Нажмите здесь <<<](https://github.com/goshante/ats20_ats_ex/releases/download/v1.18/ATS_EX_v1.18.hex) + +Задонатить автору можно здесь :) https://t.me/tribute/app?startapp=d7vv +

+ Icon +

+ +# Особенности + + - Полностью **переработан интерфейс**. Больше никаких растянутых шрифтов. Минималистичный и читабельный интерфейс. Семисегмантный шрифт частоты был воссоздан мной таким, каким он был в прошивке от swling. + - Полностью **переработаны элементы управления**. Краткое руководство пользователя находится здесь, ниже. + - **BFO теперь часть общей частоты приёмника, пока активен SSB** и регулируется общим шагом частоты, больше никаких выделенных отдельных настроек BFO, которые только путают и делают ход по частоте неудобным и ужасным. В режиме SSB доступны более точные шаги. + - **LW** Диапазон: От **153** до **520** KГц + - **MW** Диапазон: От **520** до **1710** KГц + - **SW** Диапазон: От **1710** до **30000** КГц (С большим количеством поддиапазонов) + - **FM** Диапазон: От **64** до **108** МГц. + - **Динамический RDS** для **FM** радиостанций с возможностью переключения строк информации (Отображает до 16 символов). + - **Сканирование станций** в AM/FM режимах. + - Добавлена **CW модуляция**. + - **RSSI** с плохим дизайном был удалён и заменен на опционально отображаемую шкалу, подобную **S-метру**. + - Все **КВ (SW) диапазоны** теперь ощущаются как один большой диапазон **от 1710 до 30000 КГц**. Всё еще можно переключаться между SW диапазонами, но они больше не ограничивают шаг частоты пределами поддиапазона. + - **Настройка частот стала максимально плавной** в режиме SSB благодаря объединению частоты приема с BFO. Грубое переключение частот теперь происходит каждые 16 КГц (**бесшовная настройка в обоих направлениях покрывает полные 32 КГц, намного меньше "пшиков".**). В не-SSB режимах теперь вы также можете настраивать частоту быстрее, **энкодер должен реагировать более отзывчиво**. + - **Доступно множество шагов для каждого режима**. В AM у вас есть шаги 1/5/9/10/50/100k/1M, в SSB есть шаги 1/5/9/10k и шаги 10/25/50/100/500Гц для более точной настройки. В режиме FM у вас есть шаги 10k/100k/1M. + - Добавлена страница настроек. Вы можете настроить **Управление усилением и ATT**, **Мягкое выключение звука**, **Автоматическое управление громкостью** (AVC), Включить или Выключить **AVC для режима SSB**, **Деэмфазис** для режима FM и включить или выключить **режим синхронизации SSB**, и прочие другие полезные настройки. + - **Калибровка** сдвига частоты **BFO**. + - Регулируемая **яркость экрана**. + - Регулируемая **частота процессора**, возможность снизить её на 50% для большего энергосбережения батареи и уменьшения помех. + - Добавлена **кнопка выключения звука** и **кнопка включения/выключения дисплея**. + - Добавлено **отображение состояния заряда батареи** (Требуется небольшой мод: Сделать **делитель напряжения** из двух резисторов по 10 КОм и припаять его среднюю точку к пину **A2**) + - Добавлен **S-метр** + - **Контроллер Atm328p теперь работает на полной частоте**. Управление должно быть более отзывчивым. (Не знаю, как это влияет на расход батареи.) + - Рефакторинг кода, оптимизации + - Исправлены некоторые баги + +## О просьбах нового функционала... +Пожалуйста, не просите добавлять новый функционал. Эта прошивка достигла предела максимального размера flash-памяти и оптимизирована настолько, насколько это было вообще возможно и для нового функционала буквально не хватит места. В ней также имеется бутлоадер, и я не собираюсь его вырезать. +Я принимаю только тикеты об ошибках и багах. Эта прошивка уже работает на грани возможностей приёмника ATS-20. + +Если вы хотите добавить новый функционал - вы можете сделать это сами. Исходный код открыт и доступен в этом репозитории. + + # Как прошить мой приёмник? + Вы можете использовать любой софт, способный прошивать .hex файл прошивки на Arduino. Вам понадобится только Micro USB кабель и **драйвер USB UART** (вероятнее всего, это будет драйвер для контроллера **CH341**). Я рекомендую использовать программу **AVRDUDESS**, если вы используете Windows. Это простой инструмент с графическим интерфейсом, который может дампить и прошивать файлы прошивок на микроконтроллеры Atmel(Ардуино). + + **Есть два типа ATS-20 приёмников**: Те, которые на **Arduino Nano** и на **Arduino Uno**. Их довольно просто отличить. Нужно разобрать и посмотреть. Те, что на Nano - у них Arduino распаяна отдельной платой сверху в основную плату. Если внутри нет распаянной платы и сразу распаян чип ATMega368, все компоненты находятся на одной плате, то у вас ревизия на Arduino Uno. Это важно знать перед прошивкой. + + Нужно выбрать в AVRDUDESS из раздела **Presets** либо **"Arduino Nano (ATmega328P)"**, либо **"Arduino Uno (ATmega328P)"** , затем выбрать ваш фактический **COM порт** и в разделе **"Flash"** указать **путь к файлу прошивки .hex** . Выберите режим режим **Write** и нажать рядом кнопку **Go**. После чего пойдет прошивка. В случае чего не бойтесь, если вы выбрали неправильные настройки у вас просто не получится его прошить и на приёмнике останется старая прошивка. + +Или можно собрать прошивку самостоятельно из исходников, которые лежат в этом репозитории. Я использую Visual Studio 2022 с расширением VSMicro, которое использует Arduino IDE 1.8. Вы можете просто использовать Arduino IDE, собрать скетч самостоятельно и загрузить его в контроллер прямо из IDE. + +## У меня не прошивается, что делать? +Такое бывает, если вы прошиваете через USB порт, который не способен отдавать достаточный ток для запитывания контроллера вместе со всем устройством. Если **включить приёмник** перед подключением к ПК и подключить его к порту **USB 3.0**, то шансов на успех будет куда больше. Да, приёмник не использует USB 3.0, там версия 2.0, но порты третьей версии способны отдавать больший ток. И если не помогает всё равно, попробуйте перепрошить приёмник через Arduino IDE **пустой прошивкой**, соберите пустой проект без логики и зашейте в приёмник. Если вы прошиваете Arduino Nano, убедитесь, что вы выбрали версию платы со старым загрузчиком (**Old Bootloader**). После этого попробуйте снова зашить туда саму прошивку. + +# Руководство пользователя +**ВНИМАНИЕ:** После прошивки настоятельно **рекомендуется сбросить память EEPROM**. Для этого просто удерживайте **Кнопку Энкодера** при включении приемника. + +### Функционал кнопок +#### Кнопка **BAND+** + 1. **Выбор диапазона** : Короткое нажатие для входа в режим выбора диапазона. Выберите диапазон с помощью **Вращения Энкодера** и подтвердите с помощью **Кнопки Энкодера** или повторным нажатием кнопки **BAND+**. Либо зажмите **BAND+** для быстрой прокрутки диапазонов вперёд. В **SW диапазоне** листает к ближайшему поддиапазону. После крайнего SW поддиапазона переключает на следующий (либо предыдущий) диапазон. + 2. **Переключение страницы настроек** : Данная кнопка переключает **страницы настроек** во время того, как открыты настройки. +#### Кнопка **BAND-** + 1. **Меню настроек** : Короткое нажатие для открытия/закрытия **меню настроек**. При **закрытии** настроек, все настройки **сохраняются в EEPROM**. + 2. **Выбор диапазона** : Долгое нажатие **BAND-** быстро прокручивает диапазоны назад. +#### Кнопка **VOL+** + 1. **Регулировка громкости** : Короткое нажатие для входа в режим **регулировки громкости**. Установите громкость с помощью **Вращения Энкодера** и подтвердите с помощью **Кнопки Энкодера** или повторным нажатием кнопки **VOL+**. + 2. **Быстрое увеличение громкости** : Зажмите для быстрого увеличения громкости. +#### Кнопка **VOL-** + 1. **Mute (приглушение громкости)** : Короткое нажатие для **включения и выключения беззвучного режима**. + 2. **Быстрое уменьшение громкости** : Зажмите для быстрого уменьшения громкости. +#### Кнопка **STEP** + 1. **Регулировка шага** : Короткое нажатие для входа в режим **регулировки шага**. Установите шаг с помощью **Вращения Энкодера** и подтвердите с помощью **Кнопки Энкодера** или повторным нажатием кнопки **STEP**. + 2. **Шкала уровня сигнала** : Длинное нажатие клавиши показывает снизу полосу уровня сигнала, **подобную S-метру**. Можно выключить повторным длинным или коротким нажатием. Либо сменой диапазона. +#### Кнопка **AGC** + 1. **Включение/выключение дисплея** : Короткое нажатие работает как выключатель дисплея. + 2. **Sync режим для SSB**: Долгое нажатие для переключения режима **Sync** при активной **SSB** модуляции. +#### Кнопка **BW** + 1. **Регулировка полосы пропускания** : Короткое нажатие для входа в режим **регулировки ширины полосы пропускания**. Установите шаг с помощью **Вращения Энкодера** и подтвердите с помощью **Кнопки Энкодера** или повторным нажатием кнопки **BW**. Для каждой модуляции свой набор шагов. +#### Кнопка **MODE** + 1. **Выбор модуляции в AM/SSB режиме** : Короткое нажатие для **переключения между модуляциями** в AM/SSB режиме. На диапазоне **FM** единственная доступная модуляция - **WFM** (FM с широкой полосой) и переключать модуляцию в FM диапазоне нельзя (ограничения микросхемы Si4735). На всех остальных диапазонах доступны следующие модуляции: **AM/USB/LSB/CW**. Во всех модуляциях(Особенно в SSB) улучшенная настройка частоты без прерывания на каждом шаге. + 2. **RDS в FM диапазоне** : Короткое нажатие позволяет в нижней строке под частотой отображать **строку с метаданными**, декодированную из RDS трафика текущей радиостанции. Пока не утеряна синхронизация, надпись может **динамически обновляться**, если FM радиостанция циклически выводит туда различную информацию. В этом режиме при помощи **Кнопки Энкодера** можно переключать **3 различных режима информации RDS**: **Название станции**, **Информация о станции** и **Информация о программе**. Если какая-то из ячеек информации не декодировалась или отсутствует, то будут отобрадаться три точки - **...**. Если при включении RDS не отобразились данные, то нужно выключить его и включить снова либо переместиться энкодером на другую частоту и вернуться обратно, чтобы микросхема Si4735 могла синхронизироваться с потоком RDS трафика. Если синхронизация сбилась она восстанавливается автоматически только после переключения частоты. +#### **Вращение Энкодера** +1. **Настройка Частоты** : В режиме радио (обычный режим) вращение делает ход по частоте с указанным внизу экрана шагом. +2. **навигация по настройкам** : В режиме настроек при помощи энкодера можно выбирать нужную настройку и после выбора изменять её значение. +#### **Кнопка Энкодера** +1. **Сканирование частоты** : Работает **только в FM и АМ** модуляциях и только если в настройках параметр **Sca** включен (**On**). Нажмите, чтобы сканировать станции по частоте в последнем направлении с заданным шагом. Вращайте энкодер либо нажмите на него, чтобы **остановить сканирование**. +2. **Универсальная кнопка** : Подтверждает настройки, делает выбор, переключает режимы RDS. +3. **Сброс EEPROM** : Важный функционал, позволяющий **сбросить настройки к стандартным**. Для этого включите приёмник с заранее зажатой кнопкой энкодера. После этого должна появиться надпись **EEPROM RESET**. +4. **Быстрая настройка шага** : Только для **SSB режима** - нажатие позволяет сразу перейти в режим настройки шага. Также работает и в **FM/AM** режимах, если в настройках параметр **Sca** выключен (**Off**). + +### Настройки +Навигация в настройках с помощью **Вращения Энкодера**, подтверждение выбора с помощью **Кнопки Энкодера**, изменение значения с помощью **Вращения Энкодера** и сохранение его с помощью снова той же **Кнопки Энкодера**. Закрыть настройки можно кнопкой **BAND-**. Переключаться между **страницами настроек** можно при помощи кнопки **BAND+**. + +

+ Icon +

+ +**ATT** : Значение **аттенюации**. **AUT** означает **Автоматическое управление усилением**. Этот параметр может быть либо **AUT**, либо числом от **1** до **37**. **Важно:** AGC режим (он же **AUT**) в любой из SSB модуляций добавит постоянный шум на фон (Так во всех прошивках). Он не сильно заметен, но это стоит принять к сведению. + +**SM** : **Soft Mute** (Софт мьют). Это число от **0** до **32**. + +**AVC** : **Automatic Volue Control** (Автоматическая регулировка громкости). Это число от **12** до **90**. + +**SVC** : Включает либо отключает **AVC режим для SSB**. + +**DeE** : Только для режима **FM**. Это значение **DeEmphasis** в микросекундах. Может быть либо **50**, либо **75**. + +**Syn** : Включить либо выключить **режим Sync для SSB**. + +**Scr** : **Регулировка яркости** дисплея. Это число от **5** до **125**. + +**SW** : **Единицы частоты** для AM модуляции в SW(КВ) диапазоне. Может быть либо **KHz**, либо **MHz**. В режиме MHz мегагерцы отделяются от частоты точкой. + +**SSM** : **SSB Soft Mute Mode**. Режим работы Soft Mute для SSB. Может быть либо **RSS** (SM зависит от значения RSSI) или **SNR** (SM зависит от соотношения сигнал/шум). + +**COF**: Фильтр отсечения боковых полос (**SSB Cutoff Filter**). Имеет 3 режима: **On** - полосовой фильтр для подавления обеих нежелательных частей боковой полосы и компонентов высокой частоты. **Off** - Фильтр с низким прохождением для подавления нежелательной боковой полосы. И **AUT** режим: автоматически включается и выключается в зависимости от Ширины полосы(**Bandwidth**) (выключен, когда ширина полосы выше **2.0 КГц**). + +**CPU**: **Частота процессора** (ATMega328p). Это полезно для **экономии заряда батареи**. Может быть либо **100**, либо **50%**. 100% - процессор работает на х1 от своей частоты, используя свой полный потенциал. 50% - процессор **работает на половине своей частоты**, он медленнее, но позволяет сэкономить больше заряда батареи. + +**RDS** : Порог **допустимости ошибок декодирования RDS**. Чем ниже это число, тем более точным будет декодирование RDS. Текст будет выглядеть лучше, но меньше шансов на успешную синхронизацию. Чем выше этот номер, тем проще будет синхронизироваться со стримом RDS данных, но текст может выглядеть искаженно. Это число от **0** до **3**. + +**BFO** : **Калибровка сдвига частоты BFO**. Одна единица этого параметра равна **1 Гц * 10** или **0.01 КГц**. Это значение отвечает за сдвиг частоты **BFO для SSB** на указанное количество единиц частоты. Если вам кажется, что частота SSB недостаточно точная, то вы можете откалибровать её при помощи этого значения. Допустимый диапазон значений от **-60 до 60 единиц**, что равнозначно значениям от **-0.60 КГц до 0.60 КГц**. + +**Uni** : Показать/Скрыть **отображение единиц измерения частоты**. + +**Sca** : Включить либо выключить **функцию сканирования** в режиме AM и FM при нажатии **кнопки энкодера**. В выключеном состоянии кнопка всегда **будет регулировать шаг**. + +**CW** : **Режим работы CW**. Базируется либо на **LSB**, либо на **USB** модуляции. + +### Описание элементов на дисплее + +

+ Icon +

+ +**1**. **Текущая модуляция**. От **149** до **30000** КГц доступны модуляции **AM/LSB/USB/CW**. Когда активен **Sync** в режимах **SSB**, рядом со статусом модуляции появится буква **S**. В диапазоне **FM** доступна только модуляция **FM** (или **WFM**). У меня нет патча **NBFM** (FM с узкой полосой пропускания) для **Si473x**, поэтому сейчас эта модуляция недоступна. + +**2**. **Ширина полосы приёма**. Может быть **AUTO** в режиме **FM**. Недоступно в режиме **CW**. + +**3**. **Частота**. В режимах **FM** и **SSB** показывается дробную часть для более точной настройки. + +**4**. **Диапазон**. Доступны такие диапазоны, как **LW, MW, SW, CB** и **FM**. + +**5**. **Значение шага**. Оно различно для всех модуляций, **SSB имеет более точные шаги**, но не имеет крупных. AM же имеет крупные шаги, но не имеет точных. Если после значения шага нет суффикса единиц измерения, это означает, что шаг отображается в **герцах**. + +**6**. **Единицы частоты**. Единицы измерения всегда отображаются, кроме случаев, когда десятичная часть частоты в **SSB** режиме длиной в **5 цифр**. + +**7**. **Громкость**. Это уровень громкости приемника. Может быть значением между **0** и **63**. Когда включен беззвучный режим (Mute), вместо уровня громкости будет отображаться буква **M**. + +**8**. **Уровень заряда батареи** in percents. Требует подключения пайкой **делителя напряжения** к пину **A2**. Не отображается если не подключено. Когда уровень заряда слишком низкий или USB кабель подсоединен (на некоторых ревизиях приёмника), то индикатор заряда будет отображать **---**. + +# Как сделать так, чтобы отображался уровень заряда батареи? + +Вам потребуется паяльник и два резистора по **10 КОм** каждый. Припаяйте один резистор к минусу(земле), второй напрямую к плюсу литиевой батареи. И спаяйте их вместе, это будет их средняя точка. Её нужно припаять к пину **A2** на контроллере. Таким образом на него будет подаваться половина напряжения батареи. Сделав такую простую модификацию **ATX_EX** прошивка автоматически определит подключенный пин напряжения батареи и начнет показывать заряд. diff --git a/examples/SI47XX_ATS20_ATS20+/README.md b/examples/SI47XX_ATS20_ATS20+/README.md deleted file mode 100644 index 7f20e07f..00000000 --- a/examples/SI47XX_ATS20_ATS20+/README.md +++ /dev/null @@ -1,395 +0,0 @@ -# ATS-20 and ATS-20+ sold on Aliexpress and eBay - -This firmware was specially developed for ATS-20 and ATS-20+ receivers. For more details, follow the instructions below. Click [here](https://github.com/pu2clr/SI4735/tree/master/examples/SI47XX_KITS/AliExpress) to see previous versions and contributions from other contributors. I would like to thank [EFWob](https://github.com/EFWob) for some interface improvements. - -Although the seller does not explicitly mention that the Arduino sketch used by the ATS-20 and ARS-20+ is based on this library (PU2CLR SI4735 Arduino Library), I’ve been testing successfully the sketch of this folder on some KITs and devices acquired by some Brazilians experimenters. __The author of this library does not know the seller of these kit or devices and does not have a commercial relationship with any commercial product that uses the PU2CLR SI4735 Arduino Library. It is important you understand that there is no guarantee that this sketch will work correctly in your current product__. - -__THE SSB PATCH CONTENT IS NOT PART OF THIS LIBRARY. THE AUTHOR OF THIS LIBRARY DOES NOT ENCOURAGE ANYONE TO USE THE SSB PATCHES CONTENT FOR COMMERCIAL PURPOSES. IN OTHER WORDS, WHILE THIS LIBRARY SUPPORTS SSB PATCHES, THE PATCHES THEMSELVES ARE NOT A PART OF THIS LIBRARY__. - - __ATTENTION: If you do not have experience with Arduino platform, please, do not try to upload the sketches available here to your receiver. It can make your device stop working.__ - -__IN NO EVENT SHALL THE AUTHOR OF THIS LIBRARY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE PRODUCT__. - -__Again: The author of this library is not responsible for any claims, damages, or other liabilities that may arise, whether through a contract, tort, or otherwise, related to the product. In summary, the author is disclaiming any legal responsibility related to the use or consequences of using the product that utilizes the PU2CLR SI4735 Arduino Library.__ - -__PLEASE READ ALL TEXT BELOW BEFORE UPDATING THE FIRMWARE__ - - -## Summary - -* [Firmware Update via Desktop Arduino IDE](./#firmware-update-via-desktop-arduino-ide) -* [Steps to update the firmware using the regular approach](./#steps-to-update-the-firmware-using-the-regular-approach) -* [UPDATING THE FIRMWARE WITH USBasp interface](./#updating-the-firmware-with-usbasp-interface) - - - -## Related Content - - -* [SWLing - Any thoughts on this inexpensive Si4732-based receiver?](https://swling.com/blog/2021/04/any-thoughts-on-this-inexpensive-si4732-based-receiver/) -* [Kevin O'Reilly: SI4732 The end of the line?](https://youtu.be/A3Mus-7lTrk) -* [Si47XX for Radio Experimenters](https://www.facebook.com/groups/532613604253401) -* [SI4735 SI4732 All Band Radio Receiver User Group](https://www.facebook.com/groups/340642344032449) -* [SI4732 shortwave receiver](https://swli-05940-mi.blogspot.com/2021/04/si4732-shortwave-receiver.html) -* [Amazon - Customer reviews](https://www.amazon.com/product-reviews/B08ZHXWTS1) -* [Si4732 ATS-20 Test and Update - Open Source Receiver 0-30 / 64-108](https://youtu.be/381139I2DCg) -* [ATS-20 Si4732 receiver firmware update](https://blog.marxy.org/2021/06/ats-20-si4732-receiver-firmware-update.html) - - -The photos below show the KIT sold on Aliexpress and eBay. - -
- -![PL102BA-S V:2.1 10628](./images/ali_000.png) - - -## Be a member of Facebook Group [Si47XX for Radio Experimenters](https://www.facebook.com/groups/532613604253401/) - -# Firmware Update via Desktop Arduino IDE - -__PLEASE READ ALL TEXT BELOW BEFORE UPDATING THE FIRMWARE__ - - -1) For some reason, the standard method of firmware updating doesn't work on some versions of the ATS-20+. In this case, you should use the approach demonstrated in [this video](https://youtu.be/7e9yp1MgGI8?si=2t2k9sJHBDw1WZva). -2) For regular approach see [this video](https://youtu.be/K3T6GfjZH8E). - - -## New features - -* The final code (HEX file) is about 2.5K smaller than the previous one (now you have more memory to add new features); -* Twenty three bands. Two VHF(FM); one LW; two MW; and Nineteen SW bands covering from 1800 to 30000 kHz; -* Band, Volume, Step, Bandwidth, AGC/Attenuation are now controlled by encoder; -* __"Vol-" now can be used to control Automatic Volume Control (AVC) too. See the manual below__; -* __"BAND-" now can be used to control Softmute Attenuation too. See manual below__; -* All the previous status of the receiver can be rescued when you turn it on (including SSB mode, bandwidth, volume, frequency, BFO etc); -* __The bandwidth now is a property of the band (you can use different bandwidth for different bands)__; -* Bandwidth control on FM mode (Auto, 110, 84, 60 and 40 kHz); -* Bandwidth control on AM mode (1, 2, 2.5, 3, 4 and 6 kHz); -* Bandwidth control on SSB mode (0.5, 1, 1.2, 2.2, 3 and 4 kHz); -* AGC (Automatic Gain Control) and Attenuation control; -* Softmute Attenuation Control; -* FM/RDS presentation was improved; -* __The seek function was improved (it is more precise on FM mode)__. The seek direction is controlled by the encoder (clockwise or counter-clockwise. Press encoder push button for seeking -* Steps: 1, 5, 9, 10, 50 and 100 kHz; -* __Now you can configure MW band space to 9 or 10 kHz__; -* New FM band from 64 to 84 MHz; -* New MW band from 531 to 1701 kHz for Europe, Africa and Asia; -* The frequency on Display is bigger than the previous version; -* Now the bandwidth sequence is ordered by bandwidth values; -* After about 4 seconds, all command buttons are all disabled and the encoder control goes back to the frequency; -* The current status of the receiver will be stored only after 10 seconds of inactivity. - -__Please, read the manual below for more details__. - - -The steps below will guide you to update the firmware of the KIT - -DO NOT TRY IT IF YOU DON'T KNOW WHAT ARE YOU DOING. - -Be sure you are using the last version of the Arduino IDE. - -Read the comments of the sketch before uploading. - - -## Steps to update the firmware using the regular approach - -For some reason, the standard method of firmware updating doesn't work on some versions of the ATS-20+. In this case, you should use the approach demonstrated in [this video](https://youtu.be/7e9yp1MgGI8?si=2t2k9sJHBDw1WZva). - -__Follow the steps below to the regular approach.__ - -### Step 1 - -Open the Arduino IDE and go to the __Tools__ menu and select __Manage Libraries…__ - -![Firmware Update 01](./images/p01.png) - -### Step 2 - -Look for SI4735 library, select __PU2CLR SI4735__ and click on Update - -![Firmware Update 02](./images/p02.png) - - -### Sept 3 - -Check the version installed. The last version should be 2.0.9 or greater. - -![Firmware Update 03](./images/p03.png) - - -### Step 4 - -Install the Libraries __Tiny4kOLED__ and __TinyOLED-Fonts__ Arduino libraries for OLED. Use the same approach shown before. - - -![Firmware Update 04](./images/p04.png) - - -### Step 5 - -Select the right firmware source (.ino). Follow the exaple below. - -Go to the File Menu, select Examples item and look for the firmware for this receiver / KIT - -__Please, select the last version of the firmware.__ - - -![Firmware Update 06](./images/p06.png) - - - -### Step 6 - -Check the sketch you have just loaded from PU2CLR_SI4735 Examples folder - - -![Firmware Update 07](./images/p07.png) - - -### Step 7 - -Select the right Arduino Board. On Tools Menu, select Arduino AVR Boards and __Arduino Nano__. -Read the comments of the sketche. - -![Firmware Update 08](./images/p08.png) - -### Step 8 - -Select the right Processor. On Tools menu, select __“Processor:”__ item. -Depending on the version of the Arduino Nano used by your KIT, it can be __ATmega328p (Old Bootloader)__ or just __ATmega328p__. - -Select __ATmega328p (Old Bootloader)__ first. If you get error during uploading, try it again with __ATmega328p__. - -![Firmware Update 09](./images/p09.png) - - -### Setup 9 - -Connect your Receiver (Arduino device) to your computer and select the right COM port. The COM number will depend on your computer setup. - -![Firmware Update 10](./images/p10.png) - - -### Step 10 - -Finally, run the upload. - -![Firmware Update 11](./images/p11.png) - - -### Final result - - -![Firmware Update 12](./images/OLED01.png) - - - -![Firmware Update 12](./images/OLED02.png) - - -### Video about update the firmware via regular approach - -* (KIT ST4732 from China - Firmware Update)[https://youtu.be/K3T6GfjZH8E] - - - -# User Manual - -The functions of the ATS-20 and ATS-20+ buttons have been changed in order to allow the device to provide more features. Therefore, some buttons may offer more than one function. Please read carefully the commands available on the receiver implemented by this firmware. - - -## COMMANDS - -Follow the instruction to operate with the receiver. - -### DEALING WITH AUDIO VOLUME and Automatic Volume Control (AVC) - -Press the __VOL+__ push button quickly and rotate the encoder. Press the button again to disable the command or wait for 4s. If you keep this button pressed the volume audio will increase faster. - -Press the __VOL-__ push button quickly and rotate the encoder to increase or decrease the Automatic Volume Control parameter. This function can improve the Audio Quality. If you keep this button pressed the volume audio will decrease faster. - - -### SELECTING THE BAND and SoftMute Index - -Press the __BAND+__ push button quickly and rotate the encoder. Press the button again to disable the command or wait for 4s; Keep this button pressed to go to the next band faster. - -Press the __BAND-__ push button quickly and rotate the encoder to increase or decrease the Soft Mute Index parameter. This function improves the behavior of the station selection process. Keep this button pressed the go to the previous band faster. - - -### DEALING WITH AGC (Automatic Gain Control) AND ATTENUATION - -Press the __AGC__ push button and rotate the encoder. Press the button again to disable the command or wait for 4s; - -### DEALING WITH AVC (Automatic Volume Control) - -Press the __VOL-__ push button quickly and rotate the encoder to increase or decrease the Automatic Volume Control parameter. This function can improve the Audio Quality. - -### DEALING WITH SOFTMUTE AND ATTENUATION - -Press the push button labeled __"BAND-"__ and rotate the encoder. Press the button again to disable the command or wait for 4s. This button was used to control the band switch. Due to the new interface, this button has lost its original function. - - -### SELECTING BANDWIDTH - -Press the __BW__ push button and rotate the encoder. Press the button again to disable the command or wait for 4s; - -### SWITCHING THE STEP FREQUENCY - -Press the __STEP__ push button and rotate the encoder. Press the button again to disable the command or wait for 4s; -It you are using SSB and BFO is enabled, this button will change the BFO frequency step (10 or 25 Hz). - - -### SWITCHING THE MODE AM, LSB AND USB - -On AM mode press __MODE__ push button to switch to LSB mode; press MODE push button again to switch to USB mode; and finally press MODE push button again to switch AM mode. - -### VFO/BFO Switch - -To control the VFO and BFO, you can use the encoder push button on SSB mode. The display will show you if you are using VFO or BFO. - -### SEEK COMMAND - -On FM or AM modes press the encoder push button. -The direction of the seek up or seek down will depend on the last rotating movement of the encoder, clockwise and counterclockwise respectively. - -### RESET EEPROM - -__Turn your receiver on with the encoder push button pressed__. - - -See also: -* [#205 Si4732 ATS-20 Test and Update - Open Source Receiver 0-30 / 64-108](https://youtu.be/381139I2DCg) - - -
-
- -## UPDATING THE FIRMWARE WITH USBasp interface - -This method can be useful when the regular approach does not work. - -You can find the USBasp device on eBay and AliExpress. The photos below show the USBasp device used to update the ATS-20 and ATS-20+ receivers. - - - -![Firmware Update via USBasp 01](./images/USBasp_03.jpeg) - - -![Firmware Update via USBasp 02](./images/USBasp_02.jpeg) - - -![Firmware Update via USBasp 03](./images/USBasp_01.jpeg) - - -### Contextualization - -One year ago I published a tutorial (sections above) on how to update the ATS-20 Firmware using the regular way. I mean, via Arduino board USB. However, some experimenters have been telling me that this approach doesn't work in some cases, especially with the ATS-20+. At first, I thought this might be a problem with the configuration of these experimenters' computers. But, later, I noticed that the number of occurrences was higher than expected. So, more recently I decided to check it out for myself. - -In fact, when I tried to update the firmware of a ATS-20+ device got from AliExpress, I noticed that the Arduino Nano, for some reason, I do not know why, is not recognized by the Arduino IDE as usual. - -Normally, when we connect the Arduino board to the computer via the USB port, a serial port is created, allowing the computer to communicate with the Arduino board. However, that's not happening in my case with an ATS-20+ that I get. - -There are many reasons why the traditional method of updating is not working on this device, including a USB interface problem on the Arduino board. - -If that is your issue and assuming you've already installed the Arduino IDE on your computer, please, follow these instruction: - - -### Steps to update the firmware using USBasp - - -#### STEP 1 - -Get a USBasp interface with the 10-pin to 6-pin converter. You can find it on eBay or AliExpress (look for USBasp). - -### STEP 2 - -Install the latest version of the "PU2CLR SI4735 Arduino Library". [This video shows how you can do that](https://youtu.be/M9h-tlV_l-k). - - -#### Open the Arduino IDE and go to the __Tools__ menu and select __Manage Libraries…__ - -![Firmware Update 01](./images/p01.png) - -#### Look for SI4735 library, select __PU2CLR SI4735__ and click on Install or Update - -![Firmware Update 02](./images/p02.png) - - -#### Also install the Libraries __Tiny4kOLED__ and __TinyOLED-Fonts__ Arduino libraries for OLED. Use the same approach shown before. - -![Firmware Update 04](./images/p04.png) - - -__ ATTENTION: If you are using Windows, please install the USBasp driver. See [USBASP Installation in Windows 10](https://www.instructables.com/USBASP-Installation-in-Windows-10/)__. It can work for oldest windows version. - - - -#### STEP 3 - -Check THE youtube tutorials below to know how to use USBasp on Arduino Boards. - -* [How to Use USBasp Programmer with Arduino Boards](https://youtu.be/ToKerwRR-70) -* [Programming Arduino Nano with V2.0 USBASP ICSP :DAControl](https://youtu.be/T1OkdPMRBzA) - -#### STEP 4 - -Remove the top cover. You don't need to remove all the screws. Only the top four. Be careful when removing the top cover. There is a speaker connected to the board and the wires can be broken. - -#### STEP 5 - -Configure the Arduino IDE to install the MiniCore Board. (See how to do this here)[https://github.com/MCUdude/MiniCore#how-to-install]. - -#### STEP 6 - -On Arduino IDE; menu Tools; Board:; Boards Manager, Install MiniCore (An Arduino core for the ATmega328, ATmega168, ATmega88, ATmega48 and ATmega8, all running a custom version of Optiboot for increased functionality). See this video to know more: [Install MiniCore Arduino IDE Additional Boards for ATmega328, ATmega168, ATmega88, ATmega48 ATmega8](https://youtu.be/YLTuRN1GjCU). - -#### STEP 7 - -On Arduino IDE, menu File, open the source code of the firmware you want to use on ATS-20 or ATS-20+ - -#### STEP 8 - -On Arduino IDE; menu Tools; Board:, select the MiniCore board; - -Follow theses setup: a)Board:"Atmega328"; b)Clock:"External 16MHz"; c)BOD:"BOD Disabled"; c) EEPROM: "EEPROM retained"; d)Compiler LTO: "LTO enabled"; e) Variant: "328P / 328PA"; f) Bootloader: "No bootloader". - -#### STEP 9 - -On Arduino Menu; Programmer: select USBasp (MiniCore) or USBasp slow (MiniCore). - - -#### STEP 10 - -Connect the USBasp device to the Arduino Board (ISP interface) and to the computer. See photos below. - -##### USBasp device connected to Arduino Nano board - -![Firmware Update via USBasp connected to the Arduino Nano 01](./images/USBasp_Nano_01.jpeg) - -Please, attention to the arrow-shaped bump highlighted in red. See photo below. - -![Firmware Update via USBasp connected to the Arduino Nano 02](./images/USBasp_Nano_02.jpeg) - - -##### USBasp device connected to the computer - - -![Firmware Update via USBasp connected to the computer](./images/USBasp_computer_01.jpeg) - - -#### STEP 11 - -Finally, turn the receiver on and click on the Upload icon or on the Sketch menu, select the option Upload. - - - -[See this video about this tutorial](https://youtu.be/7e9yp1MgGI8) - - - -### Problems and solutions reported for some experimenters - -#### By Pierangelo Cosimo Dal Maso: - -* MiniCore didn't recognise my USBAsp clone, was looking for usbvendor/usbproduct tags on usb device. I had to comment the corrisponding two lines in AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.1.3\avrdude.conf file. -* MiniCore fails when generating source listing if your user home directory have spaces in its name. I removed the listing generating step from file c:\Users\xxx yyy\AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.1.3\platform.txt diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.cpp b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.cpp similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.cpp rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.cpp diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.h b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.h new file mode 100755 index 00000000..ca04e8af --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/Rotary.h @@ -0,0 +1,29 @@ +// Rotary encoder library for Arduino. +#include "Arduino.h" + +#ifndef rotary_h +#define rotary_h + +// Enable this to emit codes twice per step +// #define HALF_STEP + +#define ENABLE_PULLUPS // Enable weak pullups + +// Values returned by 'process' +#define DIR_NONE 0x0 // No complete step yet +#define DIR_CW 0x10 // Clockwise step +#define DIR_CCW 0x20 // Anti-clockwise step + +class Rotary +{ + public: + Rotary(char, char); + // Process pin(s) + unsigned char process(); + private: + unsigned char state; + unsigned char pin1; + unsigned char pin2; +}; +#endif + diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9.ino b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9.ino similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9.ino rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9.ino diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.cpp b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.cpp similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.cpp rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.cpp diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.h b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.h similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.h rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SimpleButton.h diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/leiame.txt b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/leiame.txt similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/leiame.txt rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/leiame.txt diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/pull.txt b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/pull.txt similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/pull.txt rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/pull.txt diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/readme.md b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/readme.md new file mode 100644 index 00000000..9951e5da --- /dev/null +++ b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/readme.md @@ -0,0 +1,35 @@ +##Patch content + +This sketch will download a SSB patch to your SI4735 device (patch_init.h). It will take about 8KB to 15KB of the Arduino memory. + +###Patch origin + +The SSB patch content is __not a part of this library__. +These patches were published by Mr. [Vadim Afonkin](https://youtu.be/fgjPGnTAVgM) on his Dropbox repository. +The author of this Si4735 Arduino Library does not encourage anyone to use the SSB patches content for +commercial purposes. In other words, while this library supports SSB patches, the patches in and of themselves +are not a part of this library. + +###What is a patch? + +In this context, a patch is a piece of software used to change the behavior of the SI4735 device. +There is little information available about patching the SI4735. The following information is the +understanding of the author of this project and is not necessarily correct. + +A patch is executed internally (run by internal MCU) of the device. Usually, patches +are used to fix bugs or add new features over what the firmware installed +in the internal ROM of the device offers. Patches for the SI4735 are distributed in binary +form and are transferred to the internal RAM of the device by the host MCU (in this case, Arduino boards). + +Since the RAM is volatile memory, the patch stored on the device gets lost when you turn off +the system. Consequently, the content of the patch has to be transferred to the device every +time the device is powered up. + +###ATTENTION: +The author of this project cannot guarantee that procedures shown +here will work in your development environment. Proceed at your own risk. +This library works with the I²C communication protocol to send an SSB extension +PATCH to SI4735-D60 and SI4732-A10 devices. Once again, __the author disclaims any +and all liability for any damage or effects__ this procedure may have on your devices. +Proceed at your own risk. + diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/user_manual.txt b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/user_manual.txt similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/user_manual.txt rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/user_manual.txt diff --git a/examples/SI47XX_ATS20_ATS20+/images/OLED01.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/OLED01.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/OLED01.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/OLED01.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/OLED02.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/OLED02.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/OLED02.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/OLED02.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/USBasp_01.jpeg b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_01.jpeg similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/USBasp_01.jpeg rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_01.jpeg diff --git a/examples/SI47XX_ATS20_ATS20+/images/USBasp_02.jpeg b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_02.jpeg similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/USBasp_02.jpeg rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_02.jpeg diff --git a/examples/SI47XX_ATS20_ATS20+/images/USBasp_03.jpeg b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_03.jpeg similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/USBasp_03.jpeg rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_03.jpeg diff --git a/examples/SI47XX_ATS20_ATS20+/images/USBasp_Nano_01.jpeg b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_Nano_01.jpeg similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/USBasp_Nano_01.jpeg rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_Nano_01.jpeg diff --git a/examples/SI47XX_ATS20_ATS20+/images/USBasp_Nano_02.jpeg b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_Nano_02.jpeg similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/USBasp_Nano_02.jpeg rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_Nano_02.jpeg diff --git a/examples/SI47XX_ATS20_ATS20+/images/USBasp_computer_01.jpeg b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_computer_01.jpeg similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/USBasp_computer_01.jpeg rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/USBasp_computer_01.jpeg diff --git a/examples/SI47XX_ATS20_ATS20+/images/ali_000.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/ali_000.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/ali_000.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/ali_000.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p01.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p01.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p01.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p01.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p02.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p02.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p02.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p02.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p03.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p03.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p03.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p03.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p04.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p04.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p04.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p04.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p05.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p05.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p05.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p05.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p06.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p06.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p06.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p06.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p07.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p07.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p07.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p07.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p08.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p08.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p08.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p08.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p09.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p09.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p09.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p09.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p10.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p10.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p10.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p10.png diff --git a/examples/SI47XX_ATS20_ATS20+/images/p11.png b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p11.png similarity index 100% rename from examples/SI47XX_ATS20_ATS20+/images/p11.png rename to examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/images/p11.png diff --git a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/readme.md b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/readme.md index 9951e5da..7f20e07f 100644 --- a/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/readme.md +++ b/examples/SI47XX_ATS20_ATS20+/SI473X_ALL_IN_ONE_OLED_RDS_CHINESE_V9/readme.md @@ -1,35 +1,395 @@ -##Patch content +# ATS-20 and ATS-20+ sold on Aliexpress and eBay -This sketch will download a SSB patch to your SI4735 device (patch_init.h). It will take about 8KB to 15KB of the Arduino memory. +This firmware was specially developed for ATS-20 and ATS-20+ receivers. For more details, follow the instructions below. Click [here](https://github.com/pu2clr/SI4735/tree/master/examples/SI47XX_KITS/AliExpress) to see previous versions and contributions from other contributors. I would like to thank [EFWob](https://github.com/EFWob) for some interface improvements. -###Patch origin +Although the seller does not explicitly mention that the Arduino sketch used by the ATS-20 and ARS-20+ is based on this library (PU2CLR SI4735 Arduino Library), I’ve been testing successfully the sketch of this folder on some KITs and devices acquired by some Brazilians experimenters. __The author of this library does not know the seller of these kit or devices and does not have a commercial relationship with any commercial product that uses the PU2CLR SI4735 Arduino Library. It is important you understand that there is no guarantee that this sketch will work correctly in your current product__. -The SSB patch content is __not a part of this library__. -These patches were published by Mr. [Vadim Afonkin](https://youtu.be/fgjPGnTAVgM) on his Dropbox repository. -The author of this Si4735 Arduino Library does not encourage anyone to use the SSB patches content for -commercial purposes. In other words, while this library supports SSB patches, the patches in and of themselves -are not a part of this library. +__THE SSB PATCH CONTENT IS NOT PART OF THIS LIBRARY. THE AUTHOR OF THIS LIBRARY DOES NOT ENCOURAGE ANYONE TO USE THE SSB PATCHES CONTENT FOR COMMERCIAL PURPOSES. IN OTHER WORDS, WHILE THIS LIBRARY SUPPORTS SSB PATCHES, THE PATCHES THEMSELVES ARE NOT A PART OF THIS LIBRARY__. -###What is a patch? + __ATTENTION: If you do not have experience with Arduino platform, please, do not try to upload the sketches available here to your receiver. It can make your device stop working.__ -In this context, a patch is a piece of software used to change the behavior of the SI4735 device. -There is little information available about patching the SI4735. The following information is the -understanding of the author of this project and is not necessarily correct. +__IN NO EVENT SHALL THE AUTHOR OF THIS LIBRARY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE PRODUCT__. -A patch is executed internally (run by internal MCU) of the device. Usually, patches -are used to fix bugs or add new features over what the firmware installed -in the internal ROM of the device offers. Patches for the SI4735 are distributed in binary -form and are transferred to the internal RAM of the device by the host MCU (in this case, Arduino boards). +__Again: The author of this library is not responsible for any claims, damages, or other liabilities that may arise, whether through a contract, tort, or otherwise, related to the product. In summary, the author is disclaiming any legal responsibility related to the use or consequences of using the product that utilizes the PU2CLR SI4735 Arduino Library.__ -Since the RAM is volatile memory, the patch stored on the device gets lost when you turn off -the system. Consequently, the content of the patch has to be transferred to the device every -time the device is powered up. +__PLEASE READ ALL TEXT BELOW BEFORE UPDATING THE FIRMWARE__ -###ATTENTION: -The author of this project cannot guarantee that procedures shown -here will work in your development environment. Proceed at your own risk. -This library works with the I²C communication protocol to send an SSB extension -PATCH to SI4735-D60 and SI4732-A10 devices. Once again, __the author disclaims any -and all liability for any damage or effects__ this procedure may have on your devices. -Proceed at your own risk. +## Summary + +* [Firmware Update via Desktop Arduino IDE](./#firmware-update-via-desktop-arduino-ide) +* [Steps to update the firmware using the regular approach](./#steps-to-update-the-firmware-using-the-regular-approach) +* [UPDATING THE FIRMWARE WITH USBasp interface](./#updating-the-firmware-with-usbasp-interface) + + + +## Related Content + + +* [SWLing - Any thoughts on this inexpensive Si4732-based receiver?](https://swling.com/blog/2021/04/any-thoughts-on-this-inexpensive-si4732-based-receiver/) +* [Kevin O'Reilly: SI4732 The end of the line?](https://youtu.be/A3Mus-7lTrk) +* [Si47XX for Radio Experimenters](https://www.facebook.com/groups/532613604253401) +* [SI4735 SI4732 All Band Radio Receiver User Group](https://www.facebook.com/groups/340642344032449) +* [SI4732 shortwave receiver](https://swli-05940-mi.blogspot.com/2021/04/si4732-shortwave-receiver.html) +* [Amazon - Customer reviews](https://www.amazon.com/product-reviews/B08ZHXWTS1) +* [Si4732 ATS-20 Test and Update - Open Source Receiver 0-30 / 64-108](https://youtu.be/381139I2DCg) +* [ATS-20 Si4732 receiver firmware update](https://blog.marxy.org/2021/06/ats-20-si4732-receiver-firmware-update.html) + + +The photos below show the KIT sold on Aliexpress and eBay. + +
+ +![PL102BA-S V:2.1 10628](./images/ali_000.png) + + +## Be a member of Facebook Group [Si47XX for Radio Experimenters](https://www.facebook.com/groups/532613604253401/) + +# Firmware Update via Desktop Arduino IDE + +__PLEASE READ ALL TEXT BELOW BEFORE UPDATING THE FIRMWARE__ + + +1) For some reason, the standard method of firmware updating doesn't work on some versions of the ATS-20+. In this case, you should use the approach demonstrated in [this video](https://youtu.be/7e9yp1MgGI8?si=2t2k9sJHBDw1WZva). +2) For regular approach see [this video](https://youtu.be/K3T6GfjZH8E). + + +## New features + +* The final code (HEX file) is about 2.5K smaller than the previous one (now you have more memory to add new features); +* Twenty three bands. Two VHF(FM); one LW; two MW; and Nineteen SW bands covering from 1800 to 30000 kHz; +* Band, Volume, Step, Bandwidth, AGC/Attenuation are now controlled by encoder; +* __"Vol-" now can be used to control Automatic Volume Control (AVC) too. See the manual below__; +* __"BAND-" now can be used to control Softmute Attenuation too. See manual below__; +* All the previous status of the receiver can be rescued when you turn it on (including SSB mode, bandwidth, volume, frequency, BFO etc); +* __The bandwidth now is a property of the band (you can use different bandwidth for different bands)__; +* Bandwidth control on FM mode (Auto, 110, 84, 60 and 40 kHz); +* Bandwidth control on AM mode (1, 2, 2.5, 3, 4 and 6 kHz); +* Bandwidth control on SSB mode (0.5, 1, 1.2, 2.2, 3 and 4 kHz); +* AGC (Automatic Gain Control) and Attenuation control; +* Softmute Attenuation Control; +* FM/RDS presentation was improved; +* __The seek function was improved (it is more precise on FM mode)__. The seek direction is controlled by the encoder (clockwise or counter-clockwise. Press encoder push button for seeking +* Steps: 1, 5, 9, 10, 50 and 100 kHz; +* __Now you can configure MW band space to 9 or 10 kHz__; +* New FM band from 64 to 84 MHz; +* New MW band from 531 to 1701 kHz for Europe, Africa and Asia; +* The frequency on Display is bigger than the previous version; +* Now the bandwidth sequence is ordered by bandwidth values; +* After about 4 seconds, all command buttons are all disabled and the encoder control goes back to the frequency; +* The current status of the receiver will be stored only after 10 seconds of inactivity. + +__Please, read the manual below for more details__. + + +The steps below will guide you to update the firmware of the KIT + +DO NOT TRY IT IF YOU DON'T KNOW WHAT ARE YOU DOING. + +Be sure you are using the last version of the Arduino IDE. + +Read the comments of the sketch before uploading. + + +## Steps to update the firmware using the regular approach + +For some reason, the standard method of firmware updating doesn't work on some versions of the ATS-20+. In this case, you should use the approach demonstrated in [this video](https://youtu.be/7e9yp1MgGI8?si=2t2k9sJHBDw1WZva). + +__Follow the steps below to the regular approach.__ + +### Step 1 + +Open the Arduino IDE and go to the __Tools__ menu and select __Manage Libraries…__ + +![Firmware Update 01](./images/p01.png) + +### Step 2 + +Look for SI4735 library, select __PU2CLR SI4735__ and click on Update + +![Firmware Update 02](./images/p02.png) + + +### Sept 3 + +Check the version installed. The last version should be 2.0.9 or greater. + +![Firmware Update 03](./images/p03.png) + + +### Step 4 + +Install the Libraries __Tiny4kOLED__ and __TinyOLED-Fonts__ Arduino libraries for OLED. Use the same approach shown before. + + +![Firmware Update 04](./images/p04.png) + + +### Step 5 + +Select the right firmware source (.ino). Follow the exaple below. + +Go to the File Menu, select Examples item and look for the firmware for this receiver / KIT + +__Please, select the last version of the firmware.__ + + +![Firmware Update 06](./images/p06.png) + + + +### Step 6 + +Check the sketch you have just loaded from PU2CLR_SI4735 Examples folder + + +![Firmware Update 07](./images/p07.png) + + +### Step 7 + +Select the right Arduino Board. On Tools Menu, select Arduino AVR Boards and __Arduino Nano__. +Read the comments of the sketche. + +![Firmware Update 08](./images/p08.png) + +### Step 8 + +Select the right Processor. On Tools menu, select __“Processor:”__ item. +Depending on the version of the Arduino Nano used by your KIT, it can be __ATmega328p (Old Bootloader)__ or just __ATmega328p__. + +Select __ATmega328p (Old Bootloader)__ first. If you get error during uploading, try it again with __ATmega328p__. + +![Firmware Update 09](./images/p09.png) + + +### Setup 9 + +Connect your Receiver (Arduino device) to your computer and select the right COM port. The COM number will depend on your computer setup. + +![Firmware Update 10](./images/p10.png) + + +### Step 10 + +Finally, run the upload. + +![Firmware Update 11](./images/p11.png) + + +### Final result + + +![Firmware Update 12](./images/OLED01.png) + + + +![Firmware Update 12](./images/OLED02.png) + + +### Video about update the firmware via regular approach + +* (KIT ST4732 from China - Firmware Update)[https://youtu.be/K3T6GfjZH8E] + + + +# User Manual + +The functions of the ATS-20 and ATS-20+ buttons have been changed in order to allow the device to provide more features. Therefore, some buttons may offer more than one function. Please read carefully the commands available on the receiver implemented by this firmware. + + +## COMMANDS + +Follow the instruction to operate with the receiver. + +### DEALING WITH AUDIO VOLUME and Automatic Volume Control (AVC) + +Press the __VOL+__ push button quickly and rotate the encoder. Press the button again to disable the command or wait for 4s. If you keep this button pressed the volume audio will increase faster. + +Press the __VOL-__ push button quickly and rotate the encoder to increase or decrease the Automatic Volume Control parameter. This function can improve the Audio Quality. If you keep this button pressed the volume audio will decrease faster. + + +### SELECTING THE BAND and SoftMute Index + +Press the __BAND+__ push button quickly and rotate the encoder. Press the button again to disable the command or wait for 4s; Keep this button pressed to go to the next band faster. + +Press the __BAND-__ push button quickly and rotate the encoder to increase or decrease the Soft Mute Index parameter. This function improves the behavior of the station selection process. Keep this button pressed the go to the previous band faster. + + +### DEALING WITH AGC (Automatic Gain Control) AND ATTENUATION + +Press the __AGC__ push button and rotate the encoder. Press the button again to disable the command or wait for 4s; + +### DEALING WITH AVC (Automatic Volume Control) + +Press the __VOL-__ push button quickly and rotate the encoder to increase or decrease the Automatic Volume Control parameter. This function can improve the Audio Quality. + +### DEALING WITH SOFTMUTE AND ATTENUATION + +Press the push button labeled __"BAND-"__ and rotate the encoder. Press the button again to disable the command or wait for 4s. This button was used to control the band switch. Due to the new interface, this button has lost its original function. + + +### SELECTING BANDWIDTH + +Press the __BW__ push button and rotate the encoder. Press the button again to disable the command or wait for 4s; + +### SWITCHING THE STEP FREQUENCY + +Press the __STEP__ push button and rotate the encoder. Press the button again to disable the command or wait for 4s; +It you are using SSB and BFO is enabled, this button will change the BFO frequency step (10 or 25 Hz). + + +### SWITCHING THE MODE AM, LSB AND USB + +On AM mode press __MODE__ push button to switch to LSB mode; press MODE push button again to switch to USB mode; and finally press MODE push button again to switch AM mode. + +### VFO/BFO Switch + +To control the VFO and BFO, you can use the encoder push button on SSB mode. The display will show you if you are using VFO or BFO. + +### SEEK COMMAND + +On FM or AM modes press the encoder push button. +The direction of the seek up or seek down will depend on the last rotating movement of the encoder, clockwise and counterclockwise respectively. + +### RESET EEPROM + +__Turn your receiver on with the encoder push button pressed__. + + +See also: +* [#205 Si4732 ATS-20 Test and Update - Open Source Receiver 0-30 / 64-108](https://youtu.be/381139I2DCg) + + +
+
+ +## UPDATING THE FIRMWARE WITH USBasp interface + +This method can be useful when the regular approach does not work. + +You can find the USBasp device on eBay and AliExpress. The photos below show the USBasp device used to update the ATS-20 and ATS-20+ receivers. + + + +![Firmware Update via USBasp 01](./images/USBasp_03.jpeg) + + +![Firmware Update via USBasp 02](./images/USBasp_02.jpeg) + + +![Firmware Update via USBasp 03](./images/USBasp_01.jpeg) + + +### Contextualization + +One year ago I published a tutorial (sections above) on how to update the ATS-20 Firmware using the regular way. I mean, via Arduino board USB. However, some experimenters have been telling me that this approach doesn't work in some cases, especially with the ATS-20+. At first, I thought this might be a problem with the configuration of these experimenters' computers. But, later, I noticed that the number of occurrences was higher than expected. So, more recently I decided to check it out for myself. + +In fact, when I tried to update the firmware of a ATS-20+ device got from AliExpress, I noticed that the Arduino Nano, for some reason, I do not know why, is not recognized by the Arduino IDE as usual. + +Normally, when we connect the Arduino board to the computer via the USB port, a serial port is created, allowing the computer to communicate with the Arduino board. However, that's not happening in my case with an ATS-20+ that I get. + +There are many reasons why the traditional method of updating is not working on this device, including a USB interface problem on the Arduino board. + +If that is your issue and assuming you've already installed the Arduino IDE on your computer, please, follow these instruction: + + +### Steps to update the firmware using USBasp + + +#### STEP 1 + +Get a USBasp interface with the 10-pin to 6-pin converter. You can find it on eBay or AliExpress (look for USBasp). + +### STEP 2 + +Install the latest version of the "PU2CLR SI4735 Arduino Library". [This video shows how you can do that](https://youtu.be/M9h-tlV_l-k). + + +#### Open the Arduino IDE and go to the __Tools__ menu and select __Manage Libraries…__ + +![Firmware Update 01](./images/p01.png) + +#### Look for SI4735 library, select __PU2CLR SI4735__ and click on Install or Update + +![Firmware Update 02](./images/p02.png) + + +#### Also install the Libraries __Tiny4kOLED__ and __TinyOLED-Fonts__ Arduino libraries for OLED. Use the same approach shown before. + +![Firmware Update 04](./images/p04.png) + + +__ ATTENTION: If you are using Windows, please install the USBasp driver. See [USBASP Installation in Windows 10](https://www.instructables.com/USBASP-Installation-in-Windows-10/)__. It can work for oldest windows version. + + + +#### STEP 3 + +Check THE youtube tutorials below to know how to use USBasp on Arduino Boards. + +* [How to Use USBasp Programmer with Arduino Boards](https://youtu.be/ToKerwRR-70) +* [Programming Arduino Nano with V2.0 USBASP ICSP :DAControl](https://youtu.be/T1OkdPMRBzA) + +#### STEP 4 + +Remove the top cover. You don't need to remove all the screws. Only the top four. Be careful when removing the top cover. There is a speaker connected to the board and the wires can be broken. + +#### STEP 5 + +Configure the Arduino IDE to install the MiniCore Board. (See how to do this here)[https://github.com/MCUdude/MiniCore#how-to-install]. + +#### STEP 6 + +On Arduino IDE; menu Tools; Board:; Boards Manager, Install MiniCore (An Arduino core for the ATmega328, ATmega168, ATmega88, ATmega48 and ATmega8, all running a custom version of Optiboot for increased functionality). See this video to know more: [Install MiniCore Arduino IDE Additional Boards for ATmega328, ATmega168, ATmega88, ATmega48 ATmega8](https://youtu.be/YLTuRN1GjCU). + +#### STEP 7 + +On Arduino IDE, menu File, open the source code of the firmware you want to use on ATS-20 or ATS-20+ + +#### STEP 8 + +On Arduino IDE; menu Tools; Board:, select the MiniCore board; + +Follow theses setup: a)Board:"Atmega328"; b)Clock:"External 16MHz"; c)BOD:"BOD Disabled"; c) EEPROM: "EEPROM retained"; d)Compiler LTO: "LTO enabled"; e) Variant: "328P / 328PA"; f) Bootloader: "No bootloader". + +#### STEP 9 + +On Arduino Menu; Programmer: select USBasp (MiniCore) or USBasp slow (MiniCore). + + +#### STEP 10 + +Connect the USBasp device to the Arduino Board (ISP interface) and to the computer. See photos below. + +##### USBasp device connected to Arduino Nano board + +![Firmware Update via USBasp connected to the Arduino Nano 01](./images/USBasp_Nano_01.jpeg) + +Please, attention to the arrow-shaped bump highlighted in red. See photo below. + +![Firmware Update via USBasp connected to the Arduino Nano 02](./images/USBasp_Nano_02.jpeg) + + +##### USBasp device connected to the computer + + +![Firmware Update via USBasp connected to the computer](./images/USBasp_computer_01.jpeg) + + +#### STEP 11 + +Finally, turn the receiver on and click on the Upload icon or on the Sketch menu, select the option Upload. + + + +[See this video about this tutorial](https://youtu.be/7e9yp1MgGI8) + + + +### Problems and solutions reported for some experimenters + +#### By Pierangelo Cosimo Dal Maso: + +* MiniCore didn't recognise my USBAsp clone, was looking for usbvendor/usbproduct tags on usb device. I had to comment the corrisponding two lines in AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.1.3\avrdude.conf file. +* MiniCore fails when generating source listing if your user home directory have spaces in its name. I removed the listing generating step from file c:\Users\xxx yyy\AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.1.3\platform.txt diff --git a/examples/SI47XX_KITS/THIAGO_LIMA/SI473X_TFT_TOUCH_THIAGO_BETA_V4/SI473X_TFT_TOUCH_THIAGO_BETA_V4.ino b/examples/SI47XX_KITS/THIAGO_LIMA/SI473X_TFT_TOUCH_THIAGO_BETA_V4/SI473X_TFT_TOUCH_THIAGO_BETA_V4.ino index 2d024d62..71e02c0c 100644 --- a/examples/SI47XX_KITS/THIAGO_LIMA/SI473X_TFT_TOUCH_THIAGO_BETA_V4/SI473X_TFT_TOUCH_THIAGO_BETA_V4.ino +++ b/examples/SI47XX_KITS/THIAGO_LIMA/SI473X_TFT_TOUCH_THIAGO_BETA_V4/SI473X_TFT_TOUCH_THIAGO_BETA_V4.ino @@ -77,8 +77,8 @@ #include "Serif_plain_15.h" // ================================================= -//#define IhaveVertTFT -#define IhaveHoriTFT + #define IhaveVertTFT +// #define IhaveHoriTFT // ================================================= #include // SSB patch for whole SSBRX initialization string