Version 2.6 - 9th April 2019

Version 2.6 - 9th April 2019

New features and bug fixes: fixes #21, fixes #20, fixes #19, fixes #18

--- ulisp.ino
+++ ulisp.ino
@@ -1,8 +1,11 @@
-/* uLisp AVR Version 2.5 - www.ulisp.com
-   David Johnson-Davies - www.technoblogy.com - 30th November 2018
+/* uLisp AVR Version 2.6 - www.ulisp.com
+   David Johnson-Davies - www.technoblogy.com - 9th April 2019

    Licensed under the MIT license: https://opensource.org/licenses/MIT
 */
+
+// Lisp Library
+const char LispLibrary[] PROGMEM = "";

 // Compile options

@@ -44,6 +47,7 @@
 #define pop(y)             ((y) = cdr(y))

 #define integerp(x)        ((x) != NULL && (x)->type == NUMBER)
+#define floatp(x)          ((x) != NULL && (x)->type == FLOAT)
 #define symbolp(x)         ((x) != NULL && (x)->type == SYMBOL)
 #define stringp(x)         ((x) != NULL && (x)->type == STRING)
 #define characterp(x)      ((x) != NULL && (x)->type == CHARACTER)
@@ -61,23 +65,23 @@
 // Constants

 const int TRACEMAX = 3; // Number of traced functions
-enum type { ZERO=0, SYMBOL=2, NUMBER=4, STREAM=6, CHARACTER=8, STRING=10, PAIR=12 };  // STRING and PAIR must be last
+enum type { ZERO=0, SYMBOL=2, NUMBER=4, STREAM=6, CHARACTER=8, FLOAT=10, STRING=12, PAIR=14 };  // STRING and PAIR must be last
 enum token { UNUSED, BRA, KET, QUO, DOT };
 enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM };

-enum function { SYMBOLS, NIL, TEE, NOTHING, AMPREST, LAMBDA, LET, LETSTAR, CLOSURE, SPECIAL_FORMS, QUOTE,
+enum function { NIL, TEE, NOTHING, OPTIONAL, AMPREST, LAMBDA, LET, LETSTAR, CLOSURE, SPECIAL_FORMS, QUOTE,
 DEFUN, DEFVAR, SETQ, LOOP, PUSH, POP, INCF, DECF, SETF, DOLIST, DOTIMES, TRACE, UNTRACE, FORMILLIS,
-WITHSERIAL, WITHI2C, WITHSPI, WITHSDCARD, TAIL_FORMS, PROGN, RETURN, IF, COND, WHEN, UNLESS, AND, OR,
-FUNCTIONS, NOT, NULLFN, CONS, ATOM, LISTP, CONSP, SYMBOLP, STREAMP, EQ, CAR, FIRST, CDR, REST, CAAR, CADR,
-SECOND, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR, THIRD, CDAAR, CDADR, CDDAR, CDDDR, LENGTH, LIST, REVERSE,
-NTH, ASSOC, MEMBER, APPLY, FUNCALL, APPEND, MAPC, MAPCAR, ADD, SUBTRACT, MULTIPLY, DIVIDE, TRUNCATE, MOD,
-ONEPLUS, ONEMINUS, ABS, RANDOM, MAXFN, MINFN, NOTEQ, NUMEQ, LESS, LESSEQ, GREATER, GREATEREQ, PLUSP,
-MINUSP, ZEROP, ODDP, EVENP, INTEGERP, NUMBERP, CHAR, CHARCODE, CODECHAR, CHARACTERP, STRINGP, STRINGEQ,
-STRINGLESS, STRINGGREATER, SORT, STRINGFN, CONCATENATE, SUBSEQ, READFROMSTRING, PRINCTOSTRING,
-PRIN1TOSTRING, LOGAND, LOGIOR, LOGXOR, LOGNOT, ASH, LOGBITP, EVAL, GLOBALS, LOCALS, MAKUNBOUND, BREAK,
-READ, PRIN1, PRINT, PRINC, TERPRI, READBYTE, READLINE, WRITEBYTE, WRITESTRING, WRITELINE, RESTARTI2C, GC,
-ROOM, SAVEIMAGE, LOADIMAGE, CLS, PINMODE, DIGITALREAD, DIGITALWRITE, ANALOGREAD, ANALOGWRITE, DELAY,
-MILLIS, SLEEP, NOTE, EDIT, PPRINT, PPRINTALL, ENDFUNCTIONS };
+WITHSERIAL, WITHI2C, WITHSPI, WITHSDCARD, TAIL_FORMS, PROGN, RETURN, IF, COND, WHEN, UNLESS, CASE, AND,
+OR, FUNCTIONS, NOT, NULLFN, CONS, ATOM, LISTP, CONSP, SYMBOLP, STREAMP, EQ, CAR, FIRST, CDR, REST, CAAR,
+CADR, SECOND, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR, THIRD, CDAAR, CDADR, CDDAR, CDDDR, LENGTH, LIST,
+REVERSE, NTH, ASSOC, MEMBER, APPLY, FUNCALL, APPEND, MAPC, MAPCAR, MAPCAN, ADD, SUBTRACT, MULTIPLY,
+DIVIDE, TRUNCATE, MOD, ONEPLUS, ONEMINUS, ABS, RANDOM, MAXFN, MINFN, NOTEQ, NUMEQ, LESS, LESSEQ, GREATER,
+GREATEREQ, PLUSP, MINUSP, ZEROP, ODDP, EVENP, INTEGERP, NUMBERP, CHAR, CHARCODE, CODECHAR, CHARACTERP,
+STRINGP, STRINGEQ, STRINGLESS, STRINGGREATER, SORT, STRINGFN, CONCATENATE, SUBSEQ, READFROMSTRING,
+PRINCTOSTRING, PRIN1TOSTRING, LOGAND, LOGIOR, LOGXOR, LOGNOT, ASH, LOGBITP, EVAL, GLOBALS, LOCALS,
+MAKUNBOUND, BREAK, READ, PRIN1, PRINT, PRINC, TERPRI, READBYTE, READLINE, WRITEBYTE, WRITESTRING,
+WRITELINE, RESTARTI2C, GC, ROOM, SAVEIMAGE, LOADIMAGE, CLS, PINMODE, DIGITALREAD, DIGITALWRITE,
+ANALOGREAD, ANALOGWRITE, DELAY, MILLIS, SLEEP, NOTE, EDIT, PPRINT, PPRINTALL, REQUIRE, LISTLIBRARY, ENDFUNCTIONS };

 // Typedefs

@@ -121,7 +125,7 @@
 #define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */

 #elif defined(__AVR_ATmega2560__)
-#define WORKSPACESIZE 1216-SDSIZE       /* Cells (4*bytes) */
+#define WORKSPACESIZE 1214-SDSIZE       /* Cells (4*bytes) */
 #define EEPROMSIZE 4096                 /* Bytes */
 #define SYMBOLTABLESIZE 512             /* Bytes */

@@ -129,6 +133,7 @@
 #define WORKSPACESIZE 2816-SDSIZE       /* Cells (4*bytes) */
 #define EEPROMSIZE 4096                 /* Bytes */
 #define SYMBOLTABLESIZE 512             /* Bytes */
+
 #endif

 object Workspace[WORKSPACESIZE] WORDALIGNED;
@@ -170,6 +175,7 @@
 intptr_t lookupfn (symbol_t name);
 int builtin (char* n);
 void error (PGM_P string);
+void error3 (symbol_t name, PGM_P string);
 void pfstring (PGM_P s, pfun_t pfun);

 // Set up workspace
@@ -349,18 +355,25 @@

 char *MakeFilename (object *arg) {
   char *buffer = SymbolTop;
-  int i = 0;
+  int max = maxbuffer(buffer);
+  buffer[0]='/';
+  int i = 1;
   do {
     char c = nthchar(arg, i);
     if (c == '\0') break;
     buffer[i++] = c;
-  } while (i<12);  // Truncate to 12 chars
+  } while (i<max);
   buffer[i] = '\0';
   return buffer;
 }

 // Save-image and load-image

+#if defined(sdcardsupport)
+void SDWriteInt (File file, int data) {
+  file.write(data & 0xFF); file.write(data>>8 & 0xFF);
+}
+#else
 typedef struct {
   unsigned int eval;
   unsigned int datasize;
@@ -374,9 +387,36 @@
 } struct_image;

 struct_image EEMEM image;
-
-int saveimage (object *arg) {
+#endif
+
+unsigned int saveimage (object *arg) {
   unsigned int imagesize = compactimage(&arg);
+#if defined(sdcardsupport)
+  SD.begin(SDCARD_SS_PIN);
+  File file;
+  if (stringp(arg)) {
+    file = SD.open(MakeFilename(arg), O_RDWR | O_CREAT | O_TRUNC);
+    arg = NULL;
+  } else if (arg == NULL || listp(arg)) file = SD.open("/ULISP.IMG", O_RDWR | O_CREAT | O_TRUNC);
+  else error3(SAVEIMAGE, PSTR("illegal argument"));
+  if (!file) error(PSTR("Problem saving to SD card"));
+  SDWriteInt(file, (uintptr_t)arg);
+  SDWriteInt(file, imagesize);
+  SDWriteInt(file, (uintptr_t)GlobalEnv);
+  SDWriteInt(file, (uintptr_t)GCStack);
+  #if SYMBOLTABLESIZE > BUFFERSIZE
+  SDWriteInt(file, (uintptr_t)SymbolTop);
+  for (int i=0; i<SYMBOLTABLESIZE; i++) file.write(SymbolTable[i]);
+  #endif
+  for (unsigned int i=0; i<imagesize; i++) {
+    object *obj = &Workspace[i];
+    SDWriteInt(file, (uintptr_t)car(obj));
+    SDWriteInt(file, (uintptr_t)cdr(obj));
+  }
+  file.close();
+  return imagesize;
+#else
+  if (!(arg == NULL || listp(arg))) error3(SAVEIMAGE, PSTR(" illegal argument"));
   int bytesneeded = imagesize*4 + SYMBOLTABLESIZE + 10;
   if (bytesneeded > EEPROMSIZE) {
     pfstring(PSTR("Error: Image size too large: "), pserial);
@@ -394,10 +434,41 @@
   #endif
   eeprom_update_block(Workspace, image.data, imagesize*4);
   return imagesize;
-}
-
-int loadimage (object *filename) {
-  (void) filename;
+#endif
+}
+
+#if defined(sdcardsupport)
+int SDReadInt (File file) {
+  uintptr_t b0 = file.read(); uintptr_t b1 = file.read();
+  return b0 | b1<<8;
+}
+#endif
+
+unsigned int loadimage (object *arg) {
+#if defined(sdcardsupport)
+  SD.begin(SDCARD_SS_PIN);
+  File file;
+  if (stringp(arg)) file = SD.open(MakeFilename(arg));
+  else if (arg == NULL) file = SD.open("/ULISP.IMG");
+  else error3(LOADIMAGE, PSTR("illegal argument"));
+  if (!file) error(PSTR("Problem loading from SD card"));
+  SDReadInt(file);
+  int imagesize = SDReadInt(file);
+  GlobalEnv = (object *)SDReadInt(file);
+  GCStack = (object *)SDReadInt(file);
+  #if SYMBOLTABLESIZE > BUFFERSIZE
+  SymbolTop = (char *)SDReadInt(file);
+  for (int i=0; i<SYMBOLTABLESIZE; i++) SymbolTable[i] = file.read();
+  #endif
+  for (int i=0; i<imagesize; i++) {
+    object *obj = &Workspace[i];
+    car(obj) = (object *)SDReadInt(file);
+    cdr(obj) = (object *)SDReadInt(file);
+  }
+  file.close();
+  gc(NULL, NULL);
+  return imagesize;
+#else
   unsigned int imagesize = eeprom_read_word(&image.datasize);
   if (imagesize == 0 || imagesize == 0xFFFF) error(PSTR("No saved image"));
   GlobalEnv = (object *)eeprom_read_word(&image.globalenv);
@@ -409,15 +480,29 @@
   eeprom_read_block(Workspace, image.data, imagesize*4);
   gc(NULL, NULL);
   return imagesize;
+#endif
 }

 void autorunimage () {
+#if defined(sdcardsupport)
+  SD.begin(SDCARD_SS_PIN);
+  File file = SD.open("ULISP.IMG");
+  if (!file) error(PSTR("Problem autorunning from SD card"));
+  object *autorun = (object *)SDReadInt(file);
+  object *nullenv = NULL;
+  file.close();
+  if (autorun != NULL) {
+    loadimage(NULL);
+    apply(autorun, NULL, &nullenv);
+  }
+#else
   object *nullenv = NULL;
   object *autorun = (object *)eeprom_read_word(&image.eval);
   if (autorun != NULL && (unsigned int)autorun != 0xFFFF) {
     loadimage(nil);
     apply(autorun, NULL, &nullenv);
   }
+#endif
 }

 // Error handling
@@ -438,6 +523,15 @@
   longjmp(exception, 1);
 }

+void error3 (symbol_t name, PGM_P string) {
+  pfl(pserial); pfstring(PSTR("Error: "), pserial);
+  if (symbol == NULL) pfstring(PSTR("function "), pserial);
+  else { pserial('\''); pstring(lookupbuiltin(name), pserial); pfstring(PSTR("' "), pserial); }
+  pfstring(string, pserial); pln(pserial);
+  GCStack = NULL;
+  longjmp(exception, 1);
+}
+
 // Tracing

 boolean tracing (symbol_t name) {
@@ -486,6 +580,11 @@
   if (x == NULL) return true;
   unsigned int type = x->type;
   return type >= PAIR || type == ZERO;
+}
+
+boolean improperp (object *x) {
+  unsigned int type = x->type;
+  return type < PAIR && type != ZERO;
 }

 int toradix40 (char ch) {
@@ -563,6 +662,7 @@
 int listlength (object *list) {
   int length = 0;
   while (list != NULL) {
+    if (improperp(list)) error(PSTR("List argument is not a proper list"));
     list = cdr(list);
     length++;
   }
@@ -573,8 +673,10 @@

 object *assoc (object *key, object *list) {
   while (list != NULL) {
+    if (improperp(list)) error3(ASSOC, PSTR("argument is not a proper list"));
     object *pair = first(list);
-    if (eq(key,car(pair))) return pair;
+    if (!listp(pair)) error2(pair, PSTR("in 'assoc' is not a list"));
+    if (pair != NULL && eq(key,car(pair))) return pair;
     list = cdr(list);
   }
   return nil;
@@ -710,26 +812,39 @@
     state = cdr(state);
   }
   // Add arguments to environment
-  while (params != NULL && args != NULL) {
+  boolean optional = false;
+  while (params != NULL) {
     object *value;
     object *var = first(params);
-    if (var->name == AMPREST) {
-      params = cdr(params);
-      var = first(params);
-      value = args;
-      args = NULL;
-    } else {
-      value = first(args);
-      args = cdr(args);
-    }
-    object *pair = findtwin(var, *env);
-    if (tc && (pair != NULL)) cdr(pair) = value;
-    else push(cons(var,value), *env);
-    params = cdr(params);
-    if (trace) { pserial(' '); printobject(value, pserial); }
-  }
-  if (params != NULL) error2(fname, PSTR("has too few parameters"));
-  if (args != NULL) error2(fname, PSTR("has too many parameters"));
+    if (symbolp(var) && var->name == OPTIONAL) optional = true;
+    else {
+      if (consp(var)) {
+        if (!optional) error2(fname, PSTR("invalid default value"));
+        if (args == NULL) value = eval(second(var), *env);
+        else { value = first(args); args = cdr(args); }
+        var = first(var);
+        if (!symbolp(var)) error2(fname, PSTR("illegal optional parameter"));
+      } else if (!symbolp(var)) {
+        error2(fname, PSTR("illegal parameter"));
+      } else if (var->name == AMPREST) {
+        params = cdr(params);
+        var = first(params);
+        value = args;
+        args = NULL;
+      } else {
+        if (args == NULL) {
+          if (optional) value = nil;
+          else error2(fname, PSTR("has too few arguments"));
+        } else { value = first(args); args = cdr(args); }
+      }
+      object *pair = findtwin(var, *env);
+      if (tc && (pair != NULL)) cdr(pair) = value;
+      else push(cons(var,value), *env);
+      if (trace) { pserial(' '); printobject(value, pserial); }
+    }
+    params = cdr(params);
+  }
+  if (args != NULL) error2(fname, PSTR("has too many arguments"));
   if (trace) { pserial(')'); pln(pserial); }
   // Do an implicit progn
   return tf_progn(function, *env);
@@ -1174,8 +1289,8 @@
   args = cdr(args);
   if (args != NULL) increment = integer(eval(first(args), env));
   #if defined(checkoverflow)
-  if (increment < 1) { if (INT_MIN - increment > result) error(PSTR("'incf' arithmetic overflow")); }
-  else { if (INT_MAX - increment < result) error(PSTR("'incf' arithmetic overflow")); }
+  if (increment < 1) { if (INT_MIN - increment > result) error3(INCF, PSTR("arithmetic overflow")); }
+  else { if (INT_MAX - increment < result) error3(INCF, PSTR("arithmetic overflow")); }
   #endif
   result = result + increment;
   *loc = number(result);
@@ -1189,8 +1304,8 @@
   args = cdr(args);
   if (args != NULL) decrement = integer(eval(first(args), env));
   #if defined(checkoverflow)
-  if (decrement < 1) { if (INT_MAX + decrement < result) error(PSTR("'decf' arithmetic overflow")); }
-  else { if (INT_MIN + decrement > result) error(PSTR("'decf' arithmetic overflow")); }
+  if (decrement < 1) { if (INT_MAX + decrement < result) error3(DECF, PSTR("arithmetic overflow")); }
+  else { if (INT_MIN + decrement > result) error3(DECF, PSTR("arithmetic overflow")); }
   #endif
   result = result - decrement;
   *loc = number(result);
@@ -1209,13 +1324,13 @@
   object *var = first(params);
   object *result;
   object *list = eval(second(params), env);
-  if (!listp(list)) error(PSTR("'dolist' argument is not a list"));
   push(list, GCStack); // Don't GC the list
   object *pair = cons(var,nil);
   push(pair,env);
   params = cdr(cdr(params));
   object *forms = cdr(args);
   while (list != NULL) {
+    if (improperp(list)) error3(DOLIST, PSTR("argument is not a proper list"));
     cdr(pair) = first(list);
     list = cdr(list);
     result = eval(tf_progn(forms,env), env);
@@ -1290,7 +1405,7 @@
   object *param = first(args);
   unsigned long start = millis();
   unsigned long now, total = 0;
-  if (param != NULL) total = integer(first(param));
+  if (param != NULL) total = integer(eval(first(param), env));
   eval(tf_progn(cdr(args),env), env);
   do {
     now = millis() - start;
@@ -1348,7 +1463,7 @@
   params = cddr(params);
   if (params != NULL) {
     int d = integer(eval(first(params), env));
-    if (d<1 || d>7) error(PSTR("'with-spi' invalid divider"));
+    if (d<1 || d>7) error3(WITHSPI, PSTR("invalid divider"));
     if (d == 7) divider = 3;
     else if (d & 1) divider = (d>>1) + 4;
     else divider = (d>>1) - 1;
@@ -1422,7 +1537,7 @@
 }

 object *tf_if (object *args, object *env) {
-  if (args == NULL || cdr(args) == NULL) error(PSTR("'if' missing argument(s)"));
+  if (args == NULL || cdr(args) == NULL) error3(IF, PSTR("missing argument(s)"));
   if (eval(first(args), env) != nil) return second(args);
   args = cddr(args);
   return (args != NULL) ? first(args) : nil;
@@ -1431,7 +1546,7 @@
 object *tf_cond (object *args, object *env) {
   while (args != NULL) {
     object *clause = first(args);
-    if (!consp(clause)) error2(clause, PSTR("is an illegal clause"));
+    if (!consp(clause)) error2(clause, PSTR("is an illegal 'cond' clause"));
     object *test = eval(first(clause), env);
     object *forms = cdr(clause);
     if (test != nil) {
@@ -1443,15 +1558,34 @@
 }

 object *tf_when (object *args, object *env) {
-  if (args == NULL) error(PSTR("'when' missing argument"));
+  if (args == NULL) error3(WHEN, PSTR("missing argument"));
   if (eval(first(args), env) != nil) return tf_progn(cdr(args),env);
   else return nil;
 }

 object *tf_unless (object *args, object *env) {
-  if (args == NULL) error(PSTR("'unless' missing argument"));
+  if (args == NULL) error3(UNLESS, PSTR("missing argument"));
   if (eval(first(args), env) != nil) return nil;
   else return tf_progn(cdr(args),env);
+}
+
+object *tf_case (object *args, object *env) {
+  object *test = eval(first(args), env);
+  args = cdr(args);
+  while (args != NULL) {
+    object *clause = first(args);
+    if (!consp(clause)) error2(clause, PSTR("is an illegal 'case' clause"));
+    object *key = car(clause);
+    object *forms = cdr(clause);
+    if (consp(key)) {
+      while (key != NULL) {
+        if (eq(test,car(key))) return tf_progn(forms, env);
+        key = cdr(key);
+      }
+    } else if (eq(test,key) || eq(key,tee)) return tf_progn(forms, env);
+    args = cdr(args);
+  }
+  return nil;
 }

 object *tf_and (object *args, object *env) {
@@ -1466,14 +1600,11 @@
 }

 object *tf_or (object *args, object *env) {
-  object *more = cdr(args);
-  while (more != NULL) {
-    object *result = eval(car(args), env);
-    if (result != NULL) return result;
-    args = more;
-    more = cdr(args);
-  }
-  return car(args);
+  while (args != NULL) {
+    if (eval(car(args), env) != NULL) return car(args);
+    args = cdr(args);
+  }
+  return nil;
 }

 // Core functions
@@ -1485,7 +1616,7 @@

 object *fn_cons (object *args, object *env) {
   (void) env;
-  return cons(first(args),second(args));
+  return cons(first(args), second(args));
 }

 object *fn_atom (object *args, object *env) {
@@ -1596,7 +1727,7 @@
   (void) env;
   object *arg = first(args);
   if (listp(arg)) return number(listlength(arg));
-  if (!stringp(arg)) error(PSTR("'length' argument is not a list or string"));
+  if (!stringp(arg)) error3(LENGTH, PSTR("argument is not a list or string"));
   return number(stringlength(arg));
 }

@@ -1608,9 +1739,9 @@
 object *fn_reverse (object *args, object *env) {
   (void) env;
   object *list = first(args);
-  if (!listp(list)) error(PSTR("'reverse' argument is not a list"));
   object *result = NULL;
   while (list != NULL) {
+    if (improperp(list)) error3(REVERSE, PSTR("argument is not a proper list"));
     push(first(list),result);
     list = cdr(list);
   }
@@ -1621,8 +1752,8 @@
   (void) env;
   int n = integer(first(args));
   object *list = second(args);
-  if (!listp(list)) error(PSTR("'nth' second argument is not a list"));
   while (list != NULL) {
+    if (improperp(list)) error3(NTH, PSTR("argument is not a proper list"));
     if (n == 0) return car(list);
     list = cdr(list);
     n--;
@@ -1634,7 +1765,7 @@
   (void) env;
   object *key = first(args);
   object *list = second(args);
-  if (!listp(list)) error(PSTR("'assoc' second argument is not a list"));
+  if (!listp(list)) error3(ASSOC, PSTR("second argument is not a list"));
   return assoc(key,list);
 }

@@ -1642,8 +1773,8 @@
   (void) env;
   object *item = first(args);
   object *list = second(args);
-  if (!listp(list)) error(PSTR("'member' second argument is not a list"));
   while (list != NULL) {
+    if (improperp(list)) error3(MEMBER, PSTR("argument is not a proper list"));
     if (eq(item,car(list))) return list;
     list = cdr(list);
   }
@@ -1657,7 +1788,7 @@
     previous = last;
     last = cdr(last);
   }
-  if (!listp(car(last))) error(PSTR("'apply' last argument is not a list"));
+  if (!listp(car(last))) error3(APPLY, PSTR("last argument is not a list"));
   cdr(previous) = car(last);
   return apply(first(args), cdr(args), &env);
 }
@@ -1670,41 +1801,38 @@
   (void) env;
   object *head = NULL;
   object *tail = NULL;
-  while (args != NULL) {
+  while (args != NULL) {
     object *list = first(args);
-    if (!listp(list)) error(PSTR("'append' argument is not a list"));
-    while (list != NULL) {
-      object *obj = cons(first(list),NULL);
-      if (head == NULL) {
-        head = obj;
-        tail = obj;
-      } else {
-        cdr(tail) = obj;
-        tail = obj;
-      }
+    while ((unsigned int)list >= PAIR) {
+      object *obj = cons(car(list), cdr(list));
+      if (head == NULL) head = obj;
+      else cdr(tail) = obj;
+      tail = obj;
       list = cdr(list);
     }
+    if (cdr(args) != NULL && list != NULL) error3(APPEND, PSTR("argument is not a proper list"));
     args = cdr(args);
   }
   return head;
 }

 object *fn_mapc (object *args, object *env) {
+  symbol_t name = MAPC;
   object *function = first(args);
   object *list1 = second(args);
   object *result = list1;
-  if (!listp(list1)) error(PSTR("'mapc' second argument is not a list"));
   object *list2 = cddr(args);
   if (list2 != NULL) {
     list2 = car(list2);
-    if (!listp(list2)) error(PSTR("'mapc' third argument is not a list"));
     while (list1 != NULL && list2 != NULL) {
+      if (improperp(list1)) error3(name, PSTR("second argument is not a proper list"));
+      if (improperp(list2)) error3(name, PSTR("third argument is not a proper list"));
       apply(function, cons(car(list1),cons(car(list2),NULL)), &env);
-      list1 = cdr(list1);
-      list2 = cdr(list2);
+      list1 = cdr(list1); list2 = cdr(list2);
     }
   } else {
     while (list1 != NULL) {
+      if (improperp(list1)) error3(name, PSTR("second argument is not a proper list"));
       apply(function, cons(car(list1),NULL), &env);
       list1 = cdr(list1);
     }
@@ -1713,49 +1841,75 @@
 }

 object *fn_mapcar (object *args, object *env) {
+  symbol_t name = MAPCAR;
   object *function = first(args);
   object *list1 = second(args);
-  if (!listp(list1)) error(PSTR("'mapcar' second argument is not a list"));
   object *list2 = cddr(args);
+  object *head = cons(NULL, NULL);
+  push(head,GCStack);
+  object *tail = head;
   if (list2 != NULL) {
     list2 = car(list2);
-    if (!listp(list2)) error(PSTR("'mapcar' third argument is not a list"));
-  }
-  object *head = NULL;
-  object *tail = NULL;
-  if (list2 != NULL) {
     while (list1 != NULL && list2 != NULL) {
-      object *result = apply(function, cons(car(list1),cons(car(list2),NULL)), &env);
+      if (improperp(list1)) error3(name, PSTR("second argument is not a proper list"));
+      if (improperp(list2)) error3(name, PSTR("third argument is not a proper list"));
+      object *result = apply(function, cons(car(list1), cons(car(list2),NULL)), &env);
       object *obj = cons(result,NULL);
-      if (head == NULL) {
-        head = obj;
-        push(head,GCStack);
-        tail = obj;
-      } else {
-        cdr(tail) = obj;
-        tail = obj;
-      }
-      list1 = cdr(list1);
-      list2 = cdr(list2);
-    }
-    pop(GCStack);
+      cdr(tail) = obj;
+      tail = obj;
+      list1 = cdr(list1); list2 = cdr(list2);
+    }
   } else if (list1 != NULL) {
     while (list1 != NULL) {
+      if (improperp(list1)) error3(name, PSTR("second argument is not a proper list"));
       object *result = apply(function, cons(car(list1),NULL), &env);
       object *obj = cons(result,NULL);
-      if (head == NULL) {
-        head = obj;
-        push(head,GCStack);
-        tail = obj;
-      } else {
-        cdr(tail) = obj;
-        tail = obj;
+      cdr(tail) = obj;
+      tail = obj;
+      list1 = cdr(list1);
+    }
+  }
+  pop(GCStack);
+  return cdr(head);
+}
+
+object *fn_mapcan (object *args, object *env) {
+  symbol_t name = MAPCAN;
+  object *function = first(args);
+  object *list1 = second(args);
+  object *list2 = cddr(args);
+  object *head = cons(NULL, NULL);
+  push(head,GCStack);
+  object *tail = head;
+  if (list2 != NULL) {
+    list2 = car(list2);
+    while (list1 != NULL && list2 != NULL) {
+      if (improperp(list1)) error3(name, PSTR("second argument is not a proper list"));
+      if (improperp(list2)) error3(name, PSTR("third argument is not a proper list"));
+      object *result = apply(function, cons(car(list1), cons(car(list2),NULL)), &env);
+      while (result != NULL && (unsigned int)result >= PAIR) {
+        cdr(tail) = result;
+        tail = result;
+        result = cdr(result);
       }
+      if (cdr(list1) != NULL && cdr(list2) != NULL && result != NULL) error3(name, PSTR("result is not a proper list"));
+      list1 = cdr(list1); list2 = cdr(list2);
+    }
+  } else if (list1 != NULL) {
+    while (list1 != NULL) {
+      if (improperp(list1)) error3(name, PSTR("second argument is not a proper list"));
+      object *result = apply(function, cons(car(list1),NULL), &env);
+      while (result != NULL && (unsigned int)result >= PAIR) {
+        cdr(tail) = result;
+        tail = result;
+        result = cdr(result);
+      }
+      if (cdr(list1) != NULL && result != NULL) error3(name, PSTR("result is not a proper list"));
       list1 = cdr(list1);
     }
-    pop(GCStack);
-  }
-  return head;
+  }
+  pop(GCStack);
+  return cdr(head);
 }

 // Arithmetic functions
@@ -1766,8 +1920,8 @@
   while (args != NULL) {
     int temp = integer(car(args));
     #if defined(checkoverflow)
-    if (temp < 1) { if (INT_MIN - temp > result) error(PSTR("'+' arithmetic overflow")); }
-    else { if (INT_MAX - temp < result) error(PSTR("'+' arithmetic overflow")); }
+    if (temp < 1) { if (INT_MIN - temp > result) error3(ADD, PSTR("arithmetic overflow")); }
+    else { if (INT_MAX - temp < result) error3(ADD, PSTR("arithmetic overflow")); }
     #endif
     result = result + temp;
     args = cdr(args);
@@ -1781,15 +1935,15 @@
   args = cdr(args);
   if (args == NULL) {
     #if defined(checkoverflow)
-    if (result == INT_MIN) error(PSTR("'-' arithmetic overflow"));
+    if (result == INT_MIN) error3(SUBTRACT, PSTR("arithmetic overflow"));
     #endif
     return number(-result);
   }
   while (args != NULL) {
     int temp = integer(car(args));
     #if defined(checkoverflow)
-    if (temp < 1) { if (INT_MAX + temp < result) error(PSTR("'-' arithmetic overflow")); }
-    else { if (INT_MIN + temp > result) error(PSTR("'-' arithmetic overflow")); }
+    if (temp < 1) { if (INT_MAX + temp < result) error3(SUBTRACT, PSTR("arithmetic overflow")); }
+    else { if (INT_MIN + temp > result) error3(SUBTRACT, PSTR("arithmetic overflow")); }
     #endif
     result = result - temp;
     args = cdr(args);
@@ -1803,7 +1957,7 @@
   while (args != NULL){
     #if defined(checkoverflow)
     signed long temp = (signed long) result * integer(car(args));
-    if ((temp > INT_MAX) || (temp < INT_MIN)) error(PSTR("'*' arithmetic overflow"));
+    if ((temp > INT_MAX) || (temp < INT_MIN)) error3(MULTIPLY, PSTR("arithmetic overflow"));
     result = temp;
     #else
     result = result * integer(car(args));
@@ -1821,7 +1975,7 @@
     int arg = integer(car(args));
     if (arg == 0) error(PSTR("Division by zero"));
     #if defined(checkoverflow)
-    if ((result == INT_MIN) && (arg == -1)) error(PSTR("'/' arithmetic overflow"));
+    if ((result == INT_MIN) && (arg == -1)) error3(DIVIDE, PSTR("arithmetic overflow"));
     #endif
     result = result / arg;
     args = cdr(args);
@@ -1843,7 +1997,7 @@
   (void) env;
   int result = integer(first(args));
   #if defined(checkoverflow)
-  if (result == INT_MAX) error(PSTR("'1+' arithmetic overflow"));
+  if (result == INT_MAX) error3(ONEPLUS, PSTR("arithmetic overflow"));
   #endif
   return number(result + 1);
 }
@@ -1852,7 +2006,7 @@
   (void) env;
   int result = integer(first(args));
   #if defined(checkoverflow)
-  if (result == INT_MIN) error(PSTR("'1-' arithmetic overflow"));
+  if (result == INT_MIN) error3(ONEMINUS, PSTR("arithmetic overflow"));
   #endif
   return number(result - 1);
 }
@@ -1861,7 +2015,7 @@
   (void) env;
   int result = integer(first(args));
   #if defined(checkoverflow)
-  if (result == INT_MIN) error(PSTR("'abs' arithmetic overflow"));
+  if (result == INT_MIN) error3(ABS, PSTR("arithmetic overflow"));
   #endif
   return number(abs(result));
 }
@@ -2028,7 +2182,7 @@
   object *arg = first(args);
   if (!stringp(arg)) error2(arg, PSTR("is not a string"));
   char c = nthchar(arg, integer(second(args)));
-  if (c == 0) error(PSTR("'char' index out of range"));
+  if (c == 0) error3(CHAR, PSTR("index out of range"));
   return character(c);
 }

@@ -2054,10 +2208,10 @@
   return stringp(first(args)) ? tee : nil;
 }

-bool stringcompare (object *args, bool lt, bool gt, bool eq) {
+bool stringcompare (object *args, bool lt, bool gt, bool eq, symbol_t name) {
   object *arg1 = first(args);
   object *arg2 = second(args);
-  if (!stringp(arg1) || !stringp(arg2)) error(PSTR("String compare argument is not a string"));
+  if (!stringp(arg1) || !stringp(arg2)) error3(name, PSTR("argument is not a string"));
   arg1 = cdr(arg1);
   arg2 = cdr(arg2);
   while ((arg1 != NULL) || (arg2 != NULL)) {
@@ -2073,17 +2227,17 @@

 object *fn_stringeq (object *args, object *env) {
   (void) env;
-  return stringcompare(args, false, false, true) ? tee : nil;
+  return stringcompare(args, false, false, true, STRINGEQ) ? tee : nil;
 }

 object *fn_stringless (object *args, object *env) {
   (void) env;
-  return stringcompare(args, true, false, false) ? tee : nil;
+  return stringcompare(args, true, false, false, STRINGLESS) ? tee : nil;
 }

 object *fn_stringgreater (object *args, object *env) {
   (void) env;
-  return stringcompare(args, false, true, false) ? tee : nil;
+  return stringcompare(args, false, true, false, STRINGGREATER) ? tee : nil;
 }

 object *fn_sort (object *args, object *env) {
@@ -2144,7 +2298,7 @@
   (void) env;
   object *arg = first(args);
   symbol_t name = arg->name;
-  if (name != STRINGFN) error(PSTR("'concatenate' only supports strings"));
+  if (name != STRINGFN) error3(CONCATENATE, PSTR("only supports strings"));
   args = cdr(args);
   object *result = myalloc();
   result->type = STRING;
@@ -2172,7 +2326,7 @@
 object *fn_subseq (object *args, object *env) {
   (void) env;
   object *arg = first(args);
-  if (!stringp(arg)) error(PSTR("'subseq' first argument is not a string"));
+  if (!stringp(arg)) error3(SUBSEQ, PSTR("first argument is not a string"));
   int start = integer(second(args));
   int end;
   args = cddr(args);
@@ -2183,7 +2337,7 @@
   int chars = 0;
   for (int i=start; i<end; i++) {
     char ch = nthchar(arg, i);
-    if (ch == 0) error(PSTR("'subseq' index out of range"));
+    if (ch == 0) error3(SUBSEQ, PSTR("index out of range"));
     buildstring(ch, &chars, &head);
   }
   result->cdr = head;
@@ -2203,7 +2357,7 @@
 object *fn_readfromstring (object *args, object *env) {
   (void) env;
   object *arg = first(args);
-  if (!stringp(arg)) error(PSTR("'read-from-string' argument is not a string"));
+  if (!stringp(arg)) error3(READFROMSTRING, PSTR("argument is not a string"));
   GlobalString = arg;
   GlobalStringIndex = 0;
   return read(gstr);
@@ -2426,7 +2580,7 @@
     read = (rw != NULL);
   }
   int address = stream & 0xFF;
-  if (stream>>8 != I2CSTREAM) error(PSTR("'restart' not i2c"));
+  if (stream>>8 != I2CSTREAM) error3(RESTARTI2C, PSTR("not i2c"));
   return I2Crestart(address, read) ? tee : nil;
 }

@@ -2660,7 +2814,7 @@
     object *pair = first(globals);
     object *var = car(pair);
     object *val = cdr(pair);
-    if (listp(val) && symbolp(car(val)) && car(val)->name == LAMBDA) {
+    if (consp(val) && symbolp(car(val)) && car(val)->name == LAMBDA) {
       pln(pserial);
       superprint(cons(symbol(DEFUN), cons(var, cdr(val))), 0, pserial);
       pln(pserial);
@@ -2670,14 +2824,54 @@
   return symbol(NOTHING);
 }

+// LispLibrary
+
+object *fn_require (object *args, object *env) {
+  object *arg = first(args);
+  object *globals = GlobalEnv;
+  if (!symbolp(arg)) error3(REQUIRE, PSTR("argument is not a symbol"));
+  while (globals != NULL) {
+    object *pair = first(globals);
+    object *var = car(pair);
+    if (symbolp(var) && var == arg) return nil;
+    globals = cdr(globals);
+  }
+  GlobalStringIndex = 0;
+  object *line = read(glibrary);
+  while (line != NULL) {
+    // Is this the definition we want
+    int fname = first(line)->name;
+    if ((fname == DEFUN || fname == DEFVAR) && symbolp(second(line)) && second(line)->name == arg->name) {
+      eval(line, env);
+      return tee;
+    }
+    line = read(glibrary);
+  }
+  return nil;
+}
+
+object *fn_listlibrary (object *args, object *env) {
+  (void) args, (void) env;
+  GlobalStringIndex = 0;
+  object *line = read(glibrary);
+  while (line != NULL) {
+    int fname = first(line)->name;
+    if (fname == DEFUN || fname == DEFVAR) {
+      pstring(name(second(line)), pserial); pserial(' ');
+    }
+    line = read(glibrary);
+  }
+  return symbol(NOTHING);
+}
+
 // Insert your own function definitions here

 // Built-in procedure names - stored in PROGMEM

-const char string0[] PROGMEM = "symbols";
-const char string1[] PROGMEM = "nil";
-const char string2[] PROGMEM = "t";
-const char string3[] PROGMEM = "nothing";
+const char string0[] PROGMEM = "nil";
+const char string1[] PROGMEM = "t";
+const char string2[] PROGMEM = "nothing";
+const char string3[] PROGMEM = "&optional";
 const char string4[] PROGMEM = "&rest";
 const char string5[] PROGMEM = "lambda";
 const char string6[] PROGMEM = "let";
@@ -2710,133 +2904,137 @@
 const char string33[] PROGMEM = "cond";
 const char string34[] PROGMEM = "when";
 const char string35[] PROGMEM = "unless";
-const char string36[] PROGMEM = "and";
-const char string37[] PROGMEM = "or";
-const char string38[] PROGMEM = "functions";
-const char string39[] PROGMEM = "not";
-const char string40[] PROGMEM = "null";
-const char string41[] PROGMEM = "cons";
-const char string42[] PROGMEM = "atom";
-const char string43[] PROGMEM = "listp";
-const char string44[] PROGMEM = "consp";
-const char string45[] PROGMEM = "symbolp";
-const char string46[] PROGMEM = "streamp";
-const char string47[] PROGMEM = "eq";
-const char string48[] PROGMEM = "car";
-const char string49[] PROGMEM = "first";
-const char string50[] PROGMEM = "cdr";
-const char string51[] PROGMEM = "rest";
-const char string52[] PROGMEM = "caar";
-const char string53[] PROGMEM = "cadr";
-const char string54[] PROGMEM = "second";
-const char string55[] PROGMEM = "cdar";
-const char string56[] PROGMEM = "cddr";
-const char string57[] PROGMEM = "caaar";
-const char string58[] PROGMEM = "caadr";
-const char string59[] PROGMEM = "cadar";
-const char string60[] PROGMEM = "caddr";
-const char string61[] PROGMEM = "third";
-const char string62[] PROGMEM = "cdaar";
-const char string63[] PROGMEM = "cdadr";
-const char string64[] PROGMEM = "cddar";
-const char string65[] PROGMEM = "cdddr";
-const char string66[] PROGMEM = "length";
-const char string67[] PROGMEM = "list";
-const char string68[] PROGMEM = "reverse";
-const char string69[] PROGMEM = "nth";
-const char string70[] PROGMEM = "assoc";
-const char string71[] PROGMEM = "member";
-const char string72[] PROGMEM = "apply";
-const char string73[] PROGMEM = "funcall";
-const char string74[] PROGMEM = "append";
-const char string75[] PROGMEM = "mapc";
-const char string76[] PROGMEM = "mapcar";
-const char string77[] PROGMEM = "+";
-const char string78[] PROGMEM = "-";
-const char string79[] PROGMEM = "*";
-const char string80[] PROGMEM = "/";
-const char string81[] PROGMEM = "truncate";
-const char string82[] PROGMEM = "mod";
-const char string83[] PROGMEM = "1+";
-const char string84[] PROGMEM = "1-";
-const char string85[] PROGMEM = "abs";
-const char string86[] PROGMEM = "random";
-const char string87[] PROGMEM = "max";
-const char string88[] PROGMEM = "min";
-const char string89[] PROGMEM = "/=";
-const char string90[] PROGMEM = "=";
-const char string91[] PROGMEM = "<";
-const char string92[] PROGMEM = "<=";
-const char string93[] PROGMEM = ">";
-const char string94[] PROGMEM = ">=";
-const char string95[] PROGMEM = "plusp";
-const char string96[] PROGMEM = "minusp";
-const char string97[] PROGMEM = "zerop";
-const char string98[] PROGMEM = "oddp";
-const char string99[] PROGMEM = "evenp";
-const char string100[] PROGMEM = "integerp";
-const char string101[] PROGMEM = "numberp";
-const char string102[] PROGMEM = "char";
-const char string103[] PROGMEM = "char-code";
-const char string104[] PROGMEM = "code-char";
-const char string105[] PROGMEM = "characterp";
-const char string106[] PROGMEM = "stringp";
-const char string107[] PROGMEM = "string=";
-const char string108[] PROGMEM = "string<";
-const char string109[] PROGMEM = "string>";
-const char string110[] PROGMEM = "sort";
-const char string111[] PROGMEM = "string";
-const char string112[] PROGMEM = "concatenate";
-const char string113[] PROGMEM = "subseq";
-const char string114[] PROGMEM = "read-from-string";
-const char string115[] PROGMEM = "princ-to-string";
-const char string116[] PROGMEM = "prin1-to-string";
-const char string117[] PROGMEM = "logand";
-const char string118[] PROGMEM = "logior";
-const char string119[] PROGMEM = "logxor";
-const char string120[] PROGMEM = "lognot";
-const char string121[] PROGMEM = "ash";
-const char string122[] PROGMEM = "logbitp";
-const char string123[] PROGMEM = "eval";
-const char string124[] PROGMEM = "globals";
-const char string125[] PROGMEM = "locals";
-const char string126[] PROGMEM = "makunbound";
-const char string127[] PROGMEM = "break";
-const char string128[] PROGMEM = "read";
-const char string129[] PROGMEM = "prin1";
-const char string130[] PROGMEM = "print";
-const char string131[] PROGMEM = "princ";
-const char string132[] PROGMEM = "terpri";
-const char string133[] PROGMEM = "read-byte";
-const char string134[] PROGMEM = "read-line";
-const char string135[] PROGMEM = "write-byte";
-const char string136[] PROGMEM = "write-string";
-const char string137[] PROGMEM = "write-line";
-const char string138[] PROGMEM = "restart-i2c";
-const char string139[] PROGMEM = "gc";
-const char string140[] PROGMEM = "room";
-const char string141[] PROGMEM = "save-image";
-const char string142[] PROGMEM = "load-image";
-const char string143[] PROGMEM = "cls";
-const char string144[] PROGMEM = "pinmode";
-const char string145[] PROGMEM = "digitalread";
-const char string146[] PROGMEM = "digitalwrite";
-const char string147[] PROGMEM = "analogread";
-const char string148[] PROGMEM = "analogwrite";
-const char string149[] PROGMEM = "delay";
-const char string150[] PROGMEM = "millis";
-const char string151[] PROGMEM = "sleep";
-const char string152[] PROGMEM = "note";
-const char string153[] PROGMEM = "edit";
-const char string154[] PROGMEM = "pprint";
-const char string155[] PROGMEM = "pprintall";
+const char string36[] PROGMEM = "case";
+const char string37[] PROGMEM = "and";
+const char string38[] PROGMEM = "or";
+const char string39[] PROGMEM = "functions";
+const char string40[] PROGMEM = "not";
+const char string41[] PROGMEM = "null";
+const char string42[] PROGMEM = "cons";
+const char string43[] PROGMEM = "atom";
+const char string44[] PROGMEM = "listp";
+const char string45[] PROGMEM = "consp";
+const char string46[] PROGMEM = "symbolp";
+const char string47[] PROGMEM = "streamp";
+const char string48[] PROGMEM = "eq";
+const char string49[] PROGMEM = "car";
+const char string50[] PROGMEM = "first";
+const char string51[] PROGMEM = "cdr";
+const char string52[] PROGMEM = "rest";
+const char string53[] PROGMEM = "caar";
+const char string54[] PROGMEM = "cadr";
+const char string55[] PROGMEM = "second";
+const char string56[] PROGMEM = "cdar";
+const char string57[] PROGMEM = "cddr";
+const char string58[] PROGMEM = "caaar";
+const char string59[] PROGMEM = "caadr";
+const char string60[] PROGMEM = "cadar";
+const char string61[] PROGMEM = "caddr";
+const char string62[] PROGMEM = "third";
+const char string63[] PROGMEM = "cdaar";
+const char string64[] PROGMEM = "cdadr";
+const char string65[] PROGMEM = "cddar";
+const char string66[] PROGMEM = "cdddr";
+const char string67[] PROGMEM = "length";
+const char string68[] PROGMEM = "list";
+const char string69[] PROGMEM = "reverse";
+const char string70[] PROGMEM = "nth";
+const char string71[] PROGMEM = "assoc";
+const char string72[] PROGMEM = "member";
+const char string73[] PROGMEM = "apply";
+const char string74[] PROGMEM = "funcall";
+const char string75[] PROGMEM = "append";
+const char string76[] PROGMEM = "mapc";
+const char string77[] PROGMEM = "mapcar";
+const char string78[] PROGMEM = "mapcan";
+const char string79[] PROGMEM = "+";
+const char string80[] PROGMEM = "-";
+const char string81[] PROGMEM = "*";
+const char string82[] PROGMEM = "/";
+const char string83[] PROGMEM = "truncate";
+const char string84[] PROGMEM = "mod";
+const char string85[] PROGMEM = "1+";
+const char string86[] PROGMEM = "1-";
+const char string87[] PROGMEM = "abs";
+const char string88[] PROGMEM = "random";
+const char string89[] PROGMEM = "max";
+const char string90[] PROGMEM = "min";
+const char string91[] PROGMEM = "/=";
+const char string92[] PROGMEM = "=";
+const char string93[] PROGMEM = "<";
+const char string94[] PROGMEM = "<=";
+const char string95[] PROGMEM = ">";
+const char string96[] PROGMEM = ">=";
+const char string97[] PROGMEM = "plusp";
+const char string98[] PROGMEM = "minusp";
+const char string99[] PROGMEM = "zerop";
+const char string100[] PROGMEM = "oddp";
+const char string101[] PROGMEM = "evenp";
+const char string102[] PROGMEM = "integerp";
+const char string103[] PROGMEM = "numberp";
+const char string104[] PROGMEM = "char";
+const char string105[] PROGMEM = "char-code";
+const char string106[] PROGMEM = "code-char";
+const char string107[] PROGMEM = "characterp";
+const char string108[] PROGMEM = "stringp";
+const char string109[] PROGMEM = "string=";
+const char string110[] PROGMEM = "string<";
+const char string111[] PROGMEM = "string>";
+const char string112[] PROGMEM = "sort";
+const char string113[] PROGMEM = "string";
+const char string114[] PROGMEM = "concatenate";
+const char string115[] PROGMEM = "subseq";
+const char string116[] PROGMEM = "read-from-string";
+const char string117[] PROGMEM = "princ-to-string";
+const char string118[] PROGMEM = "prin1-to-string";
+const char string119[] PROGMEM = "logand";
+const char string120[] PROGMEM = "logior";
+const char string121[] PROGMEM = "logxor";
+const char string122[] PROGMEM = "lognot";
+const char string123[] PROGMEM = "ash";
+const char string124[] PROGMEM = "logbitp";
+const char string125[] PROGMEM = "eval";
+const char string126[] PROGMEM = "globals";
+const char string127[] PROGMEM = "locals";
+const char string128[] PROGMEM = "makunbound";
+const char string129[] PROGMEM = "break";
+const char string130[] PROGMEM = "read";
+const char string131[] PROGMEM = "prin1";
+const char string132[] PROGMEM = "print";
+const char string133[] PROGMEM = "princ";
+const char string134[] PROGMEM = "terpri";
+const char string135[] PROGMEM = "read-byte";
+const char string136[] PROGMEM = "read-line";
+const char string137[] PROGMEM = "write-byte";
+const char string138[] PROGMEM = "write-string";
+const char string139[] PROGMEM = "write-line";
+const char string140[] PROGMEM = "restart-i2c";
+const char string141[] PROGMEM = "gc";
+const char string142[] PROGMEM = "room";
+const char string143[] PROGMEM = "save-image";
+const char string144[] PROGMEM = "load-image";
+const char string145[] PROGMEM = "cls";
+const char string146[] PROGMEM = "pinmode";
+const char string147[] PROGMEM = "digitalread";
+const char string148[] PROGMEM = "digitalwrite";
+const char string149[] PROGMEM = "analogread";
+const char string150[] PROGMEM = "analogwrite";
+const char string151[] PROGMEM = "delay";
+const char string152[] PROGMEM = "millis";
+const char string153[] PROGMEM = "sleep";
+const char string154[] PROGMEM = "note";
+const char string155[] PROGMEM = "edit";
+const char string156[] PROGMEM = "pprint";
+const char string157[] PROGMEM = "pprintall";
+const char string158[] PROGMEM = "require";
+const char string159[] PROGMEM = "list-library";

 const tbl_entry_t lookup_table[] PROGMEM = {
-  { string0, NULL, NIL, NIL },
+  { string0, NULL, 0, 0 },
   { string1, NULL, 0, 0 },
-  { string2, NULL, 1, 0 },
-  { string3, NULL, 1, 0 },
-  { string4, NULL, 1, 0 },
+  { string2, NULL, 0, 0 },
+  { string3, NULL, 0, 0 },
+  { string4, NULL, 0, 0 },
   { string5, NULL, 0, 127 },
   { string6, NULL, 0, 127 },
   { string7, NULL, 0, 127 },
@@ -2868,126 +3066,130 @@
   { string33, tf_cond, 0, 127 },
   { string34, tf_when, 1, 127 },
   { string35, tf_unless, 1, 127 },
-  { string36, tf_and, 0, 127 },
-  { string37, tf_or, 0, 127 },
-  { string38, NULL, NIL, NIL },
-  { string39, fn_not, 1, 1 },
+  { string36, tf_case, 1, 127 },
+  { string37, tf_and, 0, 127 },
+  { string38, tf_or, 0, 127 },
+  { string39, NULL, NIL, NIL },
   { string40, fn_not, 1, 1 },
-  { string41, fn_cons, 2, 2 },
-  { string42, fn_atom, 1, 1 },
-  { string43, fn_listp, 1, 1 },
-  { string44, fn_consp, 1, 1 },
-  { string45, fn_symbolp, 1, 1 },
-  { string46, fn_streamp, 1, 1 },
-  { string47, fn_eq, 2, 2 },
-  { string48, fn_car, 1, 1 },
+  { string41, fn_not, 1, 1 },
+  { string42, fn_cons, 2, 2 },
+  { string43, fn_atom, 1, 1 },
+  { string44, fn_listp, 1, 1 },
+  { string45, fn_consp, 1, 1 },
+  { string46, fn_symbolp, 1, 1 },
+  { string47, fn_streamp, 1, 1 },
+  { string48, fn_eq, 2, 2 },
   { string49, fn_car, 1, 1 },
-  { string50, fn_cdr, 1, 1 },
+  { string50, fn_car, 1, 1 },
   { string51, fn_cdr, 1, 1 },
-  { string52, fn_caar, 1, 1 },
-  { string53, fn_cadr, 1, 1 },
+  { string52, fn_cdr, 1, 1 },
+  { string53, fn_caar, 1, 1 },
   { string54, fn_cadr, 1, 1 },
-  { string55, fn_cdar, 1, 1 },
-  { string56, fn_cddr, 1, 1 },
-  { string57, fn_caaar, 1, 1 },
-  { string58, fn_caadr, 1, 1 },
-  { string59, fn_cadar, 1, 1 },
-  { string60, fn_caddr, 1, 1 },
+  { string55, fn_cadr, 1, 1 },
+  { string56, fn_cdar, 1, 1 },
+  { string57, fn_cddr, 1, 1 },
+  { string58, fn_caaar, 1, 1 },
+  { string59, fn_caadr, 1, 1 },
+  { string60, fn_cadar, 1, 1 },
   { string61, fn_caddr, 1, 1 },
-  { string62, fn_cdaar, 1, 1 },
-  { string63, fn_cdadr, 1, 1 },
-  { string64, fn_cddar, 1, 1 },
-  { string65, fn_cdddr, 1, 1 },
-  { string66, fn_length, 1, 1 },
-  { string67, fn_list, 0, 127 },
-  { string68, fn_reverse, 1, 1 },
-  { string69, fn_nth, 2, 2 },
-  { string70, fn_assoc, 2, 2 },
-  { string71, fn_member, 2, 2 },
-  { string72, fn_apply, 2, 127 },
-  { string73, fn_funcall, 1, 127 },
-  { string74, fn_append, 0, 127 },
-  { string75, fn_mapc, 2, 3 },
-  { string76, fn_mapcar, 2, 3 },
-  { string77, fn_add, 0, 127 },
-  { string78, fn_subtract, 1, 127 },
-  { string79, fn_multiply, 0, 127 },
-  { string80, fn_divide, 2, 127 },
-  { string81, fn_divide, 1, 2 },
-  { string82, fn_mod, 2, 2 },
-  { string83, fn_oneplus, 1, 1 },
-  { string84, fn_oneminus, 1, 1 },
-  { string85, fn_abs, 1, 1 },
-  { string86, fn_random, 1, 1 },
-  { string87, fn_maxfn, 1, 127 },
-  { string88, fn_minfn, 1, 127 },
-  { string89, fn_noteq, 1, 127 },
-  { string90, fn_numeq, 1, 127 },
-  { string91, fn_less, 1, 127 },
-  { string92, fn_lesseq, 1, 127 },
-  { string93, fn_greater, 1, 127 },
-  { string94, fn_greatereq, 1, 127 },
-  { string95, fn_plusp, 1, 1 },
-  { string96, fn_minusp, 1, 1 },
-  { string97, fn_zerop, 1, 1 },
-  { string98, fn_oddp, 1, 1 },
-  { string99, fn_evenp, 1, 1 },
-  { string100, fn_integerp, 1, 1 },
-  { string101, fn_numberp, 1, 1 },
-  { string102, fn_char, 2, 2 },
-  { string103, fn_charcode, 1, 1 },
-  { string104, fn_codechar, 1, 1 },
-  { string105, fn_characterp, 1, 1 },
-  { string106, fn_stringp, 1, 1 },
-  { string107, fn_stringeq, 2, 2 },
-  { string108, fn_stringless, 2, 2 },
-  { string109, fn_stringgreater, 2, 2 },
-  { string110, fn_sort, 2, 2 },
-  { string111, fn_stringfn, 1, 1 },
-  { string112, fn_concatenate, 1, 127 },
-  { string113, fn_subseq, 2, 3 },
-  { string114, fn_readfromstring, 1, 1 },
-  { string115, fn_princtostring, 1, 1 },
-  { string116, fn_prin1tostring, 1, 1 },
-  { string117, fn_logand, 0, 127 },
-  { string118, fn_logior, 0, 127 },
-  { string119, fn_logxor, 0, 127 },
-  { string120, fn_lognot, 1, 1 },
-  { string121, fn_ash, 2, 2 },
-  { string122, fn_logbitp, 2, 2 },
-  { string123, fn_eval, 1, 1 },
-  { string124, fn_globals, 0, 0 },
-  { string125, fn_locals, 0, 0 },
-  { string126, fn_makunbound, 1, 1 },
-  { string127, fn_break, 0, 0 },
-  { string128, fn_read, 0, 1 },
-  { string129, fn_prin1, 1, 2 },
-  { string130, fn_print, 1, 2 },
-  { string131, fn_princ, 1, 2 },
-  { string132, fn_terpri, 0, 1 },
-  { string133, fn_readbyte, 0, 2 },
-  { string134, fn_readline, 0, 1 },
-  { string135, fn_writebyte, 1, 2 },
-  { string136, fn_writestring, 1, 2 },
-  { string137, fn_writeline, 1, 2 },
-  { string138, fn_restarti2c, 1, 2 },
-  { string139, fn_gc, 0, 0 },
-  { string140, fn_room, 0, 0 },
-  { string141, fn_saveimage, 0, 1 },
-  { string142, fn_loadimage, 0, 1 },
-  { string143, fn_cls, 0, 0 },
-  { string144, fn_pinmode, 2, 2 },
-  { string145, fn_digitalread, 1, 1 },
-  { string146, fn_digitalwrite, 2, 2 },
-  { string147, fn_analogread, 1, 1 },
-  { string148, fn_analogwrite, 2, 2 },
-  { string149, fn_delay, 1, 1 },
-  { string150, fn_millis, 0, 0 },
-  { string151, fn_sleep, 1, 1 },
-  { string152, fn_note, 0, 3 },
-  { string153, fn_edit, 1, 1 },
-  { string154, fn_pprint, 1, 2 },
-  { string155, fn_pprintall, 0, 0 },
+  { string62, fn_caddr, 1, 1 },
+  { string63, fn_cdaar, 1, 1 },
+  { string64, fn_cdadr, 1, 1 },
+  { string65, fn_cddar, 1, 1 },
+  { string66, fn_cdddr, 1, 1 },
+  { string67, fn_length, 1, 1 },
+  { string68, fn_list, 0, 127 },
+  { string69, fn_reverse, 1, 1 },
+  { string70, fn_nth, 2, 2 },
+  { string71, fn_assoc, 2, 2 },
+  { string72, fn_member, 2, 2 },
+  { string73, fn_apply, 2, 127 },
+  { string74, fn_funcall, 1, 127 },
+  { string75, fn_append, 0, 127 },
+  { string76, fn_mapc, 2, 3 },
+  { string77, fn_mapcar, 2, 3 },
+  { string78, fn_mapcan, 2, 3 },
+  { string79, fn_add, 0, 127 },
+  { string80, fn_subtract, 1, 127 },
+  { string81, fn_multiply, 0, 127 },
+  { string82, fn_divide, 2, 127 },
+  { string83, fn_divide, 1, 2 },
+  { string84, fn_mod, 2, 2 },
+  { string85, fn_oneplus, 1, 1 },
+  { string86, fn_oneminus, 1, 1 },
+  { string87, fn_abs, 1, 1 },
+  { string88, fn_random, 1, 1 },
+  { string89, fn_maxfn, 1, 127 },
+  { string90, fn_minfn, 1, 127 },
+  { string91, fn_noteq, 1, 127 },
+  { string92, fn_numeq, 1, 127 },
+  { string93, fn_less, 1, 127 },
+  { string94, fn_lesseq, 1, 127 },
+  { string95, fn_greater, 1, 127 },
+  { string96, fn_greatereq, 1, 127 },
+  { string97, fn_plusp, 1, 1 },
+  { string98, fn_minusp, 1, 1 },
+  { string99, fn_zerop, 1, 1 },
+  { string100, fn_oddp, 1, 1 },
+  { string101, fn_evenp, 1, 1 },
+  { string102, fn_integerp, 1, 1 },
+  { string103, fn_numberp, 1, 1 },
+  { string104, fn_char, 2, 2 },
+  { string105, fn_charcode, 1, 1 },
+  { string106, fn_codechar, 1, 1 },
+  { string107, fn_characterp, 1, 1 },
+  { string108, fn_stringp, 1, 1 },
+  { string109, fn_stringeq, 2, 2 },
+  { string110, fn_stringless, 2, 2 },
+  { string111, fn_stringgreater, 2, 2 },
+  { string112, fn_sort, 2, 2 },
+  { string113, fn_stringfn, 1, 1 },
+  { string114, fn_concatenate, 1, 127 },
+  { string115, fn_subseq, 2, 3 },
+  { string116, fn_readfromstring, 1, 1 },
+  { string117, fn_princtostring, 1, 1 },
+  { string118, fn_prin1tostring, 1, 1 },
+  { string119, fn_logand, 0, 127 },
+  { string120, fn_logior, 0, 127 },
+  { string121, fn_logxor, 0, 127 },
+  { string122, fn_lognot, 1, 1 },
+  { string123, fn_ash, 2, 2 },
+  { string124, fn_logbitp, 2, 2 },
+  { string125, fn_eval, 1, 1 },
+  { string126, fn_globals, 0, 0 },
+  { string127, fn_locals, 0, 0 },
+  { string128, fn_makunbound, 1, 1 },
+  { string129, fn_break, 0, 0 },
+  { string130, fn_read, 0, 1 },
+  { string131, fn_prin1, 1, 2 },
+  { string132, fn_print, 1, 2 },
+  { string133, fn_princ, 1, 2 },
+  { string134, fn_terpri, 0, 1 },
+  { string135, fn_readbyte, 0, 2 },
+  { string136, fn_readline, 0, 1 },
+  { string137, fn_writebyte, 1, 2 },
+  { string138, fn_writestring, 1, 2 },
+  { string139, fn_writeline, 1, 2 },
+  { string140, fn_restarti2c, 1, 2 },
+  { string141, fn_gc, 0, 0 },
+  { string142, fn_room, 0, 0 },
+  { string143, fn_saveimage, 0, 1 },
+  { string144, fn_loadimage, 0, 1 },
+  { string145, fn_cls, 0, 0 },
+  { string146, fn_pinmode, 2, 2 },
+  { string147, fn_digitalread, 1, 1 },
+  { string148, fn_digitalwrite, 2, 2 },
+  { string149, fn_analogread, 1, 1 },
+  { string150, fn_analogwrite, 2, 2 },
+  { string151, fn_delay, 1, 1 },
+  { string152, fn_millis, 0, 0 },
+  { string153, fn_sleep, 1, 1 },
+  { string154, fn_note, 0, 3 },
+  { string155, fn_edit, 1, 1 },
+  { string156, fn_pprint, 1, 2 },
+  { string157, fn_pprintall, 0, 0 },
+  { string158, fn_require, 1, 1 },
+  { string159, fn_listlibrary, 0, 0 },
 };

 // Table lookup functions
@@ -2995,7 +3197,7 @@
 int builtin (char* n) {
   int entry = 0;
   while (entry < ENDFUNCTIONS) {
-    if (strcmp_P(n, (char*)pgm_read_word(&lookup_table[entry].string)) == 0)
+    if (strcasecmp_P(n, (char*)pgm_read_word(&lookup_table[entry].string)) == 0)
       return entry;
     entry++;
   }
@@ -3005,7 +3207,7 @@
 int longsymbol (char *buffer) {
   char *p = SymbolTable;
   int i = 0;
-  while (strcmp(p, buffer) != 0) {p = p + strlen(p) + 1; i++; }
+  while (strcasecmp(p, buffer) != 0) {p = p + strlen(p) + 1; i++; }
   if (p == buffer) {
     // Add to symbol table?
     char *newtop = SymbolTop + strlen(p) + 1;
@@ -3061,6 +3263,7 @@
 object *eval (object *form, object *env) {
   int TC=0;
   EVAL:
+  yield(); // Needed on ESP8266 to avoid Soft WDT Reset
   // Enough space?
   if (End != 0xA5) error(PSTR("Stack overflow"));
   if (Freespace <= WORKSPACESIZE>>4) gc(form, env);
@@ -3072,7 +3275,7 @@

   if (form == NULL) return nil;

-  if (integerp(form) || characterp(form) || stringp(form)) return form;
+  if (integerp(form) || floatp(form) || characterp(form) || stringp(form)) return form;

   if (symbolp(form)) {
     symbol_t name = form->name;
@@ -3088,6 +3291,8 @@
   // It's a list
   object *function = car(form);
   object *args = cdr(form);
+
+  if (function == NULL) error(PSTR("'nil' is an illegal function"));
   if (!listp(args)) error(PSTR("Can't evaluate a dotted pair"));

   // List starts with a symbol?
@@ -3131,6 +3336,8 @@
       return cons(symbol(CLOSURE), cons(envcopy,args));
     }

+    if (name < SPECIAL_FORMS) error2(function, PSTR("can't be used as a function"));
+
     if ((name > SPECIAL_FORMS) && (name < TAIL_FORMS)) {
       return ((fn_ptr_type)lookupfn(name))(args, env);
     }
@@ -3320,7 +3527,6 @@

 // Read functions

-#if defined(lisplibrary)
 int glibrary () {
   if (LastChar) {
     char temp = LastChar;
@@ -3339,7 +3545,6 @@
     line = read(glibrary);
   }
 }
-#endif

 int gserial () {
   if (LastChar) {
@@ -3389,7 +3594,7 @@
     else if (ch == 'B') base = 2;
     else if (ch == 'O') base = 8;
     else if (ch == 'X') base = 16;
-    else if (ch == 0x07); // Ignore '
+    else if (ch == 0x07) return (object *)QUO;
     else error(PSTR("Illegal character after #"));
     ch = gfun();
   }
@@ -3476,7 +3681,7 @@
   initworkspace();
   initenv();
   initsleep();
-  pfstring(PSTR("uLisp 2.5 "), pserial); pln(pserial);
+  pfstring(PSTR("uLisp 2.6 "), pserial); pln(pserial);
 }

 // Read/Evaluate/Print loop
@@ -3527,4 +3732,4 @@
   if (!tstflag(LIBRARYLOADED)) { setflag(LIBRARYLOADED); loadfromlibrary(NULL); }
   #endif
   repl(NULL);
-}+}


Remove the DoThisOnce family of functors. Realize that the if(wpb.pressed()) block occurs just once and so all DoThisOnce functionality can be moved there without the use of any static variables keeping track of state. Move UI changing and motor speed-setting once per case entry functionality to these blocks. Stop implementing Mode as a struct, since each whole struct takes up a lot of memory just for the sake of unnecessary modularity and prettification. Replace said prettification with a new changeUI(timeStr, instrucitonStr) function that fits our context perfectly and with 0 memory footprint. Return to implementing Mode as an enumeration. Fix some formatting. Add \`line\` enumeration for corresponding indices to UI elements.

--- dremelfuge.ino
+++ dremelfuge.ino
@@ -12,7 +12,7 @@
 #include <LiquidCrystal.h>                              // library inside of Arduino.app, use <> DIRECTIVE
 #include "LEDButton.h"                                  // library not in Arduino.app but in sketch folder, use "" LITERAL PATHNAME
 #include "Printers.h"                                   // wrapper/extension class of LiquidCrystal and other Stream classes
-#include <avr/pgmspace.h>                               // contains macros for storing data in Flash instead of RAM, and fetching it
+#include <avr/pgmspace.h>                               // contains macros for storing data in Flash instead of RAM, and fetching it

 /*============================GLOBAL VARIABLES=================================//
   These variables MUST be global because they're being used by multiple
@@ -24,15 +24,14 @@
   comment in loop() for details on `static`)
   */

-// 1) Mode structs in PROGMEM, Mode::id in PROGMEM
-// 2) constexpr function and named variables for timeIndex and speedIndex, to be put
+// 1) constexpr function and named variables for timeCol and speedCol, to be put
 // in printfval
-// 3) Define custom value decorations in THIS FILE, to be fed into lcdPrinter where
+// 2) Define custom value decorations in THIS FILE, to be fed into lcdPrinter where
 // it will be parsed and the value will be inserted by the placement of the ' ' i.e.
 // "< >" => "<15:00>" (constexpr)

-namespace
-{
+namespace {
+
   const uint8_t MOTOR_PIN = 9;
   const uint8_t POT_PIN = A5;

@@ -47,38 +46,20 @@
   const uint8_t LCD_COLUMNS = 20;
   const uint8_t LCD_ROWS = 4;

-  // UI Modes - Each Mode is associated with UI Strings describing its UI
-  struct Mode {
-
-    /* `PROGMEM` is a macro for an attribute that tells the compiler to write
-      instructions for loading this variable into Flash memory at the start of
-      runtime.
-      `PGM_P` is a macro for the type const char* (pointer to (the start of) a
-      string), useful since it connotates that this variable is stored in Flash,
-      which clarifies for the user and helps the compiler optimize assembly code.
-      */
-
-    // Store array of 4 pointers to strings in Flash memory
-    // Note: strings to which pointers point aren't restricted in length
-    PGM_P UIStringPtrs[LCD_ROWS] PROGMEM;
-    const uint8_t id;
-
-    // Tie integer row indicies to meaningful names based on information layout
-    enum UIStringType : char {
-      Title = 0,
-      Time,
-      Speed,
-      Instruction
-    };
-
-    // Tie UI Modes with integer IDs so that `switch` works--only accepts ints
-    enum ID : char {
-      SETTING_TIME,
-      SETTING_SPEED,
-      SPINNING
-    };
-
-  }; // struct Mode
+  // Macro to prettify long line for reading and then printing to LCD from Flash
+  #define lcd_print_P(string)  lcd.print( (PGM_P) pgm_read_word( &(string) ))
+
+  /* `pgm_read_word` implementation:
+    Implements a Load Program Memory instruction on an 8-bit RAM
+    address argument that's converted to a 16-bit Flash memory address before
+    the address is read and has its stored value returned.
+    http://www.atmel.com/images/doc1233.pdf
+    http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__pgmspace_1ga7fa92c0a662403a643859e0f33b0a182.html
+
+    Need `&` because it's not just the char arrays[] that's stored in Flash,
+    but the pointers to these strings, held in UIStringPtrs, are stored in
+    Flash too. So, convert the pointer and then return its Flash data.
+    */

   /* Explanation of string problem and contants/literals:
     Note: Literals are not temporaries.
@@ -111,6 +92,18 @@
       The latter can be inefficient since you're storing and retrieving in Flash
       an additional array, of pointers.
       */
+  namespace UI {
+
+    const char title[] PROGMEM      = "  Dremel Centrifuge";
+    const char setTime[] PROGMEM    = "Set time: ";
+    const char setSpeed[] PROGMEM   = "Set speed: ---";
+    const char pushStart[] PROGMEM  = "   Push to Start!";
+
+    // UI associated with SPINNING mode
+    const char finishedIn[] PROGMEM = "Finished in: ";
+    const char pushStop[] PROGMEM   = "   Push to Stop! ";
+
+  } // namespace UI

   /* char var_name[] vs char* var_name:
     `char var_name[]` is an ARRAY of characters, which is different from `char*`
@@ -133,24 +126,26 @@
     determined based on the number of elements, and only the minimum amount of
     memory to store the full array is allocated.
     */
-
-  // Static (not the keyword) UI elements + UI associated with setting up Modes
-  const char titleStr[] PROGMEM      = "  Dremel Centrifuge";
-  const char setTimeStr[] PROGMEM    = "Set time: ";
-  const char setSpeedStr[] PROGMEM   = "Set speed: ---";
-  const char startStr[] PROGMEM      = "   Push to Start!";
-
-  // UI associated with SPINNING mode
-  const char finishedInStr[] PROGMEM = "Finished in: ";
-  const char stopStr[] PROGMEM       = "   Push to Stop! ";
-  const char null_str[] PROGMEM      = "";
+
+  // Enumeration of row indices as UI elements
+  enum line : char {
+
+    Title = 0,
+    Time,
+    Speed,
+    Instructions
+
+  };

 } // namespace

+// Function Prototypes
+inline void changeUI(PGM_P timeStr, PGM_P instructionStr);
+
 /////////////////////////////////////////////////////////////////////////////////

-void setup()
-{
+void setup() {
+
   lcd.begin(LCD_COLUMNS, LCD_ROWS);

   wpb.begin();
@@ -162,46 +157,22 @@
   // Motor is OFF on startup
   digitalWrite(MOTOR_PIN, LOW);

-  // Associate static elements of LCD UI with a temporary Mode
-  Mode setupMode = {
-    .UIStringPtrs =
-    {
-      titleStr,  // accessing a whole array, so its (starting) address is returned
-      null_str,
-      setSpeedStr,
-      null_str
-    },
-    .id = 0
-  };
-
-  // Initialize static (not the keyword) elements of LCD UI
-  for(int row = 0; row < LCD_ROWS; row++) {
-
-    lcd.setCursor(0, row);
-
-    /* `pgm_read_word` implementation:
-      Implements a Load Program Memory instruction on an 8-bit RAM
-      address argument that's converted to a 16-bit Flash memory address before
-      the address is read and has its stored value returned.
-      http://www.atmel.com/images/doc1233.pdf
-      http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__pgmspace_1ga7fa92c0a662403a643859e0f33b0a182.html
-
-      Need `&` because it's not just the char arrays[] that's stored in Flash,
-      but the pointers to these strings, held in UIStringPtrs, are stored in
-      Flash too. So, convert the pointer and then return its Flash data.
-      */
-
-    // Read and the UIStringPtrs from Flash memory, and print
-    lcd.print( (PGM_P) pgm_read_word(&(setupMode.UIStringPtrs[row])));
-
-  } // for loop LCD_ROWS
+  // Read and print the UIStrings from Flash memory
+  using namespace UI;
+  lcd.setCursor(0, line::Title);
+  lcd_print_P(title);
+  lcd.setCursor(0, line::Time);
+  lcd_print_P(setTime);
+  lcd.setCursor(0, line::Speed);
+  lcd_print_P(setSpeed);
+  lcd.setCursor(0, line::Instructions);
+  lcd_print_P(pushStart);

 } // void setup()

 /////////////////////////////////////////////////////////////////////////////////

-void loop()
-{
+void loop() {

   /* Explanation of `static` and statically allocated memory in C++:

@@ -264,103 +235,19 @@
     different areas of RAM and by different means.
     */

-  // Alias for the verbose Mode::UIStringType type name
-  typedef Mode::UIStringType line;
-
-  // Functor that encapsulates (hides the implementation of) changing UI ONCE
-  // per the current mode's own UIStringPtrs; extension of LiquidCrystal:
-
-  struct UIChanger {
-
-    private:
-
-      // Remember state of (re)initialization, so UI changes ONCE per case switch
-      bool initialized;
-
-      // Remember which LiquidCrystal object to operate on
-      LiquidCrystal* lcdPtr;
-
-    public:
-
-      // Initialization of state
-      UIChanger(LiquidCrystal* dPtr)
-      : lcdPtr(dPtr)
-      , initialized(false)
-      {}
-
-      // Initialization of UI based on current Mode
-      void operator() (Mode* currentModePtr) {
-
-        // Don't initialize UI more than once per case entry
-        if(initialized)
-          return;
-
-        // Change UI based on currentMode's own UIStringPtrs, read from Flash memory
-        // Partial overwrite due to overwriteStrs that do not span the entire row
-
-        // "Set time:" => "Finished in:"
-        lcdPtr->setCursor(0, line::Time);
-        lcdPtr->print( (PGM_P) pgm_read_word(&(currentModePtr->UIStringPtrs[line::Time])));
-
-        // "Push to Start!" => "Push to Stop!"
-        lcdPtr->setCursor(0, line::Instruction);
-        lcdPtr->print( (PGM_P) pgm_read_word(&(currentModePtr->UIStringPtrs[line::Instruction])));
-
-        initialized = true;
-
-      }
-
-      // Uninitialize so that UI can be initialized only once for another case
-      void uninitialize() {
-        initialized = false;
-      }
-
-  };
-
-  // All static--initialize structs only once, before main()
-
-  static Mode setTimeMode = {
-    .UIStringPtrs =
-    {
-      null_str,
-      setTimeStr,
-      null_str,
-      startStr
-    },
-    .id = Mode::ID::SETTING_TIME
-  };
-
-  static Mode setSpeedMode = {
-    .UIStringPtrs =
-    {
-      null_str,
-      null_str,
-      null_str,
-      null_str
-    },
-    .id = Mode::ID::SETTING_SPEED
-  };
-
-  static Mode spinningMode = {
-    .UIStringPtrs =
-    {
-      null_str,
-      finishedInStr,
-      null_str,
-      stopStr
-    },
-    .id = Mode::ID::SPINNING
-  };
-
-  // Initialize setTimeMode as the starting Mode
-  // Current Mode kept in a pointer so that entire struct needn't be copied
-  static Mode* currentModePtr = &setTimeMode;
+  // Define a custom type, whose enumerated values are the size of `char`s
+  enum class Mode : char {
+
+    SETTING_TIME,
+    SETTING_SPEED,
+    SPINNING
+
+  }; // enum class Mode
+
+  static Mode currentMode = Mode::SETTING_TIME;         // initialization over assignment; set SETTING_TIME as the first mode!

   // Extension of LiquidCystal as a Stream
   static Printer<LiquidCrystal> lcdPrinter(&lcd);
-
-  // Initialize UIChanger functor for use in each case-also extends LiquidCrystal
-  static UIChanger changeUI(&lcd);

   // Functors customizing methods on lcdPrinter
   static NormalPrint raw;                               // Default constructors
@@ -375,36 +262,28 @@
   static unsigned long spinningStartTime;               // point in time when countdown starts i.e. when wpb pressed the 2nd time
   static uint8_t motorSpeed;                            // uint8_t == typedef for an unsigned 8-bit integer aka a char aka a byte

-  switch(currentModePtr->id) {
-
-    case Mode::ID::SETTING_TIME: {
-
-      // Initialize UI only once per case entry; state of initialization
-      // remembered due to functor being holding state and being `static`
-      changeUI(currentModePtr);
+  switch(currentMode) {
+
+    case Mode::SETTING_TIME: {

       // Map time value from potentiometer
       setDuration = map(analogRead(POT_PIN), 0, 1024, 0, 901);

       // Add selector braces: "Set time: <15:00>"
       lcdPrinter.printfval(setDuration, fsecs, ValueDecor::SELECTING,
-                             10, line::Time);
-
-      if(wpb.pressed())
-      {
+                          10, line::Time);
+
+      if(wpb.pressed()) {

         // Overwrite "<>" selector braces
         lcdPrinter.printfval(setDuration, fsecs, ValueDecor::DESELECTING,
-                               10, line::Time);
+                            10, line::Time);

         // Convert mapped time to calculatable millis after printing from seconds
         setDuration *= 1000;

         // Change the mode for next loop() call
-        currentModePtr = &setSpeedMode;
-
-        // Reset functor state so that another Mode's UI can be initialized once
-        changeUI.uninitialize();
+        currentMode = Mode::SETTING_SPEED;

       } // if(wpb.pressed())

@@ -412,14 +291,15 @@

     } // case Mode::SETTING_TIME

-    case Mode::ID::SETTING_SPEED: {
+
+    case Mode::SETTING_SPEED: {

       // Map motor speed to potentiometer
       motorSpeed = map(analogRead(POT_PIN), 0, 1024, 0, 255);

       // Add selector braces: "Set speed: <255>"
       lcdPrinter.printfval(motorSpeed, raw, ValueDecor::SELECTING,
-                             11, line::Speed);
+                          11, line::Speed);
       // Map printing of motorSpeed to rpm range?!

       if(wpb.pressed()) {
@@ -429,10 +309,17 @@

         // Overwrite "<>" selector braces
         lcdPrinter.printfval(motorSpeed, raw, ValueDecor::DESELECTING,
-                               11, line::Speed);
+                            11, line::Speed);

         // Change the mode for next loop() call
-        currentModePtr = &spinningMode;
+        currentMode = Mode::SPINNING;
+
+        // Initialize the UI for SPINNING Mode (note: only done once, this case
+        // becomes unreachable due to changing the Mode pointer!)
+        changeUI(UI::finishedIn, UI::pushStop);
+
+        // Turn on motor right before the countdown starts, for more accuracy
+        analogWrite(MOTOR_PIN, motorSpeed);

         // Start the counter from here, now that the mode is about to change
         spinningStartTime = millis();
@@ -444,10 +331,7 @@
     } // case Mode::SETTING_SPEED

-    case Mode::ID::SPINNING: {
-
-      // Change UI once
-      changeUI(currentModePtr);
+    case Mode::SPINNING: {

       // Truncate milliseconds (/1000) for more consistent displayed countdowns
       // Store in a variable so that it's calculated only once
@@ -463,11 +347,11 @@
         // Turn off motor
         digitalWrite(MOTOR_PIN, LOW);

-        // Allow static UIChanger to reinitialize once on next entry to this case
-        changeUI.uninitialize();
-
         // Change mode for next loop() call
-        currentModePtr = &setTimeMode;
+        currentMode = Mode::SETTING_TIME;
+
+        // Initialize the UI for SETTING_TIME Mode (note: only done once, here!)
+        changeUI(UI::setTime, UI::pushStart);

       } //if(wpb.pressed())

@@ -477,4 +361,16 @@

   } // switch(mode)

-} // void loop()+} // void loop()
+
+inline void changeUI(PGM_P timeStr, PGM_P instructionStr) {
+
+  // "Set time:" <=> "Finished in:"
+  lcd.setCursor(0, line::Time);
+  lcd_print_P(timeStr);
+
+  // "Push to Start!" <=> "Push to Stop!"
+  lcd.setCursor(0, line::Instructions);
+  lcd_print_P(instructionStr);
+
+} // void changeUI


Fixed megadrive buttons readings. All use pullup reads. Added comments

--- teensy_adapter_in_teensyduino/teensy_adapter_in_teensyduino.ino
+++ teensy_adapter_in_teensyduino/teensy_adapter_in_teensyduino.ino
@@ -80,6 +80,7 @@
   megadrive_read_button(MEGADRIVE_BUTTON_START, MEGADRIVE_DATA_START_C_PIN);
   megadrive_read_button(MEGADRIVE_BUTTON_A, MEGADRIVE_DATA_A_B_PIN);
   digitalWrite(MEGADRIVE_SELECT_PIN, HIGH);
+  //wait for next frame
   delay(16);
   delayMicroseconds(666);
 }
@@ -89,13 +90,13 @@
 }

 void megadrive_read_axis_y(int pin_index, int axis_value){
-  if(digitalRead(pin_index)){
+  if(!digitalRead(pin_index)){
     hat_direction = axis_value;
   }
 }

 void megadrive_read_axis_x(int pin_index, int direction){
-  if(digitalRead(pin_index)){
+  if(!digitalRead(pin_index)){

     if(hat_direction == HAT_DEFAULT){
       hat_direction = direction;
@@ -114,8 +115,6 @@

 void setup() {
   megadrive_read_buttons();
-
-  Serial.begin(9600);
 }

 void snes_read_button_raw(int button_index){
@@ -186,10 +185,9 @@
   snes_read_button(SNES_BUTTON_X);
   snes_read_button(SNES_BUTTON_L);
   snes_read_button(SNES_BUTTON_R);
-
+  //wait for next frame
   delay(16);
   delayMicroseconds(466);
-  Serial.println(hat_direction, DEC);
 }

 void loop() {


set events for deliveries to send to owner's device

--- sensors/letterbox/letterbox.ino
+++ sensors/letterbox/letterbox.ino
@@ -139,7 +139,6 @@
     if(ownerSmoothedVal > postmanSmoothedVal) {                                                  // owner opens letterbox
         Serial.println("\t => Owner opened letterbox");
         ownerOpened = true;
-        deliveries = 0;                                         // owner has checked letterbox
     } else {                                                                                // postman opens letterbox
         Serial.println("\t => Postman opened letterbox");
         postmanOpened = true;
@@ -150,7 +149,7 @@
       if(postmanOpened) {                                       // postman just closed
          if(proximitySmoothedVal > 10 + previousMail) {
             Serial.println("\t => NEW MAIL DELIVERED !!!");
-            deliveries++; // send notification to owner's device
+            deliveries++; // send notification to owner's device -  mails delivered
             postmanOpened = false;
             previousMail = proximitySmoothedVal;
          } else {
@@ -160,9 +159,9 @@
          }
       }
       if(ownerOpened){                                        // owner just closed
-
         ownerOpened = false;
         previousMail = proximitySmoothedVal;
+        deliveries = 0;   // send notification to owner's device -  mails cleared
         Serial.println("\t => mails cleared");
       }
     } else {


added the meta.json to m2m_commands_wiolte

--- wireless/quickstart/m2m_commands_wiolte/m2m_commands_wiolte.ino
+++ wireless/quickstart/m2m_commands_wiolte/m2m_commands_wiolte.ino
@@ -27,23 +27,24 @@
     return;
   }
   delay(3000);
-
+
   SerialUSB.println("### Setup completed");
   delay(3000);
-
-  /*
-    Send an SMS to short code 2936, which is a Twilio
-    Machine-to-Machine command.
-  */
-  SerialUSB.println("### Sending Command");

   /*
     Keep `command` under 160 ASCII characters, or 67 UCS-2 characters.
     https://www.twilio.com/docs/glossary/what-sms-character-limit
+  */
+  SerialUSB.println("### Sending Command");
+  char message[128] = "Hello from Wio LTE!";
+
+  /*
+    Send an SMS to short code 2936, which is a Twilio
+    Machine-to-Machine command.

     Write a Twilio M2M command.
   */
-  if (!Wio.SendSMS("2936", "Hello from the Wio LTE!")) {
+  if (!Wio.SendSMS("2936", message)) {
     SerialUSB.println("### ERROR! ###");
     return;
   }
@@ -54,21 +55,22 @@

 void loop() {
   while (true) {
-
+
     char str[100];
-
+
     /*
         Read a Twilio M2M command. Note that it will find the lowest
         indexed one with the code as is; in your code. if you cache the
         index you can start the next read_command to move to the next one.
     */
     int strLen = Wio.ReceiveSMS(str, sizeof (str));
-
+
     if (strLen < 0) {
       SerialUSB.println("### ERROR! ###");
       goto err;
     }
-
+
+    /* Receive command */
     if (strLen == 0) break;
     SerialUSB.println("### Command Received");


Added a preprocessor define statement for payload function

--- module/module_rfid.ino
+++ module/module_rfid.ino
@@ -98,13 +98,14 @@
 			j -= eightBits;
 		}
 	}
+	#ifdef FRONTDESK
 	else if (command == COMMAND_REGISTER) {
 		payload[0] = (id >> eightBits) & MSB;
 		payload[1] = id & MSB;
-		payload[2] = 0x00;
-		payload[3] = 0x00;
+		payload[2] = characterRead[1];
+		payload[3] = characterRead[2];
 	}
-
+	#endif

 }
 /*


Update Demo with writeback of values to SPAD.neXt

--- Scripts/serialdemo_spad.ino
+++ Scripts/serialdemo_spad.ino
@@ -1,6 +1,8 @@
 // *** SNDEMO ***

 // This example shows how to autoconnect between the SPAD.neXt and Arduino.
+//
+// SPAD.neXt >= 0.9.7.3 required
 //
 // It demonstrates how to
 // - Respond to a connection request from SPAD.neXt
@@ -11,7 +13,7 @@
 #include <CmdMessenger.h>  // CmdMessenger

 // Internal led
-const int ledPin = 13;
+const int ledPin = LED_BUILTIN;

 // Listen on serial connection for messages from the pc
 CmdMessenger messenger(Serial);
@@ -58,7 +60,7 @@
   if (strcmp(szRequest, "INIT") == 0) {
     messenger.sendCmdStart(kRequest);
     messenger.sendCmdArg("SPAD");
-    // Unique Device ID
+    // Unique Device ID: Change this!
     messenger.sendCmdArg(F("{DD7E3826-E439-4484-B186-A1443F3BC521}"));
     // Device Name for UI
     messenger.sendCmdArg("Arduino Demo");
@@ -96,6 +98,9 @@
     // tell SPAD.neXT we are done with config
     messenger.sendCmd(kRequest, "CONFIG");
     isReady = true;
+
+	// Make sure led is turned off and SPAD.neXt gets the value
+	setLED( LOW);
     return;
   }
 }
@@ -106,12 +111,17 @@
   int32_t newLed = messenger.readInt32Arg();
   if (newLed == 1)
   {
-    digitalWrite(ledPin, HIGH);
+    setLED( HIGH);
     messenger.sendCmd(kDebug, "LED COMMAND ON");
+
+	// For Demo Purpose:
+	// If we received a LED ON command, we set the heading in the sim to 180
+	messenger.sendCmd(kHeading,180);
+
   }
   else
   {
-    digitalWrite(ledPin, LOW);
+    setLED( LOW);
     messenger.sendCmd(kDebug, "LED COMMAND OFF");

     // When we turned it off we unsubscribe from the heading lock for demo purpose
@@ -129,22 +139,32 @@
   int32_t newHeading = messenger.readInt32Arg();
   if (newHeading > 180)
   {
-    digitalWrite(ledPin, HIGH);
+    setLED( HIGH);
     messenger.sendCmd(kDebug, "HEADING > 180");
   }
   else
   {
-    digitalWrite(ledPin, LOW);
+    setLED( LOW);
     messenger.sendCmd(kDebug, "HEADING < 180");
   }
 }

+// Update system LED and post state back to SPAD.neXt
+void setLED(int ledVal)
+{
+	digitalWrite(ledPin,ledVal);
+	// Update Led-Data on SPAD.neXt
+	if ((ledVal != lastLedState) && isReady)
+	{
+		lastLedState = ledVal;
+		messenger.sendCmd(kLed,ledVal);
+	}
+}
 // ------------------ M A I N  ----------------------

 // Setup function
 void setup()
 {
-  // Listen on serial connection for messages from the pc

   // 115200 is typically the maximum speed for serial over USB
   Serial.begin(115200);
@@ -155,8 +175,8 @@
   // initialize the digital pin as an output.
   pinMode(ledPin, OUTPUT);

-  // Make sure led is turned off after start or reset
-  digitalWrite(ledPin, LOW);
+  // Turn LED on, will be turned off when connection to SPAD.neXt it up and running
+  setLED( HIGH);
 }

 // Loop function
@@ -165,12 +185,4 @@
   // Process incoming serial data, and perform callbacks
   messenger.feedinSerialData();

-  // Update Led-Data on SPAD.neXt
-  int ledVal = digitalRead(ledPin);
-  if ((ledVal != lastLedState) && isReady)
-  {
-     lastLedState = ledVal;
-     messenger.sendCmd(kLed,ledVal);
-  }
-
 }


Local Aprs library included; Arduino version requirements

--- PortableAntennaRotorControl.ino
+++ PortableAntennaRotorControl.ino
@@ -1,10 +1,11 @@
 /*
  * This program is for Teensy 3
+ * Requires Arduino 1.6.8+ and compatible Teensyduino
  */

 #include <TimeLib.h>
 #include <EEPROM.h>
-
+#include "Aprs/Aprs.h"
 #define GPSLEN 100 //max length of GPS output to be sent
 #define APRSLEN 100 // max length of APRS message packet
 #define TERMLEN 100 // max length of terminal message


Update comments.

Update comments.

--- gps-transmitter/gps_transmitter_m0/gps_transmitter_m0.ino
+++ gps-transmitter/gps_transmitter_m0/gps_transmitter_m0.ino
@@ -38,6 +38,8 @@
 // Interrupt handler for SERCOM1
 void SERCOM1_Handler() {
   GPS.read();
+  //char c = GPS.read();
+  //if (c) Serial.print(c);
   Serial2.IrqHandler();
 }

@@ -64,7 +66,7 @@
   setXBeeChannelVerification(XBEE_VERIFY_CHANNEL);
 #endif

-  // GPS.begin() doesn't do anything on !__AVR__
+  // GPS.begin() doesn't quite work with the SERCOM stuff...
   Serial2.begin(9600);

   pinPeripheral(10, PIO_SERCOM);


fixed withoutLib_SS

fixed withoutLib_SS

- Now Compatible with ARDUINO UNO
- Clean and correct ordered response

--- examples/withoutLib_SS/withoutLib_SS.ino
+++ examples/withoutLib_SS/withoutLib_SS.ino
@@ -1,5 +1,5 @@
 /*
-  1 - THIS DOES NOT USES CS5490 LIBRARY, use only if you want to understand
+  1 - THIS EXAMPLE DOES NOT USES CS5490 LIBRARY, use only if you want to understand
 only if you are having some trouble or needs to understante how it works

   2 - THIS EXAMPLES USES SoftwareSerial library, it means that will be useful
@@ -8,35 +8,46 @@

 #include <SoftwareSerial.h>

-#define rx 13
+#define rx 11
 #define tx 12

 SoftwareSerial mySerial(rx, tx); // RX, TX
-int incbyte;
+byte data[3]; //data buffer

 void setup(){
   // Open serial communications and wait for port to open:
   Serial.begin(115200);
-  while (!Serial) {
-    ; // wait for serial port to connect. Needed for Leonardo only
-  }
+  // wait for serial port to connect. Needed for Leonardo only
+  while (!Serial);
   // set the data rate for the SoftwareSerial port
   mySerial.begin(600);
-  //Defines the page for the register address
-  mySerial.write(0b10010000); //Select Page 16
 }

 void loop(){
-  mySerial.write(0x31); //Read Address 49
-  while (mySerial.available() > 3){ //Wait for 3 bytes to arrive
-    char inByte = mySerial.read();
-    Serial.print(inByte,HEX);
-    inByte = mySerial.read();
-    Serial.print(inByte,HEX);
-    inByte = mySerial.read();
-    Serial.print(inByte,HEX);
-    //The result must be 9A991
-  }
-  Serial.println("");
+
+  /* The default value is: 01999A */
+  clearmySerialBuffer();
+  mySerial.write(0b10010000); //Select Page 16
+  mySerial.write(0b00110001); //Read Address 49
+	//Wait for 3 bytes to arrive
+	while(mySerial.available() < 3);
+  //Read 3 byte information
+	for(int i=0; i<3; i++){
+		data[i] = mySerial.read();
+	}
+  //Data concatenation
+  uint32_t value = 0;
+  value = value + data[2] << 8;
+  value = value + data[1] << 8;
+  value = value + data[0];
+
+  Serial.println(value,HEX);
   delay(1000);
 }
+
+/* Clearing mySerial Buffer */
+void clearmySerialBuffer(){
+  while(mySerial.available()){
+    mySerial.read();
+  }
+}


Messages fragmented into smaller messages

--- sensing/controller.ino
+++ sensing/controller.ino
@@ -23,7 +23,9 @@
 #include "TinyGPS++.h"
 #include "DHT.h"

-#define DEBUG true
+#define NODE_ID 1 //The network id of the present device
+
+#define DEBUG true // Set true to display debug messages on serial port
 #define SERIAL_BAUD_RATE 9600

 #define GPS_RX 2
@@ -38,8 +40,7 @@
 #define DHTPIN 7
 #define DHTTYPE DHT11

-//This is the network id of the present device
-#define NODE_ID 1
+

 #define request "?"
 #define clear_to_send ":"
@@ -49,7 +50,7 @@

 #define SEPARATOR ","

-#define DELAY_TIME 500
+#define DELAY_TIME 150

 #define PIN_LIGHT A1
 #define SD_CHIP_SELECT 10
@@ -59,12 +60,13 @@
 const char string_0[] PROGMEM = "log.txt";
 const char string_1[] PROGMEM = "node_id=";
 const char string_2[] PROGMEM = "&type=data";
-const char string_3[] PROGMEM = "&header=datetime,lat,lng,light,temperature,humidity,rain";
-const char string_4[] PROGMEM = "&load=";
+const char string_3[] PROGMEM = "&header=datetime,lat,lng,"; // This string is broken in 2 so the buffer is not very big
+const char string_4[] PROGMEM = "light,temperature,humidity,rain";
+const char string_5[] PROGMEM = "&load=";

 const char* const string_table[] PROGMEM = {string_0, string_1, string_2,
-                                            string_3, string_4
+                                            string_3, string_4, string_5
                                            };

 #if DEBUG
@@ -86,7 +88,7 @@
                                           };
 #endif

-char buffer[65];
+char buffer[40];

 //DateTime is in DDMMYYHHMMSSCC format
 TinyGPSPlus gps;
@@ -118,7 +120,6 @@
   Serial.begin(SERIAL_BAUD_RATE);
   wifiSerial.begin(WIFI_BAUD_RATE);
   gpsSerial.begin(GPS_BAUD_RATE);  // gps begins after wifi because it is used first
-  //dht.begin();

   pinMode(10, OUTPUT);

@@ -141,11 +142,7 @@
 #endif
 }

-void send_data() {
-#if DEBUG
-  print_debug(4);
-#endif
-
+void start_message() {
   //Create the message
   strcpy_P(buffer, (char*)pgm_read_word(&(string_table[1]))); //node_id=
   wifiSerial.print(String(buffer) + NODE_ID);
@@ -153,20 +150,43 @@
   strcpy_P(buffer, (char*)pgm_read_word(&(string_table[2]))); //&type=data
   wifiSerial.print(buffer);

-  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); //&header=datetime,lat,lng,light,temperature,humidity,rain
-  wifiSerial.print(buffer);
-
-  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[4]))); //&load=
-  wifiSerial.print(buffer);
+  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); //&header=datetime,lat,lng,
+  wifiSerial.print(buffer);
+
+  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[4]))); //light,temperature,humidity,rain
+  wifiSerial.print(buffer);
+
+  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[5]))); //&load=
+  wifiSerial.print(buffer);
+}
+
+void send_data() {
+#if DEBUG
+  print_debug(4);
+#endif

   //Send the whole file
   strcpy_P(buffer, (char*)pgm_read_word(&(string_table[0])));
   File data_file = SD.open(buffer);
-  char a;
+  String a;
+  int i = 0;
+
   while (data_file.available()) {
-    a = char(data_file.read());
-    wifiSerial.write(a);
-    delay(5);
+    start_message();
+    while (data_file.available()) {
+      i++;
+      a = data_file.readStringUntil('\n');
+      wifiSerial.print(a + "\n");
+      delay(5);
+      if (i > 20) {
+        wifiSerial.write(end_of_file);
+        start_message();
+      }
+    }
+    delay(DELAY_TIME);
+    if (!start_connection()){
+      break;
+    }
   }
   wifiSerial.write(end_of_file);
   data_file.close();
@@ -179,39 +199,42 @@
 #endif
 }

+bool start_connection() {
+  // Check for connection
+  wifiSerial.listen();
+  wifiSerial.print(request);
+  int index = 0;
+  while (!wifiSerial.available() || !wifiSerial.isListening()) {
+#if DEBUG
+    print_debug(3);
+#endif
+    index++;
+    if (index > wifi_timeout)
+      break;
+    delay(DELAY_TIME);
+  }
+
+  String wifiIn("");
+  while (wifiSerial.available()) {
+    delay(DELAY_TIME);
+    wifiIn += String(char (wifiSerial.read()));
+  }
+  return (wifiIn == clear_to_send);
+}
+
 void loop() {
-
   iteration++;
   if (iteration % wifi_freq == 0) {
-
 #if DEBUG
     print_debug(2); //Asking for WiFi
 #endif
-
-    // Check for connection
-    wifiSerial.listen();
-    wifiSerial.print(request);
-    int index = 0;
-    while (!wifiSerial.available() || !wifiSerial.isListening()) {
-      Serial.println("Waiting WiFi");
-      index++;
-      if (index > wifi_timeout)
-        break;
-      delay(100);
-    }
-
-    String wifiIn("");
-    while (wifiSerial.available()) {
-      delay(100);
-      wifiIn += String(char (wifiSerial.read()));
-    }
-    if (wifiIn == clear_to_send) {
+    if (start_connection()) {
       send_data();
-#if DEBUG
+#if DEBUG  // An ugly way to make code smaller
       print_debug(5); //WiFi available
     } else {
       print_debug(8); //WiFi not available
-#endif
+#endif
     }
   }

@@ -235,10 +258,9 @@
     print_debug(1); //GPS Found!
 #endif

-    String data_string = //String(iteration) + SEPARATOR +
-      String(gps.date.value()) + String(gps.time.value()) + SEPARATOR +
-      String(gps.location.lat(), 10) + SEPARATOR +
-      String(gps.location.lng(), 10) + SEPARATOR +
+    String data_string = String(gps.date.value()) + String(gps.time.value()) + SEPARATOR +
+      String(gps.location.lat(), 5) + SEPARATOR +
+      String(gps.location.lng(), 5) + SEPARATOR +
       String(analogRead(PIN_LIGHT)) + SEPARATOR +
       String(dht.readTemperature()) + SEPARATOR +
       String(dht.readHumidity()) + SEPARATOR +


cleaning up IFTTT LIFX demo and adding NeoPixels

--- examples/Hover_IFTTT_LIFX_Demo/Hover_IFTTT_LIFX_Demo.ino
+++ examples/Hover_IFTTT_LIFX_Demo/Hover_IFTTT_LIFX_Demo.ino
@@ -80,16 +80,34 @@

 #include "application.h"
 #include "Hover.h"
+#include "neopixel/neopixel.h"
+
+SYSTEM_MODE(AUTOMATIC);
+
+/* ======================= Neopixel declarations ======================== */
+
+// IMPORTANT: Set pixel COUNT, PIN and TYPE
+#define PIXEL_COUNT 24
+#define PIXEL_PIN D3
+#define PIXEL_TYPE WS2812B
+
+Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
+
+
+/* ======================= Hover declarations ======================== */

 // pin declarations for Hover
 int ts = 5;
 int res = 6;
+
+int delay1 = 40;
+int delay2 = 20;

 // Enable or disable different modes. Only gestures and taps are enabled by default.
 // Set the following four options to 0x01 if you want to capture every event. Note that the Serial console will be 'spammy'.
 #define GESTUREMODE 0x01    //0x00 = disable gestures, 0x01 = enable gestures
 #define TOUCHMODE 0x00      //0x00 = disable touch, 0x01 = enable touch
-#define TAPMODE 0x00        //0x00 = disable taps, 0x01 = enable taps, 0x02 = single taps only, 0x03 = double taps only
+#define TAPMODE 0x01        //0x00 = disable taps, 0x01 = enable taps, 0x02 = single taps only, 0x03 = double taps only
 #define POSITIONMODE 0x00   //0x00 = disable position tracking, 0x01 = enable position tracking

 // initialize Hover
@@ -104,47 +122,167 @@
   delay(2000);
   Serial.println("Initializing Hover...please wait.");
   hover.begin();
-}
-
-
+  strip.begin();
+  strip.show(); // Initialize all pixels to 'off'
+}

 void loop(void) {

-    // read incoming data stream from Hover
-    hover.readI2CData();
-
-    // retreive a gesture, touch, or position
-    Gesture g = hover.getGesture();
-
-    if (g.gestureID == 0x01){
-
-        // if swipe gesture was detected
-        switch (g.gestureValue) {
+  unsigned long currentMillis = millis();    // used for updating 3D position coordinates. Set to update every 150ms by default.
+
+  // read incoming data stream from Hover
+  hover.readI2CData();
+
+  // retreive a gesture, touch, or position
+  Gesture g = hover.getGesture();
+  Touch t = hover.getTouch();
+  Position p = hover.getPosition();
+
+  // print Gesture data
+  if ( g.gestureID != 0){
+    Serial.print("Event: "); Serial.print(g.gestureType); Serial.print("\t");
+    Serial.print("Gesture ID: "); Serial.print(g.gestureID,HEX); Serial.print("\t");
+    Serial.print("Value: "); Serial.print(g.gestureValue,HEX); Serial.println("");
+
+    if (g.gestureID == 1){
+        switch (g.gestureValue){
             case 0x01:
-                //swiped right
-                Particle.publish("lifx_action", "swipe_right", 60, PRIVATE);
-                Serial.println("swipe right");
-                break;
+              Particle.publish("lifxleft", "swipeleft");
+              waveLeft();
+              break;
             case 0x02:
-                //swiped left
-                Particle.publish("lifx_action", "swipe_left", 60, PRIVATE);
-                Serial.println("swipe left");
-                break;
+              Particle.publish("lifxright", "swiperight");
+              waveRight();
+              break;
             case 0x03:
-                //swiped up
-                Particle.publish("lifx_action", "swipe_up", 60, PRIVATE);
-                Serial.println("swipe up");
-                break;
+              Particle.publish("lifxup","swipeup");
+              waveDown();
+              break;
             case 0x04:
-                //swiped down
-                Particle.publish("lifx_action", "swipe_down", 60, PRIVATE);
-                Serial.println("swipe down");
-                break;
-            default:
-                break;
-        }
-
+              Particle.publish("lifxdown","swipedown");
+              waveUp();
+              break;
+            default:
+              break;
+        }
     }
-
-
+
+  }
+
+}
+
+
+
+
+
+void waveUp(){
+  int brightness = 135;
+
+  for(uint16_t i=4; i<12; i++) {
+
+    brightness = (i > 7) ? 255 : 135;
+
+    strip.setColorDimmed(i, 0, 0, 200, brightness);
+    strip.setColorDimmed(23-i, 0, 0, 200, brightness);
+
+    strip.show();
+    delay(delay1);
+  }
+
+  for(uint16_t i=4; i<12; i++) {
+    strip.setPixelColor(i, strip.Color(0, 0, 0));
+    strip.setPixelColor(23-i, strip.Color(0, 0, 0));
+
+    strip.show();
+    delay(delay2);
+  }
+
+}
+
+void waveDown(){
+  int brightness = 135;
+
+  for(uint16_t i=8; i>0; i--) {
+
+    brightness = (i < 4) ? 255 : 135;
+
+    strip.setColorDimmed(i, 0, 0, 200, brightness);
+    strip.setColorDimmed(23-i, 0, 0, 200, brightness);
+
+    strip.show();
+    delay(delay1);
+  }
+
+  for(uint16_t i=8; i>0; i--) {
+    strip.setPixelColor(i, strip.Color(0, 0, 0));
+    strip.setPixelColor(23-i, strip.Color(0, 0, 0));
+
+    strip.show();
+    delay(delay2);
+  }
+
+}
+
+
+
+void waveRight(){
+  int brightness = 135;
+
+  for(uint16_t i=0; i<6; i++) {
+
+    brightness = (i > 2) ? 255 : 135;
+
+    strip.setColorDimmed(12+i, 0, 0, 200, brightness);
+    strip.setColorDimmed(23-i, 0, 0, 200, brightness);
+
+    strip.show();
+    delay(delay1);
+  }
+
+  for(uint16_t i=0; i<6; i++) {
+    strip.setPixelColor(12+i, strip.Color(0, 0, 0));
+    strip.setPixelColor(23-i, strip.Color(0, 0, 0));
+
+    strip.show();
+    delay(delay2);
+  }
+
+}
+
+void waveLeft(){
+  int brightness = 135;
+
+  for(uint16_t i=0; i<6; i++) {
+
+    brightness = (i > 2) ? 255 : 135;
+
+    strip.setColorDimmed(12-i, 0, 0, 200, brightness);
+    strip.setColorDimmed(i, 0, 0, 200, brightness);
+
+    strip.show();
+    delay(delay1);
+  }
+
+  for(uint16_t i=0; i<6; i++) {
+    strip.setPixelColor(12-i, strip.Color(0, 0, 0));
+    strip.setPixelColor(i, strip.Color(0, 0, 0));
+
+    strip.show();
+    delay(delay2);
+  }
+
+}
+
+// Input a value 0 to 255 to get a color value.
+// The colours are a transition r - g - b - back to r.
+uint32_t Wheel(byte WheelPos) {
+  if(WheelPos < 85) {
+   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
+  } else if(WheelPos < 170) {
+   WheelPos -= 85;
+   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
+  } else {
+   WheelPos -= 170;
+   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
+  }
 }


Remove comment about BatteryMonitor_Central sketch

As it has been moved to the test folder for now.

--- libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino
+++ libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino
@@ -38,7 +38,6 @@
      This name will appear in advertising packets
      and can be used by remote devices to identify this BLE device
      The name can be changed but maybe be truncated based on space left in advertisement packet
-     If you want to make this work with the BatteryMonitor_Central sketch, do not modufy the name.
   */
   BLE.setLocalName("BatteryMonitor");
   BLE.setAdvertisedService(batteryService);  // add the service UUID


Ensure that the timer device is selected.

--- examples/attiny84/timer.ino
+++ examples/attiny84/timer.ino
@@ -19,8 +19,10 @@
 void loop(void)
 {
 	timer.enable();
-	devices.select();
-	digitalWrite(LED, !digitalRead(LED));
-	dt *= 2;
-	timer.delay(dt);
+	if (devices.select() == 1)
+	{
+		digitalWrite(LED, !digitalRead(LED));
+		dt *= 2;
+		timer.delay(dt);
+	}
 }


Small identation fix

Small identation fix

Just to make everything look perfect...