NewSoftSerial send and recieve

Hi
I’ve been using the NewSoftSerial library to communicate with a thermal printer. I’ve got the printing commands working perfectly, but they are one way signals.
There’s a function with the printer where if you send a few request bytes, it’ll send back a status string.
When I set this up in a standalone sketch, it works fine.

#include <NewSoftSerial.h>
NewSoftSerial mySerial(9,13);

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  mySerial.print("^S|^");
}
void loop() {
  if (mySerial.available()) {
      Serial.print((char)mySerial.read());     
  }
}

However, when I try and put this into a function, I can’t get it to consistently return useful bytes. This is the function I have at the moment.

void check_printer(){
  printer.println("^S|^");
  delay(1);
  while (printer.available()) {
      Serial.print((char)printer.read());
  }
  Serial.println(" ");
}

I’ve tried playing around with different delays between the send and recieve parts but I can’t seem to get consistent results. I’m guessing that there’s an inherent delay between going from the setup() function to the loop() function that gives the arduino enough time to wait for a reply.
I imagine that there’s a technique for dealing with this situation and I guess it’s probably a pretty basic one, so sorry if this an obvious problem.
Cheers

I'm guessing that there's an inherent delay between going from the setup() function to the loop() function

A couple of hundred nanoseconds. What happens if you increase the "delay" to say, 5 milliseconds?

Your second example isn't like the first one at all, because "loop" loops testing to see if something has arrived, whereas the second one will transmit the query string, wait a millisecond, and if there's nothing in the receive buffer, it will transmit the query string again.

Edit: Sorry, the second function just bombs out if it doesn't get anything. Same argument applies - wait longer for a response.

Is there something in the response that indicates that the end of the response has been received? Some special symbol or exact number of characters?

Serial data is not transmitted and received instantaneously. Your function needs to keep looking for a response, until the complete response has been received (or until some time limit has expired).

The delay between setup() and loop() is probably very small as AWOL says. How often is the check_printer() function called, and how exactly is it called? It's watching for incoming traffic as fast as it can. But, the first time there is none, it exits. If it's only called once, or not frequently enough, that may account for the inconsistent behavior.

hi, thanks for the reply

Is there something in the response that indicates that the end of the response has been received?

The start of the response is always “S|0", then a load more bytes then the end should finish with "P2|”.

The thing is though, because there isn’t a clock line, I don’t understand how if the Arduino has missed the first bits of the response it can make any sense of it.

I guess I could save it into a an array of bits then invert it and work backwards, because I assume the bits always finish the same even if they interpreted into the wrong ascii characters when they’re printed to the serial terminal. The byte of info that I’m looking for is about 2/3rds of the way through a string about 24 bytes long, so this might be the most reliable way, but it does seem a bit of a roundabout one.

What I’m confused by though is why it won’t work when sequenced in the function, but will when spread across the setup() and loop() functions.

Sorry, the second function just bombs out if it doesn’t get anything.

This is what I thought, but the function isn’t called in a loop, it’s only called once on this occasion, so I’m not sure why it would carry on transmitting if, on the first pass through the function, it hadn’t recieved anything.

Anyway, cheers

The thing is though, because there isn't a clock line, I don't understand how if the Arduino has missed the first bits of the response it can make any sense of it.

Nor is there on any asynchronous serial line. Like the one connecting your Arduino to your PC. If it misses the start bit, then yes, you can't make sense of the rest of the serial stream. A hardware UART usually samples at 16 times the line speed when the line is idle. When it detects the edge of a start bit, it counts eight sample periods and samples again to be sure the start condition is still there, then samples every 16 clock periods to sample the stream as close to the centre of each bit as possible.

What I'm confused by though is why it won't work when sequenced in the function, but will when spread across the setup() and loop() functions.

Look at how your code is structured. The first sends the query string, and then just prints whatever comes back from the printer, no matter how long it takes.

The second sends the query string, then gives up if it doesn't hear from the printer within about one millisecond. If it helps to figure this out, it takes about 1.04 milliseconds to send a character by serial at 9600 bits per second.

I don't understand how if the Arduino has missed the first bits of the response it can make any sense of it.

If the serial buffer is empty when the status request is sent to the printer, is there any way for anything other than the status response to be in the buffer? How can the Arduino miss the first bits of the response? Serial data can get lost, but you would know that that happened if the first few characters weren't what you were expecting.

The thing is though, because there isn't a clock line, I don't understand how if the Arduino has missed the first bits of the response it can make any sense of it.

Newsoftserial takes care of this for you with interrupts and an RX buffer. Just as a test, set the delay in your function to 5000, hopefully, that will be ample time for the printer to respond and get the 24 bytes you're looking for into the buffer. Also, please post the entire sketch so we can see how you're calling it.

Cheers for the replies.

If it helps to figure this out, it takes about 1.04 milliseconds to send a character by serial at 9600 bits per second.

So I took this on board and tried holding it in a timer loop for 2 seconds, possibly similar to what happens in the loop() function?

	long t = millis();
	int byt = 0;
	char inbytes[30];
	printer.println("^S|^");
	while(t < millis()-2000){
		while (printer.available()) {
			inbytes[byt]  = (char)printer.read();
			byt++;
		}
	}
	Serial.println(inbytes);

As you suggested wildbill, I also tried changing the delay in the original function to 5 seconds but both of these not really working. I say not really because about 1 in 15 tries it’ll return what I’m expecting, but not at all consistently.

This is part of a much larger program that’s pretty linear in operation, to play out a sequence in an arcade cabinet, but it totals about 1200 lines of very untidy code. There are a total of 3 NewSoftSerial instances, but none of get called anywhere near each other. I’ve copied a stripped down version of the code I’ve been using.

#include <NewSoftSerial.h>

#define BTN1_PIN 2    //button pin

NewSoftSerial printer(9, 13);		// Printer serial instance

int byt = 0;
char inbytes[30];
boolean resetpush1 = 0;
unsigned long t = 0;

void setup() {
  
	pinMode(BTN1_PIN, INPUT);
	printer.begin(9600);
	Serial.begin(9600);
	delay(1000);
	check_printer();
	Serial.println("start");
}


void loop() {

button1_test();

}


void check_printer(){
  
	t = millis();
	printer.println("^S|^");
	while(t > millis()-1000){
		while (printer.available()) {
			inbytes[byt]  =(char)printer.read();
			byt++;
		}
	}
	Serial.println(inbytes);
	byt = 0;
}


void button1_test() {
  
	if(digitalRead(BTN1_PIN) && resetpush1 == 1){
		resetpush1 = 0;
		delay(50);
	}

	if(!digitalRead(BTN1_PIN) && resetpush1 == 0){
		resetpush1 = 1;
		Serial.println("request");
		check_printer();
	}

  }

By the way thanks for explaining exactly how the micro listens to asynchronous serial. It’s a lot easier to learn things when you’re stuck on them.

Cheers

while(t < millis()-2000){
		while (printer.available()) {
			inbytes[byt]  = (char)printer.read();
			byt++;
		}
	}
	Serial.println(inbytes);

You’re not terminating your string.

while(t < millis()-2000){
		while (printer.available()) {
			inbytes[byt++]  = (char)printer.read();
			inbytes[byt] = '\0';;
		}
	}
	Serial.println(inbytes);

As you suggested wildbill, I also tried changing the delay in the original function to 5 seconds

It was me, not wildbill, and I suggested a delay of 5 (milliseconds), not 5 seconds.

Can you explain what it is returning?
Saying “not really working” doesn’t help - it is best to explain in what way you think it isn’t working.

	long t = millis();
	int byt = 0;
	char inbytes[30];
	printer.println("^S|^");
	while(t < millis()-2000){

Suppose this function gets called 10 milliseconds after the Arduino starts up. t will be assigned a value of 20. The while loop will then run how long?

That would look a lot clearer as

while(millis() - t < 2000)
{

There are a total of 3 NewSoftSerial instances, but none of get called anywhere near each other.

Might be time to give some serious thought to upgrading to a Mega with its 4 hardware serial ports.

Paul S, thanks a lot for pointing that out, my bad. In retrospect, it probably would have been better to use a Mega but this is the last bit to a long program so it’s a bit late to change now.

AWOL, thanks a lotfor that. I didn’t know you could increase a variable at the same time as referring to it. The termination bit though, I thought that the whole while() loop goes round adding each character to the string. In the code you put I would have thought that each character is seperated by a space. I tried it and it works perfectly, but it’s baffling nonetheless.

I had a bit of progression though, by changing the baud rate to 38400. It seems to work OK about 9 times out of 10 now.

I’m still a little stuck though. I thought as a redundancy, I could request the data twice, then compare them. If they’re not equal, repeat the double request.
This is what the serial terminal looks like.

*S|0|GRUSA4100||@|@|P |* *S|0|GRUSA4100|@|@|@|P |*l?;I¯ÝõÐ4100|@|@|@|P Í|@|@|@|P |*l?;I¯ÝõÐ4100|@|@|@|P |*100||@|@|P |*S|0|GRUSA4100
request
åjß0|GRU
SA4100|@|@|@|P |

request
S|0|GRUSA4100|@|@|@|P |
S|0|GRUSA4100|@|@|@|P |
request
S|0|GRUSA4100|@|@|@|P |
S|0|GRUSA4100|@|@|@|P |
request
S|0|GRUSA4100|@|@|@|P |
S|0|GRUSA4100|@|@|@|P |
request
S|0|GRUSA4100|@|@|@|P |4?;I¯ÝõÐ4100|@|@|@|P |
?S|0|GRU
request
SA4100|@|@|@|P |

S|0|GRUSA4100|@|@|@|P |
request
S|0|GRUSA4100|@|@|@|P |
S|0|GRUSA4100|@|@|@|P |
request

The ones that show ‘S|0|GRUSA4100|@|@|@|P |’ twice are what I’m aiming for. Basically when the paper runs out of the printer, the third ‘@’ symbol changes. So once I know I’ve got the correct reply, I can filter through and verify whether or not there’s paper left.

Anyway, to check that the 2 status replies, I put them into 2 seperate strings and used the ‘==’ comparator in an if statement, but the strings don’t seem to be equal. I tested this by converting them to integers and the first and second responses were different, but surprisingly every first response and every second response was the same.
Below is how the function looks now.

long inlong[2] ={0,0};
void check_printer(){
	for(int i=0;i<2;i++)
	{
		printer.println("^S|^");
		while (printer.available()) 
		{
			inbytes[i][byt++]  =(char)printer.read();
			inbytes[i][byt] = '\0';
		}
		Serial.println(inbytes[i]);
		inlong[i] = long(inbytes[i]);
		byt = 0;
		inlong[i] = 0;
		delay(50);
	}
}
}

I also noticed that each return of ‘inlong’ is 30 higher than the last, which is the length of the character array ‘inbytes’. Is this something to do with pointers? I don’t know much about how they work but I remember running into issues when I tried using the sizeof() function.

Cheers

In the code you put I would have thought that each character is seperated by a space.

The NULL that AWOL appended (which is not the same as a space) does not cause the index to be incremented. Therefore, the next character you add overwrites the NULL, and another NULL is appended.

Your check_printer function reads whatever serial data has arrived by the time that the function reads the last character. In some cases, the rest of the packet has not yet arrived.

The check_printer function needs to keep spinning in another while loop, waiting for one of two events to occur. Those events are for some time limit to expire or for the end of packet marker to arrive.

I also noticed that each return of 'inlong' is 30 higher than the last, which is the length of the character array 'inbytes'. Is this something to do with pointers?

Yes. When you increment a pointer, it points to the next place in memory where the pointed to object type can start.

Cheers for clarifying that Paul.
So I’ve got it sort of working by receiving 3 replies, then comparing them for similarity. If there are a set number of similar characters, it looks at one of them and returns it. If not, like when it recieves garbled data, it restarts the function. This loops round until a good result is returned. Below is an example.

void check_printer(){
	functionstart:
	Serial.println(numbtries);
	reachpoint = 0;
	for(int i=0;i<3;i++)
	{
		printer.println("^S|^");
		while (printer.available()) 
		{
			inbytes[i][byt++]  =(char)printer.read();
			inbytes[i][byt] = '\0';
		}
		Serial.println(inbytes[i]);
		byt = 0;
		inlong[i] = 0;
	}
	for(int i=0;i<30;i++)
	{
		if(inbytes[0][i] == inbytes[1][i] && inbytes[1][i] == inbytes[2][i])
		{
			reachpoint++;
		}
	}
	Serial.print("reachpoint is: ");
	Serial.println(reachpoint);
	if (reachpoint<22 && numbtries<10)
	{
		numbtries++;
		goto functionstart;
	}
	else
	{
		int j = inbytes[0][15];
		if(bitRead(j,2)){Serial.println("Out of paper");}
		if(!bitRead(j,2)){Serial.println("Paper is OK");}
		Serial.print("Number of tries: ");
		Serial.println(numbtries);
	}
}

One problem I’m still having though is I want a time-out built in, so that in the unlikely event that it doesnt recieve a useful string for many many tries, then it’ll carry on as normal. I’ve been trying to do this incrementing the integer ‘numbtries’, but wherever in the function I put it, it always returns 0 or 1. I can’t find any reason for it to reset. Is the incrementing a good way to go or maybe I should just put it all into a timer loop?
Cheers

There is no reason to use a goto statement in that code. You want something like this:

void check_printer()
{
  unsigned long funStart = millis();
  int numTries = 0;

  while(numTries < 10 && (millis() - funStart < 2000) // try 10 times or 2 seconds (adjust as necessary)
  {
     // Send request to printer
     // Get reply from printer
     // Do whatever...

     numTries++;
  }
}

You are still expecting to get the complete reply instantly, and that won’t happen. You know what (sequence of) character(s) constitutes the start and end of the reply (apparently a single ‘*’). So, read and discard any data until a * appears. Then, read and save until another * appears, or a timeout occurs:

bool started = false;
bool ended = false;
unsigned long blockStart = millis();
while(!ended && millis() - blockStart < 1000) // wait up to one second for a full reply
{
   while(printer.available() > 0)
   {
      char aByte = printer.read();
      if(started)
      {
         if(aByte == '*')
         {
            ended = true;
            break;
         }
         else
         {
            inbytes[i][byt++] = aByte;
            inbytes[i][byt] = '\0';
         }
      }
      else
      {
         if(aByte == '*')
            started = true;
      }
   }
}

Put this code in place of the comment // Get reply from printer. When this block ends, you either got a complete reply, or you’ve waited too long, depending on the value of ended.

With this method, you don’t need to keep asking the printer to send a reply hoping to get a complete reply in one fell swoop.

printer.println("^S|^");
        while (printer.available())

What PaulS said. Here, the last character of the query string (the second carat) will be written to the transmit register of the UART, and "printer.println" will return. However, it will still be 1.04 milliseconds before that character is fully clocked out of the transmitter and received by the printer, which will then take at least another 1.04 milliseconds to return the first character of the reply. So, expecting a character to be available mere nanoseconds after "printer.println" has returned is unrealistic.

Thanks a lot. I tried the code but it seems that quite often a '*' will show up in the wrong place. This error seemed to happen a little too often. In the example I posted I think all the characters were as desired, but they were just misaligned. When I ran it a few times, quite a few bad characters would get passed and confuse the routine. I ended up finding that the brute force method of comparing 3 strings for similarity over and over was more reliable and the function is called in a part of the program where a few seconds can be afforded.

I found though that adding or changing 'Serial.print' functions made a big difference to the operation and often the prgram would stop running altogether. Do both the native serial and NewSoftSerial share the same interrupts and buffers? I read on the NewSoftSerial that you can't use 2 instances of NewSoftSerial in parallel but does this include normal 'Serial'? If this is the case should, would using the '.flush()' function after sending or receiving help things run smoother? I normally write some code and add in a load of 'Serial.println's to get a feel of where in the program it is, but maybe this is a bad habit.

Anyway I've got the prgram running reliably enough now to leave it as it is.

Another question on a slightly different tip is, when you're in a function say 'void myFunction(){', before you reach the end of it, is it OK to then call that function again, so as to have the effect of restarting the function? Is this good programming? Paul S and others have told me to try and avoid using the 'goto' function if at all possible, so would recalling a function from within itself be counted as a form of this?

Many thanks

Adding serial prints changes the timing of the sketch, so that may be affecting things.

"flush" affects only receive.

before you reach the end of it, is it OK to then call that function again,

No, this may be a bad idea, depending on how much of stack frame your function requires. Recursion on a RAM-limited architecture is generally to be avoided.

If you find yourself using "goto", try using "while" or "do..while" instead.

Brilliant! Thanks a lot for the tips.