DIY simplex repeater (beta) - Survivalist Forum
Survivalist Forum

Advertise Here

Go Back   Survivalist Forum > > >
Articles Classifieds Donations Gallery Groups Links Store Survival Files


Notices

Advertise Here
Similar Threads
Thread Thread Starter Forum Replies Last Post
Survival Comms: Simplex Repeater ComancheSniper Communications 167 02-19-2018 01:57 PM
simplex radio test Docmafia Communications 35 06-19-2014 04:45 PM
Have you really tested 2 meter simplex on your rig? merlinfire Communications 12 04-18-2014 10:30 AM
My simplex repeater ALprepper Communications 3 02-14-2014 12:57 PM
backpack radio repeater: simplex vs. crossband amayumi Communications 16 12-16-2013 09:24 PM
The Direct Talk (i355) Argent Simplex Repeater Project. Jackson Pointe Communications 36 06-04-2013 11:49 AM
PROJECT: building a portable simplex repeater from scratch amayumi Communications 3 05-30-2013 07:17 AM
Simplex repeater software v 2.07 arctic Communications 3 09-10-2012 09:17 AM
MFJ-662 Simplex Pocket Repeater kx250kev Communications 3 01-14-2012 07:43 PM
Simplex Repeaters arctic Communications 13 11-05-2011 11:14 PM

Reply
 
Thread Tools Display Modes
Old 04-28-2013, 05:34 PM
ski.ninja ski.ninja is offline
Prepared
 
Join Date: Mar 2010
Posts: 282
Thanks: 77
Thanked 230 Times in 122 Posts
Post DIY simplex repeater (beta)



Advertise Here

I've put together some code for a portable simplex repeater based on an arduino board. The parts are on a slow boat from China, but here's what I have so far (see design guide for example schematics.) Hopefully this will turn into a fun little tutorial someday and I can move this to the DIY sub-forum. Until then, compile it if you dare!

Code:
//////////////////////////////////////////////////////////////////
//                                                              //
//      Simplex Light                                           //
//      By Ski.Ninja                                            //
//      V0.2  April 28, 2013                                    //
//                                                              //
//////////////////////////////////////////////////////////////////
/*

Inspired heavily by ON7EQs HF simplex repeater project:
http://www.qsl.net/on7eq/projects/arduino_simplex_repeater.htm


/// ///
-  Compiled in Arduino 1.0
-  Rewrite for ISD17xx chip with SPI interface
  -  chip.g_erase() takes 2.8 seconds to clear memory
  -  chip.erase() erase time ~1:1
-  Added a DTMF sleep/wake toggle
-  System will run on 3.3v
-  54 Ohm resistor on ISD frequency pin for 12KHz

*/

#include <ISD1700.h>             // ISD1700 controls
#include <avr/wdt.h>             // Watchdog function
#include <DTMF.h>                // DTMF detection


/////////////// Simple Arduino CW Beacon //////////////////

// Written by Mark VandeWettering K6HX
// Email: [email protected]
//
// Thx Mark de ON7EQ !
//
struct t_mtab { char c, pat; } ;
struct t_mtab morsetab[] = {
  	{'.', 106},
	{',', 115},
	{'?', 76},
	{'/', 41},
	{'A', 6},
	{'B', 17},
	{'C', 21},
	{'D', 9},
	{'E', 2},
	{'F', 20},
	{'G', 11},
	{'H', 16},
	{'I', 4},
	{'J', 30},
	{'K', 13},
	{'L', 18},
	{'M', 7},
	{'N', 5},
	{'O', 15},
	{'P', 22},
	{'Q', 27},
	{'R', 10},
	{'S', 8},
	{'T', 3},
	{'U', 12},
	{'V', 24},
	{'W', 14},
	{'X', 25},
	{'Y', 29},
	{'Z', 19},
	{'1', 62},
	{'2', 60},
	{'3', 56},
	{'4', 48},
	{'5', 32},
	{'6', 33},
	{'7', 35},
	{'8', 39},
	{'9', 47},
	{'0', 63}
        } ;
#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0]))
#define CW_SPEED  (18)
#define DOTLEN  (1200/CW_SPEED)
#define DASHLEN  (3.5*(1200/CW_SPEED))  // CW weight  3.5 / 1

////////// end CW section ////////////

/////////////////    R E P E A T E R     P A R A M E T E R S   ////////////////////// 

    #define  RPT_call       ("AB1CDE") // Call sign of the repeater 
    
    #define  RPT_ctcss      ("91.5")   // CTCSS tone / letter

    #define  REC_tone        (1)      // enable tone on record while RPT_active == 1
 
//    #define  DTMF_MSG_BTN    ('1')    // Save message button
    
//    #define DTMF_PLY_BTN     ('5')    // Play saved message
    
    #define DTMF_SLP_WK      ('#')    // Wake/Sleep toggle
    
////////////////////////////////////////////////////////////////////////////////////    

// ISD1700 init

ISD1700 chip(10); // Initialize chipcorder with SS on digital pin 10

int ISD_status = (1);


//DTMF pin
    #define DTMF_pin        (A2)

//CW tone output
    #define  CW_pin          (A5)    // Pin for CW audio out
    #define  CW_PITCH        (900)   // CW pitch 

//PTT pin
    #define  PTT_pin     (13)    // Pin for PTT control : TX = H  /  RX = L   - connect via 'open collector' transistor

//VOX detect
    #define  VOX_pin     (A0)    // Pin for vox check



////////////////    Define some time related variables ///////////////////
    
    unsigned long BeaconTime = 0;    // timestamp beacon repeat interval
    unsigned long ActBeaconTime = 0; // timestamp beacon repeat while repeater active   
    unsigned long RecMsgTime = 0;    // timestamp message record START
    unsigned long RecEndTime = 0;    // timestamp message record END 
    unsigned long RecMsgLength = 0;  // message recorded length
    
    unsigned long MiniMsgTime = 0;   // timer message minimum length
    
    unsigned long PlayMsgTime = 0;   // timer message play length
    unsigned long PlayStartTime = 0; // timestamp message play start

    unsigned long ClrQRGTime = 0;    // timer for clear frequency delay
    unsigned long KerchunkTime = 0;  // timer anti-kerchunk 
    unsigned long SQLdropTime = 0;   // timer to define the max allowed signal/SQL drop (flutter)
    unsigned long BeepDropTime = 0;   // timer to define the delay SQL routine inhibited after 'frequency busy' beep is generated
    unsigned long MsgDelayTime = 0;  // timer for delay before message repeat starts after PTT keyed
    
    
    

////////////////    Define some time related constants ///////////////////

    unsigned long V_BeaconTime = 10;    // beacon repeat interval while repeater idle (minutes)
    unsigned long V_ActBeaconTime = 3;  // beacon repeat interval  while repeater active (minutes)
    unsigned long V_RptActTime = 5;     // Time interval to consider repeater 'active' (minutes - must be > than V_ActBeaconTime)
    unsigned long V_RecMsgTime = 60;    // message max record lenght - in function of the voice recorder IC (seconds)
    unsigned long V_MiniMsgTime = 3;    // message minimum length (seconds)
    unsigned long V_PlayMsgTime = 60;   // message max play lenght - in function of the voice recorder IC (seconds)
    unsigned long V_MsgAgeTime = 10;    // max message age (to declare it obsolete)(seconds)
    unsigned long V_ClrQRGTime =  2;    // clear frequency waiting delay before repeating & beaconing (seconds)
    unsigned long V_KerchunkTime = 50;  // timer anti-kerchunk (milliseconds)= minimum time to consider effective signal
    unsigned long V_SQLdropTime = 200;  // max allowed signal/SQL drop (flutter)(milliseconds)
    unsigned long V_MsgDelayTime = 300; // delay before message repeat starts after PTT key signal (milliseconds)
    unsigned long V_SQLTailTime = 350;  // total delay SQL dropoff - used to stop play before EOM (milliseconds)
    unsigned long V_BeepDropTime = 2000;// delay SQL routine inhibited after'frequency busy' beep is generated (milliseconds)
  
// Define misc variables

    
    byte Beacon_RQ = (0);              // 1 = Beacon Request pending
    byte A_Beacon_RQ = (0);            // 1 = Beacon Request while active pending
    
    byte MaxMsgLength = (0);           // 1 = max Message Length reached

    byte RPT_active = (0);             // 1 = Repeater has been active during ActBeaconTime
    byte RPT_status = (0);             // Repeater status: 
    
                                             // 0 = startup phase 
                                             // 1 = Beacon mode 
                                             // 2 = Record mode 
                                             // 3 = Play mode
                                             
    byte Clear_QRG = (0);              // 1 = Clear QRG status
    byte CTCSS_QRG = (0);              // 1 = CTCSS is present on QRG during a mimimum period to initiate REC status evaluation
    byte TX_status = (0);              // 1 = TX mode / 0 = RX mode 
    byte RxBusy = (0);                 // 1 = Receiver Unsquelched, disregarding CTCSS present or not    
    byte PlayOnce = (0);               // 1 = initate the Play sequence
    
//  DTMF interaction
//  DTMF_pin = A2

// NOTE that N MUST NOT exceed 160
// This is the number of samples which are taken in a call to
// .sample. The smaller the value of N the wider the bandwidth.
// For example, with N=128 at a sample rate of 8926Hz the tone
// detection bandwidth will be 8926/128 = 70Hz. If you make N
// smaller, the bandwidth increases which makes it harder to detect
// the tones because some of the energy of one tone can cross into
// an adjacent (in frequency) tone. But a larger value of N also means
// that it takes longer to collect the samples.
// A value of 64 works just fine, as does 128.
// NOTE that the value of N does NOT have to be a power of 2.
    float n=64.0;

// sampling rate in Hz
    float sampling_rate=8926.0;

    byte DTMF_message = (0);            // 1 = save message for playback later
    byte DTMF_ply_msg = (0);            // 1 = play last message
    byte DTMF_slp_wk = (0);              // 1 = sleep, set RPT_status to 0 (setup) and wait for wake
    
    float d_mags[8];
    
// Instantiate the dtmf library with the number of samples to be taken
// and the sampling rate.
    DTMF dtmf = DTMF(n,sampling_rate);
    



////////////////////////////////////   S E T U P ///////////////////////////////////


void setup() {
  wdt_enable(WDTO_8S);  // Enable watchdog, timeout 10 seconds 
  
  // Define & initialise radio pins 
 
  digitalWrite(PTT_pin, LOW);     // PTT pin init, and make sure transceiver in RX mode !  
  pinMode(PTT_pin, OUTPUT);

  pinMode(CW_pin, OUTPUT);
  digitalWrite(CW_pin, LOW);           // CW pin init
    
    

// Recalculate some pre-defined time constants in milliseconds  
  
    V_BeaconTime = V_BeaconTime * 60000; 
    V_ActBeaconTime = V_ActBeaconTime * 60000;
    V_RptActTime = V_RptActTime * 60000;
    V_RecMsgTime = V_RecMsgTime * 1000;  
    V_MiniMsgTime = V_MiniMsgTime * 1000;
    V_PlayMsgTime = V_PlayMsgTime * 1000;
    V_MsgAgeTime = V_MsgAgeTime * 1000;
    V_ClrQRGTime = V_ClrQRGTime * 1000;

// DTMF variables


// float d_mags[8];


 // Off we go ... prepare the 1st beacon

    RPT_status = (1);     //  Set in beaconing mode at startup 

    Beacon_RQ = (1);      //  Force beacon send immediately at startup 

    BeaconTime = millis();
}
   
////////////////////////////////////////////////////////     L O O P    ////////////////////////////////////////////////////////////////

void loop() {

  wdt_reset();        // Reset Watchdog timer

// Test if frequency is free for a while //

  squelchtest();                          // test SQL
  if (RxBusy == 1){  // QRG not free !  
      ClrQRGTime = millis ();
      Clear_QRG = (0);
         }
  if ((millis() -  ClrQRGTime) > V_ClrQRGTime) {
         Clear_QRG = (1);               // QRG is now clear !
         }

////////////   R E C O R D     A N D    P L A Y    T O G G L E   ///////////////////

// While in RX , test if a CTCSS is present --> prepare for RECORD status

   if ((TX_status == 0) and ((millis() - BeepDropTime) > V_BeepDropTime)) {  
            if (((RxBusy == 0) and (millis() - SQLdropTime)) > V_SQLdropTime) {  //No CTCSS present, no flutter dropout
                KerchunkTime = millis ();         
                CTCSS_QRG = (0);
                if (RPT_status == 2) {
                              RPT_status = (3);              // end of RECORD status, now we must PLAY
                              PlayOnce = (1);
                              //stop the chip
                              RecEndTime = (millis () - V_SQLTailTime);  // Remove SQL crash
                              MsgDelayTime = millis();
                              RecMsgLength = RecEndTime - RecMsgTime;
                               }
                   }


            if (((RxBusy == 1) and (millis() -  KerchunkTime)) > V_KerchunkTime) {          //CTCSS present
                   if ((RPT_status != 0) and (CTCSS_QRG == 0)){                        // we detect CTCSS event 'UP'
                           RecMsgTime = millis();              // we timestamp start of record 
                           PlayOnce = (0);                     // disregard any msg possibly waiting in memory, start a new one!                
                           CTCSS_QRG = (1);                    // A valid CTCSS is present during a minimum time
                           RPT_status = (2);                   // Toggle to Record mode 
                           MaxMsgLength = (0);                 // Reset overflow condition, if valid
                             }
                     SQLdropTime = millis();                    // Flutter drop timestamp
                     if ((millis() - RecMsgTime) >= V_RecMsgTime) MaxMsgLength = (1);   // Set flag for OVERFLOW                 
                         }
            if (((RxBusy == 1) and (millis() - RecMsgTime)) < V_BeepDropTime) {    // check for DTMF tones
                dtmf_check();
            }  
                          
       }

//////////////////////////////  R E C O R D    M O D E /////////////////////////////
 
 if (RPT_status == 2)  {   
     
     if (ISD_status != 2) isd_start_record() ;  // start record on chip
     
  
     if (MaxMsgLength == 1) {    // Test if max msg length reached
           RPT_status = (3);     // Toggle to play mode
           PlayOnce = (1);
               
           isd_stop_record() ;  // stop recording on the chip, memory is full !!
           
           RecEndTime = millis ();
           RecMsgLength = RecEndTime - RecMsgTime ;
           }
    
 } ////// END of RECORD mode  /////////
 
 
//////////////////////////////  P L A Y   M O D E /////////////////////////////////
 
 if (RPT_status == 3)  {   
  
     if (ISD_status == 2) isd_stop_record() ;  // stop recording on the chip    

//
    if (RecMsgLength < V_MiniMsgTime) { // recorded message too short, revert to beacon mode
      RPT_status = (1);
      digitalWrite(PTT_pin, LOW);  // Just in case :o) 
      MaxMsgLength = (0);              // Just in case :o)  
      TX_status = (0);
      CTCSS_QRG = (0); 
      PlayOnce = (0);
      Beacon_RQ = (0); 
      chip.erase();
      chip.pd();
      loop();
      }

     
// test once if msg obsolete to play  

    if (((millis() - SQLdropTime) > (V_MsgAgeTime + V_ClrQRGTime + 1000) ) and (PlayOnce == 1)) { // recorded message obsolete, return to beacon mode
                          
      RPT_status = (1);
      digitalWrite(PTT_pin, LOW);  // Just in case :o)
      TX_status = (0);               //reset some parameters, before jumping to start of loop
      CTCSS_QRG = (0); 
      PlayOnce = (0); 
      Beacon_RQ = (0); 
      A_Beacon_RQ = 0;               // there is non CTCSS activity, cancel repeater 'active' status 
      RPT_active = 0; 
      MaxMsgLength = (0);    
//      chip.g_erase();
//      chip.pd();
      loop(); 
      }    

       
// message is OK, now play   

     if (Clear_QRG == 1) {         // Frequency is clear !
  
              if (PlayOnce == 1) tone(CW_pin,CW_PITCH+500);   // generate signalling beep at start of message 
              digitalWrite(PTT_pin, HIGH);                // PTT enable
              delay(100);        // Wait for TX to go live and RX unsquelch
       
              TX_status = (1);
              RPT_active = (1);       // Repeater has played a msg and is considered 'active'   
              A_Beacon_RQ = (1);      // Set message beacon active
              
                    if (PlayOnce == 1) {           //initiate the play
                        PlayOnce = (0);
                             
                         delay(V_MsgDelayTime/2);    // Lead in delay
                         tone(CW_pin,CW_PITCH);
                         delay(V_MsgDelayTime/2);    // Lead in delay
                         
                         
                         PlayStartTime = millis(); 
                         RPT_active = (1);         // Repeater has played a msg and is considered 'active'         
                        
                         isd_start_play ();        // start playing of chip message
                        
                        
                   /// Generate beacon 'while active'
                        
                        if ((RecMsgLength >= 10000) and (A_Beacon_RQ == 1)){  // we only generate beacon if message lenght > 10sec
                                 delay(V_MsgDelayTime);                // Lead in delay
                                 
                                 sendmsg(RPT_call) ;                  // send call
                                 ActBeaconTime = millis();            // reset timers
                                 BeaconTime = millis();
                                 A_Beacon_RQ = 0;                     // reset request
                                }
                        
                        
                        
                                                     
                        }  // end playonce

   
                 if ((millis() - PlayStartTime) >= RecMsgLength ) {  // entire message played and remove SQL crash at EOM; 
                        ISD_status = (1);                     //Chip status now undefined, message is played

                         if ((RecMsgLength >= V_MiniMsgTime) and  (MaxMsgLength == 0)) {      // only 'K' if effective message repeated
                        delay (300);
                        sendmsg ("K");                      
                        delay (100);
                        chip.pd();
                        }

                        if (MaxMsgLength == 1) {                          // play msg if OVERFLOW detected
                        delay (500);
                
                        sendmsg ("OVER");
  
                        delay (100);
                        }
                        /*
                        if ((RecMsgLength < V_MiniMsgTime) and (RecMsgLength > 100) )   {      // if record too short play 'S'
                        delay (500);
                        sendmsg ("S");
                        delay (100);
                        }
                        */
                        

                        digitalWrite(PTT_pin, LOW);     // PTT disable
                        MaxMsgLength = (0);                 // reset overflow indicator
                        TX_status = (0);
                        RPT_status = (1);                   // Now set beacon mode
                        Beacon_RQ = (0);
                        MaxMsgLength = (0);
                        RPT_active = (1);                   // Repeater has played a msg and is considered 'active'
                        ActBeaconTime = millis();           // Reset the activity timer
                        loop ();
    
                        } // end message played routine
              
        
              }
     
  
        
    
 } 
 ////// END of PLAY  mode  /////////

 
///////////////////////////  B E A C O N    M O D E ///////////////////////////////

if (RPT_status == 0) {
  if ((millis() > 604,800,000) and (Beacon_RQ == 0) and (RxBusy == 0)) softReset(); // 7 day rollover, if repeater left off
}
      
if (RPT_status == 1)  {                       // We are in BCN mode  


// Check for rollover millis() //

    //  4,294,967,295 = 49 days roll-over
    //  2,592,000,000 = 30 days roll-over
    //    604,800,000 = 7 days roll-over
    //     86,400,000 = 24 h roll-over
       
    // only reset while in BCN mode, not waiting for send BCN, freq clear, repeater not active

//    if ((millis() > 86400000) and (Beacon_RQ == 0) and (RxBusy == 0) and (RPT_active == 0)) softReset(); 
     
      if ((millis() > 604,800,000) and (Beacon_RQ == 0) and (RxBusy == 0) and (RPT_active == 0)) softReset(); 


// Beacon Request //

  if (((millis() -  BeaconTime) > V_BeaconTime)) {    
         Beacon_RQ = (1);                     // A beacon must be generated, set Beacon Request = 1
         }
  else {
              // Beacon_RQ = (0);                    //reset Beacon request  - uncomment --> no beacon at startup
              digitalWrite(PTT_pin, LOW);     // Sure in RX mode !
              TX_status = (0);
        }
  
// Beacon Send //

  if ((Beacon_RQ == 1) and (Clear_QRG == 1))  {
              chip.pu();
              delay(70);
              chip.g_erase();                        // just to be sure nothing's hiding.
              chip.pd();
              Beacon_RQ = (0);                       //reset Beacon request
              // BeaconTime = millis();
              digitalWrite(PTT_pin, HIGH);     // PTT enable
              TX_status = (1);
              delay(V_MsgDelayTime);               // Lead in delay
              sendmsg(RPT_call) ;                  //send call
              delay(7*DOTLEN) ;
              sendmsg("QRV") ;                       //send info
              delay(7*DOTLEN) ;
              sendmsg(RPT_ctcss) ;                 //send ctcss info
//              delay(7*DOTLEN) ;
//              sendmsg(RPT_loc) ;                   //send qth locator
              delay(3*DOTLEN) ;
              digitalWrite(PTT_pin, LOW);     // PTT disable
              BeaconTime = millis();              // reset time beacon sent
              ActBeaconTime = millis();           // reset as well beacon time 'while active'
              A_Beacon_RQ = 0;                    // a full beacon was sent, cancel any pending request 
              TX_status = (0);
              Clear_QRG = (0);                      // Reset Free frequency indicator
              ClrQRGTime = millis ();             // Reset Clr QRG timer (inhibit immediate Re-TX)           
         }

 }  /// END OF BCN MODE

}  ////////  E N D     L O O P    //////////

/////////////////////////    CW GENERATION ROUTINES  //////////////////////////
void dash() {
    tone(CW_pin,CW_PITCH); 
    delay(DASHLEN);
    noTone(CW_pin);     
    delay(DOTLEN) ;
    }
////////////////////////////
void dit() {
  tone(CW_pin,CW_PITCH); 
  delay(DOTLEN);
  noTone(CW_pin);    
  delay(DOTLEN);
  }
///////////////////////////
void send(char c) {
  int i ;
    if (c == ' ') {
    delay(7*DOTLEN) ;
    return ;
    }
   if (c == '+') {
    delay(4*DOTLEN) ; 
    dit();
    dash();
    dit();
    dash();
    dit();
    delay(4*DOTLEN) ; 
    return ;
    }    
    
  for (i=0; i<N_MORSE; i++) {
    if (morsetab[i].c == c) {
      unsigned char p = morsetab[i].pat ;
      while (p != 1) {
          if (p & 1)
            dash() ;
          else
            dit() ;
          p = p / 2 ;
          }
      delay(2*DOTLEN) ;
      wdt_reset();        // Reset Watchdog timer
      return ;
      }
    }
  }
///////////////////////////
void sendmsg(char *str) {
  while (*str)
    send(*str++) ;
  }

//////////   SQUELCH READ ROUTINE ////////

void squelchtest() {
  RxBusy = (0);                                     // assume RX is squelched, no signal

  if (analogRead(VOX_pin) < 720) RxBusy = (1);  // RX is unsquelched, change status ( < 3.5v) DIGITAL REWRITE?
  
  
  if ((millis() - BeepDropTime) < V_BeepDropTime) {   // keep record active after 'frequency busy' beep
   RxBusy = (1);   
        }
  }
//////////   ISD1700  RECORD MODE  ////////

void isd_start_record() {
  wdt_reset();
  chip.pu();    //power up chip
  delay(10);
  while (chip.CMD_ERR() == 1) {
    chip.pu();
    delay(10);
  }
  delay(60);
  chip.clr_int();
  delay(10);

    while (!(chip.RDY() == 1))  {
      delay(10);
    }
      ISD_status = (2);
      if ((RPT_active == 1) and (REC_tone == 1)) {  // If repeater active in last 5 minutes, play tone.
        tone(CW_pin,1300);
        digitalWrite(PTT_pin, HIGH);  // Turn on PTT
        delay(100);
        digitalWrite(PTT_pin, LOW);  // Turn off PTT
        BeepDropTime = millis();
        chip.rec();      // Start record
        delay(10);
        while (chip.CMD_ERR() == 1) {
          chip.rec();
          delay(10);
        }
         noTone(CW_pin);
    }     
      else {
        BeepDropTime = millis();   // do not comment, ESSENTIAL!
        chip.rec();
       delay(10);
       while (chip.CMD_ERR() == 1) {
          delay(10);
          chip.rec();
        }
   }
}


void isd_stop_record() {
  chip.stop();
  delay(10);
  while (!(chip.RDY() == 1)) {
    delay(10);
  }
      chip.clr_int();
      ISD_status = (1);
      delay(10);
}



//////////   ISD1700  PLAY MODE  ////////

void isd_start_play() {
  ISD_status = (3);  
  noTone(CW_pin);                     // end of signal beep at start of msg
  chip.play();
  delay(20);
  chip.g_erase();
}

/////////// DTMF handling ///////////////

void dtmf_check() {
  float d_mags[8];
  char thischar;
  // This reads N samples from sensorpin (must be an analog input)
  // and stores them in an array within the library. Use while(1)
  // to determine the actual sampling frequency as described in the
  // comment at the top of this file  
 
  /* while(1) */  dtmf.sample(DTMF_pin);
  
  // The first argument is the address of a user-supplied array
  // of 8 floats in which the function will return the magnitudes
  // of the eight tones.
  // The second argument is the value read by the ADC when there
  // is no signal present. A voltage divider with precisely equal
  // resistors will presumably give a value of 511 or 512.
  // My divider gives a value of 506.
  // If you aren't sure what to use, set this to 512
 
  dtmf.detect(d_mags, 512);
  
  // detect the button
  // If it is recognized, returns one of 0123456789ABCD*#
  // If unrecognized, returns binary zero

  // Pass it the magnitude array used when calling .sample
  // and specify a magnitude which is used as the threshold
  // for determining whether a tone is present or not
  //
  // If N=64 magnitude needs to be around 1200
  // If N=128 the magnitude can be set to 1800
  // but you will need to play with it to get the right value
  thischar = dtmf.button(d_mags,1200.);

if (thischar) {
  chip.stop();
  delay(10);
  chip.g_erase();
  chip.pd();
  
  switch(thischar) {
/*
    case '1':
      DTMF_message = (1);

    case '5':
      DTMF_ply_msg = (1);
*/      
    case DTMF_SLP_WK:                        // Sleep/wake toggle
      if (DTMF_slp_wk == 0) {                // Go to sleep
        DTMF_slp_wk = (1);    
        RPT_status = (0);
        digitalWrite(PTT_pin, HIGH);
        delay(300);
        sendmsg(RPT_call);
        delay(300);
        digitalWrite(PTT_pin, LOW);

      } else {                       // Wake up

      DTMF_slp_wk = (0);
      RPT_status = (1);
      Beacon_RQ = (1);
      }
    }
  }
}


////////////   RESET  CONTROLLER  //////////////

void softReset(){
  chip.stop();
  delay(10);
  chip.g_erase();
  digitalWrite(PTT_pin, HIGH);     // PTT enable
  delay(300);
  sendmsg(RPT_call) ;                  //send call  
  sendmsg(" RESET");      //send reset msg 
  delay(300); 
  digitalWrite(PTT_pin, LOW);     // PTT disable
  chip.reset();                          // Power down chip
  delay(1500);
  
  asm volatile ("  jmp 0");          // start software from scratch
}
It should record/replay 60 seconds of 12 KHz audio, as well as broadcast a CW ID. The unit can also be put into a sleep (silent) mode and re-activated via DTMF tones. This project uses an ISD1700 series chip which provides an SPI interface, allowing increased precision when controlling the IC. If you choose to just use an atmega328 chip and forgo the cost of an arduino, the whole thing can be put together for less than $30 and run on 3.3/5v.

Future versions would expand functionality to include more robust DTMF controls, 'say again' message storing, and even coded mailboxes. This would probably require ditching the ISD chips in favor of a pair of NAND flash ICs, much like the Argent SR1.

Functions:
- Record/repeat up to 60 seconds of 12 KHz audio
- CW ID at 10 minute intervals when no traffic is being passed
- CW ID after each re-transmission over 10 seconds
- Busy signal on frequency when recording
- Overflow tone broadcast
- Sleep/Wake on DTMF tone
- Automatic rollover/soft reset
- Watchdog timer

Limitations:
- No transceiver control (must be configured separately)
- No message storing
- ~2.8 second global erase time on chip

ISD1700 arduino library: http://www.eatelier.nl/attachments/a...50/ISD1700.zip

ISD1700 Datasheet: http://www.eatelier.nl/attachments/a...0datasheet.pdf

ISD1700 Design guide: http://www.eatelier.nl/attachments/a...0Rev%201.1.pdf

DTMF library: http://members.shaw.ca/el.supremo/DTMF.zip
Quick reply to this message
Old 04-28-2013, 06:18 PM
CB Rambo CB Rambo is offline
This is a great survival forum
 
Join Date: Mar 2013
Posts: 737
Thanks: 187
Thanked 312 Times in 200 Posts
Default

Sounds good except for the 60 second rule.
You can buy one that connects directly to your home computer that holds up to 300 seconds of audio for less then $100.
Don't remember where I saw it - but at that price it was cheap enough that it wouldn't be worth building.
Quick reply to this message
Old 04-29-2013, 03:48 PM
ski.ninja ski.ninja is offline
Prepared
 
Join Date: Mar 2010
Posts: 282
Thanks: 77
Thanked 230 Times in 122 Posts
Default

The 60 second limit is negotiable depending on which ISD17xx chip you get (see datasheet.) A commercial product this is not, but if anybody wants to tinker with it or send some feedback my way, they are most welcome.

CBR, I think you're talking about the ADS-SR1:
https://www.argentdata.com/catalog/p...products_id=98
Quick reply to this message
Sponsored Links
Advertisement
 
Old 05-27-2013, 04:38 AM
kyndal kyndal is offline
Newbie
 
Join Date: May 2013
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

very interesting

i have been toying with the idea myself.
but i was going to base it around the "wave shield"
giving "practically unlimited "record / playback
to/from an SD card..

/Kyndal
Quick reply to this message
Old 05-27-2013, 05:16 PM
ski.ninja ski.ninja is offline
Prepared
 
Join Date: Mar 2010
Posts: 282
Thanks: 77
Thanked 230 Times in 122 Posts
Default

This is a good educational project, I found. Ultimately, there are better commercial solutions, but I can appreciate that you want to build your own. Regrettably, the wave shield will only play audio and not record it. There is enough memory on the atmega328p (Arduino) chip to play from or record to the SD card, but not enough to do both. When working with NAND flash, erase times are the crux of the issue; requiring multiple components and careful timing, or a large amount of storage which can be erased during periods of non-activity.
Quick reply to this message
Old 05-28-2013, 04:47 AM
kyndal kyndal is offline
Newbie
 
Join Date: May 2013
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

there is a library (waverp) for the wave shield and similar hardware.
that allows it to record aswell as playback..

Quote:
WaveRP is an Arduino library for recording and playing Wave files with the Adafruit Wave Shield. It records 8-bit mono files at 4,000 to 44,100 samples per second. Use of the Wave record/play library, WaveRP, requires the following: Arduino with a 5 volt 328 processor. Low noise power source such as a nine volt DC adapter or battery. Adafruit Wave Shield (version 1.1 is best but 1.0 works) Microphone preamp. A circuit for a simple preamp is included in the documentation. Microphone, PC type with 3.5 mm plug. See the documentation for details. SD/SDHC formatted with 32KB allocation units.
obviously i would build a costum board "inspired" by the "waveshield"
change the actual audio path (skip mike, preamps etc.)
so it fits the radios.

but off the bat.. having not yet experimented with it yet
(parts are on the boat...from china )

i think it should be possibly to stream uncompressed audio
directly to an SD card.. and then play it back again right away..

dedicated DAC and ADC should cut down on the
ram/workload requirements.

using much the same setup as the code above..
dtmf, squelch etc.

and ya. maby stick a big card in there and keep a few days worth
of recorded audio ? might be usefull later..
2gb is probably the largest with fat16 32kb sectors? havent looked into that yet but should still go along way at reasonable bitrate

/Kyndal
Quick reply to this message
Old 05-28-2013, 03:15 PM
ski.ninja ski.ninja is offline
Prepared
 
Join Date: Mar 2010
Posts: 282
Thanks: 77
Thanked 230 Times in 122 Posts
Default

Sounds great! I had no idea there was a library for that. If you're going to design your own board, you may be interested in forgoing the shield concept entirely. This can really cut down on power usage (if you decide to make it portable.)
Quick reply to this message
Old 05-28-2013, 05:54 PM
kyndal kyndal is offline
Newbie
 
Join Date: May 2013
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

oh ya... will be no shield.. might go with an arduino "nano" in a socket
for simplicity.. Usb, serial etc. but in a custom board

/Kyndal
Quick reply to this message
Old 05-28-2013, 06:11 PM
ski.ninja ski.ninja is offline
Prepared
 
Join Date: Mar 2010
Posts: 282
Thanks: 77
Thanked 230 Times in 122 Posts
Default

Or less than the nano, if you really want to strip it down.

http://arduino.cc/en/Main/Standalone
Quick reply to this message
Old 02-12-2020, 08:18 AM
justineodal justineodal is offline
Newbie
 
Join Date: Feb 2020
Posts: 3
Thanks: 0
Thanked 5 Times in 2 Posts
Default

Quote:
Originally Posted by ski.ninja View Post
I've put together some code for a portable simplex repeater based on an arduino board. The parts are on a slow boat from China, but here's what I have so far (see design guide for example schematics.) Hopefully this will turn into a fun little tutorial someday and I can move this to the DIY sub-forum. Until then, compile it if you dare!

Code:
//////////////////////////////////////////////////////////////////
//                                                              //
//      Simplex Light                                           //
//      By Ski.Ninja                                            //
//      V0.2  April 28, 2013                                    //
//                                                              //
//////////////////////////////////////////////////////////////////
/*

Inspired heavily by ON7EQs HF simplex repeater project:
http://www.qsl.net/on7eq/projects/arduino_simplex_repeater.htm


/// ///
-  Compiled in Arduino 1.0
-  Rewrite for ISD17xx chip with SPI interface
  -  chip.g_erase() takes 2.8 seconds to clear memory
  -  chip.erase() erase time ~1:1
-  Added a DTMF sleep/wake toggle
-  System will run on 3.3v
-  54 Ohm resistor on ISD frequency pin for 12KHz

*/

#include <ISD1700.h>             // ISD1700 controls
#include <avr/wdt.h>             // Watchdog function
#include <DTMF.h>                // DTMF detection


/////////////// Simple Arduino CW Beacon //////////////////

// Written by Mark VandeWettering K6HX
// Email: [email protected]
//
// Thx Mark de ON7EQ !
//
struct t_mtab { char c, pat; } ;
struct t_mtab morsetab[] = {
  	{'.', 106},
	{',', 115},
	{'?', 76},
	{'/', 41},
	{'A', 6},
	{'B', 17},
	{'C', 21},
	{'D', 9},
	{'E', 2},
	{'F', 20},
	{'G', 11},
	{'H', 16},
	{'I', 4},
	{'J', 30},
	{'K', 13},
	{'L', 18},
	{'M', 7},
	{'N', 5},
	{'O', 15},
	{'P', 22},
	{'Q', 27},
	{'R', 10},
	{'S', 8},
	{'T', 3},
	{'U', 12},
	{'V', 24},
	{'W', 14},
	{'X', 25},
	{'Y', 29},
	{'Z', 19},
	{'1', 62},
	{'2', 60},
	{'3', 56},
	{'4', 48},
	{'5', 32},
	{'6', 33},
	{'7', 35},
	{'8', 39},
	{'9', 47},
	{'0', 63}
        } ;
#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0]))
#define CW_SPEED  (18)
#define DOTLEN  (1200/CW_SPEED)
#define DASHLEN  (3.5*(1200/CW_SPEED))  // CW weight  3.5 / 1

////////// end CW section ////////////

/////////////////    R E P E A T E R     P A R A M E T E R S   ////////////////////// 

    #define  RPT_call       ("AB1CDE") // Call sign of the repeater 
    
    #define  RPT_ctcss      ("91.5")   // CTCSS tone / letter

    #define  REC_tone        (1)      // enable tone on record while RPT_active == 1
 
//    #define  DTMF_MSG_BTN    ('1')    // Save message button
    
//    #define DTMF_PLY_BTN     ('5')    // Play saved message
    
    #define DTMF_SLP_WK      ('#')    // Wake/Sleep toggle
    
////////////////////////////////////////////////////////////////////////////////////    

// ISD1700 init

ISD1700 chip(10); // Initialize chipcorder with SS on digital pin 10

int ISD_status = (1);


//DTMF pin
    #define DTMF_pin        (A2)

//CW tone output
    #define  CW_pin          (A5)    // Pin for CW audio out
    #define  CW_PITCH        (900)   // CW pitch 

//PTT pin
    #define  PTT_pin     (13)    // Pin for PTT control : TX = H  /  RX = L   - connect via 'open collector' transistor

//VOX detect
    #define  VOX_pin     (A0)    // Pin for vox check



////////////////    Define some time related variables ///////////////////
    
    unsigned long BeaconTime = 0;    // timestamp beacon repeat interval
    unsigned long ActBeaconTime = 0; // timestamp beacon repeat while repeater active   
    unsigned long RecMsgTime = 0;    // timestamp message record START
    unsigned long RecEndTime = 0;    // timestamp message record END 
    unsigned long RecMsgLength = 0;  // message recorded length
    
    unsigned long MiniMsgTime = 0;   // timer message minimum length
    
    unsigned long PlayMsgTime = 0;   // timer message play length
    unsigned long PlayStartTime = 0; // timestamp message play start

    unsigned long ClrQRGTime = 0;    // timer for clear frequency delay
    unsigned long KerchunkTime = 0;  // timer anti-kerchunk 
    unsigned long SQLdropTime = 0;   // timer to define the max allowed signal/SQL drop (flutter)
    unsigned long BeepDropTime = 0;   // timer to define the delay SQL routine inhibited after 'frequency busy' beep is generated
    unsigned long MsgDelayTime = 0;  // timer for delay before message repeat starts after PTT keyed
    
    
    

////////////////    Define some time related constants ///////////////////

    unsigned long V_BeaconTime = 10;    // beacon repeat interval while repeater idle (minutes)
    unsigned long V_ActBeaconTime = 3;  // beacon repeat interval  while repeater active (minutes)
    unsigned long V_RptActTime = 5;     // Time interval to consider repeater 'active' (minutes - must be > than V_ActBeaconTime)
    unsigned long V_RecMsgTime = 60;    // message max record lenght - in function of the voice recorder IC (seconds)
    unsigned long V_MiniMsgTime = 3;    // message minimum length (seconds)
    unsigned long V_PlayMsgTime = 60;   // message max play lenght - in function of the voice recorder IC (seconds)
    unsigned long V_MsgAgeTime = 10;    // max message age (to declare it obsolete)(seconds)
    unsigned long V_ClrQRGTime =  2;    // clear frequency waiting delay before repeating & beaconing (seconds)
    unsigned long V_KerchunkTime = 50;  // timer anti-kerchunk (milliseconds)= minimum time to consider effective signal
    unsigned long V_SQLdropTime = 200;  // max allowed signal/SQL drop (flutter)(milliseconds)
    unsigned long V_MsgDelayTime = 300; // delay before message repeat starts after PTT key signal (milliseconds)
    unsigned long V_SQLTailTime = 350;  // total delay SQL dropoff - used to stop play before EOM (milliseconds)
    unsigned long V_BeepDropTime = 2000;// delay SQL routine inhibited after'frequency busy' beep is generated (milliseconds)
  
// Define misc variables

    
    byte Beacon_RQ = (0);              // 1 = Beacon Request pending
    byte A_Beacon_RQ = (0);            // 1 = Beacon Request while active pending
    
    byte MaxMsgLength = (0);           // 1 = max Message Length reached

    byte RPT_active = (0);             // 1 = Repeater has been active during ActBeaconTime
    byte RPT_status = (0);             // Repeater status: 
    
                                             // 0 = startup phase 
                                             // 1 = Beacon mode 
                                             // 2 = Record mode 
                                             // 3 = Play mode
                                             
    byte Clear_QRG = (0);              // 1 = Clear QRG status
    byte CTCSS_QRG = (0);              // 1 = CTCSS is present on QRG during a mimimum period to initiate REC status evaluation
    byte TX_status = (0);              // 1 = TX mode / 0 = RX mode 
    byte RxBusy = (0);                 // 1 = Receiver Unsquelched, disregarding CTCSS present or not    
    byte PlayOnce = (0);               // 1 = initate the Play sequence
    
//  DTMF interaction
//  DTMF_pin = A2

// NOTE that N MUST NOT exceed 160
// This is the number of samples which are taken in a call to
// .sample. The smaller the value of N the wider the bandwidth.
// For example, with N=128 at a sample rate of 8926Hz the tone
// detection bandwidth will be 8926/128 = 70Hz. If you make N
// smaller, the bandwidth increases which makes it harder to detect
// the tones because some of the energy of one tone can cross into
// an adjacent (in frequency) tone. But a larger value of N also means
// that it takes longer to collect the samples.
// A value of 64 works just fine, as does 128.
// NOTE that the value of N does NOT have to be a power of 2.
    float n=64.0;

// sampling rate in Hz
    float sampling_rate=8926.0;

    byte DTMF_message = (0);            // 1 = save message for playback later
    byte DTMF_ply_msg = (0);            // 1 = play last message
    byte DTMF_slp_wk = (0);              // 1 = sleep, set RPT_status to 0 (setup) and wait for wake
    
    float d_mags[8];
    
// Instantiate the dtmf library with the number of samples to be taken
// and the sampling rate.
    DTMF dtmf = DTMF(n,sampling_rate);
    



////////////////////////////////////   S E T U P ///////////////////////////////////


void setup() {
  wdt_enable(WDTO_8S);  // Enable watchdog, timeout 10 seconds 
  
  // Define & initialise radio pins 
 
  digitalWrite(PTT_pin, LOW);     // PTT pin init, and make sure transceiver in RX mode !  
  pinMode(PTT_pin, OUTPUT);

  pinMode(CW_pin, OUTPUT);
  digitalWrite(CW_pin, LOW);           // CW pin init
    
    

// Recalculate some pre-defined time constants in milliseconds  
  
    V_BeaconTime = V_BeaconTime * 60000; 
    V_ActBeaconTime = V_ActBeaconTime * 60000;
    V_RptActTime = V_RptActTime * 60000;
    V_RecMsgTime = V_RecMsgTime * 1000;  
    V_MiniMsgTime = V_MiniMsgTime * 1000;
    V_PlayMsgTime = V_PlayMsgTime * 1000;
    V_MsgAgeTime = V_MsgAgeTime * 1000;
    V_ClrQRGTime = V_ClrQRGTime * 1000;

// DTMF variables


// float d_mags[8];


 // Off we go ... prepare the 1st beacon

    RPT_status = (1);     //  Set in beaconing mode at startup 

    Beacon_RQ = (1);      //  Force beacon send immediately at startup 

    BeaconTime = millis();
}
   
////////////////////////////////////////////////////////     L O O P    ////////////////////////////////////////////////////////////////

void loop() {

  wdt_reset();        // Reset Watchdog timer

// Test if frequency is free for a while //

  squelchtest();                          // test SQL
  if (RxBusy == 1){  // QRG not free !  
      ClrQRGTime = millis ();
      Clear_QRG = (0);
         }
  if ((millis() -  ClrQRGTime) > V_ClrQRGTime) {
         Clear_QRG = (1);               // QRG is now clear !
         }

////////////   R E C O R D     A N D    P L A Y    T O G G L E   ///////////////////

// While in RX , test if a CTCSS is present --> prepare for RECORD status

   if ((TX_status == 0) and ((millis() - BeepDropTime) > V_BeepDropTime)) {  
            if (((RxBusy == 0) and (millis() - SQLdropTime)) > V_SQLdropTime) {  //No CTCSS present, no flutter dropout
                KerchunkTime = millis ();         
                CTCSS_QRG = (0);
                if (RPT_status == 2) {
                              RPT_status = (3);              // end of RECORD status, now we must PLAY
                              PlayOnce = (1);
                              //stop the chip
                              RecEndTime = (millis () - V_SQLTailTime);  // Remove SQL crash
                              MsgDelayTime = millis();
                              RecMsgLength = RecEndTime - RecMsgTime;
                               }
                   }


            if (((RxBusy == 1) and (millis() -  KerchunkTime)) > V_KerchunkTime) {          //CTCSS present
                   if ((RPT_status != 0) and (CTCSS_QRG == 0)){                        // we detect CTCSS event 'UP'
                           RecMsgTime = millis();              // we timestamp start of record 
                           PlayOnce = (0);                     // disregard any msg possibly waiting in memory, start a new one!                
                           CTCSS_QRG = (1);                    // A valid CTCSS is present during a minimum time
                           RPT_status = (2);                   // Toggle to Record mode 
                           MaxMsgLength = (0);                 // Reset overflow condition, if valid
                             }
                     SQLdropTime = millis();                    // Flutter drop timestamp
                     if ((millis() - RecMsgTime) >= V_RecMsgTime) MaxMsgLength = (1);   // Set flag for OVERFLOW                 
                         }
            if (((RxBusy == 1) and (millis() - RecMsgTime)) < V_BeepDropTime) {    // check for DTMF tones
                dtmf_check();
            }  
                          
       }

//////////////////////////////  R E C O R D    M O D E /////////////////////////////
 
 if (RPT_status == 2)  {   
     
     if (ISD_status != 2) isd_start_record() ;  // start record on chip
     
  
     if (MaxMsgLength == 1) {    // Test if max msg length reached
           RPT_status = (3);     // Toggle to play mode
           PlayOnce = (1);
               
           isd_stop_record() ;  // stop recording on the chip, memory is full !!
           
           RecEndTime = millis ();
           RecMsgLength = RecEndTime - RecMsgTime ;
           }
    
 } ////// END of RECORD mode  /////////
 
 
//////////////////////////////  P L A Y   M O D E /////////////////////////////////
 
 if (RPT_status == 3)  {   
  
     if (ISD_status == 2) isd_stop_record() ;  // stop recording on the chip    

//
    if (RecMsgLength < V_MiniMsgTime) { // recorded message too short, revert to beacon mode
      RPT_status = (1);
      digitalWrite(PTT_pin, LOW);  // Just in case :o) 
      MaxMsgLength = (0);              // Just in case :o)  
      TX_status = (0);
      CTCSS_QRG = (0); 
      PlayOnce = (0);
      Beacon_RQ = (0); 
      chip.erase();
      chip.pd();
      loop();
      }

     
// test once if msg obsolete to play  

    if (((millis() - SQLdropTime) > (V_MsgAgeTime + V_ClrQRGTime + 1000) ) and (PlayOnce == 1)) { // recorded message obsolete, return to beacon mode
                          
      RPT_status = (1);
      digitalWrite(PTT_pin, LOW);  // Just in case :o)
      TX_status = (0);               //reset some parameters, before jumping to start of loop
      CTCSS_QRG = (0); 
      PlayOnce = (0); 
      Beacon_RQ = (0); 
      A_Beacon_RQ = 0;               // there is non CTCSS activity, cancel repeater 'active' status 
      RPT_active = 0; 
      MaxMsgLength = (0);    
//      chip.g_erase();
//      chip.pd();
      loop(); 
      }    

       
// message is OK, now play   

     if (Clear_QRG == 1) {         // Frequency is clear !
  
              if (PlayOnce == 1) tone(CW_pin,CW_PITCH+500);   // generate signalling beep at start of message 
              digitalWrite(PTT_pin, HIGH);                // PTT enable
              delay(100);        // Wait for TX to go live and RX unsquelch
       
              TX_status = (1);
              RPT_active = (1);       // Repeater has played a msg and is considered 'active'   
              A_Beacon_RQ = (1);      // Set message beacon active
              
                    if (PlayOnce == 1) {           //initiate the play
                        PlayOnce = (0);
                             
                         delay(V_MsgDelayTime/2);    // Lead in delay
                         tone(CW_pin,CW_PITCH);
                         delay(V_MsgDelayTime/2);    // Lead in delay
                         
                         
                         PlayStartTime = millis(); 
                         RPT_active = (1);         // Repeater has played a msg and is considered 'active'         
                        
                         isd_start_play ();        // start playing of chip message
                        
                        
                   /// Generate beacon 'while active'
                        
                        if ((RecMsgLength >= 10000) and (A_Beacon_RQ == 1)){  // we only generate beacon if message lenght > 10sec
                                 delay(V_MsgDelayTime);                // Lead in delay
                                 
                                 sendmsg(RPT_call) ;                  // send call
                                 ActBeaconTime = millis();            // reset timers
                                 BeaconTime = millis();
                                 A_Beacon_RQ = 0;                     // reset request
                                }
                        
                        
                        
                                                     
                        }  // end playonce

   
                 if ((millis() - PlayStartTime) >= RecMsgLength ) {  // entire message played and remove SQL crash at EOM; 
                        ISD_status = (1);                     //Chip status now undefined, message is played

                         if ((RecMsgLength >= V_MiniMsgTime) and  (MaxMsgLength == 0)) {      // only 'K' if effective message repeated
                        delay (300);
                        sendmsg ("K");                      
                        delay (100);
                        chip.pd();
                        }

                        if (MaxMsgLength == 1) {                          // play msg if OVERFLOW detected
                        delay (500);
                
                        sendmsg ("OVER");
  
                        delay (100);
                        }
                        /*
                        if ((RecMsgLength < V_MiniMsgTime) and (RecMsgLength > 100) )   {      // if record too short play 'S'
                        delay (500);
                        sendmsg ("S");
                        delay (100);
                        }
                        */
                        

                        digitalWrite(PTT_pin, LOW);     // PTT disable
                        MaxMsgLength = (0);                 // reset overflow indicator
                        TX_status = (0);
                        RPT_status = (1);                   // Now set beacon mode
                        Beacon_RQ = (0);
                        MaxMsgLength = (0);
                        RPT_active = (1);                   // Repeater has played a msg and is considered 'active'
                        ActBeaconTime = millis();           // Reset the activity timer
                        loop ();
    
                        } // end message played routine
              
        
              }
     
  
        
    
 } 
 ////// END of PLAY  mode  /////////

 
///////////////////////////  B E A C O N    M O D E ///////////////////////////////

if (RPT_status == 0) {
  if ((millis() > 604,800,000) and (Beacon_RQ == 0) and (RxBusy == 0)) softReset(); // 7 day rollover, if repeater left off
}
      
if (RPT_status == 1)  {                       // We are in BCN mode  


// Check for rollover millis() //

    //  4,294,967,295 = 49 days roll-over
    //  2,592,000,000 = 30 days roll-over
    //    604,800,000 = 7 days roll-over
    //     86,400,000 = 24 h roll-over
       
    // only reset while in BCN mode, not waiting for send BCN, freq clear, repeater not active

//    if ((millis() > 86400000) and (Beacon_RQ == 0) and (RxBusy == 0) and (RPT_active == 0)) softReset(); 
     
      if ((millis() > 604,800,000) and (Beacon_RQ == 0) and (RxBusy == 0) and (RPT_active == 0)) softReset(); 


// Beacon Request //

  if (((millis() -  BeaconTime) > V_BeaconTime)) {    
         Beacon_RQ = (1);                     // A beacon must be generated, set Beacon Request = 1
         }
  else {
              // Beacon_RQ = (0);                    //reset Beacon request  - uncomment --> no beacon at startup
              digitalWrite(PTT_pin, LOW);     // Sure in RX mode !
              TX_status = (0);
        }
  
// Beacon Send //

  if ((Beacon_RQ == 1) and (Clear_QRG == 1))  {
              chip.pu();
              delay(70);
              chip.g_erase();                        // just to be sure nothing's hiding.
              chip.pd();
              Beacon_RQ = (0);                       //reset Beacon request
              // BeaconTime = millis();
              digitalWrite(PTT_pin, HIGH);     // PTT enable
              TX_status = (1);
              delay(V_MsgDelayTime);               // Lead in delay
              sendmsg(RPT_call) ;                  //send call
              delay(7*DOTLEN) ;
              sendmsg("QRV") ;                       //send info
              delay(7*DOTLEN) ;
              sendmsg(RPT_ctcss) ;                 //send ctcss info
//              delay(7*DOTLEN) ;
//              sendmsg(RPT_loc) ;                   //send qth locator
              delay(3*DOTLEN) ;
              digitalWrite(PTT_pin, LOW);     // PTT disable
              BeaconTime = millis();              // reset time beacon sent
              ActBeaconTime = millis();           // reset as well beacon time 'while active'
              A_Beacon_RQ = 0;                    // a full beacon was sent, cancel any pending request 
              TX_status = (0);
              Clear_QRG = (0);                      // Reset Free frequency indicator
              ClrQRGTime = millis ();             // Reset Clr QRG timer (inhibit immediate Re-TX)           
         }

 }  /// END OF BCN MODE

}  ////////  E N D     L O O P    //////////

/////////////////////////    CW GENERATION ROUTINES  //////////////////////////
void dash() {
    tone(CW_pin,CW_PITCH); 
    delay(DASHLEN);
    noTone(CW_pin);     
    delay(DOTLEN) ;
    }
////////////////////////////
void dit() {
  tone(CW_pin,CW_PITCH); 
  delay(DOTLEN);
  noTone(CW_pin);    
  delay(DOTLEN);
  }
///////////////////////////
void send(char c) {
  int i ;
    if (c == ' ') {
    delay(7*DOTLEN) ;
    return ;
    }
   if (c == '+') {
    delay(4*DOTLEN) ; 
    dit();
    dash();
    dit();
    dash();
    dit();
    delay(4*DOTLEN) ; 
    return ;
    }    
    
  for (i=0; i<N_MORSE; i++) {
    if (morsetab[i].c == c) {
      unsigned char p = morsetab[i].pat ;
      while (p != 1) {
          if (p & 1)
            dash() ;
          else
            dit() ;
          p = p / 2 ;
          }
      delay(2*DOTLEN) ;
      wdt_reset();        // Reset Watchdog timer
      return ;
      }
    }
  }
///////////////////////////
void sendmsg(char *str) {
  while (*str)
    send(*str++) ;
  }

//////////   SQUELCH READ ROUTINE ////////

void squelchtest() {
  RxBusy = (0);                                     // assume RX is squelched, no signal

  if (analogRead(VOX_pin) < 720) RxBusy = (1);  // RX is unsquelched, change status ( < 3.5v) DIGITAL REWRITE?
  
  
  if ((millis() - BeepDropTime) < V_BeepDropTime) {   // keep record active after 'frequency busy' beep
   RxBusy = (1);   
        }
  }
//////////   ISD1700  RECORD MODE  ////////

void isd_start_record() {
  wdt_reset();
  chip.pu();    //power up chip
  delay(10);
  while (chip.CMD_ERR() == 1) {
    chip.pu();
    delay(10);
  }
  delay(60);
  chip.clr_int();
  delay(10);

    while (!(chip.RDY() == 1))  {
      delay(10);
    }
      ISD_status = (2);
      if ((RPT_active == 1) and (REC_tone == 1)) {  // If repeater active in last 5 minutes, play tone.
        tone(CW_pin,1300);
        digitalWrite(PTT_pin, HIGH);  // Turn on PTT
        delay(100);
        digitalWrite(PTT_pin, LOW);  // Turn off PTT
        BeepDropTime = millis();
        chip.rec();      // Start record
        delay(10);
        while (chip.CMD_ERR() == 1) {
          chip.rec();
          delay(10);
        }
         noTone(CW_pin);
    }     
      else {
        BeepDropTime = millis();   // do not comment, ESSENTIAL!
        chip.rec();
       delay(10);
       while (chip.CMD_ERR() == 1) {
          delay(10);
          chip.rec();
        }
   }
}


void isd_stop_record() {
  chip.stop();
  delay(10);
  while (!(chip.RDY() == 1)) {
    delay(10);
  }
      chip.clr_int();
      ISD_status = (1);
      delay(10);
}



//////////   ISD1700  PLAY MODE  ////////

void isd_start_play() {
  ISD_status = (3);  
  noTone(CW_pin);                     // end of signal beep at start of msg
  chip.play();
  delay(20);
  chip.g_erase();
}

/////////// DTMF handling ///////////////

void dtmf_check() {
  float d_mags[8];
  char thischar;
  // This reads N samples from sensorpin (must be an analog input)
  // and stores them in an array within the library. Use while(1)
  // to determine the actual sampling frequency as described in the
  // comment at the top of this file  
 
  /* while(1) */  dtmf.sample(DTMF_pin);
  
  // The first argument is the address of a user-supplied array
  // of 8 floats in which the function will return the magnitudes
  // of the eight tones.
  // The second argument is the value read by the ADC when there
  // is no signal present. A voltage divider with precisely equal
  // resistors will presumably give a value of 511 or 512.
  // My divider gives a value of 506.
  // If you aren't sure what to use, set this to 512
 
  dtmf.detect(d_mags, 512);
  
  // detect the button
  // If it is recognized, returns one of 0123456789ABCD*#
  // If unrecognized, returns binary zero

  // Pass it the magnitude array used when calling .sample
  // and specify a magnitude which is used as the threshold
  // for determining whether a tone is present or not
  //
  // If N=64 magnitude needs to be around 1200
  // If N=128 the magnitude can be set to 1800
  // but you will need to play with it to get the right value
  thischar = dtmf.button(d_mags,1200.);

if (thischar) {
  chip.stop();
  delay(10);
  chip.g_erase();
  chip.pd();
  
  switch(thischar) {
/*
    case '1':
      DTMF_message = (1);

    case '5':
      DTMF_ply_msg = (1);
*/      
    case DTMF_SLP_WK:                        // Sleep/wake toggle
      if (DTMF_slp_wk == 0) {                // Go to sleep
        DTMF_slp_wk = (1);    
        RPT_status = (0);
        digitalWrite(PTT_pin, HIGH);
        delay(300);
        sendmsg(RPT_call);
        delay(300);
        digitalWrite(PTT_pin, LOW);

      } else {                       // Wake up

      DTMF_slp_wk = (0);
      RPT_status = (1);
      Beacon_RQ = (1);
      }
    }
  }
}


////////////   RESET  CONTROLLER  //////////////

void softReset(){
  chip.stop();
  delay(10);
  chip.g_erase();
  digitalWrite(PTT_pin, HIGH);     // PTT enable
  delay(300);
  sendmsg(RPT_call) ;                  //send call  
  sendmsg(" RESET");      //send reset msg 
  delay(300); 
  digitalWrite(PTT_pin, LOW);     // PTT disable
  chip.reset();                          // Power down chip
  delay(1500);
  
  asm volatile ("  jmp 0");          // start software from scratch
}
It should record/replay 60 seconds of 12 KHz audio, as well as broadcast a CW ID. The unit can also be put into a sleep (silent) mode and re-activated via DTMF tones. This project uses an ISD1700 series chip which provides an SPI interface, allowing increased precision when controlling the IC. If you choose to just use an atmega328 chip and forgo the cost of an arduino, the whole thing can be put together for less than $30 and run on 3.3/5v.

Future versions would expand functionality to include more robust DTMF controls, 'say again' message storing, and even coded mailboxes. This would probably require ditching the ISD chips in favor of a pair of NAND flash ICs, much like the Argent SR1.

Functions:
- Record/repeat up to 60 seconds of 12 KHz audio
- CW ID at 10 minute intervals when no traffic is being passed
- CW ID after each re-transmission over 10 seconds
- Busy signal on frequency when recording
- Overflow tone broadcast
- Sleep/Wake on DTMF tone
- Automatic rollover/soft reset
- Watchdog timer

Limitations:
- No transceiver control (must be configured separately)
- No message storing
- ~2.8 second global erase time on chip

ISD1700 arduino library: http://www.eatelier.nl/attachments/a...50/ISD1700.zip

ISD1700 Datasheet: http://www.eatelier.nl/attachments/a...0datasheet.pdf

ISD1700 Design guide: http://www.eatelier.nl/attachments/a...0Rev%201.1.pdf

DTMF library: http://members.shaw.ca/el.supremo/DTMF.zip
Can you share schematic of your prototype?
Quick reply to this message
Reply

Bookmarks

Tags
arduino, communications, diy, handheld gps, parrot, portable, repeater, simplex



Quick Reply
Message:
Options

Register Now

In order to be able to post messages on the Survivalist Forum forums, you must first register.
Please enter your desired user name, your email address and other required details in the form below.
User Name:
Password
Please enter a password for your user account. Note that passwords are case-sensitive.
Password:
Confirm Password:
Email Address
Please enter a valid email address for yourself.
Email Address:
Gender
Insurance
Please select your insurance company (Optional)

Log-in

Human Verification

In order to verify that you are a human and not a spam bot, please enter the answer into the following box below based on the instructions contained in the graphic.



Thread Tools
Display Modes

Posting Rules
You may post new threads
You may post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 08:23 AM.


Powered by vBulletin®
Copyright ©2000 - 2020, vBulletin Solutions, Inc.
Search Engine Optimisation provided by DragonByte SEO (Lite) - vBulletin Mods & Addons Copyright © 2020 DragonByte Technologies Ltd.
vBulletin Security provided by vBSecurity v2.2.2 (Pro) - vBulletin Mods & Addons Copyright © 2020 DragonByte Technologies Ltd.
Copyright © Kevin Felts 2006 - 2015,
Green theme by http://www.themesbydesign.net