NewSoftSerial Library: An AFSoftSerial update

Ah, I was using pin3 (isn't that TX?) but the signal is on Pin2 and very strong.

You need to say more about how the data you are receiving is formatted. If you have control over the sending side you could send a unique character to indicate the start of the data. If the data is not fixed length, you could also send an end of data character.

Well that was q and p intended for, maybe its the NSS' limitation?

@grindylow, could you post some lines of the data you are sending and also show what the sketch with NSS displays.

This one sends compass data and uses hardware serial:

#include <Wire.h>
#include <CMPS03.h>

CMPS03 compass;

int compassReading;
int led = 13;
int btnRead;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  pinMode(led, OUTPUT);
  delay(1000);
}

void loop()
{
  compassReading = compass.read();
  Serial.print('q');
  Serial.print(compassReading, DEC);
  Serial.print('p');
  if(Serial.available() > 0)
    btnRead = Serial.read();
  
  if(btnRead == 'a') digitalWrite(led, HIGH);
  else digitalWrite(led, LOW);
}

This one receives data and uses NSS. It also displays serial output to the serial monitor:

#include <NewSoftSerial.h>

const byte RX = 2;
const byte TX = 3;
const byte led = 13;
const byte button = 4;
NewSoftSerial mySerial(RX, TX);
char inData;
int data = 0;

void setup()
{
  Serial.begin(9600);
  mySerial.begin(9600);
  pinMode(led, OUTPUT);
  pinMode(button, INPUT);
  delay(1000);
}

void loop()
{
  if(mySerial.available() >= 6 && mySerial.read() == 'q')
  {
    data = 0;
    inData = 0;    
      while(inData != 'p')
      {      
        inData = mySerial.read();
        if(inData >= '0' && inData <= '9')
          data = data * 10 + inData - '0';
      }
      Serial.println(data, DEC);
  }

  if(digitalRead(button) == HIGH)
  {
    mySerial.print('a');
    digitalWrite(led, HIGH); }
  else
  {
    mySerial.print('b');
    digitalWrite(led, LOW); }
}

Look for my post above for the display on the serial monitor.

thanks..

It may also be worth trying to add a delay in your loop code to see if that helps. Try delay(100);

If not, try running a test that sends known data:

void loop()
{
  Serial.print('q');
  Serial.print(1234, DEC);
  Serial.print('p');
  if(Serial.available() > 0)
    btnRead = Serial.read();
  
  if(btnRead == 'a') digitalWrite(led, HIGH);
  else digitalWrite(led, LOW);
}

Then post the results.

Perhaps this is a silly question...

Is there any reason why the NewSoftSerial library busy waits in the interrupt handler? Surely this is pretty inefficient, and obviously has major challenges at very low baud rates.

Is there a reason why you couldn't utilise the timer and pin change interrupts to do things slightly differently?

For the moment I'm thinking about receive (although you could do the same for xmit), there are two approaches that I can think of...

  1. Interrupt on the first pin change (as done currently), then set a timer to overflow in the middle of each following bit and use the interrupt to read the value. Once you get the stop bit you can disable the timer and save the byte in the buffer.

  2. Possibly a bit more complex, but potentially less overhead, interrupt on the first pin change, set a timer to end on the stop bit, track any state changes in between and create the byte according to the time when the state change happened (using the timer countdown value).

With some clever timer work I would have thought you could get to a point where you can run a couple of concurrent software serial lines at reasonable speeds, and generally have much more available cpu time for the non-interrupt code.

I am very new to the Arduino, so I'm probably missing something fundamental, but if not I'll have a go at hacking something together to test the theory.

Lee.

Lee, that's not a silly question at all.

About a year ago a couple of us investigated doing something like this and actually developed some code that demonstrated the workability at low baud rates. The problem is that higher baud rates demand a level of timing precision that the Arduino cannot provide. Let's say you've just received 6 bits of your byte and are eagerly waiting for that interrupt to fire, signaling the arrival of the 7th. But wait -- another timer or interrupt has fired first and yours is delayed. At higher speeds it doesn't take much delay to cause the bit to be read after it has already changed values -- corrupting the data stream.

Thanks for the input.

Mikal

Mikal, thanks for the response.

Very interesting ... but that suggests to me that my second option would still work, in that model you are only really interested in pin transitions and providing you log the time they happen it almost doesn't matter when you come back to analyse the data and recreate the stream. In fact the timer is only really needed to ensure that you can capture the last duplicate-high bit if there is no following data.

i.e.

pin goes low, so this is the beginning of the start bit, start a timer for 10.5 bits time (middle of stop bit), set data to 00000000.
pin goes high, look at the time, this is the third bit, toggle 3rd bit onwards, data becomes 00111111.
ping goes low, look at the time, this is the 7th bit, toggle 7th bit onwards, data becomes 00111100.
etc.

In this case the stop bit would trigger an interrupt so the timer wouldn't actually be needed, but if the data ended in 1's, then the stop bit wouldn't trigger and therefore the timer would be needed.

Worst case, if somehow the timer is way delayed, we get the new start bit trigger and have to deal with the previous byte first.

The pin change interrupts are higher priority than all the timers, so it only leaves the external interrupts to give you a concern.

I may be flogging a dead horse, but it's been years since I've played with this kind of stuff and I seem to have turned into a kid again :slight_smile:

Lee.

Ok, I've built a very rough example (with loads of hardcoding and assumptions) ... this seems to work nicely at 9600 baud ... it's actually all hardcoded at that speed for the moment. Obviously there's a whole lot of things that could be done to make this more efficient, and there's a few more error checks that would be needed (like getting a new start bit when the timer hadn't gone off etc) which would also make it more resilient against major timer issues.

I would have thought you could get this to use Timer0 (without changing wiring.c) with some clever use of the compare interrupts, but I don't think the standard 64 clock cycle resolution would cut it for much faster than 9600, although I expect you could manage multiple 9600's ok -- that will be my next test as well as seeing how fast I can push this.

If you needed to support multiple serial lines at different rates, then the timer mechanism would need to get more sophisticated.

Please don't laugh at my code ... this was done in a hurry, I did borrow some from NewSoftSerial, and it's my first Arduino code (other than noddy examples) ...

#include <avr/interrupt.h>
#include "pins_arduino.h"

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


#if !defined(digitalPinToPCICR) // Courtesy Paul Stoffregen
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
#define digitalPinToPCICR(p)    (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)NULL))
#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1))
#define digitalPinToPCMSK(p)    (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)NULL))))
#define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 13) ? ((p) - 8) : ((p) - 14)))
#else
#define digitalPinToPCICR(p)    ((uint8_t *)NULL)
#define digitalPinToPCICRbit(p) 0
#define digitalPinToPCMSK(p)    ((uint8_t *)NULL)
#define digitalPinToPCMSKbit(p) 0
#endif
#endif

uint8_t            bit = 0;          // 0 = start bit, 1-8 = data, 9= stop bit
volatile uint8_t   data = 0;         // the byte we are reading

// This is a noddy buffer to store the input... no overflow at the moment
uint8_t          buffer[32];
int              p_save = 0;
int              p_get = 0;
volatile int     avail;

// Quick way of twiddling the right bits given the bit coming in...
uint8_t          bitmasks[8] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };

// Bits for the port access...
int              port;
volatile uint8_t *portRegister;
uint8_t          bitMask;

// This will be called for a pin state change, but we'll also call it if the timer
// goes off...
inline void handle_interrupt()
{
  uint8_t      val = *portRegister & bitMask;

  // If we're the first signal the we are expecting a start bit (low), anything else
  // we just ignore.
  if(bit == 0) {
     if(val) return;
     
     data = 0;
     bit++;
     
     // Allow the timer to run...
     TCNT2 = 255-247;     // 247 = 988us which is 9.5 bits
     sbi(TIFR2, TOV2);    // this clears the overflow flag to stop instant interrupts.
     sbi(TIMSK2, TOIE2);
  } else {
    // This is a bit hacky ... we'll count what time the bit comes in using the timer
    // but we'll allow 13 timer ticks (52us) of flexibility, this is good since we
    // can cope with timing problems, but it does mean the timer will probably come in
    // at bit 10 (or wrap to bit 0 in this calc...)
     bit = (TCNT2 - ((255-247) - 13)) / 26;
     if(bit == 9 || bit == 0) {
        // clear the timer...
        cbi(TIMSK2, TOIE2);
        bit = 0;
        buffer[p_save] = data;
        p_save++;
        if(p_save == 32) p_save = 0;
        avail++;
        return; 
     }
     
     if(!val) {
         data &= ~bitmasks[bit-1];
     } else {
         data |= bitmasks[bit-1];
     }     
  }
}

#if defined(TIMER2_OVF_vect)
ISR(TIMER2_OVF_vect)
{
    cbi(TIMSK2, TOIE2);
    // pretent we've had a signal change...
    handle_interrupt();
}
#endif

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  handle_interrupt();
}
#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
  handle_interrupt();
}
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
  handle_interrupt();
}
#endif


int rx_pin = 5;
int tx_pin = 6;


void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
 
  pinMode(rx_pin, INPUT);    // RX
  pinMode(tx_pin, OUTPUT);   // TX
  digitalWrite(rx_pin, HIGH);    // pullup for normal logic
  
  *digitalPinToPCICR(rx_pin) |= _BV(digitalPinToPCICRbit(rx_pin));
  *digitalPinToPCMSK(rx_pin) |= _BV(digitalPinToPCMSKbit(rx_pin));
  port = digitalPinToPort(rx_pin);
  portRegister = portInputRegister(port);
  bitMask = digitalPinToBitMask(rx_pin);
  
  // Let's mess around with timer2....
  // Need to set it to normal mode, since the wiring.c code will set phase correct PWM
  cbi(TCCR2A, WGM20);
  
  // Let's make sure we have normal port operation
  cbi(TCCR2A, COM2A0);
  cbi(TCCR2A, COM2A1);
  cbi(TCCR2A, COM2B0);
  cbi(TCCR2A, COM2B1);
  
  // We'll set the /64 scaler, since this gives us 4us per tick on a 16Mhz board, so the
  // overflow will happen at 1024us ... we need 988us for a 9.5 bit 9600 clock
  // probably already set by wiring, but we'll do it anyway
  sbi(TCCR2B, CS22);
  
}

int by;

void loop() {
  while(avail > 0) {
    // This is a "get" routine ... but it's in here for now...
    cli();
    by = buffer[p_get];
    p_get++; if(p_get == 32) p_get = 0;
    avail--;
    sei();
    
    Serial.print(by, BYTE);
  }
  delay(100);
 }

Lee.

Hi, I was happy to find the NewSoftSerial and I'm trying to implement it in my Code, using a rfid-reader(ID20) to a trigger a soundfile on a mp3-decoder(Vmusic2).

It works but it's really slow -it takes around 10 seconds to process what didn't happened to me with the old softwareSerial which reacted instantly (but i could only use 1 soft serial).

What seems strange to me is that my LED test light (Line 57 - first part of the loop) goes high, but the delay instead of its 500ms works for around 10 seconds before it goes low again. Seems like it suspends, but i don't understand what could be happening there...??

Any guess what's wrong?

(obviously i'm a newbie and thats just my second code i write..)
thanks a lot
flo

#include <NewSoftSerial.h>
/*
  Microcontroller RFID Reader
  Language: Wiring/Arduino

  Reads data serially from a (Parallax or) ID Innovations ID12 
  RFID reader.
*/
#define ID20_RX 2
#define ID20_TX 3

#define VMUSIC_RX 7
#define VMUSIC_TX 8

#define tagLength 10    // each tag ID contains 10 bytes
#define startByte 0x02  // for the ID Innovations reader, use 0x02
#define endByte 0x03    // for the ID Innovations reader, use 0x03
#define dataRate 9600   // for the ID Innovations reader, use 9600


//set up a new serial port
NewSoftSerial musicSerial = NewSoftSerial(VMUSIC_RX, VMUSIC_TX);
NewSoftSerial idSerial = NewSoftSerial(ID20_RX, ID20_TX);

char val = 0; // TEST variable to store the data from the serial port
int LED = 4;  // TEST testLED
int test = 333; // TEST variable

char tagID[tagLength + 1];            // array to hold the tag you read
int tagIndex = 0;                 // counter for number of bytes read
int tagComplete = false;          // whether the whole tag's been read
char rotundrund[] = "3A0082556D";
char weissekarte[] = "460071B5EC";

int tracknumber = 0;

void setup() {
  Serial.begin(dataRate);  
  
  // define pin modes for tx, rx:
  pinMode(VMUSIC_RX, INPUT);
  pinMode(VMUSIC_TX, OUTPUT);
  pinMode(ID20_RX, INPUT);
  pinMode(ID20_TX, OUTPUT);
  pinMode(LED, OUTPUT); 
  
  // set the data rate for the SoftwareSerial port
  musicSerial.begin(9600);
  idSerial.begin(9600);
}

void loop() {
  
  // read in and parse serial data:
  if (idSerial.available() > 0) {
    readByte();
    digitalWrite(LED, HIGH);
    delay(500);
    digitalWrite(LED, LOW);
  } 
// if - check ob tag schon da ist, sonst nicht ausdrucken 
  if(tagComplete == true) {
    Serial.println(tagID);

// in Array reinschreiben  -> Neue Funktion  

    tagComplete = false;

    if (strcmp(tagID, rotundrund) == 0) {
//        Serial.println("rot und rund");    
        // read from Vmusic
//        Serial.println("playing track 2 "); //ich glaube musicserial eht hier nicht weil nict aktiviert
//        musicSerial.print("VPF 002.mp3");
//        musicSerial.print(0x0D,BYTE); 
    }
    else if (strcmp(tagID, weissekarte) == 0) {
      Serial.println("weisse karte");
    }
    else {
      Serial.println("unbekannte ID");
    }

    
  }
// Array abchecken mit PlayList -> Neue Funktion

// ------------- Warum druckt es immer wieder aus? Kann man das stoppen? ---------------
}

/*
  This method reads the bytes, and puts the 
 appropriate ones in the tagID
 
 */
void readByte() {
  char thisChar = idSerial.read();
//Serial.print(thisChar, HEX); // TEST: read whole tagstrip with StartByte & Endbyte
  switch (thisChar) {
  case startByte:     // start character
    // reset the tag index counter
    tagIndex = 0; 
    break;
  case endByte:    // end character 
    tagComplete = true;  // you have the whole tag
    tagID[tagLength + 1] = '\0';
    break;
  default:    // any other character
    tagComplete = false;  // there are still more bytes to read
    // add the byte to the tagID
    if (tagIndex < tagLength) {
      tagID[tagIndex] = thisChar; 
      // increment the tag byte counter
      tagIndex++;
    }
    break;
  }
}

Could it be something to do with repeated calls to "delay (500);"?
Like, a half-second delay for every received character?

Eoo, Great hint, that was it!
Now it works.
Thanks a lot, Groove.

There's another thing i can't explain myself: After uploading the code on to the arduino, i use the first tag and the LED shows me that it reads it, but in the serial window nothing appears (it should write the tag number and a string with the tagname). The next tag i give him to read (or the same tag again) it reads and writes now as it should without problems. I tried that out several times and i reloaded the code as well, but it's always the same.
And when i open up the serial monitor the monitor bar below says "closing..."

What might that be?

@Lee:

I'm very interested in your entirely interrupt-driven serial port. I had also had this idea, but was too daunted to implement it myself.

Have you developed it any further? Have you come across particular problems, or know perhaps where the threshold of compromise appears, when the increased interrupts start to consume as much resources as the polling of the original NewSoftSerial?

Thanks most kindly,
Robert.

very good post, thanks ..........

I recently purchased a cellular shield with SM5100B GPRS module from sparkfun. I then downloaded the sketch "cellular shield throughpass" from the sparkfun website, my problem is that when I try to compile it I get a error.

Because "Newsoftserial" is not included in my import library, I've being using "#include <SoftwareSerial.h> at the beginning of the sketch instead, i then get a " error 'newsoftserial' does not name a type".

Any help on this would be much appreciated,

Because "Newsoftserial" is not included in my import library,

So, download it. NewSoftSerial | Arduiniana

I've being using "#include <SoftwareSerial.h> at the beginning of the sketch instead,

How is that supposed to help?

i then get a " error 'newsoftserial' does not name a type".

Well, NewSoftSerial would name a type, if you included the proper header file.

I'm trying to create a solution to allow gauges to send data to a PC or handheld device wirelessly.

NewSoftSerial has been great to allow me to put my XBee module to sleep, and wake it upon receiving data from a gauge. (Very important for battery life)

I have it working with basic serial (Tx, Rx, Grnd).
However, I have a device that needs RTS, CTS support. I have since had to purchase a shifter that uses the MAX232 board so I can have access to those lines.

I'm using the Arduino FIO board. Below are the pins I'm using, and what I'm using them for.

D2 = Rx (Gauge Rx)
D3 = Tx (Gauge Tx)
D4 = CTS (XBee CTS)
D5 = DTR (XBee DTR)
D6 = RTS (Gauge RTS)
D7 = CTS (Gauge CTS)

The particular gauge I'm trying to connect to is the Tohnichi CTB2 using their PC output.

It seems they actually route their "RTS" to the CD (Pin 1 of a DE9), and CTS gets Pigtailed into the Pin 7 and 8 of the DE9. I have a modified cable that is wired just the way they had their RS232 cable wired. I can post some excel files once I'm allowed to do so that will show the pin outs and cabling I'm using. I'm thinking I may need to re-wire my cable, but I'm not positive at this point. I also know that NewSoftSerial doesn't support RTS/CTS.

I finally found Jin's code where he had some RTS/CTS stuff in with a TimerSerial setup. I'm not sure how to adapt that to be used with the latest NewSoftSerial. I would be more than happy to make those changes myself, but I don't know what they are, and I would like some ideas on the direction to take for the cable. I did put the RTS and CTS lines to their correct locations and I'm getting a CTS: High, and RTS: Low while it's just sitting there. I don't seem to see those change. Even when I try to send data from the wrench. :-/

Also here's my sketch. I can't post code in my first post?

Obviously I have some code in there checking those pins. However, they both are coming in as the same thing, and never change except when I change them. Though I just tested with having changed the cable, and I'm getting values as described above, but still nothing changing when I send data FROM the wrench TO the Arduino FIO.

Thanks for ANY and ALL suggestions!! I'm trying to get this done in the next day or two.

Thanks,
Jarvis

Here's the code portion of the above post since I couldn't post it.

/*
Arduino FIO + XBee Radio + RS-232 Breakout (DB9 Female)
Created this to allow the XBee radio to sleep. Gauges can't
wake the module. The FIO board wakes the radio.

Below are the steps this sketch should take to work properly

1. When serial data is recieved. Hold the Data
2. Wake up the XBee Radio (Set DTR(PN D5) HIGH)
3. Wait for XBee to signal it's ready. (Check CTS(PN D4) LOW)
4. Wait a few mili seconds.
5. Send received serial data through XBee

Added RTS/CTS support through setting the pins high and low.

Created by: Jarvis J Stubblefield
Created on: 2010/11/05
Updated on: 2010/11/12
*/

#include <NewSoftSerial.h>
#include <avr/power.h>
#include <avr/sleep.h>

//Pin CONSTANTS
const byte pinRx   = 2;
const byte pinTx   = 3;
const byte pinCTS  = 4;
const byte pinDTR  = 5;
const byte pinGRTS = 6;
const byte pinGCTS = 7;

//String for Serial Data
String strData;

//Counter for Auto-Sleep
int intCounter;

//Soft Serial for Storing Data
NewSoftSerial objGauge(pinRx, pinTx); //This function takes care of setting pinMode

void setup() {
  pinMode(pinGRTS, INPUT);
  digitalWrite(pinGRTS, LOW);
  pinMode(pinGCTS, OUTPUT);
  digitalWrite(pinGCTS, HIGH);
  pinMode(pinDTR, OUTPUT); //Set DTR to be an OUTPUT pin
  digitalWrite(pinDTR, HIGH); //Set pin to HIGH so we can wake with LOW
  digitalWrite(pinCTS, HIGH); //Set pin to HIGH so we can later wake and check for LOW
  Serial.begin(9600);
  Serial.println("Setup almost done.");
  objGauge.begin(9600);
  intCounter = 0;
}

void loop() {
  if (digitalRead(pinGRTS) == HIGH) {
    Serial.println("RTS: HIGH");
  }
  else if (digitalRead(pinGRTS) == LOW) {
    Serial.println("RTS: LOW");
    digitalWrite(pinGRTS, HIGH);
  }
  if (digitalRead(pinGCTS) == HIGH) {
    Serial.println("CTS: HIGH");
  }
  else if (digitalRead(pinGCTS) == LOW) {
    Serial.println("CTS: LOW");
    digitalWrite(pinGCTS, HIGH);
  }
  
  if (objGauge.available())
  {
    char chrIncoming;
    
    //Serial Data is coming in. Wake up the XBee.
    if (digitalRead(pinDTR) == HIGH) {
      digitalWrite(pinDTR, LOW);
    }
    
    //Read in the characters until you have a full string.
    while (objGauge.available()) {
      chrIncoming = (char)objGauge.read();
      strData += chrIncoming;
      // If you find a LFCR or CRLF then you have a full reading.
      if (strData.endsWith("\n\r") || strData.endsWith("\r\n")){
        break;
      }
    }
    
    delay(50); //Delay to wait for XBee to wake.
    //Check for the radio to be awake (CTS = LOW)
    if (digitalRead(pinCTS) == LOW) {
      if (strData.length() > 0) {
        Serial.print(strData);
        strData = ""; //Prepare for new data coming in.
        delay(50); //Wait a bit then sleep again.
        digitalWrite(pinDTR, HIGH);
        Serial.flush(); //Flush the serial stream.
        objGauge.flush(); //Flush the gauge stream.
      } //End strData has length
    } //End pinCTS == LOW
  } //End objGauge.available()
  delay(100); //Sleep a little between looping (ms)
  intCounter++;
  
  if (intCounter == 10)
  {
    intCounter = 0; //Reset counter
    //sleepNow();
  }
}

void sleepNow()
{
    /* Now is the time to set the sleep mode. In the Atmega8 datasheet
     * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
     * there is a list of sleep modes which explains which clocks and 
     * wake up sources are available in which sleep modus.
     *
     * In the avr/sleep.h file, the call names of these sleep modus are to be found:
     *
     * The 5 different modes are:
     *     SLEEP_MODE_IDLE         -the least power savings 
     *     SLEEP_MODE_ADC
     *     SLEEP_MODE_PWR_SAVE
     *     SLEEP_MODE_STANDBY
     *     SLEEP_MODE_PWR_DOWN     -the most power savings
     *
     *  the power reduction management <avr/power.h>  is described in 
     *  http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
     */

  set_sleep_mode(SLEEP_MODE_IDLE);   // sleep mode is set here

  // enables the sleep bit in the mcucr register so sleep is possible. just a safety pin 
  sleep_enable(); 

  //Turn OFF unused modules to pull even less power.
  power_adc_disable();
  power_spi_disable();
  power_timer0_disable();
  power_timer1_disable();
  power_timer2_disable();
  power_twi_disable();
  
  sleep_mode(); // here the device is actually put to sleep!!
 
// THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
  sleep_disable(); // first thing after waking from sleep: disable sleep...

  power_all_enable(); // Turn EVERYTHING back on.
}

Thanks again for all help and suggestions,
Jarvis

Here's the code that I have now. It gives me a RTS: HIGH, CTS: HIGH only SOMETIMES when I press the send reading button on the gauge.

Mostly it stays like this...

RTS: LOW, CTS: LOW
RTS: LOW, CTS: HIGH
RTS: LOW, CTS: LOW
RTS: LOW, CTS: HIGH
RTS: LOW, CTS: LOW

Occasionally when I press the button, I get the following...

RTS: LOW, CTS: LOW
RTS: HIGH, CTS: HIGH
RTS: LOW, CTS: LOW
RTS: LOW, CTS: HIGH

I'm guessing this is because I probably need interrupts on the RTS/CTS not checking during my loop. I'm unsure how to get this implemented, but I'm looking at the TimerSerial by Jin to see if I can figure it out.

Here's my current code generating the above output.

/*
Arduino FIO + XBee Radio + RS-232 Breakout (DB9 Female)
Created this to allow the XBee radio to sleep. Gauges can't
wake the module. The FIO board wakes the radio.

Below are the steps this sketch should take to work properly

1. When serial data is recieved. Hold the Data
2. Wake up the XBee Radio (Set DTR(PN D5) HIGH)
3. Wait for XBee to signal it's ready. (Check CTS(PN D4) LOW)
4. Wait a few mili seconds.
5. Send received serial data through XBee

Added RTS/CTS support through setting the pins high and low.

Created by: Jarvis J Stubblefield
Created on: 2010/11/05
Updated on: 2010/11/12
*/

#include <NewSoftSerial.h>
#include <avr/power.h>
#include <avr/sleep.h>

//Pin CONSTANTS
const byte pinRx   = 2;
const byte pinTx   = 3;
const byte pinCTS  = 4;
const byte pinDTR  = 5;
const byte pinGRTS = 6;
const byte pinGCTS = 7;

//String for Serial Data
String strData;

//Counter for Auto-Sleep
int intCounter;

//Soft Serial for Storing Data
NewSoftSerial objGauge(pinRx, pinTx); //This function takes care of setting pinMode

void setup() {
  pinMode(pinGRTS, INPUT);
  digitalWrite(pinGRTS, LOW);
  pinMode(pinGCTS, OUTPUT);
  digitalWrite(pinGCTS, HIGH);
  pinMode(pinDTR, OUTPUT); //Set DTR to be an OUTPUT pin
  digitalWrite(pinDTR, HIGH); //Set pin to HIGH so we can wake with LOW
  digitalWrite(pinCTS, HIGH); //Set pin to HIGH so we can later wake and check for LOW
  Serial.begin(9600);
  Serial.println("Setup almost done.");
  objGauge.begin(9600);
  intCounter = 0;
}

void loop() {
  //Attempting to perform RTS/CTS to get a gauge reading.
  
  if (digitalRead(pinGRTS) == HIGH && digitalRead(pinGCTS) == LOW) {
    Serial.println("RTS: HIGH, CTS: LOW");
    digitalWrite(pinGRTS, LOW);
    digitalWrite(pinGCTS, HIGH);
  }
  else if (digitalRead(pinGRTS) == LOW && digitalRead(pinGCTS) == HIGH) {
    Serial.println("RTS: LOW, CTS: HIGH");
    digitalWrite(pinGRTS, HIGH);
    digitalWrite(pinGCTS, LOW);
  }
  if (digitalRead(pinGCTS) == HIGH && digitalRead(pinGRTS) == HIGH) {
    Serial.println("RTS: HIGH, CTS: HIGH");
    digitalWrite(pinGCTS, LOW);
    digitalWrite(pinGRTS, LOW);
  }
  else if (digitalRead(pinGCTS) == LOW && digitalRead(pinGRTS) == LOW) {
    Serial.println("RTS: LOW, CTS: LOW");
    digitalWrite(pinGCTS, HIGH);
    digitalWrite(pinGRTS, HIGH);
  }
  
  if (objGauge.available())
  {
    Serial.println("Gauge Available");
    char chrIncoming;
    
    //Serial Data is coming in. Wake up the XBee.
    if (digitalRead(pinDTR) == HIGH) {
      digitalWrite(pinDTR, LOW);
    }
    
    //Read in the characters until you have a full string.
    while (objGauge.available()) {
      chrIncoming = (char)objGauge.read();
      strData += chrIncoming;
      // If you find a LFCR or CRLF then you have a full reading.
      if (strData.endsWith("\n\r") || strData.endsWith("\r\n")){
        break;
      }
    }
    
    delay(50); //Delay to wait for XBee to wake.
    //Check for the radio to be awake (CTS = LOW)
    if (digitalRead(pinCTS) == LOW) {
      if (strData.length() > 0) {
        Serial.print(strData);
        strData = ""; //Prepare for new data coming in.
        delay(50); //Wait a bit then sleep again.
        digitalWrite(pinDTR, HIGH);
        Serial.flush(); //Flush the serial stream.
        objGauge.flush(); //Flush the gauge stream.
      } //End strData has length
    } //End pinCTS == LOW
  } //End objGauge.available()
  delay(100); //Sleep a little between looping (ms)
  intCounter++;
  
  if (intCounter == 10)
  {
    intCounter = 0; //Reset counter
    //sleepNow();
  }
}

void sleepNow()
{
    /* Now is the time to set the sleep mode. In the Atmega8 datasheet
     * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
     * there is a list of sleep modes which explains which clocks and 
     * wake up sources are available in which sleep modus.
     *
     * In the avr/sleep.h file, the call names of these sleep modus are to be found:
     *
     * The 5 different modes are:
     *     SLEEP_MODE_IDLE         -the least power savings 
     *     SLEEP_MODE_ADC
     *     SLEEP_MODE_PWR_SAVE
     *     SLEEP_MODE_STANDBY
     *     SLEEP_MODE_PWR_DOWN     -the most power savings
     *
     *  the power reduction management <avr/power.h>  is described in 
     *  http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
     */

  set_sleep_mode(SLEEP_MODE_IDLE);   // sleep mode is set here

  // enables the sleep bit in the mcucr register so sleep is possible. just a safety pin 
  sleep_enable(); 

  //Turn OFF unused modules to pull even less power.
  power_adc_disable();
  power_spi_disable();
  power_timer0_disable();
  power_timer1_disable();
  power_timer2_disable();
  power_twi_disable();
  
  sleep_mode(); // here the device is actually put to sleep!!
 
// THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
  sleep_disable(); // first thing after waking from sleep: disable sleep...

  power_all_enable(); // Turn EVERYTHING back on.
}

Thanks in advance,
Jarvis