Инструменты пользователя

Инструменты сайта


banddecoder_code

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
banddecoder_code [2026/01/13 12:53] eu8tbanddecoder_code [2026/01/23 08:04] (текущий) eu8t
Строка 1: Строка 1:
-IP 192.168.0.240+IP 192.168.0.240 (можно сменить через настройки) 
 + 
 +v0.81 (примерно) 
 + 
 +{{:clip2net_menu_260117214628.jpeg?800|}} 
 + 
 +{{:clip2net_menu_260117214613.jpeg?800|}}
  
-v0.44 
  
-{{:clip2net_menu_260110231520.jpeg|}} 
  
-NEW CODE 
 <code> <code>
 #include <SPI.h> #include <SPI.h>
Строка 12: Строка 15:
 #include <Wire.h> #include <Wire.h>
 #include <LiquidCrystal_I2C.h> #include <LiquidCrystal_I2C.h>
 +#include <EEPROM.h>
  
-/================= НАСТРОЙКИ ================= */ +/===================================================== 
-const float VERSION 0.46; // Версия изменена +// === CONFIG: все константы устройства в одном месте === 
-LiquidCrystal_I2C lcd(0x27, 16, 2);+// =====================================================
  
-byte mac[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }+// --- EEPROM --- 
-IPAddress ip(192, 168, 0, 240)+const byte EEPROM_MAGIC      0x42
-unsigned int localPort 12060;+const int  EEPROM_ADDR_MAGIC = 0; 
 +const int  EEPROM_ADDR_IP    1  // 1..4
  
-EthernetUDP Udp;+// --- Версия прошивки --- 
 +const float VERSION = 0.81;
  
-const unsigned long CAT_TIMEOUT = 7000;   // переключение в MANUAL +// --- CAT / сеть --- 
-const unsigned long NET_TIMEOUT = 5000;   // только надпись WAIT CAT DATA+const unsigned long CAT_TIMEOUT = 7000; 
 +const unsigned long NET_TIMEOUT = 5000;
  
-const int ENC_A   10+// --- Пины энкодера --- 
-const int ENC_B   3+const int ENC_A   8
-const int ENC_BTN = 2;+const int ENC_B   A1
 +const int ENC_BTN = 3;
  
-// Используем 4 реле (A,B,C,D) + 5-й провод земля +// --- Пины реле --- 
-const int RELAYS[] = {4, 5, 6, 7}; // Реле A,B,C,D +const int RELAYS[] = {4, 5, 6, 7}; 
-const int RELAY_COUNT = 4; // Теперь точно 4 реле+const int RELAY_COUNT = 4;
  
-/================= СОСТОЯНИЯ ================= */ +// --- Кнопка --- 
-enum Mode { AUTO, MANUAL }+const unsigned long SHORT_PRESS 100; 
-Mode mode AUTO;+const unsigned long LONG_PRESS  3000
 +const unsigned long HOLD_TIME   700;
  
-bool manualForced = false; +// --- Редактор IP --- 
-bool autoFallback false;+const unsigned long EDITOR_CLICK_GUARD 200;
  
 +// --- Анимация CAT ---
 +const int CAT_ANIM_STEPS = 5;
 +
 +
 +// =====================================================
 +// === ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ (состояние устройства) ===
 +// =====================================================
 +
 +// --- IP редактор ---
 +bool ipEditMode = false;
 +byte ipEdit[4];
 +int ipOctetIndex = 0;
 +
 +// --- LCD ---
 +LiquidCrystal_I2C lcd(0x27, 16, 2);
 +
 +// --- Ethernet ---
 +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
 +IPAddress ip(192, 168, 0, 240);
 +unsigned int localPort = 12060;
 +IPAddress effectiveIP = ip;
 +EthernetUDP Udp;
 +
 +// --- CAT состояние ---
 unsigned long lastCatTime = 0; unsigned long lastCatTime = 0;
 bool catActive = false; bool catActive = false;
- 
 unsigned long lastPacketTime = 0; unsigned long lastPacketTime = 0;
- 
 long lastFreq = 0; long lastFreq = 0;
-int currentBand -1+bool freqReceived false; 
-int manualBandIndex = 0;+byte catAnimPos = 0
 +int catPacketCounter = 0
 + 
 +byte catDot[8] = { 
 +  0b00000, 
 +  0b00100, 
 +  0b01110, 
 +  0b01110, 
 +  0b01110, 
 +  0b00100, 
 +  0b00000, 
 +  0b00000 
 +}; 
 + 
 +// --- Режимы --- 
 +enum Mode { AUTO, MANUAL }; 
 +Mode mode = AUTO; 
 +bool manualForced = false;
  
-/* ================= ДИАПАЗОНЫ ================= */+// --- Диапазоны ---
 struct Band { struct Band {
   const char* name;   const char* name;
   long from;   long from;
   long to;   long to;
-  byte abcdCode; // Код ABCD для этого диапазона+  byte abcdCode;
 }; };
  
-// Коды ABCD согласно вашей схеме: 
-// 4 реле: A(бит0), B(бит1), C(бит2), D(бит3) 
-// 160M = 0001 (A=1, B=0, C=0, D=0) 
-// 80M  = 0010 (A=0, B=1, C=0, D=0) 
-// 40M  = 0011 (A=1, B=1, C=0, D=0) 
-// 20M  = 0101 (A=1, B=0, C=1, D=0) 
-// 15M  = 0111 (A=1, B=1, C=1, D=0) 
-// 10M  = 1001 (A=1, B=0, C=0, D=1) 
 Band bands[] = { Band bands[] = {
-  {"160M", 1800000, 2000000, 0b00000001}, // 0001 +  {"160M", 1800000, 2000000, 0b00000001}, 
-  {"80M",  3500000, 3800000, 0b00000010}, // 0010 +  {"80M",  3500000, 3800000, 0b00000010}, 
-  {"40M",  7000000, 7350000, 0b00000011}, // 0011 +  {"40M",  7000000, 7350000, 0b00000011}, 
-  {"20M", 14000000, 14350000, 0b00000101}, // 0101 +  {"20M", 14000000, 14350000, 0b00000101}, 
-  {"15M", 21000000, 21450000, 0b00000111}, // 0111 +  {"15M", 21000000, 21450000, 0b00000111}, 
-  {"10M", 28000000, 29700000, 0b00001001}  // 1001+  {"10M", 28000000, 29700000, 0b00001001}
 }; };
  
 const int BAND_COUNT = sizeof(bands) / sizeof(Band); const int BAND_COUNT = sizeof(bands) / sizeof(Band);
  
-/================= СЛУЖЕБНЫЕ ================= */+// --- Текущее состояние диапазонов --- 
 +int currentBand -1; 
 +int manualBandIndex 0; 
 + 
 +// --- Энкодер --- 
 +int lastA HIGH; 
 + 
 +// --- Кнопка --- 
 +bool buttonHeld false; 
 +bool pressed false; 
 +unsigned long pressStart 0; 
 +bool ignoreNextEditorClick false; 
 + 
 +// --- LCD обновление --- 
 +Mode lastMode AUTO; 
 +int lastBandIndex -1; 
 +long lastDisplayedFreq 0; 
 +bool forceDisplayUpdate false; 
 int detectBand(long freq) { int detectBand(long freq) {
   for (int i = 0; i < BAND_COUNT; i++) {   for (int i = 0; i < BAND_COUNT; i++) {
Строка 86: Строка 144:
 } }
  
-// Устанавливаем реле согласно коду ABCD 
 void setRelaysByABCD(byte abcdCode) { void setRelaysByABCD(byte abcdCode) {
-  Serial.print("Setting ABCD code: 0b"); 
-  for (int i = 3; i >= 0; i--) { 
-    Serial.print((abcdCode >> i) & 1); 
-  } 
-  Serial.print(" (0x"); 
-  if (abcdCode < 0x10) Serial.print("0"); 
-  Serial.print(abcdCode, HEX); 
-  Serial.println(")"); 
-   
   for (int i = 0; i < RELAY_COUNT; i++) {   for (int i = 0; i < RELAY_COUNT; i++) {
-    // Проверяем соответствующий бит в коде ABCD 
-    // i=0 -> бит0 (A), i=1 -> бит1 (B), i=2 -> бит2 (C), i=3 -> бит3 (D) 
     bool state = (abcdCode >> i) & 1;     bool state = (abcdCode >> i) & 1;
-    digitalWrite(RELAYS[i], state ? HIGH : LOW); +    digitalWrite(RELAYS[i], state ? LOW : HIGH);
-     +
-    Serial.print("  Relay "); +
-    Serial.print((char)('A' + i)); +
-    Serial.print(" (pin "); +
-    Serial.print(RELAYS[i]); +
-    Serial.print(")"); +
-    Serial.println(state ? "HIGH" : "LOW");+
   }   }
-  Serial.println(); 
 } }
  
-// Установить реле для конкретного диапазона 
 void setRelaysByBand(int bandIndex) { void setRelaysByBand(int bandIndex) {
-  if (bandIndex >= 0 && bandIndex < BAND_COUNT) +  if (bandIndex >= 0 && bandIndex < BAND_COUNT)
-    Serial.print("Setting band: "); +
-    Serial.print(bands[bandIndex].name); +
-    Serial.print(" ["); +
-    Serial.print(bandIndex); +
-    Serial.println("]");+
     setRelaysByABCD(bands[bandIndex].abcdCode);     setRelaysByABCD(bands[bandIndex].abcdCode);
-  else +  else 
-    // Неизвестный диапазон - выключаем все реле +    setRelaysByABCD(0);
-    Serial.println("Unknown band - disabling all relays"); +
-    setRelaysByABCD(0b00000000); +
-  }+
 } }
  
-// Выключить все реле 
 void disableAllRelays() { void disableAllRelays() {
-  Serial.println("Disabling all relays"); +  setRelaysByABCD(0);
-  setRelaysByABCD(0b00000000);+
 } }
 +
 +void saveIPToEEPROM(IPAddress ipToSave) {
 +  EEPROM.update(EEPROM_ADDR_MAGIC, EEPROM_MAGIC);
 +  for (int i = 0; i < 4; i++)
 +    EEPROM.update(EEPROM_ADDR_IP + i, ipToSave[i]);
 +}
 +
 +bool loadIPFromEEPROM(IPAddress &out) {
 +  if (EEPROM.read(EEPROM_ADDR_MAGIC) != EEPROM_MAGIC) return false;
 +  byte b[4];
 +  for (int i = 0; i < 4; i++)
 +    b[i] = EEPROM.read(EEPROM_ADDR_IP + i);
 +  out = IPAddress(b[0], b[1], b[2], b[3]);
 +  return true;
 +}
 +
 +void enterIpEditMode() {
 +  ipEditMode = true;
 +
 +  for (int i = 0; i < 4; i++)
 +    ipEdit[i] = effectiveIP[i];
 +
 +  ipOctetIndex = 0;
 +  drawIpEditor();
 +  ignoreNextEditorClick = true;
 +}
 +
 +void drawIpEditor() {
 +  lcd.clear();
 +  lcd.setCursor(0,0);
 +  lcd.print("SET IP:");
 +
 +  lcd.setCursor(0,1);
 +
 +  for (int i = 0; i < 4; i++) {
 +
 +    // подчёркивание перед редактируемым октетом
 +    if (i == ipOctetIndex)
 +      lcd.print("_");
 +    else
 +      lcd.print("");   // ← НИЧЕГО НЕ ПЕЧАТАЕМ
 +
 +    // вывод октета с ведущими нулями
 +    int val = ipEdit[i];
 +    if (val < 100) lcd.print("0");
 +    if (val < 10)  lcd.print("0");
 +    lcd.print(val);
 +
 +    if (i < 3) lcd.print(".");
 +  }
 +}
 +
 +
  
 void printFreqCompact(long f) { void printFreqCompact(long f) {
Строка 151: Строка 231:
 } }
  
-/* ================= ЭКРАН ================= */ 
-Mode lastMode = AUTO; 
-int lastBandIndex = -1; 
-long lastDisplayedFreq = 0; 
-bool forceDisplayUpdate = false; 
  
-void updateLCDIfNeeded() { +void runRelayTest() { 
-  bool changed = false;+  lcd.clear(); 
 +  lcd.setCursor(0,0); 
 +  lcd.print("TEST RELAYS");
  
-  if (lastMode !mode) { +  for (int i 0; i < BAND_COUNT; i++) { 
-    changed = true; + 
-    Serial.print("Mode changed: "); +    // включаем реле 
-    Serial.println(mode == AUTO ? "AUTO" : "MANUAL"); +    setRelaysByBand(i); 
-  } + 
-   +    // выводим комбинацию ABCD 
-  if (mode != AUTO && lastBandIndex != manualBandIndex) { +    lcd.setCursor(0,1); 
-    changed = true; +    for (int b = 3b >0; b--
-    Serial.print("Manual band changed to: "); +      lcd.print((bands[i].abcdCode >> b) & 1);
-    Serial.println(manualBandIndex); +
-  } +
-   +
-  if (mode == AUTO && lastBandIndex != currentBand{ +
-    changed = true; +
-    Serial.print("Auto band changed to: "); +
-    Serial.println(currentBand)+
-  } +
-   +
-  if (mode == AUTO && lastDisplayedFreq != lastFreq+
-    changed = true; +
-  }+
  
-  if (forceDisplayUpdate+    delay(600);
-    changed = true; +
-    forceDisplayUpdate = false;+
   }   }
  
-  if (!changedreturn;+  disableAllRelays()
 +  delay(300);
  
-  lcd.setCursor(0,0)+  forceDisplayUpdate = true
-  lcd.print("IP:"); +  updateLCDIfNeeded(); 
-  lcd.print(ip);+}
  
 +void updateLCDIfNeeded() {
 +  if (ipEditMode) return;
 +  bool changed = false;
 +
 +  if (lastMode != mode) changed = true;
 +  if (mode != AUTO && lastBandIndex != manualBandIndex) changed = true;
 +  if (mode == AUTO && lastBandIndex != currentBand) changed = true;
 +  if (mode == AUTO && lastDisplayedFreq != lastFreq) changed = true;
 +  if (forceDisplayUpdate) { changed = true; forceDisplayUpdate = false; }
 +
 +  if (!changed) return;
 +
 +  updateTopLine();
 +  
   lcd.setCursor(0,1);   lcd.setCursor(0,1);
   lcd.print("                ");   lcd.print("                ");
Строка 199: Строка 277:
   if (mode == AUTO) {   if (mode == AUTO) {
     lcd.print("[A] ");     lcd.print("[A] ");
-    if (currentBand >= 0) +    if (currentBand >= 0) lcd.print(bands[currentBand].name); 
-      lcd.print(bands[currentBand].name); +    else lcd.print("--"); 
-    else +
-      lcd.print("--"); +
-    } +
-    +
     lcd.print(" ");     lcd.print(" ");
-    if (catActive) +    if (catActive) printFreqCompact(lastFreq); 
-      printFreqCompact(lastFreq); +    else lcd.print("NO DATA       ");
-    else +
-      lcd.print("NO CAT"); +
-    }+
  
     lastBandIndex = currentBand;     lastBandIndex = currentBand;
Строка 217: Строка 289:
     lcd.print("[M] ");     lcd.print("[M] ");
     lcd.print(bands[manualBandIndex].name);     lcd.print(bands[manualBandIndex].name);
-    lcd.print(" ABCD:"); +    lcd.print(" "); 
-    for (int i = 3; i >= 0; i--) {+    for (int i = 3; i >= 0; i--)
       lcd.print((bands[manualBandIndex].abcdCode >> i) & 1);       lcd.print((bands[manualBandIndex].abcdCode >> i) & 1);
-    }+
     lastBandIndex = manualBandIndex;     lastBandIndex = manualBandIndex;
   }   }
Строка 227: Строка 299:
 } }
  
-/================= СТАРТОВЫЙ ЭКРАН ================= */+void updateTopLine() { 
 +  if (ipEditMode) return; 
 +  lcd.setCursor(0,0); 
 +  lcd.print("                ");   
 +  lcd.setCursor(0,0); 
 + 
 +  // === AUTO режим === 
 +  if (mode == AUTO) { 
 +    lcd.print("CAT "); 
 + 
 +    if (catActive) { 
 +      // 5-позиционная анимация 
 +      for (int i 0; i < 5; i++) { 
 +        if (i == catAnimPos) 
 +          lcd.write(byte(0));   // кастомная точка 
 +        else 
 +          lcd.print("."); 
 +      } 
 + 
 +      lcd.print("   "); 
 + 
 +      // Показ реле 
 +      if (currentBand >0) { 
 +        for (int i 3; i >0; i--) 
 +          lcd.print((bands[currentBand].abcdCode >> i) & 1); 
 +      } else { 
 +        lcd.print("----"); 
 +      } 
 + 
 +    } else { 
 +      // CAT пропал 
 +      lcd.print(".....   "); 
 +      if (currentBand >0) { 
 +        for (int i 3; i >0; i--) 
 +          lcd.print((bands[currentBand].abcdCode >> i) & 1); 
 +      } else { 
 +        lcd.print("----"); 
 +      } 
 +    } 
 + 
 +    return; 
 +  } 
 + 
 +  // === MANUAL режим === 
 +  lcd.print("CAT "); 
 +  if (catActive) lcd.print("OK"); 
 +  else lcd.print("--"); 
 +
 + 
 + 
 void splashScreen() { void splashScreen() {
   lcd.clear();   lcd.clear();
   lcd.setCursor(0,0);   lcd.setCursor(0,0);
-  lcd.print("EW8ZO ver ");+  lcd.print("EW8ZO v");
   lcd.print(VERSION, 2);   lcd.print(VERSION, 2);
   lcd.setCursor(0,1);   lcd.setCursor(0,1);
   lcd.print("ABCD Decoder");   lcd.print("ABCD Decoder");
-  delay(1000); 
-   
-  lcd.clear(); 
-  lcd.setCursor(0,0); 
-  lcd.print("ABCD Codes:"); 
-  lcd.setCursor(0,1); 
-  for (int i = 0; i < min(6, BAND_COUNT); i++) { 
-    lcd.print((char)('0' + (bands[i].abcdCode & 0x0F))); 
-  } 
   delay(2000);   delay(2000);
 } }
  
-/* ================= UDP ================= */+
 void readUDP() { void readUDP() {
   int packetSize = Udp.parsePacket();   int packetSize = Udp.parsePacket();
   if (!packetSize) return;   if (!packetSize) return;
  
-  char buf[400];+  char buf[200];
   int len = Udp.read(buf, sizeof(buf) - 1);   int len = Udp.read(buf, sizeof(buf) - 1);
   if (len <= 0) return;   if (len <= 0) return;
   buf[len] = 0;   buf[len] = 0;
  
 +  // Любой пакет = связь жива
   lastPacketTime = millis();   lastPacketTime = millis();
 +  lastCatTime = millis();
 +  catActive = true;
  
-  char* f = strstr(buf"<Freq>"); +  // Если MANUAL был включён автоматически (fallback)а CAT снова появился — возвращаемся в AUTO 
-  if (!freturn;+  if (mode == MANUAL && !manualForced
 +      mode = AUTO; 
 +       
 +   
 +      // Восстанавливаем реле по текущему диапазону 
 +      if (currentBand >= 0) 
 +          setRelaysByBand(currentBand); 
 +   
 +      forceDisplayUpdate = true; 
 +      updateLCDIfNeeded(); 
 +  }
  
-  f += 6; 
-  lastFreq = atol(f) * 10; 
      
-  Serial.print("Received freq: "); +  // Двигаем CAT-анимацию каждые 2 пакета   
-  Serial.print(lastFreq); +  catPacketCounter++; 
-  Serial.print(" Hz (");+  if (catPacketCounter >= 2{   // ← каждые 2 пакета 
 +      catPacketCounter = 0
 +      catAnimPos = (catAnimPos + 1% 5
 +      updateTopLine(); 
 +  }
  
-  int b = detectBand(lastFreq); 
-  Serial.print("Band detection: "); 
-  Serial.print(b); 
-  Serial.println(")"); 
  
-  if (b >= 0currentBand b;+  // Ищем <Freq> 
 +  char* f strstr(buf, "<Freq>"); 
 +  if (f
 +    f +6;
  
-  catActive true+    lastFreq atol(f) * 10
-  lastCatTime millis();+    freqReceived true  // ← ВОТ ЭТОТ if ТЕБЕ НУЖЕН
  
-  if (mode == MANUAL && autoFallback) { +    int b detectBand(lastFreq); 
-    Serial.println("Auto-fallback to AUTO mode"); +    if (b >0) 
-    mode AUTO; +      currentBand b;
-    autoFallback false; +
-    manualForced = false; +
-    setRelaysByBand(currentBand); +
-  }+
  
-  if (mode == AUTO) { +    // В AUTO обновляем реле 
-    setRelaysByBand(currentBand);+    if (mode == AUTO && currentBand >= 0
 +      setRelaysByBand(currentBand);
   }   }
  
 +  // Если <Freq> нет — это heartbeat, ничего не трогаем
   updateLCDIfNeeded();   updateLCDIfNeeded();
 } }
  
-/* ================= КНОПКА ================= */ 
-void handleButton() { 
-  static bool pressed = false; 
-  static unsigned long lastPress = 0; 
-  const unsigned long DEBOUNCE_DELAY = 50; 
  
-  if (!digitalRead(ENC_BTN)) { + 
-    if (!pressed && (millis() - lastPress > DEBOUNCE_DELAY)) { +void handleButtonPress() { 
-      pressed = true; +    pressed = true; 
-      lastPress = millis(); +    pressStart = millis(); 
-       +    buttonHeld = false;
-      Serial.println("Button pressed - toggling mode"); +
-       +
-      if (mode == AUTO) { +
-        mode = MANUAL; +
-        manualForced = true; +
-        autoFallback = false; +
-        Serial.println("Switching to MANUAL mode"); +
-        disableAllRelays(); // Выключаем все реле при ручном режиме +
-      } else { +
-        mode = AUTO; +
-        manualForced = false; +
-        autoFallback = false; +
-        Serial.println("Switching to AUTO mode"); +
-        setRelaysByBand(currentBand); // Включаем реле для текущего диапазона +
-      } +
-      forceDisplayUpdate = true; +
-      updateLCDIfNeeded(); +
-    +
-  } else { +
-    pressed = false; +
-  }+
 } }
  
-/* ================= ЭНКОДЕР ================= */ +void handleButtonHold(unsigned long held) {
-void handleEncoder() { +
-  static int lastA = HIGH; +
-  static unsigned long lastTurn = 0; +
-  const unsigned long TURN_DEBOUNCE = 100;+
  
-  if (mode == AUTO) return; // В автоматическом режиме энкодер не активен+    // длинное → вход в редактор 
 +    if (!ipEditMode && held >= LONG_PRESS) { 
 +        enterIpEditMode(); 
 +        pressed = false; 
 +        buttonHeld = false; 
 +        return; 
 +    }
  
-  int A = digitalRead(ENC_A); +    // жест удержания 
- +    if (!ipEditMode && !buttonHeld && held >= HOLD_TIME) { 
-  if (!= lastA && A == LOW && (millis() - lastTurn TURN_DEBOUNCE)) { +        buttonHeld true;
-    lastTurn millis(); +
-     +
-    bool clockwise = digitalRead(ENC_B); +
-     +
-    if (clockwise) { +
-      manualBandIndex (manualBandIndex + 1) % BAND_COUNT; +
-      Serial.print("Encoder CW -> Band: "); +
-    } else { +
-      manualBandIndex = (manualBandIndex - 1 + BAND_COUNT) % BAND_COUNT; +
-      Serial.print("Encoder CCW -> Band: ");+
     }     }
-    Serial.print(manualBandIndex); 
-    Serial.print(" ("); 
-    Serial.print(bands[manualBandIndex].name); 
-    Serial.println(")"); 
-     
-    // Устанавливаем реле согласно выбранному диапазону в ручном режиме 
-    setRelaysByBand(manualBandIndex); 
-    forceDisplayUpdate = true; 
-    updateLCDIfNeeded(); 
-  } 
-  lastA = A; 
 } }
  
-/* ================= TIMEOUT ================= */ +void handleIpEditorClick(unsigned long pressTime) { 
-void checkCatTimeout() { +    static unsigned long lastEditorClick 0;
-  if (mode !AUTO) return;+
  
-  if (catActive && millis() - lastCatTime > CAT_TIMEOUT) { +    // игнорируем первый клик после входа в редактор 
-    catActive = false; +    if (ignoreNextEditorClick) { 
-    mode = MANUAL; +        ignoreNextEditorClick false
-    autoFallback = true; +        return
-    manualForced = false; +    }
-    manualBandIndex = (currentBand >= 0) ? currentBand : 0; +
-     +
-    Serial.println("CAT timeout - switching to MANUAL mode"); +
-     +
-    disableAllRelays(); // Выключаем все реле при переходе в ручной режим +
-    forceDisplayUpdate true+
-    updateLCDIfNeeded()+
-  } +
-}+
  
-// Тестовая функция для проверки всех диапазонов +    // длинное → выход без сохранения 
-void testAllBands() { +    if (pressTime >= LONG_PRESS) { 
-  Serial.println("\n=== TESTING ALL BANDS ===")+        ipEditMode false
-  for (int i 0i < BAND_COUNT; i++) { +        forceDisplayUpdate true
-    Serial.print("Testing "); +        updateLCDIfNeeded(); 
-    Serial.print(bands[i].name); +        return;
-    Serial.print(": ABCD="); +
-    for (int j = 3; j >= 0; j--) { +
-      Serial.print((bands[i].abcdCode >> j) & 1);+
     }     }
-    Serial.println(); 
-     
-    setRelaysByBand(i); 
-    delay(1000); 
-  } 
-   
-  Serial.println("Test complete - disabling all relays"); 
-  disableAllRelays(); 
-} 
  
-/* ================= SETUP ================= *+    // защита от двойного клика 
-void setup() { +    if (millis() - lastEditorClick < 200) return
-  Serial.begin(115200); +    lastEditorClick millis();
-  Serial.println("\n=== Band Decoder ABCD Setup ==="); +
-  Serial.print("Version: "); +
-  Serial.println(VERSION, 2);+
  
-  pinMode(ENC_A, INPUT_PULLUP); +    // короткое → следующий октет 
-  pinMode(ENC_B, INPUT_PULLUP); +    if (pressTime >= 50{ 
-  pinMode(ENC_BTN, INPUT_PULLUP);+        ipOctetIndex++;
  
-  // Инициализируем 4 реле +        if (ipOctetIndex > 3) { 
-  for (int i = 0; i < RELAY_COUNT; i++) { +            effectiveIP = IPAddress(ipEdit[0], ipEdit[1], ipEdit[2], ipEdit[3]); 
-    pinMode(RELAYS[i], OUTPUT); +            saveIPToEEPROM(effectiveIP);
-    digitalWrite(RELAYS[i], LOW); // Гарантируем выключенное состояние +
-  }+
  
-  disableAllRelays(); // Выключаем все реле при старте+            lcd.clear(); 
 +            lcd.setCursor(0,0); 
 +            lcd.print("IP SAVED"); 
 +            lcd.setCursor(0,1); 
 +            lcd.print(effectiveIP); 
 +            delay(1200);
  
-  lcd.begin()     +            ipEditMode = false
-  lcd.backlight()+            forceDisplayUpdate = true
-  splashScreen();+            updateLCDIfNeeded(); 
 +            return; 
 +        }
  
-  Ethernet.begin(mac, ip); +        drawIpEditor();
-  Serial.print("Assigned IP: "); +
-  Serial.println(Ethernet.localIP()); +
- +
-  Udp.begin(localPort); +
-  Serial.print("UDP started on port "); +
-  Serial.println(localPort); +
- +
-  // Выводим таблицу кодов ABCD при старте +
-  Serial.println("\n=== BAND CONFIGURATION ==="); +
-  Serial.println("Band    Freq Range      ABCD   Binary   Hex"); +
-  Serial.println("--------------------------------------------"); +
-  for (int i = 0; i < BAND_COUNT; i++) { +
-    Serial.print(bands[i].name); +
-    Serial.print("\t"); +
-    Serial.print(bands[i].from); +
-    Serial.print("-"); +
-    Serial.print(bands[i].to); +
-    Serial.print("\t"); +
-     +
-    // Показываем ABCD код +
-    Serial.print("ABCD="); +
-    for (int j = 3; j >= 0; j--) { +
-      Serial.print((bands[i].abcdCode >> j) & 1);+
     }     }
-     
-    Serial.print("  (0b"); 
-    for (int j = 3; j >= 0; j--) { 
-      Serial.print((bands[i].abcdCode >> j) & 1); 
-    } 
-    Serial.print(")"); 
-     
-    Serial.print("  0x"); 
-    if (bands[i].abcdCode < 0x10) Serial.print("0"); 
-    Serial.println(bands[i].abcdCode, HEX); 
-  } 
-   
-  Serial.println("\n=== PIN CONFIGURATION ==="); 
-  Serial.println("Relay  Pin  Bit"); 
-  Serial.println("----------------"); 
-  for (int i = 0; i < RELAY_COUNT; i++) { 
-    Serial.print("Relay "); 
-    Serial.print((char)('A' + i)); 
-    Serial.print(": "); 
-    Serial.print(RELAYS[i]); 
-    Serial.print("    "); 
-    Serial.print(i); 
-    Serial.print(" ("); 
-    Serial.print("Bit "); 
-    Serial.print(i); 
-    Serial.println(")"); 
-  } 
- 
-  Serial.println("\n=== CONTROL ==="); 
-  Serial.println("- Button: Toggle AUTO/MANUAL"); 
-  Serial.println("- Encoder: Change band in MANUAL mode"); 
-  Serial.println("- CAT Timeout: "); 
-  Serial.print(CAT_TIMEOUT); 
-  Serial.println(" ms"); 
-  Serial.println("- Network Timeout: "); 
-  Serial.print(NET_TIMEOUT); 
-  Serial.println(" ms"); 
- 
-  // Запускаем тест всех диапазонов 
-  Serial.println("\n=== STARTUP RELAY TEST ==="); 
-  testAllBands(); 
- 
-  lcd.clear(); 
-  lcd.setCursor(0,0); 
-  lcd.print("IP:"); 
-  lcd.print(ip); 
-  lcd.setCursor(0,1); 
-  lcd.print("READY"); 
- 
-  Serial.println("\n=== Setup complete ==="); 
-  Serial.println("System READY"); 
-  Serial.println("==============================\n"); 
 } }
  
-/* ================= LOOP ================= */ +void toggleAutoManual() {
-void loop() { +
-  readUDP(); +
-  handleEncoder(); +
-  handleButton(); +
-  checkCatTimeout();+
  
-  // Периодическое обновление дисплея +    if (mode == AUTO) { 
-  static unsigned long lastDisplayUpdate = 0; +        mode MANUAL
-  if (millis() - lastDisplayUpdate > 1000) { +        manualForced = true
-    lastDisplayUpdate millis()+        
-    updateLCDIfNeeded()+
-  }+
  
-  // Отображение статуса сети +        manualBandIndex = (currentBand >= 0 ? currentBand : 0); 
-  if (millis() - lastPacketTime > NET_TIMEOUT && mode == AUTO && !catActive) { +        setRelaysByBand(manualBandIndex);
-    static unsigned long lastStatusUpdate = 0; +
-    if (millis() - lastStatusUpdate 1000) { +
-      lastStatusUpdate millis(); +
-      lcd.setCursor(0,0); +
-      lcd.print("IP:"); +
-      lcd.print(ip); +
-      lcd.setCursor(0,1); +
-      lcd.print("NO CAT SIGNAL   ");+
     }     }
-  } +    else { 
-   +        if (!catActive) { 
-  // Небольшая задержка для стабильности +            lcd.clear(); 
-  delay(10); +            lcd.setCursor(0,0); 
-} +            lcd.print("NO CAT LINK"); 
-</code>+            lcd.setCursor(0,1); 
 +            lcd.print("AUTO DISABLED"); 
 +            delay(1200);
  
 +            forceDisplayUpdate = true;
 +            updateLCDIfNeeded();
 +            return;
 +        }
  
 +        mode = AUTO;
 +        manualForced = false;
 +        
  
 +        if (currentBand >= 0)
 +            setRelaysByBand(currentBand);
 +        else
 +            disableAllRelays();
 +    }
  
-OLD CODE +    forceDisplayUpdate = true; 
-<code> +    updateLCDIfNeeded(); 
-#include <SPI.h> +}
-#include <Ethernet.h> +
-#include <EthernetUdp.h> +
-#include <Wire.h> +
-#include <LiquidCrystal_I2C.h>+
  
-/* ================= НАСТРОЙКИ ================= */+void handleButtonRelease(unsigned long pressTime) {
  
-const float VERSION = 0.44; +    // редактор 
-LiquidCrystal_I2C lcd(0x27, 16, 2);+    if (ipEditMode) { 
 +        handleIpEditorClick(pressTime); 
 +        return; 
 +    }
  
-byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +    // длинное → вход в редактор 
-IPAddress ip(192, 168, 0, 240); +    if (pressTime >LONG_PRESS) 
-unsigned int localPort = 12060;+        enterIpEditMode(); 
 +        return; 
 +    }
  
-EthernetUDP Udp;+    // короткое → AUTO/MANUAL 
 +    if (!buttonHeld && pressTime >= SHORT_PRESS) { 
 +        toggleAutoManual(); 
 +    }
  
-const unsigned long CAT_TIMEOUT 7000  // переключение в MANUAL +    buttonHeld false
-const unsigned long NET_TIMEOUT = 5000;   // только надпись WAIT CAT DATA+}
  
-const int ENC_A   = 10; 
-const int ENC_B   = 3; 
-const int ENC_BTN = 2; 
  
-const int RELAYS[] = {4, 5, 6, 7, 8, 9}; +// 
-const int RELAY_COUNT sizeof(RELAYS) sizeof(int);+// === КНОПКА С АНТИДРЕБЕЗГОМ И ЗАЩИТОЙ ОТ ЛОЖНЫХ СРАБАТЫВАНИЙ === 
 +/
 +void handleButton() 
 +    static unsigned long lastCheck = 0;
  
-/* ================= СОСТОЯНИЯ ================= */+    if (millis() - lastCheck < 30) return; 
 +    lastCheck millis();
  
-enum Mode { AUTO, MANUAL }; +    bool btn !digitalRead(ENC_BTN);
-Mode mode AUTO;+
  
-bool manualForced = false+    // начало нажатия 
-bool autoFallback = false;+    if (btn && !pressed) { 
 +        handleButtonPress()
 +        return; 
 +    }
  
-unsigned long lastCatTime 0+    // удержание 
-bool catActive = false;+    if (btn && pressed) { 
 +        unsigned long held millis() - pressStart
 +        handleButtonHold(held); 
 +        return; 
 +    }
  
-unsigned long lastPacketTime 0;+    // отпускание 
 +    if (!btn && pressed) { 
 +        pressed = false; 
 +        unsigned long pressTime millis() - pressStart; 
 +        handleButtonRelease(pressTime); 
 +        return; 
 +    } 
 +}
  
-long lastFreq = 0; 
-int currentBand = -1; 
-int manualBandIndex = 0; 
  
-/* ================= ДИАПАЗОНЫ ================= */ 
  
-struct Band { +// 
-  const char* name; +// === ЭНКОДЕР С ЗАЩИТОЙ ОТ ЛОЖНЫХ СРАБАТЫВАНИЙ === 
-  long from; +// 
-  long to; +void handleEncoder() { 
-};+  static unsigned long lastMove = 0;
  
-Band bands[] { +  int A digitalRead(ENC_A); 
-  {"160M", 1800000, 2000000}, +  if (A == lastA) return;
-  {"80M",  3500000, 3800000}, +
-  {"40M",  7000000, 7350000}, +
-  {"20M", 14000000, 14350000}, +
-  {"15M", 21000000, 21450000}, +
-  {"10M", 28000000, 29700000} +
-};+
  
-const int BAND_COUNT sizeof(bands) / sizeof(Band);+  // реагируем только на фронт A == LOW 
 +  if (A != lastA && A == LOW{
  
-/* ================= СЛУЖЕБНЫЕ ================= */+    bool clockwise digitalRead(ENC_B);
  
-int detectBand(long freq) { +    // === УДЕРЖАНИЕ КНОПКИ ПОВОРОТ === 
-  for (int i 0; i < BAND_COUNT; i++) { +    if (buttonHeld && !ipEditMode{
-    if (freq >= bands[i].from && freq <= bands[i].to) +
-      return i; +
-  } +
-  return -1; +
-}+
  
-void setRelays(int bandIndex) { +      if (clockwise) { 
-  for (int i = 0i < RELAY_COUNT; i++) +        runRelayTest()
-    digitalWrite(RELAYS[i], (i == bandIndex) ? HIGH : LOW);+      } else { 
 +        // резерв 
 +      }
  
-  if (bandIndex < 0) +      buttonHeld = false; 
-    for (int i 0i < RELAY_COUNT; i++) +      lastA A
-      digitalWrite(RELAYS[i], LOW)+      return
-}+    }
  
-void printFreqCompact(long f) { +    // === РЕЖИМ IP-РЕДАКТОРА === 
-  int MHz = f 1000000; +    if (ipEditMode{
-  int kHz = (f 1000) % 1000; +
-  int hundred (f / 100) % 10; +
-  int tens = (f / 10% 10;+
  
-  lcd.print(MHz); +      if (millis() - lastMove 80{ 
-  lcd.print("."); +        lastA = A
-  if (kHz < 100) lcd.print("0")+        return
-  if (kHz 10 lcd.print("0"); +      } 
-  lcd.print(kHz)+      lastMove = millis();
-  lcd.print(".")+
-  lcd.print(hundred); +
-  lcd.print(tens); +
-}+
  
-/* ================= ЭКРАН ================= */+      int val ipEdit[ipOctetIndex]; 
 +      if (clockwise) val++; 
 +      else val--;
  
-Mode lastMode AUTO+      if (val < 0) val 255
-int lastBandIndex = -1; +      if (val > 255) val = 0;
-long lastDisplayedFreq = 0;+
  
-void updateLCDIfNeeded() +      ipEdit[ipOctetIndex] = val; 
-  bool changed = false;+      drawIpEditor();
  
-  if (lastMode !mode) changed = true+      lastA A
-  if (mode != AUTO && lastBandIndex != manualBandIndex) changed = true+      return
-  if (mode == AUTO && lastBandIndex != currentBand) changed = true; +    }
-  if (mode == AUTO && lastDisplayedFreq != lastFreq) changed = true;+
  
-  if (!changedreturn;+    // === MANUAL режим === 
 +    if (mode == MANUAL{
  
-  lcd.setCursor(0,0); +      if (millis() - lastMove < 50) { 
-  lcd.print("IP:")+        lastA = A
-  lcd.print(ip);+        return
 +      } 
 +      lastMove = millis();
  
-  lcd.setCursor(0,1); +      if (clockwise
-  lcd.print(               "); +        manualBandIndex = (manualBandIndex + 1% BAND_COUNT
-  lcd.setCursor(0,1);+      else 
 +        manualBandIndex = (manualBandIndex - + BAND_COUNT% BAND_COUNT;
  
-  if (mode == AUTO) { +      setRelaysByBand(manualBandIndex); 
-    lcd.print("[A]"); +      forceDisplayUpdate true
-    if (currentBand >0) lcd.print(bands[currentBand].name)+      updateLCDIfNeeded(); 
-    else lcd.print("--"); +    }
-    lcd.print(" "); +
-    if (catActive) printFreqCompact(lastFreq); +
-    else lcd.print("WAIT CAT DATA"); +
- +
-    lastBandIndex = currentBand; +
-    lastDisplayedFreq = lastFreq; +
-  else { +
-    lcd.print("MANUAL "); +
-    lcd.print(bands[manualBandIndex].name); +
-    lastBandIndex = manualBandIndex;+
   }   }
  
-  lastMode mode;+  lastA A;
 } }
  
-/* ================= СТАРТОВЫЙ ЭКРАН ================= */ 
  
-void splashScreen() { 
-  lcd.clear(); 
-  lcd.setCursor(0,0); 
-  lcd.print("EW8ZO ver "); 
-  lcd.print(VERSION, 2); 
-  lcd.setCursor(0,1); 
-  lcd.print("Band Decoder"); 
-  delay(2000); 
-} 
  
-/* ================= UDP ================= */ 
  
-void readUDP() { 
-  int packetSize = Udp.parsePacket(); 
-  if (!packetSize) return; 
  
-  char buf[400]; +void checkCatTimeout() {
-  int len = Udp.read(buf, sizeof(buf) - 1); +
-  if (len <= 0return; +
-  buf[len] = 0;+
  
-  lastPacketTime = millis();+  // 1) Сбрасываем CAT независимо от режима 
 +  if (catActive && millis() - lastCatTime > CAT_TIMEOUT) { 
 +      catActive = false;
  
-  char* f = strstr(buf, "<Freq>"); +      // ОБНОВЛЯЕМ ПЕРВУЮ СТРОКУ 
-  if (!f) return; +      updateTopLine();
- +
-  f += 6; +
-  lastFreq = atol(f) * 10; +
- +
-  int b = detectBand(lastFreq); +
-  if (b >= 0) currentBand = b; +
- +
-  catActive = true; +
-  lastCatTime = millis(); +
- +
-  if (mode == MANUAL && autoFallback) { +
-    mode = AUTO; +
-    autoFallback = false; +
-    manualForced = false; +
-    setRelays(currentBand);+
   }   }
  
-  if (mode == AUTO) setRelays(currentBand);+  // 2) Логика fallback только в AUTO 
 +  if (mode == AUTO && !catActive
 +      mode = MANUAL;       
 +      manualForced = false;
  
-  updateLCDIfNeeded(); +      if (currentBand >= 0
-}+          manualBandIndex = currentBand
 +      } 
 +      setRelaysByBand(manualBandIndex);
  
-/* ================= КНОПКА ================= */ +      forceDisplayUpdate = true;
- +
-void handleButton() { +
-  static bool pressed = false; +
- +
-  if (!digitalRead(ENC_BTN)) { +
-    pressed = true; +
-  } else { +
-    if (pressed) { +
-      if (mode == AUTO) { +
-        mode = MANUAL; +
-        manualForced = true; +
-        autoFallback = false; +
-        setRelays(-1); +
-      } else { +
-        mode = AUTO; +
-        manualForced = false; +
-        autoFallback = false; +
-        setRelays(currentBand); +
-      }+
       updateLCDIfNeeded();       updateLCDIfNeeded();
-    } 
-    pressed = false; 
   }   }
 } }
  
-/* ================= ЭНКОДЕР ================= */ 
  
-void handleEncoder() { 
-  static int lastA = HIGH; 
-  int A = digitalRead(ENC_A); 
- 
-  if (A != lastA && A == LOW && mode != AUTO) { 
-    if (digitalRead(ENC_B)) 
-      manualBandIndex = (manualBandIndex + 1) % BAND_COUNT; 
-    else 
-      manualBandIndex = (manualBandIndex - 1 + BAND_COUNT) % BAND_COUNT; 
- 
-    updateLCDIfNeeded(); 
-  } 
-  lastA = A; 
-} 
- 
-/* ================= TIMEOUT ================= */ 
- 
-void checkCatTimeout() { 
-  if (mode != AUTO) return; 
- 
-  if (catActive && millis() - lastCatTime > CAT_TIMEOUT) { 
-    catActive = false; 
-    mode = MANUAL; 
-    autoFallback = true; 
-    manualForced = false; 
-    manualBandIndex = (currentBand >= 0) ? currentBand : 0; 
-    setRelays(-1); 
-    updateLCDIfNeeded(); 
-  } 
-} 
  
-/* ================= SETUP ================= */ 
  
-void setup() { +void setup() {   
-  Serial.begin(115200); +  //Serial.begin(115200); delay(500); Serial.println("START DECODER");  
-  Serial.println("=== Setup start ===");+
  
 +  // --- Пины энкодера ---
   pinMode(ENC_A, INPUT_PULLUP);   pinMode(ENC_A, INPUT_PULLUP);
   pinMode(ENC_B, INPUT_PULLUP);   pinMode(ENC_B, INPUT_PULLUP);
   pinMode(ENC_BTN, INPUT_PULLUP);   pinMode(ENC_BTN, INPUT_PULLUP);
  
-  for (int i = 0; i < RELAY_COUNT; i++)+  // --- Пины реле --- 
 +  for (int i = 0; i < RELAY_COUNT; i++) {
     pinMode(RELAYS[i], OUTPUT);     pinMode(RELAYS[i], OUTPUT);
 +    digitalWrite(RELAYS[i], LOW);
 +  }
  
-  setRelays(-1);+  disableAllRelays();
  
-  lcd.begin();     +  // --- LCD --- 
 +  lcd.begin();
   lcd.backlight();   lcd.backlight();
-  splashScreen(); 
  
-  Ethernet.begin(mac, ip); +  // если есть сохранённый — подхватим 
-  Serial.print("Assigned IP: ")+  effectiveIP = ip;  
-  Serial.println(Ethernet.localIP());+  loadIPFromEEPROM(effectiveIP); 
  
 +  // Регистрируем кастомный символ CAT‑точки
 +  lcd.createChar(0, catDot);
 +
 +  // Splash screen
 +  splashScreen();
 +
 +  // --- Ethernet ---
 +  Ethernet.begin(mac, effectiveIP);
   Udp.begin(localPort);   Udp.begin(localPort);
-  Serial.println("UDP started"); 
  
 +  // Первичное отображение IP (потом будет перерисовано updateTopLine)
   lcd.clear();   lcd.clear();
   lcd.setCursor(0,0);   lcd.setCursor(0,0);
   lcd.print("IP:");   lcd.print("IP:");
-  lcd.print(ip);+  lcd.print(effectiveIP);
   lcd.setCursor(0,1);   lcd.setCursor(0,1);
-  lcd.print("WAIT CAT DATA");+  lcd.print("READY"); 
 + 
 +  // --- Начальные значения логики --- 
 +  currentBand = -1; 
 +  catActive = false; 
 +  manualBandIndex = 0;   
 +  lastFreq = 0;   
 +  lastPacketTime = millis(); 
 + 
 +  disableAllRelays(); 
 + 
 +  // Обновляем дисплей по новой логике 
 +  forceDisplayUpdate = true;   
 +  updateLCDIfNeeded();
  
-  Serial.println("=== Setup end ===");+  // Инициализация энкодера (важно!) 
 +  lastA digitalRead(ENC_A);
 } }
  
-/* ================= LOOP ================= */ 
  
-void loop() {+void loop() {   
 +  // === Всегда читаем UDP ===
   readUDP();   readUDP();
 +
 +  // === Обработка энкодера и кнопки ===
   handleEncoder();   handleEncoder();
   handleButton();   handleButton();
-  checkCatTimeout();  // переключение в MANUAL при CAT_TIMEOUT 
  
-  // только надпись при NET_TIMEOUT +  // === Проверка таймаута CAT === 
-  if (millis() - lastPacketTime NET_TIMEOUT && mode == AUTO) { +  checkCatTimeout(); 
-    lcd.setCursor(0,0); + 
-    lcd.print("IP:"); +  // === Периодическое обновление дисплея === 
-    lcd.print(ip); +  static unsigned long lastDisplayUpdate = 0; 
-    lcd.setCursor(0,1); +  if (!ipEditMode && millis() - lastDisplayUpdate 1000) { 
-    lcd.print("WAIT CAT DATA  "); +    lastDisplayUpdate = millis(); 
-  }+    updateLCDIfNeeded(); 
 +  }   
 +  delay(10);
 } }
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 </code> </code>
banddecoder_code.1768297988.txt.gz · Последнее изменение: eu8t