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