Dot matrix printer interpreting custom (any) escape sequences as text

The dotmatrixgraphics function never increments pxpos, so there's no carriage returns. I haven't fixed the part where it needs multiple carriage return characters yet. Also, since I only use the bottom 7 bits of the 8 bit port B, I will have to somehow make a program that takes an image, and sends 7 pixel tall, one pixel wide strips as 8 bit tall strips with the top bit set (to prevent the string from terminating itself when it first sees a byte with the value of zero). I will try to fix those when I'll have time.

LOL. There is no place for superstition in programming.

Read up and use the ++ and -- operators, you doing soemthing wrong.

When it "caus[es you]problems", come back and we see why.

a7

The root cause is that sstringr() is returning a String composed of only the escape character, because you are not allowing time for the entire escape sequence to accumulate in the Serial receive buffer. The remaining characters are returned in a later call to sstringr(), where the preceding escape character is missing, causing the code to see that as plain text.

Sending padding and multiple copies of the escape sequence causes the code to do something that takes sufficient time that the Serial receive buffer can accumulate more that one character and return the full escape sequence in a single String.

sstringr() is supposed to return up to 64 characters at a time, or am I wrong? Let's say that I send 60 characters, and two of them are my escape sequence. What will happen then? Or, send just the escape sequence. 76800 baud should be fast enough for the escape sequence to accumulate. Tell me what I got correct, and what I got wrong.

I hope that the following code will be fixed.


#include "storage.h"

// *Note: Don't use PROGMEM, because it'll make the code not get the proper values!!!

int lsindex = 0;  // index of read letter in template
int letpos = 0;
int pixpos = 0;
long steppos = 0;
byte letter = 0;
byte letter2 = 0;
byte pixval = 0;
String received = "";
byte cmsht = 29;
byte pmsht = 59;
byte stcpx = 27;
byte stppx = 61;
byte pxln = 9;
long lnfsa = 0;
char overflow = 0;
byte printmode = 0;  // defaults to text
byte pxpos = 0;
long dlzka = 0;  // length of message
byte waitforbytes = 0; // is it supposed to wait for bytes?

void setup() {

  DDRB = 255;
  PORTB = 0;

  pinMode(6, INPUT_PULLUP);   // koncový kalibračný spínač (limit switch for homing)
  pinMode(A1, INPUT_PULLUP);  // posuv papiera von (move paper out of the output(blank line feed))
  pinMode(A2, INPUT_PULLUP);  // posuv papiera dnu (move paper into the output(blank line unfeed))

  pinMode(2, OUTPUT);  // stepper 1 step
  pinMode(3, OUTPUT);  // stepper 1 dir
  pinMode(4, OUTPUT);  // stepper 2 step
  pinMode(5, OUTPUT);  // stepper 2 dir

  Serial.begin(76800);


  digitalWrite(3, LOW);
  while (digitalRead(6) == HIGH) {
    digitalWrite(2, HIGH);
    delayMicroseconds(125);
    digitalWrite(2, LOW);
    delayMicroseconds(125);
  }

  digitalWrite(3, HIGH);
  for (int s = 0; s < (stcpx * 26); s = s + 1) {
    digitalWrite(2, HIGH);
    delayMicroseconds(125);
    digitalWrite(2, LOW);
    delayMicroseconds(125);
  }

  delay(250);
  Serial.write(0x0A);  // tento riadok môžete vynechať
}

void loop() {
  while (!Serial.available()) {

    if (digitalRead(A1) == LOW) {
      delay(500);
      bplf();
    }
    if (digitalRead(A2) == LOW) {
      delay(500);
      bpluf();
    }
  }

  received = sstringr();

  for (int f = 0; f < dlzka; f = f + 1) {
    letter = received[f];
    letter2 = received[f + 1];

    if (waitforbytes == 1) {
      if (letter == 'G') {
        waitforbytes = 0;
        lnfsa = pxln * stppx;
        crlf();
        printmode = 1; // graphics mode
      }
      if (letter == 'T') {
        waitforbytes = 0;
        lnfsa = pxln * stppx;
        crlf();
        printmode = 0; // text mode
      }
      if (letter == 'L') { // load paper to top margin
        waitforbytes = 0;
        pxln = 4;
        for (byte r = 0; r < 9; r = r + 1) {
          bplf();
        }
        pxln = 9;
      }
      if (letter == 'E') {
        waitforbytes = 0;
        for (byte r = 0; r < 20; r = r + 1) {
          bpluf();
        }
      }
      continue;
    }

    if (printmode == 0) {
      if (letpos > 19) {
        letpos = 0;
        if (letter != 10 || letter != 13 || letter != 27) {
          lnfsa = pxln * stppx;
          crlf();
          overflow = letter;
          dotmatrixtext(overflow);
        } else {
          lnfsa = pxln * stppx;
          crlf();
        }
      } else if (letter == 10) {
        letpos = 0;
        lnfsa = pxln * stppx;
        crlf();
      }

      else if (letter == 13) {
        letpos = 0;
        lnfsa = pxln * stppx;
        crlf();
      }
      else {
        dotmatrixtext(letter);
        letpos = letpos + 1;
      }
    }

    if (letter == 27) {
      if (letter2 == 'G') {
        waitforbytes = 0;
        printmode = 1; // graphics mode
      }
      if (letter2 == 'T') {
        waitforbytes = 0;
        printmode = 0; // text mode
      }
      if (letter2 == 'L') { // load paper to top margin
        waitforbytes = 0;
        pxln = 4;
        for (byte r = 0; r < 9; r = r + 1) {
          bplf();
        }
        pxln = 9;
      }
      if (letter2 == 'E') {
        waitforbytes = 0;
        for (byte r = 0; r < 20; r = r + 1) {
          bpluf();
        }
      }
      else {
        waitforbytes = 1;
      }
      f = f + 1;
      continue;
    }

    if (printmode == 1) {

      if (pxpos > 119) {
        pxpos = 0;
        lnfsa = pxln * stppx;
        crlf();
      }

      else {
        dotmatrixgraphics(letter);
      }

    }

  }

}

void dotmatrixgraphics(byte graphdat) {
  pxln = 7;
  pxpos = pxpos + 1;
  Serial.println("Graphics");

  for (byte b = 8; b-- > 0;) {
    if ((pixval >> b & 0x01) == 0) {
      Serial.print(' ');
    } else {
      Serial.print('#');
    }
  }
  Serial.println();

  for (byte y = 0; y < stcpx; y = y + 1) {
    steppos = steppos + 1;

    digitalWrite(2, HIGH);
    delayMicroseconds(cmsht);
    digitalWrite(2, LOW);
    delayMicroseconds(cmsht);

    if (y > 10) {
      PORTB = 0;
    }

    else {
      PORTB = graphdat;
    }
  }

}

void dotmatrixtext(byte character) {
  pxln = 9;
  Serial.println("Text");
  digitalWrite(3, HIGH);
  lsindex = (character - 32) * 6;
  for (byte i = 0; i < 6; i = i + 1) {
    pixval = 0;
    pixval = font[lsindex + i];
    pixpos = pixpos + 1;
    pxpos = pxpos + 1;

    for (byte b = 8; b-- > 0;) {
      if ((pixval >> b & 0x01) == 0) {
        Serial.print(' ');
      } else {
        Serial.print('#');
      }
    }
    Serial.println();

    for (byte y = 0; y < stcpx; y = y + 1) {
      steppos = steppos + 1;

      digitalWrite(2, HIGH);
      delayMicroseconds(cmsht);
      digitalWrite(2, LOW);
      delayMicroseconds(cmsht);

      if (y > 10) {
        PORTB = 0;
      }

      else {
        PORTB = pixval;
      }
    }
  }
}

void crlf() {
  digitalWrite(3, LOW);
  digitalWrite(5, HIGH);
  while (steppos > 0) {
    steppos = steppos - 1;
    digitalWrite(2, HIGH);
    delayMicroseconds(cmsht);
    digitalWrite(2, LOW);
    delayMicroseconds(cmsht);
  }
  while (lnfsa > 0) {
    lnfsa = lnfsa - 1;
    digitalWrite(4, HIGH);
    delayMicroseconds(pmsht);
    digitalWrite(4, LOW);
    delayMicroseconds(pmsht);
  }
}

void bplf() {
  digitalWrite(5, HIGH);
  lnfsa = pxln * stppx;
  while (lnfsa > 0) {
    lnfsa = lnfsa - 1;
    digitalWrite(4, HIGH);
    delayMicroseconds(pmsht);
    digitalWrite(4, LOW);
    delayMicroseconds(pmsht);
  }
}

void bpluf() {
  digitalWrite(5, LOW);
  lnfsa = pxln * stppx;
  while (lnfsa > 0) {
    lnfsa = lnfsa - 1;
    digitalWrite(4, HIGH);
    delayMicroseconds(pmsht);
    digitalWrite(4, LOW);
    delayMicroseconds(pmsht);
  }
}

String sstringr() {
  Serial.write(0x13);
  char message = 0;
  dlzka = 0;
  String content = "";

  while (Serial.available()) {
    message = Serial.read();
    content += message;
    dlzka = dlzka + 1;
  }

  if (content != "" && !Serial.available()) {
    Serial.write(0x11);
    return content;
  }
  else {
    return "";
  }
}

76 800 baud is something like 7 680 character per second, for 16 MHz Arduino this means something like 2000 instructions done between any two characters received - more than enought to process every single character and wait :slight_smile: (put it in Serial buffer, retrieve it to sstringr() and decide, what kind of character it is)

When you initially send 0x13 to start the transmission, sstringr() will return an empty string, because it will take 130uS to send the 0x13, then 130uS to receive the first character of the data. Within that time, loop will be executing many times, called sstringr() repeatedly and taking no action because an empty string is return. When the first character is received, sstringr() will return a string composed of that character alone, and send 0x11 to stop further transmission of the data. Just how much data you received the next time sstringr() is called will depend on how much time your code spends processing the received character, and when exactly the sender received the command to stop sending. Since there was a character in the Serial receive buffer, the next character would likely already be in the process of being received, and the sender will already have characters in its Serial send buffer (which hopefully does not exceed 64 characters), so data will continue to be received while you process the single character that was returned from sstringr(). If that takes long enough, the next call to sstringr() will return a String with however many characters there has been time to receive at 130uS per character.

0x13 tells the master to stop sending bytes to the Arduino (XOFF)

sstringr() reads until the serial buffer is empty, meaning that by the time the while(!Serial.available) loop finishes, there's already data in the buffer.

0x11, or XON tells the master to start sending data to the Arduino.

It actually tells the master to stop creating bytes to send. There is no standard as to how many bytes that may be. So the slave has to continue reading and processing those bytes until they stop coming.

Most sources say otherwise...

Ok, I did not see where you had initially sent a command to tell the sender to start sending, and without knowing what is sending the data the 0x11 and 0x13 could do whatever you programmed it to do. That does bring up the question of once sstringr() is called, you have sent the command for the sender to stop sending, where do you send the command to resume sending if nothing is in the serial buffer, leaving content as an empty string? I< edit > I see that there is a while loop at the start of loop() where the code will stay forever until something is in the Serial receive buffer.
There still remains the problem that as soon as there is a single character in the Serial receive buffer, that while loop exits, sstringr() is called immediately afterwards, and then content will contain only a single character because there is not enough time to receive a 2nd character.

There's always time for more characters to fly in at such a baud rate

Most sources never had to actually deal with it!

This potential solution is for nothing because I didn't know the behavior of Termite, and that setting "Send low byte first" literally puts everything into the serial port backwards. Tomorrow I'll test out the final code on the printer.

I think you're right (I'm unsure if my theory is correct or not)

Well, back when dot matrix printers were first introduced, the documentation told to expect an X-OFF character to be sent by the printer when it’s buffer was 10-15 characters from being full.

130uS per character, with an atmega328 running at 16MHz (standard for an UNO or Nano), that give enough time for at worst about 700 instruction to execute between characters. Much more than enough time to exit the while loop, call sstringr(), send the single byte to stop the sender (this only goes into the send buffer, Serial.write() returns does not wait for it to be sent unless the buffer is full), and read from the Serial buffer.

But also consider that you are likely already in the process of receiving a character, and the 0x13 has to itself be sent at the same baud rate, resulting in the sender likely having already started another character by that time.

Mine will be running at 8MHz