Basic Serial Comms Qs

I have a GPS disciplined frequency reference that spits out RS232 data through a standard 9 pin d-type connector.

If I run good old HyperTerm 4800 bd 8 data bits, 1 stop, no parity oin my PC then I am seeing a string every 16 seconds spat out of the frequency reference like this:

L|.|00C71|.|.|67FE|00F5|FFFF|FC63|00

I want to read this using an Arduino and translate the individual hex numbers containing information into useful things and display them on an LCD. You can see there are 10 sections in the string each has a meaning depending on value.

I dont seem to be clever enough to get the basics working.

I am using a Mega2560 and have connected the serial lead from the reference to Serial3 (pins 14 and 15 plus ground), and am trying to read a byte at a time and pass it back out to the serial monitor. No matter what I try I see jibberish on the serial mon.

Can anyone help me get off the starting blocks?

void setup() {
// put your setup code here, to run once:
Serial3.begin(4800,SERIAL_8N1);
Serial.begin(4800);
}

void loop() {
// put your main code here, to run repeatedly:
if (Serial3.available())
{
Serial.write(Serial3.read());
Serial.print(" ");
}

}

If your device is really outputting RS232 data (and voltages), you are frying your Arduino by directly connecting the device to it. You need an MAX232 chip in between, to convert, and invert, the voltages to TTL levels that the Mega can handle and understand.

RS232 uses different signaling levels than TTL. You will need level converters. The ideal way is with a chip like a MAX232 designed for RS232-to-TTL conversion: http://microcontrollershop.com/product_info.php?products_id=4166

Since you only want to receive and don't need control signals you can probably get aways with something simple like a single transistor. There are some ideas here: RS232 without a max232 or similar ... | AVR Freaks

L|.|00C71|.|.|67FE|00F5|FFFF|FC63|00
You can see there are 10 sections in the string

The '|' character seems to be the section delimiter. With known section delimiters strtok() can be used to separate the sections.

All excellent - thank you.

Schoolboy error on the TTL levels

Mark

don't forget about the grounds, either; your cable may or may not do that connection for you.
'normal' 12v rs232 isn't 12v until you have zero volts defined. if you really only want the Arduino to rcv from the external unit, a voltage divider to reduce incoming 12v to 5 or even 3.3 is all you truly need.

Do you know if the GPS sends a terminating character at the end of each message - for example a carriage-return or line-feed?

If so have a look at the examples in Serial Input Basics

If not I suggest you note the value of micros() when each character arrives and if the interval from the previous character is longer than the normal interval you can conclude that you have the full message.

...R

wg0z:
If you really only want the Arduino to rcv from the external unit, a voltage divider to reduce incoming 12v to 5 or even 3.3 is all you truly need.

Don't forget that MARK and SPACE are -12 and +12 in RS232 but +5 and 0 in TTL. A voltage divider won't prevent the negative voltage from possibly damaging the Arduino inputs and you need a way to invert the signal. (Luckily the SoftwareSerial library can take care of that: Arduino - SoftwareSerialConstructor )

I’ve found a MAX232 in my junk box so have built a simple interface and the serial reading is now working fine.

     rx_bytes=Serial3.readString();
 
     for (int index=0; index <= 35; index++)
     {
        Serial.write(rx_bytes[index]);
      }
      Serial.println();

Is reliably delivering me a string like:

L|.|00C6E|.|.|6803|0015|0001|0B93|00

This code also works reliably and gives me the decimal of a four character hex string:

      DAC_Value_String = rx_bytes.substring(5,9);

      Serial.println(DAC_Value_String);

      DAC_Value_String.toCharArray(DAC_Value_Chars, 5);
      DAC_Value_Dec = strtoul (DAC_Value_Chars, NULL, 16);

      Serial.println(DAC_Value_Dec);

However, when I try and extract the last two charaters in my string (currently 00) using this:

     Holdover_Counter_String = rx_bytes.substring(34);

     Serial.println(Holdover_Counter_String);

The first time round the loop I get 00 the second time I always get a blank string then the third time round the loop the program stops.

I dont understand why:

     Serial.println(sizeof(rx_bytes));

always returns 6 when I have to loop 0 to 35 to print out the full string contents.

Seems I am now very confused over strings & chars and suspect I am indexing outside an array bounds somehow, but I can’t see where.

Here’s my complete code that crashes on itteration three, if I comment out the last two lines it runs reliably:

void setup() {
  // put your setup code here, to run once:
   Serial3.begin(4800,SERIAL_8N1);
   Serial.begin(4800);
}

String rx_bytes = "";
char rx_bytes_chars[40];
String FLL_Status = "";
String Alarm_Latch = "";
String DAC_Value_String = "";
String Freq_Adj_Sign_String = "";
String Fine_Course_Freq_Adj_String = ""; 
String Sampled_Frequency_String = "";
String Sample_Counter_String = "";
String Accum_Freq_Diff_String = "";
String Holdover_Counter_String = "";

char DAC_Value_Chars[4];
unsigned long DAC_Value_Dec = 0;

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial3.available())
  {
     rx_bytes=Serial3.readString();
 
     for (int index=0; index <= 35; index++)
     {
        Serial.write(rx_bytes[index]);
      }
      Serial.println();

      switch (rx_bytes[0])    // FLL Status
      {
        case 'L':
          FLL_Status = "Locked";
          break;
        case 'U':
          FLL_Status = "Unlocked";
          break;
        case 'H':
          FLL_Status = "Holdover";
          break;
        case 'D':
           FLL_Status = "Disabled";
           break;
        default:
           FLL_Status = "Unknown";
           break;
      }

      Serial.println(FLL_Status);

      switch (rx_bytes[2])    // Alarm Latch
      {
        case 'U':
          Alarm_Latch = "Unlocked";
          break;
        case 'B':
          Alarm_Latch = "Bottom";
          break;
        case 'T':
          Alarm_Latch = "Top";
          break;
        case 'H':
           Alarm_Latch = "Holdover";
           break;
        default:
           Alarm_Latch = "None";
           break;
      }

      Serial.println(Alarm_Latch);

      DAC_Value_String = rx_bytes.substring(5,9);

      Serial.println(DAC_Value_String);

      DAC_Value_String.toCharArray(DAC_Value_Chars, 5);
      DAC_Value_Dec = strtoul (DAC_Value_Chars, NULL, 16);

      Serial.println(DAC_Value_Dec);

      switch (rx_bytes[11])
      {
        case '+':
          Freq_Adj_Sign_String = "Increasing";
          break;
        case '-':
          Freq_Adj_Sign_String = "Decreasing";
          break;
        case '=':
          Freq_Adj_Sign_String = "Equal";
          break;
        default:
          Freq_Adj_Sign_String = "Averaging Cycle";
          break;
      }

      Serial.println(Freq_Adj_Sign_String);

      switch (rx_bytes[13])
      {
        case 'C':
          Fine_Course_Freq_Adj_String = "Coarse";
          break;
        case 'F':
          Fine_Course_Freq_Adj_String = "Fine";
          break;
        default:
          Fine_Course_Freq_Adj_String = "Averaging Cycle";
          break;
      }
      
     Serial.println(Fine_Course_Freq_Adj_String);

     Sampled_Frequency_String = rx_bytes.substring(14,18);

     Serial.println(Sampled_Frequency_String);

     Sample_Counter_String = rx_bytes.substring(19,23);

     Serial.println(Sample_Counter_String);

     Accum_Freq_Diff_String = rx_bytes.substring(24,28);

     Serial.println(Accum_Freq_Diff_String);

     Serial.println(sizeof(rx_bytes));

     Holdover_Counter_String = rx_bytes.substring(34);

     Serial.println(Holdover_Counter_String);
  }
  
}

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with 0.

…R

     rx_bytes=Serial3.readString();
 
     for (int index=0; index <= 35; index++)
     {

Do you have a clue how long the (useless) String really is? Why do you ASSume that it is exactly 36 characters?

PaulS:

     rx_bytes=Serial3.readString();

for (int index=0; index <= 35; index++)
    {



Do you have a clue how long the (useless) String really is? Why do you ASSume that it is exactly 36 characters?

I dont think I do make that assumption; it’s the first 36 characters that I am interested in.

I dont think I do make that assumption;

Sure you do. Regardless of much data is actually in the String, you process the first 36 bytes. Even if the String only contains 3 bytes.

Its a fixed length so will always contain at least 36 bytes of data. I suppose I could loop to the number of bytes in the string.

What I think they're trying to say is -

  • Don't assume there are x number of characters.
  • Read each character and increment your char counter as you go.
  • Test each character for the delimiter (or timeout) - then tag on the NULL terminator
  • Put the new char into your char array if you are using one
  • Process the character or the accumulated char array
  • Keep going until one of the above.

This seems entirely sensible - thanks for the help - its appreciated. :slight_smile:

Mark

Its a fixed length so will always contain at least 36 bytes of data. I suppose I could loop to the number of bytes in the string.

Those 36 bytes will NOT arrive all at once. Only a fool would argue that they will.