/*
1) პირველი რეჟიმია "ნორმალური" მოდა. კონტროლერის ჩართვისას, ვთქვათ,
ჩართულია პირველი ანტენა (შესბამისი ნომრის ლედ ნათურა ანთია RX-TX). არცერთი
TX ნათურა არ ანთია. ამ რეჟიმში მიმღები და გამდამცემი ანტენა ერთიდაიგივეა.
კლავიატურაზე 1-დან-8 მდე აკრეფვით, ჩართავთ შესაბამის ანტენას, რომელიც
მიმღებიც (RX) და გადამცემიც (TX) ერთდოულად იქნება.
2) მეორე (RX Scan Mode) რეჟიმია, სადაც ხდება მიმღები (RX) ანტენის არჩევა.
ამ რეჟიმის გააქტიურებას ვახდენთ * ღილაკით კლავიატურიდან. ამავდროულად
ინთება TX ანტენის ლედ ნათურაც. TX ანტენა იქნება ის, რომელიც იყო ბოლო
მოქმედების დროს "ნორმალურ" რეჟიმში. ამ დროს სადაც არ უნდა გადართოთ
მიმღები ანტენა კლავიატურიდან, გადამცემი ანტენა, ptt-ს დაჭერით, ირთვება
მხოლოდ არჩეული გადამცემი TX ანტენა. რჟიმიდან გამოსვლა ხდება *-ზე ხელახალი
დაკლიკებით.
3) მესამე (TX Scan Mode) რეჟიმია, სადაც ხდება გადამცემი (TX) ანტენის არჩევა.
ამ რჟიმში შესვლა შესაძლებელია # ზე დაკლიკებით. ამ რეჟიმში RX ანტენა
ფიქსირებულია და ვახდენთ მხოლოდ TX ანტენის გადართვას. რეჟიმიდან გამოსვლა
ხდება #-ზე ხელახალი დაკლიკებით. (2)-დან (3) რეჟიმში, ან (3)-დან (2)-ში
გადასვლა ხდება ეგრევე * ან # ის დაკლიკებით.
4) არსებობს ასევე, Swap რეჟიმი, როცა არჩეული TX და RX ანტენებს შეგიძლიათ
შეუცვალოთ როლები და გადამცემი ანტენა გახდეს მიმღები, ხოლო მიმღები - გადამცემი,
9-ს ღილაკით. Swap რეჟიმი ჩართვადია მხოლოდ RX Scan Mode ან TX Scan Mode რეჟიმში.
Swap რეჟიმში შეუძლებელია სხვა ანტენის არჩევა ან შეცვლა, მხოლოდ არჩეული ორი ანტენა
ცვლის როლებს. Swap ის გამორთვა ხდება ისევ 9 ღილაკით, ან (RX Scan Mode)
რეჟიმის (*-ით), ან (TX Scan Mode) რეჟიმის (#-ით) გაუქმებით).
ანუ, SWAP რეჟიმიდან 3 გზაა: ა) 9, ბ) *, გ) #.
ა) SWAP-დან 9-ით გამოსვლა დაგაბრუნებს იმ რეჟიმში რომელიდანაც გადახვედი SWAP-ში.
ბ) SWAP-დან *-ით გამოსვლა გადაგიყვანს ეგრევე RX Scan Mode-ში.
გ) SWAP-დან #-ით გამოსვლა გადაგიყვანს ეგრევე TX Scan Mode-ში.
დ) გადასვლა (RX Scan Mode) -> (TX Scan Mode): TX-RX ანტენა ხდება ის,
რომელიც იყო RX (იმიტომ, რომ RX სკანირებას ახდენდი).
ე) გადასვლა (TX Scan Mode) -> (RX Scan Mode): TX-RX ანტენა ხდება ის,
რომელიც იყო TX (იმიტომ, რომ TX სკანირებას ახდენდი).
5) ანტენების შეზღუდვა: დავუშვათ 8 ანტენის ნაცვლად გვაქვს მხოლოდ 5 ანტენა:
1 დან 5 ის ჩათვლით დაკავებულია კონექტორები სვიჩზე. ამ დროს 6, 7, 8 კონექტორი ცარიელია.
შეგვიძლია შევზღუდოთ გადართვა 6, 7, 8 ანტენებზე შემდეგნაირად: ვკრებთ 005 კომბინაციას და
სისტემა გადავა 5 ანტენიან სვიჩის რეჟიმზე. ამ დროს შეუძლებელია 6, 7, 8 ანტენის ჩართვა.
შეზღუდვიდან გამოსვლის 2 გზაა: ა) 008 კომბინაცია, და ბ) სვიჩის გადატივრთვა, გამორთვა-ჩართვა.
ასევე, კლავიატურაზე მხოლოდ 0-ის აკრეფა რეაგირებას არ ახდენს; ხოლო 00 კომბინაციის მერე
კოდი გელოდება 2 წამი. ამ 2 წამში თუ არ მოხდა ანტენების რაოდენობის არჩევა, სისტემა
გამოდის ლოდინის რეჟიმიდან და უქმდება 00 კომბინაცია (რათა არ მოხდეს სისტემის ბლოკირება).
ავტორები: 4L7ZS, 4L4CR და 4L0VE.
--------------------------------
4x3 kaypad Black pinouts:
Pin_Number = Pad_RC
NC
1 = C2
2 = R1
3 = C1
4 = R4
5 = C3
6 = R3
7 = R2
NC
-------------- ---------------
Connections:
(Arduino) = (Pad_RC) = (Pad Kay_Number)
A0 = R1 = 2
A1 = R2 = 7
A2 = R3 = 6
A3 = R4 = 4
D3 = C1 = 3
D4 = C2 = 1
D5 = C3 = 5
-----------------------------
*/
#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 columns, 2 rows
// Shift register pins for 74HC595
const int latchPin1 = 6; // CTCP Latch for first 74HC595
const int clockPin1 = 7; // SHCP
const int dataPin1 = 8; // DS
const int latchPin2 = 9; // CTCP Latch for second 74HC595
const int clockPin2 = 10; // SHCP
const int dataPin2 = 11; // DS
const int statusLedPin = 13; // Status LED for RX or TX Scan Mode
const int swapLedPin = 12; // Status LED for RX<->TX swap mode
const int pttPin = 2; // Pin for PTT control (HIGH activates TX antenna)
// State variables
int activeRelay = 0; // Currently selected antenna (0-7)
int txRelay = 0; // TX antenna in RX/TX Scan Mode
int rxRelay = 0; // RX antenna in RX/TX Scan Mode
bool rxScanMode = false; // Flag for RX Scan Mode
bool txScanMode = false; // Flag for TX Scan Mode
bool swapMode = false; // Flag for Swap Mode
bool zeroPressed = false;
int maxAntennas = 8; // Default to all 8 antennas available
// For tracking changes
int lastRxRelay = -1;
int lastTxRelay = -1;
int lastActiveRelay = -1;
bool lastPttState = false;
bool lastRxScanMode = false;
bool lastTxScanMode = false;
bool lastSwapMode = false;
// Antenna names (max 7 characters for each name)
const String antennaNames[] = {
"Inv-L", // Antenna1
"FanDip", // Antenna2
"Inv160", // Antenna3
"Inv-80", // Antenna4
"5-Ant", // Antenna5
"6-Ant", // Antenna6
"7-Ant", // Antenna7
"8-Ant" // Antenna8
};
// Keypad configuration
const byte ROWS = 4; // Four rows
const byte COLS = 3; // Three columns
char keys[ROWS][COLS] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
byte rowPins[ROWS] = {A0, A1, A2, A3}; // Connect to the row pinouts of the keypad
byte colPins[COLS] = {3, 4, 5}; // Connect to the column pinouts of the keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
void setup() {
// Initialize LCD
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("HELLO FROM 4L7ZS");
lcd.setCursor(0, 1);
lcd.print("4L4CR AND 4L0VE");
delay(2000);
// Set Arduino Pins as outputs for shift register pins
pinMode(latchPin1, OUTPUT); // Antenna relays and RX-TX LEDS
pinMode(clockPin1, OUTPUT);
pinMode(dataPin1, OUTPUT);
pinMode(latchPin2, OUTPUT); // TX LEDS (no relays)
pinMode(clockPin2, OUTPUT);
pinMode(dataPin2, OUTPUT);
pinMode(pttPin, INPUT); // Set PTT pin as input
pinMode(statusLedPin, OUTPUT); // Scan mode status LED pin as output
pinMode(swapLedPin, OUTPUT); // SWAP mode LED pin as output
// Initialize all states
activeRelay = 0; // Default to antenna 1
txRelay = 0; // TX starts same as active
rxRelay = 0; // RX starts same as active
rxScanMode = false; // Start in normal mode
txScanMode = false; // Start in normal mode
swapMode = false; // Swap mode off
// Initialize both latches
digitalWrite(latchPin1, LOW);
shiftOut(dataPin1, clockPin1, MSBFIRST, 1 << 7); // Antenna 1 on Latch1
digitalWrite(latchPin1, HIGH);
digitalWrite(latchPin2, LOW);
shiftOut(dataPin2, clockPin2, MSBFIRST, 0); // Clear Latch2 initially
digitalWrite(latchPin2, HIGH);
// Update LEDs
digitalWrite(statusLedPin, LOW);
digitalWrite(swapLedPin, LOW);
updateLCD(); // Update display
}
void loop() {
checkKeypadInput();
checkPTT();
updateAntennas();
// Check if anything relevant has changed
bool currentPtt = digitalRead(pttPin);
int currentRx = (rxScanMode || txScanMode) ? rxRelay : activeRelay;
int currentTx = (rxScanMode || txScanMode) ? txRelay : activeRelay;
if (currentRx != lastRxRelay || currentTx != lastTxRelay ||
currentPtt != lastPttState || rxScanMode != lastRxScanMode ||
txScanMode != lastTxScanMode || swapMode != lastSwapMode) {
updateLCD();
// Update last known states
lastRxRelay = currentRx;
lastTxRelay = currentTx;
lastPttState = currentPtt;
lastRxScanMode = rxScanMode;
lastTxScanMode = txScanMode;
lastSwapMode = swapMode;
}
}
void checkKeypadInput() {
static String inputBuffer = "";
static unsigned long zeroPressTime = 0; // Track when first zero was pressed
char key = keypad.getKey();
// Check for timeout if we're in the middle of 00 input
if (inputBuffer.startsWith("00") && inputBuffer.length() == 2) {
if (millis() - zeroPressTime > 2000) { // 2-second timeout
inputBuffer = ""; // Reset buffer
updateLCD(); // Refresh display
return;
}
}
if (!key) return; // No key pressed, exit early
// Handle special 00N combination for setting max antennas
if (key == '0') {
if (inputBuffer.length() < 2) {
inputBuffer += '0';
if (inputBuffer.length() == 2) { // Just completed "00"
zeroPressTime = millis(); // Record when we got the second zero
}
}
return; // Wait for more input
}
// Check if we're in the middle of 00N sequence
else if (inputBuffer.startsWith("00") && inputBuffer.length() == 2 && isDigit(key)) {
int newMax = key - '0';
if (newMax >= 1 && newMax <= 8) {
maxAntennas = newMax;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Max Antennas:");
lcd.setCursor(0, 1);
lcd.print("Now set to ");
lcd.print(maxAntennas);
delay(1000);
// Reset any active antenna beyond new limit
if (activeRelay >= maxAntennas) {
activeRelay = maxAntennas - 1;
updateAntennas();
}
if (rxRelay >= maxAntennas) rxRelay = maxAntennas - 1;
if (txRelay >= maxAntennas) txRelay = maxAntennas - 1;
}
inputBuffer = ""; // Reset buffer
updateLCD();
return;
}
else {
inputBuffer = ""; // Not a 00N sequence, reset buffer
}
// Handle normal keypad functions
if (key == '9') {
toggleSwapMode();
}
else if (key == '*') {
toggleRxScanMode();
}
else if (key == '#') {
toggleTxScanMode();
}
else if (rxScanMode || txScanMode) {
if (swapMode && key == '9') {
// Swap antennas in swap mode
int temp = rxRelay;
rxRelay = txRelay;
txRelay = temp;
updateLatch2();
}
else if (!swapMode) {
// Normal scan mode antenna selection
int antenna = key - '1'; // Convert to 0-based index
if (antenna >= 0 && antenna < maxAntennas) {
setScanAntenna(antenna);
}
}
}
else {
// Normal mode antenna selection
int antenna = key - '1'; // Convert to 0-based index
if (antenna >= 0 && antenna < maxAntennas) {
setActiveAntenna(antenna);
}
}
}
void toggleRxScanMode() {
bool cameFromTxScan = false;
if (txScanMode) {
rxRelay = txRelay;
txScanMode = false;
cameFromTxScan = true;
}
rxScanMode = !rxScanMode;
digitalWrite(statusLedPin, rxScanMode ? HIGH : LOW);
if (rxScanMode) {
if (!cameFromTxScan) {
txRelay = activeRelay;
rxRelay = activeRelay;
}
swapMode = false;
digitalWrite(swapLedPin, LOW);
updateLatch2();
} else {
activeRelay = rxRelay;
swapMode = false;
digitalWrite(swapLedPin, LOW);
digitalWrite(latchPin2, LOW);
shiftOut(dataPin2, clockPin2, MSBFIRST, 0);
digitalWrite(latchPin2, HIGH);
}
}
void toggleTxScanMode() {
bool cameFromRxScan = false;
if (rxScanMode) {
txRelay = rxRelay;
rxScanMode = false;
cameFromRxScan = true;
}
txScanMode = !txScanMode;
digitalWrite(statusLedPin, txScanMode ? HIGH : LOW);
if (txScanMode) {
if (!cameFromRxScan) {
rxRelay = activeRelay;
txRelay = activeRelay;
}
swapMode = false;
digitalWrite(swapLedPin, LOW);
updateLatch2();
} else {
activeRelay = txRelay;
swapMode = false;
digitalWrite(swapLedPin, LOW);
digitalWrite(latchPin2, LOW);
shiftOut(dataPin2, clockPin2, MSBFIRST, 0);
digitalWrite(latchPin2, HIGH);
}
}
void toggleSwapMode() {
if (rxScanMode || txScanMode) { // Only works in RX or TX Scan Mode
swapMode = !swapMode;
if (swapMode) {
// Swap RX and TX antennas
int temp = rxRelay;
rxRelay = txRelay;
txRelay = temp;
} else {
// Restore original configuration
int temp = rxRelay;
rxRelay = txRelay;
txRelay = temp;
}
digitalWrite(swapLedPin, swapMode ? HIGH : LOW);
updateLatch2();
}
}
void setScanAntenna(int antennaNumber) {
if (antennaNumber < 0 || antennaNumber >= maxAntennas) return;
// Don't allow antenna changes when in swap mode
if (swapMode) {
return; // Exit without making any changes
}
if (rxScanMode) {
// In RX Scan Mode, set RX antenna
rxRelay = antennaNumber;
} else if (txScanMode) {
// In TX Scan Mode, set TX antenna
txRelay = antennaNumber;
updateLatch2();
}
}
void setActiveAntenna(int antennaNumber) {
if (antennaNumber < 0 || antennaNumber >= maxAntennas) return;
activeRelay = antennaNumber;
}
void checkPTT() {
static bool lastPttState = LOW;
bool currentPttState = digitalRead(pttPin);
if (currentPttState != lastPttState) {
lastPttState = currentPttState;
updateAntennas();
}
}
void updateAntennas() {
if (rxScanMode || txScanMode) {
bool pttActive = digitalRead(pttPin);
// Determine which antenna to activate on Latch1
int activeAntenna;
if (rxScanMode) {
activeAntenna = pttActive ? txRelay : rxRelay;
} else { // TX Scan Mode
activeAntenna = pttActive ? txRelay : rxRelay;
}
byte latch1Pattern = 1 << activeAntenna;
digitalWrite(latchPin1, LOW);
shiftOut(dataPin1, clockPin1, MSBFIRST, latch1Pattern);
digitalWrite(latchPin1, HIGH);
updateLatch2();
} else {
// Normal mode
byte pattern = 1 << activeRelay;
digitalWrite(latchPin1, LOW);
shiftOut(dataPin1, clockPin1, MSBFIRST, pattern);
digitalWrite(latchPin1, HIGH);
}
}
void updateLatch2() {
// Latch2 shows TX antenna
byte latch2Pattern = 1 << txRelay;
digitalWrite(latchPin2, LOW);
shiftOut(dataPin2, clockPin2, MSBFIRST, latch2Pattern);
digitalWrite(latchPin2, HIGH);
}
void updateLCD() {
lcd.clear();
// First line: RX and TX status
lcd.setCursor(0, 0);
lcd.print("RX:");
lcd.print(antennaNames[(rxScanMode || txScanMode) ? rxRelay : activeRelay]);
lcd.setCursor(10, 0);
if (digitalRead(pttPin)) {
if (rxScanMode) {
lcd.print("TX_Sc");
} else if (txScanMode) {
lcd.print("TXScM");
} else {
lcd.print("TX");
}
} else {
if (rxScanMode) {
lcd.print("RXScM");
} else if (txScanMode) {
lcd.print("TXScM");
} else {
lcd.print("RX");
}
}
// Second line: TX antenna and swap status
lcd.setCursor(0, 1);
lcd.print("TX:");
lcd.print(antennaNames[(rxScanMode || txScanMode) ? txRelay : activeRelay]);
lcd.setCursor(10, 1);
if (swapMode) {
lcd.print("Swap");
} else {
lcd.print("NoSwaP");
}
}
PTT
TX
RX-TX
RX Scan Mode (*)
SWAP
Antenna-1
Antenna-2
Antenna-3
Antenna-4
Antenna-5
Antenna-6
Antenna-7
Antenna-8
1
1