lot of fixes, initial previousMillis logic

--- f-preform-02/f-preform-02.ino
+++ f-preform-02/f-preform-02.ino
@@ -31,12 +31,49 @@
 #define B24_PIN A8
 #define B25_PIN A9

+#define L101_PIN 24
+#define L102_PIN 23
+#define L103_PIN 26
+#define L104_PIN 45
+#define L105_PIN 43
+#define L106_PIN 42
+#define L107_PIN 36
+#define L108_PIN 35
+#define L109_PIN 39
+#define L110_PIN 32
+#define L111_PIN 31
+#define L112_PIN 28
+#define L201_PIN 29
+#define L202_PIN 30
+#define L203_PIN 33
+#define L204_PIN 37
+#define L205_PIN 34
+#define L206_PIN 44
+#define L207_PIN 38
+#define L208_PIN 41
+#define L209_PIN 40
+#define L210_PIN 25
+#define L211_PIN 27
+#define L212_PIN 22
+#define L1R_PIN 2
+#define L1G_PIN 3
+#define L1B_PIN 4
+#define L2R_PIN 5
+#define L2G_PIN 6
+#define L2B_PIN 7
+#define L3R_PIN 8
+#define L3G_PIN 9
+#define L3B_PIN 10
+#define L4R_PIN 11
+#define L4G_PIN 12
+#define L4B_PIN 13
+
 class Button {
   public:
     Button(byte pin, byte timeButton);  // конструктор
     boolean flagPress;    // признак кнопка в нажатом состоянии
     boolean flagClick;    // признак нажатия кнопки (клик)
-    void scanState();    // метод проверки состояние сигнала
+    boolean scanState();    // метод проверки состояние сигнала
     void filterAvarage(); // метод фильтрации сигнала по среднему значению
     void setPinTime(byte pin, byte timeButton); // метод установки номера вывода и времени (числа) подтверждения
   private:
@@ -73,8 +110,8 @@
 // при нажатой кнопке flagPress= true
 // при отжатой кнопке flagPress= false
 // при нажатии на кнопку flagClick= true
-void Button::scanState() {
-
+boolean Button::scanState() {
+  boolean _stateChanged = false;
   if ( flagPress != digitalRead(_pin) ) {
     //  состояние кнопки осталось прежним
     _buttonCount = 0; // сброс счетчика подтверждений
@@ -88,10 +125,12 @@
       // состояние кнопки стало устойчивым
       flagPress = ! flagPress; // инверсия признака состояния
       _buttonCount = 0; // сброс счетчика подтверждений
+      _stateChanged = true;

       if ( flagPress == true ) flagClick = true; // признак клика кнопки
     }
   }
+  return (_stateChanged);
 }
 // метод установки номера вывода и времени подтверждения
 void Button::setPinTime(byte pin, byte timeButton)  {
@@ -126,7 +165,7 @@
   button13.scanState();
   button14.scanState();
   button15.scanState();
-  button21.scanState();  // вызов метода ожидания стабильного состояния для кнопки
+  button21.scanState();
   button22.scanState();
   button23.scanState();
   button24.scanState();
@@ -135,12 +174,15 @@

 LiquidCrystal_I2C lcd(0x3f, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

-int b11 = A0, b12 = A1, b13 = A2, b14 = A3, b15 = A4, b21 = A5, b22 = A6, b23 = A7, b24 = A8, b25 = A9, //buttons
-    l101 = 24, l102 = 23, l103 = 26, l104 = 45, l105 = 43, l106 = 42, l107 = 36, l108 = 35, l109 = 39, l110 = 32, //leds
-    l111 = 31, l112 = 28, l201 = 29, l202 = 30, l203 = 33, l204 = 37, l205 = 34, l206 = 44, l207 = 38, l208 = 41,
-    l209 = 40, l210 = 25, l211 = 27, l212 = 22,
-    l1r = 2, l1g = 3, l1b = 4, l2r = 5, l2g = 6, l2b = 7, l3r = 8, l3g = 9, l3b = 10, l4r = 11, l4g = 12, l4b = 13; //rgb leds
-int field[12][5] = {
+const int b11 = B11_PIN, b12 = B12_PIN, b13 = B13_PIN, b14 = B14_PIN, b15 = B15_PIN, b21 = B21_PIN, b22 = B22_PIN, b23 = B23_PIN, //buttons
+          b24 = B24_PIN, b25 = B25_PIN,
+          l101 = L101_PIN, l102 = L102_PIN, l103 = L103_PIN, l104 = L104_PIN, l105 = L105_PIN, l106 = L106_PIN, l107 = L107_PIN, //leds
+          l108 = L108_PIN, l109 = L109_PIN, l110 = L110_PIN, l111 = L111_PIN, l112 = L112_PIN, l201 = L201_PIN, l202 = L202_PIN,
+          l203 = L203_PIN, l204 = L204_PIN, l205 = L205_PIN, l206 = L206_PIN, l207 = L207_PIN, l208 = L208_PIN, l209 = L209_PIN,
+          l210 = L210_PIN, l211 = L211_PIN, l212 = L212_PIN,
+          l1r = L1R_PIN, l1g = L1G_PIN, l1b = L1B_PIN, l2r = L2R_PIN, l2g = L2G_PIN, l2b = L2B_PIN, l3r = L3R_PIN, l3g = L3G_PIN, //rgb leds
+          l3b = L3B_PIN, l4r = L4R_PIN, l4g = L4G_PIN, l4b = L4B_PIN;
+const int field[12][5] = {
   { -1, -1,   -1,   -1,   -1},
   { -1, 1,    l108, 3,    -1},
   { -1, l210, l204, l201, -1},
@@ -155,12 +197,18 @@
   { -1, -1,   -1,   -1,   -1}
 };
 int game = GAME_START,
-    x, y, vector;
+    x = -1, y = -1, vector = 0, xPrev = -1, yPrev = -1, vectorPrev = 0;
+unsigned long currentMillis = 0, previousMillis = 0;
+boolean ballkick = false;

 void setup() {
   // put your setup code here, to run once:
   MsTimer2::set(2, timerInterupt); // задаем период прерывания по таймеру 2 мс
-  MsTimer2::start();              // разрешаем прерывание по таймеру
+  MsTimer2::start();              // разрешаем прерывание по таймеру
+
+  currentMillis = millis();
+  previousMillis = currentMillis;
+
   randomSeed(analogRead(RANDOMSEED_PIN));
   pinMode(SPEAKER_PIN, OUTPUT);

@@ -177,58 +225,7 @@
   }
 }

-void start_game() {
-  game = 999;
-  lcd.init();
-  button11.flagClick = 0;
-  button21.flagClick = 0;
-  for (int j = 1000; j > 0; j = j - 100 ) {
-    for (int i = 2; i <= 13; i++) { //init rgb leds
-      digitalWrite(i, HIGH);
-    }
-    delay(j);
-    for (int i = 2; i <= 13; i++) { //init rgb leds
-      //lcd.setCursor(12, 1); lcd.print(analogRead(b11));
-      //lcd.setCursor(15, 1); lcd.print(analogRead(b21));
-      digitalWrite(i, LOW);
-    }
-    delay(j);
-
-    if (button11.flagClick == 1 && button21.flagClick == 1) {
-      lcd.setCursor(0, 0); lcd.print("Both plrs false start!");
-      return;
-    }
-    else if (button21.flagClick == 1) {
-      lcd.setCursor(0, 0); lcd.print("Red plr false start! Green plr wins!");
-      game = GAME_PERFORMED;
-      x = 5; y = 2; vector = 1;
-      return;
-    }
-    else if (button11.flagClick == 1) {
-      lcd.setCursor(0, 0); lcd.print("Green plr false start! Red plr wins!");
-      game = GAME_PERFORMED;
-      x = 6; y = 2; vector = -1;
-      return;
-    }
-  }
-  tone(SPEAKER_PIN, 3000, 250);
-  while (1) {
-    if (button11.flagClick == 1) {
-      lcd.setCursor(0, 0); lcd.print("Green plr wins!");
-      game = GAME_PERFORMED;
-      x = 5; y = 2; vector = 1;
-      return;
-    }
-    else if (button21.flagClick == 1) {
-      lcd.setCursor(0, 0); lcd.print("Red plr wins!");
-      game = GAME_PERFORMED;
-      x = 6; y = 2; vector = -1;
-      return;
-    }
-  }
-}
-
-void in_game() {
+void reset_buttons_flagClick() {
   button11.flagClick = 0;
   button12.flagClick = 0;
   button13.flagClick = 0;
@@ -239,20 +236,98 @@
   button23.flagClick = 0;
   button24.flagClick = 0;
   button25.flagClick = 0;
+}
+
+void newxy (int _x, int _y, int _vector) {
+  xPrev = x;
+  yPrev = y;
+  vectorPrev = vector;
+  x = _x;
+  y = _y;
+  vector = _vector;
+  currentMillis = millis();
+  previousMillis = currentMillis;
+  reset_buttons_flagClick();
   digitalWrite(field[x][y], HIGH);
-  delay(1000);
-  digitalWrite(field[x][y], LOW );
+  digitalWrite(field[xPrev][yPrev], LOW);
+}
+
+void start_game() {
+  game = 999;
+  lcd.init();
+  reset_buttons_flagClick;
+  for (int j = random(100, 1000); j > 0; j = j - 100 ) {
+    for (int i = 2; i <= 13; i++) { //init rgb leds
+      digitalWrite(i, HIGH);
+    }
+    delay(j);
+    for (int i = 2; i <= 13; i++) { //init rgb leds
+      //lcd.setCursor(12, 1); lcd.print(analogRead(b11));
+      //lcd.setCursor(15, 1); lcd.print(analogRead(b21));
+      digitalWrite(i, LOW);
+    }
+    delay(j);
+
+    if (button11.flagClick == 1 && button21.flagClick == 1) {
+      lcd.setCursor(0, 0); lcd.print("Both plrs false start!");
+      return;
+    }
+    else if (button21.flagClick == 1) {
+      lcd.setCursor(0, 0); lcd.print("Red plr false start! Green plr wins!");
+      game = GAME_PERFORMED;
+      newxy(5, 2, 1);
+      return;
+    }
+    else if (button11.flagClick == 1) {
+      lcd.setCursor(0, 0); lcd.print("Green plr false start! Red plr wins!");
+      game = GAME_PERFORMED;
+      newxy(6, 2, -1);
+      return;
+    }
+  }
+  tone(SPEAKER_PIN, 3000, 250);
+  reset_buttons_flagClick;
+  while (1) {
+    if (button11.flagClick == 1) {
+      lcd.setCursor(0, 0); lcd.print("Green plr wins!");
+      game = GAME_PERFORMED;
+      newxy(5, 2, 1);
+      return;
+    }
+    else if (button21.flagClick == 1) {
+      lcd.setCursor(0, 0); lcd.print("Red plr wins!");
+      game = GAME_PERFORMED;
+      newxy(6, 2, -1);
+      return;
+    }
+  }
+}
+
+void in_game() {
+  currentMillis = millis();
+  if (!ballkick && ((currentMillis - previousMillis) >= 3000)) {
+    game = GAME_OFFSIDE;
+    return;
+  }
+  else if (ballkick && ((currentMillis - previousMillis) >= 300)) {
+    newxy(x + x - xPrev, y + y - yPrev, vector);
+    return;
+  }
   if ((button14.flagClick == 1) || (button24.flagClick == 1)) {
-    x = x +  vector;
+    newxy(x + vector, y, vector);
+    ballkick = true;
   }
   if ((button13.flagClick == 1) || (button23.flagClick == 1)) {
-    x = x -  vector;
+    newxy(x - vector, y, vector);
+    ballkick = true;
   }
   if ((button12.flagClick == 1) || (button22.flagClick == 1)) {
-    y = y + vector;
+    newxy(x, y + vector, vector);
+    ballkick = true;
   }
   if ((button15.flagClick == 1) || (button25.flagClick == 1)) {
-    y = y - vector;
+    newxy(x, y - vector, vector);
+    ballkick = true;
   }
 }


null terminator is 'q'

null terminator is 'q'

--- src/arduino_demo.ino
+++ src/arduino_demo.ino
@@ -78,7 +78,7 @@
 	char c;
 	while (Serial.available() && buf_len < BUF_SIZE) {
 		c = (char)Serial.read();
-		if (c == '\n') {
+		if (c == 'q') {
 			sent_string = 1;
 			return;
 		}


Changed period to 5 min and added Vierashuone

--- src/MQTT-monitor.ino
+++ src/MQTT-monitor.ino
@@ -89,7 +89,7 @@
 unsigned long lastrun = millis();
 int state = 0;
 //#define PERIOD 5*60*1000
-#define PERIOD 3000
+#define PERIOD 5*60*1000

 bool bme_status;

@@ -171,26 +171,25 @@
 void loop() {

   if (mqtt.connected()) {
-    //  if (1) {
     if (millis() > (lastrun + PERIOD)) {
       switch(state) {
       case 0:
 	senddht22(Makuuhuone, topicMakuuhuone);
 	state++;
 	break;
-
+
       case 1:
 	sendbme(topicOlohuone);
 	state++;
 	break;
-
+
       case 2:
 	senddht22(WC, topicWC);
 	state++;
 	break;

       case 3:
-	// Foo
+	senddht22(Vierashuone, topicVierashuone);
 	state = 0;
 	lastrun = millis();
       }


servo refined

servo refined

--- src/arduino/bear.ino
+++ src/arduino/bear.ino
@@ -7,25 +7,27 @@

 const int SERVO = 2;

-const int BLUE = 3;
-const int GREEN = 5;
-const int RED = 6;
+// const int BLUE = 3;
+// const int GREEN = 5;
+// const int RED = 6;

 const int LED_1 = 9;
 const int LED_2 = 8;

-const int SERVO_MAX_ANGLE = 30;
 const int SERVO_BASE_ANGLE = 60;
+const int SERVO_MAX_ANGLE = 60;

 const int BLINK_INTERVAL = 150; // in milliseconds
 const int DEGREE_STEP = 1;
-const int SERVO_INTERVAL = 10; // in milliseconds
+const int SERVO_INTERVAL = 8; // in milliseconds

 Servo myservo; // create servo object to control a servo

 int current_bpm = 120;
 int current_angle = 0;
+int current_max_angle = 60;

+// time variables
 bool current_velocity_positive = true;
 unsigned long previous_blink_downbeat = 0;
 unsigned long previous_blink_upbeat = 0;
@@ -39,17 +41,17 @@
   Serial.begin(9600);

   // initialize digital pin LED_BUILTIN as an output.
-  pinMode(LED_BUILTIN, OUTPUT);
-  pinMode(RED, OUTPUT);
-  pinMode(GREEN, OUTPUT);
-  pinMode(BLUE, OUTPUT);
+  // pinMode(LED_BUILTIN, OUTPUT);
+  // pinMode(RED, OUTPUT);
+  // pinMode(GREEN, OUTPUT);
+  // pinMode(BLUE, OUTPUT);
   pinMode(LED_1, OUTPUT);
   pinMode(LED_2, OUTPUT);

-  digitalWrite(LED_BUILTIN, LOW);
-  digitalWrite(RED, LOW);
-  digitalWrite(GREEN, LOW);
-  digitalWrite(BLUE, LOW);
+  // digitalWrite(LED_BUILTIN, LOW);
+  // digitalWrite(RED, LOW);
+  // digitalWrite(GREEN, LOW);
+  // digitalWrite(BLUE, LOW);
   digitalWrite(LED_1, LOW);
   digitalWrite(LED_2, LOW);

@@ -77,7 +79,6 @@
     switch (translate_ir()) {
       case 1: flash_beat(true, current_millis); break;
       case 2: flash_beat(false, current_millis); break;
-//      case 7: test_speed(75); break;
       default: break;
     }
     irrecv.resume();
@@ -88,41 +89,44 @@

   if (previous_servo_move && current_millis - previous_servo_move >= SERVO_INTERVAL) {
     // check if it should return
-    if (current_angle >= 60000 / current_bpm * 4 / 5 / (2 * SERVO_INTERVAL / DEGREE_STEP)) {
-      --current_angle;
-      current_velocity_positive = !current_velocity_positive;
-    }
+    if (current_angle >= current_max_angle) current_velocity_positive = false;
+
     // move the servo
     current_angle += (current_velocity_positive ? 1 : -1) * DEGREE_STEP;
     myservo.write(SERVO_BASE_ANGLE + current_angle);
+
     if (current_angle == 0 && !current_velocity_positive) {
-      previous_servo_move = 0;
+      previous_servo_move = 0; // finish moving
     } else {
       previous_servo_move += SERVO_INTERVAL;
     }
   }
 }

+int get_max_angle() {
+  return 60000 / current_bpm / (2 * SERVO_INTERVAL / DEGREE_STEP);
+}
+
 void flash_beat(bool is_downbeat, unsigned long current_millis) {
-  digitalWrite(LED_BUILTIN, HIGH);
-  digitalWrite(is_downbeat ? RED : BLUE, HIGH);
+  // digitalWrite(LED_BUILTIN, HIGH);
+  // digitalWrite(is_downbeat ? RED : BLUE, HIGH);
   digitalWrite(is_downbeat ? LED_1 : LED_2, HIGH);
   start_servo(current_millis);
   (is_downbeat ? previous_blink_downbeat : previous_blink_upbeat) = current_millis;
 }

 void stop_beat() {
-  digitalWrite(LED_BUILTIN, LOW);
-  digitalWrite(RED, LOW);
-  digitalWrite(GREEN, LOW);
-  digitalWrite(BLUE, LOW);
+  // digitalWrite(LED_BUILTIN, LOW);
+  // digitalWrite(RED, LOW);
+  // digitalWrite(GREEN, LOW);
+  // digitalWrite(BLUE, LOW);
   digitalWrite(LED_1, LOW);
   digitalWrite(LED_2, LOW);
 }

 void stop_blink(bool is_downbeat) {
-  digitalWrite(LED_BUILTIN, LOW);
-  digitalWrite(is_downbeat ? RED : BLUE, LOW);
+  // digitalWrite(LED_BUILTIN, LOW);
+  // digitalWrite(is_downbeat ? RED : BLUE, LOW);
   digitalWrite(is_downbeat ? LED_1 : LED_2, LOW);
   (is_downbeat ? previous_blink_downbeat : previous_blink_upbeat) = 0;
 }
@@ -130,27 +134,9 @@
 void start_servo(unsigned long current_millis) {
   myservo.write(SERVO_BASE_ANGLE);
   current_angle = 0;
+  current_max_angle = min(SERVO_MAX_ANGLE, get_max_angle());
   current_velocity_positive = true;
   previous_servo_move = current_millis;
-}
-
-void switch_servo() {
-  int num_steps = 4;
-  int base_angle = 90;
-  int step = SERVO_MAX_ANGLE / num_steps;
-
-  int interval = 60000 / current_bpm;
-  int delay_ms = interval * 4 / 5 / (num_steps * 2);
-
-  for (int i = 0; i < num_steps; ++i) {
-    myservo.write(base_angle + i * step);
-    delay(delay_ms);
-  }
-  for (int i = num_steps; i > 0; --i) {
-    myservo.write(base_angle + i * step);
-    delay(delay_ms);
-  }
-  myservo.write(base_angle);
 }

 int translate_ir() {
@@ -181,12 +167,3 @@
     default: return -1; // other button
   }
 }
-
-void test_speed(int angle) {
-  for (int i = 0; i < 20; ++i) {
-    myservo.write(90 - angle);
-    delay(300);
-    myservo.write(90 + angle);
-    delay(300);
-  }
-}


funguje okamzity stop strun na tap, trochu sliding

--- guitar-arduino.ino
+++ guitar-arduino.ino
@@ -31,6 +31,7 @@
 #define LASER_THOLD 1000 //[us]
 #define STOP_THOLD 500000 //[us]
 #define FRET_SLIDING_THOLD 500000 //[us]
+#define PITCH_HALFTONE 12 * 4
 #define VEL_TIME_OUT_OF_RANGE 200000 //[us]
 #define VEL_TIME_MIN 6000
 #define VEL_TIME_MAX 25000
@@ -116,10 +117,14 @@
 //------ handle events --------

 inline void send_fret_sliding_event(int dir, int f) {
-  pitch += dir * 10;
-
-  if (dir == 0)
+  pitch += dir * PITCH_HALFTONE;
+
+  if (dir == 0) {
     pitch = 0;
+  }
+  else {
+    send_vibrato_event(pitch);
+  }

 #ifdef DBG
   Serial.print(f);
@@ -144,16 +149,20 @@
   Serial.println(f * 10 + s);
 #endif

-
-/*
   //check for sliding
-  unsigned long diff_us = micros() - fret_status_last_f_us;
-  int dir = f - fret_status_last_used; //0: no sliding, -1:sliding outward: 1: sliding inward
-  if (diff_us < FRET_SLIDING_THOLD) {
-    send_fret_sliding_event(dir, f);
-  }
-*/
-  fret_status_last_used = f;
+  if (s ==0) {  //jen pro vybrnkavani
+    unsigned long diff_us = micros() - fret_status_last_f_us;
+    if (diff_us < FRET_SLIDING_THOLD) {
+      int dir = f - fret_status_last_used; //0: no sliding, -1:sliding outward: 1: sliding inward
+      send_fret_sliding_event(dir, f);
+    }
+    else {
+      send_fret_sliding_event(0, f);  //reset pitch
+    }
+    fret_status_last_used = f;
+    fret_status_last_f_us = micros();
+  }
+
   //set status
   if (pressed) {
     fret_status_last_f = f;
@@ -163,14 +172,6 @@
     fret_status_last_f = UNDEF;
     fret_status_last_s = UNDEF;
   }
-/*
-  //midi fake controller for debuging
-  midiEventPacket_t packet = {0x0B, 0xB0, 0x50, s};
-  MidiUSB.sendMIDI(packet);
-  packet = {0x0B, 0xB0, 0x51, f};
-  MidiUSB.sendMIDI(packet);
-  MidiUSB.flush();
-*/
 }

@@ -183,12 +184,21 @@
   Serial.println(l_idx);
 #endif

-  if (laser_blocked) {  // finger is muting string(laser)
-      midiEventPacket_t packet0 = {0x08, 0x80 | 0, played_notes[l_idx], 0};
+  bool sp = is_string_pressed_on_active_fret(0);
+  if (laser_blocked && sp) {
+    for (int i = 0; i < 6; i++) { //mute all strings if vybrnkavaci struna is pressed
+      midiEventPacket_t packet0 = {0x08, 0x80 | 0, played_notes[i], 0};
       MidiUSB.sendMIDI(packet0);
       MidiUSB.flush();
-  }
-
+      delay(2);
+    }
+  }
+  if (laser_blocked && !sp) {  // finger is muting current laser
+    midiEventPacket_t packet0 = {0x08, 0x80 | 0, played_notes[l_idx], 0};
+    MidiUSB.sendMIDI(packet0);
+    MidiUSB.flush();
+    delay(2);
+  }
   if (!laser_blocked) {  //finger is releasing string(laser)
     if (pitch != 0) {
       send_fret_sliding_event(0, 0);  //reset pitch
@@ -233,10 +243,6 @@
     //noteOn
     uint8_t note = get_note(l_idx);
     if (note != 0) {
-      //midiEventPacket_t packet0 = {0x08, 0x80 | 0, played_notes[l_idx], 0};  //note off last played note
-      //MidiUSB.sendMIDI(packet0);
-      //MidiUSB.flush();
-      //delay(2); //next midi command gets omitted if delay is not present, because midiusb is ignoring the fact, just change it
       played_notes[l_idx] = note;
       midiEventPacket_t packet = {0x09, 0x90 | 0, note, velocity};  //channel, pitch, velocity
       MidiUSB.sendMIDI(packet);
@@ -258,8 +264,9 @@
   }
 }

-inline void send_stop_tone_event(boolean stop_pressed) {
-
+inline void send_stop_tone_event(boolean stop_pressed, boolean skip_setup) {
+
+  if (!skip_setup) {
     //find if instrument selection is pressed (top string), fret selects instrument
     for (int i = 0; i < 13; i++) {
       if ((fret_status[i][5] == true)) {
@@ -275,6 +282,7 @@
         selected_capo_id = (i < 8) ? i + 1 : 0;  //frets 9 and more removes capo
       }
     }
+  }

   if (stop_pressed) {
     //noteOff for all lasers
@@ -368,6 +376,7 @@

       if (all_lasers_blocked()) {  //detect hand over all lasers, stop sound
         set_all_laser_unblocked();
+        send_stop_tone_event(true, true);
       }

       laser_status_last_update_us[l_idx] = us;
@@ -409,7 +418,7 @@
   if (stop == LOW) { //confirm the actual value on pin
     unsigned long us = micros();
     if (us > (stop_status_last_update_us + STOP_THOLD)) {
-      send_stop_tone_event(true);
+      send_stop_tone_event(true, false);
       stop_status_last_update_us = us;
     }
   }


latest with RCMM from microtherion

latest with RCMM from microtherion


WIP: Ball animation. Need to fix bug with windowing.

--- FancyEPD_Demo/FancyEPD_Demo.ino
+++ FancyEPD_Demo/FancyEPD_Demo.ino
@@ -22,16 +22,50 @@
 	}
 }

+void loop() {
+	loop_anim();
+	//loop_images();
+}
+
 // Animation
-/*
 int16_t ball_x = 0;
+int16_t dir_x = 4;
 int16_t ball_y = 0;
-void loop() {
+int16_t dir_y = 4;
+const int16_t BALL_SZ = 32;
+
+void loop_anim() {
+	// Erase old position
+	epd.fillCircle(ball_x + BALL_SZ / 2, ball_y + BALL_SZ / 2, BALL_SZ / 2, 0x0);
+
+	ball_x += dir_x;
+	ball_y += dir_y;
+
+	if ((ball_x + BALL_SZ) >= epd.width()) {
+		dir_x *= -1;
+	} else if (ball_x <= 0) {
+		dir_x *= -1;
+	}
+
+	if ((ball_y + BALL_SZ) >= epd.height()) {
+		dir_y *= -1;
+	} else if (ball_y <= 0) {
+		dir_y *= -1;
+	}
+
+	// Draw new position
+	epd.fillCircle(ball_x + BALL_SZ / 2, ball_y + BALL_SZ / 2, BALL_SZ / 2, 0xff);
+
+	epd.markEntireDisplayDirty();
+
+	epd.setBorderColor(0xff);
+
+	epd.updateScreen(k_update_no_blink);
+
 	delay(1000);
 }
-*/

-void loop() {
+void loop_images() {
 	if (DO_ROTATION) epd.setRotation(0);
 	drawCircles();
 	drawLabel("Update:\n builtin_refresh");


Version 1.9b 3rd September 2017

Version 1.9b 3rd September 2017

Incorporates several improvements from the SAM/SAMD version and several bug fixes.

--- ulisp.ino
+++ ulisp.ino
@@ -1,11 +1,12 @@
-/* uLisp Version 1.9 - www.ulisp.com
-   David Johnson-Davies - www.technoblogy.com - 16th May 2017
+/* uLisp Version 1.9b - www.ulisp.com
+   David Johnson-Davies - www.technoblogy.com - 3rd September 2017

    Licensed under the MIT license: https://opensource.org/licenses/MIT
 */

 #include <setjmp.h>
 #include <SPI.h>
+#include <limits.h>

 // Compile options

@@ -35,9 +36,9 @@
 #define characterp(x)      ((x)->type == CHARACTER)
 #define streamp(x)         ((x)->type == STREAM)

-#define mark(x)            (car(x) = (object *)(((unsigned int)(car(x))) | MARKBIT))
-#define unmark(x)          (car(x) = (object *)(((unsigned int)(car(x))) & ~MARKBIT))
-#define marked(x)          ((((unsigned int)(car(x))) & MARKBIT) != 0)
+#define mark(x)            (car(x) = (object *)(((uintptr_t)(car(x))) | MARKBIT))
+#define unmark(x)          (car(x) = (object *)(((uintptr_t)(car(x))) & ~MARKBIT))
+#define marked(x)          ((((uintptr_t)(car(x))) & MARKBIT) != 0)
 #define MARKBIT            1

 #define setflag(x)         (Flags = Flags | 1<<(x))
@@ -119,6 +120,8 @@

 object Workspace[WORKSPACESIZE] WORDALIGNED;
 char SymbolTable[SYMBOLTABLESIZE];
+extern uint8_t _end;
+typedef int BitOrder;

 // Global variables

@@ -126,7 +129,6 @@
 unsigned int Freespace = 0;
 object *Freelist;
 char *SymbolTop = SymbolTable;
-extern uint8_t _end;
 unsigned int I2CCount;
 unsigned int TraceFn[TRACEMAX];
 unsigned int TraceDepth[TRACEMAX];
@@ -268,7 +270,7 @@
   markobject(env);
   sweep();
   #if defined(printgcs)
-  pserial('{'); pint(Freespace - start, pserial); pserial('}');
+  pfl(pserial); pserial('{'); pint(Freespace - start, pserial); pserial('}');
   #endif
 }

@@ -358,7 +360,8 @@
   return imagesize;
 }

-int loadimage () {
+int loadimage (object *filename) {
+  (void) filename;
   unsigned int imagesize = eeprom_read_word(&image.datasize);
   if (imagesize == 0 || imagesize == 0xFFFF) error(PSTR("No saved image"));
   GlobalEnv = (object *)eeprom_read_word(&image.globalenv);
@@ -376,7 +379,7 @@
   object *nullenv = NULL;
   object *autorun = (object *)eeprom_read_word(&image.eval);
   if (autorun != NULL && (unsigned int)autorun != 0xFFFF) {
-    loadimage();
+    loadimage(nil);
     apply(autorun, NULL, &nullenv);
   }
 }
@@ -565,17 +568,20 @@

 void buildstring (char ch, int *chars, object **head) {
   static object* tail;
+  static uint8_t shift;
   if (*chars == 0) {
-    *chars = ch<<8;
+    shift = (sizeof(int)-1)*8;
+    *chars = ch<<shift;
     object *cell = myalloc();
     if (*head == NULL) *head = cell; else tail->car = cell;
     cell->car = NULL;
     cell->integer = *chars;
     tail = cell;
   } else {
-    *chars = *chars | ch;
+    shift = shift - 8;
+    *chars = *chars | ch<<shift;
     tail->integer = *chars;
-    *chars = 0;
+    if (shift == 0) *chars = 0;
   }
 }

@@ -599,8 +605,9 @@
   form = cdr(form);
   while (form != NULL) {
     int chars = form->integer;
-    if (chars & 0xFF) length++;
-    if (chars & 0xFF00) length++;
+    for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
+      if (chars>>i & 0xFF) length++;
+    }
     form = car(form);
   }
   return length;
@@ -608,15 +615,15 @@

 char nthchar (object *string, int n) {
   object *arg = cdr(string);
-  int top = n>>1;
+  int top;
+  if (sizeof(int) == 4) { top = n>>2; n = 3 - (n&3); }
+  else { top = n>>1; n = 1 - (n&1); }
   for (int i=0; i<top; i++) {
     if (arg == NULL) return 0;
     arg = car(arg);
   }
   if (arg == NULL) return 0;
-  char ch;
-  if (n&1) ch = (arg->integer) & 0xFF; else ch = (arg->integer)>>8 & 0xFF;
-  return ch;
+  return (arg->integer)>>(n*8) & 0xFF;
 }

 // Lookup variable in environment
@@ -1046,8 +1053,8 @@
   args = cdr(args);
   if (args != NULL) increment = integer(eval(first(args), env));
   #if defined(checkoverflow)
-  if (increment < 1) { if (-32768 - increment > result) error(PSTR("'incf' arithmetic overflow")); }
-  else { if (32767 - increment < result) error(PSTR("'incf' arithmetic overflow")); }
+  if (increment < 1) { if (INT_MIN - increment > result) error(PSTR("'incf' arithmetic overflow")); }
+  else { if (INT_MAX - increment < result) error(PSTR("'incf' arithmetic overflow")); }
   #endif
   result = result + increment;
   *loc = number(result);
@@ -1061,8 +1068,8 @@
   args = cdr(args);
   if (args != NULL) decrement = integer(eval(first(args), env));
   #if defined(checkoverflow)
-  if (decrement < 1) { if (32767 + decrement < result) error(PSTR("'decf' arithmetic overflow")); }
-  else { if (-32768 + decrement > result) error(PSTR("'decf' arithmetic overflow")); }
+  if (decrement < 1) { if (INT_MAX + decrement < result) error(PSTR("'decf' arithmetic overflow")); }
+  else { if (INT_MIN + decrement > result) error(PSTR("'decf' arithmetic overflow")); }
   #endif
   result = result - decrement;
   *loc = number(result);
@@ -1131,7 +1138,7 @@
   while (args != NULL) {
       trace(first(args)->name);
       args = cdr(args);
-    }
+  }
   int i = 0;
   while (i < TRACEMAX) {
     if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args);
@@ -1165,7 +1172,7 @@
   if (param != NULL) total = integer(first(param));
   eval(tf_progn(cdr(args),env), env);
   do now = millis() - start; while (now < total);
-  if (now <= 32767) return number(now);
+  if (now <= INT_MAX) return number(now);
   return nil;
 }

@@ -1181,11 +1188,11 @@
   object *pair = cons(var, stream(SERIALSTREAM, address));
   push(pair,env);
   #if defined(__AVR_ATmega1284P__)
-  if (address == 1) Serial1.begin(baud*100);
+  if (address == 1) Serial1.begin((long)baud*100);
   #elif defined(__AVR_ATmega2560__)
-  if (address == 1) Serial1.begin(baud*100);
-  else if (address == 2) Serial2.begin(baud*100);
-  else if (address == 3) Serial3.begin(baud*100);
+  if (address == 1) Serial1.begin((long)baud*100);
+  else if (address == 2) Serial2.begin((long)baud*100);
+  else if (address == 3) Serial3.begin((long)baud*100);
   #endif
   object *forms = cdr(args);
   object *result = eval(tf_progn(forms,env), env);
@@ -1244,7 +1251,7 @@
   }
   pinMode(pin, OUTPUT);
   digitalWrite(pin, LOW);
-  SPI.setBitOrder(bitorder);
+  SPI.setBitOrder((BitOrder)bitorder);
   SPI.setClockDivider(divider);
   SPI.setDataMode(mode);
   object *forms = cdr(args);
@@ -1591,7 +1598,8 @@
       list1 = cdr(list1);
       list2 = cdr(list2);
     }
-  } else {
+    pop(GCStack);
+  } else if (list1 != NULL) {
     while (list1 != NULL) {
       object *result = apply(function, cons(car(list1),NULL), &env);
       object *obj = cons(result,NULL);
@@ -1605,8 +1613,8 @@
       }
       list1 = cdr(list1);
     }
-  }
-  pop(GCStack);
+    pop(GCStack);
+  }
   return head;
 }

@@ -1618,8 +1626,8 @@
   while (args != NULL) {
     int temp = integer(car(args));
     #if defined(checkoverflow)
-    if (temp < 1) { if (-32768 - temp > result) error(PSTR("'+' arithmetic overflow")); }
-    else { if (32767 - temp < result) error(PSTR("'+' arithmetic overflow")); }
+    if (temp < 1) { if (INT_MIN - temp > result) error(PSTR("'+' arithmetic overflow")); }
+    else { if (INT_MAX - temp < result) error(PSTR("'+' arithmetic overflow")); }
     #endif
     result = result + temp;
     args = cdr(args);
@@ -1633,15 +1641,15 @@
   args = cdr(args);
   if (args == NULL) {
     #if defined(checkoverflow)
-    if (result == -32768) error(PSTR("'-' arithmetic overflow"));
+    if (result == INT_MIN) error(PSTR("'-' arithmetic overflow"));
     #endif
     return number(-result);
   }
   while (args != NULL) {
     int temp = integer(car(args));
     #if defined(checkoverflow)
-    if (temp < 1) { if (32767 + temp < result) error(PSTR("'-' arithmetic overflow")); }
-    else { if (-32768 + temp > result) error(PSTR("'-' arithmetic overflow")); }
+    if (temp < 1) { if (INT_MAX + temp < result) error(PSTR("'-' arithmetic overflow")); }
+    else { if (INT_MIN + temp > result) error(PSTR("'-' arithmetic overflow")); }
     #endif
     result = result - temp;
     args = cdr(args);
@@ -1655,7 +1663,7 @@
   while (args != NULL){
     #if defined(checkoverflow)
     signed long temp = (signed long) result * integer(car(args));
-    if ((temp > 32767) || (temp < -32768)) error(PSTR("'*' arithmetic overflow"));
+    if ((temp > INT_MAX) || (temp < INT_MIN)) error(PSTR("'*' arithmetic overflow"));
     result = temp;
     #else
     result = result * integer(car(args));
@@ -1673,7 +1681,7 @@
     int arg = integer(car(args));
     if (arg == 0) error(PSTR("Division by zero"));
     #if defined(checkoverflow)
-    if ((result == -32768) && (arg == -1)) error(PSTR("'/' arithmetic overflow"));
+    if ((result == INT_MIN) && (arg == -1)) error(PSTR("'/' arithmetic overflow"));
     #endif
     result = result / arg;
     args = cdr(args);
@@ -1695,7 +1703,7 @@
   (void) env;
   int result = integer(first(args));
   #if defined(checkoverflow)
-  if (result == 32767) error(PSTR("'1+' arithmetic overflow"));
+  if (result == INT_MAX) error(PSTR("'1+' arithmetic overflow"));
   #endif
   return number(result + 1);
 }
@@ -1704,7 +1712,7 @@
   (void) env;
   int result = integer(first(args));
   #if defined(checkoverflow)
-  if (result == -32768) error(PSTR("'1-' arithmetic overflow"));
+  if (result == INT_MIN) error(PSTR("'1-' arithmetic overflow"));
   #endif
   return number(result - 1);
 }
@@ -1713,7 +1721,7 @@
   (void) env;
   int result = integer(first(args));
   #if defined(checkoverflow)
-  if (result == -32768) error(PSTR("'abs' arithmetic overflow"));
+  if (result == INT_MIN) error(PSTR("'abs' arithmetic overflow"));
   #endif
   return number(abs(result));
 }
@@ -1995,11 +2003,11 @@
     if (obj->type != STRING) error2(obj, PSTR("not a string"));
     obj = cdr(obj);
     while (obj != NULL) {
-      int pair = obj->integer;
-       while (pair != 0) {
-         char ch = pair>>8 & 0xFF;
+      int quad = obj->integer;
+      while (quad != 0) {
+         char ch = quad>>((sizeof(int)-1)*8) & 0xFF;
          buildstring(ch, &chars, &head);
-         pair = pair<<8;
+         quad = quad<<8;
       }
       obj = car(obj);
     }
@@ -2084,7 +2092,7 @@

 object *fn_logand (object *args, object *env) {
   (void) env;
-  unsigned int result = 0xFFFF;
+  int result = -1;
   while (args != NULL) {
     result = result & integer(first(args));
     args = cdr(args);
@@ -2094,7 +2102,7 @@

 object *fn_logior (object *args, object *env) {
   (void) env;
-  unsigned int result = 0;
+  int result = 0;
   while (args != NULL) {
     result = result | integer(first(args));
     args = cdr(args);
@@ -2104,7 +2112,7 @@

 object *fn_logxor (object *args, object *env) {
   (void) env;
-  unsigned int result = 0;
+  int result = 0;
   while (args != NULL) {
     result = result ^ integer(first(args));
     args = cdr(args);
@@ -2261,13 +2269,14 @@
 }

 object *fn_gc (object *obj, object *env) {
+  int initial = Freespace;
   unsigned long start = micros();
-  int initial = Freespace;
   gc(obj, env);
+  unsigned long elapsed = micros() - start;
   pfstring(PSTR("Space: "), pserial);
   pint(Freespace - initial, pserial);
   pfstring(PSTR(" bytes, Time: "), pserial);
-  pint(micros() - start, pserial);
+  pint(elapsed, pserial);
   pfstring(PSTR(" uS\r"), pserial);
   return nil;
 }
@@ -2283,8 +2292,9 @@
 }

 object *fn_loadimage (object *args, object *env) {
-  (void) args, (void) env;
-  return number(loadimage());
+  (void) env;
+  if (args != NULL) args = first(args);
+  return number(loadimage(args));
 }

 object *fn_cls(object *args, object *env) {
@@ -2763,7 +2773,7 @@
   { string136, fn_gc, 0, 0 },
   { string137, fn_room, 0, 0 },
   { string138, fn_saveimage, 0, 1 },
-  { string139, fn_loadimage, 0, 0 },
+  { string139, fn_loadimage, 0, 1 },
   { string140, fn_cls, 0, 0 },
   { string141, fn_pinmode, 2, 2 },
   { string142, fn_digitalread, 1, 1 },
@@ -3020,12 +3030,11 @@
   form = cdr(form);
   while (form != NULL) {
     int chars = form->integer;
-    char ch = chars>>8 & 0xFF;
-    if (PrintReadably && (ch == '"' || ch == '\\')) pfun('\\');
-    pfun(ch);
-    ch = chars & 0xFF;
-    if (PrintReadably && (ch == '"' || ch == '\\')) pfun('\\');
-    if (ch) pfun(ch);
+    for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
+      char ch = chars>>i & 0xFF;
+      if (PrintReadably && (ch == '"' || ch == '\\')) pfun('\\');
+      if (ch) pfun(ch);
+    }
     form = car(form);
   }
   if (PrintReadably) pfun('"');
@@ -3042,8 +3051,13 @@

 void pint (int i, pfun_t pfun) {
   int lead = 0;
+  #if INT_MAX == 32767
+  int p = 10000;
+  #else
+  int p = 1000000000;
+  #endif
   if (i<0) pfun('-');
-  for (int d=10000; d>0; d=d/10) {
+  for (int d=p; d>0; d=d/10) {
     int j = i/d;
     if (j!=0 || lead || d==1) { pfun(abs(j)+'0'); lead=1;}
     i = i - j*d;
@@ -3165,7 +3179,8 @@
   if (ch == '(') LastChar = '(';

   if (isnumber) {
-    if (base == 10 && result > ((unsigned int)32767+(1-sign)/2)) error(PSTR("Number out of range"));
+    if (base == 10 && result > ((unsigned int)INT_MAX+(1-sign)/2))
+      error(PSTR("Number out of range"));
     return number(result*sign);
   } else if (base == 0) {
     if (index == 1) return character(buffer[0]);
@@ -3211,6 +3226,7 @@

 object *read (gfun_t gfun) {
   object *item = nextitem(gfun);
+  if (item == (object *)KET) error(PSTR("Incomplete list"));
   if (item == (object *)BRA) return readrest(gfun);
   if (item == (object *)DOT) return read(gfun);
   if (item == (object *)QUO) return cons(symbol(QUOTE), cons(read(gfun), NULL));
@@ -3229,8 +3245,7 @@
   while (!Serial);  // wait for Serial to initialize
   initworkspace();
   initenv();
-  _end = 0xA5;      // Canary to check stack
-  pfstring(PSTR("uLisp 1.9 "), pserial); pln(pserial);
+  pfstring(PSTR("uLisp 1.9b "), pserial); pln(pserial);
 }

 // Read/Evaluate/Print loop
@@ -3267,5 +3282,8 @@
     autorunimage();
     #endif
   }
+  // Come here after error
+  _end = 0xA5;      // Canary to check stack
+  for (int i=0; i<TRACEMAX; i++) TraceDepth[i] = 0;
   repl(NULL);
 }


Version 1.1 6th June 2016

Version 1.1 6th June 2016

* save-image and load-image allow you to save the uLisp image to EEPROM and reload it.
* #x, #o, and #b allow you to enter numbers in hexadecimal, octal, or binary respectively.
* note now supports the ATmega1284P.

--- ulisp.ino
+++ ulisp.ino
@@ -1,5 +1,4 @@
-/* uLisp Version 1.0 - www.ulisp.com
-
+/* uLisp Version 1.1 - www.ulisp.com
    Copyright (c) 2016 David Johnson-Davies

    Licensed under the MIT license: https://opensource.org/licenses/MIT
@@ -10,6 +9,7 @@
 // Compile options

 #define checkoverflow
+#define resetautorun

 // C Macros

@@ -40,28 +40,33 @@
 // Constants

 #if defined(__AVR_ATmega328P__)
-const int workspacesize = 315;
+const int workspacesize = 317;
+const int EEPROMsize = 1024;
 #elif defined(__AVR_ATmega32U4__)
 const int workspacesize = 421;
+const int EEPROMsize = 1024;
 #elif defined(__AVR_ATmega2560__)
 const int workspacesize = 1461;
+const int EEPROMsize = 4096;
 #elif defined(__AVR_ATmega1284P__)
 const int workspacesize = 3000;
+const int EEPROMsize = 4096;
 #else
-const int workspacesize = 315;
+const int workspacesize = 317;
+const int EEPROMsize = 1024;
 #endif

-const int buflen = 13;  // Length of longest symbol + 1
+const int buflen = 17;  // Length of longest symbol + 1
 enum type {NONE, SYMBOL, NUMBER};
-
-enum function { SYMBOLS, BRA, KET, QUO, DOT, NIL, TEE, LAMBDA, LET, LETSTAR, CLOSURE, SPECIAL_FORMS, QUOTE,
-DEFUN, DEFVAR, SETQ, LOOP, PUSH, POP, INCF, DECF, DOLIST, DOTIMES, FORMILLIS, TAIL_FORMS, PROGN, RETURN, IF,
-COND, WHEN, UNLESS, AND, OR, FUNCTIONS, NOT, NULLFN, CONS, ATOM, LISTP, CONSP, NUMBERP, EQ, CAR, FIRST, CDR,
-REST, CAAR, CADR, SECOND, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR, THIRD, CDAAR, CDADR, CDDAR, CDDDR, LENGTH,
-LIST, REVERSE, NTH, ASSOC, MEMBER, APPLY, FUNCALL, APPEND, MAPC, MAPCAR, ADD, SUBTRACT, MULTIPLY, DIVIDE, MOD,
-ONEPLUS, ONEMINUS, ABS, RANDOM, MAX, MIN, NUMEQ, LESS, LESSEQ, GREATER, GREATEREQ, NOTEQ, PLUSP, MINUSP, ZEROP,
-ODDP, EVENP, READ, EVAL, LOCALS, GLOBALS, MAKUNBOUND, BREAK, PRINT, PRINC, GC, PINMODE, DIGITALREAD, DIGITALWRITE,
-ANALOGREAD, ANALOGWRITE, DELAY, MILLIS, SHIFTOUT, SHIFTIN, NOTE, ENDFUNCTIONS };
+enum token { UNUSED, BRA, KET, QUO, DOT};
+
+enum function { SYMBOLS, NIL, TEE, LAMBDA, LET, LETSTAR, CLOSURE, SPECIAL_FORMS, QUOTE, DEFUN, DEFVAR, SETQ, LOOP, PUSH, POP, INCF,
+DECF, DOLIST, DOTIMES, FORMILLIS, TAIL_FORMS, PROGN, RETURN, IF, COND, WHEN, UNLESS, AND, OR, FUNCTIONS, NOT, NULLFN, CONS, ATOM,
+LISTP, CONSP, NUMBERP, EQ, CAR, FIRST, CDR, REST, CAAR, CADR, SECOND, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR, THIRD, CDAAR, CDADR,
+CDDAR, CDDDR, LENGTH, LIST, REVERSE, NTH, ASSOC, MEMBER, APPLY, FUNCALL, APPEND, MAPC, MAPCAR, ADD, SUBTRACT, MULTIPLY, DIVIDE, MOD,
+ONEPLUS, ONEMINUS, ABS, RANDOM, MAX, MIN, NUMEQ, LESS, LESSEQ, GREATER, GREATEREQ, NOTEQ, PLUSP, MINUSP, ZEROP, ODDP, EVENP, READ,
+EVAL, LOCALS, GLOBALS, MAKUNBOUND, BREAK, PRINT, PRINC, GC, SAVEIMAGE, LOADIMAGE, PINMODE, DIGITALREAD, DIGITALWRITE, ANALOGREAD,
+ANALOGWRITE, DELAY, MILLIS, SHIFTOUT, SHIFTIN, NOTE, ENDFUNCTIONS };

 // Typedefs

@@ -95,17 +100,18 @@
 jmp_buf exception;
 object workspace[workspacesize];
 unsigned int freespace = 0;
-int ReturnFlag = 0;
+char ReturnFlag = 0;
 object *freelist;
+extern uint8_t _end;

 object *GlobalEnv;
 object *GCStack = NULL;
 char buffer[buflen+1];
-int BreakLevel = 0;
+char BreakLevel = 0;
+char LastChar = 0;

 // Forward references
-object *tee, *bra, *ket, *quo, *dot;
-
+object *tee;
 object *tf_progn (object *form, object *env);
 object *eval (object *form, object *env);
 object *read();
@@ -119,7 +125,7 @@

 void initworkspace () {
   freelist = NULL;
-  for (int i=0; i<workspacesize; i++) {
+  for (int i=workspacesize-1; i>=0; i--) {
     object *obj = &workspace[i];
     car(obj) = NULL;
     cdr(obj) = freelist;
@@ -168,6 +174,7 @@
 // Garbage collection

 void markobject (object *obj) {
+  MARK:
   if (obj == NULL) return;

   object* arg = car(obj);
@@ -178,14 +185,15 @@

   if (type != SYMBOL && type != NUMBER) { // cons
     markobject(arg);
-    markobject(cdr(obj));
+    obj = cdr(obj);
+    goto MARK;
   }
 }

 void sweep () {
   freelist = NULL;
   freespace = 0;
-  for (int i=0; i<workspacesize; i++) {
+  for (int i=workspacesize-1; i>=0; i--) {
     object *obj = &workspace[i];
     if (!marked(obj)) {
       car(obj) = NULL;
@@ -197,25 +205,100 @@
 }

 void gc (object *form, object *env) {
-#if defined(debug1)
+  #if defined(debug1)
   int start = freespace;
-#endif
-  markobject(bra); markobject(ket); markobject(quo);
-  markobject(tee); markobject(dot);
+  #endif
+  markobject(tee);
   markobject(GlobalEnv);
   markobject(GCStack);
   markobject(form);
   markobject(env);
   sweep();
-#if defined(debug1)
+  #if defined(debug1)
   Serial.print('{');
   Serial.print(freespace - start);
   Serial.println('}');
-#endif
-}
-
+  #endif
+}
+
+// Save-image and load-image
+
+typedef struct {
+  unsigned int eval;
+  unsigned int datasize;
+  unsigned int globalenv;
+  unsigned int tee;
+  char data[];
+} struct_image;
+
+struct_image EEMEM image;
+
+void movepointer (object *from, object *to) {
+  for (int i=0; i<workspacesize; i++) {
+    object *obj = &workspace[i];
+    int type = (obj->type) & 0x7FFF;
+    if (marked(obj) && type != SYMBOL && type != NUMBER) {
+      if (car(obj) == (object *)((unsigned int)from | 0x8000))
+        car(obj) = (object *)((unsigned int)to | 0x8000);
+      if (cdr(obj) == from) cdr(obj) = to;
+    }
+  }
+}
+
+int compactimage (object **arg) {
+  markobject(tee);
+  markobject(GlobalEnv);
+  markobject(GCStack);
+  object *firstfree = workspace;
+  while (marked(firstfree)) firstfree++;
+
+  for (int i=0; i<workspacesize; i++) {
+    object *obj = &workspace[i];
+    if (marked(obj) && firstfree < obj) {
+      car(firstfree) = car(obj);
+      cdr(firstfree) = cdr(obj);
+      unmark(obj);
+      movepointer(obj, firstfree);
+      if (GlobalEnv == obj) GlobalEnv = firstfree;
+      if (GCStack == obj) GCStack = firstfree;
+      if (tee == obj) tee = firstfree;
+      if (*arg == obj) *arg = firstfree;
+      while (marked(firstfree)) firstfree++;
+    }
+  }
+  sweep();
+  return firstfree - workspace;
+}
+
+int saveimage (object *arg) {
+  unsigned int imagesize = compactimage(&arg);
+  // Save to EEPROM
+  if ((imagesize*4+8) > EEPROMsize) {
+    Serial.print(F("Error: Image size too large: "));
+    Serial.println(imagesize+2);
+    GCStack = NULL;
+    longjmp(exception, 1);
+  }
+  eeprom_write_word(&image.datasize, imagesize);
+  eeprom_write_word(&image.eval, (unsigned int)arg);
+  eeprom_write_word(&image.globalenv, (unsigned int)GlobalEnv);
+  eeprom_write_word(&image.tee, (unsigned int)tee);
+  eeprom_write_block(workspace, image.data, imagesize*4);
+  return imagesize+2;
+}
+
+int loadimage () {
+  unsigned int imagesize = eeprom_read_word(&image.datasize);
+  if (imagesize == 0 || imagesize == 0xFFFF) error(F("No saved image"));
+  GlobalEnv = (object *)eeprom_read_word(&image.globalenv);
+  tee = (object *)eeprom_read_word(&image.tee) ;
+  eeprom_read_block(workspace, image.data, imagesize*4);
+  gc(NULL, NULL);
+  return imagesize+2;
+}

 // Error handling
+
 void error (const __FlashStringHelper *string) {
   Serial.print(F("Error: "));
   Serial.println(string);
@@ -236,10 +319,9 @@

 int toradix40 (int ch) {
   if (ch == 0) return 0;
-  if (ch >= 'A' && ch <= 'Z') return ch-'A'+1;
+  if (ch >= '0' && ch <= '9') return ch-'0'+30;
+  ch = ch | 0x20;
   if (ch >= 'a' && ch <= 'z') return ch-'a'+1;
-  if (ch >= '0' && ch <= '9') return ch-'0'+30;
-  if (ch == '-') return 27;
   error(F("Illegal character in symbol"));
   return 0;
 }
@@ -253,6 +335,13 @@

 int pack40 (char *buffer) {
   return (((toradix40(buffer[0]) * 40) + toradix40(buffer[1])) * 40 + toradix40(buffer[2]));
+}
+
+int digitvalue (char d) {
+  if (d>='0' && d<='9') return d-'0';
+  d = d | 0x20;
+  if (d>='a' && d<='f') return d-'a'+10;
+  return 16;
 }

 char *name(object *obj){
@@ -311,14 +400,14 @@
   return NULL;
 }

-object *closure (int tail, object *fname, object *function, object *args, object **env) {
-  object *local = first(function);
-  object *params = second(function);
-  function = cdr(cdr(function));
-  // Push locals
-   while (local != NULL) {
-    push(first(local), *env);
-    local = cdr(local);
+object *closure (int tail, object *fname, object *state, object *function, object *args, object **env) {
+  object *params = first(function);
+  function = cdr(function);
+  // Push state if not already in env
+  while (state != NULL) {
+    object *pair = first(state);
+    if (findtwin(car(pair), *env) == NULL) push(first(state), *env);
+    state = cdr(state);
   }
   // Add arguments to environment
   while (params != NULL && args != NULL) {
@@ -355,10 +444,18 @@
     if (nargs<lookupmin(name)) error2(function, F("has too few arguments"));
     if (nargs>lookupmax(name)) error2(function, F("has too many arguments"));
     return ((fn_ptr_type)lookupfn(name))(args, *env);
-  } else if (listp(function) && issymbol(car(function), CLOSURE)) {
-    object *result = closure(0, NULL, cdr(function), args, env);
-    return eval(result,*env);
-  } else error2(function, F("illegal function"));
+  }
+  if (listp(function) && issymbol(car(function), LAMBDA)) {
+    function = cdr(function);
+    object *result = closure(0, NULL, NULL, function, args, env);
+    return eval(result, *env);
+  }
+  if (listp(function) && issymbol(car(function), CLOSURE)) {
+    function = cdr(function);
+    object *result = closure(0, NULL, car(function), cdr(function), args, env);
+    return eval(result, *env);
+  }
+  error2(function, F("illegal function"));
   return NULL;
 }

@@ -387,7 +484,7 @@
   (void) env;
   object *var = first(args);
   if (var->type != SYMBOL) error2(var, F("is not a symbol"));
-  object *val = cons(symbol(CLOSURE), cons(NULL,cdr(args)));
+  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);
@@ -808,6 +905,7 @@
 object *fn_mapc (object *args, object *env) {
   object *function = first(args);
   object *list1 = second(args);
+  object *result = list1;
   if (!listp(list1)) error(F("'mapc' second argument is not a list"));
   object *list2 = third(args);
   if (!listp(list2)) error(F("'mapc' third argument is not a list"));
@@ -823,7 +921,7 @@
       list1 = cdr(list1);
     }
   }
-  return nil;
+  return result;
 }

 object *fn_mapcar (object *args, object *env) {
@@ -930,6 +1028,9 @@
   while (args != NULL) {
     int arg = integer(car(args));
     if (arg == 0) error(F("Division by zero"));
+    #if defined(checkoverflow)
+    if ((result == -32768) && (arg == -1)) error(F("'/' arithmetic overflow"));
+    #endif
     result = result / arg;
     args = cdr(args);
   }
@@ -1198,6 +1299,17 @@
   Serial.print(micros() - start);
   Serial.println(F(" uS"));
   return nil;
+}
+
+object *fn_saveimage (object *args, object *env) {
+  object *var = eval(first(args), env);
+  return number(saveimage(var));
+}
+
+object *fn_loadimage (object *args, object *env) {
+  (void) args;
+  (void) env;
+  return number(loadimage());
 }

 // Arduino procedures
@@ -1307,6 +1419,7 @@
     OCR2A = pgm_read_byte(&scale[note%12]);
     TCCR2B = 0<<WGM22 | prescaler<<CS20;
   } else TCCR2B = 0<<WGM22 | 0<<CS20;
+
   #elif defined(__AVR_ATmega2560__)
   if (args != NULL) {
     int pin = integer(first(args));
@@ -1325,6 +1438,25 @@
     OCR2A = pgm_read_byte(&scale[note%12]);
     TCCR2B = 0<<WGM22 | prescaler<<CS20;
   } else TCCR2B = 0<<WGM22 | 0<<CS20;
+
+  #elif defined(__AVR_ATmega1284P__)
+  if (args != NULL) {
+    int pin = integer(first(args));
+    int note = integer(second(args));
+    if (pin == 14) {
+      DDRD = DDRD | 1<<DDD6; // PD6 (Arduino D14) as output
+      TCCR2A = 0<<COM2A0 | 1<<COM2B0 | 2<<WGM20; // Toggle OC2B on match
+    } 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(F("'note' pin not supported"));
+    int prescaler = 0;
+    if (cdr(cdr(args)) != NULL) prescaler = integer(third(args));
+    prescaler = 9 - prescaler - note/12;
+    if (prescaler<3 || prescaler>6) error(F("'note' octave out of range"));
+    OCR2A = pgm_read_byte(&scale[note%12]);
+    TCCR2B = 0<<WGM22 | prescaler<<CS20;
+  } else TCCR2B = 0<<WGM22 | 0<<CS20;
   #endif
   return nil;
 }
@@ -1335,231 +1467,227 @@
 // Built-in procedure names - stored in PROGMEM

 const char string0[] PROGMEM = "symbols";
-const char string1[] PROGMEM = "(";
-const char string2[] PROGMEM = ")";
-const char string3[] PROGMEM = "'";
-const char string4[] PROGMEM = ".";
-const char string5[] PROGMEM = "nil";
-const char string6[] PROGMEM = "t";
-const char string7[] PROGMEM = "lambda";
-const char string8[] PROGMEM = "let";
-const char string9[] PROGMEM = "let*";
-const char string10[] PROGMEM = "closure";
-const char string11[] PROGMEM = "special_forms";
-const char string12[] PROGMEM = "quote";
-const char string13[] PROGMEM = "defun";
-const char string14[] PROGMEM = "defvar";
-const char string15[] PROGMEM = "setq";
-const char string16[] PROGMEM = "loop";
-const char string17[] PROGMEM = "push";
-const char string18[] PROGMEM = "pop";
-const char string19[] PROGMEM = "incf";
-const char string20[] PROGMEM = "decf";
-const char string21[] PROGMEM = "dolist";
-const char string22[] PROGMEM = "dotimes";
-const char string23[] PROGMEM = "for-millis";
-const char string24[] PROGMEM = "tail_forms";
-const char string25[] PROGMEM = "progn";
-const char string26[] PROGMEM = "return";
-const char string27[] PROGMEM = "if";
-const char string28[] PROGMEM = "cond";
-const char string29[] PROGMEM = "when";
-const char string30[] PROGMEM = "unless";
-const char string31[] PROGMEM = "and";
-const char string32[] PROGMEM = "or";
-const char string33[] PROGMEM = "functions";
-const char string34[] PROGMEM = "not";
-const char string35[] PROGMEM = "null";
-const char string36[] PROGMEM = "cons";
-const char string37[] PROGMEM = "atom";
-const char string38[] PROGMEM = "listp";
-const char string39[] PROGMEM = "consp";
-const char string40[] PROGMEM = "numberp";
-const char string41[] PROGMEM = "eq";
-const char string42[] PROGMEM = "car";
-const char string43[] PROGMEM = "first";
-const char string44[] PROGMEM = "cdr";
-const char string45[] PROGMEM = "rest";
-const char string46[] PROGMEM = "caar";
-const char string47[] PROGMEM = "cadr";
-const char string48[] PROGMEM = "second";
-const char string49[] PROGMEM = "cdar";
-const char string50[] PROGMEM = "cddr";
-const char string51[] PROGMEM = "caaar";
-const char string52[] PROGMEM = "caadr";
-const char string53[] PROGMEM = "cadar";
-const char string54[] PROGMEM = "caddr";
-const char string55[] PROGMEM = "third";
-const char string56[] PROGMEM = "cdaar";
-const char string57[] PROGMEM = "cdadr";
-const char string58[] PROGMEM = "cddar";
-const char string59[] PROGMEM = "cdddr";
-const char string60[] PROGMEM = "length";
-const char string61[] PROGMEM = "list";
-const char string62[] PROGMEM = "reverse";
-const char string63[] PROGMEM = "nth";
-const char string64[] PROGMEM = "assoc";
-const char string65[] PROGMEM = "member";
-const char string66[] PROGMEM = "apply";
-const char string67[] PROGMEM = "funcall";
-const char string68[] PROGMEM = "append";
-const char string69[] PROGMEM = "mapc";
-const char string70[] PROGMEM = "mapcar";
-const char string71[] PROGMEM = "+";
-const char string72[] PROGMEM = "-";
-const char string73[] PROGMEM = "*";
-const char string74[] PROGMEM = "/";
-const char string75[] PROGMEM = "mod";
-const char string76[] PROGMEM = "1+";
-const char string77[] PROGMEM = "1-";
-const char string78[] PROGMEM = "abs";
-const char string79[] PROGMEM = "random";
-const char string80[] PROGMEM = "max";
-const char string81[] PROGMEM = "min";
-const char string82[] PROGMEM = "=";
-const char string83[] PROGMEM = "<";
-const char string84[] PROGMEM = "<=";
-const char string85[] PROGMEM = ">";
-const char string86[] PROGMEM = ">=";
-const char string87[] PROGMEM = "/=";
-const char string88[] PROGMEM = "plusp";
-const char string89[] PROGMEM = "minusp";
-const char string90[] PROGMEM = "zerop";
-const char string91[] PROGMEM = "oddp";
-const char string92[] PROGMEM = "evenp";
-const char string93[] PROGMEM = "read";
-const char string94[] PROGMEM = "eval";
-const char string95[] PROGMEM = "locals";
-const char string96[] PROGMEM = "globals";
-const char string97[] PROGMEM = "makunbound";
-const char string98[] PROGMEM = "break";
-const char string99[] PROGMEM = "print";
-const char string100[] PROGMEM = "princ";
-const char string101[] PROGMEM = "gc";
-const char string102[] PROGMEM = "pinmode";
-const char string103[] PROGMEM = "digitalread";
-const char string104[] PROGMEM = "digitalwrite";
-const char string105[] PROGMEM = "analogread";
-const char string106[] PROGMEM = "analogwrite";
-const char string107[] PROGMEM = "delay";
-const char string108[] PROGMEM = "millis";
-const char string109[] PROGMEM = "shiftout";
-const char string110[] PROGMEM = "shiftin";
-const char string111[] PROGMEM = "note";
+const char string1[] PROGMEM = "nil";
+const char string2[] PROGMEM = "t";
+const char string3[] PROGMEM = "lambda";
+const char string4[] PROGMEM = "let";
+const char string5[] PROGMEM = "let*";
+const char string6[] PROGMEM = "closure";
+const char string7[] PROGMEM = "special_forms";
+const char string8[] PROGMEM = "quote";
+const char string9[] PROGMEM = "defun";
+const char string10[] PROGMEM = "defvar";
+const char string11[] PROGMEM = "setq";
+const char string12[] PROGMEM = "loop";
+const char string13[] PROGMEM = "push";
+const char string14[] PROGMEM = "pop";
+const char string15[] PROGMEM = "incf";
+const char string16[] PROGMEM = "decf";
+const char string17[] PROGMEM = "dolist";
+const char string18[] PROGMEM = "dotimes";
+const char string19[] PROGMEM = "for-millis";
+const char string20[] PROGMEM = "tail_forms";
+const char string21[] PROGMEM = "progn";
+const char string22[] PROGMEM = "return";
+const char string23[] PROGMEM = "if";
+const char string24[] PROGMEM = "cond";
+const char string25[] PROGMEM = "when";
+const char string26[] PROGMEM = "unless";
+const char string27[] PROGMEM = "and";
+const char string28[] PROGMEM = "or";
+const char string29[] PROGMEM = "functions";
+const char string30[] PROGMEM = "not";
+const char string31[] PROGMEM = "null";
+const char string32[] PROGMEM = "cons";
+const char string33[] PROGMEM = "atom";
+const char string34[] PROGMEM = "listp";
+const char string35[] PROGMEM = "consp";
+const char string36[] PROGMEM = "numberp";
+const char string37[] PROGMEM = "eq";
+const char string38[] PROGMEM = "car";
+const char string39[] PROGMEM = "first";
+const char string40[] PROGMEM = "cdr";
+const char string41[] PROGMEM = "rest";
+const char string42[] PROGMEM = "caar";
+const char string43[] PROGMEM = "cadr";
+const char string44[] PROGMEM = "second";
+const char string45[] PROGMEM = "cdar";
+const char string46[] PROGMEM = "cddr";
+const char string47[] PROGMEM = "caaar";
+const char string48[] PROGMEM = "caadr";
+const char string49[] PROGMEM = "cadar";
+const char string50[] PROGMEM = "caddr";
+const char string51[] PROGMEM = "third";
+const char string52[] PROGMEM = "cdaar";
+const char string53[] PROGMEM = "cdadr";
+const char string54[] PROGMEM = "cddar";
+const char string55[] PROGMEM = "cdddr";
+const char string56[] PROGMEM = "length";
+const char string57[] PROGMEM = "list";
+const char string58[] PROGMEM = "reverse";
+const char string59[] PROGMEM = "nth";
+const char string60[] PROGMEM = "assoc";
+const char string61[] PROGMEM = "member";
+const char string62[] PROGMEM = "apply";
+const char string63[] PROGMEM = "funcall";
+const char string64[] PROGMEM = "append";
+const char string65[] PROGMEM = "mapc";
+const char string66[] PROGMEM = "mapcar";
+const char string67[] PROGMEM = "+";
+const char string68[] PROGMEM = "-";
+const char string69[] PROGMEM = "*";
+const char string70[] PROGMEM = "/";
+const char string71[] PROGMEM = "mod";
+const char string72[] PROGMEM = "1+";
+const char string73[] PROGMEM = "1-";
+const char string74[] PROGMEM = "abs";
+const char string75[] PROGMEM = "random";
+const char string76[] PROGMEM = "max";
+const char string77[] PROGMEM = "min";
+const char string78[] PROGMEM = "=";
+const char string79[] PROGMEM = "<";
+const char string80[] PROGMEM = "<=";
+const char string81[] PROGMEM = ">";
+const char string82[] PROGMEM = ">=";
+const char string83[] PROGMEM = "/=";
+const char string84[] PROGMEM = "plusp";
+const char string85[] PROGMEM = "minusp";
+const char string86[] PROGMEM = "zerop";
+const char string87[] PROGMEM = "oddp";
+const char string88[] PROGMEM = "evenp";
+const char string89[] PROGMEM = "read";
+const char string90[] PROGMEM = "eval";
+const char string91[] PROGMEM = "locals";
+const char string92[] PROGMEM = "globals";
+const char string93[] PROGMEM = "makunbound";
+const char string94[] PROGMEM = "break";
+const char string95[] PROGMEM = "print";
+const char string96[] PROGMEM = "princ";
+const char string97[] PROGMEM = "gc";
+const char string98[] PROGMEM = "save-image";
+const char string99[] PROGMEM = "load-image";
+const char string100[] PROGMEM = "pinmode";
+const char string101[] PROGMEM = "digitalread";
+const char string102[] PROGMEM = "digitalwrite";
+const char string103[] PROGMEM = "analogread";
+const char string104[] PROGMEM = "analogwrite";
+const char string105[] PROGMEM = "delay";
+const char string106[] PROGMEM = "millis";
+const char string107[] PROGMEM = "shiftout";
+const char string108[] PROGMEM = "shiftin";
+const char string109[] PROGMEM = "note";

 const tbl_entry_t lookup_table[] PROGMEM = {
   { string0, NULL, NIL, NIL },
   { string1, NULL, 0, 0 },
-  { string2, NULL, 0, 0 },
-  { string3, NULL, 0, 0 },
-  { string4, NULL, 0, 0 },
-  { string5, NULL, 0, 0 },
-  { string6, NULL, 0, 0 },
-  { string7, NULL, 0, 127 },
-  { string8, NULL, 0, 127 },
-  { string9, NULL, 0, 127 },
-  { string10, NULL, 0, 127 },
-  { string11, NULL, NIL, NIL },
-  { string12, sp_quote, 1, 1 },
-  { string13, sp_defun, 0, 127 },
-  { string14, sp_defvar, 0, 127 },
-  { string15, sp_setq, 2, 2 },
-  { string16, sp_loop, 0, 127 },
-  { string17, sp_push, 2, 2 },
-  { string18, sp_pop, 1, 1 },
-  { string19, sp_incf, 1, 2 },
-  { string20, sp_decf, 1, 2 },
-  { string21, sp_dolist, 1, 127 },
-  { string22, sp_dotimes, 1, 127 },
-  { string23, sp_formillis, 1, 127 },
-  { string24, NULL, NIL, NIL },
-  { string25, tf_progn, 0, 127 },
-  { string26, tf_return, 0, 127 },
-  { string27, tf_if, 2, 3 },
-  { string28, tf_cond, 0, 127 },
-  { string29, tf_when, 1, 127 },
-  { string30, tf_unless, 1, 127 },
-  { string31, tf_and, 0, 127 },
-  { string32, tf_or, 0, 127 },
-  { string33, NULL, NIL, NIL },
-  { string34, fn_not, 1, 1 },
-  { string35, fn_not, 1, 1 },
-  { string36, fn_cons, 2, 2 },
-  { string37, fn_atom, 1, 1 },
-  { string38, fn_listp, 1, 1 },
-  { string39, fn_consp, 1, 1 },
-  { string40, fn_numberp, 1, 1 },
-  { string41, fn_eq, 2, 2 },
-  { string42, fn_car, 1, 1 },
-  { string43, fn_car, 1, 1 },
-  { string44, fn_cdr, 1, 1 },
-  { string45, fn_cdr, 1, 1 },
-  { string46, fn_caar, 1, 1 },
-  { string47, fn_cadr, 1, 1 },
-  { string48, fn_cadr, 1, 1 },
-  { string49, fn_cdar, 1, 1 },
-  { string50, fn_cddr, 1, 1 },
-  { string51, fn_caaar, 1, 1 },
-  { string52, fn_caadr, 1, 1 },
-  { string53, fn_cadar, 1, 1 },
-  { string54, fn_caddr, 1, 1 },
-  { string55, fn_caddr, 1, 1 },
-  { string56, fn_cdaar, 1, 1 },
-  { string57, fn_cdadr, 1, 1 },
-  { string58, fn_cddar, 1, 1 },
-  { string59, fn_cdddr, 1, 1 },
-  { string60, fn_length, 1, 1 },
-  { string61, fn_list, 0, 127 },
-  { string62, fn_reverse, 1, 1 },
-  { string63, fn_nth, 2, 2 },
-  { string64, fn_assoc, 2, 2 },
-  { string65, fn_member, 2, 2 },
-  { string66, fn_apply, 2, 127 },
-  { string67, fn_funcall, 1, 127 },
-  { string68, fn_append, 0, 127 },
-  { string69, fn_mapc, 2, 3 },
-  { string70, fn_mapcar, 2, 3 },
-  { string71, fn_add, 0, 127 },
-  { string72, fn_subtract, 1, 127 },
-  { string73, fn_multiply, 0, 127 },
-  { string74, fn_divide, 2, 127 },
-  { string75, fn_mod, 2, 2 },
-  { string76, fn_oneplus, 1, 1 },
-  { string77, fn_oneminus, 1, 1 },
-  { string78, fn_abs, 1, 1 },
-  { string79, fn_random, 1, 1 },
-  { string80, fn_max, 1, 127 },
-  { string81, fn_min, 1, 127 },
-  { string82, fn_numeq, 1, 127 },
-  { string83, fn_less, 1, 127 },
-  { string84, fn_lesseq, 1, 127 },
-  { string85, fn_greater, 1, 127 },
-  { string86, fn_greatereq, 1, 127 },
-  { string87, fn_noteq, 1, 127 },
-  { string88, fn_plusp, 1, 1 },
-  { string89, fn_minusp, 1, 1 },
-  { string90, fn_zerop, 1, 1 },
-  { string91, fn_oddp, 1, 1 },
-  { string92, fn_evenp, 1, 1 },
-  { string93, fn_read, 0, 0 },
-  { string94, fn_eval, 1, 1 },
-  { string95, fn_locals, 0, 0 },
-  { string96, fn_globals, 0, 0 },
-  { string97, fn_makunbound, 1, 1 },
-  { string98, fn_break, 0, 0 },
-  { string99, fn_print, 1, 1 },
-  { string100, fn_princ, 1, 1 },
-  { string101, fn_gc, 0, 0 },
-  { string102, fn_pinmode, 2, 2 },
-  { string103, fn_digitalread, 1, 1 },
-  { string104, fn_digitalwrite, 2, 2 },
-  { string105, fn_analogread, 1, 1 },
-  { string106, fn_analogwrite, 2, 2 },
-  { string107, fn_delay, 1, 1 },
-  { string108, fn_millis, 0, 0 },
-  { string109, fn_shiftout, 4, 4 },
-  { string110, fn_shiftin, 3, 3 },
-  { string111, fn_note, 0, 3 },
+  { string2, NULL, 1, 0 },
+  { string3, NULL, 0, 127 },
+  { string4, NULL, 0, 127 },
+  { string5, NULL, 0, 127 },
+  { string6, NULL, 0, 127 },
+  { string7, NULL, NIL, NIL },
+  { string8, sp_quote, 1, 1 },
+  { string9, sp_defun, 0, 127 },
+  { string10, sp_defvar, 0, 127 },
+  { string11, sp_setq, 2, 2 },
+  { string12, sp_loop, 0, 127 },
+  { string13, sp_push, 2, 2 },
+  { string14, sp_pop, 1, 1 },
+  { string15, sp_incf, 1, 2 },
+  { string16, sp_decf, 1, 2 },
+  { string17, sp_dolist, 1, 127 },
+  { string18, sp_dotimes, 1, 127 },
+  { string19, sp_formillis, 1, 127 },
+  { string20, NULL, NIL, NIL },
+  { string21, tf_progn, 0, 127 },
+  { string22, tf_return, 0, 127 },
+  { string23, tf_if, 2, 3 },
+  { string24, tf_cond, 0, 127 },
+  { string25, tf_when, 1, 127 },
+  { string26, tf_unless, 1, 127 },
+  { string27, tf_and, 0, 127 },
+  { string28, tf_or, 0, 127 },
+  { string29, NULL, NIL, NIL },
+  { string30, fn_not, 1, 1 },
+  { string31, fn_not, 1, 1 },
+  { string32, fn_cons, 2, 2 },
+  { string33, fn_atom, 1, 1 },
+  { string34, fn_listp, 1, 1 },
+  { string35, fn_consp, 1, 1 },
+  { string36, fn_numberp, 1, 1 },
+  { string37, fn_eq, 2, 2 },
+  { string38, fn_car, 1, 1 },
+  { string39, fn_car, 1, 1 },
+  { string40, fn_cdr, 1, 1 },
+  { string41, fn_cdr, 1, 1 },
+  { string42, fn_caar, 1, 1 },
+  { string43, fn_cadr, 1, 1 },
+  { string44, fn_cadr, 1, 1 },
+  { string45, fn_cdar, 1, 1 },
+  { string46, fn_cddr, 1, 1 },
+  { string47, fn_caaar, 1, 1 },
+  { string48, fn_caadr, 1, 1 },
+  { string49, fn_cadar, 1, 1 },
+  { string50, fn_caddr, 1, 1 },
+  { string51, fn_caddr, 1, 1 },
+  { string52, fn_cdaar, 1, 1 },
+  { string53, fn_cdadr, 1, 1 },
+  { string54, fn_cddar, 1, 1 },
+  { string55, fn_cdddr, 1, 1 },
+  { string56, fn_length, 1, 1 },
+  { string57, fn_list, 0, 127 },
+  { string58, fn_reverse, 1, 1 },
+  { string59, fn_nth, 2, 2 },
+  { string60, fn_assoc, 2, 2 },
+  { string61, fn_member, 2, 2 },
+  { string62, fn_apply, 2, 127 },
+  { string63, fn_funcall, 1, 127 },
+  { string64, fn_append, 0, 127 },
+  { string65, fn_mapc, 2, 3 },
+  { string66, fn_mapcar, 2, 3 },
+  { string67, fn_add, 0, 127 },
+  { string68, fn_subtract, 1, 127 },
+  { string69, fn_multiply, 0, 127 },
+  { string70, fn_divide, 2, 127 },
+  { string71, fn_mod, 2, 2 },
+  { string72, fn_oneplus, 1, 1 },
+  { string73, fn_oneminus, 1, 1 },
+  { string74, fn_abs, 1, 1 },
+  { string75, fn_random, 1, 1 },
+  { string76, fn_max, 1, 127 },
+  { string77, fn_min, 1, 127 },
+  { string78, fn_numeq, 1, 127 },
+  { string79, fn_less, 1, 127 },
+  { string80, fn_lesseq, 1, 127 },
+  { string81, fn_greater, 1, 127 },
+  { string82, fn_greatereq, 1, 127 },
+  { string83, fn_noteq, 1, 127 },
+  { string84, fn_plusp, 1, 1 },
+  { string85, fn_minusp, 1, 1 },
+  { string86, fn_zerop, 1, 1 },
+  { string87, fn_oddp, 1, 1 },
+  { string88, fn_evenp, 1, 1 },
+  { string89, fn_read, 0, 0 },
+  { string90, fn_eval, 1, 1 },
+  { string91, fn_locals, 0, 0 },
+  { string92, fn_globals, 0, 0 },
+  { string93, fn_makunbound, 1, 1 },
+  { string94, fn_break, 0, 0 },
+  { string95, fn_print, 1, 1 },
+  { string96, fn_princ, 1, 1 },
+  { string97, fn_gc, 0, 0 },
+  { string98, fn_saveimage, 0, 1 },
+  { string99, fn_loadimage, 0, 0 },
+  { string100, fn_pinmode, 2, 2 },
+  { string101, fn_digitalread, 1, 1 },
+  { string102, fn_digitalwrite, 2, 2 },
+  { string103, fn_analogread, 1, 1 },
+  { string104, fn_analogwrite, 2, 2 },
+  { string105, fn_delay, 1, 1 },
+  { string106, fn_millis, 0, 0 },
+  { string107, fn_shiftout, 4, 4 },
+  { string108, fn_shiftin, 3, 3 },
+  { string109, fn_note, 0, 3 },
 };

 // Table lookup functions
@@ -1593,14 +1721,14 @@

 // Main evaluator

-unsigned int Canary = 0xA5A5;
+// unsigned int Canary = 0xA5A5;

 object *eval (object *form, object *env) {
   int TC=0;
   EVAL:
   // Enough space?
   if (freespace < 10) gc(form, env);
-  if (Canary != 0xA5A5) error(F("Error: Stack overflow"));
+  if (_end != 0xA5) error(F("Error: Stack overflow"));
   // Break
   if (Serial.read() == '~') error(F("Break!"));

@@ -1645,6 +1773,7 @@
     }

     if (name == LAMBDA) {
+      if (env == NULL) return form;
       object *envcopy = NULL;
       while (env != NULL) {
         object *pair = first(env);
@@ -1697,28 +1826,35 @@
     return result;
   }

+  if (listp(function) && issymbol(car(function), LAMBDA)) {
+    form = closure(TCstart, fname, NULL, cdr(function), args, &env);
+    pop(GCStack);
+    TC = 1;
+    goto EVAL;
+  }
+
   if (listp(function) && issymbol(car(function), CLOSURE)) {
-    form = closure(TCstart, fname, cdr(function), args, &env);
+    function = cdr(function);
+    form = closure(TCstart, fname, car(function), cdr(function), args, &env);
     pop(GCStack);
     TC = 1;
     goto EVAL;
   }
-
+
   error2(fname, F("is an illegal function")); return nil;
 }

 // Input/Output

 void printobject(object *form){
+  #if defined(debug2)
+  Serial.print('[');Serial.print((int)form);Serial.print(']');
+  #endif
   if (form == NULL) Serial.print(F("nil"));
-  else if (listp(form) && issymbol(car(form), CLOSURE) && car(cdr(form)) != NULL) {
-    Serial.print(F("<closure>"));
-  } else if (listp(form)) {
+  else if (listp(form) && issymbol(car(form), CLOSURE)) Serial.print(F("<closure>"));
+  else if (listp(form)) {
     Serial.print('(');
-    if (issymbol(car(form), CLOSURE)) {
-      Serial.print(F("lambda"));
-      form = cdr(form);
-    } else printobject(car(form));
+    printobject(car(form));
     form = cdr(form);
     while (form != NULL && listp(form)) {
       Serial.print(' ');
@@ -1734,14 +1870,9 @@
     Serial.print(integer(form));
   } else if (form->type == SYMBOL){
     Serial.print(name(form));
-    #if defined(debug2)
-    Serial.print('[');Serial.print((int)form);Serial.print(']');
-    #endif
   } else
     error(F("Error in print."));
 }
-
-int LastChar = 0;

 int Getc () {
   if (LastChar) {
@@ -1767,29 +1898,50 @@
   if (ch == '\n') ch = Getc();
   if (ch == EOF) exit(0);

-  if (ch == ')') return ket;
-  if (ch == '(') return bra;
-  if (ch == '\'') return quo;
-  if (ch == '.') return dot;
-
-  int index = 0;
-  if (ch == '+' || ch == '-') {
+  if (ch == ')') return (object *)KET;
+  if (ch == '(') return (object *)BRA;
+  if (ch == '\'') return (object *)QUO;
+  if (ch == '.') return (object *)DOT;
+
+  // Parse variable or number
+  int index = 0, base = 10, sign = 1;
+  unsigned int result = 0;
+  if (ch == '+') {
     buffer[index++] = ch;
     ch = Getc();
-  }
-  int isnumber = isdigit(ch);
+  } else if (ch == '-') {
+    sign = -1;
+    buffer[index++] = ch;
+    ch = Getc();
+  } else if (ch == '#') {
+    ch = Getc() | 0x20;
+    if (ch == 'b') base = 2;
+    else if (ch == 'o') base = 8;
+    else if (ch == 'x') base = 16;
+    else error(F("Illegal character after #"));
+    ch = Getc();
+  }
+  int isnumber = (digitvalue(ch)<base);
   buffer[2] = '\0'; // In case variable is one letter

   while(!isspace(ch) && ch != ')' && index < buflen){
     buffer[index++] = ch;
-    isnumber = isnumber && isdigit(ch);
+    int temp = digitvalue(ch);
+    result = result * base + temp;
+    isnumber = isnumber && (digitvalue(ch)<base);
     ch = Getc();
   }

   buffer[index] = '\0';
   if (ch == ')') LastChar = ')';

-  if (isnumber) return number(atoi(buffer));
+  if (isnumber) {
+    if (base == 10 && result > ((unsigned int)32767+(1-sign)/2)) {
+      Serial.println();
+      error(F("Number out of range"));
+    }
+    return number(result*sign);
+  }

   int x = builtin(buffer);
   if (x == NIL) return nil;
@@ -1800,36 +1952,34 @@
 object *readrest() {
   object *item = nextitem();

-  if(item == ket) return NULL;
+  if(item == (object *)KET) return NULL;

-  if(item == dot) {
+  if(item == (object *)DOT) {
     object *arg1 = read();
     if (readrest() != NULL) error(F("Malformed list"));
     return arg1;
   }

-  if(item == quo) {
+  if(item == (object *)QUO) {
     object *arg1 = read();
     return cons(cons(symbol(QUOTE), cons(arg1, NULL)), readrest());
   }

-  if(item == bra) item = readrest();
+  if(item == (object *)BRA) item = readrest();
   return cons(item, readrest());
 }

 object *read() {
   object *item = nextitem();
-  if (item == bra) return readrest();
-  if (item == dot) return read();
-  if (item == quo) return cons(symbol(QUOTE), cons(read(), NULL));
+  if (item == (object *)BRA) return readrest();
+  if (item == (object *)DOT) return read();
+  if (item == (object *)QUO) return cons(symbol(QUOTE), cons(read(), NULL));
   return item;
 }

 void initenv() {
   GlobalEnv = NULL;
-  bra = symbol(BRA); ket = symbol(KET);
-  tee = symbol(TEE); quo = symbol(QUO);
-  dot = symbol(DOT);
+  tee = symbol(TEE);
 }

 // Setup
@@ -1839,7 +1989,8 @@
   while (!Serial);  // wait for Serial to initialize
   initworkspace();
   initenv();
-  Serial.println(F("uLisp 1.0"));
+  _end = 0xA5;
+  Serial.println(F("uLisp 1.1"));
 }

 // Read/Evaluate/Print loop
@@ -1855,7 +2006,7 @@
     }
     Serial.print(F("> "));
     object *line = read();
-    if (line == nil) return;
+    if (line == nil) { Serial.println(); return; }
     Serial.println();
     push(line, GCStack);
     printobject(eval(line,env));
@@ -1866,6 +2017,14 @@
 }

 void loop() {
-  setjmp(exception);
+  if (!setjmp(exception)) {
+    #if defined(resetautorun)
+    object *autorun = (object *)eeprom_read_word(&image.eval);
+    if (autorun != NULL && (unsigned int)autorun != 0xFFFF) {
+      loadimage();
+      apply(autorun, NULL, NULL);
+    }
+    #endif
+  }
   repl(NULL);
-}
+}


Update ThingspeakWriteSingleField.ino

Update ThingspeakWriteSingleField.ino

--- Arduino/NetworkCommunication/ThingspeakWriteSingleField/ThingspeakWriteSingleField.ino
+++ Arduino/NetworkCommunication/ThingspeakWriteSingleField/ThingspeakWriteSingleField.ino
@@ -1,3 +1,4 @@
+
 /*
   WriteSingleField

@@ -22,7 +23,7 @@
   Copyright 2018, The MathWorks, Inc.
 */

-#include "ThingSpeak.h"
+#include <ThingSpeak.h>
 #include <WiFiNINA.h>
 #include "secrets.h"


Testing ATtiny85V low-power down mode.

Testing ATtiny85V low-power down mode.

--- examples/VWI/CosaVWItempsensor/CosaVWItempsensor.ino
+++ examples/VWI/CosaVWItempsensor/CosaVWItempsensor.ino
@@ -41,6 +41,7 @@
 #include "Cosa/VWI/Codec/VirtualWireCodec.hh"
 #include "Cosa/FixedPoint.hh"
 #include "Cosa/Watchdog.hh"
+#include "Cosa/Power.hh"

 // Connect to one-wire device. Assuming only one device even if connected
 OWI owi(Board::D2);
@@ -63,6 +64,11 @@
   // Connect to the temperature sensor and give some time for startup
   sensor.connect(0);
   SLEEP(1);
+
+  // Disable hardware
+  VWI::disable();
+  Power::adc_disable();
+  Power::usi_disable();
 }

 // Message from the device. Use one-wire identity as virtual wire identity
@@ -81,11 +87,13 @@
   sensor.convert_request();
   SLEEP(1);

-  // Read temperature and send a message with identiy and sequence number
-  sensor.read_temperature();
+  // Read temperature and send a message with identity and sequence number
+  sensor.read_scratchpad();
   memcpy(&msg.id, sensor.get_rom(), sizeof(msg.id));
   msg.nr = nr++;
   msg.temperature = sensor.get_temperature();
+  VWI::enable();
   tx.send(&msg, sizeof(msg));
   tx.await();
+  VWI::disable();
 }


Cleanup.

Cleanup.

--- examples/TWI/CosaDS1307/CosaDS1307.ino
+++ examples/TWI/CosaDS1307/CosaDS1307.ino
@@ -34,7 +34,7 @@
 #include "Cosa/Memory.h"

 // Set the real-time clock
-// #define __RTC_SET_TIME__
+#define SET_TIME

 // The real-time device, latest start and sample time in ram
 DS1307 rtc;
@@ -47,11 +47,6 @@

 // Use the builtin led for a heartbeat
 OutputPin ledPin(Board::LED);
-
-#if defined(__ARDUINO_TINYX5__)
-#include "Cosa/Soft/UART.hh"
-Soft::UART uart(Board::D4);
-#endif

 void setup()
 {
@@ -67,34 +62,35 @@

   // Read the latest set and run time
   latest_t latest;
-  rtc.read(&latest, sizeof(latest), DS1307::RAM_START);
+  int count = rtc.read(&latest, sizeof(latest), DS1307::RAM_START);
+  TRACE(count);
+
+  // Print latest set and the latest run time
+  trace.print_P(PSTR("set on "));
+  trace << latest.set << endl;
+  trace.print_P(PSTR("run on "));
+  trace << latest.run << endl;

   // Set the time. Adjust below to your current time
   time_t now;
-#ifdef __RTC_SET_TIME__
+#if defined(SET_TIME)
   now.year = 0x13;
-  now.month = 0x07;
-  now.date = 0x15;
+  now.month = 0x12;
+  now.date = 0x31;
   now.day = 0x02;
-  now.hours = 0x12;
-  now.minutes = 0x45;
+  now.hours = 0x23;
+  now.minutes = 0x55;
   now.seconds = 0x00;
   rtc.set_time(now);
   latest.set = now;
+#else
+  rtc.get_time(now);
 #endif

-  // Print latest set time
-  trace.print_P(PSTR("set on "));
-  trace << latest.set << endl;
-
-  // And the latest run time
-  trace.print_P(PSTR("run on "));
-  trace << latest.run << endl;
-
   // Update the run time with the current time and update ram
-  rtc.get_time(now);
   latest.run = now;
-  rtc.write(&latest, sizeof(latest), DS1307::RAM_START);
+  count = rtc.write(&latest, sizeof(latest), DS1307::RAM_START);
+  TRACE(count);
 }

 void loop()
@@ -105,8 +101,8 @@

   // Read the time from the rtc device and print
   time_t now;
-  rtc.get_time(now);
-  trace << now << endl;
+  if (rtc.get_time(now))
+    trace << now << endl;

   // Heartbeat
   ledPin.toggle();


Add waiting message to graphics test example

--- examples/graphicstest/graphicstest.ino
+++ examples/graphicstest/graphicstest.ino
@@ -25,11 +25,15 @@
 ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);

 void setup() {
+  tft.begin();
+  tft.fillScreen(ILI9341_BLACK);
+  tft.setTextColor(ILI9341_YELLOW);
+  tft.setTextSize(2);
+  tft.println("Waiting for Arduino Serial Monitor...");
+
   Serial.begin(9600);
   while (!Serial) ; // wait for Arduino Serial Monitor
   Serial.println("ILI9341 Test!");
-
-  tft.begin();

   // read diagnostics (optional but can help debug problems)
   uint8_t x = tft.readcommand8(ILI9341_RDMODE);


DigitalClockDisplay requires argument for non-current time display.

--- PortableAntennaRotorControl.ino
+++ PortableAntennaRotorControl.ino
@@ -211,7 +211,7 @@
     Serial.print( "GPS Flags:" );Serial.print( gpsTFFlg );
     Serial.println( gpsZDAFlg );
   }
-  digitalClockDisplay();
+  digitalClockDisplay(now());
 }

 void procZDAMsg() {
@@ -361,19 +361,19 @@
   return Teensy3Clock.get();
 }

-void digitalClockDisplay() {
+void digitalClockDisplay(time_t t) {
   // digital clock display of the time
-  int hr = hour();
+  int hr = hour(t);
   if ( hr < 10 ) Serial.print( '0' );
   Serial.print(hr);
-  printDigits(minute());
-  printDigits(second());
+  printDigits(minute(t));
+  printDigits(second(t));
   Serial.print(" ");
-  Serial.print(day());
+  Serial.print(day(t));
   Serial.print(" ");
-  Serial.print(month());
+  Serial.print(month(t));
   Serial.print(" ");
-  Serial.print(year());
+  Serial.print(year(t));
   Serial.println();
 }

@@ -391,7 +391,7 @@
   }

   // Set clock(s) to UTC time if a valid fix came in since the last PPS
-  //if ( DEBUG ) digitalClockDisplay();
+  //if ( DEBUG ) digitalClockDisplay( now() );
   if ( gpsTimeValid && gpsTimeFlg ) {
     time_t oldTime = getTeensy3Time();
     setTime( gpsHr,gpsMin,gpsSec,gpsDay,gpsMon,gpsYr ); // "Library" time


Improve spitftbitmap example

Improve spitftbitmap example

--- examples/spitftbitmap/spitftbitmap.ino
+++ examples/spitftbitmap/spitftbitmap.ino
@@ -30,32 +30,39 @@
 #define SD_CS 4

 void setup(void) {
-  delay(2000);
-  Serial.begin(9600);
+  // Keep the SD card inactive while working the display.
+  pinMode(SD_CS, INPUT_PULLUP);
+  delay(200);

   tft.begin();
   tft.fillScreen(ILI9341_BLUE);
-
-  Serial.print("Initializing SD card...");
-  if (!SD.begin(SD_CS)) {
-    Serial.println("failed!");
+
+  Serial.begin(9600);
+  tft.setTextColor(ILI9341_WHITE);
+  tft.setTextSize(2);
+  tft.println(F("Waiting for Arduino Serial Monitor..."));
+  while (!Serial) {
+    if (millis() > 8000) break;
+  }
+
+  Serial.print(F("Initializing SD card..."));
+  tft.println(F("Init SD card..."));
+  while (!SD.begin(SD_CS)) {
+    Serial.println(F("failed to access SD card!"));
+    tft.println(F("failed to access SD card!"));
+    delay(2000);
   }
   Serial.println("OK!");
-
+}
+
+void loop() {
+  tft.fillScreen(ILI9341_GREEN);
   bmpDraw("purple.bmp", 0, 0);
-}
-
-void loop() {
   delay(5000);
-  Serial.println("Try using writeRect");
-  tft.fillScreen(ILI9341_GREEN);
-  bmpDrawWriteRect("purple.bmp", 0, 0);
-
+  bmpDraw("flowers.bmp", 0, 0);
   delay(5000);
-  Serial.println("Using pushCOlor");
-  tft.fillScreen(ILI9341_BLUE);
-  bmpDraw("purple.bmp", 0, 0);
-
+  bmpDraw("cat.bmp", 0, 0);
+  delay(5000);
 }

 // This function opens a Windows Bitmap (BMP) file and
@@ -64,11 +71,20 @@
 // (rather than pixel by pixel).  Increasing the buffer
 // size takes more of the Arduino's precious RAM but
 // makes loading a little faster.  20 pixels seems a
-// good balance.
-
-#define BUFFPIXEL 20
-
-void bmpDraw(char *filename, uint8_t x, uint16_t y) {
+// good balance for tiny AVR chips.
+
+// Larger buffers are slightly more efficient, but if
+// the buffer is too large, extra data is read unnecessarily.
+// For example, if the image is 240 pixels wide, a 100
+// pixel buffer will read 3 groups of 100 pixels.  The
+// last 60 pixels from the 3rd read may not be used.
+
+#define BUFFPIXEL 80
+
+
+//===========================================================
+// Try Draw using writeRect
+void bmpDraw(const char *filename, uint8_t x, uint16_t y) {

   File     bmpFile;
   int      bmpWidth, bmpHeight;   // W+H in pixels
@@ -76,13 +92,15 @@
   uint32_t bmpImageoffset;        // Start of image data in file
   uint32_t rowSize;               // Not always = bmpWidth; may have padding
   uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
-  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
+  uint16_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
   boolean  goodBmp = false;       // Set to true on valid header parse
   boolean  flip    = true;        // BMP is stored bottom-to-top
   int      w, h, row, col;
   uint8_t  r, g, b;
   uint32_t pos = 0, startTime = millis();

+  uint16_t awColors[320];  // hold colors for one row at a time...
+
   if((x >= tft.width()) || (y >= tft.height())) return;

   Serial.println();
@@ -91,7 +109,7 @@
   Serial.println('\'');

   // Open requested file on SD card
-  if ((bmpFile = SD.open(filename)) == NULL) {
+  if (!(bmpFile = SD.open(filename))) {
     Serial.print(F("File not found"));
     return;
   }
@@ -133,9 +151,6 @@
         if((x+w-1) >= tft.width())  w = tft.width()  - x;
         if((y+h-1) >= tft.height()) h = tft.height() - y;

-        // Set TFT address window to clipped image bounds
-        tft.setAddrWindow(x, y, x+w-1, y+h-1);
-
         for (row=0; row<h; row++) { // For each scanline...

           // Seek to start of scan line.  It might seem labor-
@@ -164,118 +179,6 @@
             b = sdbuffer[buffidx++];
             g = sdbuffer[buffidx++];
             r = sdbuffer[buffidx++];
-            tft.pushColor(tft.color565(r,g,b));
-          } // end pixel
-        } // end scanline
-        Serial.print(F("Loaded in "));
-        Serial.print(millis() - startTime);
-        Serial.println(" ms");
-      } // end goodBmp
-    }
-  }
-
-  bmpFile.close();
-  if(!goodBmp) Serial.println(F("BMP format not recognized."));
-}
-
-
-//===========================================================
-// Try Draw using writeRect
-void bmpDrawWriteRect(char *filename, uint8_t x, uint16_t y) {
-
-  File     bmpFile;
-  int      bmpWidth, bmpHeight;   // W+H in pixels
-  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
-  uint32_t bmpImageoffset;        // Start of image data in file
-  uint32_t rowSize;               // Not always = bmpWidth; may have padding
-  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
-  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
-  boolean  goodBmp = false;       // Set to true on valid header parse
-  boolean  flip    = true;        // BMP is stored bottom-to-top
-  int      w, h, row, col;
-  uint8_t  r, g, b;
-  uint32_t pos = 0, startTime = millis();
-
-  uint16_t awColors[320];  // hold colors for one row at a time...
-
-  if((x >= tft.width()) || (y >= tft.height())) return;
-
-  Serial.println();
-  Serial.print(F("Loading image '"));
-  Serial.print(filename);
-  Serial.println('\'');
-
-  // Open requested file on SD card
-  if ((bmpFile = SD.open(filename)) == NULL) {
-    Serial.print(F("File not found"));
-    return;
-  }
-
-  // Parse BMP header
-  if(read16(bmpFile) == 0x4D42) { // BMP signature
-    Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
-    (void)read32(bmpFile); // Read & ignore creator bytes
-    bmpImageoffset = read32(bmpFile); // Start of image data
-    Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
-    // Read DIB header
-    Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
-    bmpWidth  = read32(bmpFile);
-    bmpHeight = read32(bmpFile);
-    if(read16(bmpFile) == 1) { // # planes -- must be '1'
-      bmpDepth = read16(bmpFile); // bits per pixel
-      Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
-      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
-
-        goodBmp = true; // Supported BMP format -- proceed!
-        Serial.print(F("Image size: "));
-        Serial.print(bmpWidth);
-        Serial.print('x');
-        Serial.println(bmpHeight);
-
-        // BMP rows are padded (if needed) to 4-byte boundary
-        rowSize = (bmpWidth * 3 + 3) & ~3;
-
-        // If bmpHeight is negative, image is in top-down order.
-        // This is not canon but has been observed in the wild.
-        if(bmpHeight < 0) {
-          bmpHeight = -bmpHeight;
-          flip      = false;
-        }
-
-        // Crop area to be loaded
-        w = bmpWidth;
-        h = bmpHeight;
-        if((x+w-1) >= tft.width())  w = tft.width()  - x;
-        if((y+h-1) >= tft.height()) h = tft.height() - y;
-
-        for (row=0; row<h; row++) { // For each scanline...
-
-          // Seek to start of scan line.  It might seem labor-
-          // intensive to be doing this on every line, but this
-          // method covers a lot of gritty details like cropping
-          // and scanline padding.  Also, the seek only takes
-          // place if the file position actually needs to change
-          // (avoids a lot of cluster math in SD library).
-          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
-            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
-          else     // Bitmap is stored top-to-bottom
-            pos = bmpImageoffset + row * rowSize;
-          if(bmpFile.position() != pos) { // Need seek?
-            bmpFile.seek(pos);
-            buffidx = sizeof(sdbuffer); // Force buffer reload
-          }
-
-          for (col=0; col<w; col++) { // For each pixel...
-            // Time to read more pixel data?
-            if (buffidx >= sizeof(sdbuffer)) { // Indeed
-              bmpFile.read(sdbuffer, sizeof(sdbuffer));
-              buffidx = 0; // Set index to beginning
-            }
-
-            // Convert pixel from BMP to TFT format, push to display
-            b = sdbuffer[buffidx++];
-            g = sdbuffer[buffidx++];
-            r = sdbuffer[buffidx++];
             awColors[col] = tft.color565(r,g,b);
           } // end pixel
           tft.writeRect(0, row, w, 1, awColors);


fix linkit one demo

fix linkit one demo

--- libraries/ATT_IOT/examples/GPRSLinkit/GPRSLinkit.ino
+++ libraries/ATT_IOT/examples/GPRSLinkit/GPRSLinkit.ino
@@ -44,9 +44,9 @@
   - Make certain that the data type you used to create the asset is the expected data type. Ex, when you define the asset as 'int', don't send strings or boolean values.
 */

-char deviceId[] = "PTFcFdHlkjuSECMWdDjTyd2";
-char clientId[] = "jb1";
-char clientKey[] = "sa5slr2vhcf";
+char deviceId[] = "";
+char clientId[] = "";
+char clientKey[] = "";

 //Warning the LinkIt maps pin numbers differently compared to the arduino. LinkIt pin numbers can be
 //bigger then 9, which doesn't work well with the AllthingsTalk library: it expects asset id's between 0 and 9,


Nothing still

Nothing still

--- hispatracker/hispatracker.ino
+++ hispatracker/hispatracker.ino
@@ -1,37 +1,3 @@
-/* trackuino copyright (C) 2010  EA5HAV Javi
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-// Mpide 22 fails to compile Arduino code because it stupidly defines ARDUINO
-// as an empty macro (hence the +0 hack). UNO32 builds are fine. Just use the
-// real Arduino IDE for Arduino builds. Optionally complain to the Mpide
-// authors to fix the broken macro.
-// #if (ARDUINO + 0) == 0
-// #error "Oops! We need the real Arduino IDE (version 22 or 23) for Arduino builds."
-// #error "See trackuino.pde for details on this"
-
-// // Refuse to compile on arduino version 21 or lower. 22 includes an
-// // optimization of the USART code that is critical for real-time operation
-// // of the AVR code.
-// #elif (ARDUINO + 0) < 22
-// #error "Oops! We need Arduino 22 or 23"
-// #error "See trackuino.pde for details on this"
-
-// #endif
-
 // // Arduino/AVR libs
 // #if (ARDUINO + 1) >= 100
 // #  include <Arduino.h>
@@ -54,11 +20,10 @@
 #include <NMEAGPS.h>
 #include <NeoSWSerial.h>

-NMEAGPS gps;  // The parsing object
-gps_fix fix;  // GPS fields are stored here as they are parsed
-uint8_t gpsUpdates = 0; // how many updates since power_save
+static NMEAGPS gps;  // The parsing object
+static gps_fix fix;  // GPS fields are stored here as they are parsed

-NeoSWSerial gpsS(RXPIN, TXPIN);
+static NeoSWSerial gpsS(RXPIN, TXPIN);

 //Internal Temp sensor
 #include <Wire.h>
@@ -66,7 +31,7 @@

 Adafruit_BMP085 bmp;

-unsigned long next_aprs = 0;
+static uint8_t next_aprs = 0;

 void setup() {
     pinMode(LED_PIN, OUTPUT);
@@ -78,50 +43,52 @@

     gpsS.begin(9600);

-    next_aprs = millis() + 5000l;
+    next_aprs = 0;
 }

 void loop() {
-  if (gps.available(gpsS)) {
+  while (gps.available(gpsS)) {
     fix = gps.read();
-    gpsUpdates++;
-  }
+    next_aprs++;

-  if (millis() >= next_aprs && gpsUpdates >= 2) {
-    gpsUpdates = 0;
+    if (next_aprs >= 5) {
+      next_aprs = 0;

-    fix = gps.read();
-    char latitude[9];
-    snprintf(latitude, 9, "%02d%02d.%02u%c", fix.latitudeDMS.degrees, fix.latitudeDMS.minutes, truncate(fix.latitudeDMS.seconds_whole, 2), fix.latitudeDMS.NS() );
+      char latitude[9];
+      char longitude[10];
+      char datetime[7];
+      if(fix.valid.location) {
+        snprintf(latitude, 9, "%02d%02d.%02u%c", fix.latitudeDMS.degrees, fix.latitudeDMS.minutes, truncate(fix.latitudeDMS.seconds_whole, 2), fix.latitudeDMS.NS() );
+        snprintf(longitude, 10, "%03d%02d.%02u%c", fix.longitudeDMS.degrees, fix.longitudeDMS.minutes, truncate(fix.longitudeDMS.seconds_whole, 2), fix.longitudeDMS.EW() );
+      }

-    char longitude[10];
-    snprintf(longitude, 10, "%03d%02d.%02u%c", fix.longitudeDMS.degrees, fix.longitudeDMS.minutes, truncate(fix.longitudeDMS.seconds_whole, 2), fix.longitudeDMS.EW() );

-    char datetime[7];
-    snprintf(datetime, 7, "%02d%02d%02d", fix.dateTime.hours, fix.dateTime.minutes, fix.dateTime.seconds);
-
-    //Check pressure the flight day in http://weather.noaa.gov/weather/current/LEMD.html
-    //We take the value between parenthesis. If pressure is 1013 write in readAltitude
-    //this value multiplied by 100
-    APRSPacket packet(datetime,
-                      bmp.readAltitude(101900),
-                      fix.speed(),
-                      fix.heading(),
-                      bmp.readTemperature(),
-                      bmp.readPressure());
+      snprintf(datetime, 7, "%02d%02d%02d", fix.dateTime.hours, fix.dateTime.minutes, fix.dateTime.seconds);

-    packet.setLatitude(latitude);
-    packet.setLongitude(longitude);
-    packet.setLatitudeF(fix.latitude());
-    packet.setLongitudeF(fix.longitude());
-
-    packet.aprs_send();
-
-    while (afsk_flush()) {
-      power_save();
+
+      //Check pressure the flight day in http://weather.noaa.gov/weather/current/LEMD.html
+      //We take the value between parenthesis. If pressure is 1013 write in readAltitude
+      //this value multiplied by 100
+      APRSPacket packet(datetime,
+                        bmp.readAltitude(101900),
+                        fix.speed(),
+                        fix.heading(),
+                        bmp.readTemperature(),
+                        bmp.readPressure());
+
+      packet.setLatitude(latitude);
+      packet.setLongitude(longitude);
+      if(fix.valid.location){
+        packet.setLatitudeF(fix.latitude());
+        packet.setLongitudeF(fix.longitude());
+      }
+
+      packet.aprs_send();
+
+      while (afsk_flush()) {
+        power_save();
+      }
     }
-
-    next_aprs = millis() + 5000l;
   }
 }


Update DigestAuthorization.ino (Simple example update) (#7579)

Simple example update to pass the method as a parameter to getDigestAuth(), so it is more easily used for POST.
Add setting the ransom seed to RANDOM_REG32 in setup() for better getCNonce() values.

--- libraries/ESP8266HTTPClient/examples/DigestAuthorization/DigestAuthorization.ino
+++ libraries/ESP8266HTTPClient/examples/DigestAuthorization/DigestAuthorization.ino
@@ -47,7 +47,7 @@
   return s;
 }

-String getDigestAuth(String& authReq, const String& username, const String& password, const String& uri, unsigned int counter) {
+String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter) {
   // extracting required parameters for RFC 2069 simpler Digest
   String realm = exractParam(authReq, "realm=\"", '"');
   String nonce = exractParam(authReq, "nonce=\"", '"');
@@ -64,7 +64,7 @@
   String h1 = md5.toString();

   md5.begin();
-  md5.add(String("GET:") + uri);
+  md5.add(method + ":" + uri);
   md5.calculate();
   String h2 = md5.toString();

@@ -81,6 +81,7 @@
 }

 void setup() {
+  randomSeed(RANDOM_REG32);
   Serial.begin(115200);

   WiFi.mode(WIFI_STA);
@@ -118,7 +119,7 @@
     String authReq = http.header("WWW-Authenticate");
     Serial.println(authReq);

-    String authorization = getDigestAuth(authReq, String(username), String(password), String(uri), 1);
+    String authorization = getDigestAuth(authReq, String(username), String(password), "GET", String(uri), 1);

     http.end();
     http.begin(client, String(server) + String(uri));


Deleted small trash lines

Deleted small trash lines

--- clock/clock.ino
+++ clock/clock.ino
@@ -3,7 +3,6 @@
 #include <Wire.h>
 #include <LiquidCrystal_I2C.h>
 #include <Time.h>
-#include <DateTime.h>
 #include <DS1302RTC.h>
 // Set pins:  CE, IO,CLK
 DS1302RTC RTC(13, 12, 11);
@@ -11,8 +10,6 @@

 byte LT[8] =
 {
-
-
   B00111,
   B01111,
   B11111,
@@ -107,11 +104,7 @@
 // loop counter
 int count = 0;

-
-
 void setup () {
-  Serial.begin(57600);
-  Wire.begin();
   lcd.init();                      // initialize the lcd
   lcd.backlight();
   lcd.home();
@@ -127,11 +120,7 @@

   // sets the LCD's rows and colums:
   lcd.clear();
-
-
-
-}
-
+}

 void custom0(int x)
@@ -256,16 +245,6 @@

 }

-//void clearnumber(int x)
-//{ // clears the area the custom number is displayed in
-// lcd.setCursor(x,0);
-// lcd.print("   ");
-// lcd.setCursor(x,1);
-// lcd.print("   ");
-//}
-
-
-

 void loop () {

@@ -285,11 +264,6 @@

   printDigits(tm.Minute/10,9);
   printDigits(tm.Minute%10,13);
-
-  // lcd.setCursor(7, 1);
-  //  lcd.print(now.second()/10);
-  //   lcd.print(now.second()%10);
-

   if (tm.Second%10%2==0){
     lcd.setCursor(7, 0);
@@ -324,8 +298,6 @@

 }

-
-
 void printDigits(int digits, int x){
   // utility function for digital clock display: prints preceding colon and leading 0

@@ -362,8 +334,5 @@
     break;

   }
-
-
-
-}
-
+}
+


COH-71: making FHT8V getHCx() ISR- safe

COH-71: making FHT8V getHCx() ISR- safe

--- COHEAT2015/20160316-COHEAT-M1/COHEAT-M1/COHEAT-M1.ino
+++ COHEAT2015/20160316-COHEAT-M1/COHEAT-M1/COHEAT-M1.ino
@@ -625,7 +625,6 @@
       // After decode instance should be valid and with correct house code.
       if(c.isValid())
         {
-//        p->print(F("+CC1 * ")); p->print(a.getHC1()); p->print(' '); p->println(a.getHC2());
         // Process the message only if it is targetted at this node.
         const uint8_t hc1 = FHT8VGetHC1();
         const uint8_t hc2 = FHT8VGetHC2();
@@ -882,6 +881,7 @@
   {
   if((buflen < 8) || (OTRadioLink::FTp2_CC1PollAndCmd != buf[0])) { return(false); }
   buflen = 8; // Truncate message to correct size for efficiency.
+  // TODO: filter on inbound address/housecode as FHT8V.getHC{1,2}() are thread-safe.
   return(true); // Accept message.
   }
 #elif defined(ALLOW_CC1_SUPPORT_HUB)
@@ -896,7 +896,6 @@
   }
 #endif

-#if defined(ALLOW_CC1_SUPPORT)
 // COHEAT: REV2/REV9 talking on fast GFSK channel 0, REV9 TX to FHT8V on slow OOK.
 #define RADIO_CONFIG_NAME "COHEAT DUAL CHANNEL"
 static const uint8_t nPrimaryRadioChannels = 2;
@@ -907,18 +906,7 @@
   // FS20/FHT8V compatible channel 1 full config, used for TX only, not secure, unframed.
   OTRadioLink::OTRadioChannelConfig(OTRFM23BLink::StandardRegSettingsOOK5000, true, false, true, false, false, true),
   };
-#elif defined(ENABLE_FAST_FRAMED_CARRIER_SUPPORT)
-#define RADIO_CONFIG_NAME "GFSK"
-// Nodes talking on fast GFSK channel 0.
-static const uint8_t nPrimaryRadioChannels = 1;
-static const OTRadioLink::OTRadioChannelConfig RFM23BConfigs[nPrimaryRadioChannels] =
-  {
-  // GFSK channel 0 full config, RX/TX, not in itself secure.
-  OTRadioLink::OTRadioChannelConfig(OTRFM23BLink::StandardRegSettingsGFSK57600, true),
-  };
-#endif
-
-#ifdef ENABLE_EXTENDED_CLI
+
 // Handle CLI extension commands.
 // Commands of form:
 //   +EXT .....
@@ -1005,7 +993,6 @@

   return(false); // FAILED if not otherwise handled.
   }
-#endif

 // Remaining minutes to keep CLI active; zero implies inactive.
 // Starts up with full value to allow easy setting of time, etc, without specially activating CLI.