Wednesday, January 23, 2013

Print Positioning Demo with Arduino and BPP-440L

Seetron's serial displays have a rich set of features, but the workhorse is the ctrl-P position instruction. Consider this scenario: Your program is sampling several sensors and displaying their outputs on screen, something like this:

Label1: 1234
Label2: 5678
..etc.

As new data comes in, you could clear the screen and reprint everything, all the labels and data. The problem is that each update would produce an annoying flash as the screen goes momentarily blank before the contents are replaced.


A better approach is to print the static text (labels) just once and replace only the changing data. That's the main application of the ctrl-P position instruction--it lets you put the printing position right where you need it in order to update a specific part of the screen.

Syntax of the instruction is simple: ctrl-P position where ctrl-P is a byte/char with the value16 decimal (0x10 hex) and position is screen location + 64. For example, the BPP-440L has four lines of 40 characters each. They're numbered starting at 0 (upper left corner of the screen) and ending at 159 (lower right). So in the example below the "t" in the word "text" is located at position 53.

Add the row and column numbers to get the screen location.
If you don't send any positioning instructions to the BPP-440L, it prints exactly as you'd expect, starting at position 0 and moving left to right across and down the screen. Using the position instruction, you can jump to any screen location and start printing there instead.

Suppose you wanted to replace the word "text" above with "data". You could clear the whole screen and reprint it with the change, or you could send the following: six bytes/chars:

  16 (53+64) d a t a

In an Arduino program, this is very easy to do, translating to

  mySerial.write(16) ;
  mySerial.write(53+64) ;
  mySerial.print("data") ;

However, there's a simpler way to express the same thing using the Streaming library and some #define directives:

  mySerial <<MOVETO <<POSITION "data" ;

..where MOVETO has been previously defined as 16 and POSITION as 53+64.

Without further fanfare, here's an example program that makes lots of use of ctrl-P and Streaming to efficiently fill a 4x40 screen with labels and data. Data updates change only the numbers on the screen, not  the labels, so there's no blinking or flashing. To ensure that new data completely erases old data, there's a simple function that pads or trims the number strings to the correct size.



/*
Demonstrating positioning with the BPP-440L.   
 */

// SoftwareSerial lets us use any I/O pin for serial output 
// to the BPP-440L, saving the hardware UART for the bootloader  
#include <SoftwareSerial.h>  // or other critical comms.

// Streaming allows items for serial output to be grouped
#include <Streaming.h>  // logically on a single line

#define rxPin 255         // Not used, so set to invalid pin #
#define txPin 3           // Hook SER input to Arduino pin 3.
#define inverted 1       // Inverted serial (like RS-232, but TTL)
#define noninverted 0    // Noninverted serial (like UART output)

// Change the definition of SPOL to inverted or noninverted 
// depending on your display configuration. On newer BPP-440Ls, 
// if the SPOL jumper is intact (factory setting), you want 
// "inverted." If it's cut, use "noninverted." A macro will 
#define SPOL inverted

// For inverted serial, the output pin must be LOW when data is 
// not being sent; for noninverted, HIGH. 
#if SPOL
 #define STOPBIT LOW
#else
 #define STOPBIT HIGH
#endif

//=====================================================================
//          PREDEFINED POSITION VALUES FOR DEMO
//
// Position values are 64 + # , where # is the screen position
// at which the next character will print. The BPP-440L has 
// 4 lines of 40 characters (total 160) numbered 0-159 from 
// upper left to lower right.
//
// All of the #s are cast as char types so that they're suitable
// for use with the << streaming operator. A valid alternative 
// would be to use the Serial.write() or SoftwareSerial.write() 
// method, but streaming is (IMO) tidier and easier to read 
// and maintain. 
#define CLS (char) 12             // Clear screen, moveto 0.
#define MOVETO (char) 16          //Position instruction
#define LABEL1 (char) (64+40)     // Position 40, 1st char of 2nd line. 
#define LABEL2 (char) (64+80)     // Position 80, 1st char of 3nd line. 
#define LABEL3 (char) (64+120)    // Position 120, 1st char of 4th line. 
#define LABEL4 (char) (64+60)     // Position 60, middle of 2nd line. 
#define LABEL5 (char) (64+100)    // Position 100, middle of 3nd line. 
#define LABEL6 (char) (64+140)    // Position 140, middle of 4th line. 

#define DATA1 (char) (LABEL1+14)  //LABEL1 position + 14 characters
#define DATA2 (char) (LABEL2+14)  //LABEL2 position + 14 characters
#define DATA3 (char) (LABEL3+14)  //LABEL3 position + 14 characters
#define DATA4 (char) (LABEL4+15)  //LABEL4 position + 15 characters
#define DATA5 (char) (LABEL5+17)  //LABEL5 position + 17 characters
#define DATA6 (char) (LABEL6+14)  //LABEL6 position + 14 characters

//=====================================================================
// Set up a new serial output using the definitions above. 

SoftwareSerial mySerial =  SoftwareSerial(rxPin, txPin, SPOL);

// Create the string object that will be used for our output data.
String printMe = "" ;  

//=====================================================================
// SETUP: Put the serial-output pin in the stop-bit state and 
// set the bit rate to 9600bps. Then print the banner and the 
// fixed labels at the #defined locations. 
void setup()  {
  // define pin modes for tx, rx:
  delay(500) ;
  digitalWrite(txPin, STOPBIT);  // Preset pin to stop-bit state
  mySerial.begin(9600);          // Set the data rate
  delay(10) ;                    // wait.

// Print banner across the top line.
  mySerial <<CLS <<"======= BPP-440L Positioning Demo ======" ;

// Now print each of the labels at their #defined spots.
  mySerial <<MOVETO <<LABEL1 <<"Originality:" ;
  mySerial <<MOVETO <<LABEL2 <<"Flavor:" ;
  mySerial <<MOVETO <<LABEL3 <<"Ingredient:" ;

  mySerial <<MOVETO <<LABEL4 <<"Talent:" ;
  mySerial <<MOVETO <<LABEL5 <<"Evening Gown:" ;
  mySerial <<MOVETO <<LABEL6 <<"Swimsuit:" ;
}

//=====================================================================
// LOOP: The program loop simulates acquiring, formatting and printing 
// data in the locations set by the #defines at the beginning of 
// the program. (Using random() to provide the ersatz data.)
// The function prepstring() is explained below--it trims or pads 
// the string to make sure it completely overwrites old data. 

void loop() {
  printMe = String(random(200));          // Get a random # 0-200, make
  prepstring(printMe,3,false) ;           // it fit 3-char space, left 
  mySerial <<MOVETO <<DATA1 <<printMe ;   // aligned, then print. 
  
  printMe = String(random(20));
  prepstring(printMe,2,false) ;
  mySerial <<MOVETO <<DATA2 <<printMe ;

  printMe = String(random(9));            // No fix needed for 1 digit..
  mySerial <<MOVETO <<DATA3 <<printMe ;

  printMe = String(random(5000));
  prepstring(printMe,4,true) ;
  mySerial <<MOVETO <<DATA4 <<printMe ;

  printMe = String(random(20));
  prepstring(printMe,2,true) ;
  mySerial <<MOVETO <<DATA5 <<printMe ;

  printMe = String(random(15000));
  prepstring(printMe,5,true) ;
  mySerial <<MOVETO <<DATA6 <<printMe ;
    
  delay(300) ;  // Slow down the updates so text is legible. 
}

//=====================================================================
// FUNCTION: prepstring() 
// When you're printing changing data in a fixed location, it's not 
// necessary to erase the old data--just print over it. However, 
// if a short number like "6" is replacing a longer one like "839"
// the old number won't be completely erased. 
//
// This function fixes that problem by padding short numbers with 
// spaces to make sure that they completely overwrite old data.  
// It accepts a String object, a length and a switch for 
// align to the right (true) or the left (false). It modifies 
// the String to the specified length either by shortening it, 
// or by adding spaces to pad it out to the length. Right or
// left alignment controls whether the spaces are added to the 
// beginning of the string or the end.
// 
// The BPP-440L is capable of handling the right-alignment case
// on its own using the ctrl-R instruction. (Most of the 
// displays from seetron.com have this feature.) We'll demo 
// that feature in another app note. 

void prepstring(String & s, int olen, boolean rtalign) {
  int ilen ; 
  ilen = s.length() ;
  if (ilen > olen) {            // If the string is too long
    s = s.substring(0,olen) ;   // shorten it. 
  }
  else {
    while ( ilen < olen ){      // If it's too short, 
      if (rtalign) {            // add spaces to the start
        s = String(" ") + s ;
      }
      else {
        s = s + String(" ") ;   // ..or the end, depending 
      }                         // on desired alignment. 
      ilen ++ ;
    }
  }
}

No comments:

Post a Comment