segunda-feira, 31 de agosto de 2026

10kHz 225MHz Cobra 148 GTL 480 Canais e Clarificador.

Vocês, que curte e gosta dos projetos do blog, por gentileza olhem às propagandas e click para maiores detalhes no que interessar, desta forma vocês estarão ajudando o blog. Muito obrigado por acessar e ajudar o blog, conto com vocês muito agradecido.
Olá pessoal. Mês Julho pela primeira vez em 12 anos de blog, ficou sem postagem. Motivo: Tive um pequeno acidente de Moto dia 11 Junho, nada grave só alguns contusão muscular, ferimentos e dias de repouso, já estou recuperado, para honra e gloria do Altíssimo Senhor Deus. Este mês tem mais algumas implementações ou atualizações do sketch mês Junho, este aqui e vídeo do mesmo projeto código livre do Sr. Júlio Cesar, meus agradecimentos na divulgação deste famoso 10 kHz a 225 MHz versão 2. Bem publico o que estava faltando ainda simples neste sketch que foi super copiado. O clarificador com potenciômetro 1k com centro ou meio dia 00 Hz e início com 20 Hz até 8000 Hz ou 8 kHz mostrado no display acima do S/Meter. Postado mês Julho mas não postado. Modo canal novo neste sketch, com 80 canais em cada banda, A até F total 480 canais, faixa de PX CB 80 atualizada aqui do Brasil e outros países. Tive que perder muito tempo em atualizar o sketch para economizar espaço no Arduíno Nano, um sketch quando é muito copiado e modificado os hobbistas ou amantes como eu, que faz tais modificações vai empurrando do jeito que dá certo, assim vai inchando e ocupando muito espaço, são comandos repetidos rodando no setup e loop sem precisar, comandos que podem serem excluídos ou atualizados, diminuindo muito o espaço de armazenamento. Então este agora está bem leve mesmo com todas ás implementações deste mês. Sobre o clarificador: Vocês terão total 8 kHz em passos inicial meio 0 Hz de 20 Hz e depois de 10 Hz de deslocamento para direita e esquerda, esta faixa cobre quase de um canal para o outro em PX CB, por isso não deixei o STEP atuando, em SSB vocês poderão clarificar o áudio variando a frequência em RX e transmitir na mesma frequência se o rádio estiver alinhado corretamente. Ainda se quiserem aumentar o 8000Hz para 10000Hz ou 10kHz basta modificar a linha de comando que fica logo no começo do seu void loop, //LEITURA DO CLARIFICADOR. " long rawClarifier = map(clarRead, 0, 1023, -8000, 8000); // Converte para Hz (-8000 a +8000). Para esta " long rawClarifier = map(clarRead, 0, 1023, -10000, 10000); // Converte para Hz (-10000 a +10000) . Se quiser outro valor só modifique os números, mas cuidado quanto mais você sobe de frequência nesta linha de comando mais sensível fica clarificar agora cobre mais Hz. Na prática costuma ser imperceptível, mas é bom saber caso perceba o clarificador "menos, fino" ao girar. Modo canal: Ao pressionar o Encoder por alguns segundos, mesmo que você esteja em qualquer banda vai entrar a mensagem Canais D se tiver sido último canal e frequência visitada, 11m letras ou caracteres pequenos permanecerá sempre em todo modo canal, não coloquei para definir se é 12m em 24 MHz ou 10m em 28 MHz, simplesmente 11m em todos os canais de A a F, não tem muita importância para o operador.
Notaram no esquema do Arduíno os periféricos ou acessórios tem vários capacitores de 10nF a 100nF de desacoplamentos nos circuitos ligados aos pinos "X" do Arduino, poderá ser do pino "X" ao GND negativo ou pino "X" e +Vcc. Ligações de RF sempre com cabo coaxial curto, fiação curtas. Vejam abaixo algumas fotos finais deste projeto, desculpe-me não sou programador e tenho muitas dificuldades em acertar, para eu entregar um projeto destes a vocês aqui publicado, eu passo muito tempo para chegar no resultado final e ficar bom, são várias gravações. Faço isso porque gosto muito é como se eu estivesse fazendo para mim mesmo, sou rigoroso em tudo que faço, para mim ou presta ou não presta, se não prestar não servirá para mim e nem meus seguidores e não publico, só publico se eu não tiver notado o erro ou erros, mas este aqui vem atualizar o anterior do mês de Junho, fiz vários testes e está muito bom mesmo, vocês vão poderem adaptar seu transceptor Cobra 148GTL ou outros, com este DDS digital VFO com modo canal com memória total. Outras alterações de FI frequências poderão ser modificadas no sketch ou esboço, mas o montador terá de saber tudo do seu transceptor. Quero agradecer aos 100 seguidores de todo planeta terra, meu muito obrigado, muito agradecido mesmo pela confiança que vocês tem no blog e em minha pessoa nestes 12 anos. Vejam ás fotos finais como ficou, e logo abaixo o sketch ou esboço todo comentado em português do Brasil.
Vejam que o CL indica o clarificador e frequência em 0 Hz.
Bem a tela inicial ficou assim mesmo, acho melhor assim.
Abaixo modo VFO, girei o clarificador para direita subindo frequência 8kHz de 26.965.000.
Abaixo modo VFO, girei o clarificador para esquerda descendo frequência 8kHz de 26.965.000.
Vamos agora modo canal. Vejam que estou tentando ajustar os caracteres.
Ainda ajustando os caracteres nas suas posições.
Acho que não vai dá o que quero. O display tem 128 colunas e acho que não vai dá.
Assim acho que dá com MHz e mode AM pequenos.
Estava esquecendo de mostrar, ao pressionar Encoder a breve mensagem modo canal.
Continuando nas posições dos caracteres, é acho que vai ficar assim.
Ainda não está bom para mim, vamos ver outras posições.
A mensagem rápida estava muito pequena, dava para aumentar e ficou assim.
Ainda não estou gostando, acho que vou retirar modo para o centro e grande.
Ficou mais difícil só com 128 colunas do display, muito pequeno.
É aparentemente ficou bom, mas e quando for C80 como fica.
Fica assim sobrepondo o modo AM. Não vai dá mesmo não.
Agora sim ficou muito bom caracteres maiores boa visibilidade.
 
Dois caracteres pequenos 11m que será sempre visível no modo canal e MHz, são quase insignificantes.
Esquema Arduíno e componentes.
Se vocês tiverem mensagens de erros verifique tudo desde o cabo USB se é spo para carregamento de celular ou e cabo de dados e carregamento, tem que ser cabo de dados original de celulares. Sistema operacional Windows 7 64 bits x64 ou Windows 10 32 bits x86 vocês podem instalar o Arduino IDE 1.8.19
Instale os drivers do Arduíno mesmo se for clone. Vá em Sistema "Gerenciador de dispositivo" abra vá em Portas (COM E LPT) agora click e veja se consta a porta USB SERIAL CH340 (COM"X"). Vocês poderão ver se a porta está instalada no seu Arduino IDE, vá em "Ferramentas" e "Porta" dependendo de quantas portas tiverem escolha uma 6, 7, 8, 9.
Selecione todo sketch, copie e cole. Abra Arduino IDE vá em "Arquivo" depois "Novo", ou ícone ao lado da seta para "Carregar ou Compilar, abra um novo delete todas ás linhas e cole este sketch, renomeei com final .ino se preferir e salve em uma pasta. adicione todas ás bibliotecas. Lembre-se que muitas bibliotecas para o mesmo arquivo pode causar conflitos, observe a porta USB entre outras preferencias poderá provocar erros na compilação. 
Sketch abaixo. Cuidado ao copiar vem junto algumas propagandas isso também dá erro na compilação ou gravação. Ao compilar veja onde ele dá erro e confira com o sketch aqui no blog, geralmente ás propagandas são de letras grandes azuis. Eu já tentei retirar essas propaganda de cima do sketch mas ela é posta pelo Google AdSense não consigo retira-las. 

//**********************************************************************************************************
// 10kHz to 225MHz VFO / RF Generator with Si5351 and Arduino Nano, with Intermediate Frequency (IF) offset
// (+ or -), RX/TX Selector for QRP Transceivers, Band Presets and Bargraph S-Meter. See the schematics for
// wiring and README.txt for details. By J. CesarSound - ver 2.0 - Feb/2021.
//***********************************************************************************************************/
// ============================================================================
// Sketch para transceptor COBRA 148 GTL - CONTROLE DIGITAL COM Si5351
// ============================================================================
// Sketch original: 10kHz a 225MHz
// Autor original: Júlio Cesar  Fevereiro 2021
// Modificado por: Waldir Cardoso   Agosto 2026
// Blog: https://projetosetransceptores.blogspot.com/2026/08/10khz-225mhz-cobra-148-gtl-480-canais-e.html
// ============================================================================
// FUNCIONALIDADES PRINCIPAIS:
// ============================================================================
// - Frequência: 10 kHz até 225 MHz (cobertura HF/VHF/UHF completa)
// - CLK0 (VFO): Oscilador local para recepção
//   Exemplo: CLK0 Canal 1 PX (CB) AM = 34.765 MHz (26.965 + 7.800 MHz)
// - CLK1 (BFO): Oscilador de frequência intermediária (FI)
//   FI padrão: AM (7.8000 Hz) LSB (7.7985 MHz) USB (7.8015 MHz) (ajustável no sketch)
// - Modos de operação: AM, LSB, USB (teclas físicas separadas)
// - Encoder STEP ajustável: 1Hz, 10Hz, 1kHz, 10kHz, 100kHz, 1MHz, 1.5MHz, 2MHz
// - Bandas: 21 bandas pré-programadas (GEN, MW, 160m a 1m)
// - Botões: BAND+ (D10) sobe banda, BAND- (A1) desce banda
// - S-Meter: Escala estilo Cobra 148 GTL (SIG 1-9 +30 dB)
//   Com suavização e peak hold para melhor visualização
// - Modo canal com 480 de A até F 80 canais CB PX apartir de D1 26.965.00MHz.
// - Clarificador variando do centro meio dia para direita ou esquerda de 8000Hz = 8kHz
//   Com passos de 10Hz nos modos VFO canal (ajustável no sketch) 
// - EEPROM: Memória total - salva frequência, canal, banda, step e BFO ao desligar
// - Ao ligar, retorna exatamente na última configuração usada
// ============================================================================

// ============================================================================
// BIBLIOTECAS NECESSÁRIAS
// ============================================================================
#include <EEPROM.h>           // Memória EEPROM para salvar configurações
#include <Wire.h>             // Comunicação I2C (display e Si5351)
#include <Rotary.h>           // Controle do encoder rotativo
#include <SPI.h>              // Comunicação SPI
#include "PU2REO_Si5351Lite.h" // Gerador de frequência Si5351
#include <Adafruit_GFX.h>     // Biblioteca gráfica para display
#include <Adafruit_SSD1306.h> // Driver para display OLED SSD1306

// ============================================================================
// ENDEREÇOS DA MEMÓRIA EEPROM (onde os dados são salvos permanentemente)
// ============================================================================
#define EEPROM_FREQ        0    // Endereço 0: Frequência atual
#define EEPROM_BAND        10   // Endereço 10: Banda atual
#define EEPROM_STEP        20   // Endereço 20: Step (passo) atual
#define EEPROM_BAND_FREQ   100  // Endereço 100: Frequências das bandas
#define EEPROM_CHANNEL_MODE 40  // Endereço 40: Se estava em modo Canal (0=não, 1=sim)
#define EEPROM_BANK         41  // Endereço 41: Banco atual (0=A ... 5=F)
#define EEPROM_CHANNEL      42  // Endereço 42: Canal atual (0-79)
//31/08/26. 22:46Hs
//#define EEPROM_SAVED_VFO_FREQ  43  // 4 bytes (43-46): frequência do VFO guardada
//#define EEPROM_SAVED_BAND      47  // 1 byte: banda guardada


// ============================================================================
// FREQUÊNCIAS DO BFO (Beat Frequency Oscillator) para cada modo
// ============================================================================
#define BFO_AM    7800000UL     // BFO para modo AM  (7.800 MHz)
#define BFO_LSB   7798500UL     // BFO para modo LSB (7.7985 MHz)
#define BFO_USB   7801500UL     // BFO para modo USB (7.8015 MHz)

// ============================================================================
// PINOS DE SELEÇÃO DE MODO (AM, LSB, USB)
// ============================================================================
#define USB_PIN   6             // Pino para selecionar modo USB
#define LSB_PIN   7             // Pino para selecionar modo LSB
#define AM_PIN    8             // Pino para selecionar modo AM

// ============================================================================
// CONFIGURAÇÕES GERAIS 
// ============================================================================
#define BAND_INIT 15            // Banda inicial ao ligar (15 = 11m CB 27MHz)
#define XT_CAL_F  18000         // Fator de calibração do cristal Si5351
#define S_GAIN    303           // Sensibilidade do S-Meter (1.5V máximo)

// ============================================================================
// PINOS DE ENTRADA (BOTÕES E CHAVES)
// ============================================================================
#define tunestep  A0            // Botão do encoder (muda step)
#define band      A1            // Botão BAND+ (sobe banda)
#define rx_tx     A2            // Chave RX/TX (LOW = TX)
#define band_dn   10            // Botão BAND- (desce banda)
#define CLARIFIER_PIN A6        // Entrada analógica do potenciômetro do clarificador

// ============================================================================
// CONFIGURAÇÕES DO DISPLAY OLED
// ============================================================================
#define SCREEN_WIDTH 128        // Largura do display em pixels
#define SCREEN_HEIGHT 64        // Altura do display em pixels
#define OLED_RESET     -1       // Pino de reset (não usado)

// ============================================================================
// OBJETOS DAS BIBLIOTECAS Encoder display si5351
// ============================================================================
Rotary r = Rotary(2, 3);        // Encoder nos pinos 2 e 3
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Display OLED
Si5351 si5351(0x60);            // Si5351 no endereço I2C 0x60

// ============================================================================
// VARIÁVEIS DO S-METER (Medidor de Sinal)
// ============================================================================
const int sMeterPin = A3;       // Pino de entrada do S-Meter
int smoothedSMeter = 0;         // Valor suavizado do S-Meter
int peakHold = 0;               // Valor de pico mantido
unsigned long peakTimer = 0;    // Temporizador do pico

// ============================================================================
// VARIÁVEIS GLOBAIS PRINCIPAIS
// ============================================================================
unsigned long lastSave = 0;     // Último salvamento na EEPROM
unsigned long freq, freqold, fstep; // Frequência atual, anterior e passo
unsigned long savedVfoFreq = 26965000UL; // Padrão seguro (11m) — evita "000.000" se desligar durante o modo Canal
byte savedBandCount = 15;                // Padrão seguro (11m)
long cal = XT_CAL_F;            // Calibração do Si5351
byte stp, n = 1;                // Step atual e contador do gráfico
byte count;                     // Banda atual (1-21)
bool sts = 0;                   // Status RX/TX (0=RX, 1=TX)
unsigned long bfo = BFO_AM;     // Frequência do BFO atual
byte mode = 0;                  // Modo atual (0=AM, 1=LSB, 2=USB)
long clarifier = 0;             // Offset do clarificador em Hz (-8000 a +8000)
volatile bool wrapOccurred = false; // Marca quando a frequência "dá a volta" (225MHz↔100kHz)

// Variáveis do modo Canal
bool channelMode = false;       // Modo Canal ativo (bancos A-F)
byte currentBank = 3;           // Banco atual (0=A, 1=B, 2=C, 3=D, 4=E, 5=F) — inicia em D (11m)
byte currentChannel = 0;        // Canal atual dentro do banco (0-79, ou seja canal 1 a 80)

// Variáveis da mensagem temporária "Canais X"
bool toastActive = false;              // Se há uma mensagem temporária na tela agora
unsigned long toastStart = 0;          // Quando a mensagem começou (millis())
const unsigned long TOAST_DURATION = 2000; // Mensagem some depois de 2 segundos

// ============================================================================
// VARIÁVEIS GLOBAIS PRINCIPAIS
// ============================================================================
unsigned long lastSave = 0;     // Último salvamento na EEPROM
unsigned long freq, freqold, fstep; // Frequência atual, anterior e passo
unsigned long savedVfoFreq = 0; // Guarda a frequência do VFO ao entrar no modo Canal
byte savedBandCount = 15;        // Guarda a banda (count) ao entrar no modo Canal
long cal = XT_CAL_F;            // Calibração do Si5351
byte stp, n = 1;                // Step atual e contador do gráfico
byte count;                     // Banda atual (1-21)
bool sts = 0;                   // Status RX/TX (0=RX, 1=TX)
unsigned long bfo = BFO_AM;     // Frequência do BFO atual
byte mode = 0;                  // Modo atual (0=AM, 1=LSB, 2=USB)
long clarifier = 0;             // Offset do clarificador em Hz (-8000 a +8000)
volatile bool wrapOccurred = false; // Marca quando a frequência "dá a volta" (225MHz↔100kHz)

// Variáveis do modo Canal
bool channelMode = false;       // Modo Canal ativo (bancos A-F)
byte currentBank = 3;           // Banco atual (0=A, 1=B, 2=C, 3=D, 4=E, 5=F) — inicia em D (11m)
byte currentChannel = 0;        // Canal atual dentro do banco (0-79, ou seja canal 1 a 80)

// Variáveis da mensagem temporária "Canais X"
bool toastActive = false;              // Se há uma mensagem temporária na tela agora
unsigned long toastStart = 0;          // Quando a mensagem começou (millis())
const unsigned long TOAST_DURATION = 2000; // Mensagem some depois de 2 segundos

// ============================================================================
// Tabela de frequências dos 6 bancos de canais (A a F), 80 canais cada, em MHz
// ============================================================================
const float frequencies[6][80] PROGMEM = {
  {24.555, 24.565, 24.575, 24.585, 24.595, 24.605, 24.615, 24.625, 24.635, 24.645, 24.655, 24.665, 24.675, 24.685, 24.695, 24.705, 24.715, 24.725, 24.735, 24.745, 24.755, 24.765, 24.775, 24.785, 24.795, 24.805, 24.815, 24.825, 24.835, 24.845, 24.855, 24.865, 24.875, 24.885, 24.895, 24.905, 24.915, 24.925, 24.935, 24.945, 24.955, 24.965, 24.975, 24.985, 24.995, 25.005, 25.015, 25.025, 25.035, 25.045, 25.055, 25.065, 25.075, 25.085, 25.095, 25.105, 25.115, 25.125, 25.135, 25.145, 25.155, 25.165, 25.175, 25.185, 25.195, 25.205, 25.215, 25.225, 25.235, 25.245, 25.255, 25.265, 25.275, 25.285, 25.295, 25.305, 25.315, 25.325, 25.335, 25.345},
  {25.355, 25.365, 25.375, 25.385, 25.395, 25.405, 25.415, 25.425, 25.435, 25.445, 25.455, 25.465, 25.475, 25.485, 25.495, 25.505, 25.515, 25.525, 25.535, 25.545, 25.555, 25.565, 25.575, 25.585, 25.595, 25.605, 25.615, 25.625, 25.635, 25.645, 25.655, 25.665, 25.675, 25.685, 25.695, 25.705, 25.715, 25.725, 25.735, 25.745, 25.755, 25.765, 25.775, 25.785, 25.795, 25.805, 25.815, 25.825, 25.835, 25.845, 25.855, 25.865, 25.875, 25.885, 25.895, 25.905, 25.915, 25.925, 25.935, 25.945, 25.955, 25.965, 25.975, 25.985, 25.995, 26.005, 26.015, 26.025, 26.035, 26.045, 26.055, 26.065, 26.075, 26.085, 26.095, 26.105, 26.115, 26.125, 26.135, 26.145},
  {26.155, 26.165, 26.175, 26.185, 26.195, 26.205, 26.215, 26.225, 26.235, 26.245, 26.255, 26.265, 26.275, 26.285, 26.295, 26.305, 26.315, 26.325, 26.335, 26.345, 26.355, 26.365, 26.375, 26.385, 26.395, 26.405, 26.415, 26.425, 26.435, 26.445, 26.455, 26.465, 26.475, 26.485, 26.495, 26.505, 26.515, 26.525, 26.535, 26.545, 26.555, 26.565, 26.575, 26.585, 26.595, 26.605, 26.615, 26.625, 26.635, 26.645, 26.655, 26.665, 26.675, 26.685, 26.695, 26.705, 26.715, 26.725, 26.735, 26.745, 26.755, 26.765, 26.775, 26.785, 26.795, 26.805, 26.815, 26.825, 26.835, 26.845, 26.855, 26.865, 26.875, 26.885, 26.895, 26.905, 26.915, 26.925, 26.935, 26.945},
  {26.965, 26.975, 26.985, 27.005, 27.015, 27.025, 27.035, 27.055, 27.065, 27.075, 27.085, 27.105, 27.115, 27.125, 27.135, 27.155, 27.165, 27.175, 27.185, 27.205, 27.215, 27.225, 27.255, 27.235, 27.245, 27.265, 27.275, 27.285, 27.295, 27.305, 27.315, 27.325, 27.335, 27.345, 27.355, 27.365, 27.375, 27.385, 27.395, 27.405, 27.415, 27.425, 27.435, 27.455, 27.465, 27.475, 27.485, 27.505, 27.515, 27.525, 27.535, 27.555, 27.565, 27.575, 27.585, 27.605, 27.615, 27.625, 27.635, 27.655, 27.665, 27.675, 27.705, 27.685, 27.695, 27.715, 27.725, 27.735, 27.745, 27.755, 27.765, 27.775, 27.785, 27.795, 27.805, 27.815, 27.825, 27.835, 27.845, 27.855},
  {27.865, 27.875, 27.885, 27.895, 27.905, 27.915, 27.925, 27.935, 27.945, 27.955, 27.965, 27.975, 27.985, 27.995, 28.005, 28.015, 28.025, 28.035, 28.045, 28.055, 28.065, 28.075, 28.085, 28.095, 28.105, 28.115, 28.125, 28.135, 28.145, 28.155, 28.165, 28.175, 28.185, 28.195, 28.205, 28.215, 28.225, 28.235, 28.245, 28.255, 28.265, 28.275, 28.285, 28.295, 28.305, 28.315, 28.325, 28.335, 28.345, 28.355, 28.365, 28.375, 28.385, 28.395, 28.405, 28.415, 28.425, 28.435, 28.445, 28.455, 28.465, 28.475, 28.485, 28.495, 28.505, 28.515, 28.525, 28.535, 28.545, 28.555, 28.565, 28.575, 28.585, 28.595, 28.605, 28.615, 28.625, 28.635, 28.645, 28.655},
  {28.665, 28.675, 28.685, 28.695, 28.705, 28.715, 28.725, 28.735, 28.745, 28.755, 28.765, 28.775, 28.785, 28.795, 28.805, 28.815, 28.825, 28.835, 28.845, 28.855, 28.865, 28.875, 28.885, 28.895, 28.905, 28.915, 28.925, 28.935, 28.945, 28.955, 28.965, 28.975, 28.985, 28.995, 29.005, 29.015, 29.025, 29.035, 29.045, 29.055, 29.065, 29.075, 29.085, 29.095, 29.105, 29.115, 29.125, 29.135, 29.145, 29.155, 29.165, 29.175, 29.185, 29.195, 29.205, 29.215, 29.225, 29.235, 29.245, 29.255, 29.265, 29.275, 29.285, 29.295, 29.305, 29.315, 29.325, 29.335, 29.345, 29.355, 29.365, 29.375, 29.385, 29.395, 29.405, 29.415, 29.425, 29.435, 29.445, 29.455}
};

// ============================================================================
// INTERRUPÇÃO DO ENCODER (executada VFO Canais quando gira o encoder)
// ============================================================================
ISR(PCINT2_vect) {
  char result = r.process();
  if (result == DIR_CW) {
    if (channelMode) changeChannel(1);
    else set_frequency(1);
  }
  else if (result == DIR_CCW) {
    if (channelMode) changeChannel(-1);
    else set_frequency(-1);
  }
}

// ============================================================================
// INTERRUPÇÃO DO ENCODER (executada quando gira o encoder de 225MHz para 100kHz)
// ============================================================================
  void set_frequency(short dir) {
  if (dir == 1) {
    freq = freq + fstep;
    if (freq > 225000000) { freq = 100000; wrapOccurred = true; }
  }
  if (dir == -1) {
    if (freq <= fstep) {
      freq = 225000000; wrapOccurred = true;
    } else {
      freq = freq - fstep;
    }
  }
}

// ============================================================================
// FUNÇÃO: Modo Canal: Muda o canal (navegação contínua entre os 6 bancos A-F)
// ============================================================================
void changeChannel(short dir) {
  int idx = currentBank * 80 + currentChannel; // Índice linear: 0 a 479
  idx += dir;
  if (idx < 0) idx = 479;   // Desce abaixo de A1 → vai pro último canal (F80)
  if (idx > 479) idx = 0;   // Sobe acima de F80 → volta pro primeiro (A1)
  currentBank = idx / 80;
  currentChannel = idx % 80;
  freq = (unsigned long)(pgm_read_float(&frequencies[currentBank][currentChannel]) * 1000000.0);
}

// ============================================================================
// FUNÇÃO: Salva configurações na memória EEPROM
// ============================================================================

void saveMemory() {
  EEPROM.put(EEPROM_FREQ, freq);
  EEPROM.put(EEPROM_BAND, count);
  EEPROM.put(EEPROM_STEP, stp);
  EEPROM.put(EEPROM_CHANNEL_MODE, (byte)channelMode); // Salva se estava em modo Canal
  EEPROM.put(EEPROM_BANK, currentBank);               // Salva o banco (A-F)
  EEPROM.put(EEPROM_CHANNEL, currentChannel);         // Salva o canal (0-79)
 //31/08/26. atualizado 22:47Hs.
  //EEPROM.put(EEPROM_SAVED_VFO_FREQ, savedVfoFreq);
  //EEPROM.put(EEPROM_SAVED_BAND, savedBandCount);
}

// ============================================================================
// FUNÇÃO: Carrega configurações da memória EEPROM
// ============================================================================
void loadMemory() {
  EEPROM.get(EEPROM_FREQ, freq);   // Carrega frequência
  EEPROM.get(EEPROM_BAND, count);  // Carrega banda
  EEPROM.get(EEPROM_STEP, stp);    // Carrega step

    byte savedChannelMode;
  EEPROM.get(EEPROM_CHANNEL_MODE, savedChannelMode);
  channelMode = (savedChannelMode == 1);   // Restaura se estava em modo Canal

  EEPROM.get(EEPROM_BANK, currentBank);
  if (currentBank > 5) currentBank = 3;    // Segurança: se EEPROM vier "vazia" (255), usa banco D

  EEPROM.get(EEPROM_CHANNEL, currentChannel);
  if (currentChannel > 79) currentChannel = 0; // Segurança: mesma ideia
 //-----------------------------------------------------
/*  //31/08/26. Atualizada 22:49Hs
  EEPROM.get(EEPROM_SAVED_VFO_FREQ, savedVfoFreq);
  if (savedVfoFreq < 10000UL || savedVfoFreq > 225000000UL) {
  savedVfoFreq = 26965000UL; // segurança: 26.965MHz (banda 11m, canal A1), valor fixo
  }
   EEPROM.get(EEPROM_SAVED_BAND, savedBandCount);
  if (savedBandCount < 1 || savedBandCount > 21) {
  savedBandCount = BAND_INIT; // segurança: usa 11m padrão
  }
*/  if (count < 1 || count > 21) count = BAND_INIT; // segurança extra contra corrupção na EEPROM
  //-----------------------------------------------------
  // Se frequência inválida, usa banda inicial
  if (freq < 10000 || freq > 225000000) {
    count = BAND_INIT;
    bandpresets();
  }

  // Converte código do step para valor em Hz
  switch (stp) {
    case 1: fstep = 2000000; break;  // 2 MHz
    case 2: fstep = 1; break;        // 1 Hz
    case 3: fstep = 10; break;       // 10 Hz
    case 4: fstep = 1000; break;     // 1 kHz
    case 5: fstep = 10000; break;    // 10 kHz
    case 6: fstep = 100000; break;   // 100 kHz
    case 7: fstep = 1000000; break;  // 1 MHz
    case 8: fstep = 1500000; break;  // 1.5 MHz
    default:
    stp = 5;                       // Padrão: 10 kHz
    fstep = 10000;
    break;
  }
}

// ============================================================================
// FUNÇÃO: Salva frequência da banda atual na EEPROM
// ============================================================================
void saveBandFrequency() {
  int addr = EEPROM_BAND_FREQ + ((count - 1) * sizeof(unsigned long));
  EEPROM.put(addr, freq);
}

// ============================================================================
// FUNÇÃO: Carrega frequência da banda atual da EEPROM
// ============================================================================
bool loadBandFrequency() {
  int addr = EEPROM_BAND_FREQ + ((count - 1) * sizeof(unsigned long));
  unsigned long tempFreq;
  EEPROM.get(addr, tempFreq);
  if (tempFreq >= 10000UL && tempFreq <= 225000000UL) {
    freq = tempFreq;
    return true;
  }
  return false;
}

// ============================================================================
// FUNÇÃO: Modo de operação (AM/LSB/USB) pelos pinos
// ============================================================================
void readMode() {
  if (digitalRead(USB_PIN) == LOW) {
    mode = 2;
    bfo = BFO_USB;
  }
  if (digitalRead(LSB_PIN) == LOW) {
    mode = 1;
    bfo = BFO_LSB;
  }
  if (digitalRead(AM_PIN) == LOW) {
    mode = 0;
    bfo = BFO_AM;
  }
}

// ============================================================================
// FUNÇÃO DE CONFIGURAÇÃO INICIAL (executada uma vez ao ligar)
// ============================================================================
void setup() {
  Wire.begin();                 // Inicia comunicação I2C
  
  // Inicializa display OLED
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.display();

  // Tela inicial de boas-vindas
  display.setTextSize(2);
  display.setCursor(10, 4);
  display.print(F("Cobra 148"));
  display.setCursor(46, 26);
  display.print(F("GTL"));
  display.setCursor(6, 48);
  display.print(F("Clarifier"));
  display.display();
  delay(2000);

  // Configura pinos dos botões como entrada com pull-up
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(tunestep, INPUT_PULLUP);
  pinMode(band, INPUT_PULLUP);
  pinMode(rx_tx, INPUT_PULLUP);
  pinMode(band_dn, INPUT_PULLUP);
  pinMode(AM_PIN, INPUT_PULLUP);
  pinMode(LSB_PIN, INPUT_PULLUP);
  pinMode(USB_PIN, INPUT_PULLUP);
  
  // Inicializa gerador de frequência Si5351
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  si5351.set_correction(cal, SI5351_PLL_INPUT_XO);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);  // CLK0: 8mA (VFO)
  si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_6MA);  // CLK1: 6mA (BFO)
  si5351.output_enable(SI5351_CLK0, 1);  // Habilita CLK0
  si5351.output_enable(SI5351_CLK1, 1);  // Habilita CLK1
 
  // Configura interrupção do encoder
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();                      // Habilita interrupções globais
  
  loadMemory();               // Carrega configurações da EEPROM
  
  // Se frequência inválida, usa valores padrão
  if (freq == 0) {
    count = BAND_INIT;
    bandpresets();
    stp = 5;
    setstep();
  }    

// ============================================================================
// FUNÇÃO PRINCIPAL (executada em loop infinito)
// ============================================================================
void loop() {
  readMode();    // Lê modo de operação (AM/LSB/USB)

  // ============================================================
  // LEITURA DO CLARIFICADOR — versão testada e aprovada:
  // filtro rápido (mais peso na leitura nova), zona morta de 20Hz
  // no centro, e histerese para não "tremer" perto dos limites.
  // ============================================================
  analogRead(CLARIFIER_PIN);                 // Leitura de descarte (limpa crosstalk do ADC)
  int clarRead = analogRead(CLARIFIER_PIN);  // Leitura real do potenciômetro

  // 2. Converte para Hz (-8000 a +8000)
  long rawClarifier = map(clarRead, 0, 1023, -8000, 8000); // Converte para Hz (-8000 a +8000)

  // 3. Filtro. Suavização mais forte (constante de tempo maior que a versão original 3:2)
  static long lastSmoothed = 0;

  //Constante de tempo menor para o filtro.
  //long smoothedClarifier = (rawClarifier + lastSmoothed * 2) / 3; // Lenta um pouco rápida com filtro.
  long smoothedClarifier = (rawClarifier * 2 + lastSmoothed) / 3;  // Mais rápida menos filtro ainda.
  //long smoothedClarifier = (rawClarifier * 4 + lastSmoothed) / 5; // Filtro mais rápido
  
  //long smoothedClarifier = (rawClarifier * 2 + lastSmoothed) / 3; // Filtro suavizador rápido
  lastSmoothed = smoothedClarifier;

  // 4. Arredondamento para passos de 10 Hz
  const int CLAR_STEP = 10;      // Resolução de 10Hz por passo
  long quantized;
  if (smoothedClarifier >= 0) {
    quantized = (smoothedClarifier + CLAR_STEP / 2) / CLAR_STEP * CLAR_STEP;
  } else {
    quantized = (smoothedClarifier - CLAR_STEP / 2) / CLAR_STEP * CLAR_STEP;
  }

  // 5. Zona Morta central de 20 Hz (agora estável graças ao oversampling)
  const int CLAR_DEADZONE = 20;  // Zona morta central de 20Hz
  if (quantized > -CLAR_DEADZONE && quantized < CLAR_DEADZONE) {
    quantized = 0;
  }

  // 6. Histerese: só aceita o novo valor se a mudança for de pelo menos 1 passo (10Hz).
  // É isso que elimina o "pulando muito estreito" entre dois valores vizinhos.
  static long clarifierOutput = 0;
  if (abs(quantized - clarifierOutput) >= CLAR_STEP) { // Histerese: só muda após 1 passo
    clarifierOutput = quantized;
  }

  // 7. Atualiza apenas se mudar
  if (clarifier != clarifierOutput) { // Atualiza a variável só se realmente mudou
    clarifier = clarifierOutput;
  }

  // Estabilizar o encoder e frequências 225MHz para 100kHz. 
  static long clarifierold = 0;
  static unsigned long bfoold = bfo;
  if (freqold != freq || clarifierold != clarifier || bfoold != bfo) {
    tunegen();
    if (wrapOccurred) {
    si5351.pll_reset(SI5351_PLLA); // Reforço extra: acabou de dar a volta 225MHz↔100kHz
    wrapOccurred = false;
    } 
    if (freq >= 100000000UL || freqold >= 100000000UL) {
    si5351.pll_reset(SI5351_PLLA);
  }
    freqold = freq;
    clarifierold = clarifier;
    bfoold = bfo;
  }

  // Botão do encoder: toque curto = muda STEP | segurar 1 s = entra/sai do modo Canal
  static unsigned long btnTimer = 0;
  static bool btnState = false;
  static bool longPressDetected = false;

  if (digitalRead(tunestep) == LOW) {
    if (!btnState) {
      btnState = true;
      btnTimer = millis();
      longPressDetected = false;
    }
   
  // Atualizada para mostrar a banda 11m sempre modo canal
  if (btnState && !longPressDetected && (millis() - btnTimer > 800)) {  // Tempo para segurar encoder para entrar modo Canal
  channelMode = !channelMode; // Liga/desliga o modo Canal
  if (channelMode) {
  savedVfoFreq = freq;        // Guarda a frequência do VFO antes de entrar no canal
  savedBandCount = count;     // Guarda a banda atual antes de entrar no canal
  count = 15;                 // Força mostrar "11m", já que canal é sempre banda 11m
  freq = (unsigned long)(pgm_read_float(&frequencies[currentBank][currentChannel]) * 1000000.0);
  toastActive = true;
  toastStart = millis();
  } else {
  freq = savedVfoFreq;        // Restaura a frequência do VFO ao sair do canal
  count = savedBandCount;     // Restaura a banda (ex: WFM) que estava antes
  }
  longPressDetected = true;
   }      
  } else {
    if (btnState) {
      if (!longPressDetected) {
        setstep(); // Toque curto normal: muda o STEP, como sempre
      }
      btnState = false;
    }
  }
  
  // Botão BAND+ (sobe banda)
  if (digitalRead(band) == LOW) {
    inc_preset();
    delay(300);
  }
  
  // Botão BAND- (desce banda)
  if (digitalRead(band_dn) == LOW) {
    dec_preset();
    delay(300);
  }
  
  // Status RX/TX
  sts = (digitalRead(rx_tx) == LOW) ? 1 : 0;
  
  // Valor do S-Meter
  int sMeterVal = analogRead(sMeterPin);

  // Display completo com mensagem Canais
  display.clearDisplay();
  displayfreq();              // Mostra frequência
  layout();                   // Mostra layout (STEP, modo, etc)
  updateDisplay(sMeterVal);   // Mostra S-Meter
  drawToast();                // Mostra "Canais X" temporariamente, se acabou de entrar
  display.display();

  // Salva na EEPROM após 5 segundos sem mexer
  if (millis() - lastSave > 5000) {
    saveMemory();
    lastSave = millis();
  }
  delay(50);                  // Delay para estabilidade
}

// ============================================================================
// FUNÇÃO: Desenha uma mensagem temporária "Canais X" ao entrar no modo Canal
// ============================================================================

 void drawToast() {
  if (!toastActive) return;
  if (millis() - toastStart > TOAST_DURATION) { toastActive = false; return; }
  display.fillRect(8, 20, 112, 22, BLACK);   // Caixa um pouco maior, pra caber a fonte grande
  display.drawRect(8, 20, 112, 22, WHITE);
  display.setTextSize(2);                    // Fonte grande, como você pediu
  display.setCursor(14, 24);
  display.print(F("Canais "));
  display.print((char)('A' + currentBank)); // Mostra a letra do banco atual (A a F)
  display.setTextSize(1);                    // Restaura o tamanho padrão pro resto da tela
}

// ============================================================================
// FUNÇÃO: Gera as frequências no Si5351 (CLK0 = VFO, CLK1 = BFO + clarificador)
// ============================================================================
void tunegen() {
  long vfoFreq = (long)(freq + bfo) + clarifier;  // VFO = frequência + BFO + clarificador  
  si5351.set_freq((uint64_t)vfoFreq * 100ULL, SI5351_CLK0);  // CLK0: VFO
  si5351.set_freq((uint64_t)bfo * 100ULL, SI5351_CLK1);     // Define CLK1     // CLK1: BFO 
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_6MA);
  si5351.output_enable(SI5351_CLK0, 1);
  si5351.output_enable(SI5351_CLK1, 1);
}

// ============================================================================
// FUNÇÃO: Exibe a frequência no display
// ============================================================================
void displayfreq() {
  long displayFreq = (long)freq + clarifier; // Inclui o offset do clarificador na tela
  if (displayFreq < 0) displayFreq = 0;
  unsigned int m = displayFreq / 1000000;           // MHz
  unsigned int k = (displayFreq % 1000000) / 1000;  // kHz
  unsigned int h = (displayFreq % 1000);            // Hz
  
  display.setTextSize(2);
  char buffer[15] = "";
  
  if (m < 1) {
    display.setCursor(41, 1);
    sprintf(buffer, "%003d.%003d", k, h);
  }
  else if (m < 100) {
    display.setCursor(5, 1);
    sprintf(buffer, "%2d.%003d.%003d", m, k, h);
  }

    else if (m >= 100) {
    unsigned int h2 = (displayFreq % 1000) / 10; // Aparece os Hz mas com os 3 zeros depois dos 100 Hz.
    display.setCursor(5, 1);
    
    sprintf(buffer, "%2d.%003d.%02d", m, k, h2); // Linha que gera 2 números finais Hz display.
    //sprintf(buffer, "%2d.%003d.%02d", m, k, h); // Linha que gera 3 números finais display.
  }
  display.print(buffer);
}

// ============================================================================
// FUNÇÃO: Muda o step (passo) de ajuste da frequência
// ============================================================================
void setstep() {
  switch (stp) {
    case 1: stp = 2; fstep = 1; break;        // 2MHz → 1Hz
    case 2: stp = 3; fstep = 10; break;       // 1Hz → 10Hz
    case 3: stp = 4; fstep = 1000; break;     // 10Hz → 1kHz
    case 4: stp = 5; fstep = 10000; break;    // 1kHz → 10kHz
    case 5: stp = 6; fstep = 100000; break;   // 10kHz → 100kHz
    case 6: stp = 7; fstep = 1000000; break;  // 100kHz → 1MHz
    case 7: stp = 8; fstep = 1500000; break;  // 1MHz → 1.5MHz
    case 8: stp = 1; fstep = 2000000; break;  // 1.5MHz → 2MHz
  }
}

// ============================================================================
// FUNÇÃO: Sobe para a próxima banda (com suporte ao clarificador)
// ============================================================================
void inc_preset() {
  saveBandFrequency();        // Salva frequência atual da banda antes de trocar
  count++;
  if (count > 21) count = 1;  // Volta para banda 1 se passou da 21

  if (!loadBandFrequency()) bandpresets(); // Carrega frequência salva ou usa padrão

  clarifier = 0; // Reseta o clarificador ao trocar de banda

  // Segurança: garante que a frequência carregada está dentro da faixa válida
  if (freq < 100000UL || freq > 225000000UL) {
    bandpresets();
  }

  delay(10);      // Pequeno respiro para o barramento I2C estabilizar
  tunegen();      // Gera a nova frequência já com o clarificador zerado

  // Reset do PLL APENAS para grandes saltos de banda (evita instabilidade no Si5351)
  si5351.pll_reset(SI5351_PLLA); // Reset do PLL para estabilidade na troca de banda

  saveMemory();   // Salva as configurações na EEPROM
  delay(50);      // Aguarda a gravação e estabilização final
  tunegen();      // Reafirma a frequência após o reset do PLL
}

// ============================================================================
// FUNÇÃO: Desce para a banda anterior (com suporte ao clarificador)
// ============================================================================
void dec_preset() {
  saveBandFrequency();        // Salva frequência atual da banda antes de trocar
  if (count <= 1) count = 21; // Volta para banda 21 se estava na 1
  else count--;

  if (!loadBandFrequency()) bandpresets(); // Carrega frequência salva ou usa padrão

  clarifier = 0; // Reseta o clarificador ao trocar de banda

  // Segurança: garante que a frequência carregada está dentro da faixa válida
  if (freq < 100000UL || freq > 225000000UL) {
    bandpresets();
  }

  delay(10);      // Pequeno respiro para o barramento I2C estabilizar
  tunegen();      // Gera a nova frequência já com o clarificador zerado

  // Reset do PLL APENAS para grandes saltos de banda (evita instabilidade no Si5351)
  si5351.pll_reset(SI5351_PLLA); // Reset do PLL para estabilidade na troca de banda

  saveMemory();   // Salva as configurações na EEPROM
  delay(50);      // Aguarda a gravação e estabilização final
  tunegen();      // Reafirma a frequência após o reset do PLL
}

//25/08/2026 Atualizei diminuir espaço.
// ============================================================================
// Tabela de frequências padrão de cada banda, guardada na FLASH (PROGMEM)
// ============================================================================
const unsigned long bandFreqTable[] PROGMEM = {
  100000,     // banda 1  - GEN
  800000,     // banda 2  - MW
  1800000,    // banda 3  - 160m
  3650000,    // banda 4  - 80m
  4985000,    // banda 5  - 60m
  6180000,    // banda 6  - 49m
  7200000,    // banda 7  - 40m
  10000000,   // banda 8  - 31m
  11780000,   // banda 9  - 25m
  13630000,   // banda 10 - 22m
  14100000,   // banda 11 - 20m
  15000000,   // banda 12 - 19m
  17655000,   // banda 13 - 16m
  21525000,   // banda 14 - 13m
  26965000,   // banda 15 - 11m
  28400000,   // banda 16 - 10m
  50000000,   // banda 17 - 6m
  100000000,  // banda 18 - WFM
  130000000,  // banda 19 - AIR
  144000000,  // banda 20 - 2m
  220000000   // banda 21 - 1m
};

// ============================================================================
// FUNÇÃO: Define frequências padrão para cada banda
// ============================================================================
 void bandpresets() {
  freq = pgm_read_dword(&bandFreqTable[count - 1]);
  if (count == 1) tunegen(); // Mantém o comportamento original (só a banda 1 chamava tunegen aqui)
  si5351.pll_reset(SI5351_PLLA);
  saveMemory();
// Atualização menos espaço.
// ============================================================================
// Tabela de textos do STEP guardada na FLASH (PROGMEM)
// ============================================================================
 const char stepName1[] PROGMEM = "  2MHz";
 const char stepName2[] PROGMEM = "   1Hz";
 const char stepName3[] PROGMEM = "  10Hz";
 const char stepName4[] PROGMEM = "  1kHz";
 const char stepName5[] PROGMEM = " 10kHz";
 const char stepName6[] PROGMEM = "100kHz";
 const char stepName7[] PROGMEM = "  1MHz";
 const char stepName8[] PROGMEM = "1.5MHz";

 const char* const stepNameTable[] PROGMEM = {
  stepName1, stepName2, stepName3, stepName4,
  stepName5, stepName6, stepName7, stepName8
};

// ============================================================================
// FUNÇÃO: Desenha o layout completo do display (linhas, textos, etc)
// ============================================================================
void layout() {
  display.setTextColor(WHITE);
  // Linha abaixo da frequência e linha abaixo do STEP/Canal — sempre aparecem
  display.drawLine(0, 18, 127, 18, WHITE);
  display.drawLine(0, 42, 127, 42, WHITE);

  // Linha vertical que separa a coluna MHz/AM do resto — só aparece no modo VFO;
  // some no modo Canal
  if (!channelMode) {
    display.drawLine(105, 19, 105, 41, WHITE);
  }

  // Linha vertical que separava STEP de RX/TX — só existe no modo VFO normal;
  // some no modo Canal
  if (!channelMode) {
    display.drawLine(87, 19, 87, 41, WHITE);
  }

  display.setTextSize(1);
  if (!channelMode) {
    // ==========================================================
    // MODO VFO (normal): STEP à esquerda, RX/TX à direita
    // ==========================================================
    display.setCursor(57, 22);
    display.print(F("STEP"));
    display.setCursor(48, 32);
    {
      char stepBuffer[7];
      strcpy_P(stepBuffer, (char*)pgm_read_word(&(stepNameTable[stp - 1])));
      display.print(stepBuffer);
    }
    display.setCursor(91, 27);
    if (!sts) display.print(F("RX"));
    if (sts) display.print(F("TX"));
  } else {
    // ==========================================================
    // MODO CANAL: "11m  D1  AM LSB USB" na mesma linha
    // ==========================================================
    display.setTextSize(2);    //Tamanho da fonte 
    display.setCursor(28, 23); // banco+canal, mais perto do "11m" menor
    //display.setCursor(24, 23); // banco+canal, mais perto do "11m" menor
    //display.setCursor(42, 23); // banco+canal, logo depois do "11m"
    display.print((char)('A' + currentBank)); // Letra do banco: A a F
    display.print(currentChannel + 1);        // Canal 1 a 80
   
    display.setCursor(70, 23); // Modo canal posição AM LSB USB, logo depois do D1
    //display.setCursor(72, 23); // Modo canal posição AM LSB USB, logo depois do D1
    if (mode == 0) display.print(F("AM"));
    if (mode == 1) display.print(F("LSB"));
    if (mode == 2) display.print(F("USB"));
    display.setTextSize(1);
  }

  // ============================================================
  // Clarificador (CL) e BFO — sempre aparecem, em qualquer modo
  // ============================================================
  display.setCursor(1, 45);
  display.print(F("CL:"));
  display.print(clarifier);
  display.print(F("Hz"));
  display.setCursor(74, 45);
  display.print(bfo / 1000);
  display.print(F("."));
  display.print((bfo % 1000) / 100);
  display.print(F("kHz"));

  // ============================================================
  // Unidade (kHz/MHz) — sempre igual, mesma posição em qualquer modo
  // ============================================================
  display.setCursor(110, 21);
  if (freq < 1000000) display.print(F("kHz"));
  if (freq >= 1000000) display.print(F("MHz"));

  // Modo AM/LSB/USB — só aparece aqui no modo VFO; no modo Canal já foi
  // desenhado acima, ao lado do D1
  if (!channelMode) {
    display.setCursor(110, 32); //Posição modo VFO AM LSB USB
    if (mode == 0) display.print(F("AM"));
    if (mode == 1) display.print(F("LSB"));
    if (mode == 2) display.print(F("USB"));
  }
  bandlist();  // Mostra o nome da banda (11m, etc.) — sempre aparece
}

//Atualização das bandas menor espaço
// ============================================================================
// Tabela de nomes das bandas guardada na FLASH (PROGMEM)
// ============================================================================
 const char bandName01[] PROGMEM = "GEN";
 const char bandName02[] PROGMEM = "MW";
 const char bandName03[] PROGMEM = "160m";
 const char bandName04[] PROGMEM = "80m";
 const char bandName05[] PROGMEM = "60m";
 const char bandName06[] PROGMEM = "49m";
 const char bandName07[] PROGMEM = "40m";
 const char bandName08[] PROGMEM = "31m";
 const char bandName09[] PROGMEM = "25m";
 const char bandName10[] PROGMEM = "22m";
 const char bandName11[] PROGMEM = "20m";
 const char bandName12[] PROGMEM = "19m";
 const char bandName13[] PROGMEM = "16m";
 const char bandName14[] PROGMEM = "13m";
 const char bandName15[] PROGMEM = "11m";
 const char bandName16[] PROGMEM = "10m";
 const char bandName17[] PROGMEM = "6m";
 const char bandName18[] PROGMEM = "WFM";
 const char bandName19[] PROGMEM = "AIR";
 const char bandName20[] PROGMEM = "2m";
 const char bandName21[] PROGMEM = "1m";

 const char* const bandNameTable[] PROGMEM = {
  bandName01, bandName02, bandName03, bandName04, bandName05,
  bandName06, bandName07, bandName08, bandName09, bandName10,
  bandName11, bandName12, bandName13, bandName14, bandName15,
  bandName16, bandName17, bandName18, bandName19, bandName20,
  bandName21
};

 void bandlist() {
  char buffer[6];
  strcpy_P(buffer, (char*)pgm_read_word(&(bandNameTable[count - 1])));

  if (channelMode) {
    display.setTextSize(1);      // Tamanho fonte 11m etc. Menor, só no modo Canal
    display.setCursor(0, 26);    // Mesma posição X original, Y ajustado pra centralizar
    //display.setCursor(0, 27);    // Mesma posição X original, Y ajustado pra centralizar
  } else {
    display.setTextSize(2);      // Tamanho fonte 11m etc. Tamanho original, modo VFO intocado
    display.setCursor(0, 23);
  }
  display.print(buffer);
}

// ============================================================================
// FUNÇÃO: Desenha o S-Meter (medidor de sinal) no rodapé
// ============================================================================
void updateDisplay(int sMeterVal) {
  // Moldura do S-Meter
  display.drawRect(0, 53, 128, 11, WHITE);
  display.fillRect(1, 54, 126, 8, BLACK);

  // Reduz sensibilidade em 50%
  sMeterVal = (sMeterVal * 50) / 100;
  sMeterVal = constrain(sMeterVal, 0, S_GAIN);
  
  // Converte valor para largura da barra
  int barWidth = map(sMeterVal, 0, S_GAIN, 0, 106);
  
  // Suavização do valor (média móvel)
  smoothedSMeter = ((smoothedSMeter * 2) + barWidth) / 3;

  // Peak Hold (mantém valor de pico)
  if (smoothedSMeter > peakHold) {
    peakHold = smoothedSMeter;
    peakTimer = millis();
  }
  
  // Decay do pico (diminui gradualmente)
  if (millis() - peakTimer > 15) {
    if (peakHold > 0) peakHold -= 2;
  }
  peakHold = constrain(peakHold, 0, 125);

  // Desenha barra do S-Meter
  if (smoothedSMeter > 0) {
    display.fillRect(20, 54, smoothedSMeter, 9, WHITE);
  }

  // Desenha linha de pico
  display.drawLine(peakHold + 20, 53, peakHold + 20, 62, WHITE);
  
  // Desenha escala vertical
  for (int i = 0; i < 126; i++) {
    if (i < smoothedSMeter) {
      display.drawFastVLine(i + 20, 54, 9, WHITE);
    }
  }
  
  // Escala numérica do S-Meter
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(2, 55);
  display.print(F("SIG"));
  display.setCursor(24, 55);
  display.print(F("1"));
  display.setCursor(36, 55);
  display.print(F("3"));
  display.setCursor(48, 55);
  display.print(F("5"));
  display.setCursor(60, 55);
  display.print(F("7"));
  display.setCursor(72, 55);
  display.print(F("9"));
  display.setCursor(79, 55);
  display.print(F("..."));
  display.setCursor(96, 55);
  display.print(F("+30"));
  display.setCursor(115, 55);
  display.print(F("dB"));
}

Nenhum comentário:

Postar um comentário

Façam seus comentários com experiências no projeto. Ou de sua opinião.