Version 3.2 - 29th April 2020

Version 3.2 - 29th April 2020

Fixes #34, fixes #33, fixes #32

--- ulisp.ino
+++ ulisp.ino
@@ -1,5 +1,5 @@
-/* uLisp AVR Version 3.0b - www.ulisp.com
-   David Johnson-Davies - www.technoblogy.com - 11th January 2020
+/* uLisp AVR Version 3.2 - www.ulisp.com
+   David Johnson-Davies - www.technoblogy.com - 29th April 2020

    Licensed under the MIT license: https://opensource.org/licenses/MIT
 */
@@ -12,10 +12,11 @@
 #define checkoverflow
 // #define resetautorun
 #define printfreespace
-#define serialmonitor
 // #define printgcs
 // #define sdcardsupport
 // #define lisplibrary
+// #define lineeditor
+// #define vt100

 // Includes

@@ -33,7 +34,7 @@
 #define SDSIZE 0
 #endif

-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
 #define PROGMEM
 #define PSTR(s) (s)
 #endif
@@ -71,7 +72,7 @@
 // Constants

 const int TRACEMAX = 3; // Number of traced functions
-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 type { ZZERO=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 };

@@ -87,7 +88,8 @@
 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 };
+ANALOGREAD, ANALOGWRITE, DELAY, MILLIS, SLEEP, NOTE, EDIT, PPRINT, PPRINTALL, FORMAT, REQUIRE,
+LISTLIBRARY, ENDFUNCTIONS };

 // Typedefs

@@ -104,6 +106,7 @@
       union {
         symbol_t name;
         int integer;
+        int chars; // For strings
       };
     };
   };
@@ -114,8 +117,7 @@
 typedef struct {
   const char *string;
   fn_ptr_type fptr;
-  uint8_t min;
-  uint8_t max;
+  uint8_t minmax;
 } tbl_entry_t;

 typedef int (*gfun_t)();
@@ -130,40 +132,41 @@
 #define BUFFERSIZE 18

 #if defined(__AVR_ATmega328P__)
-#define WORKSPACESIZE 315-SDSIZE        /* Objects (4*bytes) */
-#define EEPROMSIZE 1024                 /* Bytes */
-#define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
+  #define WORKSPACESIZE 315-SDSIZE        /* Objects (4*bytes) */
+  #define EEPROMSIZE 1024                 /* Bytes */
+  #define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
+  #define STACKDIFF 0

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

 #elif defined(__AVR_ATmega1284P__)
-#define WORKSPACESIZE 2816-SDSIZE       /* Objects (4*bytes) */
-#define EEPROMSIZE 4096                 /* Bytes */
-#define SYMBOLTABLESIZE 512             /* Bytes */
+  #define WORKSPACESIZE 2816-SDSIZE       /* Objects (4*bytes) */
+  #define EEPROMSIZE 4096                 /* Bytes */
+  #define SYMBOLTABLESIZE 512             /* Bytes */
+  #define STACKDIFF 320

 #elif defined(ARDUINO_AVR_NANO_EVERY)
-#define WORKSPACESIZE 1068-SDSIZE       /* Objects (4*bytes) */
-#define EEPROMSIZE 256                  /* Bytes */
-#define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
-
-#elif defined(ARDUINO_AVR_ATmega4809)   /* Curiosity Nano using MegaCoreX */
-#define Serial Serial3
-#define WORKSPACESIZE 1068-SDSIZE       /* Objects (4*bytes) */
-#define EEPROMSIZE 256                  /* Bytes */
-#define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
+  #define WORKSPACESIZE 1066-SDSIZE       /* Objects (4*bytes) */
+  #define EEPROMSIZE 256                  /* Bytes */
+  #define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
+  #define STACKDIFF 320
+
+#elif defined(ARDUINO_AVR_ATmega4809)     /* Curiosity Nano using MegaCoreX */
+  #define Serial Serial3
+  #define WORKSPACESIZE 1066-SDSIZE       /* Objects (4*bytes) */
+  #define EEPROMSIZE 256                  /* Bytes */
+  #define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
+  #define STACKDIFF 320

 #elif defined(__AVR_ATmega4809__)
-#define WORKSPACESIZE 1068-SDSIZE       /* Objects (4*bytes) */
-#define EEPROMSIZE 256                  /* Bytes */
-#define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
-
-#elif defined(__AVR_ATtiny3216__)
-#define WORKSPACESIZE 306-SDSIZE        /* Objects (4*bytes) */
-#define EEPROMSIZE 256                  /* Bytes */
-#define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
+  #define WORKSPACESIZE 1066-SDSIZE       /* Objects (4*bytes) */
+  #define EEPROMSIZE 256                  /* Bytes */
+  #define SYMBOLTABLESIZE BUFFERSIZE      /* Bytes - no long symbols */
+  #define STACKDIFF 320

 #endif

@@ -185,7 +188,8 @@
 object *GCStack = NULL;
 object *GlobalString;
 int GlobalStringIndex = 0;
-char BreakLevel = 0;
+uint8_t PrintCount = 0;
+uint8_t BreakLevel = 0;
 char LastChar = 0;
 char LastPrint = 0;

@@ -197,15 +201,56 @@
 object *tee;
 object *tf_progn (object *form, object *env);
 object *eval (object *form, object *env);
-object *read ();
+object *read (gfun_t gfun);
 void repl (object *env);
 void printobject (object *form, pfun_t pfun);
 char *lookupbuiltin (symbol_t name);
 intptr_t lookupfn (symbol_t name);
 int builtin (char* n);
 void pfstring (PGM_P s, pfun_t pfun);
-void error (symbol_t fname, PGM_P string, object *symbol);
-void error2 (symbol_t fname, PGM_P string);
+
+// Error handling
+
+void errorsub (symbol_t fname, PGM_P string) {
+  pfl(pserial); pfstring(PSTR("Error: "), pserial);
+  if (fname) {
+    pserial('\'');
+    pstring(symbolname(fname), pserial);
+    pfstring(PSTR("' "), pserial);
+  }
+  pfstring(string, pserial);
+}
+
+void error (symbol_t fname, PGM_P string, object *symbol) {
+  errorsub(fname, string);
+  pfstring(PSTR(": "), pserial); printobject(symbol, pserial);
+  pln(pserial);
+  GCStack = NULL;
+  longjmp(exception, 1);
+}
+
+void error2 (symbol_t fname, PGM_P string) {
+  errorsub(fname, string);
+  pln(pserial);
+  GCStack = NULL;
+  longjmp(exception, 1);
+}
+
+// Save space as these are used multiple times
+const char notanumber[] PROGMEM = "argument is not a number";
+const char notastring[] PROGMEM = "argument is not a string";
+const char notalist[] PROGMEM = "argument is not a list";
+const char notasymbol[] PROGMEM = "argument is not a symbol";
+const char notproper[] PROGMEM = "argument is not a proper list";
+const char toomanyargs[] PROGMEM = "too many arguments";
+const char toofewargs[] PROGMEM = "too few arguments";
+const char noargument[] PROGMEM = "missing argument";
+const char nostream[] PROGMEM = "missing stream argument";
+const char overflow[] PROGMEM = "arithmetic overflow";
+const char invalidarg[] PROGMEM = "invalid argument";
+const char invalidpin[] PROGMEM = "invalid pin";
+const char resultproper[] PROGMEM = "result is not a proper list";
+const char oddargs[] PROGMEM = "odd number of arguments";

 // Set up workspace

@@ -291,7 +336,7 @@
   unsigned int type = obj->type;
   mark(obj);

-  if (type >= PAIR || type == ZERO) { // cons
+  if (type >= PAIR || type == ZZERO) { // cons
     markobject(arg);
     obj = cdr(obj);
     goto MARK;
@@ -337,7 +382,7 @@
   for (int i=0; i<WORKSPACESIZE; i++) {
     object *obj = &Workspace[i];
     unsigned int type = (obj->type) & ~MARKBIT;
-    if (marked(obj) && (type >= STRING || type==ZERO)) {
+    if (marked(obj) && (type >= STRING || type==ZZERO)) {
       if (car(obj) == (object *)((uintptr_t)from | MARKBIT))
         car(obj) = (object *)((uintptr_t)to | MARKBIT);
       if (cdr(obj) == from) cdr(obj) = to;
@@ -355,8 +400,8 @@
     }
   }
 }
-
-int compactimage (object **arg) {
+
+uintptr_t compactimage (object **arg) {
   markobject(tee);
   markobject(GlobalEnv);
   markobject(GCStack);
@@ -416,7 +461,7 @@
     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 error(SAVEIMAGE, PSTR("illegal argument"), arg);
+  else error(SAVEIMAGE, invalidarg, arg);
   if (!file) error2(SAVEIMAGE, PSTR("problem saving to SD card"));
   SDWriteInt(file, (uintptr_t)arg);
   SDWriteInt(file, imagesize);
@@ -434,7 +479,7 @@
   file.close();
   return imagesize;
 #else
-  if (!(arg == NULL || listp(arg))) error(SAVEIMAGE, PSTR("illegal argument"), arg);
+  if (!(arg == NULL || listp(arg))) error(SAVEIMAGE, invalidarg, arg);
   int bytesneeded = imagesize*4 + SYMBOLTABLESIZE + 10;
   if (bytesneeded > EEPROMSIZE) error(SAVEIMAGE, PSTR("image size too large"), number(imagesize));
   unsigned int addr = 0;
@@ -473,7 +518,7 @@
   File file;
   if (stringp(arg)) file = SD.open(MakeFilename(arg));
   else if (arg == NULL) file = SD.open("/ULISP.IMG");
-  else error(LOADIMAGE, PSTR("illegal argument"), arg);
+  else error(LOADIMAGE, invalidarg, arg);
   if (!file) error2(LOADIMAGE, PSTR("problem loading from SD card"));
   SDReadInt(file);
   int imagesize = SDReadInt(file);
@@ -533,47 +578,9 @@
 #endif
 }

-// Error handling
-
-void errorsub (symbol_t fname, PGM_P string) {
-  pfl(pserial); pfstring(PSTR("Error: "), pserial);
-  if (fname) {
-    pserial('\'');
-    pstring(symbolname(fname), pserial);
-    pfstring(PSTR("' "), pserial);
-  }
-  pfstring(string, pserial);
-}
-
-void error (symbol_t fname, PGM_P string, object *symbol) {
-  errorsub(fname, string);
-  pfstring(PSTR(": "), pserial); printobject(symbol, pserial);
-  pln(pserial);
-  GCStack = NULL;
-  longjmp(exception, 1);
-}
-
-void error2 (symbol_t fname, PGM_P string) {
-  errorsub(fname, string);
-  pln(pserial);
-  GCStack = NULL;
-  longjmp(exception, 1);
-}
-
-// Save space as these are used multiple times
-const char notanumber[] PROGMEM = "argument is not a number";
-const char notastring[] PROGMEM = "argument is not a string";
-const char notalist[] PROGMEM = "argument is not a list";
-const char notproper[] PROGMEM = "argument is not a proper list";
-const char noargument[] PROGMEM = "missing argument";
-const char nostream[] PROGMEM = "missing stream argument";
-const char overflow[] PROGMEM = "arithmetic overflow";
-const char invalidpin[] PROGMEM = "invalid pin";
-const char resultproper[] PROGMEM = "result is not a proper list";
-
 // Tracing

-boolean tracing (symbol_t name) {
+bool tracing (symbol_t name) {
   int i = 0;
   while (i < TRACEMAX) {
     if (TraceFn[i] == name) return i+1;
@@ -603,33 +610,42 @@

 // Helper functions

-boolean consp (object *x) {
+bool consp (object *x) {
   if (x == NULL) return false;
   unsigned int type = x->type;
-  return type >= PAIR || type == ZERO;
-}
-
-boolean atom (object *x) {
+  return type >= PAIR || type == ZZERO;
+}
+
+bool atom (object *x) {
   if (x == NULL) return true;
   unsigned int type = x->type;
-  return type < PAIR && type != ZERO;
-}
-
-boolean listp (object *x) {
+  return type < PAIR && type != ZZERO;
+}
+
+bool listp (object *x) {
   if (x == NULL) return true;
   unsigned int type = x->type;
-  return type >= PAIR || type == ZERO;
-}
-
-boolean improperp (object *x) {
+  return type >= PAIR || type == ZZERO;
+}
+
+bool improperp (object *x) {
   if (x == NULL) return false;
   unsigned int type = x->type;
-  return type < PAIR && type != ZERO;
-}
+  return type < PAIR && type != ZZERO;
+}
+
+object *quote (object *arg) {
+  return cons(symbol(QUOTE), cons(arg,NULL));
+}
+
+// Radix 40 encoding
+
+#define MAXSYMBOL 64000

 int toradix40 (char ch) {
   if (ch == 0) return 0;
   if (ch >= '0' && ch <= '9') return ch-'0'+30;
+  if (ch == '$') return 27; if (ch == '*') return 28; if (ch == '-') return 29;
   ch = ch | 0x20;
   if (ch >= 'a' && ch <= 'z') return ch-'a'+1;
   return -1; // Invalid
@@ -637,6 +653,7 @@

 int fromradix40 (int n) {
   if (n >= 1 && n <= 26) return 'a'+n-1;
+  if (n == 27) return '$'; if (n == 28) return '*'; if (n == 29) return '-';
   if (n >= 30 && n <= 39) return '0'+n-30;
   return 0;
 }
@@ -645,8 +662,20 @@
   return (((toradix40(buffer[0]) * 40) + toradix40(buffer[1])) * 40 + toradix40(buffer[2]));
 }

-boolean valid40 (char *buffer) {
+bool valid40 (char *buffer) {
  return (toradix40(buffer[0]) >= 0 && toradix40(buffer[1]) >= 0 && toradix40(buffer[2]) >= 0);
+}
+
+char *symbolname (symbol_t x) {
+  if (x < ENDFUNCTIONS) return lookupbuiltin(x);
+  else if (x >= MAXSYMBOL) return lookupsymbol(x);
+  char *buffer = SymbolTop;
+  buffer[3] = '\0';
+  for (int n=2; n>=0; n--) {
+    buffer[n] = fromradix40(x % 40);
+    x = x / 40;
+  }
+  return buffer;
 }

 int digitvalue (char d) {
@@ -656,20 +685,8 @@
   return 16;
 }

-char *symbolname (symbol_t x) {
-  if (x < ENDFUNCTIONS) return lookupbuiltin(x);
-  else if (x >= 64000) return lookupsymbol(x);
-  char *buffer = SymbolTop;
-  buffer[3] = '\0';
-  for (int n=2; n>=0; n--) {
-    buffer[n] = fromradix40(x % 40);
-    x = x / 40;
-  }
-  return buffer;
-}
-
 int checkinteger (symbol_t name, object *obj) {
-  if (!integerp(obj)) error(name, PSTR("argument is not an integer"), obj);
+  if (!integerp(obj)) error(name, notanumber, obj);
   return obj->integer;
 }

@@ -690,8 +707,7 @@
 void checkargs (symbol_t name, object *args) {
   int nargs = listlength(name, args);
   if (name >= ENDFUNCTIONS) error(0, PSTR("not valid here"), symbol(name));
-  if (nargs<lookupmin(name)) error2(name, PSTR("has too few arguments"));
-  if (nargs>lookupmax(name)) error2(name, PSTR("has too many arguments"));
+  checkminmax(name, nargs);
 }

 int eq (object *arg1, object *arg2) {
@@ -745,8 +761,16 @@

 // String utilities

-void indent (int spaces, pfun_t pfun) {
-  for (int i=0; i<spaces; i++) pfun(' ');
+void indent (uint8_t spaces, char ch, pfun_t pfun) {
+  for (uint8_t i=0; i<spaces; i++) pfun(ch);
+}
+
+object *startstring (symbol_t name) {
+  object *string = myalloc();
+  string->type = STRING;
+  GlobalString = NULL;
+  GlobalStringIndex = 0;
+  return string;
 }

 void buildstring (char ch, int *chars, object **head) {
@@ -758,12 +782,12 @@
     object *cell = myalloc();
     if (*head == NULL) *head = cell; else tail->car = cell;
     cell->car = NULL;
-    cell->integer = *chars;
+    cell->chars = *chars;
     tail = cell;
   } else {
     shift = shift - 8;
     *chars = *chars | ch<<shift;
-    tail->integer = *chars;
+    tail->chars = *chars;
     if (shift == 0) *chars = 0;
   }
 }
@@ -788,7 +812,7 @@
   int length = 0;
   form = cdr(form);
   while (form != NULL) {
-    int chars = form->integer;
+    int chars = form->chars;
     for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
       if (chars>>i & 0xFF) length++;
     }
@@ -807,7 +831,22 @@
     arg = car(arg);
   }
   if (arg == NULL) return 0;
-  return (arg->integer)>>(n*8) & 0xFF;
+  return (arg->chars)>>(n*8) & 0xFF;
+}
+
+int gstr () {
+  if (LastChar) {
+    char temp = LastChar;
+    LastChar = 0;
+    return temp;
+  }
+  char c = nthchar(GlobalString, GlobalStringIndex++);
+  if (c != 0) return c;
+  return '\n'; // -1?
+}
+
+void pstr (char c) {
+  buildstring(c, &GlobalStringIndex, &GlobalString);
 }

 // Lookup variable in environment
@@ -819,6 +858,13 @@
     env = cdr(env);
   }
   return nil;
+}
+
+bool boundp (object *var, object *env) {
+  symbol_t varname = var->name;
+  if (value(varname, env) != NULL) return true;
+  if (value(varname, GlobalEnv) != NULL) return true;
+  return false;
 }

 object *findvalue (object *var, object *env) {
@@ -835,7 +881,7 @@
   int trace = 0;
   if (name) trace = tracing(name);
   if (trace) {
-    indent(TraceDepth[trace-1]<<1, pserial);
+    indent(TraceDepth[trace-1]<<1, ' ', pserial);
     pint(TraceDepth[trace-1]++, pserial);
     pserial(':'); pserial(' '); pserial('('); pstring(symbolname(name), pserial);
   }
@@ -855,7 +901,7 @@
     state = cdr(state);
   }
   // Add arguments to environment
-  boolean optional = false;
+  bool optional = false;
   while (params != NULL) {
     object *value;
     object *var = first(params);
@@ -878,7 +924,7 @@
         if (args == NULL) {
           if (optional) value = nil;
           else {
-            if (name) error2(name, PSTR("has too few arguments"));
+            if (name) error2(name, toofewargs);
             else error2(0, PSTR("function has too few arguments"));
           }
         } else { value = first(args); args = cdr(args); }
@@ -889,7 +935,7 @@
     params = cdr(params);
   }
   if (args != NULL) {
-    if (name) error2(name, PSTR("has too many arguments"));
+    if (name) error2(name, toomanyargs);
     else error2(0, PSTR("function has too many arguments"));
   }
   if (trace) { pserial(')'); pln(pserial); }
@@ -951,13 +997,13 @@
 // Checked car and cdr

 inline object *carx (object *arg) {
-  if (!listp(arg)) error(0, PSTR("Can't take car"), arg);
+  if (!listp(arg)) error(0, PSTR("can't take car"), arg);
   if (arg == nil) return nil;
   return car(arg);
 }

 inline object *cdrx (object *arg) {
-  if (!listp(arg)) error(0, PSTR("Can't take cdr"), arg);
+  if (!listp(arg)) error(0, PSTR("can't take cdr"), arg);
   if (arg == nil) return nil;
   return cdr(arg);
 }
@@ -978,7 +1024,7 @@
 uint8_t const TWI_SCL_PIN = 5;
 #endif

-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
 uint32_t const FREQUENCY = 400000L;  // Hardware I2C clock in Hz
 uint32_t const T_RISE = 300L;        // Rise time
 #else
@@ -993,12 +1039,12 @@
 #endif

 void I2Cinit(bool enablePullup) {
-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   if (enablePullup) {
     pinMode(PIN_WIRE_SDA, INPUT_PULLUP);
     pinMode(PIN_WIRE_SCL, INPUT_PULLUP);
   }
-  uint32_t baud = ((F_CPU_CORRECTED/FREQUENCY) - (((F_CPU_CORRECTED*T_RISE)/1000)/1000)/1000 - 10)/2;
+  uint32_t baud = ((F_CPU/FREQUENCY) - (((F_CPU*T_RISE)/1000)/1000)/1000 - 10)/2;
   TWI0.MBAUD = (uint8_t)baud;
   TWI0.MCTRLA = TWI_ENABLE_bm;                                        // Enable as master, no interrupts
   TWI0.MSTATUS = TWI_BUSSTATE_IDLE_gc;
@@ -1013,7 +1059,7 @@
 }

 int I2Cread() {
-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   if (I2CCount != 0) I2CCount--;
   while (!(TWI0.MSTATUS & TWI_RIF_bm));                               // Wait for read interrupt flag
   uint8_t data = TWI0.MDATA;
@@ -1030,7 +1076,7 @@
 }

 bool I2Cwrite(uint8_t data) {
-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   while (!(TWI0.MSTATUS & TWI_WIF_bm));                               // Wait for write interrupt flag
   TWI0.MDATA = data;
   TWI0.MCTRLB = TWI_MCMD_RECVTRANS_gc;                                // Do nothing
@@ -1044,7 +1090,7 @@
 }

 bool I2Cstart(uint8_t address, uint8_t read) {
-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   TWI0.MADDR = address<<1 | read;                                     // Send START condition
   while (!(TWI0.MSTATUS & (TWI_WIF_bm | TWI_RIF_bm)));                // Wait for write or read interrupt flag
   if ((TWI0.MSTATUS & TWI_ARBLOST_bm)) return false;                  // Return false if arbitration lost or bus error
@@ -1067,7 +1113,7 @@
 }

 void I2Cstop (uint8_t read) {
-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   (void) read;
   TWI0.MCTRLB = TWI_ACKACT_bm | TWI_MCMD_STOP_gc;                     // Send STOP
 #else
@@ -1200,13 +1246,13 @@
   if (!(pin>=0 && pin<=15)) error(ANALOGREAD, invalidpin, number(pin));
 #elif defined(__AVR_ATmega1284P__)
   if (!(pin>=0 && pin<=7)) error(ANALOGREAD, invalidpin, number(pin));
+#elif defined(ARDUINO_AVR_NANO_EVERY)
+  if (!((pin>=14 && pin<=21))) error(ANALOGREAD, invalidpin, number(pin));
 #elif defined(ARDUINO_AVR_ATmega4809)  /* MegaCoreX core */
   if (!((pin>=22 && pin<=33) || (pin>=36 && pin<=39))) error(ANALOGREAD, invalidpin, number(pin));
 #elif defined(__AVR_ATmega4809__)
   if (!(pin>=14 && pin<=21)) error(ANALOGREAD, invalidpin, number(pin));
-#elif defined(__AVR_ATtiny3216__)
-  if (!((pin>=0 && pin<=5) || pin==8 || pin==9 || (pin>=14 && pin<=17))) error(ANALOGREAD, invalidpin, number(pin));
-#endif
+  #endif
 }

 void checkanalogwrite (int pin) {
@@ -1216,18 +1262,18 @@
   if (!((pin>=2 && pin<=13) || (pin>=44 && pin <=46))) error(ANALOGWRITE, invalidpin, number(pin));
 #elif defined(__AVR_ATmega1284P__)
   if (!(pin==3 || pin==4 || pin==6 || pin==7 || (pin>=12 && pin<=15))) error(ANALOGWRITE, invalidpin, number(pin));
+#elif defined(ARDUINO_AVR_NANO_EVERY)
+  if (!(pin==3 || (pin>=5 && pin<=6) || (pin>=9 && pin<=11))) error(ANALOGWRITE, invalidpin, number(pin));
 #elif defined(ARDUINO_AVR_ATmega4809)  /* MegaCoreX core */
   if (!((pin>=16 && pin<=19) || (pin>=38 && pin<=39))) error(ANALOGWRITE, invalidpin, number(pin));
 #elif defined(__AVR_ATmega4809__)
   if (!(pin==3 || pin==5 || pin==6 || pin==9 || pin==10)) error(ANALOGWRITE, invalidpin, number(pin));
-#elif defined(__AVR_ATtiny3216__)
-  if (!((pin>=0 && pin<=2) || (pin>=7 && pin<=11) || pin==16)) error(ANALOGWRITE, invalidpin, number(pin));
 #endif
 }

 // Note

-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
 const int scale[] PROGMEM = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902};
 #else
 const uint8_t scale[] PROGMEM = {239,226,213,201,190,179,169,160,151,142,134,127};
@@ -1241,7 +1287,7 @@
   } else if (pin == 11) {
     DDRB = DDRB | 1<<DDB3; // PB3 (Arduino D11) as output
     TCCR2A = 1<<COM2A0 | 0<<COM2B0 | 2<<WGM20; // Toggle OC2A on match
-  } else error(NOTE, PSTR("invalid pin"), number(pin));
+  } else error(NOTE, invalidpin, number(pin));
   int prescaler = 9 - octave - note/12;
   if (prescaler<3 || prescaler>6) error(NOTE, PSTR("octave out of range"), number(prescaler));
   OCR2A = pgm_read_byte(&scale[note%12]) - 1;
@@ -1254,7 +1300,7 @@
   } else if (pin == 10) {
     DDRB = DDRB | 1<<DDB4; // PB4 (Arduino D10) as output
     TCCR2A = 1<<COM2A0 | 0<<COM2B0 | 2<<WGM20; // Toggle OC2A on match
-  } else error(NOTE, PSTR("invalid pin"), number(pin));
+  } else error(NOTE, invalidpin, number(pin));
   int prescaler = 9 - octave - note/12;
   if (prescaler<3 || prescaler>6) error(NOTE, PSTR("octave out of range"), number(prescaler));
   OCR2A = pgm_read_byte(&scale[note%12]) - 1;
@@ -1267,13 +1313,13 @@
   } else if (pin == 15) {
     DDRD = DDRD | 1<<DDD7; // PD7 (Arduino D15) as output
     TCCR2A = 1<<COM2A0 | 0<<COM2B0 | 2<<WGM20; // Toggle OC2A on match
-  } else error(NOTE, PSTR("invalid pin"), number(pin));
+  } else error(NOTE, invalidpin, number(pin));
   int prescaler = 9 - octave - note/12;
   if (prescaler<3 || prescaler>6) error(NOTE, PSTR("octave out of range"), number(prescaler));
   OCR2A = pgm_read_byte(&scale[note%12]) - 1;
   TCCR2B = 0<<WGM22 | prescaler<<CS20;

-#elif defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#elif defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   int prescaler = 8 - octave - note/12;
   if (prescaler<0 || prescaler>8) error(NOTE, PSTR("octave out of range"), number(prescaler));
   tone(pin, scale[note%12]>>prescaler);
@@ -1281,7 +1327,7 @@
 }

 void nonote (int pin) {
-#if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+#if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   noTone(pin);
 #else
   (void) pin;
@@ -1291,7 +1337,7 @@

 // Sleep

-#if !defined(__AVR_ATmega4809__) && !defined(__AVR_ATtiny3216__)
+#if !defined(__AVR_ATmega4809__) && !defined(ARDUINO_AVR_ATmega4809)
   // Interrupt vector for sleep watchdog
   ISR(WDT_vect) {
   WDTCSR |= 1<<WDIE;
@@ -1303,7 +1349,7 @@
 }

 void sleep (int secs) {
-#if !defined(__AVR_ATmega4809__) && !defined(__AVR_ATtiny3216__)
+#if !defined(__AVR_ATmega4809__) && !defined(ARDUINO_AVR_ATmega4809)
   // Set up Watchdog timer for 1 Hz interrupt
   WDTCSR = 1<<WDCE | 1<<WDE;
   WDTCSR = 1<<WDIE | 6<<WDP0;     // 1 sec interrupt
@@ -1334,6 +1380,87 @@
 #endif
 }

+// Prettyprint
+
+const int PPINDENT = 2;
+const int PPWIDTH = 80;
+
+void pcount (char c) {
+  if (c == '\n') PrintCount++;
+  PrintCount++;
+}
+
+uint8_t atomwidth (object *obj) {
+  PrintCount = 0;
+  printobject(obj, pcount);
+  return PrintCount;
+}
+
+uint8_t hexwidth (object *obj) {
+  PrintCount = 0;
+  pinthex(obj->integer, pcount);
+  return PrintCount;
+}
+
+boolean quoted (object *obj) {
+  return (consp(obj) && car(obj) != NULL && car(obj)->name == QUOTE && consp(cdr(obj)) && cddr(obj) == NULL);
+}
+
+int subwidth (object *obj, int w) {
+  if (atom(obj)) return w - atomwidth(obj);
+  if (quoted(obj)) return subwidthlist(car(cdr(obj)), w - 1);
+  return subwidthlist(obj, w - 1);
+}
+
+int subwidthlist (object *form, int w) {
+  while (form != NULL && w >= 0) {
+    if (atom(form)) return w - (2 + atomwidth(form));
+    w = subwidth(car(form), w - 1);
+    form = cdr(form);
+  }
+  return w;
+}
+
+void superprint (object *form, int lm, pfun_t pfun) {
+  if (atom(form)) {
+    if (symbolp(form) && form->name == NOTHING) pstring(symbolname(form->name), pfun);
+    else printobject(form, pfun);
+  }
+  else if (quoted(form)) { pfun('\''); superprint(car(cdr(form)), lm + 1, pfun); }
+  else if (subwidth(form, PPWIDTH - lm) >= 0) supersub(form, lm + PPINDENT, 0, pfun);
+  else supersub(form, lm + PPINDENT, 1, pfun);
+}
+
+const int ppspecials = 15;
+const char ppspecial[ppspecials] PROGMEM =
+  { DOTIMES, DOLIST, IF, SETQ, TEE, LET, LETSTAR, LAMBDA, WHEN, UNLESS, WITHI2C, WITHSERIAL, WITHSPI, WITHSDCARD, FORMILLIS };
+
+void supersub (object *form, int lm, int super, pfun_t pfun) {
+  int special = 0, separate = 1;
+  object *arg = car(form);
+  if (symbolp(arg)) {
+    int name = arg->name;
+    if (name == DEFUN) special = 2;
+    else for (int i=0; i<ppspecials; i++) {
+      #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
+      if (name == ppspecial[i]) { special = 1; break; }
+      #else
+      if (name == pgm_read_byte(&ppspecial[i])) { special = 1; break; }
+      #endif
+    }
+  }
+  while (form != NULL) {
+    if (atom(form)) { pfstring(PSTR(" . "), pfun); printobject(form, pfun); pfun(')'); return; }
+    else if (separate) { pfun('('); separate = 0; }
+    else if (special) { pfun(' '); special--; }
+    else if (!super) pfun(' ');
+    else { pln(pfun); indent(lm, ' ', pfun); }
+    superprint(car(form), lm, pfun);
+    form = cdr(form);
+  }
+  pfun(')'); return;
+}
+
 // Special forms

 object *sp_quote (object *args, object *env) {
@@ -1346,30 +1473,31 @@
   (void) env;
   checkargs(DEFUN, args);
   object *var = first(args);
-  if (var->type != SYMBOL) error(DEFUN, PSTR("not a symbol"), var);
+  if (!symbolp(var)) error(DEFUN, notasymbol, var);
   object *val = cons(symbol(LAMBDA), cdr(args));
   object *pair = value(var->name,GlobalEnv);
-  if (pair != NULL) { cdr(pair) = val; return var; }
-  push(cons(var, val), GlobalEnv);
+  if (pair != NULL) cdr(pair) = val;
+  else push(cons(var, val), GlobalEnv);
   return var;
 }

 object *sp_defvar (object *args, object *env) {
   checkargs(DEFVAR, args);
   object *var = first(args);
-  if (var->type != SYMBOL) error(DEFVAR, PSTR("not a symbol"), var);
+  if (!symbolp(var)) error(DEFVAR, notasymbol, var);
   object *val = NULL;
-  val = eval(second(args), env);
+  args = cdr(args);
+  if (args != NULL) { setflag(NOESC); val = eval(first(args), env); clrflag(NOESC); }
   object *pair = value(var->name, GlobalEnv);
-  if (pair != NULL) { cdr(pair) = val; return var; }
-  push(cons(var, val), GlobalEnv);
+  if (pair != NULL) cdr(pair) = val;
+  else push(cons(var, val), GlobalEnv);
   return var;
 }

 object *sp_setq (object *args, object *env) {
   object *arg = nil;
   while (args != NULL) {
-    if (cdr(args) == NULL) error2(SETQ, PSTR("odd number of parameters"));
+    if (cdr(args) == NULL) error2(SETQ, oddargs);
     object *pair = findvalue(first(args), env);
     arg = eval(second(args), env);
     cdr(pair) = arg;
@@ -1415,7 +1543,7 @@
   return result;
 }

-// Special forms incf/decf
+// Accessors

 object *sp_incf (object *args, object *env) {
   checkargs(INCF, args);
@@ -1452,7 +1580,7 @@
 object *sp_setf (object *args, object *env) {
   object *arg = nil;
   while (args != NULL) {
-    if (cdr(args) == NULL) error2(SETF, PSTR("odd number of parameters"));
+    if (cdr(args) == NULL) error2(SETF, oddargs);
     object **loc = place(SETF, first(args), env);
     arg = eval(second(args), env);
     *loc = arg;
@@ -1460,6 +1588,8 @@
   }
   return arg;
 }
+
+// Other special forms

 object *sp_dolist (object *args, object *env) {
   if (args == NULL) error2(DOLIST, noargument);
@@ -1701,7 +1831,7 @@
     object *test = eval(first(clause), env);
     object *forms = cdr(clause);
     if (test != nil) {
-      if (forms == NULL) return test; else return tf_progn(forms, env);
+      if (forms == NULL) return quote(test); else return tf_progn(forms, env);
     }
     args = cdr(args);
   }
@@ -2365,8 +2495,8 @@
   while ((arg1 != NULL) || (arg2 != NULL)) {
     if (arg1 == NULL) return lt;
     if (arg2 == NULL) return gt;
-    if (arg1->integer < arg2->integer) return lt;
-    if (arg1->integer > arg2->integer) return gt;
+    if (arg1->chars < arg2->chars) return lt;
+    if (arg1->chars > arg2->chars) return gt;
     arg1 = car(arg1);
     arg2 = car(arg2);
   }
@@ -2426,7 +2556,7 @@
     object *cell = myalloc();
     cell->car = NULL;
     uint8_t shift = (sizeof(int)-1)*8;
-    cell->integer = (arg->integer)<<shift;
+    cell->chars = (arg->chars)<<shift;
     obj->cdr = cell;
   } else if (type == SYMBOL) {
     char *s = symbolname(arg->name);
@@ -2455,10 +2585,10 @@
   int chars = 0;
   while (args != NULL) {
     object *obj = first(args);
-    if (obj->type != STRING) error(CONCATENATE, notastring, obj);
+    if (!stringp(obj)) error(CONCATENATE, notastring, obj);
     obj = cdr(obj);
     while (obj != NULL) {
-      int quad = obj->integer;
+      int quad = obj->chars;
       while (quad != 0) {
          char ch = quad>>((sizeof(int)-1)*8) & 0xFF;
          buildstring(ch, &chars, &head);
@@ -2493,16 +2623,6 @@
   return result;
 }

-int gstr () {
-  if (LastChar) {
-    char temp = LastChar;
-    LastChar = 0;
-    return temp;
-  }
-  char c = nthchar(GlobalString, GlobalStringIndex++);
-  return (c != 0) ? c : '\n'; // -1?
-}
-
 object *fn_readfromstring (object *args, object *env) {
   (void) env;
   object *arg = first(args);
@@ -2512,21 +2632,11 @@
   return read(gstr);
 }

-void pstr (char c) {
-  buildstring(c, &GlobalStringIndex, &GlobalString);
-}
-
 object *fn_princtostring (object *args, object *env) {
   (void) env;
   object *arg = first(args);
-  object *obj = myalloc();
-  obj->type = STRING;
-  GlobalString = NULL;
-  GlobalStringIndex = 0;
-  char temp = Flags;
-  clrflag(PRINTREADABLY);
-  printobject(arg, pstr);
-  Flags = temp;
+  object *obj = startstring(PRINCTOSTRING);
+  prin1object(arg, pstr);
   obj->cdr = GlobalString;
   return obj;
 }
@@ -2534,10 +2644,7 @@
 object *fn_prin1tostring (object *args, object *env) {
   (void) env;
   object *arg = first(args);
-  object *obj = myalloc();
-  obj->type = STRING;
-  GlobalString = NULL;
-  GlobalStringIndex = 0;
+  object *obj = startstring(PRIN1TOSTRING);
   printobject(arg, pstr);
   obj->cdr = GlobalString;
   return obj;
@@ -2615,9 +2722,10 @@

 object *fn_makunbound (object *args, object *env) {
   (void) env;
-  object *key = first(args);
-  delassoc(key, &GlobalEnv);
-  return key;
+  object *var = first(args);
+  if (!symbolp(var)) error(MAKUNBOUND, notasymbol, var);
+  delassoc(var, &GlobalEnv);
+  return var;
 }

 object *fn_break (object *args, object *env) {
@@ -2649,7 +2757,7 @@
   pfun_t pfun = pstreamfun(cdr(args));
   pln(pfun);
   printobject(obj, pfun);
-  (pfun)(' ');
+  pfun(' ');
   return obj;
 }

@@ -2657,10 +2765,7 @@
   (void) env;
   object *obj = first(args);
   pfun_t pfun = pstreamfun(cdr(args));
-  char temp = Flags;
-  clrflag(PRINTREADABLY);
-  printobject(obj, pfun);
-  Flags = temp;
+  prin1object(obj, pfun);
   return obj;
 }

@@ -2878,80 +2983,6 @@

 // Pretty printer

-const int PPINDENT = 2;
-const int PPWIDTH = 80;
-
-void pcount (char c) {
-  LastPrint = c;
-  if (c == '\n') GlobalStringIndex++;
-  GlobalStringIndex++;
-}
-
-int atomwidth (object *obj) {
-  GlobalStringIndex = 0;
-  printobject(obj, pcount);
-  return GlobalStringIndex;
-}
-
-boolean quoted (object *obj) {
-  return (consp(obj) && car(obj) != NULL && car(obj)->name == QUOTE && consp(cdr(obj)) && cddr(obj) == NULL);
-}
-
-int subwidth (object *obj, int w) {
-  if (atom(obj)) return w - atomwidth(obj);
-  if (quoted(obj)) return subwidthlist(car(cdr(obj)), w - 1);
-  return subwidthlist(obj, w - 1);
-}
-
-int subwidthlist (object *form, int w) {
-  while (form != NULL && w >= 0) {
-    if (atom(form)) return w - (2 + atomwidth(form));
-    w = subwidth(car(form), w - 1);
-    form = cdr(form);
-  }
-  return w;
-}
-
-void superprint (object *form, int lm, pfun_t pfun) {
-  if (atom(form)) {
-    if (symbolp(form) && form->name == NOTHING) pstring(symbolname(form->name), pfun);
-    else printobject(form, pfun);
-  }
-  else if (quoted(form)) { pfun('\''); superprint(car(cdr(form)), lm + 1, pfun); }
-  else if (subwidth(form, PPWIDTH - lm) >= 0) supersub(form, lm + PPINDENT, 0, pfun);
-  else supersub(form, lm + PPINDENT, 1, pfun);
-}
-
-const int ppspecials = 15;
-const char ppspecial[ppspecials] PROGMEM =
-  { DOTIMES, DOLIST, IF, SETQ, TEE, LET, LETSTAR, LAMBDA, WHEN, UNLESS, WITHI2C, WITHSERIAL, WITHSPI, WITHSDCARD, FORMILLIS };
-
-void supersub (object *form, int lm, int super, pfun_t pfun) {
-  int special = 0, separate = 1;
-  object *arg = car(form);
-  if (symbolp(arg)) {
-    int name = arg->name;
-    if (name == DEFUN) special = 2;
-    else for (int i=0; i<ppspecials; i++) {
-      #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
-      if (name == ppspecial[i]) { special = 1; break; }
-      #else
-      if (name == pgm_read_byte(&ppspecial[i])) { special = 1; break; }
-      #endif
-    }
-  }
-  while (form != NULL) {
-    if (atom(form)) { pfstring(PSTR(" . "), pfun); printobject(form, pfun); pfun(')'); return; }
-    else if (separate) { pfun('('); separate = 0; }
-    else if (special) { pfun(' '); special--; }
-    else if (!super) pfun(' ');
-    else { pln(pfun); indent(lm, pfun); }
-    superprint(car(form), lm, pfun);
-    form = cdr(form);
-  }
-  pfun(')'); return;
-}
-
 object *fn_pprint (object *args, object *env) {
   (void) env;
   object *obj = first(args);
@@ -2962,32 +2993,108 @@
 }

 object *fn_pprintall (object *args, object *env) {
-  (void) args, (void) env;
+  (void) env;
+  pfun_t pfun = pstreamfun(args);
   object *globals = GlobalEnv;
   while (globals != NULL) {
     object *pair = first(globals);
     object *var = car(pair);
     object *val = cdr(pair);
-    pln(pserial);
+    pln(pfun);
     if (consp(val) && symbolp(car(val)) && car(val)->name == LAMBDA) {
-      superprint(cons(symbol(DEFUN), cons(var, cdr(val))), 0, pserial);
+      superprint(cons(symbol(DEFUN), cons(var, cdr(val))), 0, pfun);
     } else {
-      superprint(cons(symbol(DEFVAR),cons(var,cons(cons(symbol(QUOTE),cons(val,NULL))
-      ,NULL))), 0, pserial);
-    }
-    pln(pserial);
+      superprint(cons(symbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pserial);
+    }
+    pln(pfun);
+    testescape();
     globals = cdr(globals);
   }
   return symbol(NOTHING);
 }

+// Format
+
+void formaterr (object *formatstr, PGM_P string, uint8_t p) {
+  pln(pserial); indent(4, ' ', pserial); printstring(formatstr, pserial); pln(pserial);
+  indent(p+5, ' ', pserial); pserial('^');
+  errorsub(FORMAT, string);
+  pln(pserial);
+  GCStack = NULL;
+  longjmp(exception, 1);
+}
+
+object *fn_format (object *args, object *env) {
+  (void) env;
+  pfun_t pfun = pserial;
+  object *output = first(args);
+  object *obj;
+  if (output == nil) { obj = startstring(FORMAT); pfun = pstr; }
+  else if (output != tee) pfun = pstreamfun(args);
+  object *formatstr = second(args);
+  if (!stringp(formatstr)) error(FORMAT, notastring, formatstr);
+  object *save = NULL;
+  args = cddr(args);
+  int len = stringlength(formatstr);
+  uint8_t n = 0, width = 0, w, bra = 0;
+  char pad = ' ';
+  bool tilde = false, comma, quote;
+  while (n < len) {
+    char ch = nthchar(formatstr, n);
+    char ch2 = ch & ~0x20; // force to upper case
+    if (tilde) {
+      if (comma && quote) { pad = ch; comma = false, quote = false; }
+      else if (ch == '\'') {
+        if (comma) quote = true;
+        else formaterr(formatstr, PSTR("quote not valid"), n);
+      }
+      else if (ch == '~') { pfun('~'); tilde = false; }
+      else if (ch >= '0' && ch <= '9') width = width*10 + ch - '0';
+      else if (ch == ',') comma = true;
+      else if (ch == '%') { pln(pfun); tilde = false; }
+      else if (ch == '&') { pfl(pfun); tilde = false; }
+      else if (ch == '{') {
+        if (save != NULL) formaterr(formatstr, PSTR("can't nest ~{"), n);
+        if (args == NULL) formaterr(formatstr, noargument, n);
+        if (!listp(first(args))) formaterr(formatstr, notalist, n);
+        save = args; args = first(args); bra = n; tilde = false;
+      }
+      else if (ch == '}') {
+        if (save == NULL) formaterr(formatstr, PSTR("no matching ~{"), n);
+        if (args == NULL) { args = cdr(save); save = NULL; } else n = bra;
+        tilde = false;
+      }
+      else if (ch2 == 'A' || ch2 == 'S' || ch2 == 'D' || ch2 == 'G' || ch2 == 'X') {
+        if (args == NULL) formaterr(formatstr, noargument, n);
+        object *arg = first(args); args = cdr(args);
+        uint8_t aw = atomwidth(arg);
+        if (width < aw) w = 0; else w = width-aw;
+        tilde = false;
+        if (ch2 == 'A') { prin1object(arg, pfun); indent(w, pad, pfun); }
+        else if (ch2 == 'S') { printobject(arg, pfun); indent(w, pad, pfun); }
+        else if (ch2 == 'D' || ch2 == 'G') { indent(w, pad, pfun); prin1object(arg, pfun); }
+        else if (ch2 == 'X' && integerp(arg)) {
+          uint8_t hw = hexwidth(arg); if (width < hw) w = 0; else w = width-hw;
+          indent(w, pad, pfun); pinthex(arg->integer, pfun);
+        } else if (ch2 == 'X') { indent(w, pad, pfun); prin1object(arg, pfun); }
+        tilde = false;
+      } else formaterr(formatstr, PSTR("invalid directive"), n);
+    } else {
+      if (ch == '~') { tilde = true; pad = ' '; width = 0; comma = false; quote = false; }
+      else pfun(ch);
+    }
+    n++;
+  }
+  if (output == nil) { obj->cdr = GlobalString; return obj; }
+  else return nil;
+}
+
 // LispLibrary

 object *fn_require (object *args, object *env) {
-#if !defined(__AVR_ATtiny3216__)
   object *arg = first(args);
   object *globals = GlobalEnv;
-  if (!symbolp(arg)) error(REQUIRE, PSTR("argument is not a symbol"), arg);
+  if (!symbolp(arg)) error(REQUIRE, notasymbol, arg);
   while (globals != NULL) {
     object *pair = first(globals);
     object *var = car(pair);
@@ -3005,10 +3112,7 @@
     }
     line = read(glibrary);
   }
-  return nil;
-#else
-  error2(REQUIRE, PSTR("not supported"));
-#endif
+  return nil;
 }

 object *fn_listlibrary (object *args, object *env) {
@@ -3038,7 +3142,7 @@
 const char string6[] PROGMEM = "let";
 const char string7[] PROGMEM = "let*";
 const char string8[] PROGMEM = "closure";
-const char string9[] PROGMEM = "special_forms";
+const char string9[] PROGMEM = "";
 const char string10[] PROGMEM = "quote";
 const char string11[] PROGMEM = "defun";
 const char string12[] PROGMEM = "defvar";
@@ -3059,7 +3163,7 @@
 const char string27[] PROGMEM = "with-i2c";
 const char string28[] PROGMEM = "with-spi";
 const char string29[] PROGMEM = "with-sd-card";
-const char string30[] PROGMEM = "tail_forms";
+const char string30[] PROGMEM = "";
 const char string31[] PROGMEM = "progn";
 const char string32[] PROGMEM = "if";
 const char string33[] PROGMEM = "cond";
@@ -3068,7 +3172,7 @@
 const char string36[] PROGMEM = "case";
 const char string37[] PROGMEM = "and";
 const char string38[] PROGMEM = "or";
-const char string39[] PROGMEM = "functions";
+const char string39[] PROGMEM = "";
 const char string40[] PROGMEM = "not";
 const char string41[] PROGMEM = "null";
 const char string42[] PROGMEM = "cons";
@@ -3187,170 +3291,173 @@
 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 char string158[] PROGMEM = "format";
+const char string159[] PROGMEM = "require";
+const char string160[] PROGMEM = "list-library";
+
+// Third parameter is no. of arguments; 1st hex digit is min, 2nd hex digit is max, 0xF is unlimited
 const tbl_entry_t lookup_table[] PROGMEM = {
-  { string0, NULL, 0, 0 },
-  { string1, NULL, 0, 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 },
-  { string8, NULL, 0, 127 },
-  { string9, NULL, NIL, NIL },
-  { string10, sp_quote, 1, 1 },
-  { string11, sp_defun, 0, 127 },
-  { string12, sp_defvar, 2, 2 },
-  { string13, sp_setq, 2, 126 },
-  { string14, sp_loop, 0, 127 },
-  { string15, sp_return, 0, 127 },
-  { string16, sp_push, 2, 2 },
-  { string17, sp_pop, 1, 1 },
-  { string18, sp_incf, 1, 2 },
-  { string19, sp_decf, 1, 2 },
-  { string20, sp_setf, 2, 126 },
-  { string21, sp_dolist, 1, 127 },
-  { string22, sp_dotimes, 1, 127 },
-  { string23, sp_trace, 0, 1 },
-  { string24, sp_untrace, 0, 1 },
-  { string25, sp_formillis, 1, 127 },
-  { string26, sp_withserial, 1, 127 },
-  { string27, sp_withi2c, 1, 127 },
-  { string28, sp_withspi, 1, 127 },
-  { string29, sp_withsdcard, 2, 127 },
-  { string30, NULL, NIL, NIL },
-  { string31, tf_progn, 0, 127 },
-  { string32, tf_if, 2, 3 },
-  { string33, tf_cond, 0, 127 },
-  { string34, tf_when, 1, 127 },
-  { string35, tf_unless, 1, 127 },
-  { 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_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_car, 1, 1 },
-  { string51, fn_cdr, 1, 1 },
-  { string52, fn_cdr, 1, 1 },
-  { string53, fn_caar, 1, 1 },
-  { string54, fn_cadr, 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_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, 127 },
-  { string77, fn_mapcar, 2, 127 },
-  { string78, fn_mapcan, 2, 127 },
-  { 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 },
+  { string0, NULL, 0x00 },
+  { string1, NULL, 0x00 },
+  { string2, NULL, 0x00 },
+  { string3, NULL, 0x00 },
+  { string4, NULL, 0x00 },
+  { string5, NULL, 0x0F },
+  { string6, NULL, 0x0F },
+  { string7, NULL, 0x0F },
+  { string8, NULL, 0x0F },
+  { string9, NULL, 0x00 },
+  { string10, sp_quote, 0x11 },
+  { string11, sp_defun, 0x2F },
+  { string12, sp_defvar, 0x12 },
+  { string13, sp_setq, 0x2F },
+  { string14, sp_loop, 0x0F },
+  { string15, sp_return, 0x0F },
+  { string16, sp_push, 0x22 },
+  { string17, sp_pop, 0x11 },
+  { string18, sp_incf, 0x12 },
+  { string19, sp_decf, 0x12 },
+  { string20, sp_setf, 0x2F },
+  { string21, sp_dolist, 0x1F },
+  { string22, sp_dotimes, 0x1F },
+  { string23, sp_trace, 0x01 },
+  { string24, sp_untrace, 0x01 },
+  { string25, sp_formillis, 0x1F },
+  { string26, sp_withserial, 0x1F },
+  { string27, sp_withi2c, 0x1F },
+  { string28, sp_withspi, 0x1F },
+  { string29, sp_withsdcard, 0x2F },
+  { string30, NULL, 0x00 },
+  { string31, tf_progn, 0x0F },
+  { string32, tf_if, 0x23 },
+  { string33, tf_cond, 0x0F },
+  { string34, tf_when, 0x1F },
+  { string35, tf_unless, 0x1F },
+  { string36, tf_case, 0x1F },
+  { string37, tf_and, 0x0F },
+  { string38, tf_or, 0x0F },
+  { string39, NULL, 0x00 },
+  { string40, fn_not, 0x11 },
+  { string41, fn_not, 0x11 },
+  { string42, fn_cons, 0x22 },
+  { string43, fn_atom, 0x11 },
+  { string44, fn_listp, 0x11 },
+  { string45, fn_consp, 0x11 },
+  { string46, fn_symbolp, 0x11 },
+  { string47, fn_streamp, 0x11 },
+  { string48, fn_eq, 0x22 },
+  { string49, fn_car, 0x11 },
+  { string50, fn_car, 0x11 },
+  { string51, fn_cdr, 0x11 },
+  { string52, fn_cdr, 0x11 },
+  { string53, fn_caar, 0x11 },
+  { string54, fn_cadr, 0x11 },
+  { string55, fn_cadr, 0x11 },
+  { string56, fn_cdar, 0x11 },
+  { string57, fn_cddr, 0x11 },
+  { string58, fn_caaar, 0x11 },
+  { string59, fn_caadr, 0x11 },
+  { string60, fn_cadar, 0x11 },
+  { string61, fn_caddr, 0x11 },
+  { string62, fn_caddr, 0x11 },
+  { string63, fn_cdaar, 0x11 },
+  { string64, fn_cdadr, 0x11 },
+  { string65, fn_cddar, 0x11 },
+  { string66, fn_cdddr, 0x11 },
+  { string67, fn_length, 0x11 },
+  { string68, fn_list, 0x0F },
+  { string69, fn_reverse, 0x11 },
+  { string70, fn_nth, 0x22 },
+  { string71, fn_assoc, 0x22 },
+  { string72, fn_member, 0x22 },
+  { string73, fn_apply, 0x2F },
+  { string74, fn_funcall, 0x1F },
+  { string75, fn_append, 0x0F },
+  { string76, fn_mapc, 0x2F },
+  { string77, fn_mapcar, 0x2F },
+  { string78, fn_mapcan, 0x2F },
+  { string79, fn_add, 0x0F },
+  { string80, fn_subtract, 0x1F },
+  { string81, fn_multiply, 0x0F },
+  { string82, fn_divide, 0x2F },
+  { string83, fn_divide, 0x12 },
+  { string84, fn_mod, 0x22 },
+  { string85, fn_oneplus, 0x11 },
+  { string86, fn_oneminus, 0x11 },
+  { string87, fn_abs, 0x11 },
+  { string88, fn_random, 0x11 },
+  { string89, fn_maxfn, 0x1F },
+  { string90, fn_minfn, 0x1F },
+  { string91, fn_noteq, 0x1F },
+  { string92, fn_numeq, 0x1F },
+  { string93, fn_less, 0x1F },
+  { string94, fn_lesseq, 0x1F },
+  { string95, fn_greater, 0x1F },
+  { string96, fn_greatereq, 0x1F },
+  { string97, fn_plusp, 0x11 },
+  { string98, fn_minusp, 0x11 },
+  { string99, fn_zerop, 0x11 },
+  { string100, fn_oddp, 0x11 },
+  { string101, fn_evenp, 0x11 },
+  { string102, fn_integerp, 0x11 },
+  { string103, fn_numberp, 0x11 },
+  { string104, fn_char, 0x22 },
+  { string105, fn_charcode, 0x11 },
+  { string106, fn_codechar, 0x11 },
+  { string107, fn_characterp, 0x11 },
+  { string108, fn_stringp, 0x11 },
+  { string109, fn_stringeq, 0x22 },
+  { string110, fn_stringless, 0x22 },
+  { string111, fn_stringgreater, 0x22 },
+  { string112, fn_sort, 0x22 },
+  { string113, fn_stringfn, 0x11 },
+  { string114, fn_concatenate, 0x1F },
+  { string115, fn_subseq, 0x23 },
+  { string116, fn_readfromstring, 0x11 },
+  { string117, fn_princtostring, 0x11 },
+  { string118, fn_prin1tostring, 0x11 },
+  { string119, fn_logand, 0x0F },
+  { string120, fn_logior, 0x0F },
+  { string121, fn_logxor, 0x0F },
+  { string122, fn_lognot, 0x11 },
+  { string123, fn_ash, 0x22 },
+  { string124, fn_logbitp, 0x22 },
+  { string125, fn_eval, 0x11 },
+  { string126, fn_globals, 0x00 },
+  { string127, fn_locals, 0x00 },
+  { string128, fn_makunbound, 0x11 },
+  { string129, fn_break, 0x00 },
+  { string130, fn_read, 0x01 },
+  { string131, fn_prin1, 0x12 },
+  { string132, fn_print, 0x12 },
+  { string133, fn_princ, 0x12 },
+  { string134, fn_terpri, 0x01 },
+  { string135, fn_readbyte, 0x02 },
+  { string136, fn_readline, 0x01 },
+  { string137, fn_writebyte, 0x12 },
+  { string138, fn_writestring, 0x12 },
+  { string139, fn_writeline, 0x12 },
+  { string140, fn_restarti2c, 0x12 },
+  { string141, fn_gc, 0x00 },
+  { string142, fn_room, 0x00 },
+  { string143, fn_saveimage, 0x01 },
+  { string144, fn_loadimage, 0x01 },
+  { string145, fn_cls, 0x00 },
+  { string146, fn_pinmode, 0x22 },
+  { string147, fn_digitalread, 0x11 },
+  { string148, fn_digitalwrite, 0x22 },
+  { string149, fn_analogread, 0x11 },
+  { string150, fn_analogwrite, 0x22 },
+  { string151, fn_delay, 0x11 },
+  { string152, fn_millis, 0x00 },
+  { string153, fn_sleep, 0x11 },
+  { string154, fn_note, 0x03 },
+  { string155, fn_edit, 0x11 },
+  { string156, fn_pprint, 0x12 },
+  { string157, fn_pprintall, 0x01 },
+  { string158, fn_format, 0x2F },
+  { string159, fn_require, 0x11 },
+  { string160, fn_listlibrary, 0x00 },
 };

 // Table lookup functions
@@ -3358,7 +3465,7 @@
 int builtin (char* n) {
   int entry = 0;
   while (entry < ENDFUNCTIONS) {
-    #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+    #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
     if (strcasecmp(n, (char*)lookup_table[entry].string) == 0)
     #else
     if (strcasecmp_P(n, (char*)pgm_read_word(&lookup_table[entry].string)) == 0)
@@ -3379,37 +3486,31 @@
     if (SYMBOLTABLESIZE - (newtop - SymbolTable) < BUFFERSIZE) error2(0, PSTR("no room for long symbols"));
     SymbolTop = newtop;
   }
-  if (i > 1535) error2(0, PSTR("Too many long symbols"));
-  return i + 64000; // First number unused by radix40
+  if (i > 1535) error2(0, PSTR("too many long symbols"));
+  return i + MAXSYMBOL; // First number unused by radix40
 }

 intptr_t lookupfn (symbol_t name) {
-  #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+  #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   return (intptr_t)lookup_table[name].fptr;
   #else
   return pgm_read_word(&lookup_table[name].fptr);
   #endif
 }

-uint8_t lookupmin (symbol_t name) {
-  #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
-  return lookup_table[name].min;
+void checkminmax (symbol_t name, int nargs) {
+  #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
+  uint8_t minmax = lookup_table[name].minmax;
   #else
-  return pgm_read_byte(&lookup_table[name].min);
+  uint8_t minmax = pgm_read_byte(&lookup_table[name].minmax);
   #endif
-}
-
-uint8_t lookupmax (symbol_t name) {
-  #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
-  return lookup_table[name].max;
-  #else
-  return pgm_read_byte(&lookup_table[name].max);
-  #endif
+  if (nargs<(minmax >> 4)) error2(name, toofewargs);
+  if ((minmax & 0x0f) != 0x0f && nargs>(minmax & 0x0f)) error2(name, toomanyargs);
 }

 char *lookupbuiltin (symbol_t name) {
   char *buffer = SymbolTop;
-  #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+  #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   strcpy(buffer, (char *)(lookup_table[name].string));
   #else
   strcpy_P(buffer, (char *)(pgm_read_word(&lookup_table[name].string)));
@@ -3419,7 +3520,7 @@

 char *lookupsymbol (symbol_t name) {
   char *p = SymbolTable;
-  int i = name - 64000;
+  int i = name - MAXSYMBOL;
   while (i > 0 && p < SymbolTop) {p = p + strlen(p) + 1; i--; }
   if (p == SymbolTop) return NULL; else return p;
 }
@@ -3439,28 +3540,26 @@

 // Main evaluator

-uint8_t End;
+extern char __bss_end[];

 object *eval (object *form, object *env) {
+  uint8_t sp[0];
   int TC=0;
   EVAL:
-  yield(); // Needed on ESP8266 to avoid Soft WDT Reset
   // Enough space?
-  if (End != 0xA5) error2(0, PSTR("Stack overflow"));
-  if (Freespace <= WORKSPACESIZE>>4) gc(form, env);
+  // Serial.println((uint16_t)sp - (uint16_t)__bss_end);
+  if ((uint16_t)sp - (uint16_t)__bss_end < STACKDIFF) error2(0, PSTR("Stack overflow"));
+  if (Freespace <= (WORKSPACESIZE)>>4) gc(form, env);
   // Escape
   if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2(0, PSTR("Escape!"));}
-  #if defined (serialmonitor)
   if (!tstflag(NOESC)) testescape();
-  #endif
-
+
   if (form == NULL) return nil;
-
-  if (integerp(form) || floatp(form) || characterp(form) || stringp(form)) return form;
+
+  if (form->type >= NUMBER && form->type <= STRING) return form;

   if (symbolp(form)) {
     symbol_t name = form->name;
-    if (name == NIL) return nil;
     object *pair = value(name, env);
     if (pair != NULL) return cdr(pair);
     pair = value(name, GlobalEnv);
@@ -3468,14 +3567,14 @@
     else if (name <= ENDFUNCTIONS) return form;
     error(0, PSTR("undefined"), form);
   }
-
+
   // It's a list
   object *function = car(form);
   object *args = cdr(form);

   if (function == NULL) error(0, PSTR("illegal function"), nil);
   if (!listp(args)) error(0, PSTR("can't evaluate a dotted pair"), args);
-
+
   // List starts with a symbol?
   if (symbolp(function)) {
     symbol_t name = function->name;
@@ -3483,6 +3582,7 @@
     if ((name == LET) || (name == LETSTAR)) {
       int TCstart = TC;
       object *assigns = first(args);
+      if (!listp(assigns)) error(name, PSTR("first argument is not a list"), assigns);
       object *forms = cdr(args);
       object *newenv = env;
       push(newenv, GCStack);
@@ -3512,8 +3612,6 @@
       }
       return cons(symbol(CLOSURE), cons(envcopy,args));
     }
-
-    if (name < SPECIAL_FORMS) error2((int)function, PSTR("can't be used as a function"));

     if ((name > SPECIAL_FORMS) && (name < TAIL_FORMS)) {
       return ((fn_ptr_type)lookupfn(name))(args, env);
@@ -3524,12 +3622,14 @@
       TC = 1;
       goto EVAL;
     }
+
+    if (name < SPECIAL_FORMS) error2((uintptr_t)function, PSTR("can't be used as a function"));
   }

   // Evaluate the parameters - result in head
   object *fname = car(form);
   int TCstart = TC;
-  object *head = cons(eval(car(form), env), NULL);
+  object *head = cons(eval(fname, env), NULL);
   push(head, GCStack); // Don't GC the result list
   object *tail = head;
   form = cdr(form);
@@ -3549,39 +3649,41 @@
   if (symbolp(function)) {
     symbol_t name = function->name;
     if (name >= ENDFUNCTIONS) error(0, PSTR("not valid here"), fname);
-    if (nargs<lookupmin(name)) error2(name, PSTR("has too few arguments"));
-    if (nargs>lookupmax(name)) error2(name, PSTR("has too many arguments"));
+    checkminmax(name, nargs);
     object *result = ((fn_ptr_type)lookupfn(name))(args, env);
     pop(GCStack);
     return result;
   }
-
-  if (consp(function) && issymbol(car(function), LAMBDA)) {
-    form = closure(TCstart, fname->name, NULL, cdr(function), args, &env);
-    pop(GCStack);
-    int trace = tracing(fname->name);
-    if (trace) {
-      object *result = eval(form, env);
-      indent((--(TraceDepth[trace-1]))<<1, pserial);
-      pint(TraceDepth[trace-1], pserial);
-      pserial(':'); pserial(' ');
-      printobject(fname, pserial); pfstring(PSTR(" returned "), pserial);
-      printobject(result, pserial); pln(pserial);
-      return result;
-    } else {
+
+  if (consp(function)) {
+
+    if (issymbol(car(function), LAMBDA)) {
+      form = closure(TCstart, fname->name, NULL, cdr(function), args, &env);
+      pop(GCStack);
+      int trace = tracing(fname->name);
+      if (trace) {
+        object *result = eval(form, env);
+        indent((--(TraceDepth[trace-1]))<<1, ' ', pserial);
+        pint(TraceDepth[trace-1], pserial);
+        pserial(':'); pserial(' ');
+        printobject(fname, pserial); pfstring(PSTR(" returned "), pserial);
+        printobject(result, pserial); pln(pserial);
+        return result;
+      } else {
+        TC = 1;
+        goto EVAL;
+      }
+    }
+
+    if (issymbol(car(function), CLOSURE)) {
+      function = cdr(function);
+      form = closure(TCstart, fname->name, car(function), cdr(function), args, &env);
+      pop(GCStack);
       TC = 1;
       goto EVAL;
     }
-  }
-
-  if (consp(function) && issymbol(car(function), CLOSURE)) {
-    function = cdr(function);
-    form = closure(TCstart, fname->name, car(function), cdr(function), args, &env);
-    pop(GCStack);
-    TC = 1;
-    goto EVAL;
-  }
-
+
+  }
   error(0, PSTR("illegal function"), fname); return nil;
 }

@@ -3621,7 +3723,7 @@
   if (tstflag(PRINTREADABLY)) pfun('"');
   form = cdr(form);
   while (form != NULL) {
-    int chars = form->integer;
+    int chars = form->chars;
     for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
       char ch = chars>>i & 0xFF;
       if (tstflag(PRINTREADABLY) && (ch == '"' || ch == '\\')) pfun('\\');
@@ -3635,7 +3737,7 @@
 void pfstring (PGM_P s, pfun_t pfun) {
   int p = 0;
   while (1) {
-    #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+    #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
     char c = s[p++];
     #else
     char c = pgm_read_byte(&s[p++]);
@@ -3660,6 +3762,16 @@
   }
 }

+void pinthex (uint16_t i, pfun_t pfun) {
+  int lead = 0;
+  uint16_t p = 0x1000;
+  for (uint16_t d=p; d>0; d=d/16) {
+    uint16_t j = i/d;
+    if (j!=0 || lead || d==1) { pfun((j<10) ? j+'0' : j+'W'); lead=1;}
+    i = i - j*d;
+  }
+}
+
 inline void pln (pfun_t pfun) {
   pfun('\n');
 }
@@ -3668,7 +3780,7 @@
   if (LastPrint != '\n') pfun('\n');
 }

-void printobject (object *form, pfun_t pfun){
+void printobject (object *form, pfun_t pfun) {
   if (form == NULL) pfstring(PSTR("nil"), pfun);
   else if (listp(form) && issymbol(car(form), CLOSURE)) pfstring(PSTR("<closure>"), pfun);
   else if (listp(form)) {
@@ -3687,10 +3799,10 @@
     pfun(')');
   } else if (integerp(form)) pint(form->integer, pfun);
   else if (symbolp(form)) { if (form->name != NOTHING) pstring(symbolname(form->name), pfun); }
-  else if (characterp(form)) pcharacter(form->integer, pfun);
+  else if (characterp(form)) pcharacter(form->chars, pfun);
   else if (stringp(form)) printstring(form, pfun);
   else if (streamp(form)) {
-    pfstring(PSTR("<"), pfun);
+    pfun('<');
     if ((form->integer)>>8 == SPISTREAM) pfstring(PSTR("spi"), pfun);
     else if ((form->integer)>>8 == I2CSTREAM) pfstring(PSTR("i2c"), pfun);
     else if ((form->integer)>>8 == SDSTREAM) pfstring(PSTR("sd"), pfun);
@@ -3702,6 +3814,13 @@
     error2(0, PSTR("Error in print"));
 }

+void prin1object (object *form, pfun_t pfun) {
+  char temp = Flags;
+  clrflag(PRINTREADABLY);
+  printobject(form, pfun);
+  Flags = temp;
+}
+
 // Read functions

 int glibrary () {
@@ -3710,7 +3829,7 @@
     LastChar = 0;
     return temp;
   }
-  #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+  #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
   char c = LispLibrary[GlobalStringIndex++];
   #else
   char c = pgm_read_byte(&LispLibrary[GlobalStringIndex++]);
@@ -3727,21 +3846,122 @@
   }
 }

+// For line editor
+const int TerminalWidth = 80;
+volatile int WritePtr = 0, ReadPtr = 0;
+const int KybdBufSize = 333; // 42*8 - 3
+char KybdBuf[KybdBufSize];
+volatile uint8_t KybdAvailable = 0;
+
+// Parenthesis highlighting
+void esc (int p, char c) {
+  Serial.write('\e'); Serial.write('[');
+  Serial.write((char)('0'+ p/100));
+  Serial.write((char)('0'+ (p/10) % 10));
+  Serial.write((char)('0'+ p % 10));
+  Serial.write(c);
+}
+
+void hilight (char c) {
+  Serial.write('\e'); Serial.write('['); Serial.write(c); Serial.write('m');
+}
+
+void Highlight (int p, int wp, uint8_t invert) {
+  wp = wp + 2; // Prompt
+#if defined (printfreespace)
+  int f = Freespace;
+  while (f) { wp++; f=f/10; }
+#endif
+  int line = wp/TerminalWidth;
+  int col = wp%TerminalWidth;
+  int targetline = (wp - p)/TerminalWidth;
+  int targetcol = (wp - p)%TerminalWidth;
+  int up = line-targetline, left = col-targetcol;
+  if (p) {
+    if (up) esc(up, 'A');
+    if (col > targetcol) esc(left, 'D'); else esc(-left, 'C');
+    if (invert) hilight('7');
+    Serial.write('('); Serial.write('\b');
+    // Go back
+    if (up) esc(up, 'B'); // Down
+    if (col > targetcol) esc(left, 'C'); else esc(-left, 'D');
+    Serial.write('\b'); Serial.write(')');
+    if (invert) hilight('0');
+  }
+}
+
+void processkey (char c) {
+  if (c == 27) { setflag(ESCAPE); return; }    // Escape key
+#if defined(vt100)
+  static int parenthesis = 0, wp = 0;
+  // Undo previous parenthesis highlight
+  Highlight(parenthesis, wp, 0);
+  parenthesis = 0;
+#endif
+  // Edit buffer
+  if (c == '\n' || c == '\r') {
+    pserial('\n');
+    KybdAvailable = 1;
+    ReadPtr = 0;
+    return;
+  }
+  if (c == 8 || c == 0x7f) {     // Backspace key
+    if (WritePtr > 0) {
+      WritePtr--;
+      Serial.write(8); Serial.write(' '); Serial.write(8);
+      if (WritePtr) c = KybdBuf[WritePtr-1];
+    }
+  } else if (WritePtr < KybdBufSize) {
+    KybdBuf[WritePtr++] = c;
+    Serial.write(c);
+  }
+#if defined(vt100)
+  // Do new parenthesis highlight
+  if (c == ')') {
+    int search = WritePtr-1, level = 0;
+    while (search >= 0 && parenthesis == 0) {
+      c = KybdBuf[search--];
+      if (c == ')') level++;
+      if (c == '(') {
+        level--;
+        if (level == 0) {parenthesis = WritePtr-search-1; wp = WritePtr; }
+      }
+    }
+    Highlight(parenthesis, wp, 1);
+  }
+#endif
+  return;
+}
+
 int gserial () {
   if (LastChar) {
     char temp = LastChar;
     LastChar = 0;
     return temp;
   }
+#if defined(lineeditor)
+  while (!KybdAvailable) {
+    while (!Serial.available());
+    char temp = Serial.read();
+    processkey(temp);
+  }
+  if (ReadPtr != WritePtr) return KybdBuf[ReadPtr++];
+  KybdAvailable = 0;
+  WritePtr = 0;
+  return '\n';
+#else
   while (!Serial.available());
   char temp = Serial.read();
   if (temp != '\n') pserial(temp);
   return temp;
-}
+#endif
+}
+
+#define issp(x) (x == ' ' || x == '\n' || x == '\r' || x == '\t')

 object *nextitem (gfun_t gfun) {
   int ch = gfun();
-  while(isspace(ch)) ch = gfun();
+  while(issp(ch)) ch = gfun();

   if (ch == ';') {
     while(ch != '(') ch = gfun();
@@ -3762,18 +3982,25 @@
   char *buffer = SymbolTop;
   int bufmax = maxbuffer(buffer); // Max index
   unsigned int result = 0;
-  if (ch == '+') {
+  if (ch == '+' || ch == '-') {
     buffer[index++] = ch;
+    if (ch == '-') sign = -1;
     ch = gfun();
-  } else if (ch == '-') {
-    sign = -1;
-    buffer[index++] = ch;
-    ch = gfun();
-  } else if (ch == '#') {
+  }
+
+  // Parse reader macros
+  else if (ch == '#') {
     ch = gfun();
     char ch2 = ch & ~0x20; // force to upper case
-    if (ch == '\\') base = 0; // character
-    else if (ch2 == 'B') base = 2;
+    if (ch == '\\') { // Character
+      base = 0; ch = gfun();
+      if (issp(ch) || ch == ')' || ch == '(') return character(ch);
+      else LastChar = ch;
+    } else if (ch == '|') {
+      do { while (gfun() != '|'); }
+      while (gfun() != '#');
+      return nextitem(gfun);
+    } else if (ch2 == 'B') base = 2;
     else if (ch2 == 'O') base = 8;
     else if (ch2 == 'X') base = 16;
     else if (ch == '\'') return nextitem(gfun);
@@ -3785,17 +4012,17 @@
     } else error2(0, PSTR("illegal character after #"));
     ch = gfun();
   }
+
   int isnumber = (digitvalue(ch)<base);
   buffer[2] = '\0'; // In case symbol is one letter

-  while(!isspace(ch) && ch != ')' && ch != '(' && index < bufmax) {
+  while(!issp(ch) && ch != ')' && ch != '(' && index < bufmax) {
     buffer[index++] = ch;
     int temp = digitvalue(ch);
     result = result * base + temp;
     isnumber = isnumber && (digitvalue(ch)<base);
     ch = gfun();
   }
-
   buffer[index] = '\0';
   if (ch == ')' || ch == '(') LastChar = ch;

@@ -3807,7 +4034,7 @@
     if (index == 1) return character(buffer[0]);
     PGM_P p = ControlCodes; char c = 0;
     while (c < 33) {
-      #if defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3216__)
+      #if defined(__AVR_ATmega4809__) || defined(ARDUINO_AVR_ATmega4809)
       if (strcasecmp(buffer, p) == 0) return character(c);
       p = p + strlen(p) + 1; c++;
       #else
@@ -3815,7 +4042,7 @@
       p = p + strlen_P(p) + 1; c++;
       #endif
     }
-    error2(0, PSTR("Unknown character"));
+    error2(0, PSTR("unknown character"));
   }

   int x = builtin(buffer);
@@ -3868,16 +4095,12 @@

 void setup () {
   Serial.begin(9600);
-  #if defined(ARDUINO_AVR_NANO_EVERY) || defined(ARDUINO_AVR_ATmega4809)
-  delay(2500);
-  #else
   int start = millis();
   while ((millis() - start) < 5000) { if (Serial) break; }
-  #endif
   initworkspace();
   initenv();
   initsleep();
-  pfstring(PSTR("uLisp 3.0 "), pserial); pln(pserial);
+  pfstring(PSTR("uLisp 3.2 "), pserial); pln(pserial);
 }

 // Read/Evaluate/Print loop
@@ -3909,7 +4132,6 @@
 }

 void loop () {
-  End = 0xA5;      // Canary to check stack
   if (!setjmp(exception)) {
     #if defined(resetautorun)
     volatile int autorun = 12; // Fudge to keep code size the same
@@ -3920,6 +4142,7 @@
   }
   // Come here after error
   delay(100); while (Serial.available()) Serial.read();
+  clrflag(NOESC);
   for (int i=0; i<TRACEMAX; i++) TraceDepth[i] = 0;
   #if defined(sdcardsupport)
   SDpfile.close(); SDgfile.close();


git-svn-id: svn://192.168.1.10/public/Zimodem@14804 0d6f1817-ed0e-0410-87c9-987e46238f29

--- zimodem/zcommand.ino
+++ zimodem/zcommand.ino
@@ -1152,7 +1152,8 @@
     currentExpiresTimeMs = millis() + 1000;
   if(!crReceived)
     return;
-  //delay(200); // uncomment this only when you can explain it.
+  delay(200); // give a pause after receiving command before responding
+  // the delay doesn't affect xon/xoff because its the periodic transmitter that manages that.
   doSerialCommand();
 }


ati9

ati9

git-svn-id: d8bfcd070c2c81762cca1b3912331324182919ba@17117 0d6f1817-ed0e-0410-87c9-987e46238f29

--- zimodem/zcommand.ino
+++ zimodem/zcommand.ino
@@ -655,6 +655,11 @@
   {
     serial.prints(compile_date);
     serial.prints(EOLN);
+  }
+  else
+  if(vval == 9)
+  {
+    serial.printf("%d%s",ESP.getFreeHeap(),EOLN.c_str());
   }
   else
     return ZERROR;


Add basic colors swticher. TODO: Implement requesting every N minutes

--- WemosD1-CodeCoverageLEDStrip/CoverallsCodeCoverageLEDStrip/CoverallsCodeCoverageLEDStrip.ino
+++ WemosD1-CodeCoverageLEDStrip/CoverallsCodeCoverageLEDStrip/CoverallsCodeCoverageLEDStrip.ino
@@ -2,13 +2,19 @@
  * This is arduino client that changes LED stripe colors according to percentage of code coverage.
  * For now the colors would change every 10%, so in total 10 different colors.
  *
+ * 90 - 100: 00FF00
+ * 80 -  90: FFCC00
+ * 70 -  80: FF9900
+ * 60 -  70: FF6600
+ * 50 -  60: FF3300
+ *     < 50: FF0000
  */

 #include <ESP8266WiFi.h>
 #include <WiFiClientSecure.h>
 #include<ArduinoJson.h>

-#define BLUEPIN 13
+#define BLUEPIN 13
 #define GREENPIN 12
 #define REDPIN 14

@@ -21,7 +27,48 @@
 // SHA1 fingerprint of the certificate
 const char* fingerprint = "6D A8 77 AB 48 93 2B 81 4B 93 6C 3E 53 54 8B 1A FC 61 1C 05";

+void changeLights(double coverage){
+  if(coverage >= 90){
+    analogWrite(REDPIN, 0);
+    analogWrite(BLUEPIN, 0);
+    analogWrite(GREENPIN, 255);
+  }
+  else if(coverage >= 80 && coverage < 90){
+    analogWrite(REDPIN, 255);
+    analogWrite(BLUEPIN, 0);
+    analogWrite(GREENPIN, 200);
+  }
+  else if(coverage >= 70 && coverage < 80){
+    analogWrite(REDPIN, 255);
+    analogWrite(BLUEPIN, 0);
+    analogWrite(GREENPIN, 150);
+  }
+  else if(coverage >= 60 && coverage < 70 ){
+    analogWrite(REDPIN, 255);
+    analogWrite(BLUEPIN, 0);
+    analogWrite(GREENPIN, 100);
+  }
+  else if(coverage >= 50 && coverage < 60){
+    analogWrite(REDPIN, 255);
+    analogWrite(BLUEPIN, 0);
+    analogWrite(GREENPIN, 50);
+  }
+  else{
+    analogWrite(REDPIN, 255);
+    analogWrite(BLUEPIN, 0);
+    analogWrite(GREENPIN, 0);
+  }
+}
+
 void setup() {
+  pinMode(BLUEPIN, OUTPUT);
+  pinMode(GREENPIN, OUTPUT);
+  pinMode(REDPIN, OUTPUT);
+
+  analogWrite(REDPIN, 0);
+  analogWrite(BLUEPIN, 0);
+  analogWrite(GREENPIN, 0);
+
   Serial.begin(115200);
   Serial.println();
   Serial.print("connecting to ");
@@ -90,9 +137,14 @@

   Serial.println("==========");
   Serial.println(percentage);
+
+  changeLights(percentage);
+
   Serial.println("==========");
   Serial.println("closing connection");
 }

 void loop() {
 }
+
+


Compiling test succesful

Compiling test succesful

--- Arduino/Test_Pressure/Test_Pressure.ino
+++ Arduino/Test_Pressure/Test_Pressure.ino
@@ -1,3 +1,7 @@
+#include <Adafruit_BMP085_U.h>
+
+#include <Wire.h>
+
 #include <Adafruit_10DOF.h>

 #include <Adafruit_Sensor.h>
@@ -6,7 +10,7 @@

 #include <Adafruit_L3GD20_U.h>

-#include <Adafruit_BMP085_U.h>
+Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);

 void setup(void) {
   Serial.begin(9600);
@@ -15,7 +19,7 @@

   if (!bmp.begin())
   {
-    Serial.print("Ooops, no BMP085 detected);
+    Serial.print("Ooops, no BMP085 detected");
     while(1);
   }
 }


all led mounted, changed lighting pattern

--- musicbox/musicbox.ino
+++ musicbox/musicbox.ino
@@ -28,8 +28,9 @@

 // Delays [ms]
-#define IDLE_PAUSE     2000
-#define BLINK_DELAY     100
+#define BLANK_DELAY    2500
+#define BLINK_DELAY      25
+#define HOLD_DELAY     1500
 #define PAUSE_DELAY    1000
 #define READ_DELAY       50
 #define IDLE_TIMEOUT  (1000L * 60L)
@@ -42,7 +43,7 @@
 #define BRIGHTNESS_PLAYING   10

 // Trellis setup
-#define NUMKEYS            4
+#define NUMKEYS           16
 #define TRELLIS_INT_PIN    1

 // Feather/Wing pin setup
@@ -67,11 +68,12 @@

 // States
 #define IDLE_LIGHT_UP   1
-#define IDLE_TURN_OFF   2
-#define IDLE_WAIT_OFF   3
-#define PLAY_SELECTED   4
-#define PLAY_PAUSED     5
-#define TIMEOUT_WAIT    6
+#define IDLE_WAIT_ON    2
+#define IDLE_TURN_OFF   3
+#define IDLE_WAIT_OFF   4
+#define PLAY_SELECTED   5
+#define PLAY_PAUSED     6
+#define TIMEOUT_WAIT    7

 // Volume
 #define SPEAKER_VOLUME_MIN         130    // max. 254 moderation
@@ -250,9 +252,14 @@
     trellis.writeDisplay();
     nextLED = (nextLED + 1) % NUMKEYS;
     if (nextLED == 0) {
-      state = IDLE_TURN_OFF;
+      state = IDLE_WAIT_ON;
     }
     return BLINK_DELAY;
+
+  // Wait with all keys lighted
+  } else if (state == IDLE_WAIT_ON) {
+    state = IDLE_TURN_OFF;
+    return HOLD_DELAY;

   // Turn off all keys in order
   } else if (state == IDLE_TURN_OFF) {
@@ -261,7 +268,7 @@
     nextLED = (nextLED + 1) % NUMKEYS;
     if (nextLED == 0) {
       state = IDLE_WAIT_OFF;
-      return IDLE_PAUSE;
+      return BLANK_DELAY;
     }
     return BLINK_DELAY;


minor changes

minor changes

--- flowmeter.ino
+++ flowmeter.ino
@@ -1,7 +1,7 @@
 /**********************************************************************/
-/* PulseCounter example
+/* Flowmeter example
 /* An example to collect data from a flowmeter using PulseSensor class
-/* version 0.2 BETA 14/09/2015
+/* version 0.3 BETA 22/09/2015
 /* Author: Jaime García  @peninquen
 /* Licence: Apache License Version 2.0.
 /*
@@ -20,8 +20,12 @@
 Output duty cycle: 50% +-10%
 Output rise time: 0.04us
 Output fall time: 0.18us
-Flow rate pulse characteristics: Frequency (Hz) = 7.5 * Flow rate (L/min)
-Pulses per Liter: 450
+Flow rate pulse characteristics:
+Flow rate: 1 pulse/s = 7.5 L/min
+Acumulated volume: 1 Liter = 450 pulses
+maximum pulse frequency: 30 L/min-> 225 imp/s -> min period 4.44 ms  (delta 0.1 L/min->0.015 ms)
+                          5 L/min-> 37.5 imp/s->     period 26.66 ms (delta 0.1 L/min->0.523 ms)
+minimun pulse frequency:  1 L/min-> 7.5 imp/s -> max period 133.3 ms (delta 0.1 L/min->12.1 ms)
 Durability: minimum 300,000 cycles
 Mechanical:
 1/2" NPS nominal pipe connections, 0.78" outer diameter, 1/2" of thread
@@ -31,13 +35,12 @@
 #include "PulseSensor.h"

-#define REFRESH_INTERVAL  1000      // refresh time, 1 second
-#define WRITE_INTERVAL 5000  // values send to serial port, 5 seconds (5 * 1000)
-#define PULSE_PIN 18 // see external interrupt pins available on your Arduino.
-                     // Conect an external 10 Kohm pull up resistor  on input pin is recomended
-#define PULSES_SEC_2_LITERS_MINUTE 7.5 // conversion factor from pulses/second to liters/minute
-#define PULSES_2_LITERS 450 // conversion factor from pulses to liters
-PulseSensor flowmeter; // instance to collect data
+#define REFRESH_INTERVAL  1000          // refresh time, 1 second
+#define WRITE_INTERVAL 5000             // values send to serial port, 5 seconds (5 * 1000)
+#define PULSE_PIN 18                    // see external interrupt pins available on your Arduino.
+#define PULSES_2_LITERS 450             // coefficient conversion from pulses to liters
+#define PULSES_SEC_2_LITERS_MINUTE 7.5  // (450/60) coefficient conversion pulses/second from to liters/minute
+PulseSensor flowmeter;                  // instance to collect data
 //variables to process and send values
 float flowRate;
 float maxRate;


Allowed guest cards to be sent to web app

--- frontdesk/frontdesk.ino
+++ frontdesk/frontdesk.ino
@@ -66,7 +66,7 @@
 			playCoinSound();

 			WIFI.listen();
-			if (scannedID != 6 && scannedID != 12 && scannedID != 0 && scannedID < 100) {
+			if ( 0 < scannedID && scannedID < 100) {
 				scanTest(scannedID);
 			}


Simplified code for wipe animations

Simplified code for wipe animations

--- BlinkenScreen.ino
+++ BlinkenScreen.ino
@@ -119,17 +119,19 @@
   return val;
 }

-inline void animation_wipe() {
+void animation_wipe() {
+  animation_raw_wipe();
+
+  s.i = massage(s.i);
+}
+
+void animation_raw_wipe() {
   static uint64_t i = 0;
-
+
+  // Show single pixel walking across the display
   i = i == 0 ? 1 : i << 1;

-  s.i = massage(i);
-}
-
-inline void animation_raw_wipe() {
-  // Show single pixel walking across the display
-  s.i = s.i == 0 ? 1 : s.i << 1;
+  s.i = i;
 }

 inline void animation_bump() {


Read the volts on the solar panel

Read the volts on the solar panel

--- LoRa/LoRa_Garden_TX/LoRa_Garden_TX.ino
+++ LoRa/LoRa_Garden_TX/LoRa_Garden_TX.ino
@@ -20,7 +20,6 @@
 #define RF95_FREQ 868.0

 #define PIN_SENSOR_LIGHT A0
-#define PIN_SENSOR_SOLAR_VOLTS A1
 #define PIN_SENSOR_SOIL A2

@@ -77,6 +76,9 @@

   tx_payload.setChargeMa(readSolarCurrent());
   tx_payload.setChargeMv(readSolarVolts());
+
+Serial.print("getChargeMv: "); Serial.println(tx_payload.getChargeMv());
+
   tx_payload.setLight(readLight(vcc));
   tx_payload.setSoil(readSoil(vcc));

@@ -147,14 +149,19 @@

 uint16_t readSolarCurrent() {
   // 1 ohm inline resistor
-  return ads.readADC_Differential_0_1();
+  // Invert result as the wires are connected goofy.
+  return ads.readADC_Differential_0_1() * -1;
 }

 uint16_t readSolarVolts() {
   // Voltage divider to reduce the maximum to 2V.
   // 8V -> 2V
   // Solar Panel --- 300K --- | --- 100K --- GND
-  return ads.readADC_SingleEnded(2);
+  int16_t val = ads.readADC_SingleEnded(2);
+  // Calibration:
+  // 1000mV = 268, 2000mV = 537, 3000mV = 805...
+  // so 3.7313mV = 1
+  return val * 3.7313F;
 }

 uint16_t readLight(uint16_t vcc) {


Code cleanup and threshhold adjstments

Code cleanup and threshhold adjstments

--- FreezerScreamer.ino
+++ FreezerScreamer.ino
@@ -36,12 +36,12 @@
 int msfiveMinutes = 300000;
 int msOneHour = 3600000;

-float lowFahrenheitThreshhold = 74.0;
+float lowFahrenheitThreshhold = -5.0;
 int lowThreshExceededCounter = 0;
 int numLowTempViolations = msfiveMinutes/msDelayInterval;

-float highFahrenheitThreshhold1 = 75.0;
-float highFahrenheitThreshhold2 = 80.0;
+float highFahrenheitThreshhold1 = 25.0;
+float highFahrenheitThreshhold2 = 30.0;
 int highThreshExceededCounter = 0;

 int numHighTempViolations_level1 = 2;
@@ -73,6 +73,10 @@
     Particle.publish(eventName, publishString);
 }

+double calcFahrenheitFromCelsius(float celsius){
+  return celsius * 1.8 + 32.0;
+}
+
 // up to here, it is the same as the address acanner
 // we need a few more variables for this example

@@ -218,7 +222,7 @@
       }
   }

-  fahrenheitTemp = celsius * 1.8 + 32.0;
+  fahrenheitTemp = calcFahrenheitFromCelsius(celsius);//celsius * 1.8 + 32.0;
   Serial.print("Temp: ");
   Serial.print(celsius);
   Serial.print(" C, ");


Added birth and lwt message functionality

--- GarHAge/GarHAge.ino
+++ GarHAge/GarHAge.ino
@@ -57,6 +57,13 @@
 unsigned long door2_lastSwitchTime = 0;
 int debounceTime = 2000;

+String availabilityBase = MQTT_CLIENTID;
+String availabilitySuffix = "/availability";
+String availabilityTopicStr = availabilityBase + availabilitySuffix;
+const char* availabilityTopic = availabilityTopicStr.c_str();
+const char* birthMessage = "online";
+const char* lwtMessage = "offline";
+
 WiFiClient espClient;
 PubSubClient client(espClient);

@@ -216,6 +223,18 @@
   }
 }

+// Function that publishes birthMessage
+
+void publish_birth_message() {
+  // Publish the birthMessage
+  Serial.print("Publishing birth message ");
+  Serial.print(birthMessage);
+  Serial.print(" to ");
+  Serial.print(availabilityTopic);
+  Serial.println("...");
+  client.publish(availabilityTopic, birthMessage, true);
+}
+
 // Function that toggles the relevant relay-connected output pin

 void toggleRelay(int pin) {
@@ -244,6 +263,7 @@
     Serial.print("Publishing on-demand status update for ");
     Serial.print(door1_alias);
     Serial.println("!");
+    publish_birth_message();
     publish_door1_status();
   }
   else if (requestedDoor == mqtt_door2_action_topic && requestedAction == "OPEN") {
@@ -262,6 +282,7 @@
     Serial.print("Publishing on-demand status update for ");
     Serial.print(door2_alias);
     Serial.println("!");
+    publish_birth_message();
     publish_door2_status();
   }
   else { Serial.println("Unrecognized action payload... taking no action!");
@@ -275,8 +296,11 @@
   while (!client.connected()) {
     Serial.print("Attempting MQTT connection...");
     // Attempt to connect
-    if (client.connect(mqtt_clientId, mqtt_username, mqtt_password)) {
+    if (client.connect(mqtt_clientId, mqtt_username, mqtt_password, availabilityTopic, 0, true, lwtMessage)) {
       Serial.println("Connected!");
+
+      // Publish the birth message on connect/reconnect
+      publish_birth_message();

       // Subscribe to the action topics to listen for action messages
       Serial.print("Subscribing to ");


[WIP] programming the PGA309

[WIP] programming the PGA309

--- arduino_sketches/PGA309/testPin_PRGPin_I2C/testPin_PRGPin_I2C.ino
+++ arduino_sketches/PGA309/testPin_PRGPin_I2C/testPin_PRGPin_I2C.ino
@@ -1,12 +1,26 @@
+#include <SendOnlySoftwareSerial.h>
+
 int testPin = 52;
 int prgPin = 48;

+SendOnlySoftwareSerial PGA309UART (prgPin);
+
 void setup(){
   Serial.begin(9600);
+  PGA309UART.begin(9600);
   pinMode(testPin, OUTPUT);
+  pinMode(prgPin, OUTPUT);
+  //Setting the test pin high enables direct writing of internal registers and stops transactions with External EEPROM
   digitalWrite(testPin, HIGH);
+  //The PRG pin should always be high when no communication is in progress
+  digitalWrite(prgPin, HIGH);
   delay(50);
-
+  //Setting up the external EEPROM pointer address
+  //Sending initialisation byte 0x55
+  PGA309UART.write(0x55);
+  PGA309UART.write(0x02);
+  PGA309UART.write();
+  PGA309UART.write();
 }

 void loop(){


Fix double usage of portpin 8

Fix double usage of portpin 8

--- switchcube_mpu6050/switchcube_mpu6050.ino
+++ switchcube_mpu6050/switchcube_mpu6050.ino
@@ -46,7 +46,7 @@
 #define USE_EEPROM // read nodeID and network settings from EEPROM at bootup, overwrites nodeID and MAC.
 #define LEDPIN 6
 #define KEEPALIVE 0 // keep connections alive with regular polling to node 0
-#define USE_LEDS // LED stripe used
+//#define USE_LEDS // LED stripe used
 #define USE_TOUCH // capacitive touch sensing
 #define TIMEOUT_HIBERNATE 5000
 #define TIMEOUT_MEMS 5000


Fixed error response

Fixed error response

--- receiver/receiver.ino
+++ receiver/receiver.ino
@@ -55,7 +55,7 @@
             } else {
                 //The received byte doesn't match. Restart the state machine.
                 currentIndex = 0;
-                Serial.println("NAK");
+                Serial.println("ERR");
             }
         } else {
             //We have received a valid header
@@ -75,7 +75,7 @@
                 } else {
                     //The checksum is invalid. Restart the state machine.
                     currentIndex = 0;
-                    Serial.println("NAK");
+                    Serial.println("ERR");
                 }
              }
         }


SetTime: don't output timer frequency tou debug pin

--- SetTime/SetTime.ino
+++ SetTime/SetTime.ino
@@ -183,6 +183,8 @@
   interrupts();             // enable all interrupts
 }

+static int flag = 0;
+
 ISR(TIMER1_OVF_vect)
 {
   TCNT1 = timerPreload;            // preload timer
@@ -194,14 +196,12 @@
     --writeBitsLeft;
     int value = ((sequence[writeCurByte] >> writeCurBit) & 1) ? HIGH : LOW;
     digitalWrite(out, value);
-    digitalWrite(debug, value);
     if (++writeCurBit == 8) {
       writeCurBit = 0;
       ++writeCurByte;
     }
   } else {
     digitalWrite(out, HIGH);
-    digitalWrite(debug, HIGH);
   }
 }


Added constant_voltage_control

Added constant_voltage_control

--- ppt.ino
+++ ppt.ino
@@ -24,6 +24,8 @@

 #define PWM_PIN             9             // the output pin for the pwm, connected to OC1A
 #define PWM_ENABLE_PIN      8             // pin used to control shutoff function of the IR2104 MOSFET driver
+#define TURN_ON_MOSFETS     digitalWrite(PWM_ENABLE_PIN, HIGH)     // enable MOSFET driver
+#define TURN_OFF_MOSFETS    digitalWrite(PWM_ENABLE_PIN, LOW)      // disable MOSFET driver

 #define ADC_SOL_AMPS_CHAN   1             // the adc channel to read solar amps
 #define ADC_SOL_VOLTS_CHAN  0             // the adc channel to read solar volts
@@ -34,13 +36,12 @@
 #define PWM_MIN             10            // the value for pwm duty cyle 0-100%
 #define PWM_START           90            // the value for pwm duty cyle 0-100%
 #define PWM_INC             1             // the value the increment to the pwm value for the ppt algorithm
-#define TURN_ON_MOSFETS     digitalWrite(PWM_ENABLE_PIN, HIGH)      // enable MOSFET driver
-#define TURN_OFF_MOSFETS    digitalWrite(PWM_ENABLE_PIN, LOW)      // disable MOSFET driver

 #define AVG_NUM             8             // number of iterations of the adc routine to average the adc readings
 #define SOL_AMPS_SCALE      0.74          // the scaling value for raw adc reading to get solar amps scaled by 100 [(1/(0.005*(3.3k/25))*(5/1023)*100]
 #define SOL_VOLTS_SCALE     2.7           // the scaling value for raw adc reading to get solar volts scaled by 100 [((10+2.2)/2.2)*(5/1023)*100]
 #define BAT_VOLTS_SCALE     2.7           // the scaling value for raw adc reading to get battery volts scaled by 100 [((10+2.2)/2.2)*(5/1023)*100]
+
 #define ONE_SECOND          50000         // count for number of interrupt in 1 second on interrupt period of 20us
 #define LOW_SOL_WATTS       500           // value of solar watts scaled by 100 so this is 5.00 watts
 #define MIN_SOL_WATTS       100           // value of solar watts scaled by 100 so this is 1.00 watts
@@ -53,17 +54,14 @@

 //------------------------------------------------------------------------------------------------------
 // global variables
-struct {
-   int sol_amps;                         // solar amps scaled by 100
-   int sol_volts;                        // solar volts scaled by 100
-   int bat_volts;                        // battery volts scaled by 100
-   int sol_watts;                        // solar watts scaled by 100
-   int pwm_duty;
+struct system_states_t {
+   int sol_amps;                         // solar amps in 10 mV (scaled by 100)
+   int sol_volts;                        // solar volts in 10 mV (scaled by 100)
+   int bat_volts;                        // battery volts in 10 mV (scaled by 100)
+   int sol_watts;                        // solar watts in 10 mV (scaled by 100)
+   uint8_t pwm_duty;
 } power_status;

-
-//int count = 0;
-//int pwm = 0;                          //pwm duty cycle 0-100%
 unsigned int seconds = 0;             // seconds from timer routine

 enum charger_mode {off, on, bulk, bat_float} charger_state;    // enumerated variable that holds state for charger state machine
@@ -75,7 +73,7 @@
 //------------------------------------------------------------------------------------------------------
 void setup()                            // run once, when the sketch starts
 {
-  Serial.begin(9600);                  // open the serial port at 9600 bps:
+  Serial.begin(115200);                  // open the serial port at 9600 bps:
   pinMode(PWM_ENABLE_PIN, OUTPUT);     // sets the digital pin as output
   Timer1.initialize(20);               // initialize timer1, and set a 20uS period
   Timer1.pwm(PWM_PIN, 0);              // setup pwm on pin 9, 0% duty cycle
@@ -334,24 +332,58 @@
 // the serial port takes. You can speed that up by speeding up the baud rate.
 // You can also run the commented out code and the charger routines will run once a second.
 //------------------------------------------------------------------------------------------------------
+
+void constant_power_control(int target_watts) {
+  static int old_sol_watts = 0;
+
+  int current_watts = power_status.sol_watts;
+  int current_pwm = power_status.pwm_duty;
+
+  if (target_watts < current_watts) {
+    current_pwm -= PWM_INC;
+  } else if (target_watts > current_watts) {
+    current_pwm += PWM_INC;
+  }
+
+  set_pwm_duty(current_pwm);
+}
+
+void constant_voltage_control(int target_voltage) {
+  static int old_bat_volt = 0;
+
+  int current_voltage = power_status.bat_volts;
+  int current_pwm = power_status.pwm_duty;
+
+  if (target_voltage < current_voltage) {
+    current_pwm -= PWM_INC;
+  } else if (target_voltage > current_voltage) {
+    current_pwm += PWM_INC;
+  }
+
+  set_pwm_duty(current_pwm);
+}
+
 void loop()                          // run over and over again
 {
   read_data();                         //read data from inputs
   //run_charger();                      //run the charger state machine
   print_data();                       //print data

+  static int current_power = 2;
   static unsigned int prev_seconds = 0;        // seconds value from previous pass
-  if ((seconds - prev_seconds) > 0) {
+  if ((seconds - prev_seconds) > 2) {
     prev_seconds = seconds;		// do this stuff once a second

-    if (power_status.pwm_duty >= 90) {
-      set_pwm_duty(10);
+    if (current_power >= 1000) {
+      current_power = 100;
     }
     else {
-      set_pwm_duty(power_status.pwm_duty + 10);
+      current_power += 100;
     }
   }

+  constant_voltage_control(current_power);
+
 //    read_data();                         //read data from inputs
 //    run_charger();
 //    print_data();                       //print data


fix goForward bug

fix goForward bug

--- pug.ino
+++ pug.ino
@@ -31,7 +31,7 @@
 int ultraBuffer[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 int ultraCount = 1;
 int ultraSum = 0;
-boolean hasEnnemy = false;
+volatile boolean hasEnnemy = false;

 volatile int loopCounter = 0;
 volatile int leftSpeed = 100;
@@ -106,7 +106,7 @@
  * @param int duration - duration in Ms
  **/
 void goForward(int speed, int duration) {
-  for ( int i = 0; i < duration * 10; i++) {
+  for ( int i = 0; i < duration / 10; i++) {
     if (hasEnnemy) {
       setLeftSpeed(-1); // Break!
       setRightSpeed(-1); // Break!


new pinglibrary for ultrasonic sensor
delay in setLed to let leds apply changes
delay in faiBeep to avoid noise recognition
removed (almost) all debug instructions

Signed-off-by: Suxsem <ffd6300eb7bdb77a8898cee133390f47c44c6773@live.com>

--- Domo.ino
+++ Domo.ino
@@ -1,8 +1,11 @@
 #include <dht.h>
 #include <SimpleTimer.h>
+#include <NewPing.h>

 #define DISTANZA_RILEVAZIONE 200 //cm
 #define DISTANZA_ATTIVAZIONE 8 //cm
+#define DISTANZA_MAX 250 //cm
+//#define RIPETI_DISTANZA 3
 #define DIFFERENZA_LUCE 60 //todo
 #define PERIODO_DI_GRAZIA 20 //s
 #define SUONA_PER 20 //s
@@ -36,6 +39,7 @@

 dht DHT;
 SimpleTimer timer;
+NewPing sonar(triggerPin, echoPin, DISTANZA_MAX);

 byte touchCount = 0;
 boolean alarmOn = false;
@@ -221,6 +225,7 @@
   analogWrite(rgbrPin, pow (2, (r / R)) - 1);
   analogWrite(rgbgPin, pow (2, (g / R)) - 1);
   analogWrite(rgbbPin, pow (2, (b / R)) - 1);
+  delay(100);
   lastPhoto = analogRead(photoPin);
   mRgb.toCharArray(lastLed, 7);
   mqttPublish(String(MQTTid) + "/Led", mRgb, 1);
@@ -231,6 +236,7 @@
   digitalWrite(buzzPin, HIGH);
   delay(millis);
   digitalWrite(buzzPin, LOW);
+  delay(20);
   attachInterrupt(0, rilevaRumore, FALLING);
 }

@@ -239,7 +245,6 @@
 }

 void rilevaRumore() {
-  digitalWrite(statusPin, HIGH); //todo remove
   rumoreRilevato = true;
 }

@@ -258,7 +263,6 @@
   pinMode(rgbrPin, OUTPUT);
   pinMode(rgbgPin, OUTPUT);
   pinMode(rgbbPin, OUTPUT);
-  pinMode(triggerPin, OUTPUT);

   lastPhoto = analogRead(photoPin);
   faiBeep(10); //contiene attachInterrupt per rilevare il rumore
@@ -273,12 +277,9 @@
   while(!connected);

   //misuro la distanza
-  digitalWrite(triggerPin, LOW);
-  delayMicroseconds(5);
-  digitalWrite(triggerPin, HIGH);
-  delayMicroseconds(10);
-  digitalWrite(triggerPin, LOW);
-  unsigned int dist = pulseIn(echoPin, HIGH, 800000) / 58; //todo vedere se si può abbassare
+  //  unsigned int dist = sonar.convert_cm(sonar.ping_median(RIPETI_DISTANZA));
+  unsigned int dist = sonar.ping_cm();
+
   if (dist == 0)
     dist = 999;

@@ -314,7 +315,6 @@
   int absTemp = photo - lastPhoto; //abs() non può contenere operazioni all'interno
   boolean mRumoreRilevato = rumoreRilevato;
   rumoreRilevato = false;
-  digitalWrite(statusPin, LOW); //todo togliere

   if ((dist < DISTANZA_RILEVAZIONE
       || abs(absTemp) > DIFFERENZA_LUCE
@@ -326,7 +326,7 @@
       if (connected) {
         mqttPublish(String(MQTTid) + "/Scattato", "1", 0);
         if (dist < DISTANZA_RILEVAZIONE) //todo remove
-          mqttPublish(String(MQTTid) + "/Motivo", "distanza", 0);
+          mqttPublish(String(MQTTid) + "/Motivo", "distanza: " + String(dist), 0);
         if (abs(absTemp) > DIFFERENZA_LUCE)
           mqttPublish(String(MQTTid) + "/Motivo", "luce", 0);
         if (mRumoreRilevato)
@@ -345,7 +345,7 @@
   lastPhoto = photo;

   if (alarmScattato)
-    //faiBeep(500);
+    faiBeep(500);
     delay(500); //todo togliere commento

   if (skip % 10 == 0 && connected) { //todo trovare skip giusto


confirm to norms

confirm to norms


added method example of sending 2 bytes

added method example of sending 2 bytes

--- SIPO.ino
+++ SIPO.ino
@@ -1,16 +1,39 @@
 #include <SPI.h>
+
+int PinSIPOSet = 10;

 void setup() {
   SPI.begin();
 }

 void loop() {
+  //BitCounter(5);
+  //SendByte(1);
+  Send2Bytes(2,1);
+}
+
+void SendByte(int K)
+{
+  digitalWrite (PinSIPOSet, LOW);
+  SPI.transfer(K);
+  // if you are daisychaining, add 2nd, 3rd SPI.transfer(next_byte_of_data )
+  digitalWrite (PinSIPOSet,  HIGH);
+}
+
+void Send2Bytes(int K, int K2)
+{
+  digitalWrite (PinSIPOSet, LOW);
+  // Keep in mind that the first shifter needs to shif the data to the second shifter
+  // Data needs to be sent in the correct order that is back to front
+  SPI.transfer(K);
+  SPI.transfer(K2);
+  digitalWrite (PinSIPOSet,  HIGH);
+}
+
+void BitCounter(int waitTime){
   for(int K=0;K<256;K++)
   {
-    digitalWrite (10, LOW);
-    SPI.transfer(K);
-    // if you are daisychaining, add 2nd, 3rd SPI.transfer(next_byte_of_data )
-    digitalWrite (10,  HIGH);
-    delay(200);
+    SendByte(K);
+    delay(waitTime);
   }
 }


adding in terrilmoore's code comments

adding in terrilmoore's code comments

--- examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino
+++ examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino
@@ -215,8 +215,11 @@
         byte humidHigh = highByte(payloadHumid);
         payload[2] = humidLow;
         payload[3] = humidHigh;
-
-        // Prepare upstream data transmission at the next possible time.
+
+        // prepare upstream data transmission at the next possible time.
+        // transmit on port 1 (the first parameter); you can use any value from 1 to 223 (others are reserved).
+        // don't request an ack (the last parameter, if not zero, requests an ack from the network).
+        // Remember, acks consume a lot of network resources; don't ask for an ack unless you really need it.
         LMIC_setTxData2(1, payload, sizeof(payload)-1, 0);
     }
     // Next TX is scheduled after TX_COMPLETE event.
@@ -234,9 +237,14 @@
     os_init();
     // Reset the MAC state. Session and pending data transfers will be discarded.
     LMIC_reset();
-
+    // Disable link-check mode and ADR, because ADR tends to complicate testing.
     LMIC_setLinkCheckMode(0);
+    // Set the data rate to Spreading Factor 7.  This is the fastest supported rate for 125 kHz channels, and it
+    // minimizes air time and battery power. Set the transmission power to 14 dBi (25 mW).
     LMIC_setDrTxpow(DR_SF7,14);
+    // in the US, with TTN, it saves join time if we start on subband 1 (channels 8-15). This will
+    // get overridden after the join by parameters from the network. If working with other
+    // networks or in other regions, this will need to be changed.
     LMIC_selectSubBand(1);

     // Start job (sending automatically starts OTAA too)
@@ -244,5 +252,10 @@
 }

 void loop() {
-    os_runloop_once();
-}
+  // we call the LMIC's runloop processor. This will cause things to happen based on events and time. One
+  // of the things that will happen is callbacks for transmission complete or received messages. We also
+  // use this loop to queue periodic data transmissions.  You can put other things here in the `loop()` routine,
+  // but beware that LoRaWAN timing is pretty tight, so if you do more than a few milliseconds of work, you
+  // will want to call `os_runloop_once()` every so often, to keep the radio running.
+  os_runloop_once();
+}


fingerprint更新

fingerprint更新

--- RssDisp/RssDisp.ino
+++ RssDisp/RssDisp.ino
@@ -56,7 +56,14 @@
   const char *figerprint;
 } site_t;

-const char *yahoo_fingerprint = "2c4cc1be32d3a6021bbce7f918290af709972130";
+const char *yahoo_fingerprint =
+    "1E8BB7F438189245FCC779DD4DA9912BBC2F70CF";  // SHA-1 until 2021/8/27
+const char *impress_fingerprint =
+    "01A4812C19BE0854B91500A627CC17796F4F4CDE";  // SHA-1 until 2022//30
+const char *jiji_fingerprint =
+    "F9F5CB7D14E5B4A104352DD9670301A1BF350E10";  // SHA-1 until 2020/12/8
+const char *asahi_fingerprint =
+    "921342996D60845FDC1BCE7E9F700D20047A3E0F";// SHA-1 until 2021/9/5

 const site_t site[] = {
     {"Yahoo Sports", 10, "https://news.yahoo.co.jp/pickup/sports/rss.xml",
@@ -66,13 +73,10 @@
     {"sankei", 10, "https://headlines.yahoo.co.jp/rss/san-dom.xml",
      yahoo_fingerprint},
     {"Impress", 10, "https://www.watch.impress.co.jp/data/rss/1.0/ipw/feed.rdf",
-     "f90c73b7caeb1bf496a1ff539e4ca5ae414d04ce"},
-    {"jiji", 10, "https://www.jiji.com/rss/ranking.rdf",
-     "f9f5cb7d14e5b4a104352dd9670301a1bf350e10"},
-    {"Make", 5, "https://feeds.feedburner.com/make_jp",
-     "058b82bcfeb3f43afa4f7745d2d048e36e29bdce"},
+     impress_fingerprint},
+    {"jiji", 10, "https://www.jiji.com/rss/ranking.rdf", jiji_fingerprint},
     {"asahi", 10, "https://rss.asahi.com/rss/asahi/newsheadlines.rdf",
-     "5a5e16a96325bb81694d2b8e6f302a2e0945ee17"},
+     asahi_fingerprint},
     {NULL},
 };

@@ -137,7 +141,7 @@
   if (pSite->name == NULL) {
     unsigned long tNow = millis();
     pSite = site;
-    Serial.printf("loop time: %d sec\n", (tNow - tStart)/1000);
+    Serial.printf("loop time: %d sec\n", (tNow - tStart) / 1000);
   }
 }