Reading one data from Hardware Serial, then Software Serial

Hi everyone,

I’m working on a university final project and I’m trying to read values from two pressure sensors via two serial ports, one being a Software Serial instance on pins 8 and 9, and the other being the Hardware Serial on pins RX1 and TX0. My board is an Arduino Pro Micro.

With great help from the people on this forum and Robin2’s thread “Serial Input Basics,” I have been able to read characters from the sensors, store them in an array, and print them out to the Serial Monitor. But so far I’ve only been able to do them separately. I can’t figure out how to read one array of characters from one sensor, then move on to the second sensor and do the same.

The sensors are named Paro and Honeywell. The Paro is programmed to give data at 10Hz. The Honeywell is programmed to give data at 2Hz. I’m not sure if the large difference in frequency is a problem.

With my code, I get five or six values from the Paro, followed by one value from the Honeywell. I can’t figure out read just one value from each sensor and print them one after the next. Basically, I want to fill up one array with characters, print it, and then fill it up again with new characters. But my code seems to run too fast and I have no control over when the array is filled up and printed.

Here is the code I have:

#include <SoftwareSerial.h>
#define honey_RX 8
#define honey_TX 9

SoftwareSerial honeySerial(honey_RX, honey_TX);     //Honeywell on pins 8(RX) and 9(TX)

const byte numChars_Honey = 12;     //Honeywell data is 12 characters long
const byte numChars_Paro = 13;      //Paro data is 13 characters long
char honeyChars[numChars_Honey];
char paroChars[numChars_Paro];
boolean newHoneyData = false;
boolean newParoData = false;

void setup(){
  Serial.begin(9600);     //with computer
  Serial1.begin(9600);      //with Paro on HardwareSerial
  honeySerial.begin(9600);      //with Honeywell on SoftwareSerial
}

void loop(){
  readParo();
  if(newParoData == true){
    Serial.println(paroChars);
    newParoData = false;
  }
  readHoney();
  if(newHoneyData == true){
    Serial.println(honeyChars);
    newHoneyData = false;
  }
}

void readHoney(){     //read each character from Honeywell and store it in array
  static boolean receiveInProgress = false;
  static byte index = 0;
  char honeyStartMarker = '?';
  char honeyEndMarker = '?';
  char hc;
  while(honeySerial.available() && newHoneyData == false){
    hc = honeySerial.read();
    if(receiveInProgress == true){
      if(hc != honeyEndMarker){
        honeyChars[index] = hc;
        index++;
        if(index >= numChars_Honey){
          index = numChars_Honey - 1;
        }
      }else{
        honeyChars[index] = '\0';      //terminate string
        receiveInProgress = false;
        index = 0;
        newHoneyData = true;
      }
    }else if(hc == honeyStartMarker){
      receiveInProgress = true;
    }
  }
}

void readParo(){      //reads each character from Paro and stores it in array
  static boolean receiveInProgress = false;
  static byte index = 0;
  char paroStartMarker = '*';
  char paroEndMarker = '\n';
  char pc;
  while(Serial1.available() && newParoData == false){
    pc = Serial1.read();
    if(receiveInProgress == true){
      if(pc != paroEndMarker){
        paroChars[index] = pc;
        index++;
        if(index >= numChars_Paro){
          index = numChars_Paro - 1;
        }
      }else{
        paroChars[index] = '\0';      //terminate string
        receiveInProgress = false;
        index = 0;
        newParoData = true;
      }
    }else if(pc == paroStartMarker){
      receiveInProgress = true;
    }
  }
}

And the output is like this:

01CP=0.0003
000114.6621 
000114.6619 
000114.6619 
000114.6621 
000114.6619 
01CP=0.0003

The values are formatted the correct way. The first and last values are from the Honeywell, while the middle ones are from the Paro. As you can see, there are five Paro data for each one Honeywell data. How can I write code to print one Honeywell data followed by one Paro data, or vice-versa? I am looking for an output like this:

01CP=0.0003
000114.6621
01CP=0.0002
000114.6621

I'm not sure if the large difference in frequency is a problem.

It is not.

How can I write code to print one Honeywell data followed by one Paro data, or vice-versa?

You want to print if (newParoData == true && newHoneyData == true)?

PaulS,

Thank you for your reply. I tried something like this inside the loop, but while I do get the right order, one sensor after the previous, I see some distorted values on the readings. This is the code I'm using in the loop:

void loop(){
  readParo();
  readHoney();
  if(newParoData == true && newHoneyData == true){
    Serial.println(paroChars);
    Serial.println(honeyChars);
    newParoData = false;
    newHoneyData = false;
    readParo();
    readHoney();
  }
}

And here is what I get on the Serial Monitor. Sometimes the values from the faster Paro sensor, which are supposed to be in the form 000114.6592, are distorted, while the values from the slower Honeywell seem to be stable:

000114.6596
01CP=0.0005
000114595
01CP=0.0006
000114.6*000
01CP=0.0005

Why is this happening?

My first question would be: Are you using Serial to talk to the Paro OR to talk to the PC?
Yes is the wrong answer. So is BOTH.

Get a Mega, with 4 hardware serial ports.

For my project, I am restricted on space, so a Arduino Mega would not be usable. I read that the Arduino Pro Micro can only receive from one Software Serial port at a time, so that is why I decided to connect one of the sensors to the Hardware Serial port. I think I’ve gotten both of these ports to work because both sets of data is showing, just with the frequency being off. Do you think it’s not possible with my setup to achieve my desired output?

I don't think it is a good idea to use Serial to talk to the PC while also using it to read from the Paro device.

I don't think it is necessary to call readParo() and readHoney() in the body of the if statement.

I think that it may be necessary to have an else block, where you copy the paro data, if there is new data, and where you copy the honey data if there is honey data, and reset the appropriate flags.

I'd put the device that talked the most on the hardware serial port, and put the other device on the software serial port.

If I'm not mistaken, the Micro has two hardware serial ports.

I am using this board:

And from here, I believe that the Pro Micro only has one Hardware Serial port, on pins RX1 and TX0:

I cannot see another RX or TX label anywhere on the board when I look at its pinout.

On the Arduino Micro page, it says:

Serial: 0 (RX) and 1 (TX). Used to receive (RX) and transmit (TX) TTL serial data using the ATmega32U4 hardware serial capability. Note that on the Micro, the Serial class refers to USB (CDC) communication; for TTL serial on pins 0 and 1, use the Serial1 class.

So, you use Serial to talk to the PC, and Serial1 to talk to the RX/TX pins.

Which appears to be what you are doing. You should have corrected me earlier when I asked if you were using Serial to talk to the PC or the Paro. You are using Serial to talk to the PC and Serial1 to talk to the Paro.

What I would do is add two more flags - copiedParoData and copiedHoneyData.

if(newParoData)
{
   // copy it
   copiedParoData = true;
   newParoData = false;
}

if(newHoneyData)
{
   // copy it
   copiedHoneyData = true;
   newHoneyData = false;
}

if(copiedParoData && copiedHoneyData)
{
   // print the copied data
   copiedParoData = false;
   copiedHoneyData = false;
}

bz26:
Do you think it’s not possible with my setup to achieve my desired output?

Yes, it’s possible to use two hardware serial ports and one software serial port. As long as the SoftwareSerial baud rate is not more than 2 times slower than the hardware serial baud rate, it should work. With all 3 ports running at 9600, you should not lose any characters.

It would be better to use AltSoftSerial on pins 5 and 13, if you could change connections. SoftwareSerial is very inefficient, because it disables interrupts for long periods of time. It’s not a problem for the sketch you have shown us, so foar. If you add more to your sketch, SoftwareSerial could eventually interfere with other parts of your sketch, or with other libraries.

The second best choice is my NeoSWSerial. It can be used on your current pins 8 & 9, at 9600.

Both AltSoftSerial and NeoSWSerial are available from the Arduino Library Manager, under the IDE menu Sketch → Include Library → Manage Libraries.

Your current problem is that you are not emptying the characters from the Paro sensor. Your sketch is losing characters because the input buffer is overflowing, not because there is some problem using 2 hardware serial ports and one software serial port. PaulS’ suggestion would work if you prevented the input buffer overflow.

Here’s what’s happening:

The Serial1 input buffer has room for 64 characters. When a Paro message is ready, no more characters are read from Serial1 until the Honeywell has sent a complete message. Let’s say that your sketch has read Paro message “000114.6601”. I’ll number each Paro message sequentially.

Now, while waiting for the 1st Honeywell message, the Serial1 input buffer continues to fill with 4 complete Paro messages. With 13 characters per Paro message, there will be 52 characters remaining in the input buffer. Here is the Serial1 input buffer, waiting to be read:

    000114.6602 
    000114.6603 
    000114.6604 
    000114.6605

When the 1st Honeywell message is received, only one Paro message has been read from the Serial1 input buffer (114.6601). Then loop clears the newData flats, and one more Paro message is read from the Serial1 input buffer (114602). There are still 3 other Paro messages.

    000114.6603 
    000114.6604 
    000114.6605

Then you wait for the 2nd Honeywell message. During that time, the 6th Paro message (13 characters) is sent, but there is only room for 12 characters. One character gets lost:

    000114.6603 
    000114.6604 
    000114.6605 
    000114.660    <-- character '6' dropped

Then the 7th, 8th, 9th and 10th messages are completely ignored.

When the 2nd Honeywell message finally arrives, readParo pulls only one Paro message from the Serial1 input buffer 114.6603), leaving 2 complete messages plus one incomplete message in the input buffer (last character was dropped). There should be 38 characters (2*13 + 12) in the buffer:

    000114.6604 
    000114.6605 
    000114.660

Now, when the 11th Paro message starts arriving, it can fit (38+13 = 51). But look at the input buffer contents:

    000114.6604 
    000114.6605 
    000114.660000114.6611

As your program runs, various parts of a Paro message run together with the next message.

I would suggest emptying the Paro input buffer every time you have both messages:

void loop(){
  readParo();
  readHoney();

  if (newParoData and newHoneyData) {
    Serial.println(paroChars);
    Serial.println(honeyChars);
    newParoData  = false;
    newHoneyData = false;

    // throw away ~4 Paro messages received while waiting for Honeywell
    while (Serial1.available())
      Serial1.read();
  }
}

Hi Dev and PaulS, thank you both for your helpful replies.

Dev, I tried your approach of emptying characters from the Paro array each time I have two messages from both sensors. I found that it works, but I did have to do one adjustment to it.

With both sensors continuously running in the background, I found that I have to do recursion in the loop to make the Arduino read and write continuously. If not, only one pair of data values gets printed to the Serial Monitor, and after that, nothing until the next reset of the Arduino board.

This is the code I’m using in the loop:

void loop(){
  readParo();
  readHoney();
  if(newParoData == true && newHoneyData == true){
    Serial.print("*"); Serial.println(paroChars);
    Serial.print("?"); Serial.println(honeyChars);
    newParoData = false;
    newHoneyData = false;
    while(Serial1.available()){      //clear the Paro data buffer
      Serial1.read();
      loop();
    }
  }
}

And this is the output, which is correct. I have added back the ‘*’ and ‘?’ characters to the front of the Paro and Honeywell data, for easier integration with a separate data acquisition system. I don’t know if this is the most efficient way to do so.

Anyway, here is the output I get. The data rate has been dropped to the slowest rate, which is the Honeywell’s 2Hz, which makes sense to me.

?01CP=0.0000
*000114.5799 
?01CP=0.0001
*000114.5795 
?01CP=0.0002
*000114.5799
    while(Serial1.available()){      //clear the Paro data buffer

Serial1.read();
      loop();
    }

So for every character in the buffer, you throw away the first one and then try to process everything else in the buffer with loop(). That looks like a very bad idea as you can end up using up all your memory with nested copies of loop().

Here's your problem...

  while(Serial1.available() && newParoData == false){

pc = Serial1.read();

You only process characters out of the Serial1 buffer when there isn't a completed data item. When there is, and you're waiting for the Honeywell, then it just fills up the buffer.

I would change this to discard the additional characters while newParaoData is true.

bz26:
With both sensors continuously running in the background, I found that I have to do recursion in the loop to make the Arduino read and write continuously. If not, only one pair of data values gets printed to the Serial Monitor, and after that, nothing until the next reset of the Arduino board.

Then there’s something you haven’t told us, or you had some other code in the sketch. Extra prints statements, perhaps?

Emptying the Serial1 input buffer would happen very quickly, much more quickly than receiving another character from the Serial1 device. Then loop would return and get called again, almost immediately. This starts the accumulation process again, in the same state as the first execution. The honeySerial input buffer might have a few characters waiting to be read, but they are easily handled by the next loop iteration.

Like MorganS’ signature, “The problem is in the code you didn’t post.”

And in case you thought recursion was a good idea, just consider it a “learning moment”. If you need the loop function to be called again, just return from loop; don’t call loop.

MorganS:
I would change this to discard the additional characters while newParaoData is true.

That should also work, but it is equivalent to reading all the characters in the caller. EDIT: It would empty the input buffer after each character, instead of empying it after both messages have been received. It should have the same behavior.

I am suspicious of this:

char honeyStartMarker = '?';
  char honeyEndMarker = '?';

You haven’t told us what the sensor is, so we don’t really know the message format. Does it really start and end with the same character? Is there no CR/LF ending?

Watching for the same start and end character can leave you “stuck” between two message.

And in case you thought recursion was a good idea, just consider it a "learning moment". If you need the loop function to be called again, just return from loop; don't call loop.

Good suggestion. But I still believe the problem is the way it stops reading from the Paro sensor while it is waiting for the Honeywell.

Hi,

I apologize for the late response. I would like to provide more details on the sensors and code that I'm using.

For the sensors, the static pressure transducer is the Paroscientific Series 6000-23A. The differential pressure transducer is a Honeywell Precision Pressure Transducer (PPT) from the year 2000. Both of these transducers use the RS232 protocol, so I can read their output values by using a RS232 to USB cable.

For the static pressure transducer, this is the exact output when I connect it to a USB port and open a serial emulator such as CoolTerm or the Arduino Serial Monitor:

*000114.6602
*000114.6603
*000114.6602

I believe the static pressure transducer outputs data values with '*' and ends with '\n', which are the start and end markers I am using in the readParo() method.

However, for the differential pressure transducer, things are different. This is the exact output when I connect it to a USB port:

?01CP=0.0000?01CP=0.0001?01CP=-0.0001?01CP=-0.0002

There are no '\n', so that is why I am using '?' as both the start and end markers in the readHoney() method. I am slightly suspicious about this output. I read through the user's manual and tried to program the Honeywell, but for some reason it does not respond to many of the commands that are listed. I have also tried to change its output frequency to 10Hz unsuccessfully.

In both cases of directly reading from the transducers, I keep the settings in CoolTerm and the Arduino Serial monitor the same.

Now, I have such a setup method in my program:

void setup(){
  Serial.begin(9600);     //with computer
  Serial1.begin(9600);      //with Paro on HardwareSerial
  honeySerial.begin(9600);      //with Honeywell on SoftwareSerial
  honeySerial.print("*00P2"); honeySerial.print('\r');     //command for continuous data output
}

The command *00P2 makes the Honeywell transducer continuously output data. I have noticed that without first sending this command, no data appears. This is true also when I try to directly read data via the RS232 to USB cable. I've also noticed that this command appears to be the only command that the Honeywell responds to. At this point, I don't know if there is something wrong with the transducer itself. Sometimes when I to read values with the Arduino, the Honeywell disappears while the Paro is working, and no matter how many times I try to restart the Honeywell, it never wants to output any data. So that is why I have added the *00P2 command to jump start the Honeywell.

I'm considering putting this command in the readHoney() method, so that I can make sure it is sent numerous times, making sure the Honeywell will output data. If I put the command in setup(), it will run only once, whenever I switch on the Arduino.

Unfortunately, I won't have a chance to test this until tomorrow, which is in 8 hours. What problems can you guys see in my system? I'm really learning a lot from this experience. Thank you all again.

I'm currently struggling with some new Honeywell pressure sensors that don't behave exactly like the datasheet says. Some of the claims in the datasheet read like wishes.

But yours seem to be functional at least. Isn't the start character the "=" not the "?"? Or do you need to check that it always has the "01CP" in front?

If it doesn't slow you down always sending the command then always send it. The sensor cannot complain that you already asked it a million times.

MorganS,

The Honeywell PPT I'm using is a unit from 2000. Attached is the user's manual I'm referring to when I try to program the Honeywell. I'm a bit doubtful on the compatibility because the user's manual is from 2016, and the pictures of the sensors on its cover page are from 2012. However, on page 10 (in Adobe Reader), or page 4 in the original booklet, section 2.4, the initial turn-on response is covered. It is stated that the PPT will generate a typical RS232 response like writing "?01PPT____10__psid". In section 5.3, it gives an example of setting the transducer to give continuous pressure readings and a user message at power-up. I haven't tried this yet, so I'm hoping that it could would, because that is what I have programmed the Paro static transducer to do. If not, I should constantly write the command *00P2 (continuously read pressure). But I'm not sure where I should put this in my code.

And from the user's manual, it also seems like the response from the Honeywell when given a read pressure command should always start with '?'.

From the user's manual for the Honeywell PPT, it looks like the sensor can be programmed to start giving continuous pressure readings and a user message at start up. The command is *00MO=P2M2.

I'm hoping my unit will respond to this and save it in its memory.