Coding : Interrupt Driven RTTY Transmission on Arduino/AVR

One of the things on my to-do list has been convert the transmission of the RTTY into the background as an interrupt. When the payload is transmitting at 50 baud it effectively sits around doing nothing for 15 seconds whilst it transmits.

By moving this to an interrupt based routine it can transmit in the background whilst allowing the foreground  tasks to run. This means more data being logged, more accurate altitude measurements, faster response to a burst if needed etc.

Having had a good read of http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/ I set about coding up the code to do this one step at a time. Firstly I replicated the high/low code from http://ukhas.org.uk/guides:linkingarduinotontx2#part_1_-_test_circuit_and_test_code

After finding a few code timing errors I think I’ve sorted it out so it runs as a stand alone procedure which TX’s in the back ground. The RFM22B seemed to struggle to switch frequencies fast enough above 300 baud using radio1.setFrequency(RADIO_FREQUENCY_LOW)/radio1.setFrequency(RADIO_FREQUENCY_HIGH). Dave Akerman suggested (as recommended by Navrac) that using the shift frequency register is better way. Indeed it is much faster, results in a much cleaner signal and allowed the baud rate to decode fine up to 600.

So here is what I’ve come up with, it will need further testing and integration into my existing flight code.


/*
 Interrupt Driven RTTY TX Demo
 
 Transmits data via RTTY with an interupt driven subroutine.
 
 By Anthony Stirk M0UPU 
 
 October 2012 Version 5
 
 Thanks and credits :
 Evolved from Rob Harrison's RTTY Code.
 Compare match register calculation by Phil Heron.
 Thanks to : http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/
 RFM22B Code from James Coxon http://ukhas.org.uk/guides:rfm22b 
 Suggestion to use Frequency Shift Registers by Dave Akerman (Daveake)/Richard Cresswell (Navrac)
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 See <http://www.gnu.org/licenses/>.
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/crc16.h>
#include <SPI.h>
#include <RFM22.h>

#define ASCII 7          // ASCII 7 or 8
#define STOPBITS 2       // Either 1 or 2
#define TXDELAY 0        // Delay between sentence TX's
#define RTTY_BAUD 100    // Baud rate for use with RFM22B Max = 600
#define RADIO_FREQUENCY 434.198


#define RFM22B_SDN 3
#define RFM22B_PIN 10

char datastring[80];
char txstring[80];
volatile int txstatus=1;
volatile int txstringlength=0;
volatile char txc;
volatile int txi;
volatile int txj;
unsigned int count=0;


rfm22 radio1(RFM22B_PIN);

void setup()
{
  initialise_interrupt();
  setupRadio();
}

void loop()
{

  sprintf(datastring,"$$$$$M0UPU,%04u,RTTY TEST BEACON RTTY TEST BEACON",count); // Puts the text in the datastring
  unsigned int CHECKSUM = gps_CRC16_checksum(datastring);  // Calculates the checksum for this datastring
  char checksum_str[6];
  sprintf(checksum_str, "*%04X\n", CHECKSUM);
  strcat(datastring,checksum_str);
  delay(1000);
  count++;
}

ISR(TIMER1_COMPA_vect)
{
  switch(txstatus) {
  case 0: // This is the optional delay between transmissions.
    txj++;
    if(txj>(TXDELAY*RTTY_BAUD)) { 
      txj=0;
      txstatus=1;
    }
    break;
  case 1: // Initialise transmission, take a copy of the string so it doesn't change mid transmission. 
    strcpy(txstring,datastring);
    txstringlength=strlen(txstring);
    txstatus=2;
    txj=0;
    break;
  case 2: // Grab a char and lets go transmit it. 
    if ( txj < txstringlength)
    {
      txc = txstring[txj];
      txj++;
      txstatus=3;
      rtty_txbit (0); // Start Bit;
      txi=0;
    }
    else 
    {
      txstatus=0; // Should be finished
      txj=0;
    }
    break;
  case 3:
    if(txi<ASCII)
    {
      txi++;
      if (txc & 1) rtty_txbit(1); 
      else rtty_txbit(0);	
      txc = txc >> 1;
      break;
    }
    else 
    {
      rtty_txbit (1); // Stop Bit
      txstatus=4;
      txi=0;
      break;
    } 
  case 4:
    if(STOPBITS==2)
    {
      rtty_txbit (1); // Stop Bit
      txstatus=2;
      break;
    }
    else
    {
      txstatus=2;
      break;
    }

  }
}

void rtty_txbit (int bit)
{
  if (bit)
  {
    radio1.write(0x73,0x03); // High
  }
  else
  {
    radio1.write(0x73,0x00); // Low
  }
}

void setupRadio(){
  pinMode(RFM22B_SDN, OUTPUT);    // RFM22B SDN is on ARDUINO A3
  digitalWrite(RFM22B_SDN, LOW);
  delay(1000);
  rfm22::initSPI();
  radio1.init();
  radio1.write(0x71, 0x00); // unmodulated carrier
  //This sets up the GPIOs to automatically switch the antenna depending on Tx or Rx state, only needs to be done at start up
  radio1.write(0x0b,0x12);
  radio1.write(0x0c,0x15);
  radio1.setFrequency(RADIO_FREQUENCY);
  radio1.write(0x6D, 0x04);// turn tx low power 11db
  radio1.write(0x07, 0x08);
  delay(500);
}

uint16_t gps_CRC16_checksum (char *string)
{
  size_t i;
  uint16_t crc;
  uint8_t c;

  crc = 0xFFFF;

  // Calculate checksum ignoring the first two $s
  for (i = 5; i < strlen(string); i++)
  {
    c = string[i];
    crc = _crc_xmodem_update (crc, c);
  }

  return crc;
}    
void initialise_interrupt() 
{
  // initialize Timer1
  cli();          // disable global interrupts
  TCCR1A = 0;     // set entire TCCR1A register to 0
  TCCR1B = 0;     // same for TCCR1B
  OCR1A = F_CPU / 1024 / RTTY_BAUD - 1;  // set compare match register to desired timer count:
  TCCR1B |= (1 << WGM12);   // turn on CTC mode:
  // Set CS10 and CS12 bits for:
  TCCR1B |= (1 << CS10);
  TCCR1B |= (1 << CS12);
  // enable timer compare interrupt:
  TIMSK1 |= (1 << OCIE1A);
  sei();          // enable global interrupts
}

  1. Hi,
    I have found a GPS/Radiometrix interrupt programme that transmits RTTY. The string prints to Serial successfully and shows up on SDR with the typical signal, but it doesn’t yield an intelligible readout on dl-FLDigi. I am using a frequency agile NTX2B together with 4k7 + 4k7 + 32k resistors and am wondering whether I should obtain a straight forward NTX2B.

    My NTX2B FA does print and transmit RTTY signals from the two example NTX2B programmes without using resistors but doesn’t do this with the GPS/Radiometrix programme that I am using (basically, Stratodene 3 with a serial link).

    I am trying to access the irc resource, but I think I will need to get myself a web site to do this.

    Basically, I think I am struggling to ask:
    1. does the NTX2B FA do away with the need for resistors (it seems to with your example programmes).
    2. does a programme that requires an ordinary NTX2B (ie, needing a resistor driven shift) need tweaking when using the frequency agile version?

    Many thanks,

    Colin.

  2. Hi Colin,

    There is no difference between the stock NTX2B and the HAB Supplies NTX2B-FA other than the frequency agility. Both need the resistor network to generate the tones. Alternatively you can generate tone using PWM (Which may or may not need a resistor). The resistor network is generally simpler.

    NTX2B-FA are drop in replacements for the stock Radiometrix NTX2B so nothing will need changing.

    Regards

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Human test : * Time limit is exhausted. Please reload CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.