Pages: [1]   Go Down
Author Topic: Yellowjacket + PS2/You LED sign + WiServer = *almost AWESOME* (Out of memory?)  (Read 816 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a PS2You LED message board that I decided would make a great wall clock/calendar/weather station/etc. and connected it to a Rugged Circuits Yellowjacket. The intent is to update the LED message board over wifi using a simple REST-like interface. After some experimentation, I can get the REST services working over wifi, or I can get the LED message board to update, but I can't seem to do both. This appears to be an issue with the Yellowjacket running out of SRAM while attempting to use both the WiServer library and the DisplayMatrix library at the same time.

I've posted my code below (which could probably be refactored some to decrease the SRAM memory usage) but I suspect that I'm going to need to refactor one or both libraries to get enough free memory to make everything work properly. Can anyone offer any help or suggestions on the best way to approach this? I'm afraid its been a long time since I've had to do any optimization of this type... smiley

Code:
/*
 * A simple sketch that uses WiServer to recieve a web call and update the display
 */
// for the wifi:
#include <EEPROM.h>
#include <TinyREST.h>
#include <WiServer.h>
// for the display:
#include <string.h>
#include "MatrixDisplay.h"
#include "DisplayToolbox.h"
#include "font.h"

#define WIRELESS_MODE_INFRA 1
#define WIRELESS_MODE_ADHOC 2

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {192,168,1,20}; // IP address of WiShield
unsigned char gateway_ip[] = {192,168,1,1}; // router or gateway IP address
unsigned char subnet_mask[] = {255,255,255,0}; // subnet mask for the local network
const prog_char ssid[] PROGMEM = {"TESTSSID"}; // max 32 bytes

unsigned char security_type = 3; // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"testpassphrase"}; // max 64 characters

// WEP 128-bit keys
// sample HEX keys
prog_uchar wep_keys[] PROGMEM = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // Key 0
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 1
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 2
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Key 3
};

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;
// End of wireless configuration parameters ----------------------------------------

// Matrix display configuration parameters ----------------------------------------

const int numLines = 6;        // Max number of lines
const int displayTime = 2500;  // Length of time in milliseconds to display each line
const int gapTime = 10;        // Time gap between lines
const int maxLineLength = 100; // Maximum length of a line in characters

String textLines[numLines];     // Array of text lines

int textLength;          // Holds current line length in pixels
int strLength;           // Holds current line length in characters
int currentLine = 0;     // Index of current line


char defaultText[numLines][30] = {{"Awaiting input..."}};

// Character lookup string -- used to map the characters from the keyboard onto the font data array
// (This should probably be moved to program memory in some form in the future)
String charLookup  = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*(),-.?></\\|[]_=+:'\"{}";

// Macro to make it the initDisplay function a little easier to understand
#define setMaster(dispNum, CSPin) initDisplay(dispNum,CSPin,true)
#define setSlave(dispNum, CSPin) initDisplay(dispNum,CSPin,false)

// Initialize matrix
//MatrixDisplay disp(3,16,15, true);
//DisplayToolbox toolbox(&disp);

// Prepare boundaries
uint8_t X_MAX = 0;
uint8_t Y_MAX = 0;

#define is_printable(c) (!(c&0x80))  

int line = 0;
const unsigned int MAX_INPUT = 100;

// End of matrix display configuration parameters ----------------------------------------

TinyREST rest = TinyREST();
static char cmd_read_string[] = {"string_read"};
static char cmd_write_string[] = {"string_write"};

// This is our page serving function that generates web pages
boolean sendMyPage(char* URL) {
  
  return rest.handleURL(URL);
  
}


void setup() {
  // Initialize WiServer and have it use the sendMyPage function to serve pages
  rest.init();
  rest.addCommand(cmd_read_string, 2, testCallback);
  rest.addCommand(cmd_write_string, 2, testCallback);
  WiServer.init(sendMyPage);
  
  // Enable Serial output and ask WiServer to generate log messages (optional)
  Serial.begin(57600);
  WiServer.enableVerboseMode(true);
  
  // Enable matrix display
//  for(int i=0; i<numLines; i++){
//    textLines[i] = defaultText[i];
//    Serial.println(defaultText[i]);
//  }  
//
//  // Fetch display bounds
//  X_MAX = disp.getDisplayCount() * (disp.getDisplayWidth()-1)+3;
//  Y_MAX = disp.getDisplayHeight()-1;
//
//  // Prepare displays
//  disp.setMaster(0,4);
//  disp.setSlave(1,5);
//  disp.setSlave(2,6);
}

void loop(){

  // Run WiServer
  WiServer.server_task();
  rest.loop();
  delay(10);
}

//This is where the lines being displayed on the message board can be updated (or read). To be implemented when memory issues are resolved.
static int testCallback(TinyREST *srv, char *cmd, int len, char **args, void *blind){
  if (strcmp(cmd, cmd_read_string)==0) {
    //read the string
    WiServer.print("<html>");
    WiServer.print("***Pretending to read string!");
    WiServer.print("</html>");
    Serial.println("***Pretending to read string!");
    return RESPONSE_INLINE_OK;
  } else if (strcmp(cmd, cmd_write_string)==0) {
    //write the string
    WiServer.print("<html>");
    WiServer.print("***Pretending to write string!");
    WiServer.print("</html>");
    Serial.println("***Pretending to write string!");
    return RESPONSE_INLINE_OK;
  }
  return RESPONSE_OK;
}
« Last Edit: March 15, 2013, 01:57:30 pm by Catfang » Logged

Switzerland
Offline Offline
Faraday Member
**
Karma: 108
Posts: 5144
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Get rid of every occurrence of the String class in your code. Use C-like character arrays instead.

The String class is not programmed in an embedded-friendly style and therefore clobbers your memory in no time. Additionally the IDEs (at least up to 1.0.3) have a bug which results in a memory leak when the String class is used.

Then use the F() macro when you have a string literal in a call to print() or println() methods.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay, I've removed all the String instances, and converted as much as I can to PROGMEM as well. After all this optimization, the board is still unable to correctly run both the web server and the display. Using a memory check function I found, I appear to still have anywhere from 200 to 400 bytes free of SRAM, so although I'm near maxed, this should be working.

Strange thing is, if you comment out the "Update Display" part of the loop() function, the web server works. If you keep the code present, then the web server doesn't respond to requests and only one of the three LED panels appears to work. I'm getting closer, but I'm still missing something! Can anyone help??

Code:
/*
*    REST-driven LED Display board
*   
*
*/


// for the wifi:
#include <EEPROM.h>
#include <TinyREST.h>
#include <WiServer.h>
// for the display:
#include <string.h>
#include "MatrixDisplay.h"
#include "DisplayToolbox.h"
#include "font.h"


#define WIRELESS_MODE_INFRA 1
#define WIRELESS_MODE_ADHOC 2

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {192,168,1,20}; // IP address of WiShield
unsigned char gateway_ip[] = {192,168,1,1}; // router or gateway IP address
unsigned char subnet_mask[] = {255,255,255,0}; // subnet mask for the local network
const prog_char ssid[] PROGMEM = {"SSID"}; // max 32 bytes

unsigned char security_type = 3; // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"passphrase"}; // max 64 characters

// WEP 128-bit keys
// sample HEX keys
prog_uchar wep_keys[] PROGMEM = {};

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;
// End of wireless configuration parameters ----------------------------------------

// Matrix display configuration parameters ----------------------------------------

const int numLines = 1;        // Max number of lines
const int displayTime = 2500;  // Length of time in milliseconds to display each line
const int gapTime = 10;        // Time gap between lines
const int maxLineLength = 50; // Maximum length of a line in characters

char lines[numLines][maxLineLength];
char text[maxLineLength];

int textLength;          // Holds current line length in pixels
int strLength;           // Holds current line length in characters
int currentLine = 0;     // Index of current line

const prog_char defaultText[] PROGMEM = {"Awaiting input..."};
const prog_char msgRecvText[] PROGMEM = {"<html><p>Message Recieved By Server</p></html>"};
const prog_char msgRecvStartText[] PROGMEM = {"<html><p>"};
const prog_char msgRecvEndText[] PROGMEM = {"</p></html>"};

// Character lookup string -- used to map the characters from the keyboard onto the font data array
const prog_char charLookup[] PROGMEM = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*(),-.?></\\|[]_=+:'\"{}";

// Memory test
extern int __bss_end;
extern int *__brkval;


// Macro to make it the initDisplay function a little easier to understand
#define setMaster(dispNum, CSPin) initDisplay(dispNum,CSPin,true)
#define setSlave(dispNum, CSPin) initDisplay(dispNum,CSPin,false)

// Initialize matrix
MatrixDisplay disp(3,16,15, false);
DisplayToolbox toolbox(&disp);

// Prepare boundaries
uint8_t X_MAX = 0;
uint8_t Y_MAX = 0;

// End of matrix display configuration parameters ----------------------------------------

TinyREST rest = TinyREST();
static char cmd_read_string[] = {"string_read"};
static char cmd_write_string[] = {"string_write"};

int lineNum;

// This is our page serving function that generates web pages
boolean sendMyPage(char* URL) {
  return rest.handleURL(URL);
}


void setup() {
  // Initialize WiServer and have it use the sendMyPage function to serve pages
  rest.init();
  rest.addCommand(cmd_read_string, 2, testCallback);
  rest.addCommand(cmd_write_string, 2, testCallback);
  WiServer.init(sendMyPage);
 
  // Enable Serial output and ask WiServer to generate log messages (optional)
  Serial.begin(57600);
  //WiServer.enableVerboseMode(true);
  Serial.println("PROGRAM START");
 
  // Enable matrix display
  for(int i=0; i<numLines; i++){
    strcpy_P(lines[i], defaultText);
  }
 
  // Fetch display bounds
  X_MAX = disp.getDisplayCount() * (disp.getDisplayWidth()-1)+3;
  Y_MAX = disp.getDisplayHeight()-1;
 
  // Prepare displays
  disp.setMaster(0,4);
  disp.setSlave(1,5);
  disp.setSlave(2,6);
}

void loop(){

  // Run WiServer
  WiServer.server_task();
  rest.loop();
 
  // Update Display
  strLength = strlen(lines[currentLine]); // Number of characters in the string
  textLength = (strLength*6); // Width of string in pixels
 
  strcpy(text, lines[currentLine]);
 
  //** Uncommenting this section causes the web server to not respond!
 
//  if(strLength > 1){
//     if(textLength < (X_MAX +1)){
//       fixedText(text);
//       delay(displayTime);
//       disp.clear();
//       disp.syncDisplays();
//       delay(gapTime);
//     }else{
//       scrollText(text);
//       delay(gapTime);
//     }
//   }
//   nextLine();
   
   //get_free_memory();
}

//This is where the lines being displayed on the message board can be updated (or read).
static int testCallback(TinyREST *srv, char *cmd, int len, char **args, void *blind){
  if (strcmp(cmd, cmd_read_string)==0) {
    //read the string
    //To be implemented if there is space.
   WiServer.print_P(msgRecvText);
    return RESPONSE_INLINE_OK;
  } else if (strcmp(cmd, cmd_write_string)==0) {
    //write the string
    if (len == 2)
    {
      lineNum = atoi(args[0]);
      if (lineNum >= 0 && lineNum < maxLineLength)
      {
        strcpy(lines[lineNum], args[1]);
        WiServer.print_P(msgRecvStartText);
        WiServer.print(F("Wrote "));
        WiServer.print(args[1]);
        WiServer.print(F(" to line "));
        WiServer.print(lineNum);
        WiServer.print_P(msgRecvEndText);
      }
    } else {
      WiServer.print_P(msgRecvText);
    }
    return RESPONSE_INLINE_OK;
  }
  return RESPONSE_OK;
}

void nextLine(){
   if(currentLine < (numLines -1)){
      currentLine++;
   }else{
     currentLine = 0;
   }   
}

void scrollText(char* text){
  int y=1;
  int endPos = 0 - textLength;
  int loopCount = 0;
  for(int Xpos = X_MAX; Xpos > endPos; Xpos--){
    loopCount++;
    disp.clear();
    drawString(Xpos,y,text);
    disp.syncDisplays();
  }
}


// Write a line of static (non-scrolling) text to the display
void fixedText(char* text){
  int y = 1;
  int x = 0;
  disp.clear();
  drawString(x,y,text);
  disp.syncDisplays();
}


// Output a string to the display
void drawString(int x, uint8_t y, char* c){
  for(char i=0; i< strLength; i++){
    drawChar(x, y, c[i]);
    x+=6; // Width of each glyph
  }
}


// Output a single character to the display
void drawChar(int x, int y, char c){
  int dots;
 
  c = getIndex(charLookup, c);
 
  for (char col=0; col< 5; col++) {
    if((x+col+1)>0 && x < X_MAX){ // dont write to the display buffer if the location is out of range
      dots = pgm_read_byte_near(&myfont[c][col]);
      for (char row=0; row < 7; row++) {
        if (dots & (64>>row))         // only 7 rows.
          toolbox.setPixel(x+col, y+row, 1);
        else
          toolbox.setPixel(x+col, y+row, 0);
      }
    }
  }
}

int getIndex(const prog_char* str, char c)
{
  const prog_char* e = strchr_P(str, c);
  if (e) {
    return (int)(e - str);
  }
  else
    return -1;
}

// Memory check
int get_free_memory(){
  int free_memory;
  if((int)__brkval == 0)
    free_memory = ((int)&free_memory) - ((int)&__bss_end);
  else
    free_memory = ((int)&free_memory) - ((int)__brkval);
    Serial.println(free_memory);
  return free_memory;
}

Logged

Switzerland
Offline Offline
Faraday Member
**
Karma: 108
Posts: 5144
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sure it doesn't work this way, you're freezing your Arduino for 2.5 seconds which is an incredibly long time in network regards. Take a look at the BlinkWithoutDelay example to find out how you can have timed events in the loop without using delay().

Code:
//  if(strLength > 1){
//     if(textLength < (X_MAX +1)){
//       fixedText(text);
//       delay(displayTime);
//       disp.clear();
//       disp.syncDisplays();
//       delay(gapTime);
//     }else{
//       scrollText(text);
//       delay(gapTime);
//     }
//   }
//   nextLine();
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Pylon! That makes complete sense. I'll work on refactoring that code right now! I believe that will fix the web server not responding issue.

I'm still worried about the display not fully working though, so I guess that will be next to investigate after this...
« Last Edit: March 15, 2013, 02:29:01 pm by Catfang » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay, getting REALLY close now!!! I refactored the code Pylon pointed out having blocking delays:

Code:
// Update Display
  currentMillis = millis();
 
  //Simple FSM for the display
  switch (current_state)
  {
    case INITIALSTATE:
     
      get_free_memory();
   
      // Update Display
      strLength = strlen(lines[currentLine]); // Number of characters in the string
      textLength = (strLength*6); // Width of string in pixels
 
      strcpy(text, lines[currentLine]);
      //Determine text type, either fixed or scrolling
      if (strLength > 1) {
        if (textLength < (X_MAX + 1)) {
          // Use fixedText()
          fixedText(text);
          current_state = FIXEDWAITINGLONGSTATE;
        } else {
          // Use scrollText()
          scrollText(text);
          current_state = SCROLLWAITINGSTATE;
        }
        // Update times
        previousMillis = currentMillis;
      } else {
        // No text on this line to print. Procede to next line. Same state.
        nextLine();
      }
      break;
    case SCROLLWAITINGSTATE:
      // Are we still waiting on the gap delay?
      if (currentMillis - previousMillis > gapTime) {
        // Gap delay complete
        nextLine();
        current_state = INITIALSTATE;
      }
      break;
    case FIXEDWAITINGLONGSTATE:
      // Are we still waiting on the display delay?
      if (currentMillis - previousMillis > displayTime) {
        // Display delay complete
        disp.clear();
        disp.syncDisplays();
        previousMillis = currentMillis;
        current_state = FIXEDWAITINGSHORTSTATE;
      }
      break;
    case FIXEDWAITINGSHORTSTATE:
      // Are we still waiting on the gap delay?
      if (currentMillis - previousMillis > gapTime) {
        // Gap delay complete
        nextLine();
        current_state = INITIALSTATE;
      }
      break;
  }

So now the webserver will respond, but for some reason if the text is long enough to scroll, then only one of the 3 LED panels actually displays the scrolling text. Static text correctly shows up on all 3 LED panels. Ideas?

[EDIT] - Upon further examination, it appears that everything works correctly as long as the scrollText function isn't used. If the text is long enough to start ScrollText, I lose two of the displays (the leftmost two) and the web server quits responding again. I'm going to start digging into the scrollText code and see if I can figure out what's going on.

[EDIT 2] - Looks like turning the displays shadow buffer to "true" fixed the part where two of the displays quit working. However, the scrolling is still a blocking action and is preventing the web server from responding while active. I need to figure out a way to unblock the scrolling function...
« Last Edit: March 15, 2013, 05:02:12 pm by Catfang » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's my final [reasonably] stable code, in case its helpful to anyone else. I optimized it as much as I can, but know there is probably more possible. Let me know if you see anything else to help improve performance or stability! I might put this up as an Instructable later so all feedback is welcome. smiley

Code:
/*
*    REST-driven LED Display board
*    
*    To use, point a browser to your IP address along with the REST commands.
*    Example: http://192.168.1.20/read will display what is currently be displayed on the board.
*    Example 2: http://192.168.1.20/write/1/OneLongTest will write OneLongTest to line 1 of the board.
*/


// for the wifi:
#include <EEPROM.h>
#include <TinyREST.h>
#include <WiServer.h>
// for the display:
#include <string.h>
#include "MatrixDisplay.h"
#include "DisplayToolbox.h"
#include "font.h"


#define WIRELESS_MODE_INFRA 1
#define WIRELESS_MODE_ADHOC 2

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {192,168,1,20}; // IP address of WiShield
unsigned char gateway_ip[] = {192,168,1,1}; // router or gateway IP address
unsigned char subnet_mask[] = {255,255,255,0}; // subnet mask for the local network
const prog_char ssid[] PROGMEM = {"Your_SSID_Here"}; // max 32 bytes

unsigned char security_type = 3; // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"Your_password_here"}; // max 64 characters

// WEP 128-bit keys
// sample HEX keys
prog_uchar wep_keys[] PROGMEM = {};

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;
// End of wireless configuration parameters ----------------------------------------

// Matrix display configuration parameters ----------------------------------------

const int numLines = 5;        // Max number of lines
const int displayTime = 2500;  // Length of time in milliseconds to display each line
const int gapTime = 10;        // Time gap between lines
const int maxLineLength = 30; // Maximum length of a line in characters

char lines[numLines][maxLineLength];
char text[maxLineLength];

int textLength;          // Holds current line length in pixels
int strLength;           // Holds current line length in characters
int currentLine = 0;     // Index of current line

const prog_char defaultText[] PROGMEM = {"I've been reset!"};
const prog_char msgRecvText[] PROGMEM = {"<html><p>Message Recieved By Server</p></html>"};
const prog_char msgRecvStartText[] PROGMEM = {"<html><p>"};
const prog_char msgRecvEndText[] PROGMEM = {"</p></html>"};

// Character lookup string -- used to map the characters from the keyboard onto the font data array
const prog_char charLookup[] PROGMEM = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*(),-.?></\\|[]_=+:'\"{}";

// Macro to make it the initDisplay function a little easier to understand
#define setMaster(dispNum, CSPin) initDisplay(dispNum,CSPin,true)
#define setSlave(dispNum, CSPin) initDisplay(dispNum,CSPin,false)

// Initialize matrix
MatrixDisplay disp(3,16,15, true);
DisplayToolbox toolbox(&disp);

// Prepare boundaries
uint8_t X_MAX = 0;
uint8_t Y_MAX = 0;

// End of matrix display configuration parameters ----------------------------------------

TinyREST rest = TinyREST();
static char cmd_read_string[] = {"read"};
static char cmd_write_string[] = {"write"};

// Define states for FSM
#define INITIALSTATE 0
#define SCROLLWRITINGSTATE 1
#define SCROLLWAITINGSTATE 2
#define FIXEDWAITINGLONGSTATE 3
#define FIXEDWAITINGSHORTSTATE 4


int lineNum;
long previousMillis = 0;
long currentMillis = 0;
int current_state = INITIALSTATE;

int end_pos;
int x_pos;

// This is our page serving function that generates web pages
boolean sendMyPage(char* URL) {
  return rest.handleURL(URL);
}


void setup() {
  // Initialize WiServer and have it use the sendMyPage function to serve pages
  rest.init();
  rest.addCommand(cmd_read_string, 0, testCallback);
  rest.addCommand(cmd_write_string, 2, testCallback);
  WiServer.init(sendMyPage);
  
  // Enable Serial output and ask WiServer to generate log messages (optional)
  Serial.begin(57600);
  
  // Enable matrix display, set default text
  strcpy_P(lines[0], defaultText);
  
  // Fetch display bounds
  X_MAX = disp.getDisplayCount() * (disp.getDisplayWidth()-1)+3;
  Y_MAX = disp.getDisplayHeight()-1;
 
  // Prepare displays
  disp.setMaster(0,4);
  disp.setSlave(1,5);
  disp.setSlave(2,6);
}

void loop(){

  // Run WiServer
  WiServer.server_task();
  rest.loop();
  
  // Update Display
  currentMillis = millis();
  
  //Simple FSM for the display
  switch (current_state)
  {
    case INITIALSTATE:
      // Update Display
      strLength = strlen(lines[currentLine]); // Number of characters in the string
      textLength = (strLength*6); // Width of string in pixels
  
      strcpy(text, lines[currentLine]);
      //Determine text type, either fixed or scrolling
      if (strLength >= 1) {
        if (textLength < (X_MAX + 1)) {
          // Use fixedText()
          fixedText(text);
          current_state = FIXEDWAITINGLONGSTATE;
        } else {
          // Use scrollText()
          current_state = SCROLLWRITINGSTATE;
          end_pos = 0 - textLength;
          x_pos = X_MAX;
        }
        // Update times
        previousMillis = currentMillis;
      } else {
        // No text on this line to print. Procede to next line. Same state.
        nextLine();
      }
      break;
    case SCROLLWRITINGSTATE:
      // Write one scrolling line
      if (x_pos > end_pos) {
        disp.clear();
        drawString(x_pos, 1, text);
        disp.syncDisplays();
        x_pos--;
      } else {
        current_state = SCROLLWAITINGSTATE;
      }
      break;
    case SCROLLWAITINGSTATE:
      // Are we still waiting on the gap delay?
      if (currentMillis - previousMillis > gapTime) {
        // Gap delay complete
        nextLine();
        current_state = INITIALSTATE;
      }
      break;
    case FIXEDWAITINGLONGSTATE:
      // Are we still waiting on the display delay?
      if (currentMillis - previousMillis > displayTime) {
        // Display delay complete
        disp.clear();
        disp.syncDisplays();
        previousMillis = currentMillis;
        current_state = FIXEDWAITINGSHORTSTATE;
      }
      break;
    case FIXEDWAITINGSHORTSTATE:
      // Are we still waiting on the gap delay?
      if (currentMillis - previousMillis > gapTime) {
        // Gap delay complete
        nextLine();
        current_state = INITIALSTATE;
      }
      break;
  }
        
  //get_free_memory();
}

//This is where the lines being displayed on the message board can be updated (or read).
static int testCallback(TinyREST *srv, char *cmd, int len, char **args, void *blind){
  if (strcmp(cmd, cmd_read_string)==0) {
    //read the string
    WiServer.print_P(msgRecvStartText);
    for (int i = 0; i < numLines; i++) {
      if ( strlen(lines[i]) > 1 )
        WiServer.print(i);
        WiServer.print(lines[i]);
        WiServer.print("<br \\>");
      }
    WiServer.print_P(msgRecvEndText);
    return RESPONSE_INLINE_OK;
  } else if (strcmp(cmd, cmd_write_string)==0) {
    //write the string
    if (len == 2)
    {
      lineNum = atoi(args[0]);
      if (lineNum >= 0 && lineNum < maxLineLength)
      {
        if (strlen(args[1]) < maxLineLength)
          strcpy(lines[lineNum], args[1]);
        WiServer.print_P(msgRecvStartText);
        WiServer.print(F("Wrote "));
        WiServer.print(lines[lineNum]);
        WiServer.print(F(" to line "));
        WiServer.print(lineNum);
        WiServer.print_P(msgRecvEndText);
      }
    } else {
      WiServer.print_P(msgRecvText);
    }
    return RESPONSE_INLINE_OK;
  }
  return RESPONSE_OK;
}

void nextLine(){
   if(currentLine < (numLines -1)){
      currentLine++;
   }else{
     currentLine = 0;
   }    
}


// Write a line of static (non-scrolling) text to the display
void fixedText(char* text){
  int y = 1;
  int x = 0;
  disp.clear();
  drawString(x,y,text);
  disp.syncDisplays();
}


// Output a string to the display
void drawString(int x, uint8_t y, char* c){
  for(char i=0; i< strLength; i++){
    drawChar(x, y, c[i]);
    x+=6; // Width of each glyph
  }
}


// Output a single character to the display
void drawChar(int x, int y, char c){
  int dots;
  
  c = getIndex(charLookup, c);
  
  for (char col=0; col< 5; col++) {
    if((x+col+1)>0 && x < X_MAX){ // dont write to the display buffer if the location is out of range
      dots = pgm_read_byte_near(&myfont[c][col]);
      for (char row=0; row < 7; row++) {
        if (dots & (64>>row))       // only 7 rows.
          toolbox.setPixel(x+col, y+row, 1);
        else
          toolbox.setPixel(x+col, y+row, 0);
      }
    }
  }
}

int getIndex(const prog_char* str, char c)
{
  const prog_char* e = strchr_P(str, c);
  if (e) {
    return (int)(e - str);
  }
  else
    return -1;
}

Logged

Illinois, USA
Offline Offline
Full Member
***
Karma: 0
Posts: 114
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

CatFang, if you have a few minutes I could really use your help, with your PS2You experience.

I'm trying to get a PS2You display sketch to compile to do an initial test, but I'm getting a few compile errors.

Did you have any such problems?   Could this have been coded in Arduino Vers prior to 1.0?

Any advise appreciated.

My sketch:
Code:
#include <ht1632_cmd.h>
#include <images.h>

/*
  PS/2/You -- Takes text input from a PS/2 keyboard and displays it on
  cascaded Sure 0832 LED matrix panels.
   
  Thanks to the authors of the PS2Keyboard (Ext2) and MatrixDisplay libraries
  on which this project is based.
 
  See the README.txt file for instructions on controlling the display output
  with the keyboard and editing font glyphs.

  This code is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This code is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <string.h>
#include <MatrixDisplay.h>
#include <DisplayToolbox.h>
#include <font.h>
#include <PS2Keyboard.h>

const int numLines = 6;        // Max number of lines
const int displayTime = 1500;  // Length of time in milliseconds to display each line
const int gapTime = 10;        // Time gap between lines
const int maxLineLength = 100; // Maximum length of a line in characters

String textLines[numLines];     // Array of text lines

int textLength;          // Holds current line length in pixels
int strLength;           // Holds current line length in characters
int currentLine = 0;     // Index of current line

int mode = 2;            // Determines whether in input (1) or output (2) mode

char defaultText[numLines][30] = {{"Hello World!"},{"Welcome to PS/2/You"},{"Start typing..."}};

// Character lookup string -- used to map the characters from the keyboard onto the font data array
// (This should probably be moved to program memory in some form in the future)
String charLookup  = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*(),-.?></\\|[]_=+:'\"{}";

// Memory test
extern int __bss_end;
extern int *__brkval;

/* DISPLAY LIBRARY SETUP */

// Parameters for Matrix Display initialization:
// Number of displays = 3
// Data pin = 10
// WR pin == 11
// Shadow buffer = true

// Macro to make it the initDisplay function a little easier to understand
#define setMaster(dispNum, CSPin) initDisplay(dispNum,CSPin,true)
#define setSlave(dispNum, CSPin) initDisplay(dispNum,CSPin,false)

// Initialize matrix
MatrixDisplay disp(3,11,10, true);
DisplayToolbox toolbox(&disp);

// Prepare boundaries
uint8_t X_MAX = 0;
uint8_t Y_MAX = 0;


/* KEYBOARD SETUP */

#define KBD_CLK_PIN  3
#define KBD_DATA_PIN 7

#define is_printable(c) (!(c&0x80))   

PS2Keyboard keyboard;



void setup() {
 
  //Serial.begin(9600); 

  for(int i=0; i<numLines; i++){
    textLines[i] = defaultText[i];
    //Serial.println(defaultText[i]);
  }
 
  // Initialize the keyboard class
  keyboard.begin(KBD_DATA_PIN);
 
 
  // Fetch display bounds
  X_MAX = disp.getDisplayCount() * (disp.getDisplayWidth()-1)+3;
  Y_MAX = disp.getDisplayHeight()-1;
 
  // Prepare displays
  disp.setMaster(0,4);
  disp.setSlave(1,5);
  disp.setSlave(2,6);
}

void loop() {
 
 
  while (keyboard.available()) {
   
    // read the key
    byte c = keyboard.read();
   
    if (mode == 2){
      mode = 1; // If any keyboard data is entered, switch to input mode
      prevLine();
      if(c != PS2_KC_ENTER){ // Ditch the first char entered on going into input mode
        return;
      }
    }
   
    //Serial.println(c);
   
    if (c == PS2_KC_ENTER) { // Pressing enter goes into output mode
        mode = 2;
    } else if (c == PS2_KC_ESC) { // ESC erases current line
      textLines[currentLine] = "";
    } else if (c == PS2_KC_UP)  { // Up arrow goes to previous line (or loops to last line)
      prevLine();
    } else if (c == PS2_KC_DOWN) { // Down arrow goes to next line
      nextLine();
    } else if (c == PS2_KC_BKSP) { // Truncate the current line by one character
      String thisLine = textLines[currentLine];
      textLines[currentLine] = thisLine.substring(0,(thisLine.length()-1));
    } else if (strLength < maxLineLength && is_printable(c)){  // Otherwise add the character to the line, if the line is not too long
      textLines[currentLine] += char(c);
      if(get_free_memory() < 80){
        low_memory_alert();
      }
    }
  }
 
 
  strLength = textLines[currentLine].length(); // Number of characters in the string
  textLength = (strLength*6); // Width of string in pixels
 
  // Convert the String into a char array
  char* text;
  char textMem[maxLineLength];
  for(int i=0;i<maxLineLength;i++){
    textMem[i]=textLines[currentLine][i];
  }
  text = textMem;
 
 
  if(mode == 2){ // Output mode
     if(strLength > 1){
       if(textLength < (X_MAX +1)){
         fixedText(text);
         delay(displayTime);
         disp.clear();
         disp.syncDisplays();
         delay(gapTime);
       }else{
         scrollText(text);
         delay(gapTime);
       }
     }
     nextLine();
  }else{ // Input mode
    if(textLength > X_MAX){ // If the line is longer than the display, trim off the beginning
      int displayLengthChars = (X_MAX/6)+1;             
      char out[displayLengthChars];                     
      int startChar = (strLength - displayLengthChars); 
      for(int i = 0; i < displayLengthChars; i++){
        startChar++;
        out[i] = text[startChar];
      }
      fixedText(out);
    }else{
      fixedText(text);
    }
  }
}

void nextLine(){
   if(currentLine < (numLines -1)){
      currentLine++;
   }else{
     currentLine = 0;
   }   
}

void prevLine(){
  if(currentLine > 0){
     currentLine--;
  } else {
     currentLine = (numLines -1);
  }
}


// Scroll a line of text across the display

/* This is a fairly inefficient function. It uses drawString() to
redraw repeatedly as it moves. Not a problem in this case, but if
faster scrolling was needed it should probably be rewritten to use
disp.shiftLeft() instead */

void scrollText(char* text){
  int y=1;
  int endPos = 0 - textLength;
  int loopCount = 0;
  for(int Xpos = X_MAX; Xpos > endPos; Xpos--){
    if(loopCount > 10){ // Check for incoming keyboard data every 10 loops
     loopCount = 0;
      if(keyboard.available()){
        return;
      }
    }
    loopCount++;
    disp.clear();
    drawString(Xpos,y,text);
    disp.syncDisplays();
  }
}


// Write a line of static (non-scrolling) text to the display
void fixedText(char* text){
  int y = 1;
  int x = 0;
  disp.clear();
  drawString(x,y,text);
  disp.syncDisplays();
}


// Output a string to the display
void drawString(int x, uint8_t y, char* c){
  for(char i=0; i< strLength; i++){
    drawChar(x, y, c[i]);
    x+=6; // Width of each glyph
  }
}


// Output a single character to the display
void drawChar(int x, int y, char c){
  int dots;
 
  c = charLookup.indexOf(c);
 
  for (char col=0; col< 5; col++) {
    if((x+col+1)>0 && x < X_MAX){ // dont write to the display buffer if the location is out of range
      dots = pgm_read_byte_near(&myfont[c][col]);
      for (char row=0; row < 7; row++) {
        if (dots & (64>>row))         // only 7 rows.
          toolbox.setPixel(x+col, y+row, 1);
        else
          toolbox.setPixel(x+col, y+row, 0);
      }
    }
  }
}

// Warn if we're running out of RAM
void low_memory_alert(){
  for(int i=0; i < 3; i++){
    int y = 1;
    int x = 0;
    disp.clear();
    drawString(x,y,"   MEMORY LOW!  ");
    disp.syncDisplays();
    delay(500);
    disp.clear();
    disp.syncDisplays();
    delay(500);
    disp.clear();
  }
}

// Memory check
int get_free_memory(){
  int free_memory;
  if((int)__brkval == 0)
    free_memory = ((int)&free_memory) - ((int)&__bss_end);
  else
    free_memory = ((int)&free_memory) - ((int)__brkval);
    Serial.println(free_memory);
  return free_memory;
}
Logged

Pages: [1]   Go Up
Jump to: