Yellowjacket + PS2/You LED sign + WiServer = *almost AWESOME* (Out of memory?)

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. :slight_smile:

/*
*    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;
}