diff --git a/examples/Morse/Morse.ino b/examples/Morse/Morse.ino new file mode 100644 index 0000000..15cb251 --- /dev/null +++ b/examples/Morse/Morse.ino @@ -0,0 +1,192 @@ +/* Hamshield + * Example: Morse Code Transceiver + * + * Serail to Morse transceiver. Sends characters from the Serial + * port over the air, and vice versa. + * Connect the HamShield to your Arduino. Screw the antenna + * into the HamShield RF jack. Connect the Arduino to wall + * power and then to your computer via USB. After uploading + * this program to your Arduino, open the Serial Monitor to + * monitor the status of the beacon. To test, set a HandyTalkie + * to 438MHz. You should hear the message " CALLSIGN HAMSHIELD" + * in morse code. +*/ + +#define DDS_REFCLK_DEFAULT 9600 +#include + +#define PWM_PIN 3 +#define RESET_PIN A3 +#define SWITCH_PIN 2 + +#define MORSE_FREQ 600 +#define MORSE_DOT 100 // ms +// Note that all timing is defined in terms of MORSE_DOT relative durations +// You may want to tweak those timings in the receiver below + +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; + +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 + pinMode(PWM_PIN, OUTPUT); + digitalWrite(PWM_PIN, LOW); + + // prep the switch + pinMode(SWITCH_PIN, INPUT_PULLUP); + + // set up the reset control pin + pinMode(RESET_PIN, OUTPUT); + digitalWrite(RESET_PIN, HIGH); + delay(5); // wait for device to come up + + // Set up the serial port at 9600 Baud + Serial.begin(9600); + + // Send a quick serial string + Serial.println("HamShield Morse Example Sketch"); + + Serial.print("Radio status: "); + int result = radio.testConnection(); + Serial.println(result,DEC); + + // Tell the HamShield to start up + radio.initialize(); + + // Set the transmit power level (0-8) + radio.setRfPower(0); + + // Set the morse code characteristics + radio.setMorseFreq(MORSE_FREQ); + radio.setMorseDotMillis(MORSE_DOT); + + radio.lookForTone(MORSE_FREQ); + + // Configure the HamShield to operate on 438.000MHz + radio.frequency((uint32_t) 438000); + 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; +} + +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(); + } + } 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) > 5) //MORSE_DOT*0.05) + { + if (tone_in_progress != 0) { + // end the last tone + uint16_t tone_time = millis() - tone_in_progress; + tone_in_progress = 0; + handleTone(tone_time); + } + } + + // we might be done with a character if the space is long enough + if ((millis() - space_in_progress) > MORSE_DOT*2.3) { + char m = parseMorse(); + 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) > MORSE_DOT*15) { + 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; + } + } + + // should we send anything + if (Serial.available()) { + Serial.println("checking channel"); + // We'll wait up to 30 seconds for a clear channel, requiring that the channel is clear for 2 seconds before we transmit + if (radio.waitForChannel(30000,2000,-5)) { + // If we get here, the channel is clear. + + // Start transmitting by putting the radio into transmit mode. + radio.setModeTransmit(); + unsigned int MORSE_BUF_SIZE = 128; + char morse_buf[MORSE_BUF_SIZE]; + unsigned int morse_idx; + morse_buf[morse_idx++] = ' '; // start with space to let PA come up + while (Serial.available() && morse_idx < MORSE_BUF_SIZE) { + morse_buf[morse_idx++] = Serial.read(); + } + morse_buf[morse_idx] = '\0'; // null terminate + + // Send a message out in morse code + radio.morseOut(morse_buf); + + // We're done sending the message, set the radio back into recieve mode. + radio.setModeReceive(); + 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: "); + Serial.println(radio.readRSSI()); + } + } +} + +void handleTone(uint16_t tone_time) { + //Serial.println(tone_time); + if (tone_time > (MORSE_DOT*0.7) && tone_time < (MORSE_DOT*1.3)) { + // add a dot + //Serial.print("."); + //nothing to do for this bit position, since . = 0 + } else if (tone_time > (MORSE_DOT*2.7) && tone_time < (MORSE_DOT*3.3)) { + // add a dash + //Serial.print("-"); + rx_morse_char += rx_morse_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; +} + diff --git a/src/HamShield.cpp b/src/HamShield.cpp index 34c7c66..9a2a500 100644 --- a/src/HamShield.cpp +++ b/src/HamShield.cpp @@ -771,6 +771,14 @@ void HamShield::setCtcss(float freq_Hz) { void HamShield::setCtcssFreq(uint16_t freq_milliHz){ HSwriteWord(devAddr, A1846S_CTCSS_FREQ_REG, freq_milliHz); + + // set RX Ctcss match thresholds (based on frequency) + // calculate thresh based on freq + float f = ((float) freq_milliHz)/100; + uint8_t thresh = (uint8_t)(-0.1*f + 25); + setCtcssDetThreshIn(thresh); + setCtcssDetThreshOut(thresh); + } uint16_t HamShield::getCtcssFreqMilliHz(){ HSreadWord(devAddr, A1846S_CTCSS_FREQ_REG, radio_i2c_buf); @@ -786,14 +794,14 @@ void HamShield::setCtcssFreqToStandard(){ setCtcssFreq(13440); } -void HamShield::enableCtcss() { +void HamShield::enableCtcss() { // enable TX HSwriteBitsW(devAddr, A1846S_CTCSS_MODE_REG, 10, 2, 3); // enable RX setCtcssGpioSel(1); HSwriteBitW(devAddr, A1846S_TX_VOICE_REG, A1846S_CTCSS_DET_BIT, 0); - HSwriteBitW(devAddr, A1846S_EMPH_FILTER_REG, A1846S_CTCSS_FILTER_BYPASS, 0); + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_CTCSS_FILTER_BYPASS, 0); setDetCtcss(); } void HamShield::disableCtcss() { @@ -801,6 +809,26 @@ void HamShield::disableCtcss() { disableCtcssCdcss(); } +// match threshold +void HamShield::setCtcssDetThreshIn(uint8_t thresh) { + HSwriteBitsW(devAddr, A1846S_CTCSS_THRESH_REG, 15, 8, thresh); +} +uint8_t HamShield::getCtcssDetThreshIn() { + HSreadBitsW(devAddr, A1846S_CTCSS_THRESH_REG, 15, 8, radio_i2c_buf); + return (uint8_t) radio_i2c_buf[0]; +} + +// unmatch threshold +void HamShield::setCtcssDetThreshOut(uint8_t thresh) { + HSwriteBitsW(devAddr, A1846S_CTCSS_THRESH_REG, 7, 8, thresh); +} +uint8_t HamShield::getCtcssDetThreshOut() { + HSreadBitsW(devAddr, A1846S_CTCSS_THRESH_REG, 7, 8, radio_i2c_buf); + return (uint8_t) radio_i2c_buf[0]; +} + + + // cdcss codes void HamShield::setCdcssCode(uint16_t code) { // note: assuming a well formed code (xyz, where x, y, and z are all 0-7) @@ -822,10 +850,10 @@ void HamShield::setCdcssCode(uint16_t code) { // TODO: CRC // set registers - uint16_t temp_code = (uint16_t) cdcss_code; - HSwriteWord(devAddr, A1846S_CDCSS_CODE_HI_REG, temp_code); - temp_code = (uint16_t) (cdcss_code >> 16); - HSwriteWord(devAddr, A1846S_CDCSS_CODE_LO_REG, temp_code); + uint16_t temp_code = (uint16_t) cdcss_code; + HSwriteWord(devAddr, A1846S_CDCSS_CODE_LO_REG, temp_code); + temp_code = ((uint16_t) (cdcss_code >> 16))&0x00FF; + HSwriteWord(devAddr, A1846S_CDCSS_CODE_HI_REG, temp_code); } uint16_t HamShield::getCdcssCode() { uint32_t oct_code; @@ -964,7 +992,7 @@ void HamShield::enableDTMFReceive(){ //HSwriteBitsW(devAddr, 0x57, 0, 1, 1); // send dtmf to speaker out // bypass pre/de-emphasis - HSwriteBitsW(devAddr, A1846S_EMPH_FILTER_REG, A1846S_EMPH_FILTER_EN, 1, 1); + HSwriteBitsW(devAddr, A1846S_FILTER_REG, A1846S_EMPH_FILTER_EN, 1, 1); } @@ -1188,16 +1216,65 @@ setStMode(0); // Pre-emphasis, De-emphasis filter void HamShield::bypassPreDeEmph(){ - HSwriteBitW(devAddr, A1846S_EMPH_FILTER_REG, A1846S_EMPH_FILTER_EN, 1); + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_EMPH_FILTER_EN, 1); } void HamShield::usePreDeEmph(){ - HSwriteBitW(devAddr, A1846S_EMPH_FILTER_REG, A1846S_EMPH_FILTER_EN, 0); + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_EMPH_FILTER_EN, 0); } bool HamShield::getPreDeEmphEnabled(){ - HSreadBitW(devAddr, A1846S_EMPH_FILTER_REG, A1846S_EMPH_FILTER_EN, radio_i2c_buf); + HSreadBitW(devAddr, A1846S_FILTER_REG, A1846S_EMPH_FILTER_EN, radio_i2c_buf); return (radio_i2c_buf[0] == 0); } +// Voice Filters +void HamShield::bypassVoiceHpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VHPF_FILTER_EN, 1); +} +void HamShield::useVoiceHpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VHPF_FILTER_EN, 0); +} +bool HamShield::getVoiceHpfEnabled(){ + HSreadBitW(devAddr, A1846S_FILTER_REG, A1846S_VHPF_FILTER_EN, radio_i2c_buf); + return (radio_i2c_buf[0] == 0); +} + +void HamShield::bypassVoiceLpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VLPF_FILTER_EN, 1); +} +void HamShield::useVoiceLpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VLPF_FILTER_EN, 0); +} +bool HamShield::getVoiceLpfEnabled(){ + HSreadBitW(devAddr, A1846S_FILTER_REG, A1846S_VLPF_FILTER_EN, radio_i2c_buf); + return (radio_i2c_buf[0] == 0); +} + +// Vox filters + +void HamShield::bypassVoxHpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VXHPF_FILTER_EN, 1); +} +void HamShield::useVoxHpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VXHPF_FILTER_EN, 0); +} +bool HamShield::getVoxHpfEnabled(){ + HSreadBitW(devAddr, A1846S_FILTER_REG, A1846S_VXHPF_FILTER_EN, radio_i2c_buf); + return (radio_i2c_buf[0] == 0); +} + +void HamShield::bypassVoxLpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VXLPF_FILTER_EN, 1); +} +void HamShield::useVoxLpf(){ + HSwriteBitW(devAddr, A1846S_FILTER_REG, A1846S_VXLPF_FILTER_EN, 0); +} +bool HamShield::getVoxLpfEnabled(){ + HSreadBitW(devAddr, A1846S_FILTER_REG, A1846S_VXLPF_FILTER_EN, radio_i2c_buf); + return (radio_i2c_buf[0] == 0); +} + + + // Read Only Status Registers int16_t HamShield::readRSSI(){ HSreadBitsW(devAddr, A1846S_RSSI_REG, A1846S_RSSI_BIT, A1846S_RSSI_LENGTH, radio_i2c_buf); @@ -1545,11 +1622,13 @@ void HamShield::morseOut(char buffer[HAMSHIELD_MORSE_BUFFER_SIZE]) { // We delay by 4 here, if we previously sent a symbol. Otherwise 7. // This could probably just be always 7 and go relatively unnoticed. if(prev == 0 || prev == ' '){ - tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis * 7); - delay(morse_dot_millis*7); + //tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis * 7); + noTone(HAMSHIELD_PWM_PIN); + delay(morse_dot_millis*7); } else { - tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis * 4); - delay(morse_dot_millis*4); + //tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis * 4); + noTone(HAMSHIELD_PWM_PIN); + delay(morse_dot_millis*4); } continue; } @@ -1564,14 +1643,16 @@ void HamShield::morseOut(char buffer[HAMSHIELD_MORSE_BUFFER_SIZE]) { tone(HAMSHIELD_PWM_PIN, morse_freq, morse_dot_millis); delay(morse_dot_millis); } - tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis); - delay(morse_dot_millis); + //tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis); + noTone(HAMSHIELD_PWM_PIN); + delay(morse_dot_millis); bits >>= 1; // Shift into the next symbol } while(bits != 1); // Wait for 1 termination to be all we have left } // End of character - tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis * 3); - delay(morse_dot_millis * 3); + //tone(HAMSHIELD_PWM_PIN, 6000, morse_dot_millis * 3); + noTone(HAMSHIELD_PWM_PIN); + delay(morse_dot_millis * 3); } return; } diff --git a/src/HamShield.h b/src/HamShield.h index 5a1f007..dff7488 100644 --- a/src/HamShield.h +++ b/src/HamShield.h @@ -49,7 +49,8 @@ #define A1846S_CDCSS_CODE_LO_REG 0x4C // cdccs_code<15:0> #define A1846S_CTCSS_MODE_REG 0x4e // copies CTCSS value from A1846S_CTCSS_FREQ_REG into CTCSS encoder #define A1846S_SQ_OUT_SEL_REG 0x54 // see sq -#define A1846S_EMPH_FILTER_REG 0x58 +#define A1846S_FILTER_REG 0x58 +#define A1846S_CTCSS_THRESH_REG 0x5B #define A1846S_FLAG_REG 0x5C // holds flags for different statuses #define A1846S_RSSI_REG 0x1B // holds RSSI (unit 1dB) #define A1846S_VSSI_REG 0x1A // holds VSSI (unit mV) @@ -161,8 +162,12 @@ // Bitfields for A1846S_SQ_OUT_SEL_REG #define A1846S_SQ_OUT_SEL_BIT 7 // sq_out_sel -// Bitfields for A1846S_EMPH_FILTER_REG +// Bitfields for A1846S_FILTER_REG +#define A1846S_VXHPF_FILTER_EN 11 +#define A1846S_VXLPF_FILTER_EN 12 #define A1846S_EMPH_FILTER_EN 7 +#define A1846S_VHPF_FILTER_EN 6 +#define A1846S_VLPF_FILTER_EN 5 #define A1846S_CTCSS_FILTER_BYPASS 3 // Bitfields for A1846S_FLAG_REG @@ -285,12 +290,6 @@ class HamShield { // Subaudio settings // Ctcss/cdcss mode sel - // x00=disable, - // 001=inner ctcss en, - // 010= inner cdcss en - // 101= outer ctcss en, - // 110=outer cdcss en - // others =disable void setCtcssCdcssMode(uint16_t mode); uint16_t getCtcssCdcssMode(); void setDetPhaseShift(); @@ -299,7 +298,7 @@ class HamShield { void setDetCtcss(); void disableCtcssCdcss(); - // ctcss freq + // ctcss settings void setCtcss(float freq_Hz); void setCtcssFreq(uint16_t freq_milliHz); uint16_t getCtcssFreqMilliHz(); @@ -307,7 +306,11 @@ class HamShield { void setCtcssFreqToStandard(); // freq must be 134.4Hz for standard cdcss mode void enableCtcss(); void disableCtcss(); - + void setCtcssDetThreshIn(uint8_t thresh); + uint8_t getCtcssDetThreshIn(); + void setCtcssDetThreshOut(uint8_t thresh); + uint8_t getCtcssDetThreshOut(); + // Ctcss_sel // 1 = ctcss_cmp/cdcss_cmp out via gpio // 0 = ctcss/cdcss sdo out vio gpio @@ -441,6 +444,22 @@ class HamShield { void bypassPreDeEmph(); void usePreDeEmph(); bool getPreDeEmphEnabled(); + + // Voice filters + void bypassVoiceHpf(); + void useVoiceHpf(); + bool getVoiceHpfEnabled(); + void bypassVoiceLpf(); + void useVoiceLpf(); + bool getVoiceLpfEnabled(); + + // Vox filters + void bypassVoxHpf(); + void useVoxHpf(); + bool getVoxHpfEnabled(); + void bypassVoxLpf(); + void useVoxLpf(); + bool getVoxLpfEnabled(); // Read Only Status Registers int16_t readRSSI();