diff --git a/ubitx_20/cat_libs.ino b/ubitx_20/cat_libs.ino new file mode 100644 index 0000000..e9375c4 --- /dev/null +++ b/ubitx_20/cat_libs.ino @@ -0,0 +1,754 @@ +/************************************************************************* + This source code is written for uBITX, but it can also be used on other radios. + + The CAT protocol is used by many radios to provide remote control to comptuers through + the serial port. + it is based on FT-817, uBITX's only protocol has been added and will be added in the future. + In addition, simple things such as FT-857 frequency control and PTT control can also be + transmitted to the FT-857 protocol. + + This code refers to the following code. + - FT857D CAT Library, by Pavel Milanes, CO7WT, pavelmc@gmail.com + https://github.com/pavelmc/FT857d/ + - Ham Radio Control Libraries, https://sourceforge.net/projects/hamlib/ + - Not found protocols decription were analyzed using an RS-232 analyzer. + using FT-817 and + - http://www.ka7oei.com/ft817_meow.html <-- It was a great help here. + +----------------------------------------------------------------------------- + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +**************************************************************************/ +#define printLineF1(x) (printLineF(1, x)) +#define printLineF2(x) (printLineF(0, x)) + +//for broken protocol +#define CAT_RECEIVE_TIMEOUT 500 + +#define CAT_MODE_LSB 0x00 +#define CAT_MODE_USB 0x01 +#define CAT_MODE_CW 0x02 +#define CAT_MODE_CWR 0x03 +#define CAT_MODE_AM 0x04 +#define CAT_MODE_FM 0x08 +#define CAT_MODE_DIG 0x0A +#define CAT_MODE_PKT 0x0C +#define CAT_MODE_FMN 0x88 + +#define ACK 0 + +unsigned int skipTimeCount = 0; +byte CAT_BUFF[5]; +byte CAT_SNDBUFF[5]; + +void SendCatData(byte sendCount) +{ + for (byte i = 0; i < sendCount; i++) + Serial.write(CAT_BUFF[i]); + //Serial.flush(); +} + +//PROTOCOL : 0x01 +//Computer ->(frequency)-> TRCV CAT_BUFF +void CatSetFreq(byte fromType) +{ + //CAT_BUFF + byte i; + unsigned long tempFreq = 0; + + if (fromType == 2 || fromType == 3) { + Serial.write(ACK); + return; + } + + //2 digit in 1 byte (4 bit + 4bit) * 4.5 byte + for (i = 0; i < 4; i++) + { + tempFreq *= 10; + tempFreq += CAT_BUFF[i] >> 4; + tempFreq *= 10; + tempFreq += CAT_BUFF[i] & 0x0f; + } + + tempFreq *= 10; + tempFreq += CAT_BUFF[4] >> 4; + + if (!inTx && (frequency != tempFreq)) + { + //Check Frequency Range + if (tempFreq >= LOWEST_FREQ_DIAL && tempFreq <= HIGHEST_FREQ_DIAL) + { + setFrequency(tempFreq); + updateDisplay(); + } + else + { + //KD8CEC + //Remark for rduce program size, if you need, you can remove remark, + //however alomost rig control software available 1.0 ~ 50Mhz + //printLine(0, "OUT OF RANGE!!!"); + //delay_background(300, 0); + } + } + + Serial.write(ACK); +} + +//#define BCD_LEN 9 +//PROTOCOL : 0x03 +//Computer <-(frequency)-> TRCV CAT_BUFF +void CatGetFreqMode(unsigned long freq, byte fromType) +{ + int i; + byte tmpValue; + unsigned BCD_LEN = 9; + + if (BCD_LEN & 1) { + CAT_BUFF[BCD_LEN / 2] &= 0x0f; + CAT_BUFF[BCD_LEN / 2] |= (freq % 10) << 4; + + freq /= 10; + } + for (i = (BCD_LEN / 2) - 1; i >= 0; i--) { + tmpValue = freq % 10; + freq /= 10; + tmpValue |= (freq % 10) << 4; + freq /= 10; + CAT_BUFF[i] = tmpValue; + } + + //Mode Check + if (isUSB) + CAT_BUFF[4] = CAT_MODE_USB; + else + CAT_BUFF[4] = CAT_MODE_LSB; + + SendCatData(5); +} + +void CatSetSplit(boolean isSplit, byte fromType) +{ + + Serial.write(ACK); +} + +void CatSetPTT(boolean isPTTOn, byte fromType) +{ + if (fromType == 2 || fromType == 3) { + Serial.write(ACK); + return; + } + + // Set PTT Mode + if (isPTTOn) + { + if (!inTx) + { + txCAT = true; + + startTx(TX_SSB, 1); + //Exit menu, Memory Keyer... ETC + if (isCWAutoMode > 0) { + isCWAutoMode = 0; + printLineF2(F("AutoKey Exit/CAT")); + //delay_background(1000, 0); + } + } + } + else + { + if (inTx) + { + stopTx(); + txCAT = false; + } + } + + Serial.write(ACK); +} + +void CatVFOToggle(boolean isSendACK, byte fromType) +{ + if (fromType != 2 && fromType != 3) { + menuVfoToggle(1); + } + + if (isSendACK) + Serial.write(ACK); //Time +} + +void CatSetMode(byte tmpMode, byte fromType) +{ + if (fromType == 2 || fromType == 3) { + Serial.write(ACK); + return; + } + + if (!inTx) + { + if (tmpMode == CAT_MODE_USB) + { + isUSB = true; + } + else + { + isUSB = false; + } + + setFrequency(frequency); + updateDisplay(); + } + + Serial.write(ACK); +} + +//Read EEProm by uBITX Manager Software +void ReadEEPRom(byte fromType) +{ + //5BYTES + //CAT_BUFF[0] [1] [2] [3] [4] //4 COMMAND + //0, 1 START ADDRESS + uint16_t eepromStartIndex = CAT_BUFF[0] + CAT_BUFF[1] * 256; + uint16_t eepromReadLength = CAT_BUFF[2] + CAT_BUFF[3] * 256;; + byte checkSum = 0; + byte read1Byte = 0; + + Serial.write(0x02); //STX + checkSum = 0x02; + for (uint16_t i = 0; i < eepromReadLength; i++) + { + read1Byte = EEPROM.read(eepromStartIndex + i); + checkSum += read1Byte; + Serial.write(read1Byte); + } + Serial.write(checkSum); + Serial.write(ACK); +} + +//Write just proecess 1byes +void WriteEEPRom(byte fromType) +{ + //5BYTES + uint16_t eepromStartIndex = CAT_BUFF[0] + CAT_BUFF[1] * 256; + byte write1Byte = CAT_BUFF[2]; + + //Check Checksum + if (CAT_BUFF[3] != ((CAT_BUFF[0] + CAT_BUFF[1] + CAT_BUFF[2]) % 256)) + { + Serial.write(0x56); //CHECK SUM ERROR + Serial.write(ACK); + } + else + { + EEPROM.write(eepromStartIndex, write1Byte); + Serial.write(0x77); //OK + Serial.write(ACK); + } +} + +void ReadEEPRom_FT817(byte fromType) +{ + byte temp0 = CAT_BUFF[0]; + byte temp1 = CAT_BUFF[1]; + + CAT_BUFF[0] = 0; + CAT_BUFF[1] = 0; + + switch (temp1) + { + case 0x45 : // + if (temp0 == 0x03) + { + CAT_BUFF[0] = 0x00; + CAT_BUFF[1] = 0xD0; + } + break; + case 0x47 : // + if (temp0 == 0x03) + { + CAT_BUFF[0] = 0xDC; + CAT_BUFF[1] = 0xE0; + } + break; + case 0x55 : + //0 : VFO A/B 0 = VFO-A, 1 = VFO-B + //1 : MTQMB Select 0 = (Not MTQMB), 1 = MTQMB ("Memory Tune Quick Memory Bank") + //2 : QMB Select 0 = (Not QMB), 1 = QMB ("Quick Memory Bank") + //3 : + //4 : Home Select 0 = (Not HOME), 1 = HOME memory + //5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE + //6 : + //7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0) + CAT_BUFF[0] = 0x80 + (vfoActive == VFO_B ? 1 : 0); + CAT_BUFF[1] = 0x00; + break; + case 0x57 : // + //0 : 1-0 AGC Mode 00 = Auto, 01 = Fast, 10 = Slow, 11 = Off + //2 DSP On/Off 0 = Off, 1 = On (Display format) + //4 PBT On/Off 0 = Off, 1 = On (Passband Tuning) + //5 NB On/Off 0 = Off, 1 = On (Noise Blanker) + //6 Lock On/Off 0 = Off, 1 = On (Dial Lock) + //7 FST (Fast Tuning) On/Off 0 = Off, 1 = On (Fast tuning) + + CAT_BUFF[0] = 0xC0; + CAT_BUFF[1] = 0x40; + break; + case 0x59 : // band select VFO A Band Select 0000 = 160 M, 0001 = 75 M, 0010 = 40 M, 0011 = 30 M, 0100 = 20 M, 0101 = 17 M, 0110 = 15 M, 0111 = 12 M, 1000 = 10 M, 1001 = 6 M, 1010 = FM BCB, 1011 = Air, 1100 = 2 M, 1101 = UHF, 1110 = (Phantom) + //http://www.ka7oei.com/ft817_memmap.html + //CAT_BUFF[0] = 0xC2; + //CAT_BUFF[1] = 0x82; + break; + case 0x5C : //Beep Volume (0-100) (#13) + CAT_BUFF[0] = 0xB2; + CAT_BUFF[1] = 0x42; + break; + case 0x5E : + //3-0 : CW Pitch (300-1000 Hz) (#20) From 0 to E (HEX) with 0 = 300 Hz and each step representing 50 Hz + //5-4 : Lock Mode (#32) 00 = Dial, 01 = Freq, 10 = Panel + //7-6 : Op Filter (#38) 00 = Off, 01 = SSB, 10 = CW + //CAT_BUFF[0] = 0x08; + CAT_BUFF[0] = sideTonePitch; + CAT_BUFF[1] = 0x25; + break; + case 0x61 : //Sidetone (Volume) (#44) + CAT_BUFF[0] = sideToneSub; + CAT_BUFF[1] = 0x08; + break; + case 0x5F : // + //4-0 CW Weight (1.:2.5-1:4.5) (#22) From 0 to 14 (HEX) with 0 = 1:2.5, incrementing in 0.1 weight steps + //5 420 ARS (#2) 0 = Off, 1 = On + //6 144 ARS (#1) 0 = Off, 1 = On + //7 Sql/RF-G (#45) 0 = Off, 1 = On + CAT_BUFF[0] = 0x32; + CAT_BUFF[1] = 0x08; + break; + case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms + CAT_BUFF[0] = cwDelayTime; + CAT_BUFF[1] = 0x32; + break; + case 0x62 : // + //5-0 CW Speed (4-60 WPM) (#21) From 0 to 38 (HEX) with 0 = 4 WPM and 38 = 60 WPM (1 WPM steps) + //7-6 Batt-Chg (6/8/10 Hours (#11) 00 = 6 Hours, 01 = 8 Hours, 10 = 10 Hours + //CAT_BUFF[0] = 0x08; + CAT_BUFF[0] = 1200 / cwSpeed - 4; + CAT_BUFF[1] = 0xB2; + break; + case 0x63 : // + //6-0 VOX Gain (#51) Contains 1-100 (decimal) as displayed + //7 Disable AM/FM Dial (#4) 0 = Enable, 1 = Disable + CAT_BUFF[0] = 0xB2; + CAT_BUFF[1] = 0xA5; + break; + case 0x64 : // + break; + case 0x67 : //6-0 SSB Mic (#46) Contains 0-100 (decimal) as displayed + CAT_BUFF[0] = 0xB2; + CAT_BUFF[1] = 0xB2; + break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed + case 0x78 : + if (isUSB) + CAT_BUFF[0] = CAT_MODE_USB; + else + CAT_BUFF[0] = CAT_MODE_LSB; + + if (CAT_BUFF[0] != 0) CAT_BUFF[0] = 1 << 5; + break; + case 0x79 : // + //1-0 TX Power (All bands) 00 = High, 01 = L3, 10 = L2, 11 = L1 + //3 PRI On/Off 0 = Off, 1 = On + //DW On/Off 0 = Off, 1 = On + //SCN (Scan) Mode 00 = No scan, 10 = Scan up, 11 = Scan down + //ART On/Off 0 = Off, 1 = On + CAT_BUFF[0] = 0x00; + CAT_BUFF[1] = 0x00; + break; + case 0x7A : //SPLIT + //7A 0 HF Antenna Select 0 = Front, 1 = Rear + //7A 1 6 M Antenna Select 0 = Front, 1 = Rear + //7A 2 FM BCB Antenna Select 0 = Front, 1 = Rear + //7A 3 Air Antenna Select 0 = Front, 1 = Rear + //7A 4 2 M Antenna Select 0 = Front, 1 = Rear + //7A 5 UHF Antenna Select 0 = Front, 1 = Rear + //7A 6 ? ? + //7A 7 SPL On/Off 0 = Off, 1 = On + + CAT_BUFF[0] = (isSplitOn ? 0xFF : 0x7F); + break; + case 0xB3 : // + CAT_BUFF[0] = 0x00; + CAT_BUFF[1] = 0x4D; + break; + + } + + // sent the data + SendCatData(2); +} + +void WriteEEPRom_FT817(byte fromType) +{ + byte temp0 = CAT_BUFF[0]; + byte temp1 = CAT_BUFF[1]; + + CAT_BUFF[0] = 0; + CAT_BUFF[1] = 0; + + if (fromType == 2 || fromType == 3) { + SendCatData(2); + Serial.write(ACK); + return; + } + switch (temp1) + { + case 0x55 : + //0 : VFO A/B 0 = VFO-A, 1 = VFO-B + //1 : MTQMB Select 0 = (Not MTQMB), 1 = MTQMB ("Memory Tune Quick Memory Bank") + //2 : QMB Select 0 = (Not QMB), 1 = QMB ("Quick Memory Bank") + //3 : + //4 : Home Select 0 = (Not HOME), 1 = HOME memory + //5 : Memory/MTUNE select 0 = Memory, 1 = MTUNE + //6 : + //7 : MEM/VFO Select 0 = Memory, 1 = VFO (A or B - see bit 0) + if (CAT_BUFF[2] & 0x01) //vfoB + { + //nowVFO Check + if (vfoActive != VFO_B) + { + CatVFOToggle(false, fromType); + } + } + else + { + //vfoA + if (vfoActive != VFO_A) + { + CatVFOToggle(false, fromType); + } + } + break; + /* + case 0x57 : // + //0 : 1-0 AGC Mode 00 = Auto, 01 = Fast, 10 = Slow, 11 = Off + //2 DSP On/Off 0 = Off, 1 = On (Display format) + //4 PBT On/Off 0 = Off, 1 = On (Passband Tuning) + //5 NB On/Off 0 = Off, 1 = On (Noise Blanker) + //6 Lock On/Off 0 = Off, 1 = On (Dial Lock) + //7 FST (Fast Tuning) On/Off 0 = Off, 1 = On (Fast tuning) + + CAT_BUFF[0] = 0xC0; + CAT_BUFF[1] = 0x40; + break; + case 0x59 : // band select VFO A Band Select 0000 = 160 M, 0001 = 75 M, 0010 = 40 M, 0011 = 30 M, 0100 = 20 M, 0101 = 17 M, 0110 = 15 M, 0111 = 12 M, 1000 = 10 M, 1001 = 6 M, 1010 = FM BCB, 1011 = Air, 1100 = 2 M, 1101 = UHF, 1110 = (Phantom) + //http://www.ka7oei.com/ft817_memmap.html + //CAT_BUFF[0] = 0xC2; + //CAT_BUFF[1] = 0x82; + break; + case 0x5C : //Beep Volume (0-100) (#13) + CAT_BUFF[0] = 0xB2; + CAT_BUFF[1] = 0x42; + break; + */ + case 0x5E : + //3-0 : CW Pitch (300-1000 Hz) (#20) From 0 to E (HEX) with 0 = 300 Hz and each step representing 50 Hz + //5-4 : Lock Mode (#32) 00 = Dial, 01 = Freq, 10 = Panel + //7-6 : Op Filter (#38) 00 = Off, 01 = SSB, 10 = CW + sideTonePitch = (CAT_BUFF[2] & 0x0F); + + if (sideTonePitch != 0 || sideToneSub != 0) + { + sideTone = (sideTonePitch * 50 + 300) + sideToneSub; + printLineF2(F("Sidetone set! CAT")); + EEPROM.put(CW_SIDETONE, sideTone); + delay(500); + printLine2(""); + } + break; + + case 0x61 : //Sidetone (Volume) (#44) + sideToneSub = (CAT_BUFF[2] & 0x7F); + if (sideTonePitch != 0 || sideToneSub != 0) + { + sideTone = (sideTonePitch * 50 + 300) + sideToneSub; + printLineF2(F("Sidetone set! CAT")); + EEPROM.put(CW_SIDETONE, sideTone); + delay(500); + printLine2(""); + } + break; + + /* + case 0x5F : // + //4-0 CW Weight (1.:2.5-1:4.5) (#22) From 0 to 14 (HEX) with 0 = 1:2.5, incrementing in 0.1 weight steps + //5 420 ARS (#2) 0 = Off, 1 = On + //6 144 ARS (#1) 0 = Off, 1 = On + //7 Sql/RF-G (#45) 0 = Off, 1 = On + CAT_BUFF[0] = 0x32; + CAT_BUFF[1] = 0x08; + break; + */ + case 0x60 : //CW Delay (10-2500 ms) (#17) From 1 to 250 (decimal) with each step representing 10 ms + //CAT_BUFF[0] = 0x19; + cwDelayTime = CAT_BUFF[2]; + printLineF2(F("CW Speed set!")); + EEPROM.put(CW_DELAY, cwDelayTime); + delay(500); + printLine2(""); + break; + case 0x62 : // + //5-0 CW Speed (4-60 WPM) (#21) From 0 to 38 (HEX) with 0 = 4 WPM and 38 = 60 WPM (1 WPM steps) + //7-6 Batt-Chg (6/8/10 Hours (#11) 00 = 6 Hours, 01 = 8 Hours, 10 = 10 Hours + cwSpeed = 1200 / ((CAT_BUFF[2] & 0x3F) + 4); + printLineF2(F("CW Speed set!")); + EEPROM.put(CW_SPEED, cwSpeed); + delay(500); + printLine2(""); + + break; + /* + case 0x63 : // + //6-0 VOX Gain (#51) Contains 1-100 (decimal) as displayed + //7 Disable AM/FM Dial (#4) 0 = Enable, 1 = Disable + CAT_BUFF[0] = 0xB2; + CAT_BUFF[1] = 0xA5; + break; + case 0x64 : // + //CAT_BUFF[0] = 0xA5; + //CAT_BUFF[1] = 0x00; + break; + case 0x67 : //6-0 SSB Mic (#46) Contains 0-100 (decimal) as displayed + CAT_BUFF[0] = 0xB2; + CAT_BUFF[1] = 0xB2; + //break; case 0x69 : //FM Mic (#29) Contains 0-100 (decimal) as displayed + //CAT_BUFF[0] = 0x32; + //CAT_BUFF[1] = 0x32; + //break; + case 0x78 : + CAT_BUFF[0] = catGetMode(); + // check, it must be a bit argument + if (CAT_BUFF[0] != 0) CAT_BUFF[0] = 1<<5; + break; + case 0x79 : // + //1-0 TX Power (All bands) 00 = High, 01 = L3, 10 = L2, 11 = L1 + //3 PRI On/Off 0 = Off, 1 = On + //DW On/Off 0 = Off, 1 = On + //SCN (Scan) Mode 00 = No scan, 10 = Scan up, 11 = Scan down + //ART On/Off 0 = Off, 1 = On + CAT_BUFF[0] = 0x00; + CAT_BUFF[1] = 0x00; + break; + case 0x7A : //SPLIT + //7A 0 HF Antenna Select 0 = Front, 1 = Rear + //7A 1 6 M Antenna Select 0 = Front, 1 = Rear + //7A 2 FM BCB Antenna Select 0 = Front, 1 = Rear + //7A 3 Air Antenna Select 0 = Front, 1 = Rear + //7A 4 2 M Antenna Select 0 = Front, 1 = Rear + //7A 5 UHF Antenna Select 0 = Front, 1 = Rear + //7A 6 ? ? + //7A 7 SPL On/Off 0 = Off, 1 = On + + CAT_BUFF[0] = (isSplitOn ? 0xFF : 0x7F); + break; + case 0xB3 : // + CAT_BUFF[0] = 0x00; + CAT_BUFF[1] = 0x4D; + break; + */ + } + + // sent the data + SendCatData(2); + Serial.write(ACK); +} + +void CatRxStatus(byte fromType) +{ + byte sMeterValue = 1; + + /* + http://www.ka7oei.com/ft817_meow.html + Command E7 - Read Receiver Status: This command returns one byte. Its contents are valid only when the '817 is in receive mode and it should be ignored when transmitting. + The lower 4 bits (0-3) of this byte indicate the current S-meter reading. 00 refers to an S-Zero reading, 04 = S4, 09 = S9, 0A = "10 over," 0B = "20 over" and so on up to 0F. + Bit 4 contains no useful information. + Bit 5 is 0 in non-FM modes, and it is 0 if the discriminator is centered (within 3.5 kHz for standard FM) when in the FM, FMN, or PKT modes, and 1 if the receiver is off-frequency. + Bit 6 is 0 if the CTCSS or DCS is turned off (or in a mode where it is not available.) It is also 0 if there is a signal being receive and the correct CTCSS tone or DCS code is being decoded. + It is 1 if there is a signal and the CTCSS/DCS decoding is enable, but the wrong CTCSS tone, DCS code, or no CTCSS/DCS is present. + Bit 7 is 0 if there is a signal present, or 1 if the receiver is squelched. + */ + // The lower 4 bits (0-3) of this byte indicate the current S-meter reading. 00 refers to an S-Zero reading, 04 = S4, 09 = S9, 0A = "10 over," 0B = "20 over" and so on up to 0F. + CAT_BUFF[0] = sMeterValue & 0b00001111; + SendCatData(1); +} + + +void CatTxStatus(byte fromType) +{ + boolean isHighSWR = false; + boolean isSplitOn = false; + + /* + Inverted -> *ptt = ((p->tx_status & 0x80) == 0); <-- souce code in ft817.c (hamlib) + */ + CAT_BUFF[0] = ((inTx ? 0 : 1) << 7) + + ((isHighSWR ? 1 : 0) << 6) + //hi swr off / on + ((isSplitOn ? 1 : 0) << 5) + //Split on / off + (0 << 4) + //dummy data + 0x08; //P0 meter data + + SendCatData(1); +} + +unsigned long rxBufferArriveTime = 0; +byte rxBufferCheckCount = 0; + +//Prevent Stack Overflow +byte isProcessCheck_Cat = 0; + +//fromType normal : 0, TX : 1, CW_STRAIGHT : 2, CW_PADDLE : 3, CW_AUTOMODE : 4 +//if cw mode, no delay +void Check_Cat(byte fromType) +{ + byte i; + + //Check Serial Port Buffer + if (Serial.available() == 0) + { + //Set Buffer Clear status + rxBufferCheckCount = 0; + return; + } + else if (Serial.available() < 5) + { + //First Arrived + if (rxBufferCheckCount == 0) + { + rxBufferCheckCount = Serial.available(); + rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout + } + else if (rxBufferArriveTime < millis()) //timeout + { + //Clear Buffer + for (i = 0; i < Serial.available(); i++) + rxBufferCheckCount = Serial.read(); + + rxBufferCheckCount = 0; + } + else if (rxBufferCheckCount < Serial.available()) //increase buffer count, slow arrived + { + rxBufferCheckCount = Serial.available(); + rxBufferArriveTime = millis() + CAT_RECEIVE_TIMEOUT; //Set time for timeout + } + + return; + } + + //Arived CAT DATA + for (i = 0; i < 5; i++) + CAT_BUFF[i] = Serial.read(); + + if (isProcessCheck_Cat == 1) + return; + + isProcessCheck_Cat = 1; + + //reference : http://www.ka7oei.com/ft817_meow.html + switch(CAT_BUFF[4]) + { + //The stability has not been verified and there seems to be no need. so i remarked codes, + //if you need, unmark lines + /* + case 0x00 : //Lock On + if (isDialLock == 1) //This command returns 00 if it was unlocked, and F0 if already locked. + CAT_BUFF[0] = 0xF0; + else { + CAT_BUFF[0] = 0x00; + setDialLock(1, fromType); + } + Serial.write(CAT_BUFF[0]); //Time + break; + case 0x80 : //Lock Off + if (isDialLock == 0) //This command returns 00 if the '817 was already locked, and F0 (HEX) if already unlocked. + CAT_BUFF[0] = 0xF0; + else { + CAT_BUFF[0] = 0x00; + setDialLock(0, fromType); + } + Serial.write(CAT_BUFF[0]); //Time + break; + */ + + case 0x01 : //Set Frequency + CatSetFreq(fromType); + break; + + case 0x02 : //Split On + case 0x82: //Split Off + CatSetSplit(CAT_BUFF[4] == 0x02, fromType); + break; + + case 0x03 : //Read Frequency and mode + CatGetFreqMode(frequency, fromType); + break; + + case 0x07 : //Set Operating Mode + CatSetMode(CAT_BUFF[0], fromType); + break; + + case 0x08 : //Set PTT_ON + case 0x88: //Set PTT Off + CatSetPTT(CAT_BUFF[4] == 0x08, fromType); + break; + + case 0x81: //Toggle VFO + CatVFOToggle(true, fromType); + break; + + case 0xDB: //Read uBITX EEPROM Data + ReadEEPRom(fromType); //Call by uBITX Manager Program + break; + case 0xBB: //Read FT-817 EEPROM Data (for comfirtable) + ReadEEPRom_FT817(fromType); + break; + + case 0xDC: //Write uBITX EEPROM Data + WriteEEPRom(fromType); //Call by uBITX Manager Program + break; + case 0xBC: //Write FT-817 EEPROM Data (for comfirtable) + WriteEEPRom_FT817(fromType); + break; + + case 0xE7 : //Read RX Status + CatRxStatus(fromType); + break; + case 0xF7: //Read TX Status + CatTxStatus(fromType); + break; + default: + /* + char buff[16]; + sprintf(buff, "DEFAULT : %x", CAT_BUFF[4]); + printLine2(buff); + */ + Serial.write(ACK); + break; + } //end of switch + + isProcessCheck_Cat = 0; +} + +void Init_Cat(long baud, int portConfig) +{ + Serial.begin(baud, portConfig); + Serial.flush(); +} + diff --git a/ubitx_20/cw_autokey.ino b/ubitx_20/cw_autokey.ino new file mode 100644 index 0000000..7c6cc75 --- /dev/null +++ b/ubitx_20/cw_autokey.ino @@ -0,0 +1,406 @@ +/************************************************************************* + This source code is written for All amateur radio operator, + I have not had amateur radio communication for a long time. CW has been + around for a long time, and I do not know what kind of keyer and keying + software is fashionable. So I implemented the functions I need mainly. + + To minimize the use of memory space, we used bitwise operations. + For the alphabet, I put Morsecode in 1 byte. The front 4Bit is the length + and the 4Bit is the Morse code. Because the number is fixed in length, + there is no separate length information. The 5Bit on the right side is + the Morse code. + + I wrote this code myself, so there is no license restriction. + So this code allows anyone to write with confidence. + But keep it as long as the original author of the code. +----------------------------------------------------------------------------- + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +**************************************************************************/ +#include + +//27 + 10 + 18 + 1(SPACE) = //56 +const PROGMEM uint8_t cwAZTable[27] = {0b00100100 , 0b01001000 , 0b01001010 , 0b00111000 , 0b00010000, 0b01000010, 0b00111100, 0b01000000 , //A ~ H +0b00100000, 0b01000111 ,0b00111010, 0b01000100, 0b00101100, 0b00101000 , 0b00111110, 0b01000110, 0b01001101, 0b00110100, //I ~ R +0b00110000, 0b00011000, 0b00110010, 0b01000001, 0b00110110, 0b01001001, 0b01001011, 0b00111000}; //S ~ Z +PGM_P pCwAZTable = reinterpret_cast(cwAZTable); + +const PROGMEM uint8_t cw09Table[27] = {0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001, 0b00000000, 0b00010000, 0b00011000, 0b00011100, 0b00011110}; +PGM_P pcw09Table = reinterpret_cast(cw09Table); + +//# : AR, ~:BT, [:AS, ]:SK, ^:KN +const PROGMEM uint8_t cwSymbolIndex[] = {'.', ',', '?', '"', '!', '/', '(', ')', '&', ':', ';', '=', '+', '-', '_', '\'', '@', '#', '~', '[', ']', '^' }; +PGM_P pCwSymbolIndex = reinterpret_cast(cwSymbolIndex); + +const PROGMEM uint8_t cwSymbolTable[] = {0b11010101, 0b11110011, 0b11001100, 0b11011110, 0b11101011, 0b10100100, 0b10101100, 0b11101101, 0b10010000, 0b11111000, 0b11101010, 0b10100010, 0b10010100, 0b11100001, 0b11001101, 0b11010010, 0b11011010, 0b10010100, 0b10100010, 0b10010000, 0b11000101, 0b10101100}; +PGM_P pCwSymbolTable = reinterpret_cast(cwSymbolTable); +////const PROGMEM uint8_t cwSymbolLength[] = {6, 6, 6, 6, 6, 5, 5, 6, 5, 6, 6, 5, 5, 6, 6, 6, 6, 5, 5, 5, 6, 5}; + +// ":(Start"), ':(End "), >: My callsign, <:QSO Callsign (Second Callsign), #:AR, ~:BT, [:AS, ]:SK + +byte knobPosition = 0; +//byte cwTextData[30]; //Maximum 30 Remarked by KD8CE -> Direct Read EEPROM +byte autoCWSendCharEndIndex = 0; +byte autoCWSendCharIndex = 0; +unsigned long autoCWbeforeTime = 0; //for interval time between chars +byte pttBeforeStatus = 1; //PTT : default high +byte isKeyStatusAfterCWStart = 0; //0 : Init, 1 : Keyup after auto CW Start, 2 : Keydown after +byte selectedCWTextIndex = 0; +unsigned long autoCWKeydownCheckTime = 0; //for interval time between chars +byte changeReserveStatus = 0; +byte isAutoCWHold = 0; //auto CW Pause => Manual Keying => auto + +void autoSendPTTCheck() +{ + if (isCWAutoMode == 2) { //Sending Mode + //check PTT Button + //short Press => reservation or cancel + //long Press => Hold + if (digitalRead(PTT) == LOW) + { + //if (isKeyStatusAfterCWStart == 0) //Yet Press PTT from start TX + //{ + //} + + if (isKeyStatusAfterCWStart == 1) //while auto cw send, ptt up and ptt down again + { + //Start Time + autoCWKeydownCheckTime = millis() + 200; //Long push time + isKeyStatusAfterCWStart = 2; //Change status => ptt down agian + } + else if (isKeyStatusAfterCWStart == 2 && autoCWKeydownCheckTime < millis()) + { + //Hold Mode + isAutoCWHold = 1; + isKeyStatusAfterCWStart = 3; + } + else if (isKeyStatusAfterCWStart == 3) + { + autoCWKeydownCheckTime = millis() + 200; + } + } + else + { + //PTT UP + if (isKeyStatusAfterCWStart == 2) //0 (down before cw start) -> 1 (up while cw sending) -> 2 (down while cw sending) + { + if (autoCWKeydownCheckTime > millis()) //Short : Reservation or cancel Next Text + { + if (autoCWSendReservCount == 0 || + (autoCWSendReservCount < AUTO_CW_RESERVE_MAX && + autoCWSendReserv[autoCWSendReservCount - 1] != selectedCWTextIndex)) + { + //Reserve + autoCWSendReserv[autoCWSendReservCount++] = selectedCWTextIndex; + changeReserveStatus = 1; + } + else if (autoCWSendReservCount > 0 && autoCWSendReserv[autoCWSendReservCount - 1] == selectedCWTextIndex) + { + autoCWSendReservCount--; + changeReserveStatus = 1; + } + } // end of Short Key up + } + else if (isKeyStatusAfterCWStart == 3) //play from Hold (pause Auto CW Send) + { + isAutoCWHold = 0; + } + + isKeyStatusAfterCWStart = 1; //Change status => ptt up (while cw send mode) + } //end of PTT UP + } +} + +//Send 1 char +void sendCWChar(char cwKeyChar) +{ + byte sendBuff[7]; + byte i, j, charLength; + byte tmpChar; + + //For Macrofunction + //replace > and < to My callsign, qso callsign, use recursive function call + if (cwKeyChar == '>' || cwKeyChar == '<') + { + uint16_t callsignStartIndex = 0; + uint16_t callsignEndIndex = 0; + + if (cwKeyChar == '>') //replace my callsign + { + if (userCallsignLength > 0) + { + callsignStartIndex = 0; + callsignEndIndex = userCallsignLength; + } + } + else if (cwKeyChar == '<') //replace qso callsign + { + //ReadLength + callsignEndIndex = EEPROM.read(CW_STATION_LEN); + if (callsignEndIndex > 0) + { + callsignStartIndex = CW_STATION_LEN - callsignEndIndex - USER_CALLSIGN_DAT; + callsignEndIndex = callsignStartIndex + callsignEndIndex; + } + } + + if (callsignStartIndex == 0 && callsignEndIndex == 0) + return; + + for (uint16_t i = callsignStartIndex; i <= callsignEndIndex; i++) + { + sendCWChar(EEPROM.read(USER_CALLSIGN_DAT + i)); + autoSendPTTCheck(); //for reserve and cancel next CW Text + if (changeReserveStatus == 1) + { + changeReserveStatus = 0; + updateDisplay(); + } + + if (i < callsignEndIndex) delay_background(cwSpeed * 3, 4); // + } + + return; + } + else if (cwKeyChar >= 'A' && cwKeyChar <= 'Z') //Encode Char by KD8CEC + { + tmpChar = pgm_read_byte(pCwAZTable + (cwKeyChar - 'A')); + charLength = (tmpChar >> 4) & 0x0F; + for (i = 0; i < charLength; i++) + sendBuff[i] = (tmpChar << i) & 0x08; + } + else if (cwKeyChar >= '0' && cwKeyChar <= '9') + { + charLength = 5; + for (i = 0; i < charLength; i++) + sendBuff[i] = (pgm_read_byte(pcw09Table + (cwKeyChar - '0')) << i) & 0x10; + } + else if (cwKeyChar == ' ') + { + charLength = 0; + delay_background(cwSpeed * 4, 4); //7 -> basic interval is 3 + } + else if (cwKeyChar == '$') //7 digit + { + charLength = 7; + for (i = 0; i < 7; i++) + sendBuff[i] = (0b00010010 << i) & 0x80; //...1..1 + } + else + { + //symbol + for (i = 0; i < 22; i++) + { + if (pgm_read_byte(pCwSymbolIndex + i) == cwKeyChar) + { + tmpChar = pgm_read_byte(pCwSymbolTable + i); + charLength = ((tmpChar >> 6) & 0x03) + 3; + + for (j = 0; j < charLength; j++) + sendBuff[j] = (tmpChar << j + 2) & 0x80; + + break; + } + } + } + + for (i = 0; i < charLength; i++) + { + cwKeydown(); + if (sendBuff[i] == 0) + delay_background(cwSpeed, 4); + else + delay_background(cwSpeed * 3, 4); + cwKeyUp(); + if (i != charLength -1) + delay_background(cwSpeed, 4); + } +} + +/* +void sendAutoCW(int cwSendLength, char *sendString) +{ + byte i; + + if (!inTx){ + keyDown = 0; + cwTimeout = millis() + cwDelayTime * 10; + startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time + updateDisplay(); + + delay_background(delayBeforeCWStartTime * 2, 2); + } + + for (i = 0; i < cwSendLength; i++) + { + sendCWChar(sendString[i]); + if (i != cwSendLength -1) delay_background(cwSpeed * 3, 3); + } + + delay_background(cwDelayTime * 10, 2); + stopTx(); +} +*/ +byte isNeedScroll = 0; +unsigned long scrollDispayTime = 0; +#define scrollSpeed 500 +byte displayScrolStep = 0; + +int controlAutoCW(){ + int knob = 0; + byte i; + + byte cwStartIndex, cwEndIndex; + + if (cwAutoDialType == 0) + knob = enc_read(); + + if (knob != 0 || beforeCWTextIndex == 255 || isNeedScroll == 1){ //start display + if (knobPosition > 0 && knob < 0) + knobPosition--; + if (knobPosition < cwAutoTextCount * 10 -1 && knob > 0) + knobPosition++; + + selectedCWTextIndex = knobPosition / 10; + + if ((beforeCWTextIndex != selectedCWTextIndex) || + (isNeedScroll == 1 && beforeCWTextIndex == selectedCWTextIndex && scrollDispayTime < millis())) { + //Read CW Text Data Position From EEProm + EEPROM.get(CW_AUTO_DATA + (selectedCWTextIndex * 2), cwStartIndex); + EEPROM.get(CW_AUTO_DATA + (selectedCWTextIndex * 2 + 1), cwEndIndex); + + if (beforeCWTextIndex == selectedCWTextIndex) + { + if (++displayScrolStep > cwEndIndex - cwStartIndex) + displayScrolStep = 0; + } + else + { + displayScrolStep = 0; + } + + printLineFromEEPRom(0, 2, cwStartIndex + displayScrolStep + CW_DATA_OFSTADJ, cwEndIndex + CW_DATA_OFSTADJ); + + lcd.setCursor(0,0); + lcd.write(byteToChar(selectedCWTextIndex)); + lcd.write(':'); + isNeedScroll = (cwEndIndex - cwStartIndex) > 14 ? 1 : 0; + scrollDispayTime = millis() + scrollSpeed; + beforeCWTextIndex = selectedCWTextIndex; + } + } //end of check knob + + if (isCWAutoMode == 1) { //ready status + if (digitalRead(PTT) == LOW) //PTT Down : Start Auto CW or DialMode Change + { + if (pttBeforeStatus == 1) //High to Low Change + { + autoCWbeforeTime = millis() + 500; //Long push time + pttBeforeStatus = 0; + } + else if (autoCWbeforeTime < millis()) //while press PTT, OK Long push then Send Auto CW Text + { + sendingCWTextIndex = selectedCWTextIndex; + + //Information about Auto Send CW Text + autoCWSendCharEndIndex = cwEndIndex; //length of CW Text //ianlee + autoCWSendCharIndex = cwStartIndex; //position of Sending Char //ianlee + + isCWAutoMode = 2; //auto sending start + autoCWbeforeTime = 0; //interval between chars, 0 = always send + isKeyStatusAfterCWStart = 0; //Init PTT Key status + autoCWSendReservCount = 0; //Init Reserve Count + isAutoCWHold = 0; + if (!inTx){ //if not TX Status, change RX -> TX + keyDown = 0; + startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time + updateDisplay(); + + delay_background(delayBeforeCWStartTime * 2, 2); //for External AMP or personal situation + } + } + } + else if (pttBeforeStatus == 0 && autoCWbeforeTime > 0) //while reade status LOW -> HIGH (before Auto send Before) + { + pttBeforeStatus = 1; //HIGH + if (autoCWbeforeTime > millis()) //short Press -> ? DialModeChange + { + cwAutoDialType = (cwAutoDialType == 1 ? 0 : 1); //Invert DialMode between select CW Text and Frequency Tune + if (cwAutoDialType == 0) + printLineF1(F("Dial:Select Text")); + else + printLineF1(F("Dial:Freq Tune")); + + delay_background(1000, 0); + updateDisplay(); + } + } + } //end of isCWAutoMode == 1 condition + + if (isCWAutoMode == 2) { //Sending Mode + autoSendPTTCheck(); + + //check interval time, if you want adjust interval between chars, modify below + if (isAutoCWHold == 0 && (millis() - autoCWbeforeTime > cwSpeed * 3)) + { + sendCWChar(EEPROM.read(CW_AUTO_DATA + autoCWSendCharIndex++)); + + if (autoCWSendCharIndex > autoCWSendCharEndIndex) { //finish auto cw send + //check reserve status + if (autoCWSendReservCount > 0) + { + //prepare + sendingCWTextIndex = autoCWSendReserv[0]; + + for (i = 0; i < AUTO_CW_RESERVE_MAX -1; i++) + autoCWSendReserv[i] = autoCWSendReserv[i + 1]; + + EEPROM.get(CW_AUTO_DATA + (sendingCWTextIndex * 2), cwStartIndex); + EEPROM.get(CW_AUTO_DATA + (sendingCWTextIndex * 2 + 1), cwEndIndex); + + //Information about Auto Send CW Text + autoCWSendCharEndIndex = cwEndIndex; //length of CW Text //ianlee + autoCWSendCharIndex = cwStartIndex; //position of Sending Char //ianlee + autoCWSendReservCount--; //Decrease + + sendCWChar(' '); //APPLY SPACE between CW Texts + changeReserveStatus = 1; + } + else + { + isCWAutoMode = 1; //ready status + delay_background(cwDelayTime * 10, 2); + stopTx(); + } + } + + autoCWbeforeTime = millis(); + + if (changeReserveStatus == 1) + { + changeReserveStatus = 0; + updateDisplay(); + } + } + } + + //abort if this button is down + if (btnDown()) + { + isCWAutoMode = 0; //dsiable Auto CW Mode + printLine2ClearAndUpdate(); + delay_background(1000, 0); + } +} + diff --git a/ubitx_20/ubitx_20.ino b/ubitx_20/ubitx_20.ino index c6aab85..9c0760b 100644 --- a/ubitx_20/ubitx_20.ino +++ b/ubitx_20/ubitx_20.ino @@ -96,6 +96,8 @@ #include LiquidCrystal lcd(8,9,10,11,12,13); +#define VERSION_NUM 0x01 //for KD8CEC'S firmware and for memory management software + /** * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. * We have to be very careful with variables that are declared inside the functions as they are @@ -141,6 +143,26 @@ int count = 0; //to generally count ticks, loops, etc #define CW_SIDETONE 24 #define CW_SPEED 28 +//AT328 has 1KBytes EEPROM +#define VFO_A_MODE 256 +#define VFO_B_MODE 257 +#define CW_DELAY 258 +#define CW_START 259 + +// +#define VERSION_ADDRESS 779 //check Firmware version +//USER INFORMATION +#define USER_CALLSIGN_KEY 780 //0x59 +#define USER_CALLSIGN_LEN 781 //1BYTE (OPTION + LENGTH) + CALLSIGN (MAXIMUM 18) +#define USER_CALLSIGN_DAT 782 //CALL SIGN DATA //direct EEPROM to LCD basic offset + +//AUTO KEY STRUCTURE +//AUTO KEY USE 800 ~ 1023 +#define CW_AUTO_MAGIC_KEY 800 //0x73 +#define CW_AUTO_COUNT 801 //0 ~ 255 +#define CW_AUTO_DATA 803 //[INDEX, INDEX, INDEX,DATA,DATA, DATA (Positon offset is CW_AUTO_DATA +#define CW_DATA_OFSTADJ CW_AUTO_DATA - USER_CALLSIGN_DAT //offset adjust for ditect eeprom to lcd (basic offset is USER_CALLSIGN_DAT +#define CW_STATION_LEN 1023 //value range : 4 ~ 30 /** * The uBITX is an upconnversion transceiver. The first IF is at 45 MHz. * The first IF frequency is not exactly at 45 Mhz but about 5 khz lower, @@ -168,6 +190,10 @@ int count = 0; //to generally count ticks, loops, etc #define LOWEST_FREQ (3000000l) #define HIGHEST_FREQ (30000000l) +//When the frequency is moved by the dial, the maximum value by KD8CEC +#define LOWEST_FREQ_DIAL (3000l) +#define HIGHEST_FREQ_DIAL (60000000l) + //we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes //these are the parameter passed to startTx #define TX_SSB 0 @@ -177,11 +203,47 @@ char ritOn = 0; char vfoActive = VFO_A; int8_t meter_reading = 0; // a -1 on meter makes it invisible unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier; +unsigned long vfoA_eeprom, vfoB_eeprom; //for protect eeprom life unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial int cwSpeed = 100; //this is actuall the dot period in milliseconds extern int32_t calibration; +//for store the mode in eeprom +byte vfoA_mode=0, vfoB_mode = 0; //0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM +byte vfoA_mode_eeprom, vfoB_mode_eeprom; //for protect eeprom life + +//KD8CEC +//for AutoSave and protect eeprom life +byte saveIntervalSec = 10; //second +unsigned long saveCheckTime = 0; +unsigned long saveCheckFreq = 0; + +bool isSplitOn = false; +byte cwDelayTime = 60; +byte delayBeforeCWStartTime = 50; + +//sideTonePitch + sideToneSub = sideTone +byte sideTonePitch=0; +byte sideToneSub = 0; + +//DialLock +byte isDialLock = 0; +byte isTxOff = 0; + +//Variables for auto cw mode +byte isCWAutoMode = 0; //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending +byte cwAutoTextCount = 0; //cwAutoText Count +byte beforeCWTextIndex = 255; //when auto cw start, always beforeCWTextIndex = 255, (for first time check) +byte cwAutoDialType = 0; //0 : CW Text Change, 1 : Frequency Tune + +#define AUTO_CW_RESERVE_MAX 3 +byte autoCWSendReserv[AUTO_CW_RESERVE_MAX]; //Reserve CW Auto Send +byte autoCWSendReservCount = 0; //Reserve CW Text Cound +byte sendingCWTextIndex = 0; //cw auto seding Text Index + +byte userCallsignLength = 0; //7 : display callsign at system startup, 6~0 : callsign length (range : 1~18) + /** * Raduino needs to keep track of current state of the transceiver. These are a few variables that do it */ @@ -202,6 +264,41 @@ boolean modeCalibrate = false;//this mode of menus shows extended menus to calib * you start hacking around */ +/* + KD8CEC + When using the basic delay of the Arduino, the program freezes. + When the delay is used, the program will generate an error because it is not communicating, + so Create a new delay function that can do background processing. + */ + +unsigned long delayBeforeTime = 0; +byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWKey -> Check Paddle + delayBeforeTime = millis(); + + while (millis() <= delayBeforeTime + delayTime) { + + if (fromType == 4) + { + //CHECK PADDLE + if (getPaddle() != 0) //Interrupt : Stop cw Auto mode by Paddle -> Change Auto to Manual + return 1; + + //Check PTT while auto Sending + autoSendPTTCheck(); + + Check_Cat(3); + } + else + { + //Background Work + Check_Cat(fromType); + } + } + + return 0; +} + + /** * Select the properly tx harmonic filters * The four harmonic filters use only three relays @@ -257,7 +354,10 @@ void setTXFilters(unsigned long freq){ void setFrequency(unsigned long f){ uint64_t osc_f; - + + //1 digits discarded + f = (f / 50) * 50; + setTXFilters(f); if (isUSB){ @@ -278,9 +378,12 @@ void setFrequency(unsigned long f){ * Note: In cw mode, doesnt key the radio, only puts it in tx mode */ -void startTx(byte txMode){ - unsigned long tx_freq = 0; - digitalWrite(TX_RX, 1); +void startTx(byte txMode, byte isDisplayUpdate){ + unsigned long tx_freq = 0; + + if (isTxOff != 1) + digitalWrite(TX_RX, 1); + inTx = 1; if (ritOn){ @@ -302,7 +405,10 @@ void startTx(byte txMode){ else si5351bx_setfreq(2, frequency - sideTone); } - updateDisplay(); + + //reduce latency time when begin of CW mode + if (isDisplayUpdate == 1) + updateDisplay(); } void stopTx(){ @@ -355,7 +461,7 @@ void checkPTT(){ return; if (digitalRead(PTT) == 0 && inTx == 0){ - startTx(TX_SSB); + startTx(TX_SSB, 1); delay(50); //debounce the PTT } @@ -374,9 +480,12 @@ void checkButton(){ return; doMenu(); + //wait for the button to go up again - while(btnDown()) + while(btnDown()) { delay(10); + Check_Cat(0); + } delay(50);//debounce } @@ -389,33 +498,46 @@ void checkButton(){ */ void doTuning(){ - int s; + int s = 0; unsigned long prev_freq; + int incdecValue = 0; + + if (isDialLock == 1) + return; + + if (isCWAutoMode == 0 || cwAutoDialType == 1) + s = enc_read(); - s = enc_read(); if (s){ prev_freq = frequency; if (s > 10) - frequency += 200000l; + incdecValue = 200000l; if (s > 7) - frequency += 10000l; + incdecValue = 10000l; else if (s > 4) - frequency += 1000l; + incdecValue = 1000l; else if (s > 2) - frequency += 500; + incdecValue = 500; else if (s > 0) - frequency += 50l; + incdecValue = 50l; else if (s > -2) - frequency -= 50l; + incdecValue = -50l; else if (s > -4) - frequency -= 500l; + incdecValue = -500l; else if (s > -7) - frequency -= 1000l; + incdecValue = -1000l; else if (s > -9) - frequency -= 10000l; + incdecValue = -10000l; else - frequency -= 200000l; + incdecValue = -200000l; + + if (incdecValue > 0 && frequency + incdecValue > HIGHEST_FREQ_DIAL) + frequency = HIGHEST_FREQ_DIAL; + else if (incdecValue < 0 && frequency < -incdecValue + LOWEST_FREQ_DIAL) //for compute and compare based integer type. + frequency = LOWEST_FREQ_DIAL; + else + frequency += incdecValue; if (prev_freq < 10000000l && frequency > 10000000l) isUSB = true; @@ -448,6 +570,39 @@ void doRIT(){ } } +/** + save Frequency and mode to eeprom + */ +void storeFrequencyAndMode(byte saveType) +{ + //freqType : 0 Both (vfoA and vfoB), 1 : vfoA, 2 : vfoB + if (saveType == 0 || saveType == 1) //vfoA + { + if (vfoA != vfoA_eeprom) { + EEPROM.put(VFO_A, vfoA); + vfoA_eeprom = vfoA; + } + + if (vfoA_mode != vfoA_mode_eeprom) { + EEPROM.put(VFO_A_MODE, vfoA_mode); + vfoA_mode_eeprom = vfoA_mode; + } + } + + if (saveType == 0 || saveType == 2) //vfoB + { + if (vfoB != vfoB_eeprom) { + EEPROM.put(VFO_B, vfoB); + vfoB_eeprom = vfoB; + } + + if (vfoB_mode != vfoB_mode_eeprom) { + EEPROM.put(VFO_B_MODE, vfoB_mode); + vfoB_mode_eeprom = vfoB_mode; + } + } +} + /** * The settings are read from EEPROM. The first time around, the values may not be * present or out of range, in this case, some intelligent defaults are copied into the @@ -462,17 +617,67 @@ void initSettings(){ EEPROM.get(VFO_B, vfoB); EEPROM.get(CW_SIDETONE, sideTone); EEPROM.get(CW_SPEED, cwSpeed); + + //for Save VFO_A_MODE to eeprom + //0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM + EEPROM.get(VFO_A_MODE, vfoA_mode); + EEPROM.get(VFO_B_MODE, vfoB_mode); + + //CW DelayTime + EEPROM.get(CW_DELAY, cwDelayTime); + + //CW interval between TX and CW Start + EEPROM.get(CW_START, delayBeforeCWStartTime); + + //User callsign information + if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59) + userCallsignLength = EEPROM.read(USER_CALLSIGN_LEN); //MAXIMUM 18 LENGTH + + //Version Write for Memory Management Software + if (EEPROM.read(VERSION_ADDRESS) != VERSION_NUM) + EEPROM.write(VERSION_ADDRESS, VERSION_NUM); + + if (cwDelayTime < 1 || cwDelayTime > 250) + cwDelayTime = 60; + + if (vfoA_mode < 2) + vfoA_mode = 2; + + if (vfoB_mode < 2) + vfoB_mode = 3; + if (usbCarrier > 12010000l || usbCarrier < 11990000l) - usbCarrier = 11997000l; - if (vfoA > 35000000l || 3500000l > vfoA) + usbCarrier = 11995000l; + + if (vfoA > 35000000l || 3500000l > vfoA) { vfoA = 7150000l; - if (vfoB > 35000000l || 3500000l > vfoB) + vfoA_mode = 2; + } + + if (vfoB > 35000000l || 3500000l > vfoB) { vfoB = 14150000l; + vfoB_mode = 3; + } + + //for protect eeprom life + vfoA_eeprom = vfoA; + vfoB_eeprom = vfoB; + vfoA_mode_eeprom = vfoA_mode; + vfoB_mode_eeprom = vfoB_mode; + if (sideTone < 100 || 2000 < sideTone) sideTone = 800; if (cwSpeed < 10 || 1000 < cwSpeed) cwSpeed = 100; - + + if (sideTone < 300 || sideTone > 1000) { + sideTonePitch = 0; + sideToneSub = 0;; + } + else{ + sideTonePitch = (sideTone - 300) / 50; + sideToneSub = sideTone % 50; + } } void initPorts(){ @@ -510,41 +715,185 @@ void initPorts(){ void setup() { - Serial.begin(9600); - + //Init EEProm for Fault EEProm TEST and Factory Reset + /* + for (int i = 0; i < 1024; i++) + EEPROM.write(i, 0); + */ + //Serial.begin(9600); lcd.begin(16, 2); - //we print this line so this shows up even if the raduino - //crashes later in the code - printLine1("uBITX v0.20"); - delay(500); - + Init_Cat(38400, SERIAL_8N1); initMeter(); //not used in this build initSettings(); + + printLineF(1, F("CECBT v0.25")); + if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) + { + userCallsignLength = userCallsignLength & 0x7F; + printLineFromEEPRom(0, 0, 0, userCallsignLength -1); //eeprom to lcd use offset (USER_CALLSIGN_DAT) + } + else + { + printLineF(0, F("uBITX v0.20")); + delay_background(500, 0); + printLine2(""); + } + initPorts(); initOscillators(); frequency = vfoA; + saveCheckFreq = frequency; //for auto save frequency + byteToMode(vfoA_mode); setFrequency(vfoA); updateDisplay(); if (btnDown()) factory_alignment(); + +/* + //This is for auto key test + EEPROM.put(CW_AUTO_MAGIC_KEY, 0x73); //MAGIC KEY + EEPROM.put(CW_AUTO_COUNT, 3); //WORD COUNT + EEPROM.put(CW_AUTO_DATA + 0, 6); // 0 word begin postion / CQCQ TEST K + EEPROM.put(CW_AUTO_DATA + 1, 33); // 0 word end postion / CQCQ TEST K + EEPROM.put(CW_AUTO_DATA + 2, 34); //1 word begin position / LOL LOL + EEPROM.put(CW_AUTO_DATA + 3, 40); //1 word end position / LOL LOL + EEPROM.put(CW_AUTO_DATA + 4, 41); //2 word begin position / /?![]789 + EEPROM.put(CW_AUTO_DATA + 5, 48); //2 word end position / /?![]789 + + EEPROM.put(CW_AUTO_DATA + 6, 'C'); // + EEPROM.put(CW_AUTO_DATA + 7, 'Q'); // + EEPROM.put(CW_AUTO_DATA + 8, 'C'); // + EEPROM.put(CW_AUTO_DATA + 9, 'Q'); // + EEPROM.put(CW_AUTO_DATA + 10, ' '); // + EEPROM.put(CW_AUTO_DATA + 11, 'D'); // + EEPROM.put(CW_AUTO_DATA + 12, 'E'); // + EEPROM.put(CW_AUTO_DATA + 13, ' '); // + EEPROM.put(CW_AUTO_DATA + 14, 'K'); // + EEPROM.put(CW_AUTO_DATA + 15, 'D'); // + EEPROM.put(CW_AUTO_DATA + 16, '8'); // + EEPROM.put(CW_AUTO_DATA + 17, 'C'); // + EEPROM.put(CW_AUTO_DATA + 18, 'E'); // + EEPROM.put(CW_AUTO_DATA + 19, 'C'); // + EEPROM.put(CW_AUTO_DATA + 20, ' '); // + EEPROM.put(CW_AUTO_DATA + 21, 'E'); // + EEPROM.put(CW_AUTO_DATA + 22, 'M'); // + EEPROM.put(CW_AUTO_DATA + 23, '3'); // + EEPROM.put(CW_AUTO_DATA + 24, '7'); // + EEPROM.put(CW_AUTO_DATA + 25, ' '); // + EEPROM.put(CW_AUTO_DATA + 26, 'D'); // + EEPROM.put(CW_AUTO_DATA + 27, 'E'); // + EEPROM.put(CW_AUTO_DATA + 28, ' '); // + EEPROM.put(CW_AUTO_DATA + 29, 'C'); // + EEPROM.put(CW_AUTO_DATA + 30, 'E'); // + EEPROM.put(CW_AUTO_DATA + 31, 'C'); // + EEPROM.put(CW_AUTO_DATA + 32, ' '); // + EEPROM.put(CW_AUTO_DATA + 33, 'K'); // +*/ + +/* + EEPROM.put(CW_AUTO_DATA + 34, '<'); // + EEPROM.put(CW_AUTO_DATA + 35, ' '); // + EEPROM.put(CW_AUTO_DATA + 36, '>'); // + EEPROM.put(CW_AUTO_DATA + 37, ' '); // + EEPROM.put(CW_AUTO_DATA + 38, '7'); // + EEPROM.put(CW_AUTO_DATA + 39, '3'); // + EEPROM.put(CW_AUTO_DATA + 40, 'K'); // + + EEPROM.put(CW_AUTO_DATA + 41, 'C'); // + EEPROM.put(CW_AUTO_DATA + 42, 'Q'); // + EEPROM.put(CW_AUTO_DATA + 43, ' '); // + EEPROM.put(CW_AUTO_DATA + 44, '>'); // start " + EEPROM.put(CW_AUTO_DATA + 45, ' '); // end " + EEPROM.put(CW_AUTO_DATA + 46, '>'); // + EEPROM.put(CW_AUTO_DATA + 47, ' '); // + EEPROM.put(CW_AUTO_DATA + 48, 'K'); // +*/ + +/* + //This is for auto key test2 + //USER CALL SIGN + EEPROM.put(USER_CALLSIGN_KEY, 0x59); //MAGIC KEY + //EEPROM.put(USER_CALLSIGN_LEN, 10); //WORD COUNT + EEPROM.put(USER_CALLSIGN_LEN, 10 + 0x80); //WORD COUNT + + EEPROM.put(USER_CALLSIGN_DAT + 1, 'K'); // + EEPROM.put(USER_CALLSIGN_DAT + 2, 'D'); // + EEPROM.put(USER_CALLSIGN_DAT + 3, '8'); // + EEPROM.put(USER_CALLSIGN_DAT + 4, 'C'); // + EEPROM.put(USER_CALLSIGN_DAT + 5, 'E'); // + EEPROM.put(USER_CALLSIGN_DAT + 6, 'C'); // + EEPROM.put(USER_CALLSIGN_DAT + 7, '/'); // + EEPROM.put(USER_CALLSIGN_DAT + 8, 'A'); // + EEPROM.put(USER_CALLSIGN_DAT + 9, 'B'); // + EEPROM.put(USER_CALLSIGN_DAT + 10, 'C'); // + + //CW QSO CALLSIGN + EEPROM.put(CW_STATION_LEN, 6); // + EEPROM.put(CW_STATION_LEN - 6 + 0 , 'A'); // + EEPROM.put(CW_STATION_LEN - 6 + 1 , 'B'); // + EEPROM.put(CW_STATION_LEN - 6 + 2 , '1'); // + EEPROM.put(CW_STATION_LEN - 6 + 3 , 'C'); // + EEPROM.put(CW_STATION_LEN - 6 + 4 , 'D'); // + EEPROM.put(CW_STATION_LEN - 6 + 5 , 'E'); // +*/ + } /** * The loop checks for keydown, ptt, function button and tuning. */ - +//for debug +int dbgCnt = 0; byte flasher = 0; -void loop(){ - - cwKeyer(); - if (!txCAT) - checkPTT(); - checkButton(); +void checkAutoSaveFreqMode() +{ + //when tx or ritOn, disable auto save + if (inTx || ritOn) + return; + + //detect change frequency + if (saveCheckFreq != frequency) + { + saveCheckTime = millis(); + saveCheckFreq = frequency; + } + else if (saveCheckTime != 0) + { + //check time for Frequency auto save + if (millis() - saveCheckTime > saveIntervalSec * 1000) + { + if (vfoActive == VFO_A) + { + vfoA = frequency; + vfoA_mode = modeToByte(); + storeFrequencyAndMode(1); + } + else + { + vfoB = frequency; + vfoB_mode = modeToByte(); + storeFrequencyAndMode(2); + } + } + } +} + +void loop(){ + if (isCWAutoMode == 0){ //when CW AutoKey Mode, disable this process + if (!txCAT) + checkPTT(); + checkButton(); + } + else + controlAutoCW(); + + cwKeyer(); + //tune only when not tranmsitting if (!inTx){ if (ritOn) @@ -552,7 +901,8 @@ void loop(){ else doTuning(); } - + //we check CAT after the encoder as it might put the radio into TX - checkCAT(); + Check_Cat(inTx? 1 : 0); + checkAutoSaveFreqMode(); } diff --git a/ubitx_20/ubitx_cat.ino b/ubitx_20/ubitx_cat.ino deleted file mode 100644 index 687595c..0000000 --- a/ubitx_20/ubitx_cat.ino +++ /dev/null @@ -1,231 +0,0 @@ -/** - * The CAT protocol is used by many radios to provide remote control to comptuers through - * the serial port. - * - * This is very much a work in progress. Parts of this code have been liberally - * borrowed from other GPLicensed works like hamlib. - * - * WARNING : This is an unstable version and it has worked with fldigi, - * it gives time out error with WSJTX 1.8.0 - */ - -// The next 4 functions are needed to implement the CAT protocol, which -// uses 4-bit BCD formatting. -// -byte setHighNibble(byte b,byte v) { - // Clear the high nibble - b &= 0x0f; - // Set the high nibble - return b | ((v & 0x0f) << 4); -} - -byte setLowNibble(byte b,byte v) { - // Clear the low nibble - b &= 0xf0; - // Set the low nibble - return b | (v & 0x0f); -} - -byte getHighNibble(byte b) { - return (b >> 4) & 0x0f; -} - -byte getLowNibble(byte b) { - return b & 0x0f; -} - -// Takes a number and produces the requested number of decimal digits, staring -// from the least significant digit. -// -void getDecimalDigits(unsigned long number,byte* result,int digits) { - for (int i = 0; i < digits; i++) { - // "Mask off" (in a decimal sense) the LSD and return it - result[i] = number % 10; - // "Shift right" (in a decimal sense) - number /= 10; - } -} - -// Takes a frequency and writes it into the CAT command buffer in BCD form. -// -void writeFreq(unsigned long freq,byte* cmd) { - // Convert the frequency to a set of decimal digits. We are taking 9 digits - // so that we can get up to 999 MHz. But the protocol doesn't care about the - // LSD (1's place), so we ignore that digit. - byte digits[9]; - getDecimalDigits(freq,digits,9); - // Start from the LSB and get each nibble - cmd[3] = setLowNibble(cmd[3],digits[1]); - cmd[3] = setHighNibble(cmd[3],digits[2]); - cmd[2] = setLowNibble(cmd[2],digits[3]); - cmd[2] = setHighNibble(cmd[2],digits[4]); - cmd[1] = setLowNibble(cmd[1],digits[5]); - cmd[1] = setHighNibble(cmd[1],digits[6]); - cmd[0] = setLowNibble(cmd[0],digits[7]); - cmd[0] = setHighNibble(cmd[0],digits[8]); -} - -// This function takes a frquency that is encoded using 4 bytes of BCD -// representation and turns it into an long measured in Hz. -// -// [12][34][56][78] = 123.45678? Mhz -// -unsigned long readFreq(byte* cmd) { - // Pull off each of the digits - byte d7 = getHighNibble(cmd[0]); - byte d6 = getLowNibble(cmd[0]); - byte d5 = getHighNibble(cmd[1]); - byte d4 = getLowNibble(cmd[1]); - byte d3 = getHighNibble(cmd[2]); - byte d2 = getLowNibble(cmd[2]); - byte d1 = getHighNibble(cmd[3]); - byte d0 = getLowNibble(cmd[3]); - return - (unsigned long)d7 * 100000000L + - (unsigned long)d6 * 10000000L + - (unsigned long)d5 * 1000000L + - (unsigned long)d4 * 100000L + - (unsigned long)d3 * 10000L + - (unsigned long)d2 * 1000L + - (unsigned long)d1 * 100L + - (unsigned long)d0 * 10L; -} - -/** - * Responds to all the cat commands, emulates FT-817 - */ - -void processCATCommand(byte* cmd) { - byte response[5]; - - // Debugging code, enable it to fix the cat implementation - - count++; - if (cmd[4] == 0x00){ - response[0]=0; - Serial.write(response, 1); - } - else if (cmd[4] == 0x01) { - unsigned long f = readFreq(cmd); - setFrequency(f); - updateDisplay(); - //sprintf(b, "set:%ld", f); - //printLine2(b); - - } - // Get frequency - else if (cmd[4] == 0x03){ - writeFreq(frequency,response); // Put the frequency into the buffer - if (isUSB) - response[4] = 0x01; //USB - else - response[4] = 0x00; //LSB - Serial.write(response,5); - printLine2("cat:getfreq"); - } - else if (cmd[4] == 0x07){ // set mode - if (cmd[0] == 0x00 || cmd[0] == 0x03) - isUSB = 0; - else - isUSB = 1; - response[0] = 0x00; - Serial.write(response, 1); - setFrequency(frequency); - //printLine2("cat: mode changed"); - //updateDisplay(); - } - else if (cmd[4] == 0x88){ - if (inTx){ - stopTx(); - txCAT = false; - } - else - response[0] = 0xf0; - printLine2("tx > rx"); - Serial.write(response,1); - } - else if (cmd[4] == 0x08) { // PTT On - if (!inTx) { - response[0] = 0; - txCAT = true; - startTx(TX_SSB); - updateDisplay(); - } else { - response[0] = 0xf0; - } - Serial.write(response,1); - printLine2("rx > tx"); - } - // Read TX keyed state - else if (cmd[4] == 0x10) { - if (!inTx) { - response[0] = 0; - } else { - response[0] = 0xf0; - } - Serial.write(response,1); - printLine2("cat;0x10"); - } - // PTT Off - else if (cmd[4] == 0x88) { - byte resBuf[0]; - if (inTx) { - response[0] = 0; - } else { - response[0] = 0xf0; - } - Serial.write(response,1); - printLine2("cat;0x88"); - //keyed = false; - //digitalWrite(13,LOW); - } - // Read receiver status - else if (cmd[4] == 0xe7) { - response[0] = 0x09; - Serial.write(response,1); - printLine2("cat;0xe7"); - } - else if (cmd[4] == 0xf5){ - - } - // Read receiver status - else if (cmd[4] == 0xf7) { - response[0] = 0x00; - if (inTx) { - response[0] = response[0] | 0xf0; - } - Serial.write(response,1); - printLine2("cat;0xf7"); - } - else { - //somehow, get this to print the four bytes - ultoa(*((unsigned long *)cmd), c, 16); - itoa(cmd[4], b, 16); - strcat(b, ":"); - strcat(b, c); - printLine2(b); - response[0] = 0x00; - Serial.write(response[0]); - } - -} - - - -void checkCAT(){ - static byte cat[5]; - byte i; - - if (Serial.available() < 5) - return; - - cat[4] = cat[3]; - cat[3] = cat[2]; - cat[2] = cat[0]; - for (i = 0; i < 5; i++) - cat[i] = Serial.read(); - - processCATCommand(cat); -} - - diff --git a/ubitx_20/ubitx_keyer.ino b/ubitx_20/ubitx_keyer.ino index 3a9c86f..5d2b668 100644 --- a/ubitx_20/ubitx_keyer.ino +++ b/ubitx_20/ubitx_keyer.ino @@ -23,7 +23,7 @@ // in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs -#define CW_TIMEOUT (600l) +//#define CW_TIMEOUT (600l) //Change to CW Delaytime for value save to eeprom #define PADDLE_DOT 1 #define PADDLE_DASH 2 #define PADDLE_BOTH 3 @@ -61,7 +61,10 @@ void cwKeydown(){ keyDown = 1; //tracks the CW_KEY tone(CW_TONE, (int)sideTone); digitalWrite(CW_KEY, 1); - cwTimeout = millis() + CW_TIMEOUT; + + //Modified by KD8CEC, for CW Delay Time save to eeprom + //cwTimeout = millis() + CW_TIMEOUT; + cwTimeout = millis() + cwDelayTime * 10; } /** @@ -72,7 +75,10 @@ void cwKeyUp(){ keyDown = 0; //tracks the CW_KEY noTone(CW_TONE); digitalWrite(CW_KEY, 0); - cwTimeout = millis() + CW_TIMEOUT; + + //Modified by KD8CEC, for CW Delay Time save to eeprom + //cwTimeout = millis() + CW_TIMEOUT; + cwTimeout = millis() + cwDelayTime * 10; } /** @@ -92,6 +98,10 @@ void cwKeyer(){ // do nothing if the paddle has not been touched, unless // we are in the cw mode and we have timed out if (!paddle){ + //modifed by KD8CEC for auto CW Send + if (isCWAutoMode > 1) //if while auto cw sending, dont stop tx by paddle position + return; + if (0 < cwTimeout && cwTimeout < millis()){ cwTimeout = 0; keyDown = 0; @@ -103,48 +113,69 @@ void cwKeyer(){ //if a paddle was used (not a straight key) we should extend the space to be a full dash //by adding two more dots long space (one has already been added at the end of the dot or dash) + /* if (cwTimeout > 0 && lastPaddle != PADDLE_STRAIGHT) - delay(cwSpeed * 2); + delay_background(cwSpeed * 2, 3); + //delay(cwSpeed * 2); // got back to the begining of the loop, if no further activity happens on the paddle or the straight key // we will time out, and return out of this routine delay(5); + */ continue; } - Serial.print("paddle:");Serial.println(paddle); + //if while auto cw send, stop auto cw + //but isAutoCWHold for Manual Keying with cwAutoSend + if (isCWAutoMode > 1 && isAutoCWHold == 0) + isCWAutoMode = 1; //read status + + //Remoark Debug code / Serial Use by CAT Protocol + //Serial.print("paddle:");Serial.println(paddle); // if we are here, it is only because the key or the paddle is pressed if (!inTx){ keyDown = 0; - cwTimeout = millis() + CW_TIMEOUT; - startTx(TX_CW); + //Modified by KD8CEC, for CW Delay Time save to eeprom + //cwTimeout = millis() + CW_TIMEOUT; + cwTimeout = millis() + cwDelayTime * 10; + + startTx(TX_CW, 0); //disable updateDisplay Command for reduce latency time updateDisplay(); + + //DelayTime Option + delay_background(delayBeforeCWStartTime * 2, 2); } // star the transmission) // we store the transmitted character in the lastPaddle cwKeydown(); if (paddle == PADDLE_DOT){ - delay(cwSpeed); + //delay(cwSpeed); + delay_background(cwSpeed, 3); lastPaddle = PADDLE_DOT; } else if (paddle == PADDLE_DASH){ - delay(cwSpeed * 3); + //delay(cwSpeed * 3); + delay_background(cwSpeed * 3, 3); lastPaddle = PADDLE_DASH; } else if (paddle == PADDLE_BOTH){ //both paddles down //depending upon what was sent last, send the other if (lastPaddle == PADDLE_DOT) { - delay(cwSpeed * 3); + //delay(cwSpeed * 3); + delay_background(cwSpeed * 3, 3); lastPaddle = PADDLE_DASH; }else{ - delay(cwSpeed); + //delay(cwSpeed); + delay_background(cwSpeed, 3); lastPaddle = PADDLE_DOT; } } else if (paddle == PADDLE_STRAIGHT){ - while (getPaddle() == PADDLE_STRAIGHT) + while (getPaddle() == PADDLE_STRAIGHT) { delay(1); + Check_Cat(2); + } lastPaddle = PADDLE_STRAIGHT; } cwKeyUp(); diff --git a/ubitx_20/ubitx_menu.ino b/ubitx_20/ubitx_menu.ino index fff0378..a242ea3 100644 --- a/ubitx_20/ubitx_menu.ino +++ b/ubitx_20/ubitx_menu.ino @@ -10,8 +10,8 @@ * - If the menu item is clicked on, then it is selected, * - If the menu item is NOT clicked on, then the menu's prompt is to be displayed */ - - +#define printLineF1(x) (printLineF(1, x)) +#define printLineF2(x) (printLineF(0, x)) int menuBand(int btn){ int knob = 0; @@ -22,14 +22,17 @@ int menuBand(int btn){ // offset = frequency % 1000000l; if (!btn){ - printLine2("Band Select?"); + printLineF2(F("Band Select?")); return; } - printLine2("Press to confirm"); + printLineF2(F("Press to confirm")); //wait for the button menu select button to be lifted) - while (btnDown()) + while (btnDown()) { delay(50); + Check_Cat(0); //To prevent disconnections + } + delay(50); ritDisable(); @@ -58,50 +61,72 @@ int menuBand(int btn){ updateDisplay(); } delay(20); + Check_Cat(0); //To prevent disconnections } - while(btnDown()) + while(btnDown()) { delay(50); + Check_Cat(0); //To prevent disconnections + } + delay(50); - printLine2(""); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } -void menuVfoToggle(int btn){ - +//0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM +byte modeToByte(){ + if (isUSB) + return 3; + else + return 2; +} + +void byteToMode(byte modeValue){ + if (modeValue == 3) + isUSB = 1; + else + isUSB = 0; +} + +void menuVfoToggle(int btn) +{ if (!btn){ if (vfoActive == VFO_A) - printLine2("Select VFO B? "); + printLineF2(F("Select VFO B?")); else - printLine2("Select VFO A? "); + printLineF2(F("Select VFO A?")); } else { if (vfoActive == VFO_B){ vfoB = frequency; - EEPROM.put(VFO_B, frequency); + vfoB_mode = modeToByte(); + storeFrequencyAndMode(2); //vfoB -> eeprom + vfoActive = VFO_A; - printLine2("Selected VFO A "); frequency = vfoA; + saveCheckFreq = frequency; + byteToMode(vfoA_mode); + printLineF2(F("Selected VFO A")); } else { vfoA = frequency; - EEPROM.put(VFO_A, frequency); + vfoA_mode = modeToByte(); + storeFrequencyAndMode(1); //vfoA -> eeprom + vfoActive = VFO_B; - printLine2("Selected VFO B "); frequency = vfoB; + saveCheckFreq = frequency; + byteToMode(vfoB_mode); + printLineF2(F("Selected VFO B")); } - + ritDisable(); - setFrequency(frequency); - if (frequency >= 10000000l) - isUSB = true; - else - isUSB = false; - updateDisplay(); - printLine2(""); - delay(1000); + + //updateDisplay(); + delay_background(500, 0); + printLine2ClearAndUpdate(); //exit the menu menuOn = 0; } @@ -110,49 +135,67 @@ void menuVfoToggle(int btn){ void menuRitToggle(int btn){ if (!btn){ if (ritOn == 1) - printLine2("RIT:On, Off? "); + printLineF2(F("RIT:On, Off?")); else - printLine2("RIT:Off, On? "); + printLineF2(F("RIT:Off, On?")); } else { if (ritOn == 0){ - printLine2("RIT is ON"); + printLineF2(F("RIT is ON")); //enable RIT so the current frequency is used at transmit ritEnable(frequency); } else{ - printLine2("RIT is OFF"); + printLineF2(F("RIT is OFF")); ritDisable(); } menuOn = 0; - delay(500); - printLine2(""); - updateDisplay(); + delay_background(500, 0); + printLine2ClearAndUpdate(); } } void menuSidebandToggle(int btn){ if (!btn){ if (isUSB == true) - printLine2("Select LSB?"); + printLineF2(F("Select LSB?")); else - printLine2("Select USB?"); + printLineF2(F("Select USB?")); } else { if (isUSB == true){ isUSB = false; - printLine2("LSB Selected"); - delay(500); - printLine2(""); + printLineF2(F("LSB Selected")); } else { isUSB = true; - printLine2("USB Selected"); - delay(500); - printLine2(""); + printLineF2(F("USB Selected")); } - - updateDisplay(); + setFrequency(frequency); + delay_background(500, 0); + printLine2ClearAndUpdate(); + menuOn = 0; + } +} + +void menuTxOnOff(int btn){ + if (!btn){ + if (isTxOff == 0) + printLineF2(F("TX OFF?")); + else + printLineF2(F("TX ON?")); + } + else { + if (isTxOff == 0){ + isTxOff = 1; + printLineF2(F("TX OFF!")); + } + else { + isTxOff = 0; + printLineF2(F("TX ON!")); + } + delay_background(500, 0); + printLine2ClearAndUpdate(); menuOn = 0; } } @@ -164,20 +207,20 @@ void menuSidebandToggle(int btn){ void menuSetup(int btn){ if (!btn){ if (!modeCalibrate) - printLine2("Setup On?"); + printLineF2(F("Setup On?")); else - printLine2("Setup Off?"); + printLineF2(F("Setup Off?")); }else { if (!modeCalibrate){ modeCalibrate = true; - printLine2("Setup:On "); + printLineF2(F("Setup:On")); } else { modeCalibrate = false; - printLine2("Setup:Off "); + printLineF2(F("Setup:Off")); } - delay(2000); - printLine2(""); + delay_background(2000, 0); + printLine2Clear(); menuOn = 0; } } @@ -185,13 +228,10 @@ void menuSetup(int btn){ void menuExit(int btn){ if (!btn){ - printLine2("Exit Menu? "); + printLineF2(F("Exit Menu?")); } else{ - printLine2("Exiting menu"); - delay(300); - printLine2(""); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } } @@ -211,12 +251,12 @@ int menuCWSpeed(int btn){ return; } - printLine1("Press PTT to set"); + printLineF1(F("Press PTT to set")); strcpy(b, "WPM:"); itoa(wpm,c, 10); strcat(b, c); printLine2(b); - delay(300); + delay_background(300, 0); while(!btnDown() && digitalRead(PTT) == HIGH){ @@ -236,19 +276,139 @@ int menuCWSpeed(int btn){ if (btnDown()) //re-enable the clock1 and clock 2 break; + + Check_Cat(0); //To prevent disconnections } //save the setting if (digitalRead(PTT) == LOW){ - printLine2("CW Speed set!"); + printLineF2(F("CW Speed set!")); cwSpeed = 1200/wpm; EEPROM.put(CW_SPEED, cwSpeed); - delay(2000); + delay_background(2000, 0); } - printLine2(""); + printLine2ClearAndUpdate(); menuOn = 0; } +int menuCWAutoKey(int btn){ + if (!btn){ + printLineF2(F("CW AutoKey Mode?")); + return; + } + + //Check CW_AUTO_MAGIC_KEY and CW Text Count + EEPROM.get(CW_AUTO_COUNT, cwAutoTextCount); + if (EEPROM.read(CW_AUTO_MAGIC_KEY) != 0x73 || cwAutoTextCount < 1) + { + printLineF2(F("Empty CW data")); + delay_background(2000, 0); + return; + } + + printLineF1(F("Press PTT to Send")); + delay_background(500, 0); + updateDisplay(); + beforeCWTextIndex = 255; //255 value is for start check + isCWAutoMode = 1; + menuOn = 0; +} + +int menuSetupCwDelay(int btn){ + int knob = 0; + int tmpCWDelay = cwDelayTime * 10; + + if (!btn){ + strcpy(b, "CW TX->RX Delay"); + printLine2(b); + return; + } + + printLineF1(F("Press PTT to set")); + strcpy(b, "DELAY:"); + itoa(tmpCWDelay,c, 10); + strcat(b, c); + printLine2(b); + delay_background(300, 0); + + while(!btnDown() && digitalRead(PTT) == HIGH){ + knob = enc_read(); + if (knob != 0){ + if (tmpCWDelay > 3 && knob < 0) + tmpCWDelay -= 10; + if (tmpCWDelay < 2500 && knob > 0) + tmpCWDelay += 10; + + strcpy(b, "DELAY:"); + itoa(tmpCWDelay,c, 10); + strcat(b, c); + printLine2(b); + } + //abort if this button is down + if (btnDown()) + break; + + Check_Cat(0); //To prevent disconnections + } + + //save the setting + if (digitalRead(PTT) == LOW){ + printLineF2(F("CW Delay set!")); + cwDelayTime = tmpCWDelay / 10; + EEPROM.put(CW_DELAY, cwDelayTime); + delay_background(2000, 0); + } + printLine2ClearAndUpdate(); + menuOn = 0; +} + +int menuSetupTXCWInterval(int btn){ + int knob = 0; + int tmpTXCWInterval = delayBeforeCWStartTime * 2; + + if (!btn){ + strcpy(b, "CW Start Delay"); + printLine2(b); + return; + } + + printLineF1(F("Press PTT to set")); + strcpy(b, "Start Delay:"); + itoa(tmpTXCWInterval,c, 10); + strcat(b, c); + printLine2(b); + delay_background(300, 0); + + while(!btnDown() && digitalRead(PTT) == HIGH){ + knob = enc_read(); + if (knob != 0){ + if (tmpTXCWInterval > 0 && knob < 0) + tmpTXCWInterval -= 2; + if (tmpTXCWInterval < 500 && knob > 0) + tmpTXCWInterval += 2; + + strcpy(b, "Start Delay:"); + itoa(tmpTXCWInterval,c, 10); + strcat(b, c); + printLine2(b); + } + //abort if this button is down + if (btnDown()) + break; + + Check_Cat(0); //To prevent disconnections + } + + //save the setting + if (digitalRead(PTT) == LOW){ + printLineF2(F("CW Start set!")); + delayBeforeCWStartTime = tmpTXCWInterval / 2; + EEPROM.put(CW_START, delayBeforeCWStartTime); + delay_background(2000, 0); + } + printLine2ClearAndUpdate(); + menuOn = 0; +} /** @@ -276,7 +436,7 @@ int factoryCalibration(int btn){ delay(100); if (!btn){ - printLine2("Set Calibration?"); + printLineF2(F("Set Calibration?")); return 0; } @@ -287,7 +447,7 @@ int factoryCalibration(int btn){ //turn off the second local oscillator and the bfo si5351_set_calibration(calibration); - startTx(TX_CW); + startTx(TX_CW, 1); si5351bx_setfreq(2, 10000000l); strcpy(b, "#1 10 MHz cal:"); @@ -324,10 +484,10 @@ int factoryCalibration(int btn){ keyDown = 0; stopTx(); - printLine2("Calibration set!"); + printLineF2(F("Calibration set!")); EEPROM.put(MASTER_CAL, calibration); initOscillators(); - setFrequency(frequency); + setFrequency(frequency); updateDisplay(); while(btnDown()) @@ -340,13 +500,13 @@ int menuSetupCalibration(int btn){ int32_t prev_calibration; if (!btn){ - printLine2("Set Calibration?"); + printLineF2(F("Set Calibration?")); return 0; } - printLine1("Set to Zero-beat,"); - printLine2("press PTT to save"); - delay(1000); + printLineF1(F("Set to Zero-beat,")); + printLineF2(F("press PTT to save")); + delay_background(1000, 0); prev_calibration = calibration; calibration = 0; @@ -385,19 +545,18 @@ int menuSetupCalibration(int btn){ //save the setting if (digitalRead(PTT) == LOW){ - printLine1("Calibration set!"); - printLine2("Set Carrier now"); + printLineF1(F("Calibration set!")); + printLineF2(F("Set Carrier now")); EEPROM.put(MASTER_CAL, calibration); - delay(2000); + delay_background(2000, 0); } else calibration = prev_calibration; - printLine2(""); initOscillators(); //si5351_set_calibration(calibration); setFrequency(frequency); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } @@ -422,14 +581,14 @@ void menuSetupCarrier(int btn){ unsigned long prevCarrier; if (!btn){ - printLine2("Set the BFO"); + printLineF2(F("Set the BFO")); return; } prevCarrier = usbCarrier; - printLine1("Tune to best Signal"); - printLine2("PTT to confirm. "); - delay(1000); + printLineF1(F("Tune to best Signal")); + printLineF1(F("PTT to confirm. ")); + delay_background(1000, 0); usbCarrier = 11995000l; si5351bx_setfreq(0, usbCarrier); @@ -449,23 +608,23 @@ void menuSetupCarrier(int btn){ si5351bx_setfreq(0, usbCarrier); printCarrierFreq(usbCarrier); - + + Check_Cat(0); //To prevent disconnections delay(100); } //save the setting if (digitalRead(PTT) == LOW){ - printLine2("Carrier set! "); + printLineF2(F("Carrier set!")); EEPROM.put(USB_CAL, usbCarrier); - delay(1000); + delay_background(1000, 0); } else usbCarrier = prevCarrier; si5351bx_setfreq(0, usbCarrier); setFrequency(frequency); - updateDisplay(); - printLine2(""); + printLine2ClearAndUpdate(); menuOn = 0; } @@ -474,18 +633,18 @@ void menuSetupCwTone(int btn){ int prev_sideTone; if (!btn){ - printLine2("Change CW Tone"); + printLineF2(F("Change CW Tone")); return; } prev_sideTone = sideTone; - printLine1("Tune CW tone"); - printLine2("PTT to confirm. "); - delay(1000); + printLineF1(F("Tune CW tone")); + printLineF2(F("PTT to confirm.")); + delay_background(1000, 0); tone(CW_TONE, sideTone); //disable all clock 1 and clock 2 - while (digitalRead(PTT) == LOW || !btnDown()) + while (digitalRead(PTT) == HIGH && !btnDown()) { knob = enc_read(); @@ -501,30 +660,58 @@ void menuSetupCwTone(int btn){ printLine2(b); delay(100); + Check_Cat(0); //To prevent disconnections } noTone(CW_TONE); //save the setting if (digitalRead(PTT) == LOW){ - printLine2("Sidetone set! "); + printLineF2(F("Sidetone set!")); EEPROM.put(CW_SIDETONE, usbCarrier); - delay(2000); + delay_background(2000, 0); } else sideTone = prev_sideTone; - printLine2(""); - updateDisplay(); + printLine2ClearAndUpdate(); menuOn = 0; } +void setDialLock(byte tmpLock, byte fromMode) { + isDialLock = tmpLock; + + if (fromMode == 2 || fromMode == 3) return; + + if (isDialLock == 1) + printLineF2(F("Dial Lock ON")); + else + printLineF2(F("Dial Lock OFF")); + + delay_background(1000, 0); + printLine2ClearAndUpdate(); +} + +int btnDownTimeCount; + void doMenu(){ int select=0, i,btnState; - - //wait for the button to be raised up - while(btnDown()) - delay(50); - delay(50); //debounce + //for DialLock On/Off function + btnDownTimeCount = 0; + + //wait for the button to be raised up + while(btnDown()){ + delay(50); + Check_Cat(0); //To prevent disconnections + + //btnDownTimeCount++; + //check long time Down Button -> 3 Second + if (btnDownTimeCount++ > (2000 / 50)) { + setDialLock(isDialLock == 1 ? 0 : 1, 0); //Reverse Dialo lock + return; + } + } + delay(50); //debounce + menuOn = 2; while (menuOn){ @@ -532,9 +719,9 @@ void doMenu(){ btnState = btnDown(); if (i > 0){ - if (modeCalibrate && select + i < 110) + if (modeCalibrate && select + i < 150) select += i; - if (!modeCalibrate && select + i < 70) + if (!modeCalibrate && select + i < 80) select += i; } if (i < 0 && select - i >= 0) @@ -551,22 +738,34 @@ void doMenu(){ else if (select < 50) menuCWSpeed(btnState); else if (select < 60) + menuCWAutoKey(btnState); + else if (select < 70) menuSetup(btnState); - else if (select < 70 && !modeCalibrate) + else if (select < 80 && !modeCalibrate) menuExit(btnState); - else if (select < 80 && modeCalibrate) - menuSetupCalibration(btnState); //crystal else if (select < 90 && modeCalibrate) - menuSetupCarrier(btnState); //lsb + menuSetupCalibration(btnState); //crystal else if (select < 100 && modeCalibrate) - menuSetupCwTone(btnState); + menuSetupCarrier(btnState); //lsb else if (select < 110 && modeCalibrate) - menuExit(btnState); + menuSetupCwTone(btnState); + else if (select < 120 && modeCalibrate) + menuSetupCwDelay(btnState); + else if (select < 130 && modeCalibrate) + menuSetupTXCWInterval(btnState); + else if (select < 140 && modeCalibrate) + menuTxOnOff(btnState); + else if (select < 150 && modeCalibrate) + menuExit(btnState); + + Check_Cat(0); //To prevent disconnections } //debounce the button - while(btnDown()) + while(btnDown()){ delay(50); + Check_Cat(0); //To prevent disconnections + } delay(50); } diff --git a/ubitx_20/ubitx_ui.ino b/ubitx_20/ubitx_ui.ino index dbf513b..20fc69e 100644 --- a/ubitx_20/ubitx_ui.ino +++ b/ubitx_20/ubitx_ui.ino @@ -5,6 +5,8 @@ * of the radio. Occasionally, it is used to provide a two-line information that is * quickly cleared up. */ +//#define printLineF1(x) (printLineF(1, x)) +//#define printLineF2(x) (printLineF(0, x)) //returns true if the button is pressed int btnDown(){ @@ -23,9 +25,9 @@ int btnDown(){ * The current reading of the meter is assembled in the string called meter */ -char meter[17]; +//char meter[17]; -byte s_meter_bitmap[] = { +const PROGMEM uint8_t s_meter_bitmap[] = { B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011, B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011, B01000,B01000,B01000,B01000,B01100,B01100,B01100,B11011, @@ -33,18 +35,53 @@ byte s_meter_bitmap[] = { B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011, B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011 }; +PGM_P ps_meter_bitmap = reinterpret_cast(s_meter_bitmap); +const PROGMEM uint8_t lock_bitmap[8] = { + 0b01110, + 0b10001, + 0b10001, + 0b11111, + 0b11011, + 0b11011, + 0b11111, + 0b00000}; +PGM_P plock_bitmap = reinterpret_cast(lock_bitmap); // initializes the custom characters // we start from char 1 as char 0 terminates the string! void initMeter(){ - lcd.createChar(1, s_meter_bitmap); - lcd.createChar(2, s_meter_bitmap + 8); - lcd.createChar(3, s_meter_bitmap + 16); - lcd.createChar(4, s_meter_bitmap + 24); - lcd.createChar(5, s_meter_bitmap + 32); - lcd.createChar(6, s_meter_bitmap + 40); + uint8_t tmpbytes[8]; + byte i; + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(plock_bitmap + i); + lcd.createChar(0, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i); + lcd.createChar(1, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 8); + lcd.createChar(2, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 16); + lcd.createChar(3, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 24); + lcd.createChar(4, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 28); + lcd.createChar(5, tmpbytes); + + for (i = 0; i < 8; i++) + tmpbytes[i] = pgm_read_byte(ps_meter_bitmap + i + 32); + lcd.createChar(6, tmpbytes); } /** @@ -53,6 +90,8 @@ void initMeter(){ * characters 2 to 6 are used to draw the needle in positions 1 to within the block * This displays a meter from 0 to 100, -1 displays nothing */ + + /* void drawMeter(int8_t needle){ int16_t best, i, s; @@ -73,6 +112,7 @@ void drawMeter(int8_t needle){ meter[i-1] = 6; meter[i] = 0; } +*/ // The generic routine to display one line on the LCD void printLine(char linenmbr, char *c) { @@ -87,6 +127,38 @@ void printLine(char linenmbr, char *c) { } } +void printLineF(char linenmbr, const __FlashStringHelper *c) +{ + int i; + char tmpBuff[17]; + PGM_P p = reinterpret_cast(c); + + for (i = 0; i < 17; i++){ + unsigned char fChar = pgm_read_byte(p++); + tmpBuff[i] = fChar; + if (fChar == 0) + break; + } + + printLine(linenmbr, tmpBuff); +} + +#define LCD_MAX_COLUMN 16 +void printLineFromEEPRom(char linenmbr, char lcdColumn, byte eepromStartIndex, byte eepromEndIndex) { + lcd.setCursor(lcdColumn, linenmbr); + + for (byte i = eepromStartIndex; i <= eepromEndIndex; i++) + { + if (++lcdColumn <= LCD_MAX_COLUMN) + lcd.write(EEPROM.read(USER_CALLSIGN_DAT + i)); + else + break; + } + + for (byte i = lcdColumn; i < 16; i++) //Right Padding by Space + lcd.write(' '); +} + // short cut to print to the first line void printLine1(char *c){ printLine(1,c); @@ -96,21 +168,54 @@ void printLine2(char *c){ printLine(0,c); } +// short cut to print to the first line +void printLine1Clear(){ + printLine(1,""); +} +// short cut to print to the first line +void printLine2Clear(){ + printLine(0, ""); +} + +void printLine2ClearAndUpdate(){ + printLine(0, ""); + updateDisplay(); +} + +//012...89ABC...Z +char byteToChar(byte srcByte){ + if (srcByte < 10) + return 0x30 + srcByte; + else + return 'A' + srcByte - 10; +} + // this builds up the top line of the display with frequency and mode void updateDisplay() { // tks Jack Purdum W8TEE // replaced fsprint commmands by str commands for code size reduction - + + // replace code for Frequency numbering error (alignment, point...) by KD8CEC + int i; + unsigned long tmpFreq = frequency; // + memset(c, 0, sizeof(c)); - memset(b, 0, sizeof(b)); - - ultoa(frequency, b, DEC); if (inTx){ - if (cwTimeout > 0) - strcpy(c, " CW:"); - else - strcpy(c, " TX:"); + if (isCWAutoMode == 2) { + for (i = 0; i < 4; i++) + c[3-i] = (i < autoCWSendReservCount ? byteToChar(autoCWSendReserv[i]) : ' '); + + //display Sending Index + c[4] = byteToChar(sendingCWTextIndex); + c[5] = '='; + } + else { + if (cwTimeout > 0) + strcpy(c, " CW:"); + else + strcpy(c, " TX:"); + } } else { if (ritOn) @@ -127,29 +232,39 @@ void updateDisplay() { strcat(c, "B:"); } - - - //one mhz digit if less than 10 M, two digits if more - if (frequency < 10000000l){ - c[6] = ' '; - c[7] = b[0]; - strcat(c, "."); - strncat(c, &b[1], 3); - strcat(c, "."); - strncat(c, &b[4], 3); - } - else { - strncat(c, b, 2); - strcat(c, "."); - strncat(c, &b[2], 3); - strcat(c, "."); - strncat(c, &b[5], 3); + //display frequency + for (int i = 15; i >= 6; i--) { + if (tmpFreq > 0) { + if (i == 12 || i == 8) c[i] = '.'; + else { + c[i] = tmpFreq % 10 + 0x30; + tmpFreq /= 10; + } + } + else + c[i] = ' '; } - if (inTx) - strcat(c, " TX"); + //remarked by KD8CEC + //already RX/TX status display, and over index (16 x 2 LCD) + //if (inTx) + // strcat(c, " TX"); printLine(1, c); + if (isDialLock == 1) { + lcd.setCursor(5,1); + lcd.write((uint8_t)0); + } + else if (isCWAutoMode == 2){ + lcd.setCursor(5,1); + lcd.write(0x7E); + } + else + { + lcd.setCursor(5,1); + lcd.write(":"); + } + /* //now, the second line memset(c, 0, sizeof(c));