Arduino Ethernet reboots repeatedly when sketch > 20k

Hey guys - hoping you can help me understand why this might be happening.

It would seem, that when my sketch size exceeds about 20k that the Arduino starts behaving erratically. It is an Arduino Ethernet board, and my understanding is it can handle a sketch size of 32k. It's a project that is growing in size, and nowhere near completion. But I'm pulling my hair out with odd behavior at this point and can't pinpoint the problem.

I can upload my code if you like, but it is around 800 lines of code. The code seems to run well without problems at around 20k size, but I add a few lines and it starts rebooting or locking up. Originally the problem was showing up earlier if I were to do a Serial.print(12345, DEC); so I avoided printing numbers to debug. I'm hoping there isn't something wrong with it! :astonished:

Michael

The actual size of the sketch probably isn't the problem.

You probably have a number of lines like Serial.print("something"); or Ethernet.print("something").

Those character constants ("strings") take up RAM which is causing RAM exhaustion. Using the F() Macro will keep those strings in PROGMEM: Serial.print(F("Something"));

Also, if you are using the String-object (different from "strings"), that is known to cause problems.

Posting your code would help.

Thanks I’ll look into figuring out how much memory my variables and string literals are… here’s the code if it helps, has to be a couple of posts since the forum limits it to 9500 chars - I would love to find the source of the seemingly random reboots.

//#define DEBUG
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print(x);
#define DEBUG_PRINTLINE(x) Serial.println(x);
#define DEBUG_PRINTNUM(x) Serial.print(x, DEC);
#define DEBUG_PRINTFLOAT(x) Serial.print(x, 2);
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLINE(x)
#define DEBUG_PRINTNUM(x)
#define DEBUG_PRINTFLOAT(x)
#endif

#define SOFTWARESERIAL
#ifdef SOFTWARESERIAL
#define LCD(x) lcd.print(x);
#define LCDR() lcd.read();
#define LCDAVAIL() lcd.available();
#define LCDBEGIN() lcd.begin(19200);
#else
#define LCD(x) Serial.print(x);
#define LCDR() Serial.read();
#define LCDAVAIL() Serial.available();
#define LCDBEGIN() Serial.begin(19200);
#endif

#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>
#include "RunningMedian.h"

// Ethernet setup
byte macAddress[] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA};
IPAddress ipAddress(192,168,2,200);
IPAddress dnsAddress(192,168,1,254);
IPAddress gatewayAddress(192,168,2,1);
IPAddress subnet(255,255,255,0);
EthernetServer server(23);
EthernetClient client;
bool clientConnected = false;	// track connect/disconnect
char currentCommand[32];		// the current command

// LCD Setup
#define PIN_LCD_RX 8
#define PIN_LCD_TX 7

#ifdef SOFTWARESERIAL
SoftwareSerial lcd( PIN_LCD_RX, PIN_LCD_TX );
#endif

unsigned char lcdBuf[32];		// tiny command buffer
unsigned char lcdBufCWP = 0;	// track the current write position
unsigned char lcdBufSOL = 0;	// track the position of the last command
unsigned char lcdJitterFilter = 3;		// set the jitter filter to within 3 levels

RunningMedian lcdSamples = RunningMedian(32);
 
// AC Load Setup
#define PIN_AC_LOAD 3		// Output to Opto Triac pin (green wire)
#define PIN_LED 9			// Monitoring LED
#define PIN_BUTTON 5		// Test Button (red wire)
#define PIN_POT A0			// Input potentiometer (analog, yellow wire)
#define INTERRUPT_NUM 0		// Use interrupt 0 on Pin 2 (yellow wire)
bool doFade = true;			// for testing

unsigned int dimLevel = 50;			// Dimming level (0-100) 0 = OFF, 100 = ON
unsigned long dimTimeOn = 0;
unsigned long dimTimeOff = 0;

volatile unsigned long lastInterrupt = 0;
volatile unsigned long previousInterrupt = 0;
volatile bool signalAquired = false;
volatile bool syncPulse = false;				// Interrupt signal
volatile unsigned long interruptElapsed = 0;
volatile float acPhaseFrequency = 0;

enum FingerTouchStatus {
	FINGER_NONE,
	FINGER_PRESS,
	FINGER_RELEASE
};

enum LCDInputCommandMode {
	ICM_WAITING,
	ICM_GETXPOS,
	ICM_GETYPOS
};

enum LCDCurrentScreen {
	SCREEN_STARTUP,
	SCREEN_MAIN,
	SCREEN_SETUP
};

unsigned long runtimeIteration = 0;
unsigned long timer1 = 0;
unsigned long timer2 = 0;
unsigned long lcdInputTimer = 0;
int lastLCDInputLevel = -1;
LCDInputCommandMode lcdInputCommandMode =ICM_WAITING;
LCDCurrentScreen lcdCurrentScreen = SCREEN_STARTUP;
FingerTouchStatus fingerTouchStatus = FINGER_NONE;
unsigned int currentLCDTouchX = 0;
unsigned int currentLCDTouchY = 0;
unsigned int previousLCDTouchX = 0;
unsigned int previousLCDTouchY = 0;
unsigned long lastLCDMovementTime = 0;
unsigned long lastLCDPress[2] = {0UL,0UL};

// fade async handling
bool fadeRunning = false;
unsigned int _fadeToLevel = 0;
int _fadeDirection = 0;
unsigned long _fadeTimer = 0;
unsigned long _fadeSpeed = 0;

// switch state
bool switchIsDown = false;
bool fadeDown = true;

// the setup routine runs once when you press reset:
void setup() {
	pinMode(PIN_LED, OUTPUT);
	pinMode(PIN_AC_LOAD, OUTPUT);
	pinMode(PIN_BUTTON, INPUT);
	//Serial.begin(19200);
	// Initialize the LCD
	InitializeLCD();
	
	// Setup Ethernet
	Ethernet.begin(macAddress, ipAddress, dnsAddress, gatewayAddress, subnet);
	server.begin();

	
	// Setup debugging
	Serial.begin(115200);
	delay(100);
	DEBUG_PRINT("\nProgram initialized on IP ");
	DEBUG_PRINT(Ethernet.localIP());
	DEBUG_PRINT("\n");

	// Setup default PIN states
	digitalWrite(PIN_LED, HIGH);
	delay(100);
	digitalWrite(PIN_LED, LOW);
	digitalWrite(PIN_AC_LOAD, LOW);	// turn the AC output off
	fadeDown = true;				// testing default
	dimLevel = limitDimLevelRange(dimLevel);	// ensure the default is valid

	// setup the interrupt trigger
	lastInterrupt = micros();
	attachInterrupt(INTERRUPT_NUM, zero_cross_int, RISING);	// Choose the zero cross interrupt # from the table above

	// Draw lcd main screen
	Screen_Main();
}

void InitializeLCD(){
	lcdCurrentScreen = SCREEN_STARTUP;
	// standard init
	// Setup LCD
	delay(1000);
	LCDBEGIN();
	//lcd.print("\recho off\r");
	LCD("\recho off\r");
	delay(50);
	//lcd.print("verbose off\r");
	LCD("verbose off\r");
	delay(50);
	//lcd.print("cls black\r");
	LCD("cls black\r");
	delay(50);
	// load the default graphic
	//lcd.print("picture Lux-1h-s.gif\r");
	LCD("picture Lux-1h-s.gif\r");
	delay(2000);
	DEBUG_PRINT("LCD is ready\n");
	lcdInputTimer = micros();
}

// the interrupt function must take no parameters and return nothing
// function to be fired at the zero crossing to dim the light
void zero_cross_int() {
	// Firing angle calculation: 60Hz = 8.333ms (1000ms / (60Hz * 2))
	// 1ms = 1000us
	// (8333.333 - 8.333ms ) / 128 steps = 65.1us
	// (8333.333 - 8.333ms ) / 255 steps = 32.64us
	// from: http://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/?ALLSTEPS

	// Here we are going to measure the frequency of the zero-cross interrupts by keeping track of
	// the last 2 interrupts and measuring the time between then. We could optionally average the last 5 for more less jitter.

	previousInterrupt = lastInterrupt;
	lastInterrupt = micros();

	interruptElapsed = (lastInterrupt - previousInterrupt);
	// calculate the AC Phase frequency in Hz
	acPhaseFrequency = ((1000.0 * 1000.0) / interruptElapsed) / 2.0;		// div / 2 because the AC hits the zero point twice per full phase (up then down)
	//acPhaseFrequency = 60.0;

	if(signalAquired)
		syncPulse = true;

	// AC Load control mechanism using delay()
	/*if(signalAquired){
		delayMicroseconds(dimTimeOff * (255 - dimLevel));
		digitalWrite(PIN_AC_LOAD, HIGH);
		delayMicroseconds(dimTimeOn);
		digitalWrite(PIN_AC_LOAD, LOW);
	}*/

}
// the loop routine is called forever in an endless loop.
void loop() {
	runtimeIteration++;

	if(syncPulse){
		syncPulse=false;

		// Interrupt has been fired
		// here we want to delay a specific amount of time, timer1 handles the start time for avoiding delay() usage.
		if(timer1 == 0)	{
			timer1 = micros();
		}
	}

	// determine if the ON timer/delay has occurred
	unsigned long timer1Distance = micros() - timer1;
	unsigned int actualDimValue = (int)((((float)dimLevel / 100.0) * 238.0 - 30.0) + 30.0);	// convert dimLevel 1-100% to a number between 30 and 238
	actualDimValue = 255 - actualDimValue;	// invert the output
	if(timer1 > 0 && timer1Distance >= (unsigned long)(dimTimeOff * actualDimValue)){
		// timer1 fired
		timer1 = 0;	// reset timer
		digitalWrite(PIN_AC_LOAD, HIGH);	// turn on the AC Load
		timer2 = micros();	// start the next timer to turn off the AC Load
	}
	// determine if the OFF timer/delay has occurred
	unsigned long timer2Distance = micros() - timer2;
	if(timer2 > 0 && timer2Distance >= dimTimeOn){
		// timer2 fired
		timer2 = 0;	// reset timer
		digitalWrite(PIN_AC_LOAD, LOW);	// turn off the AC Load
	}
	// determine if the lcd input timer/delay has occurred
	// we process lcd input about 100ms after it is received to filter out noise
	/*unsigned long lcdInputTimerDistance = micros() - lcdInputTimer;
	if(lastLCDInputLevel >= 0 && lcdInputTimerDistance > 0 && lcdInputTimerDistance >= 200){
		if(!fadeRunning){
			//fadeTo(lastLCDInputLevel, 250);
			setTo(lastLCDInputLevel);
			lastLCDInputLevel = -1;
		}
	}*/

	// Button handling for Testing
	int buttonState = 0;
	buttonState = digitalRead(PIN_BUTTON);
	if(buttonState == HIGH && !switchIsDown){
		switchIsDown = true;
		DEBUG_PRINTLINE("Button pressed.");
		if(fadeDown){
			fadeTo(0, 2500);
		}else{
			fadeTo(100, 2500);
		}
		fadeDown=!fadeDown;
	}else if(buttonState == LOW && switchIsDown)
	{
		switchIsDown = false;
	}
	// End Button handling


	if(runtimeIteration % 500 == 0){
		
	}

	// Process any fade steps
	processFadeStep();


	// Detect the AC Phase frequency once every second (approx), or if no frequency was detected.
	// This might only need to really be done once on startup, since it is unlikely that the phase will vary by more than 0.5-1Hz. (solar/generator installations?)
	if(runtimeIteration % 50000 == 0 || runtimeIteration < 25){			// 25,000 iterations, each iteration takes approx. 40us so about a second
		if(acPhaseFrequency > 8.0 && acPhaseFrequency < 140.0) {
			// todo: try to move away from floating point math where possible.
			float timeSlice = 1000.0 / (acPhaseFrequency * 2.0);	// 8.333ms		(1000ms / (60Hz * 2))
			float cycleLength = timeSlice * 1000.0;					// 8333.333ms
			float timeOff = (cycleLength - timeSlice) / 255.0;		// 32.647us		((8333.333ms - 8.333ms) / 255)
			
			dimTimeOn = (unsigned long)timeSlice;								// 8ms (8.333ms)
			dimTimeOff = (unsigned long)timeOff;								// 32.647us * dimLevel (128) = 4178.816us
			signalAquired = true;
		
			//DEBUG_PRINT("AC Frequency: ");
			//DEBUG_PRINTFLOAT(acPhaseFrequency);
			//DEBUG_PRINT("Hz\n");
			//digitalWrite(PIN_LED, LOW);

		}else{
			signalAquired = false;
			DEBUG_PRINT("No signal.\n");
			//digitalWrite(PIN_LED, HIGH);
		}
	}


	// ************************************************************
	// ************************************************************
	// LCD Input processing

	//if(lcd.available()){
	int avail = lcd.available();
	if(avail > 0){
		// read a byte into the current write position (CWP)
		//lcdBuf[lcdBufCWP] = lcd.read();
		//digitalWrite(PIN_LED, !digitalRead(PIN_LED));
		//delay(60);

		lcdBuf[lcdBufCWP] = lcd.read();
		
		if(lcdBuf[lcdBufCWP] == '\r'){
			parseLCDCommand();
		}else{
			// increment to the next write position
			lcdBufCWP++;
			if(lcdBufCWP > sizeof(lcdBuf))
				lcdBufCWP = 0;
		}
	}

	// while idle reissue the get x position until the finger press is released
	
	if(runtimeIteration % 500 == 0 && lcdInputCommandMode == ICM_WAITING && fingerTouchStatus == FINGER_PRESS){
		// if 1 second has gone by with no movement or release event, stop polling the lcd
		if(micros() - lastLCDMovementTime >= 1000000 * 1){
			fingerTouchStatus = FINGER_NONE;
			lcdInputCommandMode = ICM_WAITING;
		}else{
			LCD("TOUCHX\r");
			lcdInputCommandMode = ICM_GETXPOS;
		}
	}

	// Potentiometer handling
	/*unsigned int potVal = analogRead(PIN_POT);
	lcdSamples.add(potVal);*/
	

	// every 100ms, average the command buffer together and submit the command
	unsigned long lcdInputTimerDistance = micros() - lcdInputTimer;
	if(lcdInputTimerDistance >= 100){
		// 100ms has elapsed

		// Median LCD
		if(lcdSamples.count() > 0){
			unsigned int val = (unsigned int)lcdSamples.getMedian();
			// submit the command
			fadeTo(val, 100);
			lcdSamples.clear();
		}

		lcdInputTimer = micros();
	}

	// Wait for ethernet client
	client = server.connected();

	if(client){
		// read bytes from incoming client and echo the results
		if(!clientConnected){
			// Detected a client connect
			clientConnected=true;
			client.flush();
			client.write("Welcome to LightLux!\r\n>");
			DEBUG_PRINTLINE("Client Connected!");
		}

		// read any incoming data
		if(client.available()){
			char c = client.read();
			bool processChar = true;
			int pos=strlen(currentCommand);
			if(c == '\n'){
				// newline indicates we need to process the command
				processChar = false;
				processRemoteCommand(currentCommand, client);
				currentCommand[0] = '\0';
				client.write(">");
			}
			if(c == '\b'){
				processChar = false;
				if(pos > 0)
					currentCommand[pos-1] = '\0';
			}
			if(processChar && c < 32 || c > 122) {
				processChar = false;	// only alpha-numeric chars allowed
			}
			if(processChar){
				if(pos < sizeof(currentCommand) - 1){	// prevent string overflow
					currentCommand[pos] = c;
					currentCommand[pos + 1] = '\0';
				}else{
					// max command string size reached
					currentCommand[0] = '\0';
					//processRemoteCommand(currentCommand, client);
				}
			}
		}
	}else if(clientConnected){
		clientConnected=false;
		currentCommand[0] = '\0';
		DEBUG_PRINTLINE("Client disconnected!");
	}
}
void parseLCDCommand(){
	// lcdBufCWP is the last position written to
	// lcdBufSOL is the start of the last command (the point to start reading the command from)

	int commandSize = lcdBufCWP;
	char command[32];
	for(int i=0; i<commandSize; i++){
		if(i < sizeof(lcdBuf))
			command[i] = lcdBuf[i];
		if(i+1 < sizeof(command))
			command[i+1] = '\0';	// null terminate the string
		else
			command[i] = '\0';
	}
	

	//lcdBufSOL = 0;
	lcdBufCWP = 0;
	DEBUG_PRINT("cmd:'");
	DEBUG_PRINT(command);
	DEBUG_PRINT("'\r\n");

	bool isPress = false;
	bool isRelease = false;
	int eventNumber = 0;
	// Process touchzone events (always)
	if(commandSize == 4 && command[0] == 'T' && command[1] == 'Z'){
		if(command[2] == 'P'){
			isPress = true;
			fingerTouchStatus = FINGER_PRESS;
		}
		if(command[2] == 'R'){
			isRelease = true;
			fingerTouchStatus = FINGER_RELEASE;
		}
		if(isPress || isRelease || fingerTouchStatus == FINGER_PRESS) {
			eventNumber = ConvertBytesToNum(command, 3, 1);
			if(eventNumber == 1){
				// Touchzone 1 pressed
				DEBUG_PRINT("ICM1_RECV\r\n");
				if(isPress){
					unsigned long clickDistance = micros() - lastLCDPress[0];
					if(lastLCDPress[0] > 0 && clickDistance < 500000UL && clickDistance > 200000UL ){
						// double click detected
						DEBUG_PRINT("DOUBLE CLICK!!\r");
					}
					lastLCDPress[0] = micros();
				}
				// the touchscreen widget 1 has been pressed (DIM)
				// ask for the position
				LCD("TOUCHX\r");
				lcdInputCommandMode = ICM_GETXPOS;
				if(fingerTouchStatus == FINGER_RELEASE)
					fingerTouchStatus = FINGER_NONE;
				return;
			}else if(eventNumber == 2){
				// Touchzone 2 pressed
				DEBUG_PRINT("ICM2_RECV\r\n");
				if(isPress){
					unsigned long clickDistance = micros() - lastLCDPress[1];
					if(lastLCDPress[1] > 0 && clickDistance < 500000UL && clickDistance > 200000UL ){
						// double click detected
						DEBUG_PRINT("DOUBLE CLICK!!\r");
						// the touchscreen widget 2 has been pressed (SETUP)
						if(lcdCurrentScreen == SCREEN_MAIN)
							Screen_Setup1();
						else
							Screen_Main();
					}
					lastLCDPress[1] = micros();
				}
				lcdInputCommandMode=ICM_WAITING;
				if(fingerTouchStatus == FINGER_RELEASE)
					fingerTouchStatus = FINGER_NONE;
				return;

			}
		}
	}
	// Process get x position if we have issued that command
	if(lcdInputCommandMode == ICM_GETXPOS){
		int val = ConvertBytesToNum(command, 0, commandSize);
		Serial.print("ICM_GETXPOS ");
		//Serial.print(5000);
		Serial.print("\r\n");
		previousLCDTouchX = currentLCDTouchX;
		currentLCDTouchX = val;
		if(previousLCDTouchX != currentLCDTouchX || previousLCDTouchY != currentLCDTouchY)
			lastLCDMovementTime = micros();
		lcdInputCommandMode = ICM_WAITING;

		/*
		if(lcdCurrentScreen == SCREEN_MAIN){
			// process X touch input to dim the light
			// convert the X position into a percent value
			int perc = (int)((val / 400.0) * 100.0);
			// invert the input
			perc = 100 - perc;

			//DEBUG_PRINT("DIM ");
			//DEBUG_PRINTNUM(perc);
			//DEBUG_PRINT("\r\n");

			// if the input command is beyond what the jitter filter allows, add the command
			//int changeDifference = dimLevel - val;		// when using setTo
			int changeDifference = _fadeToLevel - perc;		// when using fadeTo
			if(abs(changeDifference) > lcdJitterFilter){
				// median samples
				lcdSamples.add((unsigned char)val);
			}
		}
		*/
		
		
		return;
	}
	// Process get y position if we have issued that command
	if(lcdInputCommandMode == ICM_GETYPOS){
		unsigned int val = ConvertBytesToNum(command, 0, commandSize);
		DEBUG_PRINT("ICM_GETYPOS ");
		//DEBUG_PRINTNUM(val);
		DEBUG_PRINT("\r\n");
		previousLCDTouchY = currentLCDTouchY;
		currentLCDTouchY = val;
		if(previousLCDTouchX != currentLCDTouchX || previousLCDTouchY != currentLCDTouchY)
			lastLCDMovementTime = micros();
		lcdInputCommandMode = ICM_WAITING;
		return;
	}

}

// convert a char array to an integer (todo: support full size int using loop or more efficient method)
int ConvertBytesToNum(char chars[], int start, int length){
	int val = 0;
	if(length > 0 && chars[start + 0] >= 48 && chars[start + 0] <= 57){
		if(length > 1 && chars[start + 1] >= 48 && chars[start + 1] <= 57)
			if(length > 2 && chars[start + 2] >= 48 && chars[start + 2] <= 57)
				val = (int)((chars[start + 0] - 48) * 100) + ((chars[start + 1] - 48) * 10) + (chars[start + 2] - 48);	// 3 digits
			else
				val = (int)((chars[start + 0] - 48) * 10) + (chars[start + 1] - 48);	// 2 digits
		else
			val = (int)(chars[start + 0] - 48);	// 1 digit
	}
	return val;
}


void Screen_Main(){
	//lcd.print("cls black\r");
	LCD("cls black\r");
	delay(50);
	//lcd.print("picture Lux-1h.gif\r");
	LCD("picture Lux-1h.gif\r");
	delay(50);
	LCD("TOUCHZONE 1 0 0 400 240 1\r");	// main dimmer
	delay(50);
	LCD("TOUCHZONE 2 340 0 60 60 1\r");	// setup button (invisible)
	delay(50);
	//LCD("XY CC\rFONT SANS24\rCOLOR YELLOW\rPRINT \"Done INIT\" CC\r");
	lcdCurrentScreen = SCREEN_MAIN;
	
}

void Screen_Setup1(){
	LCD("CLS BLUE\rFONTO 1\rCOLOR WHITE\rFONT SANS24\r");
	delay(30);
	// Draw Menu Items
	LCD("XY 10 225\rPRINT \"Mode\"\r");
	delay(30);
	LCD("XY 60 225\rPRINT \"UI\"\r");
	delay(30);
	LCD("XY 110 225\rPRINT \"Load\"\r");
	delay(30);
	LCD("XY 160 225\rPRINT \"Module\"\r");
	delay(30);
	LCD("XY 210 225\rPRINT \"Display\"\r");
	delay(30);
	LCD("XY 260 225\rPRINT \"Auto Dim\"\r");
	delay(30);
	LCD("XY 310 225\rPRINT \"Auto Off\"\r");
	
	delay(50);

	LCD("COLOR YELLOW\r");
	delay(30);
	LCD("XY 30 210\rPRINT \"DIMMER\"\r");
	delay(30);
	LCD("XY 80 210\rPRINT \"STAR TREK\"\r");
	delay(30);
	LCD("XY 130 210\rPRINT \"SINGLE\"\r");
	delay(30);
	LCD("XY 180 210\rPRINT \"NONE\"\r");
	delay(30);
	LCD("XY 230 210\rPRINT \"AUTO\"\r");
	delay(30);
	LCD("XY 280 210\rPRINT \"1 MIN\"\r");
	delay(30);
	LCD("XY 330 210\rPRINT \"NEVER\"\r");
	delay(30);
	LCD("TOUCHZONE 2 340 0 60 60 1\r");	// setup button (invisible)
	lcdCurrentScreen = SCREEN_SETUP;
}




// process a remote user command
void processRemoteCommand(char cmd[255], EthernetClient client){
	DEBUG_PRINT("Processing: ");
	DEBUG_PRINT(cmd);
	DEBUG_PRINT("\n");

	if(strcmp(cmd, "on") == 0){
		DEBUG_PRINTLINE("Turning switch on");
		setTo(255);
	}else if(strcmp(cmd, "off") == 0){
		DEBUG_PRINTLINE("Turning switch off");
		setTo(0);
	}else if(strcmp(cmd, "quit") == 0){
		client.write("\rGoodbye!\r\n");
		client.stop();
	}else if(strcmp(cmd, "help") == 0){
		client.write("\rCommand List:\r\n");
		client.write("on	- Turn load on\r\n");
		client.write("off	- Turn load off\r\n");
		client.write("dim 0-100 [0-30000ms]	- Dim to a specific level, fading over a specified amount of time.\r\n");
		client.write("get [parameter]		- Get a parameter from the device\r\n");
		client.write("	Valid parameters:\r\n");
		client.write("		level	- Get dim level\r\n");
		client.write("quit		- Quit/disconnect\r\n");
	}else if(strcmp(cmd, "get") == 0 || strstr(cmd, "get ") != NULL){
		DEBUG_PRINTLINE("Getting a value");
		char _param[16];
		int paramCnt = 0;
		char* strings=strtok(cmd," ,");
		while(strings != NULL){
			switch (paramCnt)
			{
				case 1:
					strncpy(_param, strings, 16);
				break;
			}
			strings=strtok(NULL," ,");
			paramCnt++;
		}
		if(strcmp(_param, "level") == 0){
			// get the current level
			char intBuf[3];
			itoa(dimLevel, intBuf, 10);
			client.write(intBuf);
			client.write("\r\n");
		}else{
			// no valid parameters were requested
			client.write("\rInvalid arguments. Usage: get [option]\r\n\tExample: get level\r\n");
		}
	}else if(strcmp(cmd, "dim") == 0 || strstr(cmd, "dim ") != NULL){
		DEBUG_PRINTLINE("Dimming the switch");
		int _setDimLevel = -1;
		int _timeDelay = 0;
		int paramCnt = 0;
		char* strings=strtok(cmd," ,");
		while(strings != NULL){
			switch (paramCnt)
			{
				case 1:
					removeChar(strings, '%');
					_setDimLevel = atoi(strings);
				break;
				case 2:
					_timeDelay = atoi(strings);
				break;
			}
			strings=strtok(NULL," ,");
			paramCnt++;
		}
		if(_setDimLevel >= 0){
			fadeTo(_setDimLevel, _timeDelay);
		}else{
			// not enough parameters specified
			client.write("\rInvalid arguments. Usage: dim dimlevel% [time milliseconds]\r\n\tExample: dim 100 5000\r\n");
		}

	}else{
		// unknown command
		client.write("\rUnrecognized command.\r\n");
	}

}
// process the fade animation, if running, on each loop iteration (async no delay!)
void processFadeStep(){
	if(fadeRunning){
		unsigned long elapsed = micros() - _fadeTimer;
		// if the timer has met the delay time, step to the next step
		if(elapsed >= _fadeSpeed){
			//DEBUG_PRINT("Elapsed: ");
			dimLevel = limitDimLevelRange(dimLevel + _fadeDirection);
			//DEBUG_PRINTNUM(elapsed);
			//DEBUG_PRINT("\n");
			_fadeTimer = micros();
		}
		
		// if the current fade level has reached the desired level, stop the timer loop
		if(dimLevel == _fadeToLevel){
			DEBUG_PRINTLINE("Fade Complete.");
			fadeRunning = false;
			_fadeTimer = 0;
		}
	}
}

// Fade from the current dim level to another dim level
void fadeTo(unsigned int dimValue, unsigned long fadeSeconds){
	if(signalAquired){
		// fade setup
		fadeRunning = true;
		_fadeToLevel = limitDimLevelRange(dimValue);

		// calculate the number of steps needed
		unsigned long _fadeLevels = (unsigned long)(abs((int)dimLevel - (int)dimValue));
		// calculate the delay time per iteration
		_fadeSpeed = (fadeSeconds * 1000UL) / _fadeLevels;
	
		// Depending on the direction we need to dim, fade it up or down
		if(dimValue < dimLevel){
			// Fade down
			DEBUG_PRINT("fadeTo(down): ");
			//DEBUG_PRINTNUM(dimValue);
			DEBUG_PRINT(" for ");
			//DEBUG_PRINTNUM(fadeSeconds);
			DEBUG_PRINT("ms\n");
			_fadeDirection = -1;
		}else{
			// Fade up
			DEBUG_PRINT("fadeTo(up): ");
			//DEBUG_PRINTNUM(dimValue);
			DEBUG_PRINT(" for ");
			//DEBUG_PRINTNUM(fadeSeconds);
			DEBUG_PRINT("ms\n");
			_fadeDirection = 1;
		}

		// start the async timer loop
		_fadeTimer = micros();
	}else{
		dimLevel = 0;
		if(clientConnected){
			client.write("Error: No load detected.\r\n");
		}
	}
}

// Set the dim level to a specific value
void setTo(int dimValue){
	//DEBUG_PRINT("Setting dim level to ");
	DEBUG_PRINT("setTo: ");
	//DEBUG_PRINTNUM(dimLevel);
	DEBUG_PRINT("\n");
	if(signalAquired){
		dimLevel = limitDimLevelRange(dimValue);
		fadeRunning=false;	// cancel any active fades
	}else{
		dimLevel = 0;
		if(clientConnected){
			client.write("Error: No load detected.\r\n");
		}
	}
}

// Limit the dim level to within a percentage range
int limitDimLevelRange(int i){
	if(i > 100)
		i = 100;
	else if(i < 0)
		i = 0;
	return i;
}

// remove all occurances of a character
void removeChar(char *p, char c) {
  char *f;
  while( f=strchr(p,c) )
    memmove(f,f+1,strlen(f));
}

As I suspected,you have a ton of character arrays. You need to look at a method to keep those in PROGMEM.

http://www.arduino.cc/en/Reference/PROGMEM

Keep in mind you only have 2k of ram and no memory controller.

Thanks! Will do - if that isn't sufficient (I'm only about 60% way through the project) then I will need to look at separating function over two micros I would imagine and communicate between the two.

Or cut down in the number of unique strings.