/* (c)2012 Forward Computing and Control Pty. Ltd. This code may be freely used for both private and commerical use. Provide this copyright is maintained. */ // include the library code: #include #include #include "pfodParser.h" // initialize the library with the numbers of the interface pins LiquidCrystal lcd( 8, 9, 4, 5, 6, 7 ); // set pins to suite freetronics LCD shield // set the next to constants to match your display const int noCharPerDisplayLine = 16; // chars per physical display line const int noOfDisplayLines = 2; // number of lines on physical display int numberOfLines = 0; // number of lines after breaking long lines const int maxChars = 160; // max number of bytes that can be displayed // this is set by the max msg size of 255 less the { ` | } and user prompt byte displayLine[maxChars + 1]; // null terminated boolean blinking = false; // initially no blinking boolean scrolling = true; // initially scroll int displayLineLength = 0; // length of display text as entered by user byte emptyLine[] = ""; // this is test line to display initially byte testLine[] = " test line --\n This is a very\nlong message to display. \nIt tests the\ndisplay of long lines and line \nbreaks. \n \n the last line"; pfodParser parser; void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } //parserSetup(); // initialize pfod Cmd parser // set up the LCD's number of columns and rows: lcd.begin(noCharPerDisplayLine, noOfDisplayLines); //saveLine(emptyLine); saveLine(testLine); // set an initial long display text writeLCDline(displayLine, 0); // display it parser.connect(&Serial); // connect parser to input/output } /** * main loop * * if got any serial input (from pfodApp) * parse it and if found a complete message * cmd will be !=0 * if so process that command and its args * * also processBlinking and scrolling * those methods just return if the settings are false. */ void loop() { byte cmd = 0; cmd = parser.parse(); if (cmd != 0) { byte* rtnMsg = processCmd(cmd); // this handles each command and the args parser.println((char*)rtnMsg); } processBlinking(); processScrolling(); } byte returnMsg[256]; // temp buffer to build return msg in + 1 for terminating null byte emptyCmd[] = "{}"; // change these strings if you want to change what the mobile displays to the user byte mainMenu[] = "{.LCD Display|a~Set Text|b~Setup Display}"; // if changing this one remember to limit display text length, maxChars // so that total msg (including this string) < 255 bytes byte setDisplayLineText_head[] = "{'c`160~Enter text to display.\n (maxmimum 160 chars)|"; byte setupDisplayList_head[] = "{*d"; byte setupDisplayList_tail[] = "~Choose the Display features you want|Blinking|Scrolling}"; /** * processCmd * args * cmd - byte the non-zero cmd to process * return * byte* null terminated response message to send back to pfodApp */ byte* processCmd(byte cmd) { if (cmd == '.') { // getMainMenu msg from pfodApp return mainMenu; // main menu msg } if (cmd == 'a') { // Set Text menu cmd // return text input screen with current text // as text for user to edit. byte* rtnPtr = returnMsg; // add fixed start of return msg rtnPtr = copy(setDisplayLineText_head, rtnPtr); // add current text rtnPtr = copy(displayLine, rtnPtr); *rtnPtr++ = '}'; // terminate msg *rtnPtr++ = 0; return returnMsg; } if (cmd == 'b') { // Setup Display menu cmd // build response base on current settings byte* rtnPtr = returnMsg; // add fixed start of return msg rtnPtr = copy(setupDisplayList_head, rtnPtr); // the args to show the current settings to the user if (blinking) { *rtnPtr++ = '`'; *rtnPtr++ = '0'; } if (scrolling) { *rtnPtr++ = '`'; *rtnPtr++ = '1'; } // add the prompt list of available options rtnPtr = copy(setupDisplayList_tail, rtnPtr); *rtnPtr = 0; // terminate return returnMsg; } if (cmd == 'c') { // process the user's input text return //----------------- debugging output Serial.print("Cmd:"); byte* ptr = parser.getCmd(); Serial.print((char*)ptr); Serial.println(); Serial.print("number of Args:"); Serial.println(parser.getArgsCount()); // ------------------------- byte* idxPtr = parser.getFirstArg(); // get the user's input saveLine(idxPtr); // save an process it writeLCDline(displayLine, 0); // display the processed input return emptyCmd; // return empty cmd // pfodApp will re-request last menu // in this case the main menu. } if (cmd == 'd') { // process user's selected display options //----------------- debugging output Serial.print("Cmd:"); byte* ptr = parser.getCmd(); Serial.print((char *)ptr); Serial.println(); Serial.print("number of Args:"); Serial.println(parser.getArgsCount()); // ------------------------- // start by clearing current settings // as if user de-selected all options // then no args are returned. blinking = false; scrolling = false; byte* idxPtr = parser.getFirstArg(); long listIdx = -1; for (int i = 0; i < parser.getArgsCount(); i++) { // loop through the args (if any) // parse each one as a number // this returns pointer to next arg idxPtr = parser.parseLong(idxPtr, &listIdx); // process the arg just parsed if (listIdx == 0) { blinking = true; } if (listIdx == 1) { scrolling = true; } } //------------ debugging output if (blinking) { Serial.println("Set Blinking On"); } else { Serial.println("Set Blinking Off"); } if (scrolling) { Serial.println("Set Scrolling On"); } else { Serial.println("Set Scrolling Off"); } // ------------------------------- return emptyCmd; // return empty cmd // pfodApp will re-request last menu // in this case the main menu. } // should not get here // but just in case, for everything // just return empty cmd return emptyCmd; // default return } long nextScrollTime = 0; const long scrollTime = 4000; int scrollOffset = 0; /** * Each scrollTime interval * increase the scrollOffset by noOfDisplayLines * and display then next noOfDisplayLines on the physical display */ void processScrolling() { if (!scrolling) { // option not set // just display first line scrollToTop(); // do this differently if this write causes // the display to flash writeLCDline(displayLine, scrollOffset); return; } // // if millis > timeout toggle blink if (millis() > nextScrollTime) { nextScrollTime = 0; // update below scrollOffset = scrollOffset + noOfDisplayLines; if (scrollOffset > numberOfLines ) { scrollOffset = 0; } writeLCDline(displayLine, scrollOffset); } if (nextScrollTime == 0) { nextScrollTime = millis() + scrollTime; } } /** * clear the scroll offset and set scroll time * time is set here so first screen is displayed * for its full time on startup. */ void scrollToTop() { scrollOffset = 0; nextScrollTime = millis() + scrollTime; } boolean blink = false; // true if display currently blinked long nextBlinkTime = 0; const long blinkTime = 600; /** * Each blinkTime turn the display on or off * to make it blink. */ void processBlinking() { if (!blinking) { // option not set // set lcd to display, clear values and return lcd.display(); blink = false; nextBlinkTime = 0; return; } // else need to toggle display // if millis > timeout toggle blink if (millis() > nextBlinkTime) { nextBlinkTime = 0; // update below if (blink) { lcd.noDisplay(); blink = false; } else { lcd.display(); blink = true; } if (nextBlinkTime == 0) { // set next blink time nextBlinkTime = millis() + blinkTime; } } } /** * save the users input text * saved in displayLine * for display to user later for editing. * * args byte* idxPtr -- pointer to start of null terminated * user input, usually the argument from the {c message */ void saveLine(byte*idxPtr) { scrollToTop(); int i = 0; while ((i < maxChars) && (*idxPtr != 0)) { // put text displayLine[i] = *idxPtr; ++i; ++idxPtr; } displayLineLength = i; // add terminating null displayLine[i] = 0; // calculate the number of lines numberOfLines = countLines(displayLine); // ------------------ debugging output Serial.println("Saved Text"); Serial.println("------------------------"); Serial.print((char*)displayLine); Serial.println(); Serial.println("------------------------"); Serial.print("text length:"); Serial.print(displayLineLength); Serial.print(" number of Lines:"); Serial.println(numberOfLines); // -------------------------------- } /** * counts the number of physical display lines * lines are terminated by \n * lines longer then noCharPerDisplayLine are logically broken * and will be displayed on multiple lines */ int countLines(byte* text) { // count the lines int noLines = 1; // even if empty text int k = 0; int i = 0; while (text[i] != 0) { for (k = 0; (k < noCharPerDisplayLine) && (text[i] != '\n') && (text[i] != 0); k++, i++) { //just increment k and i looking for next \n or null or noCharPerDisplayLine limit } // found \n or null or counted noCharPerDisplayLine if (i < displayLineLength) { // not end of text so increment line count noLines++; } if (text[i] == '\n') { // skip new line char if found one i++; } } // round up to next multiple of noOfDisplayLines if ((noLines - noOfDisplayLines * (noLines / noOfDisplayLines)) != 0) { noLines = ((noLines / noOfDisplayLines) + 1) * noOfDisplayLines; } return noLines; } /** * Mimics countLines to find the starting offset of a * given physical display line. * args: * byte* text --- the null terminated text to display * int fromLine -- the physical display line to display, first line is 0 */ byte* getLineOffset(byte* text, int fromLine) { // count the lines int noLines = 0; int k = 0; int i = 0; while ((text[i] != 0) && (noLines < fromLine)) { for (k = 0; (k < noCharPerDisplayLine) && (text[i] != '\n') && (text[i] != 0); k++, i++) { } if (i < displayLineLength) { noLines++; } if (text[i] == '\n') { i++; } } return text + i; // will return null if fromLine too large } /** * Write physical display lines to the display * starting from fromLine. * Writes noOfDisplayLines lines or fills with blanks * if not enough text available. * args * byte* text --- the null terminated text to display * int fromLine -- the physical display line to display, first line is 0 */ void writeLCDline(byte* text, int fromLine) { byte *idxOffset = getLineOffset(text, fromLine); int k = 0; for (int j = 0; j < noOfDisplayLines; j++) { lcd.setCursor(0, j); for (k = 0; k < noCharPerDisplayLine && (*idxOffset != 0) && (*idxOffset != '\n'); k++, idxOffset++) { lcd.write(*idxOffset); } for ( ; k < noCharPerDisplayLine; k++) { lcd.write(' '); } if (*idxOffset == '\n') { idxOffset++; } } } /** * Utility method * Copy from src to dest * stop at first null in src * return dest updated to next position */ byte* copy(byte* src, byte* dest) { while (*src != 0) { *dest++ = *src++; } return dest; }