CTCSS/PL decoder, encoder control using arduino

I am building a CTCSS/PL encoder & decoder controller. I am using separate PL encoder and decoders. The decoder output is 5 bit binary output, and the encoder needs a 5 bit binary input to select the correct PL.

The decoder also provides a valid (HIGH) when decoding a valid PL.

I am using DDRx and PORTx for simultaneous read and write of pins. Speed of time is important.

The idea is to be able to decode one PL and encode a coorisponding one for use in a radio repeater

I wrote the sketch, but I think there is an issue with the PORTx input. I wish I could just have it read the pins attached to the encoder vs all 8 pins of the port.

All three PORT will be used. Eventually a LCD will be added.

Built using an UNO.

I think I need help with the PORT values.

pl_encode-decode2.ino (5.12 KB)

You are likely to get more and faster help if you post your code in accordance with the forum guidelines in the "how to use this forum-please read" stickies. Many members are reluctant to download code and it is inconvenient for those who do download it.

        if (validPL = 1)

This is wrong.
= is for an assignment statement
== is the comparison operator which is what you need here and where you test for zero.

Pete

P.S. before you post your code in code tags, use the Tools|Auto Format feature in the IDE to tidy the code's indentation.

Pete

I posted on my ipad and could figure out how to to post the code unless it was a file

I did fix the if (validPL == 1)

//This sketch was built to interface an Arduino UNO to a CTCSS (PL) decoder capable of decoding 25 PL tones and ouputing a 5 Bit value. Using this value a corrisponding PL
//could be selected by outputing a 5 Bit vale to a PL encoder using a dip switch to select HIGH or LOW.  the arduin would replace the dip switch on the PL encoder board.
//The decoder provides a Valid PL decode HIGH.  The skecth polls PORTD the decoder input, first looking for the valid HIGH.  Then decoding the 5 bit output to depertime the rxTone.
// With te rxTone decoded the arduino then ouputs a on PORTB the corrisponfing txTone.  Last on PORTC if the rxtone matches the linkTone A0 goes high activating a link radio.
// The idea is a radio repeater could operate standalone on one set of PL's or link to other repeaters if a different set of PL's were used.
//  Port manipulation was used to expided the reading a writing of the inputs and outputs since this all needs to be done similtaniously.
//CS-TECH PL Binary                  Arduino Pin    CSTech Pin
int rxTone1 = 00000110;  //67.0Hz     //      2              6
int rxTone2 = 00001010;  //69.3Hz     //      3              14
int rxTone3 = 00001110;  //71.9Hz     //      4              13
int rxTone4 = 00010010;  //74.4Hz     //      5              12
int rxTone5 = 00010110;  //77.0Hz     //      6              11
int rxTone6 = 00011010;  //79.7Hz     //      7               4  Valid Tone HIGH
int rxTone7 = 00011110;  //82.5Hz
int rxTone8 = 00100010;  //85.4Hz
int rxTone9 = 00100110;  //88.5Hz
int rxTone10 = 00101010;  //91.5Hz
int rxTone11 = 00101110;  //94.8Hz
int rxTone12 = 00110010;  //97.4Hz
int rxTone13 = 00110110;  //100.0Hz
int rxTone14 = 00111010;  //103.5Hz
int rxTone15 = 00111110;  //107.2Hz
int rxTone16 = 01000010;  //110.9Hz
int rxTone17 = 01000110;  //114.8Hz
int rxTone18 = 01001010;  //118.8Hz
int rxTone19 = 01001110;  //123.0Hz
int rxTone20 = 01010010;  //127.3Hz
int rxTone21 = 01010110;  //131.8Hz
int rxTone22 = 01011010;  //136.5Hz
int rxTone23 = 01011110;  //141.3Hz
int rxTone24 = 01100010;  //146.2Hz
int rxTone25 = 01100110;  //151.4Hz
// Comspec TE-32P PL Binary              Arduino Pin    TE-32 Pin
int txTone1 = 00000000;  //67.0Hz    //         8             1
int txTone2 = 00000001;  //71.9Hz    //         9             2
int txTone3 = 00000010;  //74.4Hz    //         10            3
int txTone4 = 00000011;  //77.0Hz    //         11            4
int txTone5 = 00000100;  //79.7Hz    //         12            5
int txTone6 = 00000101;  //82.5Hz    //         13
int txTone7 = 00000110;  //85.4Hz
int txTone8 = 00000111;  //88.5Hz
int txTone9 = 00001000;  //91.5Hz
int txTone10 = 00001001;  //94.8Hz
int txTone11 = 00001010;  //97.4Hz
int txTone12 = 00001011;  //100.0Hz
int txTone13 = 00001100;  //103.5Hz
int txTone14 = 00001101;  //107.2Hz
int txTone15 = 00001110;  //110.9Hz
int txTone16 = 00001111;  //114.8Hz
int txTone17 = 00010000;  //118.8Hz
int txTone18 = 00010001;  //123.0Hz
int txTone19 = 00010010;  //127.3Hz
int txTone20 = 00010011;  //131.8Hz
int txTone21 = 00010100;  //136.5Hz
int txTone22 = 00010101;  //141.3Hz
int txTone23 = 00010110;  //146.2Hz
int txTone24 = 00010111;  //151.4Hz
int txTone25 = 00011000;  //156.7Hz
int validPL = PD7;  // Input from CS-Tech Decoder valid PL (HIGH)
int rxPL;
int linkTone24 = 01011010;  // 146.2Hz used to link radios.
void setup() {
  Serial.begin (9600);
  //This code takes advantage of the fact that the control registers DDRD, DDRB, DDRC.
  //Each contain 8 bits that determine whether a given digital pin is output (1) or input (0).
  // set pins 1 (serial transmit) and 2..7 as output, but leave pin 0 (serial receive) as input (otherwise serial port will stop working!) ...
  DDRD = B00000010;  // digital pins 7,6,5,4,3,2  without changing the value of pins 0 & 1, which are RX & TX
  DDRD = (0 << PD7); // Pin 7 is an input by it self. <<Valid PL decode>>
  DDRB = B00111111;  // // set digital pins -,-,13,12,11,10,9,8
  DDRC = (1 << PC0); // Analog port 0 input.
  PORTD &= B00000011;   // Turn off digital output pins 2..7 ..., but leaves pins 0 and 1 alone
  PORTB = B00111111;   // turns on -,-,13,12,11,10,9,8. The upper 2 bits in DDRB (PORTB) are not used, because there is no such thing is digital pin 14 or 15
  PORTB = B00010110;  // Sets PL encoder to 146.2Hz.
  PORTC = (0 << PC0); // Analog Port 0 OFF  // think about DDRC/PORT A0-A5 and other iputs outputs.
}
void adjust(int validPL, int rxPL, int rxTonex, int txTonex, int linkTonex) {
  validPL = (PIND & (PD7)); {  // Reads Pin 7 and stores the value.
    if (validPL == 1)
      Serial.print("Valid CTCSS");
    {
      rxPL = (PORTD);// Read Pins2-6 from Decoder I wan tto read only pins 2-7
      Serial.print(rxPL);
      if (rxTonex == rxPL); { // DOES rxPL read match rxTone table??
        PORTB = txTonex;  // Set encode PL
        linkTonex == rxTonex; {
          PORTC = (1 << PC0);
        }
        linkTonex != rxTonex; {
          PORTC = (0 << PC0);
        }
      }
    }
  }
  {
    if (validPL = 0)
    {
      delay(0);  //Kepps looking for a valid tone.
    }
  }
}
void loop() {
  adjust(validPL, rxPL, rxTone15, txTone23, linkTone24); // Local repeat NO LINK
  adjust(validPL, rxPL, rxTone24, txTone13, linkTone24); // Link repeat LINKED
}

And this?

    if (validPL = 0)

Pete

      if (rxTonex == rxPL); { // DOES rxPL read match rxTone table??

The semicolon terminates the if statement. Get rid of it.

You have a couple of statements like this:

        linkTonex == rxTonex; {

I presume that should be:

        if(linkTonex == rxTonex) {

Pete

Pete,
I will make those changes. Thank you.

What I can’t figure out is how to read just 5 bits?

I really only want to read pins 2-6. PORTDreads the entire 8 bits. Pin 0 and 1 I don’t use and pin 7 is for the valid PL read.

Same for writing PORTB. I only write pins 8-12. I think I am going to need to output pin 13 independently before I am done with this project.

In the rxTone and txTone tables I created and 8 bit value but I really only need a 5 bit value.

  validPL = (PIND & (PD7)); {  // Reads Pin 7 and stores the value.
    if (validPL == 1)
      Serial.print("Valid CTCSS");
    {

There are a couple of problems with this code. First, it won't work unless PD7 is defined to be 1, which is unlikely. PD7 is probably defined to be 0x80 in which case the result of (PIND & (PD7)) will be 0x80 if there's a valid PL tone. You could change the if statement to be

  if(validPL)

In this case, if there's a valid PL then validPL will be 0x80 which is non-zero and the if condition will be true. If there's isn't a valid PL, validPL will be zero and the if condition will be false.
The other problem is with the placement of the brace { after the Serial.print. I think it should be like this:

  validPL = (PIND & (PD7)); {  // Reads Pin 7 and stores the value.
    if (validPL) {
      Serial.print("Valid CTCSS");

I really only want to read pins 2-6. PORTDreads the entire 8 bits.

In that case, use a mask to zero the bits you aren't interested in and then shift the 5 bits that you do want down two places to the right.

      rxPL = (PORTD & 0b01111100) >> 2;

Pete

I think I understand what you are doing. The receiver detects a CTCSS tone, say 74.4Hz and then on the Tx side this 74.4Hz is relayed to the transmitter. But the decoder and encoder don't use the same 5-bit code for the tones so the Arduino translates the received code to whatever is required on the Tx side to generate the same tone.
If so, it would be easy to do this with an array. When a valid tone is received, use the line of code from my previous message to read the decoded tone:

rxPL = (PORTD & 0b01111100) >> 2;

The value of rxPL will then correspond to the decoded tone. For example, if the tone is 74.4Hz then rxPL will be 4 (00100). rxPL can then be used as an index into an array to map this code into the one required for the output.
The array would be something like this:

uint8_t dec_2_enc[25] = {
           0,   // 67.0
  0b10000000,   //invalid - The cs-tech decodes 69.3Hz but the COMSPEC does not have this tone
  0b00000001,   // 74.4
  0b00000010,   // 77.0
  0b00000011,   // 79.7
  etc.
};

and you would translate the input code to output code like this:

  out_code = dec_2_enc[rxPL - 1];
  if(out_code == 0b10000000) {
    // Handle the error here. Input CTCSS 69.3 can't be handled on output
  }

The -1 is because the decoded codes start at 1 but, in C, the array index starts at zero.

Pete

Hi,

I really only want to read pins 2-6. PORTDreads the entire 8 bits. Pin 0 and 1 I don't use and pin 7 is for the valid PL read.

Look at this link, it shows an example of what you need.

https://www.arduino.cc/en/Tutorial/BitMask

Also google arduino port masking

"bit masking" is the process you need to only change/read specific bits of a port.

Tom.. :slight_smile:

Pete,
Almost. yes the 5 bit values to not match up so that makes it a little harder. I am in effect trying to emulate a community tone panel but using Arduino. I don't necessarily want the receive CTCSS and transmit CTCSS tones to be the same.

I want to be able to set them independently, thus why I was using the adjust(). In the repeater configuration I am building there are three types of users,

Local which only use this repeater by it self

Local Private which use separate TX and RX PL's

Link which that cause the repeater to link to other repeaters out of the area.

I will probably end up configuring the Local private and Link TX PL to be the same, but the RX PL's will be different. The Local Private and Link Users don't want to always listen to the Local users.

Essentially I am emulating a community repeater tone panel that allows lots of users to access and repeater but no hear anyone other than those that share their RX and TX PL, but adding the logic outputs to switch a link radio on and off.

I though the best way to be able to configure this would be to use the adjust() that way it would be simple to reconfigure it if I wanted to change the PL later.

First, a fix. Some of the comments are wrong. It should be:

uint8_t dec_2_enc[25] = {
           0,   // 67.0
  0b10000000,   //invalid - The cs-tech decodes 69.3Hz but the COMSPEC does not have this tone
  0b00000001,   // 71.9
  0b00000010,   // 74.4
  0b00000011,   // 77.0
  etc.
};

Are the tones for Local users different from those of Local Private? And are the tone(s) used by Link users different from the others too?
If so, the one array can be used for them all. The array can, for example, map 67.0 input to 67.0 output for those that use the same Rx and Tx tone. A different input and output tone can be set, like this:

uint8_t dec_2_enc[25] = {
           0,   // 67.0 in, 67.0 out
  0b10000000,   //invalid - The cs-tech decodes 69.3Hz but the COMSPEC does not have this tone
  0b00000001,   // 71.9 in,  71.9 out
  0b00000111,   // 74.4 in,  88.5 out
  0b00011001,   // 77.0 in, 162.2 out
  etc.
};

Pete