UWAGA! Kod jest w ciągłej aktualizacji. Aktualizacje kodu będą miały informację o dacie zmiany.
Ostatnia zmiana: 24.11.2025
Platforma: ESP32
Logika: Gdy przemiennik jest wyłączony oczekuje sygnału carrier i 1750Hz przez jedną sekundę. Po tym czasie włącza PTT na 10 sekund i zaczyna mrugać dioda wskazująca odliczanie czasu. Po zaniknięciu carrier generuje roger-beep. Po kolejnych 3 sekundach generuje znamiennik przemiennika. Po odliczeniu 10 sekund od ostatniego sygnału carrier wyłącza PTT ale sekundę wcześniej generuje ton wyłączenia. Znamiennik generowany jest minimalnie co 60 sekund. Gdy nie wykorzystujesz sygnału tonu 1750Hz zewrzyj pin na stałe do masy.
Opis wejść i wyjść:
- Wejście nośnej (carrier) – GPIO34 (pull-up 10kom do VCC)
- Wejście tonu 1750Hz – GPIO35 (pull-up 10kom do VCC)
- Wyjście PTT – GPIO32 (stan wysoki po zadziałaniu)
- Wyjście dźwięku – GPIO25
- Dioda wskazująca odliczanie czasu – GPIO2 (LED do masy)
Opis parametrów do samodzielnej zmiany:
// Parametry do łatwej konfiguracji
const unsigned long TIMER_DURATION = 10000; // Czas podtrzymania nośnej [ms]
const unsigned long SIGNAL_START_TIME = 1000; // Czas jaki musi minąć aby po pojawieniu się nośnej załączyło się PTT[ms]
const int TONE_FREQUENCY = 1000; // Częstotliwość tonu znamiennika Morse’a [Hz]
const unsigned long TONE_DELAY = 3000; // Opóźnienie generowania znamiennika Morse’a po załaczęniu PTT [ms]
const unsigned long DOT_DURATION = 66; // Długość kropki w ms dla 15 znaków na minutę
// Przełącznik nadawania Morse’a
const bool ENABLE_MORSE = true; // true = nadawaj Morse’a, false = wyłącz
// Przełączniki beepów
const bool ENABLE_EDGE_BEEP = true; // true = włącz roger-beep po zniknięciu nośnej
const bool ENABLE_PRE_OFF_BEEP = true; // true = włącz beep przed wyłączeniem PTT
// Minimalny odstęp między znamiennikami Morse’a
const unsigned long MORSE_INTERVAL = 60000; // minimalny odstęp między generowaniem znamiennika [ms] obecnie 60 sek
// Stałe dla beepu po zboczu na GPIO34
const unsigned long BEEP_DELAY = 1000; // Opóźnienie po jakim generowany jest roger-beep [ms]
const unsigned long BEEP_DURATION = 100; // Czas trwania tonu beep [ms]
// Stałe dla beepu PRZED wyłączeniem GPIO32
const unsigned long PRE_OFF_BEEP_BEFORE_OFF = 1000; // Czas w [ms] okreslający odstęp od wygenerowania tonu zamknięcia przemiennika do wyłączenia PTT
const unsigned long PRE_OFF_BEEP_DURATION = 300; // Czas trwania tonu 300 ms
const int PRE_OFF_BEEP_FREQUENCY = 100; // Częstotliwość [Hz]
Pobierz plik:
Kod:
//Repeater controller by SP3VSS
//Not for commercial use
//Copyright by VSS 2k25
// Parametry do łatwej konfiguracji
const unsigned long TIMER_DURATION = 10000; // Czas podtrzymania nośnej [ms]
const unsigned long SIGNAL_START_TIME = 1000; // Czas jaki musi minąć aby po pojawieniu się nośnej załączyło się PTT[ms]
const int TONE_FREQUENCY = 1000; // Częstotliwość tonu znamiennika Morse'a [Hz]
const unsigned long TONE_DELAY = 3000; // Opóźnienie generowania znamiennika Morse'a po załaczęniu PTT [ms]
const unsigned long DOT_DURATION = 66; // Długość kropki w ms dla 15 znaków na minutę
// Przełącznik nadawania Morse'a
const bool ENABLE_MORSE = true; // true = nadawaj Morse'a, false = wyłącz
// Przełączniki beepów
const bool ENABLE_EDGE_BEEP = true; // true = włącz roger-beep po zniknięciu nośnej
const bool ENABLE_PRE_OFF_BEEP = true; // true = włącz beep przed wyłączeniem PTT
// Minimalny odstęp między znamiennikami Morse'a
const unsigned long MORSE_INTERVAL = 60000; // minimalny odstęp między generowaniem znamiennika [ms] obecnie 60 sek
// Stałe dla beepu po zboczu na GPIO34
const unsigned long BEEP_DELAY = 1000; // Opóźnienie po jakim generowany jest roger-beep [ms]
const unsigned long BEEP_DURATION = 100; // Czas trwania tonu beep [ms]
// Stałe dla beepu PRZED wyłączeniem GPIO32
const unsigned long PRE_OFF_BEEP_BEFORE_OFF = 1000; // Czas w [ms] okreslający odstęp od wygenerowania tonu zamknięcia przemiennika do wyłączenia PTT
const unsigned long PRE_OFF_BEEP_DURATION = 300; // Czas trwania tonu 300 ms
const int PRE_OFF_BEEP_FREQUENCY = 100; // Częstotliwość [Hz]
// Piny
#define PIN_INPUT_1 34 // GPIO34 jako wejście (aktywny stan niski)
#define PIN_INPUT_2 35 // GPIO35 jako wejście (aktywny stan niski)
#define PIN_OUTPUT 32 // GPIO32 jako wyjście
#define PIN_LED 2 // GPIO2 sterowanie LED
#define PIN_BUZZER 25 // GPIO25 wyjście PWM do generowania dźwięku
const int pwmChannel = 0;
const int pwmResolution = 8; // 8-bit rozdzielczość (0-255)
unsigned long signalStartTime = 0;
unsigned long outputHighStartTime = 0;
unsigned long ledLastToggleTime = 0;
unsigned long buzzerStartTime = 0;
unsigned long morseStartTime = 0;
// Zmienne dla beepu po zboczu
unsigned long beepStartTime = 0;
bool beepPending = false;
bool beepPlaying = false;
// Zmienne dla beepu PRZED wyłączeniem GPIO32
unsigned long preOffBeepStartTime = 0; // kiedy zaczął się sam dźwięk
unsigned long preOffBeepTriggerTime = 0; // kiedy należy go uruchomić (TIMER_DURATION - 1 s)
bool preOffBeepPending = false;
bool preOffBeepPlaying = false;
bool inputActive = false;
bool outputHigh = false;
bool ledState = false; // false = LED LOW, true = LED HIGH (mruganie)
bool timerRunning = false;
bool morsePlaying = false;
bool morsePending = false;
bool morseGeneratedThisCycle = false; // Flaga zapobiegająca wielokrotnej generacji Morse'a
// Drugi timer: ostatni start znamiennika
unsigned long lastMorseStartTime = 0; // czas (millis) ostatniego rozpoczęcia nadawania Morse'a
// Parametr "znamiennik"
const char znamiennik[] = "SR3X";
// Baza alfabetu Morse'a (litery i cyfry)
struct MorseCode {
char symbol;
const char* code;
};
const MorseCode morseTable[] = {
{'A', ".-"}, {'B', "-..."}, {'C', "-.-."}, {'D', "-.."}, {'E', "."},
{'F', "..-."}, {'G', "--."}, {'H', "...."}, {'I', ".."}, {'J', ".---"},
{'K', "-.-"}, {'L', ".-.."}, {'M', "--"}, {'N', "-."}, {'O', "---"},
{'P', ".--."}, {'Q', "--.-"}, {'R', ".-."}, {'S', "..."}, {'T', "-"},
{'U', "..-"}, {'V', "...-"}, {'W', ".--"}, {'X', "-..-"}, {'Y', "-.--"},
{'Z', "--.."},
{'0', "-----"}, {'1', ".----"}, {'2', "..---"}, {'3', "...--"}, {'4', "....-"},
{'5', "....."}, {'6', "-...."}, {'7', "--..."}, {'8', "---.."}, {'9', "----."}
};
int morseIndex = 0;
int morseCharIndex = 0;
unsigned long morseElementStart = 0;
enum MorseState {
IDLE,
DOT,
DASH,
GAP_ELEMENT,
GAP_LETTER
};
MorseState morseState = IDLE;
const int DOT_UNIT = DOT_DURATION;
const int DASH_UNIT = DOT_UNIT * 3;
const int GAP_ELEMENT_UNIT = DOT_DURATION;
const int GAP_LETTER_UNIT = DOT_DURATION * 3;
void setup() {
pinMode(PIN_INPUT_1, INPUT);
pinMode(PIN_INPUT_2, INPUT);
pinMode(PIN_OUTPUT, OUTPUT);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_OUTPUT, LOW);
digitalWrite(PIN_LED, LOW);
// Konfiguracja PWM dla buzzera: 1 kHz, 8 bit, kanał 0
ledcAttachChannel(PIN_BUZZER, TONE_FREQUENCY, pwmResolution, pwmChannel);
ledcWrite(PIN_BUZZER, 0);
}
void loop() {
int input1 = digitalRead(PIN_INPUT_1);
int input2 = digitalRead(PIN_INPUT_2);
// Start GPIO32 i timera
if (!outputHigh) {
digitalWrite(PIN_LED, LOW);
if (input1 == LOW && input2 == LOW) {
if (!inputActive) {
signalStartTime = millis();
inputActive = true;
} else if (millis() - signalStartTime >= SIGNAL_START_TIME) {
digitalWrite(PIN_OUTPUT, HIGH);
outputHighStartTime = millis();
ledLastToggleTime = millis();
outputHigh = true;
timerRunning = false;
inputActive = false;
morseGeneratedThisCycle = false; // reset flagi nowego cyklu
// reset beepów
beepPending = false;
beepPlaying = false;
preOffBeepPending = false;
preOffBeepPlaying = false;
// reset Morse niezależnie od ENABLE_MORSE (na wszelki wypadek)
morsePending = false;
morsePlaying = false;
}
} else {
inputActive = false;
}
} else {
// Mruganie LED co 500 ms podczas działania timera
if (millis() - ledLastToggleTime >= 500) {
ledState = !ledState;
digitalWrite(PIN_LED, ledState ? HIGH : LOW);
ledLastToggleTime = millis();
}
static int prevInput1 = HIGH;
if (input1 == HIGH && prevInput1 == LOW) {
// Zbocze narastające na GPIO34
outputHighStartTime = millis();
timerRunning = true;
// Skonfiguruj beep PRZED wyłączeniem GPIO32 (tylko jeśli włączony)
if (ENABLE_PRE_OFF_BEEP && TIMER_DURATION > PRE_OFF_BEEP_BEFORE_OFF) {
preOffBeepTriggerTime = outputHighStartTime +
(TIMER_DURATION - PRE_OFF_BEEP_BEFORE_OFF);
preOffBeepPending = true;
preOffBeepPlaying = false;
} else {
preOffBeepPending = false;
preOffBeepPlaying = false;
}
// Jednorazowy beep 1 kHz / 100 ms po 1 s,
// tylko gdy GPIO32 jest w stanie wysokim i beep włączony
if (ENABLE_EDGE_BEEP && digitalRead(PIN_OUTPUT) == HIGH) {
beepPending = true;
beepStartTime = millis() + BEEP_DELAY;
}
// Uruchamiaj Morse'a tylko gdy:
// - włączony ENABLE_MORSE
// - w tym cyklu jeszcze nie generowany
// - minął MORSE_INTERVAL od ostatniego startu znamiennika
if (ENABLE_MORSE &&
!morseGeneratedThisCycle &&
(millis() - lastMorseStartTime >= MORSE_INTERVAL)) {
morsePending = true;
morseStartTime = millis() + TONE_DELAY;
// lastMorseStartTime zostanie ustawiony przy faktycznym starcie Morse'a
}
} else if (input1 == LOW && prevInput1 == HIGH) {
// Zbocze opadające na GPIO34 - zatrzymaj timer i wyłącz generowanie
timerRunning = false;
morsePlaying = false;
morsePending = false;
beepPending = false;
beepPlaying = false;
preOffBeepPending = false;
preOffBeepPlaying = false;
ledcWrite(PIN_BUZZER, 0);
}
prevInput1 = input1;
unsigned long now = millis();
// Start generacji Morse'a po opóźnieniu (tylko jeśli włączony)
if (ENABLE_MORSE && morsePending && now >= morseStartTime) {
morsePlaying = true;
morsePending = false;
morseElementStart = now;
morseState = IDLE;
morseIndex = 0;
morseCharIndex = 0;
morseGeneratedThisCycle = true; // ustaw flagę aby nie powtarzać
// Zapisz czas faktycznego rozpoczęcia znamiennika
lastMorseStartTime = now;
}
// Jednorazowy beep 1 kHz / 100 ms po 1 s od zbocza narastającego GPIO34
if (ENABLE_EDGE_BEEP && beepPending && now >= beepStartTime) {
ledcWrite(PIN_BUZZER, 128); // ~50% wypełnienia przy 8 bitach
beepPending = false;
beepPlaying = true;
beepStartTime = now; // teraz przechowuje czas rozpoczęcia tonu
}
if (ENABLE_EDGE_BEEP && beepPlaying && now - beepStartTime >= BEEP_DURATION) {
ledcWrite(PIN_BUZZER, 0);
beepPlaying = false;
}
// Beep 1 kHz / 300 ms na 1 s przed wyłączeniem GPIO32
if (ENABLE_PRE_OFF_BEEP && preOffBeepPending && now >= preOffBeepTriggerTime) {
// Upewnij się, że wyjście nadal jest wysokie (nie wyłączono przedwcześnie)
if (digitalRead(PIN_OUTPUT) == HIGH) {
// Częstotliwość ustawiona w konfiguracji PWM (TONE_FREQUENCY / PRE_OFF_BEEP_FREQUENCY)
ledcWrite(PIN_BUZZER, 128);
preOffBeepStartTime = now;
preOffBeepPlaying = true;
}
preOffBeepPending = false; // niezależnie od stanu wyjścia spróbowałeś wyzwolić
}
if (ENABLE_PRE_OFF_BEEP &&
preOffBeepPlaying &&
now - preOffBeepStartTime >= PRE_OFF_BEEP_DURATION) {
ledcWrite(PIN_BUZZER, 0);
preOffBeepPlaying = false;
}
// Przetwarzanie Morse'a tylko gdy włączony
if (ENABLE_MORSE && morsePlaying) {
morseProcess();
}
// Główne wyłączenie GPIO32 po TIMER_DURATION
if (timerRunning && now - outputHighStartTime >= TIMER_DURATION) {
digitalWrite(PIN_OUTPUT, LOW);
digitalWrite(PIN_LED, LOW);
outputHigh = false;
timerRunning = false;
morsePlaying = false;
morsePending = false;
beepPending = false;
beepPlaying = false;
preOffBeepPending = false;
preOffBeepPlaying = false;
ledcWrite(PIN_BUZZER, 0);
}
}
}
const char* getMorseCode(char c) {
if (c >= 'a' && c <= 'z') c = c - 'a' + 'A';
for (unsigned int i = 0; i < sizeof(morseTable)/sizeof(morseTable[0]); i++) {
if (morseTable[i].symbol == c) return morseTable[i].code;
}
return "";
}
void morseProcess() {
if (morseIndex >= (int)strlen(znamiennik)) {
morsePlaying = false;
ledcWrite(PIN_BUZZER, 0);
return;
}
const char* currentCode = getMorseCode(znamiennik[morseIndex]);
int codeLen = strlen(currentCode);
unsigned long now = millis();
switch (morseState) {
case IDLE:
if (morseCharIndex < codeLen) {
char symbol = currentCode[morseCharIndex];
if (symbol == '.') {
ledcWrite(PIN_BUZZER, 128);
morseElementStart = now;
morseState = DOT;
} else if (symbol == '-') {
ledcWrite(PIN_BUZZER, 128);
morseElementStart = now;
morseState = DASH;
} else {
morseCharIndex++;
}
} else {
ledcWrite(PIN_BUZZER, 0);
morseElementStart = now;
morseState = GAP_LETTER;
}
break;
case DOT:
if (now - morseElementStart >= DOT_UNIT) {
ledcWrite(PIN_BUZZER, 0);
morseElementStart = now;
morseState = GAP_ELEMENT;
}
break;
case DASH:
if (now - morseElementStart >= DASH_UNIT) {
ledcWrite(PIN_BUZZER, 0);
morseElementStart = now;
morseState = GAP_ELEMENT;
}
break;
case GAP_ELEMENT:
if (now - morseElementStart >= GAP_ELEMENT_UNIT) {
morseCharIndex++;
morseState = IDLE;
}
break;
case GAP_LETTER:
if (now - morseElementStart >= GAP_LETTER_UNIT) {
morseCharIndex = 0;
morseIndex++;
morseState = IDLE;
}
break;
}
}
Strony
- Hello :)
- O mnie
- Sterownik przemiennika
- Satelity
- DMR
- HackRF
- Technika
- M17 OPN RTX
- Antena Biquad-jak zbudować?
- Jammer GSM GPS DCS CDMA
- Moduł LoRa E220-400T30D
- Duplexer 2m/70cm
- ADS-B Receiver 1090MHz
- Fitry KF PILIGRIM
- Sterownik przemiennika NHRC-2
- Antenna J-Pole do ADS-B
- Lekki hacking eTrex’a
- RF1100-232 RF 433MHz Transceiver
- ZASILACZ HP HSTNS-PL14 – modyfikacja 13,8V 33A
- CZTEROOBWODOWY ĆWIERĆFALOWY FILTR PASMOWY 70cm
- Odbiornik BG7YZF – MSi001&MSi025 – RSP1 clone
- Generator OSD
- Software
- APRS
- Literatura
- Moje konstrukcje
- Archiwum
- RPT Poznań
- Moje artykuły
- Informacje
- Mapy
- Youtube
- Sklep