Have I understood XON/XOFF correctly?

I am working with a SparkFun Graphical LCD with their Serial Backpack, but due to the broken firmware am using SummoningDark’s replacement firmware for the backpack.

The great addition of the new firmware is XON/XOFF flow control, so it should be possible to send data to the screen “as fast as possible” without guessing where to put pauses in my drawing code.

Am I correct in thinking that XON/XOFF is simply a case of listening for data from the display, and if it sends an XOFF, I need to wait until an XON before sending any more data?

Here’s the sort of thing I’m thinking - mostly based off assumptions and guesswork, so may be wrong :slight_smile:

#include <SoftwareSerial.h>

SoftwareSerial lcd(2, 3);

void setup() {
  lcd.begin(115200);
  Serial.begin(115200);
  delay(2000);
  wr(0x7C); wr(0x00); // send CMD+CLEAR
  delay(2000);
}

// Simple loop to draw a bunch of boxes on the screen as fast as possible
// Command for drawing a line is "0x7C, 0x0F, x1, y1, x2, y2, state"
void loop() {
  for (int i = 0; i < 10; i++) {
    wr(0x7C); wr(0x0F); // box
    wr(10+(10*i+i)); wr(27); // x1, y1
    wr(20+(10*i+i)); wr(37); // x2, y2
    wr(0x01); // state
  }
}

Now, the “naive” wr() simply bashes out the bytes given as fast as it can, so very rapidly the LCD backs up and gets flooded with corruption due to missed bytes. Here I’ve shown a “dumb” delay put in to make the boxes draw, and this works and everything displays correctly:

// Naive wr() - bashes out bytes with a fixed delay between every byte, regardless of the screen needing time to catch up or not
void wr(byte b) {
  lcd.write(b);
  delay(50);
}

Here’s a “smarter” wr(), which listens for XOFF signals (but doesn’t yet respond to them in any way):

// Smarter wr() - bashes out bytes but hears data from the LCD
void wr(byte b) {
  if (lcd.available()) {
    byte val = lcd.read();
    if (val == 0x13) { // XOFF
      Serial.println("XOFF Received");
    } else if (val == 0x11) { // XON
      Serial.println("XON Received");
    } else {
      Serial.print(val);
      Serial.println(" Received");
    }
  }
  lcd.write(b);
  delay(50);
}

The problems I’m having is that for starters I seem to be getting different values on the serial, mostly 0x93 and 0x91, even though the source for the firmware clearly seems to be sending 0x13 and 0x11 at the correct times. Another problem is that every time I actually use lcd.read() it seems to cause a lot of corruptions to appear on the screen, as if read() is causing sends. I will check for shorts and things when I am next near the spaghetti electronics, but I would have expected worse problems if I’d shorted TX to RX.

Am I correct in thinking that XON/XOFF is simply a case of listening for data from the display, and if it sends an XOFF, I need to wait until an XON before sending any more data?

Yes it is that straight forward. You need to maintain a flag variable in your sketch that 'remembers' the last state of the XON/XOFF protocol. The default at bootup would be XON. Any printing to the display should first check the state of the flag variable and decide if should send data or not at that time. The only complexity that sometimes comes up is if the external serial device is using a receive character buffer or not, as without some buffering the timing and response time to the XOFF can become more critical and it's possible to lose a character from time to time.

Good luck

Lefty

Okay, at least I know it is worth pursuing and I’m not being completely bonkers.

Making functions with proper names makes the code more readable and you need less comments :wink:

void loop() 
{
  for (int i = 0; i < 10; i++) 
  {
    byte x = 10+(10*i+i);
    line(x, 27, x+10, 37, 1);
  }
}

void line(byte x1, byte y1, byte x2, byte y2, byte state)
{
  wr(0x7C); wr(0x0F); // line command
  wr(x1); wr(y1); 
  wr(x2); wr(y2); 
  wr(state); 
}

void clear()
{
  wr(0x7C); wr(0x00); // send CMD+CLEAR
}

And if you have enough functions there suddenly will grow a library …

I realise that, and I have that, but for posting about a problem I have found that writing a minimally-failing example is easier for others to understand. If I just copy and paste my 8 source files and custom drawing API, I will either get complaints that I've posted too much, or critique about where I put my braces or lay out my API.

This is code that runs, is easily understood and fails in the same way as my actual code.