Hay cosas que sí no tiene remedio (instancia DHT, dirección MAC e instancia EthernetServer), pero algunas otras sí.
Las variables de tipo long debes tomar en cuenta lo siguiente: ¿realmente puede acabar en un número muy grande?, ¿realmente necesito la capacidad de representar valores negativos?. A la ligera decides que sea de ese tipo, pero podría ser que dicha variable nunca sobrepase los 65535, máximo del unsigned int.
Luego está esto:
String comandos [] = {"alarma", "parking", "led", "ledauto", "medicion", "estado", "comandos"};
Aunque dichos objetos no se usen para concatenar, si no para sólo lectura; aún así tienden a ocupar más espacio que al haberlos declarado de formas alternativas. Mayor ahorro de memoria de la siguiente forma:
const char comandos[][] PROGMEM = {"alarma", "parking", "led", "ledauto", "medicion", "estado", "comandos"}; // 0 bytes de RAM ocupa
char comandoBuffer[9]; // 9 bytes de RAM ocupa. Se usa cuando cierta función que lo necesita, no admite strings en memoria flash
Un string en memoria flash se recupera a RAM de la siguiente manera:
strcpy_P(comandoBuffer, comandos[indice]);
Los print y println no necesitan de este último paso, solo has un "casting de tipo" y pones la referencia directamente:
Serial.println((__FlashStringHelper)comandos[indice]);
__FlashStringHelper es el tipo de dato resultante de la macrofunción F().