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

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


banddecoder_code

Это старая версия документа!


IP 192.168.0.240

v0.50

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

const float VERSION = 0.60;
LiquidCrystal_I2C lcd(0x27, 16, 2);

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 240);
unsigned int localPort = 12060;

EthernetUDP Udp;

const unsigned long CAT_TIMEOUT = 7000;
const unsigned long NET_TIMEOUT = 5000;

const int ENC_A   = 8;
const int ENC_B   = A1;
const int ENC_BTN = 3;

const int RELAYS[] = {4, 5, 6, 7};
const int RELAY_COUNT = 4;

enum Mode { AUTO, MANUAL };
Mode mode = AUTO;

bool manualForced = false;
bool autoFallback = false;

unsigned long lastCatTime = 0;
bool catActive = false;

unsigned long lastPacketTime = 0;

long lastFreq = 0;
bool freqReceived = false;


int currentBand = -1;
int manualBandIndex = 0;
int lastA = HIGH;


struct Band {
  const char* name;
  long from;
  long to;
  byte abcdCode;
};

Band bands[] = {
  {"160M", 1800000, 2000000, 0b00000001}, // BCD 0001 → Output 1
  {"80M",  3500000, 3800000, 0b00000010}, // BCD 0010 → Output 2
  {"40M",  7000000, 7350000, 0b00000011}, // BCD 0011 → Output 3
  {"30M", 10100000, 10150000, 0b00000100}, // BCD 0100 → Output 4
  {"20M", 14000000, 14350000, 0b00000101}, // BCD 0101 → Output 5
  {"17M", 18068000, 18168000, 0b00000110}, // BCD 0110 → Output 6
  {"15M", 21000000, 21450000, 0b00000111}, // BCD 0111 → Output 7
  {"12M", 24890000, 24990000, 0b00001000}, // BCD 1000 → Output 8
  {"11M", 26900000, 27999000, 0b00001001},  // BCD 1001 → same as 10M
  {"10M", 28000000, 29700000, 0b00001001}, // BCD 1001 → Output 9
  {"6M",  50000000, 52000000, 0b00001010} // BCD 1010 → Output 10  
};


const int BAND_COUNT = sizeof(bands) / sizeof(Band);

int detectBand(long freq) {
  for (int i = 0; i < BAND_COUNT; i++) {
    if (freq >= bands[i].from && freq <= bands[i].to)
      return i;
  }
  return -1;
}

void setRelaysByABCD(byte abcdCode) {
  for (int i = 0; i < RELAY_COUNT; i++) {
    bool state = (abcdCode >> i) & 1;
    digitalWrite(RELAYS[i], state ? LOW : HIGH);
  }
}

void setRelaysByBand(int bandIndex) {
  if (bandIndex >= 0 && bandIndex < BAND_COUNT)
    setRelaysByABCD(bands[bandIndex].abcdCode);
  else
    setRelaysByABCD(0);
}

void disableAllRelays() {
  setRelaysByABCD(0);
}

void printFreqCompact(long f) {
  int MHz = f / 1000000;
  int kHz = (f / 1000) % 1000;
  int hundred = (f / 100) % 10;
  int tens = (f / 10) % 10;

  lcd.print(MHz);
  lcd.print(".");
  if (kHz < 100) lcd.print("0");
  if (kHz < 10)  lcd.print("0");
  lcd.print(kHz);
  lcd.print(".");
  lcd.print(hundred);
  lcd.print(tens);
}

Mode lastMode = AUTO;
int lastBandIndex = -1;
long lastDisplayedFreq = 0;
bool forceDisplayUpdate = false;

void updateLCDIfNeeded() {
  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;

  lcd.setCursor(0,0);
  lcd.print("IP:");
  lcd.print(ip);

  lcd.setCursor(0,1);
  lcd.print("                ");
  lcd.setCursor(0,1);

  if (mode == AUTO) {
    lcd.print("[A] ");
    if (currentBand >= 0) lcd.print(bands[currentBand].name);
    else lcd.print("--");

    lcd.print(" ");
    if (catActive) printFreqCompact(lastFreq);
    else lcd.print("NO DATA       ");

    lastBandIndex = currentBand;
    lastDisplayedFreq = lastFreq;
  } else {
    lcd.print("[M] ");
    lcd.print(bands[manualBandIndex].name);
    lcd.print(" ");
    for (int i = 3; i >= 0; i--)
      lcd.print((bands[manualBandIndex].abcdCode >> i) & 1);

    lastBandIndex = manualBandIndex;
  }

  lastMode = mode;
}

void splashScreen() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("EW8ZO v");
  lcd.print(VERSION, 2);
  lcd.setCursor(0,1);
  lcd.print("ABCD Decoder");
  delay(2000);
}


void readUDP() {
  int packetSize = Udp.parsePacket();
  if (!packetSize) return;

  char buf[200];
  int len = Udp.read(buf, sizeof(buf) - 1);
  if (len <= 0) return;
  buf[len] = 0;

  // Любой пакет = связь жива
  lastPacketTime = millis();
  lastCatTime = millis();
  catActive = true;

  // Ищем <Freq>
  char* f = strstr(buf, "<Freq>");
  if (f) {
    f += 6;

    lastFreq = atol(f) * 10;
    freqReceived = true;   // ← ВОТ ЭТОТ if ТЕБЕ НУЖЕН

    int b = detectBand(lastFreq);
    if (b >= 0)
      currentBand = b;

    // В AUTO обновляем реле
    if (mode == AUTO && currentBand >= 0)
      setRelaysByBand(currentBand);
  }

  // Если <Freq> нет — это heartbeat, ничего не трогаем
  updateLCDIfNeeded();
}




//
// === КНОПКА С АНТИДРЕБЕЗГОМ И ЗАЩИТОЙ ОТ ЛОЖНЫХ СРАБАТЫВАНИЙ ===
//
void handleButton() {
  static bool pressed = false;
  static unsigned long pressStart = 0;
  static unsigned long lastCheck = 0;

  const unsigned long SHORT_PRESS = 100;
  const unsigned long LONG_PRESS  = 3000;

  // антидребезг кнопки
  if (millis() - lastCheck < 30) return;
  lastCheck = millis();

  bool btn = !digitalRead(ENC_BTN);

  // начало нажатия
  if (btn && !pressed) {
    pressed = true;
    pressStart = millis();
  }

  // отпускание кнопки
  if (!btn && pressed) {
    unsigned long pressTime = millis() - pressStart;
    pressed = false;

    // === ДЛИННОЕ НАЖАТИЕ: ТЕСТ РЕЛЕ ===
    if (pressTime >= LONG_PRESS) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("TEST RELAYS");

      for (int i = 0; i < BAND_COUNT; i++) {
        setRelaysByBand(i);
        delay(500);
      }

      disableAllRelays();
      delay(500);

      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("ABCD:");
      lcd.setCursor(0,1);
      for (int i = 0; i < BAND_COUNT; i++) {
        for (int b = 3; b >= 0; b--)
          lcd.print((bands[i].abcdCode >> b) & 1);
        lcd.print(" ");
      }
      delay(1500);

      // восстановление реле
      if (mode == MANUAL)
        setRelaysByBand(manualBandIndex);
      else
        setRelaysByBand(currentBand);

      forceDisplayUpdate = true;
      updateLCDIfNeeded();
      return;
    }

    // === КОРОТКОЕ НАЖАТИЕ: ПЕРЕКЛЮЧЕНИЕ РЕЖИМА ===
    if (pressTime >= SHORT_PRESS) {

      // ПЕРЕХОД AUTO → MANUAL
      if (mode == AUTO) {
        mode = MANUAL;
        manualForced = true;
        autoFallback = false;

        // ВСЕГДА берём диапазон из AUTO
        if (currentBand >= 0)
          manualBandIndex = currentBand;
        else
          manualBandIndex = 0;   // fallback при отсутствии CAT

        setRelaysByBand(manualBandIndex);
      }

      // ПЕРЕХОД MANUAL → AUTO
      else {
        mode = AUTO;
        manualForced = false;
        autoFallback = false;

        if (currentBand >= 0)
          setRelaysByBand(currentBand);
        else
          disableAllRelays();
      }

      forceDisplayUpdate = true;
      updateLCDIfNeeded();
    }
  }
}


//
// === ЭНКОДЕР С ЗАЩИТОЙ ОТ ЛОЖНЫХ СРАБАТЫВАНИЙ ===
//
void handleEncoder() {
  if (mode == AUTO) return;
  if (!freqReceived) return;   // ← абсолютная защита
  
  static unsigned long lastMove = 0;

  if (millis() - lastMove < 50) return;

  int A = digitalRead(ENC_A);
  if (A == lastA) return; 
  if (A != lastA && A == LOW) {
    lastMove = millis();

    bool clockwise = digitalRead(ENC_B);

    if (clockwise)
      manualBandIndex = (manualBandIndex + 1) % BAND_COUNT;
    else
      manualBandIndex = (manualBandIndex - 1 + BAND_COUNT) % BAND_COUNT;

    setRelaysByBand(manualBandIndex);
    forceDisplayUpdate = true;
    updateLCDIfNeeded();
  }

  lastA = A;
}




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;
    //setRelaysByBand(manualBandIndex);
    if (currentBand >= 0) {
      manualBandIndex = currentBand;
      setRelaysByBand(manualBandIndex);
    } else {
        // CAT пропал, но диапазон неизвестен — оставляем реле как есть
    }

    forceDisplayUpdate = true;
    updateLCDIfNeeded();
  }
}

void setup() {
  
  //Serial.begin(115200); delay(500); Serial.println("START DECODER");
  
  pinMode(ENC_A, INPUT_PULLUP);
  pinMode(ENC_B, INPUT_PULLUP);
  pinMode(ENC_BTN, INPUT_PULLUP);

  for (int i = 0; i < RELAY_COUNT; i++) {
    pinMode(RELAYS[i], OUTPUT);
    digitalWrite(RELAYS[i], LOW);
  }

  disableAllRelays();
  lcd.begin();
  lcd.backlight();
  splashScreen();

  Ethernet.begin(mac, ip);
  Udp.begin(localPort);

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("IP:");
  lcd.print(ip);
  lcd.setCursor(0,1);
  lcd.print("READY");

  currentBand = -1;
  catActive = false;
  manualBandIndex = 0;  
  lastFreq = 0;  
  lastPacketTime = millis();
  disableAllRelays();
  forceDisplayUpdate = true;  
  updateLCDIfNeeded();
  lastA = digitalRead(ENC_A);
}

void loop() {  
  readUDP();
  handleEncoder();
  handleButton();
  checkCatTimeout();

  static unsigned long lastDisplayUpdate = 0;
  if (millis() - lastDisplayUpdate > 1000) {
    lastDisplayUpdate = millis();
    updateLCDIfNeeded();
  }

  if (millis() - lastPacketTime > NET_TIMEOUT && mode == AUTO && !catActive) {
    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("WAIT CAT DATA ");
    }
  }

  delay(10); 
}
banddecoder_code.1768593399.txt.gz · Последнее изменение: eu8t