diff --git a/examples/AFSK_SerialMessenger/AFSK_SerialMessenger.ino b/examples/AFSK_SerialMessenger/AFSK_SerialMessenger.ino index 2a8015d..174f990 100644 --- a/examples/AFSK_SerialMessenger/AFSK_SerialMessenger.ino +++ b/examples/AFSK_SerialMessenger/AFSK_SerialMessenger.ino @@ -52,7 +52,7 @@ void setup() { Serial.begin(9600); radio.initialize(); - radio.frequency(145570); + radio.frequency(144390); // default aprs frequency in North America radio.setRfPower(0); radio.setVolume1(0xFF); radio.setVolume2(0xFF); diff --git a/examples/AppSerialController/AppSerialController.ino b/examples/AppSerialController/AppSerialController.ino index b20a511..657b2be 100644 --- a/examples/AppSerialController/AppSerialController.ino +++ b/examples/AppSerialController/AppSerialController.ino @@ -36,7 +36,7 @@ void setup() { Serial.println("Setting radio to its defaults.."); radio.initialize(); radio.setRfPower(0); - radio.frequency(146520); + radio.frequency(432100); // 70cm calling frequency radio.setModeReceive(); } diff --git a/examples/CTCSS/CTCSS.ino b/examples/CTCSS/CTCSS.ino index 0bd03c3..859a1d1 100644 --- a/examples/CTCSS/CTCSS.ino +++ b/examples/CTCSS/CTCSS.ino @@ -75,13 +75,11 @@ void setup() { radio.initialize(); // initializes automatically for UHF 12.5kHz channel Serial.println("setting default Radio configuration"); - radio.dangerMode(); // set frequency Serial.println("changing frequency"); - radio.setSQOff(); - freq = 446000; + freq = 432100; // 70cm calling frequency radio.frequency(freq); // set to receive @@ -92,6 +90,11 @@ void setup() { Serial.println(radio.readCtlReg()); Serial.println(radio.readRSSI()); + // set up squelch + radio.setSQLoThresh(-80); + radio.setSQHiThresh(-70); + radio.setSQOn(); + radio.setRfPower(0); // CTCSS Setup code diff --git a/examples/DTMF/DTMF.ino b/examples/DTMF/DTMF.ino index c408ae3..2c40943 100644 --- a/examples/DTMF/DTMF.ino +++ b/examples/DTMF/DTMF.ino @@ -72,7 +72,7 @@ void setup() { //radio.setSQOff(); Serial.println("setting frequency to: "); - freq = 432250; + freq = 432100; // 70cm calling frequency radio.frequency(freq); Serial.print(radio.getFrequency()); Serial.println("kHz"); @@ -104,38 +104,18 @@ void setup() { Serial.println("ready"); } -char rx_dtmf_buf[255]; -int rx_dtmf_idx = 0; void loop() { // look for tone - if (radio.getDTMFSample() != 0) { - uint16_t code = radio.getDTMFCode(); - - rx_dtmf_buf[rx_dtmf_idx++] = code2char(code); - - // reset after this tone - int j = 0; - while (j < 4) { - if (radio.getDTMFSample() == 0) { - j++; - } else { - j = 1; - } - delay(10); - } - // reset read - //radio.enableDTMFReceive(); - } else if (rx_dtmf_idx > 0) { - rx_dtmf_buf[rx_dtmf_idx] = '\0'; // NULL terminate the string - Serial.println(rx_dtmf_buf); - rx_dtmf_idx = 0; + char m = radio.DTMFRxLoop(); + if (m != 0) { + Serial.print(m); } // Is it time to send tone? if (Serial.available()) { // get first code - uint8_t code = char2code(Serial.read()); + uint8_t code = radio.DTMFchar2code(Serial.read()); // start transmitting radio.setDTMFCode(code); // set first @@ -151,7 +131,7 @@ void loop() { delay(10); } if (Serial.available()) { - code = char2code(Serial.read()); + code = radio.DTMFchar2code(Serial.read()); if (code == 255) code = 0xE; // throw a * in there so we don't break things with an invalid code radio.setDTMFCode(code); // set first } else { @@ -170,38 +150,3 @@ void loop() { radio.setTxSourceMic(); } } - -uint8_t char2code(char c) { - uint8_t code; - if (c == '#') { - code = 0xF; - } else if (c=='*') { - code = 0xE; - } else if (c >= 'A' && c <= 'D') { - code = c - 'A' + 0xA; - } else if (c >= '0' && c <= '9') { - code = c - '0'; - } else { - // invalid code, skip it - code = 255; - } - - return code; -} - -char code2char(uint16_t code) { - char c; - if (code < 10) { - c = '0' + code; - } else if (code < 0xE) { - c = 'A' + code - 10; - } else if (code == 0xE) { - c = '*'; - } else if (code == 0xF) { - c = '#'; - } else { - c = '?'; // invalid code - } - - return c; -} \ No newline at end of file diff --git a/examples/FMBeacon/FMBeacon.ino b/examples/FMBeacon/FMBeacon.ino index 8f125fe..9f70826 100644 --- a/examples/FMBeacon/FMBeacon.ino +++ b/examples/FMBeacon/FMBeacon.ino @@ -57,8 +57,8 @@ void setup() { radio.setMorseFreq(600); radio.setMorseDotMillis(100); - // Configure the HamShield to operate on 438.000MHz - radio.frequency(438000); + // Configure the HamShield + radio.frequency(432300); // 70cm beacon frequency Serial.println("Radio Configured."); } diff --git a/examples/FoxHunt/FoxHunt.ino b/examples/FoxHunt/FoxHunt.ino index 51e2e8c..be2c938 100755 --- a/examples/FoxHunt/FoxHunt.ino +++ b/examples/FoxHunt/FoxHunt.ino @@ -61,8 +61,8 @@ void setup() { radio.setMorseFreq(600); radio.setMorseDotMillis(100); - // Configure the HamShield to operate on 438.000Mhz - radio.frequency(438000); + // Configure the HamShield frequency + radio.frequency(432400); Serial.println("Radio configured."); } diff --git a/examples/HandyTalkie/HandyTalkie.ino b/examples/HandyTalkie/HandyTalkie.ino index 99bb1a3..4a2aa62 100644 --- a/examples/HandyTalkie/HandyTalkie.ino +++ b/examples/HandyTalkie/HandyTalkie.ino @@ -72,13 +72,12 @@ void setup() { radio.initialize(); // initializes automatically for UHF 12.5kHz channel Serial.println("setting default Radio configuration"); - radio.dangerMode(); // set frequency Serial.println("changing frequency"); radio.setSQOff(); - freq = 446000; + freq = 432100; // 70cm calling frequency radio.frequency(freq); // set to receive diff --git a/examples/HandyTalkie_nRF52840/HandyTalkie_nRF52840.ino b/examples/HandyTalkie_nRF52840/HandyTalkie_nRF52840.ino index 19a63fb..da2d065 100644 --- a/examples/HandyTalkie_nRF52840/HandyTalkie_nRF52840.ino +++ b/examples/HandyTalkie_nRF52840/HandyTalkie_nRF52840.ino @@ -126,7 +126,7 @@ void setup() { Serial.println("changing frequency"); radio.setSQOff(); - freq = 446000; + freq = 432100; // 70cm calling frequency radio.frequency(freq); // set to receive diff --git a/examples/Morse/Morse.ino b/examples/Morse/Morse.ino index f2de511..b1150b3 100644 --- a/examples/Morse/Morse.ino +++ b/examples/Morse/Morse.ino @@ -36,28 +36,9 @@ // Note that all timing is defined in terms of MORSE_DOT relative durations // You may want to tweak those timings below -#define SYMBOL_END_TIME 5 //millis -#define CHAR_END_TIME (MORSE_DOT*2.7) -#define MESSAGE_END_TIME (MORSE_DOT*8) - -#define MIN_DOT_TIME (MORSE_DOT-30) -#define MAX_DOT_TIME (MORSE_DOT+55) -#define MIN_DASH_TIME (MORSE_DOT*3-30) -#define MAX_DASH_TIME (MORSE_DOT*3+55) - HamShield radio; -uint32_t last_tone_check; // track how often we check for morse tones -uint32_t tone_in_progress; // track how long the current tone lasts -uint32_t space_in_progress; // track how long since the last tone -uint8_t rx_morse_char; -uint8_t rx_morse_bit; -bool bits_to_process; - -char rx_msg[128]; -uint8_t rx_idx; - // Run our start up things here void setup() { // NOTE: if not using PWM out, it should be held low to avoid tx noise @@ -93,68 +74,19 @@ void setup() { radio.setMorseDotMillis(MORSE_DOT); radio.lookForTone(MORSE_FREQ); - - // Configure the HamShield to operate on 438.000MHz - radio.frequency(432000); + radio.setupMorseRx(); + + // Configure the HamShield frequency + radio.frequency(432100); // 70cm calling frequency radio.setModeReceive(); Serial.println("Radio Configured."); - last_tone_check = millis(); - space_in_progress = 0; // haven't checked yet - tone_in_progress = 0; // not currently listening to a tone - rx_morse_char = 0; // haven't found any tones yet - rx_idx = 0; - rx_morse_bit = 1; - bits_to_process = false; - } void loop() { - // are we receiving anything - if (radio.toneDetected()) { - space_in_progress = 0; - if (tone_in_progress == 0) { - // start a new tone - tone_in_progress = millis(); - //Serial.print('t'); - } - } else { - // keep track of how long the silence is - if (space_in_progress == 0) space_in_progress = millis(); - - // we wait for a bit of silence before ending the last - // symbol in order to smooth out the detector - if ((millis() - space_in_progress) > SYMBOL_END_TIME) - { - if (tone_in_progress != 0) { - // end the last tone - uint16_t tone_time = millis() - tone_in_progress; - tone_in_progress = 0; - //Serial.println(tone_time); - handleTone(tone_time); - } - } - - // we might be done with a character if the space is long enough - if (((millis() - space_in_progress) > CHAR_END_TIME) && bits_to_process) { - char m = parseMorse(); - bits_to_process = false; - if (m != 0) { - rx_msg[rx_idx++] = m; - } - } - - // we might be done with a message if the space is long enough - if ((millis() - space_in_progress) > MESSAGE_END_TIME) { - if (rx_idx > 0) { - // we got a message, print it now - rx_msg[rx_idx] = '\0'; // null terminate - Serial.println(rx_msg); - rx_idx = 0; // reset message buffer - } - rx_morse_char = 0; - rx_morse_bit = 1; - } + char rx_char = radio.morseRxLoop(); + if (rx_char != 0) { + Serial.print(rx_char); } // should we send anything @@ -181,9 +113,9 @@ void loop() { radio.morseOut(morse_buf); // We're done sending the message, set the radio back into recieve mode. + Serial.println("sent"); radio.setModeReceive(); radio.lookForTone(MORSE_FREQ); - Serial.println("sent"); } else { // If we get here, the channel is busy. Let's also print out the RSSI. Serial.print("The channel was busy. RSSI: "); @@ -192,34 +124,4 @@ void loop() { } } -void handleTone(uint16_t tone_time) { - //Serial.println(tone_time); - if (tone_time > MIN_DOT_TIME && tone_time < MAX_DOT_TIME) { - // add a dot - //Serial.print("."); - bits_to_process = true; - //nothing to do for this bit position, since . = 0 - } else if (tone_time > MIN_DASH_TIME && tone_time < MAX_DASH_TIME) { - // add a dash - //Serial.print("-"); - bits_to_process = true; - rx_morse_char += rx_morse_bit; - } - // prep for the next bit - rx_morse_bit = rx_morse_bit << 1; -} - -char parseMorse() { - // if morse_char is a valid morse character, return the character - // if morse_char is an invalid (incomplete) morse character, return 0 - - - //if (rx_morse_bit != 1) Serial.println(rx_morse_char, BIN); - rx_morse_char += rx_morse_bit; // add the terminator bit - // if we got a char, then print it - char c = radio.morseReverseLookup(rx_morse_char); - rx_morse_char = 0; - rx_morse_bit = 1; - return c; -} \ No newline at end of file diff --git a/examples/SSTV_M1_Static/SSTV_M1_Static.ino b/examples/SSTV_M1_Static/SSTV_M1_Static.ino index 990674c..a266633 100644 --- a/examples/SSTV_M1_Static/SSTV_M1_Static.ino +++ b/examples/SSTV_M1_Static/SSTV_M1_Static.ino @@ -54,7 +54,7 @@ void setup() { // Tell the HamShield to start up radio.initialize(); radio.setRfPower(0); - radio.frequency(145500); + radio.frequency(446000); // put your setup code here, to run once: //dds.setReferenceClock(34965/4); dds.start(); diff --git a/examples/SerialTransceiver/SerialTransceiver.ino b/examples/SerialTransceiver/SerialTransceiver.ino index 2ce2601..69633cb 100644 --- a/examples/SerialTransceiver/SerialTransceiver.ino +++ b/examples/SerialTransceiver/SerialTransceiver.ino @@ -15,22 +15,32 @@ * R1; * [Just a space] + +// see also: https://github.com/EnhancedRadioDevices/HamShield/wiki/HamShield-Serial-Mode + Commands: -Mode ASCII Description Implemented --------------- ----------- -------------------------------------------------------------------------------------------------------------------------------------------- ----------------- -Transmit space Space must be received at least every 500 mS Yes -Receive not space If space is not received and/or 500 mS timeout of space occurs, unit will go into receive mode Yes -Bandwidth E; for 12.5KHz mode is 0, for 25KHz, mode is 1 No -Frequency F; Set the receive frequency in KHz, if offset is disabled, this is the transmit frequency No +Mode ASCII Description +-------------- ----------- -------------------------------------------------------------------------------------------------------------------------------------------- +Transmit space Space must be received at least every 500 mS +Receive not space If space is not received and/or 500 mS timeout of space occurs, unit will go into receive mode +Frequency F; Set the receive frequency in KHz, if offset is disabled, this is the transmit frequency Morse Out M; A small buffer for morse code (32 chars) -Power level P; Set the power amp level, 0 = lowest, 15 = highest No -Enable Offset R; 1 turns on repeater offset mode, 0 turns off repeater offset mode No -Squelch S; Set the squelch level No -TX Offset T; The absolute frequency of the repeater offset to transmit on in KHz No -RSSI ?; Respond with the current receive level in - dBm (no sign provided on numerical response) No -Voice Level ^; Respond with the current voice level (VSSI) - +Morse In N; Sets mode to Morse In, listening for Morse +Power level P; Set the power amp level, 0 = lowest, 15 = highest +Enable Offset R; 1 turns on repeater offset mode, 0 turns off repeater offset mode +Squelch S; Set the squelch level +TX Offset T; The absolute frequency of the repeater offset to transmit on in KHz +RSSI ? Respond with the current receive level in - dBm (no sign provided on numerical response) +Voice Level ^ Respond with the current voice level (VSSI), only valid when transmitting +DTMF Out D; A small buffer for DTMF out (only 0-9,A,B,C,D,*,# accepted) +DTMF In B; Sets mode to DTMF In, listening for DTMF +PL Tone Tx A; Sets PL tone for TX, value is tone frequency in Hz (float), set to 0 to disable +PL Tone Rx C; Sets PL tone for RX, value is tone frequency in Hz (float), set to 0 to disable +Volume 1 V1; Set volume 1 (value between 0 and 15) +Volume 2 V2; Set volume 2 (value between 0 and 15) +KISS TNC K; Move to KISS TNC mode (send ^; to move back to normal mode). NOT IMPELEMENTED YET +Normal Mode _ Move to Normal mode from any other mode (except TX) Responses: @@ -42,23 +52,44 @@ Error X; Indicates an error code. The numerical value is the type Value :; In response to a query Status #; Unsolicited status message Debug Msg @; 32 character debug message +Rx Msg R; up to 32 characters of received message, only if device is in DTMF or Morse Rx modes */ + +// Note that the following are not yet implemented +// TODO: change get_value so it's intuitive +// TODO: Squelch open and squelch shut independently controllable +// TODO: pre/de emph filter +// TODO: walkie-talkie +// TODO: KISS TNC + #include "HamShield.h" #define MIC_PIN 3 #define RESET_PIN A3 #define SWITCH_PIN 2 -int state; +enum {TX, NORMAL, DTMF, MORSE, KISS}; + +int state = NORMAL; +bool rx_ctcss = false; +bool muted = false; + int txcount = 0; -long timer = 0; -long freq = 144390; -long tx = 0; +long timer = 0; // Transmit timer to track timeout (send space to reset) + +long freq = 432100; // 70cm calling frequency, receive frequency and default transmit frequency +long tx_freq = 0; // transmit frequency if repeater is on +int pwr = 0; // tx power + char cmdbuff[32] = ""; int temp = 0; -int repeater = 0; + +bool repeater = false; // true if transmit and receive operate on different frequencies +char pl_rx_buffer[32]; // pl tone rx buffer +char pl_tx_buffer[32]; // pl tone tx buffer + float ctcssin = 0; float ctcssout = 0; int cdcssin = 0; @@ -67,10 +98,8 @@ int cdcssout = 0; HamShield radio; - - void setup() { - // NOTE: if not using PWM out, it should be held low to avoid tx noise + // NOTE: if not using PWM out (MIC pin), it should be held low to avoid tx noise pinMode(MIC_PIN, OUTPUT); digitalWrite(MIC_PIN, LOW); @@ -80,7 +109,6 @@ void setup() { // set up the reset control pin pinMode(RESET_PIN, OUTPUT); digitalWrite(RESET_PIN, HIGH); - delay(5); // wait for device to come up Serial.begin(9600); Serial.println(";;;;;;;;;;;;;;;;;;;;;;;;;;"); @@ -90,137 +118,444 @@ void setup() { Serial.print(result,DEC); Serial.println(";"); radio.initialize(); // initializes automatically for UHF 12.5kHz channel - Serial.println("*START;"); radio.frequency(freq); radio.setVolume1(0xF); radio.setVolume2(0xF); radio.setModeReceive(); radio.setTxSourceMic(); - radio.setRfPower(0); - radio.setSQLoThresh(80); + radio.setRfPower(pwr); + radio.setSQLoThresh(-80); + radio.setSQHiThresh(-70); radio.setSQOn(); + Serial.println("*START;"); } void loop() { if(Serial.available()) { - int text = Serial.read(); + int text = Serial.read(); // get the first char to see what the upcoming command is - switch (state) { - - case 10: - if(text == 32) { timer = millis();} + switch (state) { + // we handle commands differently based on what state we're in + + case TX: + // we're currently transmitting + // if we got a space, reset our transmit timeout + if(text == ' ') { timer = millis();} break; - case 0: - switch(text) { + case NORMAL: + switch(text) { + case ' ': // space - transmit + if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); } + muted = false; // can't mute (for PL tones) during tx + radio.setUnmute(); + radio.setModeTransmit(); + state = TX; + Serial.println("#TX,ON;"); + timer = millis(); + break; - case 32: // space - transmit - if(repeater == 1) { radio.frequency(tx); } - radio.setModeTransmit(); - state = 10; - Serial.println("#TX,ON;"); - timer = millis(); - break; - - case 63: // ? - RSSI - Serial.print(":"); - Serial.print(radio.readRSSI(),DEC); - Serial.println(";"); - break; - - case 65: // A - CTCSS In - getValue(); - ctcssin = atof(cmdbuff); - radio.setCtcss(ctcssin); - break; - - case 66: // B - CTCSS Out - break; - - case 67: // C - CTCSS Enable - break; + case '?': // ? - RSSI + Serial.print(":"); + Serial.print(radio.readRSSI(),DEC); + Serial.println(";"); + break; - case 68: // D - CDCSS Enable - break; + case '^': // ^ - VSSI (voice) level + Serial.print(":"); + Serial.print(radio.readVSSI(),DEC); + Serial.println(";"); + break; - case 70: // F - frequency - getValue(); - freq = atol(cmdbuff); - if(radio.frequency(freq) == true) { Serial.print("@"); Serial.print(freq,DEC); Serial.println(";!;"); } else { Serial.println("X1;"); } - break; + case 'F': // F - frequency + getValue(); + freq = atol(cmdbuff); + if(radio.frequency(freq) == true) { + Serial.print("@"); + Serial.print(freq,DEC); + Serial.println(";!;"); + } else { + Serial.println("X1;"); + } + break; + + case 'P': // P - power level + getValue(); + temp = atol(cmdbuff); + radio.setRfPower(temp); + Serial.println("!;"); + break; + + case 'S': // S - squelch + getValue(); + temp = atol(cmdbuff); + if (temp < -2 && temp > -130) { + radio.setSQLoThresh(temp); + radio.setSQHiThresh(temp+2); + radio.setSQOn(); + Serial.print(temp); + Serial.println("!;"); + } else { + Serial.println("X!;"); + } + break; + + case 'R': // R - repeater offset mode + getValue(); + temp = atol(cmdbuff); + if(temp == 0) { repeater = 0; } + if(temp == 1) { repeater = 1; } + Serial.println("!;"); + break; + + case 'T': // T - transmit offset + getValue(); + tx_freq = atol(cmdbuff); + Serial.println("!;"); + break; + + case 'M': // M - Morse + getValue(); + if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); } + muted = false; // can't mute (for PL tones) during tx + radio.setUnmute(); + radio.setModeTransmit(); + delay(300); + radio.morseOut(cmdbuff); + if(repeater == true) { radio.frequency(freq); } + radio.setModeReceive(); + Serial.println("!;"); + break; + + case 'N': // N - set to Morse in Mode + morse_rx_setup(); + state = MORSE; + Serial.println("!;"); + break; + + case 'D': // D - DTMF Out + dtmfSetup(); + getValue(); + dtmf_out(cmdbuff); + Serial.println("!;"); + break; - case 'M': - getValue(); - radio.setModeTransmit(); - delay(300); - radio.morseOut(cmdbuff); - state = 10; - break; - - case 80: // P - power level - getValue(); - temp = atol(cmdbuff); - radio.setRfPower(temp); - break; - - case 82: // R - repeater offset mode - getValue(); - temp = atol(cmdbuff); - if(temp == 0) { repeater = 0; } - if(temp == 1) { repeater = 1; } - break; - - case 83: // S - squelch - getValue(); - temp = atol(cmdbuff); - radio.setSQLoThresh(temp); - break; - - case 84: // T - transmit offset - getValue(); - tx = atol(cmdbuff); - break; - - - case 94: // ^ - VSSI (voice) level - Serial.print(":"); - Serial.print(radio.readVSSI(),DEC); - Serial.println(";"); + case 'B': // B - set to DTMF in Mode + dtmfSetup(); + radio.enableDTMFReceive(); + state = DTMF; + Serial.println("!;"); + break; + + case 'A': // A - TX PL Tone configuration command + pl_tone_tx(); + Serial.println("!;"); + break; + + case 'C': // C - RX PL Tone configuration command + pl_tone_rx(); + Serial.println("!;"); + break; + + case 'V': // V - set volume + getValue(); + temp = cmdbuff[0]; + if (temp == 0x31) { + temp = atol(cmdbuff + 1); + radio.setVolume1(temp); + Serial.println("!;"); + } else if (temp == 0x32) { + temp = atol(cmdbuff + 1); + radio.setVolume2(temp); + Serial.println("!;"); + } else { + // not a valid volume command + while (Serial.available()) { Serial.read(); } + Serial.println("X!;"); + } + break; + + case 'K': // K - switch to KISS TNC mode + //state = KISS; + //TODO: set up KISS + Serial.println("X1;"); + break; + + default: + // unknown command, flush the input buffer and wait for next one + Serial.println("X1;"); + while (Serial.available()) { Serial.read(); } + break; } - break; + break; + + case KISS: + if (Serial.peek() == '_') { + state = NORMAL; + if (rx_ctcss) { + radio.enableCtcss(); + muted = true; // can't mute (for PL tones) during tx + radio.setMute(); + } + } + // TODO: handle KISS TNC + break; + + case MORSE: + if (text == '_') { state = NORMAL; } + if (text == 'M') { // tx message + getValue(); + if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); } + muted = false; // can't mute (for PL tones) during tx + radio.setUnmute(); + radio.setModeTransmit(); + delay(300); + radio.morseOut(cmdbuff); + if(repeater == true) { radio.frequency(freq); } + radio.setModeReceive(); + } else { + // not a valid cmd + while (Serial.available()) { Serial.read(); } + } + break; + + case DTMF: + if (text == '_') { state = NORMAL; } + if (text == 'D') { // tx message + getValue(); + dtmf_out(cmdbuff); + } else { + // not a valid cmd + while (Serial.available()) { Serial.read(); } + } + break; + + default: + // we're in an invalid state, reset to safe settings + while (Serial.available()) { Serial.read(); } + radio.frequency(freq); + radio.setModeReceive(); + state = NORMAL; + break; } } - if(state == 10) { - if(millis() > (timer + 500)) { Serial.println("#TX,OFF;");radio.setModeReceive(); if(repeater == 1) { radio.frequency(freq); } state = 0; txcount = 0; } + + // now handle any state related functions + switch (state) { + case TX: + if(millis() > (timer + 500)) { + Serial.println("#TX,OFF;"); + radio.setModeReceive(); + if(repeater == true) { radio.frequency(freq); } + if (rx_ctcss) { + radio.setMute(); + muted = true; + } + txcount = 0; + state = NORMAL; + } + break; + + case NORMAL: + // deal with rx ctccs if necessary + if (rx_ctcss) { + if (radio.getCtcssToneDetected()) { + if (muted) { + muted = false; + radio.setUnmute(); + } + } else { + if (!muted) { + muted = true; + radio.setMute(); + } + } + } + break; + + case DTMF: + dtmf_rx(); // wait for DTMF reception + break; + + case MORSE: + morse_rx(); // wait for Morse reception + break; + } + + // get rid of any trailing whitespace in the serial buffer + if (Serial.available()) { + char cpeek = Serial.peek(); + while (cpeek == ' ' || cpeek == '\r' || cpeek == '\n') + { + Serial.read(); + cpeek = Serial.peek(); } + + } } void getValue() { int p = 0; char temp; for(;;) { - if(Serial.available()) { - temp = Serial.read(); - if(temp == 59) { cmdbuff[p] = 0; Serial.print("@"); - for(int x = 0; x < 32; x++) { Serial.print(cmdbuff[x]);} - Serial.println(); - return; - } - cmdbuff[p] = temp; - p++; - if(p == 32) { - Serial.print("@"); - for(int x = 0; x < 32; x++) { - Serial.println(cmdbuff[x]); - } - - cmdbuff[0] = 0; - - Serial.println("X0;"); return; } // some sort of alignment issue? lets not feed junk into whatever takes this string in - } + if(Serial.available()) { + temp = Serial.read(); + if(temp == 59) { + cmdbuff[p] = 0; + return; + } + cmdbuff[p] = temp; + p++; + if(p == 32) { + cmdbuff[0] = 0; + return; + } + } } } +void dtmfSetup() { + radio.setVolume1(6); + radio.setVolume2(0); + radio.setDTMFDetectTime(24); // time to detect a DTMF code, units are 2.5ms + radio.setDTMFIdleTime(50); // time between transmitted DTMF codes, units are 2.5ms + radio.setDTMFTxTime(60); // duration of transmitted DTMF codes, units are 2.5ms +} + +void dtmf_out(char * out_buf) { + if (out_buf[0] == ';' || out_buf[0] == 0) return; // empty message + + uint8_t i = 0; + uint8_t code = radio.DTMFchar2code(out_buf[i]); + + // start transmitting + radio.setDTMFCode(code); // set first + radio.setTxSourceTones(); + if(repeater == true && tx_freq != 0) { radio.frequency(tx_freq); } + muted = false; // can't mute during transmit + radio.setUnmute(); + radio.setModeTransmit(); + delay(300); // wait for TX to come to full power + + bool dtmf_to_tx = true; + while (dtmf_to_tx) { + // wait until ready + while (radio.getDTMFTxActive() != 1) { + // wait until we're ready for a new code + delay(10); + } + if (i < 32 && out_buf[i] != ';' && out_buf[i] != 0) { + code = radio.DTMFchar2code(out_buf[i]); + if (code == 255) code = 0xE; // throw a * in there so we don't break things with an invalid code + radio.setDTMFCode(code); // set first + } else { + dtmf_to_tx = false; + break; + } + i++; + + while (radio.getDTMFTxActive() != 0) { + // wait until this code is done + delay(10); + } + } + // done with tone + radio.setModeReceive(); + if (repeater == true) {radio.frequency(freq);} + radio.setTxSourceMic(); +} + +void dtmf_rx() { + char m = radio.DTMFRxLoop(); + if (m != 0) { + // Note: not doing buffering of messages, + // we just send a single morse character + // whenever we get it + Serial.print('R'); + Serial.print(m); + Serial.println(';'); + } +} + +// TODO: morse config info + +void morse_rx_setup() { + // Set the morse code characteristics + radio.setMorseFreq(MORSE_FREQ); + radio.setMorseDotMillis(MORSE_DOT); + + radio.lookForTone(MORSE_FREQ); + + radio.setupMorseRx(); +} + +void morse_rx() { + char m = radio.morseRxLoop(); + + if (m != 0) { + // Note: not doing buffering of messages, + // we just send a single morse character + // whenever we get it + Serial.print('R'); + Serial.print(m); + Serial.println(';'); + } +} + +void pl_tone_tx() { + memset(pl_tx_buffer,0,32); + uint8_t ptr = 0; + while(1) { + if(Serial.available()) { + uint8_t buf = Serial.read(); + if(buf == 'X') { return; } + if(buf == ';') { pl_tx_buffer[ptr] = 0; program_pl_tx(); return; } + if(ptr == 31) { return; } + pl_tx_buffer[ptr] = buf; ptr++; + } + } +} + +void program_pl_tx() { + float pl_tx = atof(pl_tx_buffer); + radio.setCtcss(pl_tx); + + if (pl_tx == 0) { + radio.disableCtcssTx(); + } else { + radio.enableCtcssTx(); + } +} + +void pl_tone_rx() { + memset(pl_rx_buffer,0,32); + uint8_t ptr = 0; + while(1) { + if(Serial.available()) { + uint8_t buf = Serial.read(); + if(buf == 'X') { return; } + if(buf == ';') { pl_rx_buffer[ptr] = 0; program_pl_rx(); return; } + if(ptr == 31) { return; } + pl_rx_buffer[ptr] = buf; ptr++; + } + } +} + +void program_pl_rx() { + float pl_rx = atof(pl_rx_buffer); + radio.setCtcss(pl_rx); + if (pl_rx == 0) { + rx_ctcss = false; + radio.setUnmute(); + muted = false; + radio.disableCtcssRx(); + } else { + rx_ctcss = true; + radio.setMute(); + muted = true; + radio.enableCtcssRx(); + } +} diff --git a/examples/SignalTest/SignalTest.ino b/examples/SignalTest/SignalTest.ino index ed6d440..f4ca2ed 100755 --- a/examples/SignalTest/SignalTest.ino +++ b/examples/SignalTest/SignalTest.ino @@ -106,7 +106,7 @@ void setup() { int result = radio.testConnection(); Serial.println(result); radio.initialize(); - radio.frequency(446000); + radio.frequency(432400); radio.setVolume1(0xF); radio.setVolume2(0xF); radio.setModeReceive(); diff --git a/examples/SpeechTX/SpeechTX.ino b/examples/SpeechTX/SpeechTX.ino index 9067883..ff1b040 100644 --- a/examples/SpeechTX/SpeechTX.ino +++ b/examples/SpeechTX/SpeechTX.ino @@ -249,7 +249,7 @@ void setup() { Serial.println("Setting radio to its defaults.."); radio.initialize(); radio.setRfPower(0); - radio.frequency(144025); + radio.frequency(145010); radio.setModeTransmit(); voice.say(spKILO); // to change these to the words you would like to say, or a ham radio call sign - uncomment above encoded words diff --git a/library.properties b/library.properties index 26eb2c4..fc2feb6 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HamShield -version=1.1.2 +version=1.1.3 author=Morgan Redfield , Casey Halverson maintainer=Morgan Redfield sentence=A library for use with HamShield by Enhanced Radio Devices. diff --git a/src/HamShield.cpp b/src/HamShield.cpp index 2cc1c22..fa98090 100644 --- a/src/HamShield.cpp +++ b/src/HamShield.cpp @@ -193,7 +193,7 @@ void HamShield::initialize(bool narrowBand) { HSwriteWord(devAddr, 0x57, tx_data); tx_data = 0x800D; HSwriteWord(devAddr, 0x58, tx_data); - tx_data = 0x0EDD; + tx_data = 0x0EDB; HSwriteWord(devAddr, 0x5A, tx_data); // sq and noise detect times tx_data = 0x3FFF; HSwriteWord(devAddr, 0x63, tx_data); // pre-emphasis bypass @@ -227,7 +227,7 @@ void HamShield::initialize(bool narrowBand) { setModeReceive(); setTxSourceMic(); setRfPower(0); - setSQLoThresh(80); + setSQLoThresh(-80); setSQOn(); */ setDTMFIdleTime(50); @@ -256,11 +256,11 @@ void HamShield::setupNarrowBand() { HSwriteWord(devAddr, 0x34, tx_data); tx_data = 0x40C3; HSwriteWord(devAddr, 0x3A, tx_data); // modu_det_sel sq setting - tx_data = 0x0407; + tx_data = 0x0F1E; HSwriteWord(devAddr, 0x3C, tx_data); // pk_det_th sq setting [8:7] tx_data = 0x28D0; HSwriteWord(devAddr, 0x3F, tx_data); // rssi3_th sq setting - tx_data = 0x203E; + tx_data = 0x20BE; HSwriteWord(devAddr, 0x48, tx_data); tx_data = 0x1BB7; HSwriteWord(devAddr, 0x60, tx_data); @@ -773,20 +773,37 @@ void HamShield::setCtcssFreqToStandard(){ setCtcssFreq(13440); } -void HamShield::enableCtcss() { - // enable TX - HSwriteBitsW(devAddr, A1846S_CTCSS_MODE_REG, 10, 2, 3); - - // enable RX - setCtcssGpioSel(1); +void HamShield::enableCtcssTx() { + HSwriteBitsW(devAddr, A1846S_CTCSS_MODE_REG, 10, 2, 3); +} + +void HamShield::enableCtcssRx() { + setCtcssGpioSel(1); HSwriteBitW(devAddr, A1846S_TX_VOICE_REG, A1846S_CTCSS_DET_BIT, 0); HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_CTCSS_FILTER_BYPASS, 0); setDetCtcss(); } -void HamShield::disableCtcss() { - HSwriteBitsW(devAddr, A1846S_CTCSS_MODE_REG, 10, 2, 0); + +void HamShield::enableCtcss() { + // enable TX + enableCtcssTx(); + + // enable RX + enableCtcssRx(); +} + +void HamShield::disableCtcssTx() { + HSwriteBitsW(devAddr, A1846S_CTCSS_MODE_REG, 10, 2, 0); +} + +void HamShield::disableCtcssRx() { + setCtcssGpioSel(0); disableCtcssCdcss(); } +void HamShield::disableCtcss() { + disableCtcssTx(); + disableCtcssRx(); +} // match threshold void HamShield::setCtcssDetThreshIn(uint8_t thresh) { @@ -870,24 +887,30 @@ bool HamShield::getSQState(){ void HamShield::setSQHiThresh(int16_t sq_hi_threshold){ // Sq detect high th, rssi_cmp will be 1 when rssi>th_h_sq, unit 1dB uint16_t sq = 137 + sq_hi_threshold; - HSwriteWord(devAddr, A1846S_SQ_OPEN_THRESH_REG, sq); + HSwriteBitsW(devAddr, A1846S_SQ_OPEN_THRESH_REG, A1846S_SQ_OPEN_THRESH_BIT, A1846S_SQ_OPEN_THRESH_LENGTH, sq); } int16_t HamShield::getSQHiThresh(){ - HSreadWord(devAddr, A1846S_SQ_OPEN_THRESH_REG, radio_i2c_buf); - + HSreadBitsW(devAddr, A1846S_SQ_OPEN_THRESH_REG, A1846S_SQ_OPEN_THRESH_BIT, A1846S_SQ_OPEN_THRESH_LENGTH, radio_i2c_buf); + return radio_i2c_buf[0] - 137; } void HamShield::setSQLoThresh(int16_t sq_lo_threshold){ // Sq detect low th, rssi_cmp will be 0 when rssi= 'A' && c <= 'D') { + code = c - 'A' + 0xA; + } else if (c >= '0' && c <= '9') { + code = c - '0'; + } else { + // invalid code, skip it + code = 255; + } + + return code; +} + + void HamShield::setDTMFTxTime(uint16_t tx_time) { if (tx_time > 63) {tx_time = 63;} // maxed out // tx time is duration of DTMF Tone @@ -1616,6 +1700,10 @@ bool HamShield::waitForChannel(long timeout = 0, long breakwindow = 0, int setRS return false; } +void HamShield::setupMorseRx() { + // TODO: morse timing config (e.g. dot time, dash time, etc) +} + // Get current morse code tone frequency (in Hz) unsigned int HamShield::getMorseFreq() { @@ -1693,6 +1781,104 @@ void HamShield::morseOut(char buffer[HAMSHIELD_MORSE_BUFFER_SIZE]) { return; } +// returns '\0' if no valid morse char found yet +char HamShield::morseRxLoop() { + static uint32_t last_tone_check = 0; // track how often we check for morse tones + static uint32_t tone_in_progress; // track how long the current tone lasts + static uint32_t space_in_progress; // track how long since the last tone + static uint8_t rx_morse_char; + static uint8_t rx_morse_bit; + static bool bits_to_process; + + if (last_tone_check == 0) { + last_tone_check = millis(); + space_in_progress = 0; // haven't checked yet + tone_in_progress = 0; // not currently listening to a tone + rx_morse_char = 0; // haven't found any tones yet + rx_morse_bit = 1; + bits_to_process = false; + } + + char m = 0; + + // are we receiving anything + if (toneDetected()) { + space_in_progress = 0; + if (tone_in_progress == 0) { + // start a new tone + tone_in_progress = millis(); + //Serial.print('t'); + } + } else { + // keep track of how long the silence is + if (space_in_progress == 0) space_in_progress = millis(); + + // we wait for a bit of silence before ending the last + // symbol in order to smooth out the detector + if ((millis() - space_in_progress) > SYMBOL_END_TIME) + { + if (tone_in_progress != 0) { + // end the last tone + uint16_t tone_time = millis() - tone_in_progress; + tone_in_progress = 0; + //Serial.println(tone_time); + bits_to_process = handleMorseTone(tone_time, bits_to_process, &rx_morse_char, &rx_morse_bit); + } + } + + // we might be done with a character if the space is long enough + if (((millis() - space_in_progress) > CHAR_END_TIME) && bits_to_process) { + m = parseMorse(rx_morse_char, rx_morse_bit); + bits_to_process = false; + rx_morse_char = 0; + rx_morse_bit = 1; + } + + // we might be done with a message if the space is long enough + if ((millis() - space_in_progress) > MESSAGE_END_TIME) { + rx_morse_char = 0; + rx_morse_bit = 1; + } + } + + return m; +} + +bool HamShield::handleMorseTone(uint16_t tone_time, bool bits_to_process, + uint8_t * rx_morse_char, uint8_t * rx_morse_bit) { + //Serial.println(tone_time); + if (tone_time > MIN_DOT_TIME && tone_time < MAX_DOT_TIME) { + // add a dot + //Serial.print("."); + bits_to_process = true; + //nothing to do for this bit position, since . = 0 + } else if (tone_time > MIN_DASH_TIME && tone_time < MAX_DASH_TIME) { + // add a dash + //Serial.print("-"); + bits_to_process = true; + *rx_morse_char += *rx_morse_bit; + } + + // prep for the next bit + *rx_morse_bit = *rx_morse_bit << 1; + + return bits_to_process; +} + +char HamShield::parseMorse(uint8_t rx_morse_char, uint8_t rx_morse_bit) { + // if morse_char is a valid morse character, return the character + // if morse_char is an invalid (incomplete) morse character, return 0 + + + //if (rx_morse_bit != 1) Serial.println(rx_morse_char, BIN); + rx_morse_char += rx_morse_bit; // add the terminator bit + // if we got a char, then print it + char c = morseReverseLookup(rx_morse_char); + + return c; +} + + /* Morse code lookup table */ uint8_t HamShield::morseLookup(char letter) { diff --git a/src/HamShield.h b/src/HamShield.h index e089bbe..9d7900f 100644 --- a/src/HamShield.h +++ b/src/HamShield.h @@ -32,7 +32,7 @@ #define A1846S_TH_L_VOX_REG 0x64 // register holds vox low (shut) threshold bits #define A1846S_FM_DEV_REG 0x43 // register holds fm deviation settings #define A1846S_RX_VOLUME_REG 0x44 // register holds RX volume settings -#define A1846S_SQ_OPEN_THRESH_REG 0x48 // see sq +#define A1846S_SQ_OPEN_THRESH_REG 0x49 // see sq #define A1846S_SQ_SHUT_THRESH_REG 0x49 // see sq #define A1846S_CTCSS_FREQ_REG 0x4A // ctcss_freq<15:0> #define A1846S_CDCSS_CODE_HI_REG 0x4B // cdcss_code<23:16> @@ -145,12 +145,12 @@ #define A1846S_SHIFT_SEL_LEN 2 // Bitfields for A1846S_SQ_THRESH_REG -#define A1846S_SQ_OPEN_THRESH_BIT 9 // sq open threshold <9:0> -#define A1846S_SQ_OPEN_THRESH_LENGTH 10 +#define A1846S_SQ_OPEN_THRESH_BIT 13 // sq open threshold <6:0> +#define A1846S_SQ_OPEN_THRESH_LENGTH 7 // Bitfields for A1846S_SQ_SHUT_THRESH_REG -#define A1846S_SQ_SHUT_THRESH_BIT 9 // sq shut threshold <9:0> -#define A1846S_SQ_SHUT_THRESH_LENGTH 10 +#define A1846S_SQ_SHUT_THRESH_BIT 6 // sq shut threshold <6:0> +#define A1846S_SQ_SHUT_THRESH_LENGTH 7 // Bitfields for A1846S_SQ_OUT_SEL_REG #define A1846S_SQ_OUT_SEL_BIT 7 // sq_out_sel @@ -168,8 +168,8 @@ #define A1846S_CTCSS2_FLAG_BIT 8 // 1 when txon is enabled #define A1846S_INVERT_DET_FLAG_BIT 7 // ctcss phase shift detect #define A1846S_CSS_CMP_FLAG_BIT 2 // ctcss/cdcss compared -#define A1846S_SQ_FLAG_BIT 1 // sq final signal out from dsp -#define A1846S_VOX_FLAG_BIT 0 // vox out from dsp +#define A1846S_SQ_FLAG_BIT 0 // sq final signal out from dsp +#define A1846S_VOX_FLAG_BIT 1 // vox out from dsp // Bitfields for A1846S_RSSI_REG #define A1846S_RSSI_BIT 15 // RSSI readings <7:0> @@ -217,6 +217,20 @@ #define HAMSHIELD_PSK31_FREQ 1000 +// Morse Configuration + +#define MORSE_FREQ 600 +#define MORSE_DOT 150 // ms + +#define SYMBOL_END_TIME 5 //millis +#define CHAR_END_TIME (MORSE_DOT*2.7) +#define MESSAGE_END_TIME (MORSE_DOT*8) + +#define MIN_DOT_TIME (MORSE_DOT-30) +#define MAX_DOT_TIME (MORSE_DOT+55) +#define MIN_DASH_TIME (MORSE_DOT*3-30) +#define MAX_DASH_TIME (MORSE_DOT*3+55) + class HamShield { public: @@ -299,7 +313,11 @@ class HamShield { uint16_t getCtcssFreqMilliHz(); float getCtcssFreqHz(); void setCtcssFreqToStandard(); // freq must be 134.4Hz for standard cdcss mode + void enableCtcssTx(); + void enableCtcssRx(); void enableCtcss(); + void disableCtcssTx(); + void disableCtcssRx(); void disableCtcss(); void setCtcssDetThreshIn(uint8_t thresh); uint8_t getCtcssDetThreshIn(); @@ -344,6 +362,7 @@ class HamShield { int16_t getSQHiThresh(); void setSQLoThresh(int16_t sq_lo_threshold); // Sq detect low th, rssi_cmp will be 0 when rssi